xref: /openbsd-src/sys/arch/armv7/sunxi/sxiahci.c (revision 9fdf0c627b1fec102f212f847a6f7676c1829e65)
1*9fdf0c62Smpi /*	$OpenBSD: sxiahci.c,v 1.17 2021/10/24 17:52:28 mpi Exp $	*/
219606781Sjasper /*
319606781Sjasper  * Copyright (c) 2013 Patrick Wildt <patrick@blueri.se>
493c0f120Sjsg  * Copyright (c) 2013,2014 Artturi Alm
519606781Sjasper  *
619606781Sjasper  * Permission to use, copy, modify, and distribute this software for any
719606781Sjasper  * purpose with or without fee is hereby granted, provided that the above
819606781Sjasper  * copyright notice and this permission notice appear in all copies.
919606781Sjasper  *
1019606781Sjasper  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1119606781Sjasper  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1219606781Sjasper  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1319606781Sjasper  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1419606781Sjasper  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1519606781Sjasper  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1619606781Sjasper  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1719606781Sjasper  */
1819606781Sjasper 
1919606781Sjasper #include <sys/param.h>
2019606781Sjasper #include <sys/systm.h>
2119606781Sjasper #include <sys/device.h>
2219606781Sjasper 
2319606781Sjasper #include <machine/bus.h>
24411a93efSkettenis #include <machine/fdt.h>
2519606781Sjasper 
2619606781Sjasper #include <dev/ic/ahcireg.h>
2706133b5aSdlg #include <dev/ic/ahcivar.h>
2819606781Sjasper 
290ea1c705Spatrick #include <dev/fdt/sunxireg.h>
3019606781Sjasper 
31411a93efSkettenis #include <dev/ofw/openfirm.h>
32e904db79Skettenis #include <dev/ofw/ofw_clock.h>
335cae81a0Skettenis #include <dev/ofw/ofw_regulator.h>
34411a93efSkettenis #include <dev/ofw/fdt.h>
35411a93efSkettenis 
364e64f0a4Sjasper #define	SXIAHCI_CAP	0x0000
374e64f0a4Sjasper #define	SXIAHCI_GHC	0x0004
384e64f0a4Sjasper #define	SXIAHCI_PI	0x000c
394e64f0a4Sjasper #define	SXIAHCI_PHYCS0	0x00c0
404e64f0a4Sjasper #define	SXIAHCI_PHYCS1	0x00c4
414e64f0a4Sjasper #define	SXIAHCI_PHYCS2	0x00c8
424e64f0a4Sjasper #define	SXIAHCI_TIMER1MS	0x00e0
434e64f0a4Sjasper #define	SXIAHCI_RWC	0x00fc
444e64f0a4Sjasper #define	SXIAHCI_TIMEOUT	0x100000
454e64f0a4Sjasper #define SXIAHCI_PWRPIN	40
4619606781Sjasper 
4793c0f120Sjsg #define SXIAHCI_PREG_DMA	0x70
4893c0f120Sjsg #define  SXIAHCI_PREG_DMA_MASK	(0xff<<8)
4993c0f120Sjsg #define  SXIAHCI_PREG_DMA_INIT	(0x44<<8)
5093c0f120Sjsg 
51411a93efSkettenis int	sxiahci_match(struct device *, void *, void *);
5219606781Sjasper void	sxiahci_attach(struct device *, struct device *, void *);
5319606781Sjasper int	sxiahci_detach(struct device *, int);
5419606781Sjasper int	sxiahci_activate(struct device *, int);
5593c0f120Sjsg int	sxiahci_port_start(struct ahci_port *, int);
5619606781Sjasper 
5719606781Sjasper extern int ahci_intr(void *);
5819606781Sjasper extern u_int32_t ahci_read(struct ahci_softc *, bus_size_t);
5919606781Sjasper extern void ahci_write(struct ahci_softc *, bus_size_t, u_int32_t);
6093c0f120Sjsg extern u_int32_t ahci_pread(struct ahci_port *, bus_size_t);
6193c0f120Sjsg extern void ahci_pwrite(struct ahci_port *, bus_size_t, u_int32_t);
6293c0f120Sjsg extern int ahci_default_port_start(struct ahci_port *, int);
6319606781Sjasper 
6419606781Sjasper struct sxiahci_softc {
6519606781Sjasper 	struct ahci_softc	sc;
6619606781Sjasper 
6719606781Sjasper };
6819606781Sjasper 
69*9fdf0c62Smpi const struct cfattach sxiahci_ca = {
7019606781Sjasper 	sizeof(struct sxiahci_softc),
71411a93efSkettenis 	sxiahci_match,
7219606781Sjasper 	sxiahci_attach,
7319606781Sjasper 	sxiahci_detach,
7419606781Sjasper 	sxiahci_activate
7519606781Sjasper };
7619606781Sjasper 
7719606781Sjasper struct cfdriver sxiahci_cd = {
78411a93efSkettenis 	NULL, "sxiahci", DV_DULL
7919606781Sjasper };
8019606781Sjasper 
81411a93efSkettenis int
sxiahci_match(struct device * parent,void * match,void * aux)82411a93efSkettenis sxiahci_match(struct device *parent, void *match, void *aux)
8319606781Sjasper {
84411a93efSkettenis 	struct fdt_attach_args *faa = aux;
85411a93efSkettenis 
8685b8fad3Sjsg 	return OF_is_compatible(faa->fa_node, "allwinner,sun4i-a10-ahci") ||
8785b8fad3Sjsg 	    OF_is_compatible(faa->fa_node, "allwinner,sun8i-r40-ahci");
88411a93efSkettenis }
89411a93efSkettenis 
90411a93efSkettenis void
sxiahci_attach(struct device * parent,struct device * self,void * aux)91411a93efSkettenis sxiahci_attach(struct device *parent, struct device *self, void *aux)
92411a93efSkettenis {
9319606781Sjasper 	struct sxiahci_softc *sxisc = (struct sxiahci_softc *)self;
9419606781Sjasper 	struct ahci_softc *sc = &sxisc->sc;
95411a93efSkettenis 	struct fdt_attach_args *faa = aux;
965cae81a0Skettenis 	uint32_t target_supply;
9719606781Sjasper 	uint32_t timo;
9819606781Sjasper 
99411a93efSkettenis 	if (faa->fa_nreg < 1)
100411a93efSkettenis 		return;
10119606781Sjasper 
102411a93efSkettenis 	sc->sc_iot = faa->fa_iot;
103411a93efSkettenis 	sc->sc_ios = faa->fa_reg[0].size;
104411a93efSkettenis 	sc->sc_dmat = faa->fa_dmat;
105411a93efSkettenis 
106411a93efSkettenis 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
107411a93efSkettenis 	    faa->fa_reg[0].size, 0, &sc->sc_ioh))
10819606781Sjasper 		panic("sxiahci_attach: bus_space_map failed!");
10919606781Sjasper 
110e904db79Skettenis 	/* enable clocks */
111e904db79Skettenis 	clock_enable_all(faa->fa_node);
11219606781Sjasper 	delay(5000);
11319606781Sjasper 
114f22f0821Skettenis 	reset_deassert_all(faa->fa_node);
115f22f0821Skettenis 
11619606781Sjasper 	/* XXX setup magix */
1174e64f0a4Sjasper 	SXIWRITE4(sc, SXIAHCI_RWC, 0);
11819606781Sjasper 	delay(10);
11919606781Sjasper 
1204e64f0a4Sjasper 	SXISET4(sc, SXIAHCI_PHYCS1, 1 << 19);
12119606781Sjasper 	delay(10);
12219606781Sjasper 
12393c0f120Sjsg 	SXICMS4(sc, SXIAHCI_PHYCS0, 7 << 24,
12493c0f120Sjsg 	    1 << 23 | 5 << 24 | 1 << 18);
12519606781Sjasper 	delay(10);
12619606781Sjasper 
1274e64f0a4Sjasper 	SXICMS4(sc, SXIAHCI_PHYCS1,
12893c0f120Sjsg 	    3 << 16 | 0x1f << 8 | 3 << 6,
12993c0f120Sjsg 	    2 << 16 | 0x06 << 8 | 2 << 6);
13019606781Sjasper 	delay(10);
13119606781Sjasper 
1324e64f0a4Sjasper 	SXISET4(sc, SXIAHCI_PHYCS1, 1 << 28 | 1 << 15);
13319606781Sjasper 	delay(10);
13419606781Sjasper 
1354e64f0a4Sjasper 	SXICLR4(sc, SXIAHCI_PHYCS1, 1 << 19);
13619606781Sjasper 	delay(10);
13719606781Sjasper 
13893c0f120Sjsg 	SXICMS4(sc, SXIAHCI_PHYCS0, 0x07 << 20, 0x03 << 20);
13993c0f120Sjsg 	SXICMS4(sc, SXIAHCI_PHYCS2, 0x1f <<  5, 0x19 << 5);
14019606781Sjasper 	delay(5000);
14119606781Sjasper 
1424e64f0a4Sjasper 	SXISET4(sc, SXIAHCI_PHYCS0, 1 << 19);
14319606781Sjasper 	delay(20);
14419606781Sjasper 
1454e64f0a4Sjasper 	timo = SXIAHCI_TIMEOUT;
14693c0f120Sjsg 	while ((SXIREAD4(sc, SXIAHCI_PHYCS0) >> 28 & 7) != 2 && --timo)
14719606781Sjasper 		delay(10);
148c2f7ab5fSjasper 	if (!timo) {
149c2f7ab5fSjasper 		printf(": AHCI phy power up failed.\n");
150c2f7ab5fSjasper 		goto dismod;
151c2f7ab5fSjasper 	}
15219606781Sjasper 
1534e64f0a4Sjasper 	SXISET4(sc, SXIAHCI_PHYCS2, 1 << 24);
15419606781Sjasper 
1554e64f0a4Sjasper 	timo = SXIAHCI_TIMEOUT;
1564e64f0a4Sjasper 	while ((SXIREAD4(sc, SXIAHCI_PHYCS2) & (1 << 24)) && --timo)
15719606781Sjasper 		delay(10);
158c2f7ab5fSjasper 	if (!timo) {
159c2f7ab5fSjasper 		printf(": AHCI phy calibration failed.\n");
160c2f7ab5fSjasper 		goto dismod;
161c2f7ab5fSjasper 	}
16219606781Sjasper 
16319606781Sjasper 	delay(15000);
1644e64f0a4Sjasper 	SXIWRITE4(sc, SXIAHCI_RWC, 7);
16519606781Sjasper 
16619606781Sjasper 	/* power up phy */
1675cae81a0Skettenis 	target_supply = OF_getpropint(faa->fa_node, "target-supply", 0);
1685cae81a0Skettenis 	if (target_supply)
1695cae81a0Skettenis 		regulator_enable(target_supply);
17019606781Sjasper 
171411a93efSkettenis 	sc->sc_ih = arm_intr_establish_fdt(faa->fa_node, IPL_BIO,
17219606781Sjasper 	    ahci_intr, sc, sc->sc_dev.dv_xname);
17319606781Sjasper 	if (sc->sc_ih == NULL) {
17419606781Sjasper 		printf(": unable to establish interrupt\n");
175c2f7ab5fSjasper 		goto clrpwr;
17619606781Sjasper 	}
17719606781Sjasper 
178411a93efSkettenis 	printf(":");
179411a93efSkettenis 
1804e64f0a4Sjasper 	SXIWRITE4(sc, SXIAHCI_PI, 1);
1814e64f0a4Sjasper 	SXICLR4(sc, SXIAHCI_CAP, AHCI_REG_CAP_SPM);
18293c0f120Sjsg 	sc->sc_flags |= AHCI_F_NO_PMP;
18393c0f120Sjsg 	sc->sc_port_start = sxiahci_port_start;
18419606781Sjasper 	if (ahci_attach(sc) != 0) {
18519606781Sjasper 		/* error printed by ahci_attach */
18619606781Sjasper 		goto irq;
18719606781Sjasper 	}
18819606781Sjasper 
18919606781Sjasper 	return;
19019606781Sjasper irq:
19119606781Sjasper 	arm_intr_disestablish(sc->sc_ih);
192c2f7ab5fSjasper clrpwr:
1935cae81a0Skettenis 	if (target_supply)
1945cae81a0Skettenis 		regulator_disable(target_supply);
195c2f7ab5fSjasper dismod:
196e904db79Skettenis 	clock_disable_all(faa->fa_node);
19719606781Sjasper 	bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios);
19819606781Sjasper }
19919606781Sjasper 
20019606781Sjasper int
sxiahci_detach(struct device * self,int flags)20119606781Sjasper sxiahci_detach(struct device *self, int flags)
20219606781Sjasper {
20319606781Sjasper 	struct sxiahci_softc *sxisc = (struct sxiahci_softc *) self;
20419606781Sjasper 	struct ahci_softc *sc = &sxisc->sc;
20519606781Sjasper 
20619606781Sjasper 	ahci_detach(sc, flags);
20719606781Sjasper 	bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios);
20819606781Sjasper 	return 0;
20919606781Sjasper }
21019606781Sjasper 
21119606781Sjasper int
sxiahci_activate(struct device * self,int act)21219606781Sjasper sxiahci_activate(struct device *self, int act)
21319606781Sjasper {
21419606781Sjasper 	struct sxiahci_softc *sxisc = (struct sxiahci_softc *) self;
21519606781Sjasper 	struct ahci_softc *sc = &sxisc->sc;
21619606781Sjasper 
21719606781Sjasper 	return ahci_activate((struct device *)sc, act);
21819606781Sjasper }
21993c0f120Sjsg 
22093c0f120Sjsg int
sxiahci_port_start(struct ahci_port * ap,int fre_only)22193c0f120Sjsg sxiahci_port_start(struct ahci_port *ap, int fre_only)
22293c0f120Sjsg {
223e904db79Skettenis 	uint32_t r;
22493c0f120Sjsg 
22593c0f120Sjsg 	/* Setup DMA */
22693c0f120Sjsg 	r = ahci_pread(ap, SXIAHCI_PREG_DMA);
22793c0f120Sjsg 	r &= ~SXIAHCI_PREG_DMA_MASK;
22893c0f120Sjsg 	r |= SXIAHCI_PREG_DMA_INIT; /* XXX if fre_only? */
22993c0f120Sjsg 	ahci_pwrite(ap, SXIAHCI_PREG_DMA, r);
23093c0f120Sjsg 
23193c0f120Sjsg 	return (ahci_default_port_start(ap, fre_only));
23293c0f120Sjsg }
233