]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/arm/xscale/ixp425/avila_ata.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.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_CTRL  0x06
71
72 struct ata_config {
73         const char      *desc;          /* description for probe */
74         uint8_t         gpin;           /* GPIO pin */
75         uint8_t         irq;            /* IRQ */
76         uint32_t        base16;         /* CS base addr for 16-bit */
77         uint32_t        size16;         /* CS size for 16-bit */
78         uint32_t        off16;          /* CS offset for 16-bit */
79         uint32_t        basealt;        /* CS base addr for alt */
80         uint32_t        sizealt;        /* CS size for alt */
81         uint32_t        offalt;         /* CS offset for alt */
82 };
83
84 static const struct ata_config *
85 ata_getconfig(struct ixp425_softc *sa)
86 {
87         static const struct ata_config configs[] = {
88                 { .desc         = "Gateworks Avila IDE/CF Controller",
89                   .gpin         = 12,
90                   .irq          = IXP425_INT_GPIO_12,
91                   .base16       = IXP425_EXP_BUS_CS1_HWBASE,
92                   .size16       = IXP425_EXP_BUS_CS1_SIZE,
93                   .off16        = EXP_TIMING_CS1_OFFSET,
94                   .basealt      = IXP425_EXP_BUS_CS2_HWBASE,
95                   .sizealt      = IXP425_EXP_BUS_CS2_SIZE,
96                   .offalt       = EXP_TIMING_CS2_OFFSET,
97                 },
98                 { .desc         = "Gateworks Cambria IDE/CF Controller",
99                   .gpin         = 12,
100                   .irq          = IXP425_INT_GPIO_12,
101                   .base16       = CAMBRIA_CFSEL0_HWBASE,
102                   .size16       = CAMBRIA_CFSEL0_SIZE,
103                   .off16        = EXP_TIMING_CS3_OFFSET,
104                   .basealt      = CAMBRIA_CFSEL1_HWBASE,
105                   .sizealt      = CAMBRIA_CFSEL1_SIZE,
106                   .offalt       = EXP_TIMING_CS4_OFFSET,
107                 },
108                 { .desc         = "ADI Pronghorn Metro IDE/CF Controller",
109                   .gpin         = 0,
110                   .irq          = IXP425_INT_GPIO_0,
111                   .base16       = IXP425_EXP_BUS_CS3_HWBASE,
112                   .size16       = IXP425_EXP_BUS_CS3_SIZE,
113                   .off16        = EXP_TIMING_CS3_OFFSET,
114                   .basealt      = IXP425_EXP_BUS_CS4_HWBASE,
115                   .sizealt      = IXP425_EXP_BUS_CS4_SIZE,
116                   .offalt       = EXP_TIMING_CS4_OFFSET,
117                 },
118         };
119
120         /* XXX honor hint? (but then no multi-board support) */
121         /* XXX total hack */
122         if (cpu_is_ixp43x())
123                 return &configs[1];             /* Cambria */
124         if (EXP_BUS_READ_4(sa, EXP_TIMING_CS2_OFFSET) != 0)
125                 return &configs[0];             /* Avila */
126         return &configs[2];                     /* Pronghorn */
127 }
128
129 struct ata_avila_softc {
130         device_t                sc_dev;
131         bus_space_tag_t         sc_iot;
132         bus_space_handle_t      sc_exp_ioh;     /* Exp Bus config registers */
133         bus_space_handle_t      sc_ioh;         /* CS1/3 data registers */
134         bus_space_handle_t      sc_alt_ioh;     /* CS2/4 data registers */
135         struct bus_space        sc_expbus_tag;
136         struct resource         sc_ata;         /* hand-crafted for ATA */
137         struct resource         sc_alt_ata;     /* hand-crafted for ATA */
138         u_int32_t               sc_16bit_off;   /* EXP_TIMING_CSx_OFFSET */
139         int                     sc_rid;         /* rid for IRQ */
140         struct resource         *sc_irq;        /* IRQ resource */
141         void                    *sc_ih;         /* interrupt handler */
142         struct {
143                 void    (*cb)(void *);
144                 void    *arg;
145         } sc_intr[1];                   /* NB: 1/channel */
146 };
147
148 static void ata_avila_intr(void *);
149 bs_protos(ata);
150 static  void ata_bs_rm_2_s(void *, bus_space_handle_t, bus_size_t,
151                 u_int16_t *, bus_size_t);
152 static  void ata_bs_wm_2_s(void *, bus_space_handle_t, bus_size_t,
153                 const u_int16_t *, bus_size_t);
154
155 static int
156 ata_avila_probe(device_t dev)
157 {
158         struct ixp425_softc *sa = device_get_softc(device_get_parent(dev));
159         const struct ata_config *config;
160
161         config = ata_getconfig(sa);
162         if (config != NULL) {
163                 device_set_desc_copy(dev, config->desc);
164                 return 0;
165         }
166         return ENXIO;
167 }
168
169 static int
170 ata_avila_attach(device_t dev)
171 {
172         struct ata_avila_softc *sc = device_get_softc(dev);
173         struct ixp425_softc *sa = device_get_softc(device_get_parent(dev));
174         const struct ata_config *config;
175
176         config = ata_getconfig(sa);
177         KASSERT(config != NULL, ("no board config"));
178
179         sc->sc_dev = dev;
180         /* NB: borrow from parent */
181         sc->sc_iot = sa->sc_iot;
182         sc->sc_exp_ioh = sa->sc_exp_ioh;
183
184         if (bus_space_map(sc->sc_iot, config->base16, config->size16,
185             0, &sc->sc_ioh))
186                 panic("%s: cannot map 16-bit window (0x%x/0x%x)",
187                     __func__, config->base16, config->size16);
188         if (bus_space_map(sc->sc_iot, config->basealt, config->sizealt,
189             0, &sc->sc_alt_ioh))
190                 panic("%s: cannot map alt window (0x%x/0x%x)",
191                     __func__, config->basealt, config->sizealt);
192         sc->sc_16bit_off = config->off16;
193
194         if (config->base16 != CAMBRIA_CFSEL0_HWBASE) {
195                 /*
196                  * Craft special resource for ATA bus space ops
197                  * that go through the expansion bus and require
198                  * special hackery to ena/dis 16-bit operations.
199                  *
200                  * XXX probably should just make this generic for
201                  * accessing the expansion bus.
202                  */
203                 sc->sc_expbus_tag.bs_cookie = sc;       /* NB: backpointer */
204                 /* read single */
205                 sc->sc_expbus_tag.bs_r_1        = ata_bs_r_1,
206                 sc->sc_expbus_tag.bs_r_2        = ata_bs_r_2,
207                 /* read multiple */
208                 sc->sc_expbus_tag.bs_rm_2       = ata_bs_rm_2,
209                 sc->sc_expbus_tag.bs_rm_2_s     = ata_bs_rm_2_s,
210                 /* write (single) */
211                 sc->sc_expbus_tag.bs_w_1        = ata_bs_w_1,
212                 sc->sc_expbus_tag.bs_w_2        = ata_bs_w_2,
213                 /* write multiple */
214                 sc->sc_expbus_tag.bs_wm_2       = ata_bs_wm_2,
215                 sc->sc_expbus_tag.bs_wm_2_s     = ata_bs_wm_2_s,
216
217                 rman_set_bustag(&sc->sc_ata, &sc->sc_expbus_tag);
218                 rman_set_bustag(&sc->sc_alt_ata, &sc->sc_expbus_tag);
219         } else {
220                 /*
221                  * On Cambria use the shared CS3 expansion bus tag
222                  * that handles interlock for sharing access with the
223                  * optional UART's.
224                  */
225                 rman_set_bustag(&sc->sc_ata, &cambria_exp_bs_tag);
226                 rman_set_bustag(&sc->sc_alt_ata, &cambria_exp_bs_tag);
227         }
228         rman_set_bushandle(&sc->sc_ata, sc->sc_ioh);
229         rman_set_bushandle(&sc->sc_alt_ata, sc->sc_alt_ioh);
230
231         ixp425_set_gpio(sa, config->gpin, GPIO_TYPE_EDG_RISING);
232
233         /* configure CS1/3 window, leaving timing unchanged */
234         EXP_BUS_WRITE_4(sc, sc->sc_16bit_off,
235             EXP_BUS_READ_4(sc, sc->sc_16bit_off) |
236                 EXP_BYTE_EN | EXP_WR_EN | EXP_BYTE_RD16 | EXP_CS_EN);
237         /* configure CS2/4 window, leaving timing unchanged */
238         EXP_BUS_WRITE_4(sc, config->offalt,
239             EXP_BUS_READ_4(sc, config->offalt) |
240                 EXP_BYTE_EN | EXP_WR_EN | EXP_BYTE_RD16 | EXP_CS_EN);
241
242         /* setup interrupt */
243         sc->sc_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->sc_rid,
244             config->irq, config->irq, 1, RF_ACTIVE);
245         if (!sc->sc_irq)
246                 panic("Unable to allocate irq %u.\n", config->irq);
247         bus_setup_intr(dev, sc->sc_irq,
248             INTR_TYPE_BIO | INTR_MPSAFE | INTR_ENTROPY,
249             NULL, ata_avila_intr, sc, &sc->sc_ih);
250
251         /* attach channel on this controller */
252         device_add_child(dev, "ata", -1);
253         bus_generic_attach(dev);
254
255         return 0;
256 }
257
258 static int
259 ata_avila_detach(device_t dev)
260 {
261         struct ata_avila_softc *sc = device_get_softc(dev);
262         device_t *children;
263         int nc;
264
265         /* XXX quiesce gpio? */
266
267         /* detach & delete all children */
268         if (device_get_children(dev, &children, &nc) == 0) {
269                 if (nc > 0)
270                         device_delete_child(dev, children[0]);
271                 free(children, M_TEMP);
272         }
273
274         bus_teardown_intr(dev, sc->sc_irq, sc->sc_ih);
275         bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid, sc->sc_irq);
276
277         return 0;
278 }
279
280 static void
281 ata_avila_intr(void *xsc)
282 {
283         struct ata_avila_softc *sc = xsc;
284
285         if (sc->sc_intr[0].cb != NULL)
286                 sc->sc_intr[0].cb(sc->sc_intr[0].arg);
287 }
288
289 static struct resource *
290 ata_avila_alloc_resource(device_t dev, device_t child, int type, int *rid,
291                        u_long start, u_long end, u_long count, u_int flags)
292 {
293         struct ata_avila_softc *sc = device_get_softc(dev);
294
295         KASSERT(type == SYS_RES_IRQ && *rid == ATA_IRQ_RID,
296             ("type %u rid %u start %lu end %lu count %lu flags %u",
297              type, *rid, start, end, count, flags));
298
299         /* doesn't matter what we return so reuse the real thing */
300         return sc->sc_irq;
301 }
302
303 static int
304 ata_avila_release_resource(device_t dev, device_t child, int type, int rid,
305                          struct resource *r)
306 {
307         KASSERT(type == SYS_RES_IRQ && rid == ATA_IRQ_RID,
308             ("type %u rid %u", type, rid));
309         return 0;
310 }
311
312 static int
313 ata_avila_setup_intr(device_t dev, device_t child, struct resource *irq, 
314                    int flags, driver_filter_t *filt,
315                    driver_intr_t *function, void *argument, void **cookiep)
316 {
317         struct ata_avila_softc *sc = device_get_softc(dev);
318         int unit = ((struct ata_channel *)device_get_softc(child))->unit;
319
320         KASSERT(unit == 0, ("unit %d", unit));
321         sc->sc_intr[unit].cb = function;
322         sc->sc_intr[unit].arg = argument;
323         *cookiep = sc;
324         return 0;
325 }
326
327 static int
328 ata_avila_teardown_intr(device_t dev, device_t child, struct resource *irq,
329                       void *cookie)
330 {
331         struct ata_avila_softc *sc = device_get_softc(dev);
332         int unit = ((struct ata_channel *)device_get_softc(child))->unit;
333
334         KASSERT(unit == 0, ("unit %d", unit));
335         sc->sc_intr[unit].cb = NULL;
336         sc->sc_intr[unit].arg = NULL;
337         return 0;
338 }
339
340 /*
341  * Bus space accessors for CF-IDE PIO operations.
342  */
343
344 /*
345  * Enable/disable 16-bit ops on the expansion bus.
346  */
347 static __inline void
348 enable_16(struct ata_avila_softc *sc)
349 {
350         EXP_BUS_WRITE_4(sc, sc->sc_16bit_off,
351             EXP_BUS_READ_4(sc, sc->sc_16bit_off) &~ EXP_BYTE_EN);
352         DELAY(100);             /* XXX? */
353 }
354
355 static __inline void
356 disable_16(struct ata_avila_softc *sc)
357 {
358         DELAY(100);             /* XXX? */
359         EXP_BUS_WRITE_4(sc, sc->sc_16bit_off,
360             EXP_BUS_READ_4(sc, sc->sc_16bit_off) | EXP_BYTE_EN);
361 }
362
363 uint8_t
364 ata_bs_r_1(void *t, bus_space_handle_t h, bus_size_t o)
365 {
366         struct ata_avila_softc *sc = t;
367
368         return bus_space_read_1(sc->sc_iot, h, o);
369 }
370
371 void
372 ata_bs_w_1(void *t, bus_space_handle_t h, bus_size_t o, u_int8_t v)
373 {
374         struct ata_avila_softc *sc = t;
375
376         bus_space_write_1(sc->sc_iot, h, o, v);
377 }
378
379 uint16_t
380 ata_bs_r_2(void *t, bus_space_handle_t h, bus_size_t o)
381 {
382         struct ata_avila_softc *sc = t;
383         uint16_t v;
384
385         enable_16(sc);
386         v = bus_space_read_2(sc->sc_iot, h, o);
387         disable_16(sc);
388         return v;
389 }
390
391 void
392 ata_bs_w_2(void *t, bus_space_handle_t h, bus_size_t o, uint16_t v)
393 {
394         struct ata_avila_softc *sc = t;
395
396         enable_16(sc);
397         bus_space_write_2(sc->sc_iot, h, o, v);
398         disable_16(sc);
399 }
400
401 void
402 ata_bs_rm_2(void *t, bus_space_handle_t h, bus_size_t o,
403         u_int16_t *d, bus_size_t c)
404 {
405         struct ata_avila_softc *sc = t;
406
407         enable_16(sc);
408         bus_space_read_multi_2(sc->sc_iot, h, o, d, c);
409         disable_16(sc);
410 }
411
412 void
413 ata_bs_wm_2(void *t, bus_space_handle_t h, bus_size_t o,
414         const u_int16_t *d, bus_size_t c)
415 {
416         struct ata_avila_softc *sc = t;
417
418         enable_16(sc);
419         bus_space_write_multi_2(sc->sc_iot, h, o, d, c);
420         disable_16(sc);
421 }
422
423 /* XXX workaround ata driver by (incorrectly) byte swapping stream cases */
424
425 void
426 ata_bs_rm_2_s(void *t, bus_space_handle_t h, bus_size_t o,
427         u_int16_t *d, bus_size_t c)
428 {
429         struct ata_avila_softc *sc = t;
430         uint16_t v;
431         bus_size_t i;
432
433         enable_16(sc);
434 #if 1
435         for (i = 0; i < c; i++) {
436                 v = bus_space_read_2(sc->sc_iot, h, o);
437                 d[i] = bswap16(v);
438         }
439 #else
440         bus_space_read_multi_stream_2(sc->sc_iot, h, o, d, c);
441 #endif
442         disable_16(sc);
443 }
444
445 void
446 ata_bs_wm_2_s(void *t, bus_space_handle_t h, bus_size_t o,
447         const u_int16_t *d, bus_size_t c)
448 {
449         struct ata_avila_softc *sc = t;
450         bus_size_t i;
451
452         enable_16(sc);
453 #if 1
454         for (i = 0; i < c; i++)
455                 bus_space_write_2(sc->sc_iot, h, o, bswap16(d[i]));
456 #else
457         bus_space_write_multi_stream_2(sc->sc_iot, h, o, d, c);
458 #endif
459         disable_16(sc);
460 }
461
462 static device_method_t ata_avila_methods[] = {
463         /* device interface */
464         DEVMETHOD(device_probe,             ata_avila_probe),
465         DEVMETHOD(device_attach,            ata_avila_attach),
466         DEVMETHOD(device_detach,            ata_avila_detach),
467         DEVMETHOD(device_shutdown,          bus_generic_shutdown),
468         DEVMETHOD(device_suspend,           bus_generic_suspend),
469         DEVMETHOD(device_resume,            bus_generic_resume),
470
471         /* bus methods */
472         DEVMETHOD(bus_alloc_resource,       ata_avila_alloc_resource),
473         DEVMETHOD(bus_release_resource,     ata_avila_release_resource),
474         DEVMETHOD(bus_activate_resource,    bus_generic_activate_resource),
475         DEVMETHOD(bus_deactivate_resource,  bus_generic_deactivate_resource),
476         DEVMETHOD(bus_setup_intr,           ata_avila_setup_intr),
477         DEVMETHOD(bus_teardown_intr,        ata_avila_teardown_intr),
478
479         { 0, 0 }
480 };
481
482 devclass_t ata_avila_devclass;
483
484 static driver_t ata_avila_driver = {
485         "ata_avila",
486         ata_avila_methods,
487         sizeof(struct ata_avila_softc),
488 };
489
490 DRIVER_MODULE(ata_avila, ixp, ata_avila_driver, ata_avila_devclass, 0, 0);
491 MODULE_VERSION(ata_avila, 1);
492 MODULE_DEPEND(ata_avila, ata, 1, 1, 1);
493
494 static int
495 avila_channel_probe(device_t dev)
496 {
497         struct ata_channel *ch = device_get_softc(dev);
498
499         ch->unit = 0;
500         ch->flags |= ATA_USE_16BIT | ATA_NO_SLAVE;
501         device_set_desc_copy(dev, "ATA channel 0");
502
503         return ata_probe(dev);
504 }
505
506 static int
507 avila_channel_attach(device_t dev)
508 {
509         struct ata_avila_softc *sc = device_get_softc(device_get_parent(dev));
510         struct ata_channel *ch = device_get_softc(dev);
511         int i;
512
513         for (i = 0; i < ATA_MAX_RES; i++)
514                 ch->r_io[i].res = &sc->sc_ata;
515
516         ch->r_io[ATA_DATA].offset = ATA_DATA;
517         ch->r_io[ATA_FEATURE].offset = ATA_FEATURE;
518         ch->r_io[ATA_COUNT].offset = ATA_COUNT;
519         ch->r_io[ATA_SECTOR].offset = ATA_SECTOR;
520         ch->r_io[ATA_CYL_LSB].offset = ATA_CYL_LSB;
521         ch->r_io[ATA_CYL_MSB].offset = ATA_CYL_MSB;
522         ch->r_io[ATA_DRIVE].offset = ATA_DRIVE;
523         ch->r_io[ATA_COMMAND].offset = ATA_COMMAND;
524         ch->r_io[ATA_ERROR].offset = ATA_FEATURE;
525         /* NB: should be used only for ATAPI devices */
526         ch->r_io[ATA_IREASON].offset = ATA_COUNT;
527         ch->r_io[ATA_STATUS].offset = ATA_COMMAND;
528
529         /* NB: the control and alt status registers are special */
530         ch->r_io[ATA_ALTSTAT].res = &sc->sc_alt_ata;
531         ch->r_io[ATA_ALTSTAT].offset = AVILA_IDE_CTRL;
532         ch->r_io[ATA_CONTROL].res = &sc->sc_alt_ata;
533         ch->r_io[ATA_CONTROL].offset = AVILA_IDE_CTRL;
534
535         /* NB: by convention this points at the base of registers */
536         ch->r_io[ATA_IDX_ADDR].offset = 0;
537
538         ata_generic_hw(dev);
539         return ata_attach(dev);
540 }
541
542 static device_method_t avila_channel_methods[] = {
543         /* device interface */
544         DEVMETHOD(device_probe,     avila_channel_probe),
545         DEVMETHOD(device_attach,    avila_channel_attach),
546         DEVMETHOD(device_detach,    ata_detach),
547         DEVMETHOD(device_shutdown,  bus_generic_shutdown),
548         DEVMETHOD(device_suspend,   ata_suspend),
549         DEVMETHOD(device_resume,    ata_resume),
550
551         { 0, 0 }
552 };
553
554 driver_t avila_channel_driver = {
555         "ata",
556         avila_channel_methods,
557         sizeof(struct ata_channel),
558 };
559 DRIVER_MODULE(ata, ata_avila, avila_channel_driver, ata_devclass, 0, 0);