avila_ata.c revision 330897
1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2006 Sam Leffler, Errno Consulting
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer,
12 *    without modification.
13 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
14 *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
15 *    redistribution must be conditioned upon including a substantially
16 *    similar Disclaimer requirement for further binary redistribution.
17 *
18 * NO WARRANTY
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
22 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
24 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
27 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
29 * THE POSSIBILITY OF SUCH DAMAGES.
30 */
31
32#include <sys/cdefs.h>
33__FBSDID("$FreeBSD: stable/11/sys/arm/xscale/ixp425/avila_ata.c 330897 2018-03-14 03:19:51Z eadler $");
34
35/*
36 * Compact Flash Support for the Avila Gateworks XScale boards.
37 * The CF slot is operated in "True IDE" mode. Registers are on
38 * the Expansion Bus connected to CS1 and CS2. Interrupts are
39 * tied to GPIO pin 12.  No DMA, just PIO.
40 *
41 * The ADI Pronghorn Metro is very similar. It use CS3 and CS4 and
42 * GPIO pin 0 for interrupts.
43 *
44 * See also http://www.intel.com/design/network/applnots/302456.htm.
45 */
46#include <sys/param.h>
47#include <sys/systm.h>
48#include <sys/kernel.h>
49#include <sys/module.h>
50#include <sys/time.h>
51#include <sys/bus.h>
52#include <sys/resource.h>
53#include <sys/rman.h>
54#include <sys/sysctl.h>
55#include <sys/endian.h>
56
57#include <machine/bus.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
72struct 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
84static const struct ata_config *
85ata_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
129struct 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
148static void ata_avila_intr(void *);
149bs_protos(ata);
150static	void ata_bs_rm_2_s(bus_space_tag_t tag, bus_space_handle_t, bus_size_t,
151		u_int16_t *, bus_size_t);
152static	void ata_bs_wm_2_s(bus_space_tag_t tag, bus_space_handle_t, bus_size_t,
153		const u_int16_t *, bus_size_t);
154
155static int
156ata_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
169static int
170ata_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_privdata = 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
258static int
259ata_avila_detach(device_t dev)
260{
261	struct ata_avila_softc *sc = device_get_softc(dev);
262
263	/* XXX quiesce gpio? */
264
265	/* detach & delete all children */
266	device_delete_children(dev);
267
268	bus_teardown_intr(dev, sc->sc_irq, sc->sc_ih);
269	bus_release_resource(dev, SYS_RES_IRQ, sc->sc_rid, sc->sc_irq);
270
271	return 0;
272}
273
274static void
275ata_avila_intr(void *xsc)
276{
277	struct ata_avila_softc *sc = xsc;
278
279	if (sc->sc_intr[0].cb != NULL)
280		sc->sc_intr[0].cb(sc->sc_intr[0].arg);
281}
282
283static struct resource *
284ata_avila_alloc_resource(device_t dev, device_t child, int type, int *rid,
285		   rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
286{
287	struct ata_avila_softc *sc = device_get_softc(dev);
288
289	KASSERT(type == SYS_RES_IRQ && *rid == ATA_IRQ_RID,
290	    ("type %u rid %u start %ju end %ju count %ju flags %u",
291	     type, *rid, start, end, count, flags));
292
293	/* doesn't matter what we return so reuse the real thing */
294	return sc->sc_irq;
295}
296
297static int
298ata_avila_release_resource(device_t dev, device_t child, int type, int rid,
299			 struct resource *r)
300{
301	KASSERT(type == SYS_RES_IRQ && rid == ATA_IRQ_RID,
302	    ("type %u rid %u", type, rid));
303	return 0;
304}
305
306static int
307ata_avila_setup_intr(device_t dev, device_t child, struct resource *irq,
308		   int flags, driver_filter_t *filt,
309		   driver_intr_t *function, void *argument, void **cookiep)
310{
311	struct ata_avila_softc *sc = device_get_softc(dev);
312	int unit = ((struct ata_channel *)device_get_softc(child))->unit;
313
314	KASSERT(unit == 0, ("unit %d", unit));
315	sc->sc_intr[unit].cb = function;
316	sc->sc_intr[unit].arg = argument;
317	*cookiep = sc;
318	return 0;
319}
320
321static int
322ata_avila_teardown_intr(device_t dev, device_t child, struct resource *irq,
323		      void *cookie)
324{
325	struct ata_avila_softc *sc = device_get_softc(dev);
326	int unit = ((struct ata_channel *)device_get_softc(child))->unit;
327
328	KASSERT(unit == 0, ("unit %d", unit));
329	sc->sc_intr[unit].cb = NULL;
330	sc->sc_intr[unit].arg = NULL;
331	return 0;
332}
333
334/*
335 * Bus space accessors for CF-IDE PIO operations.
336 */
337
338/*
339 * Enable/disable 16-bit ops on the expansion bus.
340 */
341static __inline void
342enable_16(struct ata_avila_softc *sc)
343{
344	EXP_BUS_WRITE_4(sc, sc->sc_16bit_off,
345	    EXP_BUS_READ_4(sc, sc->sc_16bit_off) &~ EXP_BYTE_EN);
346	DELAY(100);		/* XXX? */
347}
348
349static __inline void
350disable_16(struct ata_avila_softc *sc)
351{
352	DELAY(100);		/* XXX? */
353	EXP_BUS_WRITE_4(sc, sc->sc_16bit_off,
354	    EXP_BUS_READ_4(sc, sc->sc_16bit_off) | EXP_BYTE_EN);
355}
356
357uint8_t
358ata_bs_r_1(bus_space_tag_t tag, bus_space_handle_t h, bus_size_t o)
359{
360	struct ata_avila_softc *sc = tag->bs_privdata;
361
362	return bus_space_read_1(sc->sc_iot, h, o);
363}
364
365void
366ata_bs_w_1(bus_space_tag_t tag, bus_space_handle_t h, bus_size_t o, u_int8_t v)
367{
368	struct ata_avila_softc *sc = tag->bs_privdata;
369
370	bus_space_write_1(sc->sc_iot, h, o, v);
371}
372
373uint16_t
374ata_bs_r_2(bus_space_tag_t tag, bus_space_handle_t h, bus_size_t o)
375{
376	struct ata_avila_softc *sc = tag->bs_privdata;
377	uint16_t v;
378
379	enable_16(sc);
380	v = bus_space_read_2(sc->sc_iot, h, o);
381	disable_16(sc);
382	return v;
383}
384
385void
386ata_bs_w_2(bus_space_tag_t tag, bus_space_handle_t h, bus_size_t o, uint16_t v)
387{
388	struct ata_avila_softc *sc = tag->bs_privdata;
389
390	enable_16(sc);
391	bus_space_write_2(sc->sc_iot, h, o, v);
392	disable_16(sc);
393}
394
395void
396ata_bs_rm_2(bus_space_tag_t tag, bus_space_handle_t h, bus_size_t o,
397	u_int16_t *d, bus_size_t c)
398{
399	struct ata_avila_softc *sc = tag->bs_privdata;
400
401	enable_16(sc);
402	bus_space_read_multi_2(sc->sc_iot, h, o, d, c);
403	disable_16(sc);
404}
405
406void
407ata_bs_wm_2(bus_space_tag_t tag, bus_space_handle_t h, bus_size_t o,
408	const u_int16_t *d, bus_size_t c)
409{
410	struct ata_avila_softc *sc = tag->bs_privdata;
411
412	enable_16(sc);
413	bus_space_write_multi_2(sc->sc_iot, h, o, d, c);
414	disable_16(sc);
415}
416
417/* XXX workaround ata driver by (incorrectly) byte swapping stream cases */
418
419void
420ata_bs_rm_2_s(bus_space_tag_t tag, bus_space_handle_t h, bus_size_t o,
421	u_int16_t *d, bus_size_t c)
422{
423	struct ata_avila_softc *sc = tag->bs_privdata;
424	uint16_t v;
425	bus_size_t i;
426
427	enable_16(sc);
428#if 1
429	for (i = 0; i < c; i++) {
430		v = bus_space_read_2(sc->sc_iot, h, o);
431		d[i] = bswap16(v);
432	}
433#else
434	bus_space_read_multi_stream_2(sc->sc_iot, h, o, d, c);
435#endif
436	disable_16(sc);
437}
438
439void
440ata_bs_wm_2_s(bus_space_tag_t tag, bus_space_handle_t h, bus_size_t o,
441	const u_int16_t *d, bus_size_t c)
442{
443	struct ata_avila_softc *sc = tag->bs_privdata;
444	bus_size_t i;
445
446	enable_16(sc);
447#if 1
448	for (i = 0; i < c; i++)
449		bus_space_write_2(sc->sc_iot, h, o, bswap16(d[i]));
450#else
451	bus_space_write_multi_stream_2(sc->sc_iot, h, o, d, c);
452#endif
453	disable_16(sc);
454}
455
456static device_method_t ata_avila_methods[] = {
457	/* device interface */
458	DEVMETHOD(device_probe,             ata_avila_probe),
459	DEVMETHOD(device_attach,            ata_avila_attach),
460	DEVMETHOD(device_detach,            ata_avila_detach),
461	DEVMETHOD(device_shutdown,          bus_generic_shutdown),
462	DEVMETHOD(device_suspend,           bus_generic_suspend),
463	DEVMETHOD(device_resume,            bus_generic_resume),
464
465	/* bus methods */
466	DEVMETHOD(bus_alloc_resource,       ata_avila_alloc_resource),
467	DEVMETHOD(bus_release_resource,     ata_avila_release_resource),
468	DEVMETHOD(bus_activate_resource,    bus_generic_activate_resource),
469	DEVMETHOD(bus_deactivate_resource,  bus_generic_deactivate_resource),
470	DEVMETHOD(bus_setup_intr,           ata_avila_setup_intr),
471	DEVMETHOD(bus_teardown_intr,        ata_avila_teardown_intr),
472
473	{ 0, 0 }
474};
475
476devclass_t ata_avila_devclass;
477
478static driver_t ata_avila_driver = {
479	"ata_avila",
480	ata_avila_methods,
481	sizeof(struct ata_avila_softc),
482};
483
484DRIVER_MODULE(ata_avila, ixp, ata_avila_driver, ata_avila_devclass, 0, 0);
485MODULE_VERSION(ata_avila, 1);
486MODULE_DEPEND(ata_avila, ata, 1, 1, 1);
487
488static int
489avila_channel_probe(device_t dev)
490{
491	struct ata_channel *ch = device_get_softc(dev);
492
493	ch->unit = 0;
494	ch->flags |= ATA_USE_16BIT | ATA_NO_SLAVE;
495	device_set_desc_copy(dev, "ATA channel 0");
496
497	return ata_probe(dev);
498}
499
500static int
501avila_channel_attach(device_t dev)
502{
503	struct ata_avila_softc *sc = device_get_softc(device_get_parent(dev));
504	struct ata_channel *ch = device_get_softc(dev);
505	int i;
506
507	for (i = 0; i < ATA_MAX_RES; i++)
508		ch->r_io[i].res = &sc->sc_ata;
509
510	ch->r_io[ATA_DATA].offset = ATA_DATA;
511	ch->r_io[ATA_FEATURE].offset = ATA_FEATURE;
512	ch->r_io[ATA_COUNT].offset = ATA_COUNT;
513	ch->r_io[ATA_SECTOR].offset = ATA_SECTOR;
514	ch->r_io[ATA_CYL_LSB].offset = ATA_CYL_LSB;
515	ch->r_io[ATA_CYL_MSB].offset = ATA_CYL_MSB;
516	ch->r_io[ATA_DRIVE].offset = ATA_DRIVE;
517	ch->r_io[ATA_COMMAND].offset = ATA_COMMAND;
518	ch->r_io[ATA_ERROR].offset = ATA_FEATURE;
519	/* NB: should be used only for ATAPI devices */
520	ch->r_io[ATA_IREASON].offset = ATA_COUNT;
521	ch->r_io[ATA_STATUS].offset = ATA_COMMAND;
522
523	/* NB: the control and alt status registers are special */
524	ch->r_io[ATA_ALTSTAT].res = &sc->sc_alt_ata;
525	ch->r_io[ATA_ALTSTAT].offset = AVILA_IDE_CTRL;
526	ch->r_io[ATA_CONTROL].res = &sc->sc_alt_ata;
527	ch->r_io[ATA_CONTROL].offset = AVILA_IDE_CTRL;
528
529	/* NB: by convention this points at the base of registers */
530	ch->r_io[ATA_IDX_ADDR].offset = 0;
531
532	ata_generic_hw(dev);
533	return ata_attach(dev);
534}
535
536static device_method_t avila_channel_methods[] = {
537	/* device interface */
538	DEVMETHOD(device_probe,     avila_channel_probe),
539	DEVMETHOD(device_attach,    avila_channel_attach),
540	DEVMETHOD(device_detach,    ata_detach),
541	DEVMETHOD(device_shutdown,  bus_generic_shutdown),
542	DEVMETHOD(device_suspend,   ata_suspend),
543	DEVMETHOD(device_resume,    ata_resume),
544
545	{ 0, 0 }
546};
547
548driver_t avila_channel_driver = {
549	"ata",
550	avila_channel_methods,
551	sizeof(struct ata_channel),
552};
553DRIVER_MODULE(ata, ata_avila, avila_channel_driver, ata_devclass, 0, 0);
554