kernel tree reorganization stage 1: Major cvs repository work (not logged as
[dragonfly.git] / sys / dev / raid / ida / ida_eisa.c
1 /*
2  * Copyright (c) 2000 Jonathan Lemon
3  * Copyright (c) 1999 by Matthew N. Dodd <winter@jurai.net>
4  * All Rights Reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions, and the following disclaimer,
11  *    without modification, immediately at the beginning of the file.
12  * 2. The name of the author may not be used to endorse or promote products
13  *    derived from this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
19  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * $FreeBSD: src/sys/dev/ida/ida_eisa.c,v 1.1.2.4 2001/07/30 20:29:58 jlemon Exp $
28  * $DragonFly: src/sys/dev/raid/ida/ida_eisa.c,v 1.3 2003/08/07 21:17:09 dillon Exp $
29  */
30
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/kernel.h>
34 #include <sys/bus.h>
35
36 #include <sys/buf.h>
37 #include <sys/devicestat.h>
38 #include <sys/disk.h>
39
40 #include <machine/bus_pio.h>
41 #include <machine/bus.h>
42 #include <machine/resource.h>
43 #include <sys/rman.h>
44
45 #include "idavar.h"
46 #include "idareg.h"
47
48 #include <bus/eisa/eisaconf.h>
49
50 #define IDA_EISA_IOPORT_START   0x0c88
51 #define IDA_EISA_IOPORT_LEN     0x0017
52
53 #define IDA_EISA_IRQ_REG        0x0cc0
54 #define IDA_EISA_IRQ_MASK       0xf0
55 #define IDA_EISA_IRQ_15         0x80
56 #define IDA_EISA_IRQ_14         0x40
57 #define IDA_EISA_IRQ_11         0x10
58 #define IDA_EISA_IRQ_10         0x20
59
60 static int
61 ida_v1_fifo_full(struct ida_softc *ida)
62 {
63         u_int8_t status;
64
65         status = ida_inb(ida, R_EISA_SYSTEM_DOORBELL);
66         return ((status & EISA_CHANNEL_CLEAR) == 0);
67 }
68
69 static void
70 ida_v1_submit(struct ida_softc *ida, struct ida_qcb *qcb)
71 {
72         u_int16_t size;
73
74         /*
75          * On these cards, this location is actually for control flags.
76          * Set them to zero and pass in structure size via an I/O port.
77          */
78         size = qcb->hwqcb->hdr.size << 2;
79         qcb->hwqcb->hdr.size = 0;
80
81         ida_outb(ida, R_EISA_SYSTEM_DOORBELL, EISA_CHANNEL_CLEAR);
82         ida_outl(ida, R_EISA_LIST_ADDR, qcb->hwqcb_busaddr);
83         ida_outw(ida, R_EISA_LIST_LEN, size);
84         ida_outb(ida, R_EISA_LOCAL_DOORBELL, EISA_CHANNEL_BUSY);
85 }
86
87 static bus_addr_t
88 ida_v1_done(struct ida_softc *ida)
89 {
90         struct ida_hardware_qcb *hwqcb;
91         bus_addr_t completed;
92         u_int8_t status;
93
94         if ((ida_inb(ida, R_EISA_SYSTEM_DOORBELL) & EISA_CHANNEL_BUSY) == 0)
95                 return (0);
96
97         ida_outb(ida, R_EISA_SYSTEM_DOORBELL, EISA_CHANNEL_BUSY);
98         completed = ida_inl(ida, R_EISA_COMPLETE_ADDR);
99         status = ida_inb(ida, R_EISA_LIST_STATUS);
100         ida_outb(ida, R_EISA_LOCAL_DOORBELL, EISA_CHANNEL_CLEAR);
101
102         if (completed != 0) {
103                 hwqcb = (struct ida_hardware_qcb *)
104                     ((bus_addr_t)ida->hwqcbs +
105                     ((completed & ~3) - ida->hwqcb_busaddr));
106                 hwqcb->req.error = status;
107         }
108
109         return (completed);
110 }
111
112 static int
113 ida_v1_int_pending(struct ida_softc *ida)
114 {
115         return (ida_inb(ida, R_EISA_SYSTEM_DOORBELL) & EISA_CHANNEL_BUSY);
116 }
117
118 static void
119 ida_v1_int_enable(struct ida_softc *ida, int enable)
120 {
121         if (enable) {
122                 ida_outb(ida, R_EISA_SYSTEM_DOORBELL, ~EISA_CHANNEL_CLEAR);
123                 ida_outb(ida, R_EISA_LOCAL_DOORBELL, EISA_CHANNEL_BUSY);
124                 ida_outb(ida, R_EISA_INT_MASK, INT_ENABLE);
125                 ida_outb(ida, R_EISA_SYSTEM_MASK, INT_ENABLE);
126                 ida->flags |= IDA_INTERRUPTS;
127         } else {
128                 ida_outb(ida, R_EISA_SYSTEM_MASK, INT_DISABLE);
129                 ida->flags &= ~IDA_INTERRUPTS;
130         }
131 }
132
133 static int
134 ida_v2_fifo_full(struct ida_softc *ida)
135 {
136         return (ida_inl(ida, R_CMD_FIFO) == 0);
137 }
138
139 static void
140 ida_v2_submit(struct ida_softc *ida, struct ida_qcb *qcb)
141 {
142         ida_outl(ida, R_CMD_FIFO, qcb->hwqcb_busaddr);
143 }
144
145 static bus_addr_t
146 ida_v2_done(struct ida_softc *ida)
147 {
148         return (ida_inl(ida, R_DONE_FIFO));
149 }
150
151 static int
152 ida_v2_int_pending(struct ida_softc *ida)
153 {
154         return (ida_inl(ida, R_INT_PENDING));
155 }
156
157 static void
158 ida_v2_int_enable(struct ida_softc *ida, int enable)
159 {
160         if (enable)
161                 ida->flags |= IDA_INTERRUPTS;
162         else
163                 ida->flags &= ~IDA_INTERRUPTS;
164         ida_outl(ida, R_INT_MASK, enable ? INT_ENABLE : INT_DISABLE);
165 }
166
167 static struct ida_access ida_v1_access = {
168         ida_v1_fifo_full,
169         ida_v1_submit,
170         ida_v1_done,
171         ida_v1_int_pending,
172         ida_v1_int_enable,
173 };
174
175 static struct ida_access ida_v2_access = {
176         ida_v2_fifo_full,
177         ida_v2_submit,
178         ida_v2_done,
179         ida_v2_int_pending,
180         ida_v2_int_enable,
181 };
182
183 static struct ida_board board_id[] = {
184         { 0x0e114001, "Compaq IDA controller",
185             &ida_v1_access, 0 },
186         { 0x0e114002, "Compaq IDA-2 controller",
187             &ida_v1_access, 0 },        
188         { 0x0e114010, "Compaq IAES controller",
189             &ida_v1_access, 0 },
190         { 0x0e114020, "Compaq SMART array controller",
191             &ida_v1_access, 0 },
192         { 0x0e114030, "Compaq SMART-2/E array controller",
193             &ida_v2_access, 0 },
194
195         { 0, "", 0, 0 }
196 };
197
198 static struct   ida_board *ida_eisa_match(eisa_id_t);
199 static int      ida_eisa_probe(device_t);
200 static int      ida_eisa_attach(device_t);
201
202 static device_method_t ida_eisa_methods[] = {
203         DEVMETHOD(device_probe,         ida_eisa_probe),
204         DEVMETHOD(device_attach,        ida_eisa_attach),
205         DEVMETHOD(device_detach,        ida_detach),
206
207         { 0, 0 }
208 };
209
210 static driver_t ida_eisa_driver = {
211         "ida",
212         ida_eisa_methods,
213         sizeof(struct ida_softc)
214 };
215
216 static devclass_t ida_devclass;
217
218 static struct ida_board *
219 ida_eisa_match(eisa_id_t id)
220 {
221         int i;
222
223         for (i = 0; board_id[i].board; i++)
224                 if (board_id[i].board == id)
225                         return (&board_id[i]);
226         return (NULL);
227 }
228
229 static int
230 ida_eisa_probe(device_t dev)
231 {
232         struct ida_board        *board;
233         u_int32_t               io_base;
234         u_int                   irq = 0;
235
236         board = ida_eisa_match(eisa_get_id(dev));
237         if (board == NULL)
238                 return (ENXIO);
239         device_set_desc(dev, board->desc);
240
241         io_base = (eisa_get_slot(dev) * EISA_SLOT_SIZE);
242
243         switch (IDA_EISA_IRQ_MASK & (inb(IDA_EISA_IRQ_REG + io_base))) {
244         case IDA_EISA_IRQ_15:
245                 irq = 15;
246                 break;
247         case IDA_EISA_IRQ_14:
248                 irq = 14;
249                 break;
250         case IDA_EISA_IRQ_11:
251                 irq = 11;
252                 break;
253         case IDA_EISA_IRQ_10:
254                 irq = 10;
255                 break;
256         default:
257                 device_printf(dev, "slot %d, illegal irq setting.\n",
258                     eisa_get_slot(dev));
259                 return (ENXIO);
260         }
261
262         eisa_add_iospace(dev, (io_base + IDA_EISA_IOPORT_START),
263                          IDA_EISA_IOPORT_LEN, RESVADDR_NONE);
264
265         eisa_add_intr(dev, irq, EISA_TRIGGER_LEVEL);            /* XXX ??? */
266
267         return (0);
268 }
269
270 static int
271 ida_eisa_attach(device_t dev)
272 {
273         struct ida_softc        *ida;
274         struct ida_board        *board;
275         int                     error;
276         int                     rid;
277
278         ida = device_get_softc(dev);
279         ida->dev = dev;
280
281         board = ida_eisa_match(eisa_get_id(dev));
282         ida->cmd = *board->accessor;
283         ida->flags = board->flags;
284
285         ida->regs_res_type = SYS_RES_IOPORT;
286         ida->regs_res_id = 0;
287         ida->regs = bus_alloc_resource(dev, ida->regs_res_type,
288             &ida->regs_res_id, 0, ~0, 1, RF_ACTIVE);
289         if (ida->regs == NULL) {
290                 device_printf(dev, "can't allocate register resources\n");
291                 return (ENOMEM);
292         }
293
294         error = bus_dma_tag_create(
295                 /* parent       */      NULL,
296                 /* alignment    */      0,
297                 /* boundary     */      0,
298                 /* lowaddr      */      BUS_SPACE_MAXADDR_32BIT,
299                 /* highaddr     */      BUS_SPACE_MAXADDR,
300                 /* filter       */      NULL,
301                 /* filterarg    */      NULL,
302                 /* maxsize      */      MAXBSIZE,
303                 /* nsegments    */      IDA_NSEG,
304                 /* maxsegsize   */      BUS_SPACE_MAXSIZE_32BIT,
305                 /* flags        */      BUS_DMA_ALLOCNOW,
306                 &ida->parent_dmat);
307
308         if (error != 0) {
309                 device_printf(dev, "can't allocate DMA tag\n");
310                 ida_free(ida);
311                 return (ENOMEM);
312         }
313
314         rid = 0;
315         ida->irq_res_type = SYS_RES_IRQ;
316         ida->irq = bus_alloc_resource(dev, ida->irq_res_type, &rid,
317             0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
318         if (ida->irq == NULL) {
319                 ida_free(ida);
320                 return (ENOMEM);
321         }
322
323         error = bus_setup_intr(dev, ida->irq, INTR_TYPE_BIO,
324             ida_intr, ida, &ida->ih);
325         if (error) {
326                 device_printf(dev, "can't setup interrupt\n");
327                 ida_free(ida);
328                 return (ENOMEM);
329         }
330
331         error = ida_init(ida);
332         if (error) {
333                 ida_free(ida);
334                 return (error);
335         }
336
337         ida_attach(ida);
338         ida->flags |= IDA_ATTACHED;
339
340         return (0);
341 }
342
343 DRIVER_MODULE(ida, eisa, ida_eisa_driver, ida_devclass, 0, 0);