]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/arm/xscale/ixp425/avila_ata.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / sys / arm / xscale / ixp425 / avila_ata.c
1 /*-
2  * Copyright (c) 2006 Sam Leffler, Errno Consulting
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.
11  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13  *    redistribution must be conditioned upon including a substantially
14  *    similar Disclaimer requirement for further binary redistribution.
15  *
16  * NO WARRANTY
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27  * THE POSSIBILITY OF SUCH DAMAGES.
28  */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 /*
34  * Compact Flash Support for the Avila Gateworks XScale boards.
35  * The CF slot is operated in "True IDE" mode. Registers are on
36  * the Expansion Bus connected to CS1 and CS2. Interrupts are
37  * tied to GPIO pin 12.  No DMA, just PIO.
38  *
39  * The ADI Pronghorn Metro is very similar. It use CS3 and CS4 and
40  * GPIO pin 0 for interrupts.
41  *
42  * See also http://www.intel.com/design/network/applnots/302456.htm.
43  */
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/kernel.h>
47 #include <sys/module.h>
48 #include <sys/time.h>
49 #include <sys/bus.h>
50 #include <sys/resource.h>
51 #include <sys/rman.h>
52 #include <sys/sysctl.h>
53 #include <sys/endian.h>
54
55 #include <machine/bus.h>
56 #include <machine/cpu.h>
57 #include <machine/cpufunc.h>
58 #include <machine/resource.h>
59 #include <machine/intr.h>
60 #include <arm/xscale/ixp425/ixp425reg.h>
61 #include <arm/xscale/ixp425/ixp425var.h>
62
63 #include <sys/ata.h>
64 #include <sys/sema.h>
65 #include <sys/taskqueue.h>
66 #include <vm/uma.h>
67 #include <dev/ata/ata-all.h>
68 #include <ata_if.h>
69
70 #define AVILA_IDE_GPIN          12              /* GPIO pin # */
71 #define AVILA_IDE_IRQ           IXP425_INT_GPIO_12
72 #define AVILA_IDE_CTRL          0x06            /* control register */
73
74 #define PRONGHORN_IDE_GPIN      0       /* GPIO pin # */
75 #define PRONGHORN_IDE_IRQ       IXP425_INT_GPIO_0
76 #define PRONGHORN_IDE_CNTRL     0x06    /* control register */
77
78 struct ata_avila_softc {
79         device_t                sc_dev;
80         bus_space_tag_t         sc_iot;
81         bus_space_handle_t      sc_exp_ioh;     /* Exp Bus config registers */
82         bus_space_handle_t      sc_ioh;         /* CS1/3 data registers */
83         bus_space_handle_t      sc_alt_ioh;     /* CS2/4 data registers */
84         struct bus_space        sc_expbus_tag;
85         struct resource         sc_ata;         /* hand-crafted for ATA */
86         struct resource         sc_alt_ata;     /* hand-crafted for ATA */
87         u_int32_t               sc_16bit_off;   /* EXP_TIMING_CSx_OFFSET */
88         int                     sc_rid;         /* rid for IRQ */
89         struct resource         *sc_irq;        /* IRQ resource */
90         void                    *sc_ih;         /* interrupt handler */
91         struct {
92                 void    (*cb)(void *);
93                 void    *arg;
94         } sc_intr[1];                   /* NB: 1/channel */
95 };
96
97 static void ata_avila_intr(void *);
98 bs_protos(ata);
99 static  void ata_bs_rm_2_s(void *, bus_space_handle_t, bus_size_t,
100                 u_int16_t *, bus_size_t);
101 static  void ata_bs_wm_2_s(void *, bus_space_handle_t, bus_size_t,
102                 const u_int16_t *, bus_size_t);
103
104 static int
105 ata_avila_probe(device_t dev)
106 {
107         struct ixp425_softc *sa = device_get_softc(device_get_parent(dev));
108
109         /* XXX any way to check? */
110         if (EXP_BUS_READ_4(sa, EXP_TIMING_CS2_OFFSET) != 0)
111                 device_set_desc_copy(dev, "Gateworks Avila IDE/CF Controller");
112         else
113                 device_set_desc_copy(dev,
114                     "ADI Pronghorn Metro IDE/CF Controller");
115         return 0;
116 }
117
118 static int
119 ata_avila_attach(device_t dev)
120 {
121         struct ata_avila_softc *sc = device_get_softc(dev);
122         struct ixp425_softc *sa = device_get_softc(device_get_parent(dev));
123         u_int32_t alt_t_off, ide_gpin, ide_irq;
124
125         sc->sc_dev = dev;
126         /* NB: borrow from parent */
127         sc->sc_iot = sa->sc_iot;
128         sc->sc_exp_ioh = sa->sc_exp_ioh;
129         if (EXP_BUS_READ_4(sc, EXP_TIMING_CS2_OFFSET) != 0) {
130                 /* Avila board */
131                 if (bus_space_map(sc->sc_iot, IXP425_EXP_BUS_CS1_HWBASE,
132                     IXP425_EXP_BUS_CS1_SIZE, 0, &sc->sc_ioh))
133                         panic("%s: unable to map Expansion Bus CS1 window",
134                             __func__);
135                 if (bus_space_map(sc->sc_iot, IXP425_EXP_BUS_CS2_HWBASE,
136                     IXP425_EXP_BUS_CS2_SIZE, 0, &sc->sc_alt_ioh))
137                         panic("%s: unable to map Expansion Bus CS2 window",
138                             __func__);
139                 ide_gpin = AVILA_IDE_GPIN;
140                 ide_irq = AVILA_IDE_IRQ;
141                 sc->sc_16bit_off = EXP_TIMING_CS1_OFFSET;
142                 alt_t_off = EXP_TIMING_CS2_OFFSET;
143         } else {
144                 /* Pronghorn */
145                 if (bus_space_map(sc->sc_iot, IXP425_EXP_BUS_CS3_HWBASE,
146                     IXP425_EXP_BUS_CS3_SIZE, 0, &sc->sc_ioh))
147                         panic("%s: unable to map Expansion Bus CS3 window",
148                             __func__);
149                 if (bus_space_map(sc->sc_iot, IXP425_EXP_BUS_CS4_HWBASE,
150                     IXP425_EXP_BUS_CS4_SIZE, 0, &sc->sc_alt_ioh))
151                         panic("%s: unable to map Expansion Bus CS4 window",
152                             __func__);
153                 ide_gpin = PRONGHORN_IDE_GPIN;
154                 ide_irq = PRONGHORN_IDE_IRQ;
155                 sc->sc_16bit_off = EXP_TIMING_CS3_OFFSET;
156                 alt_t_off = EXP_TIMING_CS4_OFFSET;
157         }
158
159         /*
160          * Craft special resource for ATA bus space ops
161          * that go through the expansion bus and require
162          * special hackery to ena/dis 16-bit operations.
163          *
164          * XXX probably should just make this generic for
165          * accessing the expansion bus.
166          */
167         sc->sc_expbus_tag.bs_cookie = sc;       /* NB: backpointer */
168         /* read single */
169         sc->sc_expbus_tag.bs_r_1        = ata_bs_r_1,
170         sc->sc_expbus_tag.bs_r_2        = ata_bs_r_2,
171         /* read multiple */
172         sc->sc_expbus_tag.bs_rm_2       = ata_bs_rm_2,
173         sc->sc_expbus_tag.bs_rm_2_s     = ata_bs_rm_2_s,
174         /* write (single) */
175         sc->sc_expbus_tag.bs_w_1        = ata_bs_w_1,
176         sc->sc_expbus_tag.bs_w_2        = ata_bs_w_2,
177         /* write multiple */
178         sc->sc_expbus_tag.bs_wm_2       = ata_bs_wm_2,
179         sc->sc_expbus_tag.bs_wm_2_s     = ata_bs_wm_2_s,
180
181         rman_set_bustag(&sc->sc_ata, &sc->sc_expbus_tag);
182         rman_set_bushandle(&sc->sc_ata, sc->sc_ioh);
183         rman_set_bustag(&sc->sc_alt_ata, &sc->sc_expbus_tag);
184         rman_set_bushandle(&sc->sc_alt_ata, sc->sc_alt_ioh);
185
186         GPIO_CONF_WRITE_4(sa, IXP425_GPIO_GPOER, 
187             GPIO_CONF_READ_4(sa, IXP425_GPIO_GPOER) | (1<<ide_gpin));
188         /* set interrupt type */
189         GPIO_CONF_WRITE_4(sa, GPIO_TYPE_REG(ide_gpin),
190             (GPIO_CONF_READ_4(sa, GPIO_TYPE_REG(ide_gpin)) &~
191              GPIO_TYPE(ide_gpin, GPIO_TYPE_MASK)) |
192              GPIO_TYPE(ide_gpin, GPIO_TYPE_EDG_RISING));
193
194         /* clear ISR */
195         GPIO_CONF_WRITE_4(sa, IXP425_GPIO_GPISR, (1<<ide_gpin));
196
197         /* configure CS1/3 window, leaving timing unchanged */
198         EXP_BUS_WRITE_4(sc, sc->sc_16bit_off,
199             EXP_BUS_READ_4(sc, sc->sc_16bit_off) |
200                 EXP_BYTE_EN | EXP_WR_EN | EXP_BYTE_RD16 | EXP_CS_EN);
201         /* configure CS2/4 window, leaving timing unchanged */
202         EXP_BUS_WRITE_4(sc, alt_t_off,
203             EXP_BUS_READ_4(sc, alt_t_off) |
204                 EXP_BYTE_EN | EXP_WR_EN | EXP_BYTE_RD16 | EXP_CS_EN);
205
206         /* setup interrupt */
207         sc->sc_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->sc_rid,
208             ide_irq, ide_irq, 1, RF_ACTIVE);
209         if (!sc->sc_irq)
210                 panic("Unable to allocate irq %u.\n", ide_irq);
211         bus_setup_intr(dev, sc->sc_irq,
212             INTR_TYPE_BIO | INTR_MPSAFE | INTR_ENTROPY,
213             NULL, ata_avila_intr, sc, &sc->sc_ih);
214
215         /* attach channel on this controller */
216         device_add_child(dev, "ata", devclass_find_free_unit(ata_devclass, 0));
217         bus_generic_attach(dev);
218
219         return 0;
220 }
221
222 static int
223 ata_avila_detach(device_t dev)
224 {
225         struct ata_avila_softc *sc = device_get_softc(dev);
226         device_t *children;
227         int nc;
228
229         /* XXX quiesce gpio? */
230
231         /* detach & delete all children */
232         if (device_get_children(dev, &children, &nc) == 0) {
233             if (nc > 0)
234                     device_delete_child(dev, children[0]);
235             free(children, M_TEMP);
236         }
237
238         bus_teardown_intr(dev, sc->sc_irq, sc->sc_ih);
239         bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid, sc->sc_irq);
240
241         return 0;
242 }
243
244 static void
245 ata_avila_intr(void *xsc)
246 {
247         struct ata_avila_softc *sc = xsc;
248
249         if (sc->sc_intr[0].cb != NULL)
250                 sc->sc_intr[0].cb(sc->sc_intr[0].arg);
251 }
252
253 static struct resource *
254 ata_avila_alloc_resource(device_t dev, device_t child, int type, int *rid,
255                        u_long start, u_long end, u_long count, u_int flags)
256 {
257         struct ata_avila_softc *sc = device_get_softc(dev);
258
259         KASSERT(type == SYS_RES_IRQ && *rid == ATA_IRQ_RID,
260             ("type %u rid %u start %lu end %lu count %lu flags %u",
261              type, *rid, start, end, count, flags));
262
263         /* doesn't matter what we return so reuse the real thing */
264         return sc->sc_irq;
265 }
266
267 static int
268 ata_avila_release_resource(device_t dev, device_t child, int type, int rid,
269                          struct resource *r)
270 {
271         KASSERT(type == SYS_RES_IRQ && rid == ATA_IRQ_RID,
272             ("type %u rid %u", type, rid));
273         return 0;
274 }
275
276 static int
277 ata_avila_setup_intr(device_t dev, device_t child, struct resource *irq, 
278                    int flags, driver_filter_t *filt,
279                    driver_intr_t *function, void *argument, void **cookiep)
280 {
281         struct ata_avila_softc *sc = device_get_softc(dev);
282         int unit = ((struct ata_channel *)device_get_softc(child))->unit;
283
284         KASSERT(unit == 0, ("unit %d", unit));
285         sc->sc_intr[unit].cb = function;
286         sc->sc_intr[unit].arg = argument;
287         *cookiep = sc;
288         return 0;
289 }
290
291 static int
292 ata_avila_teardown_intr(device_t dev, device_t child, struct resource *irq,
293                       void *cookie)
294 {
295         struct ata_avila_softc *sc = device_get_softc(dev);
296         int unit = ((struct ata_channel *)device_get_softc(child))->unit;
297
298         KASSERT(unit == 0, ("unit %d", unit));
299         sc->sc_intr[unit].cb = NULL;
300         sc->sc_intr[unit].arg = NULL;
301         return 0;
302 }
303
304 /*
305  * Bus space accessors for CF-IDE PIO operations.
306  */
307
308 /*
309  * Enable/disable 16-bit ops on the expansion bus.
310  */
311 static void __inline
312 enable_16(struct ata_avila_softc *sc)
313 {
314         EXP_BUS_WRITE_4(sc, sc->sc_16bit_off,
315             EXP_BUS_READ_4(sc, sc->sc_16bit_off) &~ EXP_BYTE_EN);
316         DELAY(100);             /* XXX? */
317 }
318
319 static void __inline
320 disable_16(struct ata_avila_softc *sc)
321 {
322         DELAY(100);             /* XXX? */
323         EXP_BUS_WRITE_4(sc, sc->sc_16bit_off,
324             EXP_BUS_READ_4(sc, sc->sc_16bit_off) | EXP_BYTE_EN);
325 }
326
327 uint8_t
328 ata_bs_r_1(void *t, bus_space_handle_t h, bus_size_t o)
329 {
330         struct ata_avila_softc *sc = t;
331
332         return bus_space_read_1(sc->sc_iot, h, o);
333 }
334
335 void
336 ata_bs_w_1(void *t, bus_space_handle_t h, bus_size_t o, u_int8_t v)
337 {
338         struct ata_avila_softc *sc = t;
339
340         bus_space_write_1(sc->sc_iot, h, o, v);
341 }
342
343 uint16_t
344 ata_bs_r_2(void *t, bus_space_handle_t h, bus_size_t o)
345 {
346         struct ata_avila_softc *sc = t;
347         uint16_t v;
348
349         enable_16(sc);
350         v = bus_space_read_2(sc->sc_iot, h, o);
351         disable_16(sc);
352         return v;
353 }
354
355 void
356 ata_bs_w_2(void *t, bus_space_handle_t h, bus_size_t o, uint16_t v)
357 {
358         struct ata_avila_softc *sc = t;
359
360         enable_16(sc);
361         bus_space_write_2(sc->sc_iot, h, o, v);
362         disable_16(sc);
363 }
364
365 void
366 ata_bs_rm_2(void *t, bus_space_handle_t h, bus_size_t o,
367         u_int16_t *d, bus_size_t c)
368 {
369         struct ata_avila_softc *sc = t;
370
371         enable_16(sc);
372         bus_space_read_multi_2(sc->sc_iot, h, o, d, c);
373         disable_16(sc);
374 }
375
376 void
377 ata_bs_wm_2(void *t, bus_space_handle_t h, bus_size_t o,
378         const u_int16_t *d, bus_size_t c)
379 {
380         struct ata_avila_softc *sc = t;
381
382         enable_16(sc);
383         bus_space_write_multi_2(sc->sc_iot, h, o, d, c);
384         disable_16(sc);
385 }
386
387 /* XXX workaround ata driver by (incorrectly) byte swapping stream cases */
388
389 void
390 ata_bs_rm_2_s(void *t, bus_space_handle_t h, bus_size_t o,
391         u_int16_t *d, bus_size_t c)
392 {
393         struct ata_avila_softc *sc = t;
394         uint16_t v;
395         bus_size_t i;
396
397         enable_16(sc);
398 #if 1
399         for (i = 0; i < c; i++) {
400                 v = bus_space_read_2(sc->sc_iot, h, o);
401                 d[i] = bswap16(v);
402         }
403 #else
404         bus_space_read_multi_stream_2(sc->sc_iot, h, o, d, c);
405 #endif
406         disable_16(sc);
407 }
408
409 void
410 ata_bs_wm_2_s(void *t, bus_space_handle_t h, bus_size_t o,
411         const u_int16_t *d, bus_size_t c)
412 {
413         struct ata_avila_softc *sc = t;
414         bus_size_t i;
415
416         enable_16(sc);
417 #if 1
418         for (i = 0; i < c; i++)
419                 bus_space_write_2(sc->sc_iot, h, o, bswap16(d[i]));
420 #else
421         bus_space_write_multi_stream_2(sc->sc_iot, h, o, d, c);
422 #endif
423         disable_16(sc);
424 }
425
426 static device_method_t ata_avila_methods[] = {
427         /* device interface */
428         DEVMETHOD(device_probe,             ata_avila_probe),
429         DEVMETHOD(device_attach,            ata_avila_attach),
430         DEVMETHOD(device_detach,            ata_avila_detach),
431         DEVMETHOD(device_shutdown,          bus_generic_shutdown),
432         DEVMETHOD(device_suspend,           bus_generic_suspend),
433         DEVMETHOD(device_resume,            bus_generic_resume),
434
435         /* bus methods */
436         DEVMETHOD(bus_alloc_resource,       ata_avila_alloc_resource),
437         DEVMETHOD(bus_release_resource,     ata_avila_release_resource),
438         DEVMETHOD(bus_activate_resource,    bus_generic_activate_resource),
439         DEVMETHOD(bus_deactivate_resource,  bus_generic_deactivate_resource),
440         DEVMETHOD(bus_setup_intr,           ata_avila_setup_intr),
441         DEVMETHOD(bus_teardown_intr,        ata_avila_teardown_intr),
442
443         { 0, 0 }
444 };
445
446 devclass_t ata_avila_devclass;
447
448 static driver_t ata_avila_driver = {
449         "ata_avila",
450         ata_avila_methods,
451         sizeof(struct ata_avila_softc),
452 };
453
454 DRIVER_MODULE(ata_avila, ixp, ata_avila_driver, ata_avila_devclass, 0, 0);
455 MODULE_VERSION(ata_avila, 1);
456 MODULE_DEPEND(ata_avila, ata, 1, 1, 1);
457
458 static int
459 avila_channel_probe(device_t dev)
460 {
461         struct ata_channel *ch = device_get_softc(dev);
462
463         ch->unit = 0;
464         ch->flags |= ATA_USE_16BIT | ATA_NO_SLAVE;
465         device_set_desc_copy(dev, "ATA channel 0");
466
467         return ata_probe(dev);
468 }
469
470 static int
471 avila_channel_attach(device_t dev)
472 {
473         struct ata_avila_softc *sc = device_get_softc(device_get_parent(dev));
474         struct ata_channel *ch = device_get_softc(dev);
475         int i;
476
477         for (i = 0; i < ATA_MAX_RES; i++)
478                 ch->r_io[i].res = &sc->sc_ata;
479
480         ch->r_io[ATA_DATA].offset = ATA_DATA;
481         ch->r_io[ATA_FEATURE].offset = ATA_FEATURE;
482         ch->r_io[ATA_COUNT].offset = ATA_COUNT;
483         ch->r_io[ATA_SECTOR].offset = ATA_SECTOR;
484         ch->r_io[ATA_CYL_LSB].offset = ATA_CYL_LSB;
485         ch->r_io[ATA_CYL_MSB].offset = ATA_CYL_MSB;
486         ch->r_io[ATA_DRIVE].offset = ATA_DRIVE;
487         ch->r_io[ATA_COMMAND].offset = ATA_COMMAND;
488         ch->r_io[ATA_ERROR].offset = ATA_FEATURE;
489         /* NB: should be used only for ATAPI devices */
490         ch->r_io[ATA_IREASON].offset = ATA_COUNT;
491         ch->r_io[ATA_STATUS].offset = ATA_COMMAND;
492
493         /* NB: the control and alt status registers are special */
494         ch->r_io[ATA_ALTSTAT].res = &sc->sc_alt_ata;
495         ch->r_io[ATA_ALTSTAT].offset = AVILA_IDE_CTRL;
496         ch->r_io[ATA_CONTROL].res = &sc->sc_alt_ata;
497         ch->r_io[ATA_CONTROL].offset = AVILA_IDE_CTRL;
498
499         /* NB: by convention this points at the base of registers */
500         ch->r_io[ATA_IDX_ADDR].offset = 0;
501
502         ata_generic_hw(dev);
503         return ata_attach(dev);
504 }
505
506 static device_method_t avila_channel_methods[] = {
507         /* device interface */
508         DEVMETHOD(device_probe,     avila_channel_probe),
509         DEVMETHOD(device_attach,    avila_channel_attach),
510         DEVMETHOD(device_detach,    ata_detach),
511         DEVMETHOD(device_shutdown,  bus_generic_shutdown),
512         DEVMETHOD(device_suspend,   ata_suspend),
513         DEVMETHOD(device_resume,    ata_resume),
514
515         { 0, 0 }
516 };
517
518 driver_t avila_channel_driver = {
519         "ata",
520         avila_channel_methods,
521         sizeof(struct ata_channel),
522 };
523 DRIVER_MODULE(ata, ata_avila, avila_channel_driver, ata_devclass, 0, 0);