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