Initial import from FreeBSD RELENG_4:
[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  */
29
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/kernel.h>
33 #include <sys/bus.h>
34
35 #include <sys/buf.h>
36 #include <sys/devicestat.h>
37 #include <sys/disk.h>
38
39 #include <machine/bus_pio.h>
40 #include <machine/bus.h>
41 #include <machine/resource.h>
42 #include <sys/rman.h>
43
44 #include <dev/ida/idavar.h>
45 #include <dev/ida/idareg.h>
46
47 #include <dev/eisa/eisaconf.h>
48
49 #define IDA_EISA_IOPORT_START   0x0c88
50 #define IDA_EISA_IOPORT_LEN     0x0017
51
52 #define IDA_EISA_IRQ_REG        0x0cc0
53 #define IDA_EISA_IRQ_MASK       0xf0
54 #define IDA_EISA_IRQ_15         0x80
55 #define IDA_EISA_IRQ_14         0x40
56 #define IDA_EISA_IRQ_11         0x10
57 #define IDA_EISA_IRQ_10         0x20
58
59 static int
60 ida_v1_fifo_full(struct ida_softc *ida)
61 {
62         u_int8_t status;
63
64         status = ida_inb(ida, R_EISA_SYSTEM_DOORBELL);
65         return ((status & EISA_CHANNEL_CLEAR) == 0);
66 }
67
68 static void
69 ida_v1_submit(struct ida_softc *ida, struct ida_qcb *qcb)
70 {
71         u_int16_t size;
72
73         /*
74          * On these cards, this location is actually for control flags.
75          * Set them to zero and pass in structure size via an I/O port.
76          */
77         size = qcb->hwqcb->hdr.size << 2;
78         qcb->hwqcb->hdr.size = 0;
79
80         ida_outb(ida, R_EISA_SYSTEM_DOORBELL, EISA_CHANNEL_CLEAR);
81         ida_outl(ida, R_EISA_LIST_ADDR, qcb->hwqcb_busaddr);
82         ida_outw(ida, R_EISA_LIST_LEN, size);
83         ida_outb(ida, R_EISA_LOCAL_DOORBELL, EISA_CHANNEL_BUSY);
84 }
85
86 static bus_addr_t
87 ida_v1_done(struct ida_softc *ida)
88 {
89         struct ida_hardware_qcb *hwqcb;
90         bus_addr_t completed;
91         u_int8_t status;
92
93         if ((ida_inb(ida, R_EISA_SYSTEM_DOORBELL) & EISA_CHANNEL_BUSY) == 0)
94                 return (0);
95
96         ida_outb(ida, R_EISA_SYSTEM_DOORBELL, EISA_CHANNEL_BUSY);
97         completed = ida_inl(ida, R_EISA_COMPLETE_ADDR);
98         status = ida_inb(ida, R_EISA_LIST_STATUS);
99         ida_outb(ida, R_EISA_LOCAL_DOORBELL, EISA_CHANNEL_CLEAR);
100
101         if (completed != 0) {
102                 hwqcb = (struct ida_hardware_qcb *)
103                     ((bus_addr_t)ida->hwqcbs +
104                     ((completed & ~3) - ida->hwqcb_busaddr));
105                 hwqcb->req.error = status;
106         }
107
108         return (completed);
109 }
110
111 static int
112 ida_v1_int_pending(struct ida_softc *ida)
113 {
114         return (ida_inb(ida, R_EISA_SYSTEM_DOORBELL) & EISA_CHANNEL_BUSY);
115 }
116
117 static void
118 ida_v1_int_enable(struct ida_softc *ida, int enable)
119 {
120         if (enable) {
121                 ida_outb(ida, R_EISA_SYSTEM_DOORBELL, ~EISA_CHANNEL_CLEAR);
122                 ida_outb(ida, R_EISA_LOCAL_DOORBELL, EISA_CHANNEL_BUSY);
123                 ida_outb(ida, R_EISA_INT_MASK, INT_ENABLE);
124                 ida_outb(ida, R_EISA_SYSTEM_MASK, INT_ENABLE);
125                 ida->flags |= IDA_INTERRUPTS;
126         } else {
127                 ida_outb(ida, R_EISA_SYSTEM_MASK, INT_DISABLE);
128                 ida->flags &= ~IDA_INTERRUPTS;
129         }
130 }
131
132 static int
133 ida_v2_fifo_full(struct ida_softc *ida)
134 {
135         return (ida_inl(ida, R_CMD_FIFO) == 0);
136 }
137
138 static void
139 ida_v2_submit(struct ida_softc *ida, struct ida_qcb *qcb)
140 {
141         ida_outl(ida, R_CMD_FIFO, qcb->hwqcb_busaddr);
142 }
143
144 static bus_addr_t
145 ida_v2_done(struct ida_softc *ida)
146 {
147         return (ida_inl(ida, R_DONE_FIFO));
148 }
149
150 static int
151 ida_v2_int_pending(struct ida_softc *ida)
152 {
153         return (ida_inl(ida, R_INT_PENDING));
154 }
155
156 static void
157 ida_v2_int_enable(struct ida_softc *ida, int enable)
158 {
159         if (enable)
160                 ida->flags |= IDA_INTERRUPTS;
161         else
162                 ida->flags &= ~IDA_INTERRUPTS;
163         ida_outl(ida, R_INT_MASK, enable ? INT_ENABLE : INT_DISABLE);
164 }
165
166 static struct ida_access ida_v1_access = {
167         ida_v1_fifo_full,
168         ida_v1_submit,
169         ida_v1_done,
170         ida_v1_int_pending,
171         ida_v1_int_enable,
172 };
173
174 static struct ida_access ida_v2_access = {
175         ida_v2_fifo_full,
176         ida_v2_submit,
177         ida_v2_done,
178         ida_v2_int_pending,
179         ida_v2_int_enable,
180 };
181
182 static struct ida_board board_id[] = {
183         { 0x0e114001, "Compaq IDA controller",
184             &ida_v1_access, 0 },
185         { 0x0e114002, "Compaq IDA-2 controller",
186             &ida_v1_access, 0 },        
187         { 0x0e114010, "Compaq IAES controller",
188             &ida_v1_access, 0 },
189         { 0x0e114020, "Compaq SMART array controller",
190             &ida_v1_access, 0 },
191         { 0x0e114030, "Compaq SMART-2/E array controller",
192             &ida_v2_access, 0 },
193
194         { 0, "", 0, 0 }
195 };
196
197 static struct   ida_board *ida_eisa_match(eisa_id_t);
198 static int      ida_eisa_probe(device_t);
199 static int      ida_eisa_attach(device_t);
200
201 static device_method_t ida_eisa_methods[] = {
202         DEVMETHOD(device_probe,         ida_eisa_probe),
203         DEVMETHOD(device_attach,        ida_eisa_attach),
204         DEVMETHOD(device_detach,        ida_detach),
205
206         { 0, 0 }
207 };
208
209 static driver_t ida_eisa_driver = {
210         "ida",
211         ida_eisa_methods,
212         sizeof(struct ida_softc)
213 };
214
215 static devclass_t ida_devclass;
216
217 static struct ida_board *
218 ida_eisa_match(eisa_id_t id)
219 {
220         int i;
221
222         for (i = 0; board_id[i].board; i++)
223                 if (board_id[i].board == id)
224                         return (&board_id[i]);
225         return (NULL);
226 }
227
228 static int
229 ida_eisa_probe(device_t dev)
230 {
231         struct ida_board        *board;
232         u_int32_t               io_base;
233         u_int                   irq = 0;
234
235         board = ida_eisa_match(eisa_get_id(dev));
236         if (board == NULL)
237                 return (ENXIO);
238         device_set_desc(dev, board->desc);
239
240         io_base = (eisa_get_slot(dev) * EISA_SLOT_SIZE);
241
242         switch (IDA_EISA_IRQ_MASK & (inb(IDA_EISA_IRQ_REG + io_base))) {
243         case IDA_EISA_IRQ_15:
244                 irq = 15;
245                 break;
246         case IDA_EISA_IRQ_14:
247                 irq = 14;
248                 break;
249         case IDA_EISA_IRQ_11:
250                 irq = 11;
251                 break;
252         case IDA_EISA_IRQ_10:
253                 irq = 10;
254                 break;
255         default:
256                 device_printf(dev, "slot %d, illegal irq setting.\n",
257                     eisa_get_slot(dev));
258                 return (ENXIO);
259         }
260
261         eisa_add_iospace(dev, (io_base + IDA_EISA_IOPORT_START),
262                          IDA_EISA_IOPORT_LEN, RESVADDR_NONE);
263
264         eisa_add_intr(dev, irq, EISA_TRIGGER_LEVEL);            /* XXX ??? */
265
266         return (0);
267 }
268
269 static int
270 ida_eisa_attach(device_t dev)
271 {
272         struct ida_softc        *ida;
273         struct ida_board        *board;
274         int                     error;
275         int                     rid;
276
277         ida = device_get_softc(dev);
278         ida->dev = dev;
279
280         board = ida_eisa_match(eisa_get_id(dev));
281         ida->cmd = *board->accessor;
282         ida->flags = board->flags;
283
284         ida->regs_res_type = SYS_RES_IOPORT;
285         ida->regs_res_id = 0;
286         ida->regs = bus_alloc_resource(dev, ida->regs_res_type,
287             &ida->regs_res_id, 0, ~0, 1, RF_ACTIVE);
288         if (ida->regs == NULL) {
289                 device_printf(dev, "can't allocate register resources\n");
290                 return (ENOMEM);
291         }
292
293         error = bus_dma_tag_create(
294                 /* parent       */      NULL,
295                 /* alignment    */      0,
296                 /* boundary     */      0,
297                 /* lowaddr      */      BUS_SPACE_MAXADDR_32BIT,
298                 /* highaddr     */      BUS_SPACE_MAXADDR,
299                 /* filter       */      NULL,
300                 /* filterarg    */      NULL,
301                 /* maxsize      */      MAXBSIZE,
302                 /* nsegments    */      IDA_NSEG,
303                 /* maxsegsize   */      BUS_SPACE_MAXSIZE_32BIT,
304                 /* flags        */      BUS_DMA_ALLOCNOW,
305                 &ida->parent_dmat);
306
307         if (error != 0) {
308                 device_printf(dev, "can't allocate DMA tag\n");
309                 ida_free(ida);
310                 return (ENOMEM);
311         }
312
313         rid = 0;
314         ida->irq_res_type = SYS_RES_IRQ;
315         ida->irq = bus_alloc_resource(dev, ida->irq_res_type, &rid,
316             0, ~0, 1, RF_ACTIVE | RF_SHAREABLE);
317         if (ida->irq == NULL) {
318                 ida_free(ida);
319                 return (ENOMEM);
320         }
321
322         error = bus_setup_intr(dev, ida->irq, INTR_TYPE_BIO,
323             ida_intr, ida, &ida->ih);
324         if (error) {
325                 device_printf(dev, "can't setup interrupt\n");
326                 ida_free(ida);
327                 return (ENOMEM);
328         }
329
330         error = ida_init(ida);
331         if (error) {
332                 ida_free(ida);
333                 return (error);
334         }
335
336         ida_attach(ida);
337         ida->flags |= IDA_ATTACHED;
338
339         return (0);
340 }
341
342 DRIVER_MODULE(ida, eisa, ida_eisa_driver, ida_devclass, 0, 0);