]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/ata/ata-cbus.c
This commit was generated by cvs2svn to compensate for changes in r152390,
[FreeBSD/FreeBSD.git] / sys / dev / ata / ata-cbus.c
1 /*-
2  * Copyright (c) 2002 - 2005 Søren Schmidt <sos@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer,
10  *    without modification, immediately at the beginning of the file.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include "opt_ata.h"
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/ata.h>
37 #include <sys/bus.h>
38 #include <sys/malloc.h>
39 #include <sys/module.h>
40 #include <sys/conf.h>
41 #include <sys/sema.h>
42 #include <sys/taskqueue.h>
43 #include <vm/uma.h>
44 #include <machine/resource.h>
45 #include <machine/bus.h>
46 #include <sys/rman.h>
47 #include <isa/isavar.h>
48 #include <dev/ata/ata-all.h>
49 #include <ata_if.h>
50
51 /* local vars */
52 struct ata_cbus_controller {
53     struct resource *io;
54     struct resource *ctlio;
55     struct resource *bankio;
56     struct resource *irq;
57     void *ih;
58     struct mtx bank_mtx;
59     int locked_bank;
60     int restart_bank;
61     int hardware_bank;
62     struct {
63         void (*function)(void *);
64         void *argument;
65     } interrupt[2];
66 };
67
68 /* local prototypes */
69 static void ata_cbus_intr(void *);
70 static int ata_cbuschannel_banking(device_t dev, int flags);
71
72 static int
73 ata_cbus_probe(device_t dev)
74 {
75     struct resource *io;
76     int rid;
77     u_long tmp;
78
79     /* dont probe PnP devices */
80     if (isa_get_vendorid(dev))
81         return (ENXIO);
82
83     /* allocate the ioport range */
84     rid = ATA_IOADDR_RID;
85     if (!(io = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0,
86                                   ATA_PC98_IOSIZE, RF_ACTIVE)))
87         return ENOMEM;
88
89     /* calculate & set the altport range */
90     rid = ATA_PC98_CTLADDR_RID;
91     if (bus_get_resource(dev, SYS_RES_IOPORT, rid, &tmp, &tmp)) {
92         bus_set_resource(dev, SYS_RES_IOPORT, rid,
93                          rman_get_start(io)+ATA_PC98_CTLOFFSET, ATA_CTLIOSIZE);
94     }
95
96     /* calculate & set the bank range */
97     rid = ATA_PC98_BANKADDR_RID;
98     if (bus_get_resource(dev, SYS_RES_IOPORT, rid, &tmp, &tmp)) {
99         bus_set_resource(dev, SYS_RES_IOPORT, rid,
100                          ATA_PC98_BANK, ATA_PC98_BANKIOSIZE);
101     }
102
103     bus_release_resource(dev, SYS_RES_IOPORT, ATA_IOADDR_RID, io);
104     return 0;
105 }
106
107 static int
108 ata_cbus_attach(device_t dev)
109 {
110     struct ata_cbus_controller *ctlr = device_get_softc(dev);
111     int rid;
112
113     /* allocate resources */
114     rid = ATA_IOADDR_RID;
115     if (!(ctlr->io = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0,
116                                         ATA_PC98_IOSIZE, RF_ACTIVE)))
117        return ENOMEM;
118
119     rid = ATA_PC98_CTLADDR_RID;
120     if (!(ctlr->ctlio = 
121           bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
122                              rman_get_start(ctlr->io) + ATA_PC98_CTLOFFSET, ~0,
123                              ATA_CTLIOSIZE, RF_ACTIVE))) {
124         bus_release_resource(dev, SYS_RES_IOPORT, ATA_IOADDR_RID, ctlr->io);
125         return ENOMEM;
126     }
127
128     rid = ATA_PC98_BANKADDR_RID;
129     if (!(ctlr->bankio = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
130                                             ATA_PC98_BANK, ~0,
131                                             ATA_PC98_BANKIOSIZE, RF_ACTIVE))) {
132         bus_release_resource(dev, SYS_RES_IOPORT, ATA_IOADDR_RID, ctlr->io);
133         bus_release_resource(dev, SYS_RES_IOPORT, ATA_CTLADDR_RID, ctlr->ctlio);
134         return ENOMEM;
135     }
136
137     rid = ATA_IRQ_RID;
138     if (!(ctlr->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
139                                              RF_ACTIVE | RF_SHAREABLE))) {
140         device_printf(dev, "unable to alloc interrupt\n");
141         bus_release_resource(dev, SYS_RES_IOPORT, ATA_IOADDR_RID, ctlr->io);
142         bus_release_resource(dev, SYS_RES_IOPORT, ATA_CTLADDR_RID, ctlr->ctlio);
143         bus_release_resource(dev, SYS_RES_IOPORT, 
144                              ATA_PC98_BANKADDR_RID, ctlr->bankio);
145         return ENXIO;
146     }
147
148     if ((bus_setup_intr(dev, ctlr->irq, ATA_INTR_FLAGS,
149                         ata_cbus_intr, ctlr, &ctlr->ih))) {
150         device_printf(dev, "unable to setup interrupt\n");
151         bus_release_resource(dev, SYS_RES_IOPORT, ATA_IOADDR_RID, ctlr->io);
152         bus_release_resource(dev, SYS_RES_IOPORT, ATA_CTLADDR_RID, ctlr->ctlio);
153         bus_release_resource(dev, SYS_RES_IOPORT, 
154                              ATA_PC98_BANKADDR_RID, ctlr->bankio);
155         bus_release_resource(dev, SYS_RES_IOPORT, ATA_IRQ_RID, ctlr->irq);
156         return ENXIO;
157     }
158
159     mtx_init(&ctlr->bank_mtx, "ATA cbus bank lock", NULL, MTX_DEF);
160     ctlr->hardware_bank = -1;
161     ctlr->locked_bank = -1;
162     ctlr->restart_bank = -1;
163
164     if (!device_add_child(dev, "ata", 0))
165         return ENOMEM;
166     if (!device_add_child(dev, "ata", 1))
167         return ENOMEM;
168
169     return bus_generic_attach(dev);
170 }
171
172 static struct resource *
173 ata_cbus_alloc_resource(device_t dev, device_t child, int type, int *rid,
174                         u_long start, u_long end, u_long count, u_int flags)
175 {
176     struct ata_cbus_controller *ctlr = device_get_softc(dev);
177
178     if (type == SYS_RES_IOPORT) {
179         switch (*rid) {
180         case ATA_IOADDR_RID:
181             return ctlr->io;
182         case ATA_CTLADDR_RID:
183             return ctlr->ctlio;
184         }
185     }
186     if (type == SYS_RES_IRQ)
187         return ctlr->irq;
188     return 0;
189 }
190
191 static int
192 ata_cbus_setup_intr(device_t dev, device_t child, struct resource *irq,
193                int flags, driver_intr_t *intr, void *arg,
194                void **cookiep)
195 {
196     struct ata_cbus_controller *controller = device_get_softc(dev);
197     int unit = ((struct ata_channel *)device_get_softc(child))->unit;
198
199     controller->interrupt[unit].function = intr;
200     controller->interrupt[unit].argument = arg;
201     *cookiep = controller;
202
203     return 0;
204 }
205
206 static int
207 ata_cbus_print_child(device_t dev, device_t child)
208 {
209     struct ata_channel *ch = device_get_softc(child);
210     int retval = 0;
211
212     retval += bus_print_child_header(dev, child);
213     retval += printf(" at bank %d", ch->unit);
214     retval += bus_print_child_footer(dev, child);
215     return retval;
216 }
217
218 static void
219 ata_cbus_intr(void *data)
220 {  
221     struct ata_cbus_controller *ctlr = data;
222     struct ata_channel *ch;
223     int unit;
224
225     for (unit = 0; unit < 2; unit++) {
226         if (!(ch = ctlr->interrupt[unit].argument))
227             continue;
228         if (ata_cbuschannel_banking(ch->dev, ATA_LF_WHICH) == unit)
229             ctlr->interrupt[unit].function(ch);
230     }
231 }
232
233 static device_method_t ata_cbus_methods[] = {
234     /* device interface */
235     DEVMETHOD(device_probe,             ata_cbus_probe),
236     DEVMETHOD(device_attach,            ata_cbus_attach),
237 //  DEVMETHOD(device_detach,            ata_cbus_detach),
238
239     /* bus methods */
240     DEVMETHOD(bus_alloc_resource,       ata_cbus_alloc_resource),
241     DEVMETHOD(bus_setup_intr,           ata_cbus_setup_intr),
242     DEVMETHOD(bus_print_child,          ata_cbus_print_child),
243
244     { 0, 0 }
245 };
246
247 static driver_t ata_cbus_driver = {
248     "atacbus",
249     ata_cbus_methods,
250     sizeof(struct ata_cbus_controller),
251 };
252
253 static devclass_t ata_cbus_devclass;
254
255 DRIVER_MODULE(atacbus, isa, ata_cbus_driver, ata_cbus_devclass, 0, 0);
256
257 static int
258 ata_cbuschannel_probe(device_t dev)
259 {
260     struct ata_cbus_controller *ctlr = device_get_softc(device_get_parent(dev));
261     struct ata_channel *ch = device_get_softc(dev);
262     device_t *children;
263     int count, i;
264
265     /* find channel number on this controller */
266     device_get_children(device_get_parent(dev), &children, &count);
267     for (i = 0; i < count; i++) {
268         if (children[i] == dev) 
269             ch->unit = i;
270     }
271     free(children, M_TEMP);
272
273     /* setup the resource vectors */
274     for (i = ATA_DATA; i <= ATA_COMMAND; i ++) {
275         ch->r_io[i].res = ctlr->io;
276         ch->r_io[i].offset = i << 1;
277     }
278     ch->r_io[ATA_CONTROL].res = ctlr->ctlio;
279     ch->r_io[ATA_CONTROL].offset = 0;
280     ch->r_io[ATA_IDX_ADDR].res = ctlr->io;
281     ata_default_registers(dev);
282
283     /* initialize softc for this channel */
284     ch->flags |= ATA_USE_16BIT;
285     ata_generic_hw(dev);
286     return ata_probe(dev);
287 }
288
289 static int
290 ata_cbuschannel_banking(device_t dev, int flags)
291 {
292     struct ata_cbus_controller *ctlr = device_get_softc(device_get_parent(dev));
293     struct ata_channel *ch = device_get_softc(dev);
294     int res;
295
296     mtx_lock(&ctlr->bank_mtx);
297     switch (flags) {
298     case ATA_LF_LOCK:
299         if (ctlr->locked_bank == -1)
300             ctlr->locked_bank = ch->unit;
301         if (ctlr->locked_bank == ch->unit) {
302             ctlr->hardware_bank = ch->unit;
303             ATA_OUTB(ctlr->bankio, 0, ch->unit);
304         }
305         else
306             ctlr->restart_bank = ch->unit;
307         break;
308
309     case ATA_LF_UNLOCK:
310         if (ctlr->locked_bank == ch->unit) {
311             ctlr->locked_bank = -1;
312             if (ctlr->restart_bank != -1) {
313                 if ((ch = ctlr->interrupt[ctlr->restart_bank].argument)) {
314                     ctlr->restart_bank = -1;
315                     mtx_unlock(&ctlr->bank_mtx);
316                     ata_start(ch->dev);
317                     return -1;
318                 }
319             }
320         }
321         break;
322
323     case ATA_LF_WHICH:
324         break;
325     }
326     res = ctlr->locked_bank;
327     mtx_unlock(&ctlr->bank_mtx);
328     return res;
329 }
330
331 static device_method_t ata_cbuschannel_methods[] = {
332     /* device interface */
333     DEVMETHOD(device_probe,     ata_cbuschannel_probe),
334     DEVMETHOD(device_attach,    ata_attach),
335     DEVMETHOD(device_detach,    ata_detach),
336     DEVMETHOD(device_suspend,   ata_suspend),
337     DEVMETHOD(device_resume,    ata_resume),
338
339     /* ATA methods */
340     DEVMETHOD(ata_locking,      ata_cbuschannel_banking),
341     { 0, 0 }
342 };
343
344 static driver_t ata_cbuschannel_driver = {
345     "ata",
346     ata_cbuschannel_methods,
347     sizeof(struct ata_channel),
348 };
349
350 DRIVER_MODULE(ata, atacbus, ata_cbuschannel_driver, ata_devclass, 0, 0);
351 MODULE_DEPEND(ata, ata, 1, 1, 1);