xref: /openbsd-src/sys/arch/armv7/exynos/exehci.c (revision 9fdf0c627b1fec102f212f847a6f7676c1829e65)
1*9fdf0c62Smpi /*	$OpenBSD: exehci.c,v 1.11 2021/10/24 17:52:27 mpi Exp $ */
207829fe8Sbmercer /*
307829fe8Sbmercer  * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se>
407829fe8Sbmercer  *
507829fe8Sbmercer  * Permission to use, copy, modify, and distribute this software for any
607829fe8Sbmercer  * purpose with or without fee is hereby granted, provided that the above
707829fe8Sbmercer  * copyright notice and this permission notice appear in all copies.
807829fe8Sbmercer  *
907829fe8Sbmercer  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1007829fe8Sbmercer  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1107829fe8Sbmercer  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1207829fe8Sbmercer  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1307829fe8Sbmercer  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1407829fe8Sbmercer  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1507829fe8Sbmercer  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1607829fe8Sbmercer  */
1707829fe8Sbmercer 
1807829fe8Sbmercer #include <sys/param.h>
1907829fe8Sbmercer #include <sys/systm.h>
2007829fe8Sbmercer #include <sys/device.h>
2107829fe8Sbmercer 
2207829fe8Sbmercer #include <machine/intr.h>
2307829fe8Sbmercer #include <machine/bus.h>
2407829fe8Sbmercer #include <machine/fdt.h>
2507829fe8Sbmercer 
2607829fe8Sbmercer #include <dev/usb/usb.h>
2707829fe8Sbmercer #include <dev/usb/usbdi.h>
2807829fe8Sbmercer #include <dev/usb/usbdivar.h>
2907829fe8Sbmercer 
30fe1242bbSkettenis #include <dev/ofw/openfirm.h>
31fe1242bbSkettenis #include <dev/ofw/ofw_clock.h>
32fe1242bbSkettenis #include <dev/ofw/ofw_gpio.h>
33196d040eSkettenis #include <dev/ofw/ofw_misc.h>
34fe1242bbSkettenis #include <dev/ofw/fdt.h>
35fe1242bbSkettenis 
3607829fe8Sbmercer #include <dev/usb/ehcireg.h>
3707829fe8Sbmercer #include <dev/usb/ehcivar.h>
3807829fe8Sbmercer 
3907829fe8Sbmercer /* registers */
4007829fe8Sbmercer #define USBPHY_CTRL0			0x00
4107829fe8Sbmercer #define USBPHY_TUNE0			0x04
4207829fe8Sbmercer #define HSICPHY_CTRL1			0x10
4307829fe8Sbmercer #define HSICPHY_TUNE1			0x14
4407829fe8Sbmercer #define HSICPHY_CTRL2			0x20
4507829fe8Sbmercer #define HSICPHY_TUNE2			0x24
4607829fe8Sbmercer #define EHCI_CTRL			0x30
4707829fe8Sbmercer #define OHCI_CTRL			0x34
4807829fe8Sbmercer #define USBOTG_SYS			0x38
4907829fe8Sbmercer #define USBOTG_TUNE			0x40
5007829fe8Sbmercer 
5107829fe8Sbmercer /* bits and bytes */
5207829fe8Sbmercer #define CLK_24MHZ			5
5307829fe8Sbmercer 
5407829fe8Sbmercer #define HOST_CTRL0_PHYSWRSTALL		(1U << 31)
5507829fe8Sbmercer #define HOST_CTRL0_COMMONON_N		(1 << 9)
5607829fe8Sbmercer #define HOST_CTRL0_SIDDQ		(1 << 6)
5707829fe8Sbmercer #define HOST_CTRL0_FORCESLEEP		(1 << 5)
5807829fe8Sbmercer #define HOST_CTRL0_FORCESUSPEND		(1 << 4)
5907829fe8Sbmercer #define HOST_CTRL0_WORDINTERFACE	(1 << 3)
6007829fe8Sbmercer #define HOST_CTRL0_UTMISWRST		(1 << 2)
6107829fe8Sbmercer #define HOST_CTRL0_LINKSWRST		(1 << 1)
6207829fe8Sbmercer #define HOST_CTRL0_PHYSWRST		(1 << 0)
6307829fe8Sbmercer 
6407829fe8Sbmercer #define HOST_CTRL0_FSEL_MASK		(7 << 16)
6507829fe8Sbmercer 
6607829fe8Sbmercer #define EHCI_CTRL_ENAINCRXALIGN		(1 << 29)
6707829fe8Sbmercer #define EHCI_CTRL_ENAINCR4		(1 << 28)
6807829fe8Sbmercer #define EHCI_CTRL_ENAINCR8		(1 << 27)
6907829fe8Sbmercer #define EHCI_CTRL_ENAINCR16		(1 << 26)
7007829fe8Sbmercer 
7154a31d3eSkettenis /* SYSREG registers */
7254a31d3eSkettenis #define USB20PHY_CFG			0x230
7354a31d3eSkettenis #define  USB20PHY_CFG_HOST_LINK_EN	(1 << 0)
7454a31d3eSkettenis 
75196d040eSkettenis /* PMU registers */
76196d040eSkettenis #define USB_HOST_POWER_5250	0x708
77196d040eSkettenis #define USB_HOST_POWER_54XX	0x70c
78196d040eSkettenis #define USB_HOST_POWER_EN	(1 << 0)
79196d040eSkettenis 
8007829fe8Sbmercer int	exehci_match(struct device *, void *, void *);
8107829fe8Sbmercer void	exehci_attach(struct device *, struct device *, void *);
8207829fe8Sbmercer int	exehci_detach(struct device *, int);
8307829fe8Sbmercer 
8407829fe8Sbmercer struct exehci_softc {
85fe1242bbSkettenis 	struct ehci_softc	sc;
8607829fe8Sbmercer 	void			*sc_ih;
87196d040eSkettenis 	int			sc_phy;
8807829fe8Sbmercer 	bus_space_handle_t	ph_ioh;
8907829fe8Sbmercer };
9007829fe8Sbmercer 
91*9fdf0c62Smpi const struct cfattach exehci_ca = {
92fe1242bbSkettenis 	sizeof (struct exehci_softc), exehci_match, exehci_attach,
93fe1242bbSkettenis 	exehci_detach
94fe1242bbSkettenis };
95fe1242bbSkettenis 
9607829fe8Sbmercer struct cfdriver exehci_cd = {
9707829fe8Sbmercer 	NULL, "exehci", DV_DULL
9807829fe8Sbmercer };
9907829fe8Sbmercer 
10007829fe8Sbmercer void	exehci_setup(struct exehci_softc *);
10107829fe8Sbmercer 
10207829fe8Sbmercer int
exehci_match(struct device * parent,void * match,void * aux)103fe1242bbSkettenis exehci_match(struct device *parent, void *match, void *aux)
10407829fe8Sbmercer {
105fe1242bbSkettenis 	struct fdt_attach_args *faa = aux;
10607829fe8Sbmercer 
107fe1242bbSkettenis 	return OF_is_compatible(faa->fa_node, "samsung,exynos4210-ehci");
10807829fe8Sbmercer }
10907829fe8Sbmercer 
11007829fe8Sbmercer void
exehci_attach(struct device * parent,struct device * self,void * aux)11107829fe8Sbmercer exehci_attach(struct device *parent, struct device *self, void *aux)
11207829fe8Sbmercer {
11307829fe8Sbmercer 	struct exehci_softc *sc = (struct exehci_softc *)self;
114fe1242bbSkettenis 	struct fdt_attach_args *faa = aux;
11507829fe8Sbmercer 	usbd_status r;
116fe1242bbSkettenis 	char *devname = sc->sc.sc_bus.bdev.dv_xname;
117fe1242bbSkettenis 	uint32_t phys[2];
118fe1242bbSkettenis 	uint32_t phy_reg[2];
119fe1242bbSkettenis 	int node;
12007829fe8Sbmercer 
121fe1242bbSkettenis 	if (faa->fa_nreg < 1)
122fe1242bbSkettenis 		return;
12307829fe8Sbmercer 
124fe1242bbSkettenis 	node = OF_child(faa->fa_node);
125fe1242bbSkettenis 	if (node == 0)
126f1cfcbbaSkettenis 		node = faa->fa_node;
12707829fe8Sbmercer 
128fe1242bbSkettenis 	if (OF_getpropintarray(node, "phys", phys,
129fe1242bbSkettenis 	    sizeof(phys)) != sizeof(phys))
130fe1242bbSkettenis 		return;
13107829fe8Sbmercer 
132196d040eSkettenis 	sc->sc_phy = OF_getnodebyphandle(phys[0]);
133196d040eSkettenis 	if (sc->sc_phy == 0)
134fe1242bbSkettenis 		return;
13507829fe8Sbmercer 
136196d040eSkettenis 	if (OF_getpropintarray(sc->sc_phy, "reg", phy_reg,
137fe1242bbSkettenis 	    sizeof(phy_reg)) != sizeof(phy_reg))
138fe1242bbSkettenis 		return;
13907829fe8Sbmercer 
140fe1242bbSkettenis 	sc->sc.iot = faa->fa_iot;
141fe1242bbSkettenis 	sc->sc.sc_bus.dmatag = faa->fa_dmat;
142fe1242bbSkettenis 	sc->sc.sc_size = faa->fa_reg[0].size;
14307829fe8Sbmercer 
14407829fe8Sbmercer 	/* Map I/O space */
145fe1242bbSkettenis 	if (bus_space_map(sc->sc.iot, faa->fa_reg[0].addr,
146fe1242bbSkettenis 	    faa->fa_reg[0].size, 0, &sc->sc.ioh)) {
14707829fe8Sbmercer 		printf(": cannot map mem space\n");
14807829fe8Sbmercer 		goto out;
14907829fe8Sbmercer 	}
15007829fe8Sbmercer 
151fe1242bbSkettenis 	if (bus_space_map(sc->sc.iot, phy_reg[0],
152fe1242bbSkettenis 	    phy_reg[1], 0, &sc->ph_ioh)) {
15307829fe8Sbmercer 		printf(": cannot map mem space\n");
154fe1242bbSkettenis 		goto mem0;
15507829fe8Sbmercer 	}
15607829fe8Sbmercer 
15707829fe8Sbmercer 	printf("\n");
15807829fe8Sbmercer 
159f1cfcbbaSkettenis 	clock_enable_all(faa->fa_node);
160f1cfcbbaSkettenis 
16107829fe8Sbmercer 	exehci_setup(sc);
16207829fe8Sbmercer 
163fe1242bbSkettenis 	sc->sc_ih = arm_intr_establish_fdt(faa->fa_node, IPL_USB,
164fe1242bbSkettenis 	    ehci_intr, &sc->sc, devname);
16507829fe8Sbmercer 	if (sc->sc_ih == NULL) {
16607829fe8Sbmercer 		printf(": unable to establish interrupt\n");
167fe1242bbSkettenis 		goto mem1;
16807829fe8Sbmercer 	}
16907829fe8Sbmercer 
170fe1242bbSkettenis 	strlcpy(sc->sc.sc_vendor, "Exynos 5", sizeof(sc->sc.sc_vendor));
171fe1242bbSkettenis 	r = ehci_init(&sc->sc);
17207829fe8Sbmercer 	if (r != USBD_NORMAL_COMPLETION) {
173fe1242bbSkettenis 		printf("%s: init failed, error=%d\n", devname, r);
17407829fe8Sbmercer 		goto intr;
17507829fe8Sbmercer 	}
17607829fe8Sbmercer 
177fe1242bbSkettenis 	config_found(self, &sc->sc.sc_bus, usbctlprint);
17807829fe8Sbmercer 
17907829fe8Sbmercer 	goto out;
18007829fe8Sbmercer 
18107829fe8Sbmercer intr:
18207829fe8Sbmercer 	arm_intr_disestablish(sc->sc_ih);
18307829fe8Sbmercer 	sc->sc_ih = NULL;
184fe1242bbSkettenis mem1:
185fe1242bbSkettenis 	bus_space_unmap(sc->sc.iot, sc->ph_ioh, phy_reg[1]);
186fe1242bbSkettenis mem0:
187fe1242bbSkettenis 	bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
188fe1242bbSkettenis 	sc->sc.sc_size = 0;
18907829fe8Sbmercer out:
19007829fe8Sbmercer 	return;
19107829fe8Sbmercer }
19207829fe8Sbmercer 
19307829fe8Sbmercer int
exehci_detach(struct device * self,int flags)19407829fe8Sbmercer exehci_detach(struct device *self, int flags)
19507829fe8Sbmercer {
19607829fe8Sbmercer 	struct exehci_softc *sc = (struct exehci_softc *)self;
197fe1242bbSkettenis 	int rv;
19807829fe8Sbmercer 
19907829fe8Sbmercer 	rv = ehci_detach(self, flags);
20007829fe8Sbmercer 	if (rv)
201fe1242bbSkettenis 		return rv;
20207829fe8Sbmercer 
20307829fe8Sbmercer 	if (sc->sc_ih != NULL) {
20407829fe8Sbmercer 		arm_intr_disestablish(sc->sc_ih);
20507829fe8Sbmercer 		sc->sc_ih = NULL;
20607829fe8Sbmercer 	}
20707829fe8Sbmercer 
208fe1242bbSkettenis 	if (sc->sc.sc_size) {
209fe1242bbSkettenis 		bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size);
210fe1242bbSkettenis 		sc->sc.sc_size = 0;
21107829fe8Sbmercer 	}
21207829fe8Sbmercer 
21307829fe8Sbmercer 	return 0;
21407829fe8Sbmercer }
21507829fe8Sbmercer 
21607829fe8Sbmercer void
exehci_setup(struct exehci_softc * sc)21707829fe8Sbmercer exehci_setup(struct exehci_softc *sc)
21807829fe8Sbmercer {
21954a31d3eSkettenis 	struct regmap *pmurm, *sysrm;
22054a31d3eSkettenis 	uint32_t pmureg, sysreg;
221196d040eSkettenis 	bus_size_t offset;
22207829fe8Sbmercer 	uint32_t val;
223196d040eSkettenis 	int node;
22407829fe8Sbmercer 
225fe1242bbSkettenis #if 0
22607829fe8Sbmercer 	/* VBUS, GPIO_X11, only on SMDK5250 and Chromebooks */
22707829fe8Sbmercer 	exgpio_set_dir(0xa9, EXGPIO_DIR_OUT);
22807829fe8Sbmercer 	exgpio_set_bit(0xa9);
22907829fe8Sbmercer 	delay(3000);
230fe1242bbSkettenis #endif
23107829fe8Sbmercer 
23254a31d3eSkettenis 	/* Enable host mode. */
23354a31d3eSkettenis 	sysreg = OF_getpropint(sc->sc_phy, "samsung,sysreg-phandle", 0);
23454a31d3eSkettenis 	sysrm = regmap_byphandle(sysreg);
23554a31d3eSkettenis 	if (sysrm) {
23654a31d3eSkettenis 		val = regmap_read_4(sysrm, USB20PHY_CFG);
23754a31d3eSkettenis 		val |= USB20PHY_CFG_HOST_LINK_EN;
23854a31d3eSkettenis 		regmap_write_4(sysrm, USB20PHY_CFG, val);
23954a31d3eSkettenis 	}
240196d040eSkettenis 
241196d040eSkettenis 	/* Power up the PHY block. */
242196d040eSkettenis 	pmureg = OF_getpropint(sc->sc_phy, "samsung,pmureg-phandle", 0);
243196d040eSkettenis 	pmurm = regmap_byphandle(pmureg);
244196d040eSkettenis 	if (pmurm) {
245196d040eSkettenis 		node = OF_getnodebyphandle(pmureg);
246196d040eSkettenis 		if (OF_is_compatible(node, "samsung,exynos5250-pmu"))
247196d040eSkettenis 			offset = USB_HOST_POWER_5250;
248196d040eSkettenis 		else
249196d040eSkettenis 			offset = USB_HOST_POWER_54XX;
250196d040eSkettenis 
251196d040eSkettenis 		val = regmap_read_4(pmurm, offset);
252196d040eSkettenis 		val |= USB_HOST_POWER_EN;
253196d040eSkettenis 		regmap_write_4(pmurm, offset, val);
254196d040eSkettenis 	}
25507829fe8Sbmercer 
25607829fe8Sbmercer 	delay(10000);
25707829fe8Sbmercer 
25807829fe8Sbmercer 	/* Setting up host and device simultaneously */
259fe1242bbSkettenis 	val = bus_space_read_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL0);
26007829fe8Sbmercer 	val &= ~(HOST_CTRL0_FSEL_MASK |
26107829fe8Sbmercer 		 /* HOST Phy setting */
26207829fe8Sbmercer 		 HOST_CTRL0_PHYSWRST |
26307829fe8Sbmercer 		 HOST_CTRL0_PHYSWRSTALL |
26407829fe8Sbmercer 		 HOST_CTRL0_SIDDQ |
26507829fe8Sbmercer 		 HOST_CTRL0_FORCESUSPEND |
26607829fe8Sbmercer 		 HOST_CTRL0_FORCESLEEP);
26707829fe8Sbmercer 	val |= (/* Setting up the ref freq */
26807829fe8Sbmercer 		 CLK_24MHZ << 16 |
269fe1242bbSkettenis 		 HOST_CTRL0_COMMONON_N |
27007829fe8Sbmercer 		 /* HOST Phy setting */
27107829fe8Sbmercer 		 HOST_CTRL0_LINKSWRST |
27207829fe8Sbmercer 		 HOST_CTRL0_UTMISWRST);
273fe1242bbSkettenis 	bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL0, val);
27407829fe8Sbmercer 	delay(10000);
275fe1242bbSkettenis 	bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL0,
276fe1242bbSkettenis 	    bus_space_read_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL0) &
27707829fe8Sbmercer 		~(HOST_CTRL0_LINKSWRST | HOST_CTRL0_UTMISWRST));
27807829fe8Sbmercer 	delay(20000);
27907829fe8Sbmercer 
28007829fe8Sbmercer 	/* EHCI Ctrl setting */
281fe1242bbSkettenis 	bus_space_write_4(sc->sc.iot, sc->ph_ioh, EHCI_CTRL,
282fe1242bbSkettenis 	    bus_space_read_4(sc->sc.iot, sc->ph_ioh, EHCI_CTRL) |
28307829fe8Sbmercer 		EHCI_CTRL_ENAINCRXALIGN |
28407829fe8Sbmercer 		EHCI_CTRL_ENAINCR4 |
28507829fe8Sbmercer 		EHCI_CTRL_ENAINCR8 |
28607829fe8Sbmercer 		EHCI_CTRL_ENAINCR16);
28707829fe8Sbmercer 
288fe1242bbSkettenis #if 0
28907829fe8Sbmercer 	/* HSIC USB Hub initialization. */
29007829fe8Sbmercer 	if (1) {
29107829fe8Sbmercer 		exgpio_set_dir(0xc8, EXGPIO_DIR_OUT);
29207829fe8Sbmercer 		exgpio_clear_bit(0xc8);
29307829fe8Sbmercer 		delay(1000);
29407829fe8Sbmercer 		exgpio_set_bit(0xc8);
29507829fe8Sbmercer 		delay(5000);
29607829fe8Sbmercer 
297fe1242bbSkettenis 		val = bus_space_read_4(sc->sc.iot, sc->ph_ioh, HSICPHY_CTRL1);
29807829fe8Sbmercer 		val &= ~(HOST_CTRL0_SIDDQ |
29907829fe8Sbmercer 			 HOST_CTRL0_FORCESLEEP |
30007829fe8Sbmercer 			 HOST_CTRL0_FORCESUSPEND);
301fe1242bbSkettenis 		bus_space_write_4(sc->sc.iot, sc->ph_ioh, HSICPHY_CTRL1, val);
30207829fe8Sbmercer 		val |= HOST_CTRL0_PHYSWRST;
303fe1242bbSkettenis 		bus_space_write_4(sc->sc.iot, sc->ph_ioh, HSICPHY_CTRL1, val);
30407829fe8Sbmercer 		delay(1000);
30507829fe8Sbmercer 		val &= ~HOST_CTRL0_PHYSWRST;
306fe1242bbSkettenis 		bus_space_write_4(sc->sc.iot, sc->ph_ioh, HSICPHY_CTRL1, val);
30707829fe8Sbmercer 	}
308fe1242bbSkettenis #endif
30907829fe8Sbmercer 
31007829fe8Sbmercer 	/* PHY clock and power setup time */
31107829fe8Sbmercer 	delay(50000);
31207829fe8Sbmercer }
313