xref: /netbsd-src/sys/arch/arm/nxp/imx6_usb.c (revision 1b53f8867012b6c9584d633cbb812d3af7e001ea)
1*1b53f886Sbouyer /*	$NetBSD: imx6_usb.c,v 1.8 2023/05/04 17:09:45 bouyer Exp $	*/
28644267aSskrll 
38644267aSskrll /*-
48644267aSskrll  * Copyright (c) 2019 Genetec Corporation.  All rights reserved.
58644267aSskrll  * Written by Hashimoto Kenichi for Genetec Corporation.
68644267aSskrll  *
78644267aSskrll  * Redistribution and use in source and binary forms, with or without
88644267aSskrll  * modification, are permitted provided that the following conditions
98644267aSskrll  * are met:
108644267aSskrll  * 1. Redistributions of source code must retain the above copyright
118644267aSskrll  *    notice, this list of conditions and the following disclaimer.
128644267aSskrll  * 2. Redistributions in binary form must reproduce the above copyright
138644267aSskrll  *    notice, this list of conditions and the following disclaimer in the
148644267aSskrll  *    documentation and/or other materials provided with the distribution.
158644267aSskrll  *
168644267aSskrll  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
178644267aSskrll  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
188644267aSskrll  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
198644267aSskrll  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
208644267aSskrll  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
218644267aSskrll  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
228644267aSskrll  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
238644267aSskrll  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
248644267aSskrll  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
258644267aSskrll  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
268644267aSskrll  * SUCH DAMAGE.
278644267aSskrll  */
288644267aSskrll 
298644267aSskrll #include <sys/cdefs.h>
30*1b53f886Sbouyer __KERNEL_RCSID(0, "$NetBSD: imx6_usb.c,v 1.8 2023/05/04 17:09:45 bouyer Exp $");
318644267aSskrll 
328644267aSskrll #include "opt_fdt.h"
338644267aSskrll 
348644267aSskrll #include <locators.h>
358644267aSskrll 
368644267aSskrll #include <sys/param.h>
378644267aSskrll #include <sys/systm.h>
388644267aSskrll #include <sys/conf.h>
398644267aSskrll #include <sys/kernel.h>
408644267aSskrll #include <sys/device.h>
418644267aSskrll #include <sys/intr.h>
428644267aSskrll #include <sys/bus.h>
438644267aSskrll 
448644267aSskrll #include <dev/usb/usb.h>
458644267aSskrll #include <dev/usb/usbdi.h>
468644267aSskrll #include <dev/usb/usbdivar.h>
478644267aSskrll #include <dev/usb/usb_mem.h>
488644267aSskrll 
498644267aSskrll #include <dev/usb/ehcireg.h>
508644267aSskrll #include <dev/usb/ehcivar.h>
518644267aSskrll 
528644267aSskrll #include <arm/nxp/imx6_usbreg.h>
538644267aSskrll #include <arm/imx/imxusbvar.h>
548644267aSskrll 
558644267aSskrll #include <dev/fdt/fdtvar.h>
568644267aSskrll 
578644267aSskrll struct imxusbc_fdt_softc {
588644267aSskrll 	struct imxusbc_softc sc_imxusbc; /* Must be first */
598644267aSskrll 
608644267aSskrll 	int sc_phandle;
618644267aSskrll };
628644267aSskrll 
638644267aSskrll static int imx6_usb_match(device_t, struct cfdata *, void *);
648644267aSskrll static void imx6_usb_attach(device_t, device_t, void *);
658644267aSskrll static int imx6_usb_init_clocks(struct imxusbc_softc *);
66*1b53f886Sbouyer static void imx6_usb_init(struct imxehci_softc *, uintptr_t);
678644267aSskrll static void init_otg(struct imxehci_softc *);
688644267aSskrll static void init_h1(struct imxehci_softc *);
698644267aSskrll static int imxusbc_print(void *, const char *);
70*1b53f886Sbouyer static void *imx6_usb_intr_establish(struct imxehci_softc *, uintptr_t);
718644267aSskrll 
728644267aSskrll /* attach structures */
738644267aSskrll CFATTACH_DECL_NEW(imxusbc_fdt, sizeof(struct imxusbc_fdt_softc),
748644267aSskrll     imx6_usb_match, imx6_usb_attach, NULL, NULL);
758644267aSskrll 
766e54367aSthorpej static const struct device_compatible_entry compat_data[] = {
77*1b53f886Sbouyer 	{ .compat = "fsl,imx6q-usb", .value = 1 },
78*1b53f886Sbouyer 	{ .compat = "fsl,imx6sx-usb", .value = 2 },
79*1b53f886Sbouyer 	{ .compat = "fsl,imx7d-usb", .value = 1 },
806e54367aSthorpej 	DEVICE_COMPAT_EOL
818644267aSskrll };
828644267aSskrll 
838644267aSskrll static int
imx6_usb_match(device_t parent,cfdata_t cf,void * aux)848644267aSskrll imx6_usb_match(device_t parent, cfdata_t cf, void *aux)
858644267aSskrll {
868644267aSskrll 	struct fdt_attach_args * const faa = aux;
878644267aSskrll 
886e54367aSthorpej 	return of_compatible_match(faa->faa_phandle, compat_data);
898644267aSskrll }
908644267aSskrll 
918644267aSskrll static void
imx6_usb_attach(device_t parent,device_t self,void * aux)928644267aSskrll imx6_usb_attach(device_t parent, device_t self, void *aux)
938644267aSskrll {
948644267aSskrll 	struct imxusbc_fdt_softc *ifsc = device_private(self);
958644267aSskrll 	struct imxusbc_softc *sc = &ifsc->sc_imxusbc;
968644267aSskrll 	struct fdt_attach_args * const faa = aux;
978644267aSskrll 	const int phandle = faa->faa_phandle;
988644267aSskrll 	bus_space_tag_t bst = faa->faa_bst;
998644267aSskrll 	bus_space_handle_t bsh;
1008644267aSskrll 	bus_addr_t addr;
1018644267aSskrll 	bus_size_t size;
1028644267aSskrll 	int error;
103*1b53f886Sbouyer 	struct fdtbus_regulator *reg;
1048644267aSskrll 
1058644267aSskrll 	aprint_naive("\n");
1068644267aSskrll 	aprint_normal("\n");
1078644267aSskrll 
1088644267aSskrll 	ifsc->sc_phandle = phandle;
1098644267aSskrll 
1108644267aSskrll 	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
1118644267aSskrll 		aprint_error(": couldn't get imxusbc registers\n");
1128644267aSskrll 		return;
1138644267aSskrll 	}
1148644267aSskrll 
1158644267aSskrll 	error = bus_space_map(bst, addr, size, 0, &bsh);
1168644267aSskrll 	if (error) {
1178644267aSskrll 		aprint_error(": couldn't map %#" PRIxBUSADDR ": %d", addr, error);
1188644267aSskrll 		return;
1198644267aSskrll 	}
1208644267aSskrll 
1218644267aSskrll 	sc->sc_clk = fdtbus_clock_get_index(phandle, 0);
1228644267aSskrll 	if (sc->sc_clk == NULL) {
1238644267aSskrll 		aprint_error(": couldn't get clock\n");
1248644267aSskrll 		return;
1258644267aSskrll 	}
1268644267aSskrll 
1278644267aSskrll 	sc->sc_init_md_hook = imx6_usb_init;
1288644267aSskrll 	sc->sc_intr_establish_md_hook = imx6_usb_intr_establish;
1298644267aSskrll 	sc->sc_setup_md_hook = NULL;
130*1b53f886Sbouyer 	sc->sc_md_hook_data = of_compatible_lookup(phandle, compat_data)->value;
1318644267aSskrll 
1328644267aSskrll 	sc->sc_dev = self;
1338644267aSskrll 	sc->sc_iot = bst;
1348644267aSskrll 	sc->sc_ioh = bsh;
1358644267aSskrll 	sc->sc_ehci_size = size;
1368644267aSskrll 	sc->sc_ehci_offset = 0;
1378644267aSskrll 
1388644267aSskrll 	struct fdt_phandle_data data;
1398644267aSskrll 	error = fdtbus_get_phandle_with_data(phandle, "fsl,usbmisc",
1408644267aSskrll 	    "#index-cells", 0, &data);
1418644267aSskrll 	if (error) {
1428644267aSskrll 		aprint_error(": couldn't get usbmisc property\n");
1438644267aSskrll 		return;
1448644267aSskrll 	}
1458644267aSskrll 	int unit = be32toh(data.values[0]);
1468644267aSskrll 
1478644267aSskrll 	if (fdtbus_get_reg(data.phandle, 0, &addr, &size) != 0) {
1488644267aSskrll 		aprint_error(": couldn't get usbmisc registers\n");
1498644267aSskrll 		return;
1508644267aSskrll 	}
1518644267aSskrll 	error = bus_space_map(bst, addr, size, 0, &bsh);
1528644267aSskrll 	if (error) {
1538644267aSskrll 		aprint_error(": couldn't map usbmisc registers: %d\n", error);
1548644267aSskrll 		return;
1558644267aSskrll 	}
1568644267aSskrll 	sc->sc_ioh_usbnc = bsh;
1578644267aSskrll 
1588644267aSskrll 	if (imx6_usb_init_clocks(sc) != 0) {
1598644267aSskrll 		aprint_error_dev(self, "couldn't init clocks\n");
1608644267aSskrll 		return;
1618644267aSskrll 	}
1628644267aSskrll 
1638644267aSskrll 	/* attach OTG/EHCI host controllers */
1648644267aSskrll 	struct imxusbc_attach_args iaa;
1658644267aSskrll 	iaa.aa_iot = sc->sc_iot;
1668644267aSskrll 	iaa.aa_ioh = sc->sc_ioh;
1678644267aSskrll 	iaa.aa_dmat = faa->faa_dmat;
1688644267aSskrll 	iaa.aa_unit = unit;
1698644267aSskrll 	iaa.aa_irq = IMXUSBCCF_IRQ_DEFAULT;
1702685996bSthorpej 	config_found(self, &iaa, imxusbc_print,
171c7fb772bSthorpej 	    CFARGS_NONE);
1728644267aSskrll 
173*1b53f886Sbouyer 	if (of_hasprop(phandle, "vbus-supply")) {
174*1b53f886Sbouyer 		reg = fdtbus_regulator_acquire(phandle, "vbus-supply");
175*1b53f886Sbouyer 		if (reg == NULL) {
176*1b53f886Sbouyer 			aprint_error_dev(self,
177*1b53f886Sbouyer 			    "couldn't acquire vbus-supply\n");
178*1b53f886Sbouyer 		} else {
179*1b53f886Sbouyer 			error = fdtbus_regulator_enable(reg);
180*1b53f886Sbouyer 			if (error != 0) {
181*1b53f886Sbouyer 				aprint_error_dev(self,
182*1b53f886Sbouyer 				    "couldn't enable vbus-supply\n");
183*1b53f886Sbouyer 			}
184*1b53f886Sbouyer 		}
185*1b53f886Sbouyer 	} else {
186*1b53f886Sbouyer 		aprint_verbose_dev(self, "no regulator\n");
187*1b53f886Sbouyer 	}
1888644267aSskrll 	return;
1898644267aSskrll }
1908644267aSskrll 
1918644267aSskrll static int
imxusbc_print(void * aux,const char * name __unused)1928644267aSskrll imxusbc_print(void *aux, const char *name __unused)
1938644267aSskrll {
1948644267aSskrll 	struct imxusbc_attach_args *iaa;
1958644267aSskrll 
1968644267aSskrll 	iaa = (struct imxusbc_attach_args *)aux;
1978644267aSskrll 
198de187dd6Sskrll 	aprint_normal(" unit %d", iaa->aa_unit);
1998644267aSskrll 	return UNCONF;
2008644267aSskrll }
2018644267aSskrll 
2028644267aSskrll 
2038644267aSskrll static int
imx6_usb_init_clocks(struct imxusbc_softc * sc)2048644267aSskrll imx6_usb_init_clocks(struct imxusbc_softc *sc)
2058644267aSskrll {
2068644267aSskrll 	int error;
2078644267aSskrll 
2088644267aSskrll 	error = clk_enable(sc->sc_clk);
2098644267aSskrll 	if (error) {
2108644267aSskrll 		aprint_error_dev(sc->sc_dev, "couldn't enable: %d\n", error);
2118644267aSskrll 		return error;
2128644267aSskrll 	}
2138644267aSskrll 
2148644267aSskrll 	return 0;
2158644267aSskrll }
2168644267aSskrll 
2178644267aSskrll static void
imx6_usb_init(struct imxehci_softc * sc,uintptr_t data)218*1b53f886Sbouyer imx6_usb_init(struct imxehci_softc *sc, uintptr_t data)
2198644267aSskrll {
220*1b53f886Sbouyer 	int notg = data;
221*1b53f886Sbouyer 
2228644267aSskrll 	switch (sc->sc_unit) {
2238644267aSskrll 	case 0:	/* OTG controller */
2248644267aSskrll 		init_otg(sc);
2258644267aSskrll 		break;
2268644267aSskrll 	case 1:	/* EHCI Host 1 */
227*1b53f886Sbouyer 		if (notg >= 2)
228*1b53f886Sbouyer 			init_otg(sc);
229*1b53f886Sbouyer 		else
2308644267aSskrll 			init_h1(sc);
2318644267aSskrll 		break;
2328644267aSskrll 	case 2:	/* EHCI Host 2 */
2338644267aSskrll 	case 3:	/* EHCI Host 3 */
2348644267aSskrll 	default:
2358644267aSskrll 		aprint_error_dev(sc->sc_dev, "unit %d not supported\n",
2368644267aSskrll 		    sc->sc_unit);
2378644267aSskrll 	}
2388644267aSskrll }
2398644267aSskrll 
2408644267aSskrll static void
init_otg(struct imxehci_softc * sc)2418644267aSskrll init_otg(struct imxehci_softc *sc)
2428644267aSskrll {
2438644267aSskrll 	struct imxusbc_softc *usbc = sc->sc_usbc;
2448644267aSskrll 	uint32_t v;
2458644267aSskrll 
2468644267aSskrll 	sc->sc_iftype = IMXUSBC_IF_UTMI_WIDE;
2478644267aSskrll 
2488644267aSskrll 	imxehci_reset(sc);
2498644267aSskrll 
2508644267aSskrll 	v = bus_space_read_4(usbc->sc_iot, usbc->sc_ioh_usbnc, USBNC_USB_OTG_CTRL);
2518644267aSskrll 	v |= USBNC_USB_OTG_CTRL_WKUP_VBUS_EN;
2528644267aSskrll 	v |= USBNC_USB_OTG_CTRL_OVER_CUR_DIS;
2538644267aSskrll 	v |= USBNC_USB_OTG_CTRL_PWR_POL;
2548644267aSskrll 	v &= ~USBNC_USB_OTG_CTRL_UTMI_ON_CLOCK;
2558644267aSskrll 	bus_space_write_4(usbc->sc_iot, usbc->sc_ioh_usbnc, USBNC_USB_OTG_CTRL, v);
2568644267aSskrll }
2578644267aSskrll 
2588644267aSskrll static void
init_h1(struct imxehci_softc * sc)2598644267aSskrll init_h1(struct imxehci_softc *sc)
2608644267aSskrll {
2618644267aSskrll 	struct imxusbc_softc *usbc = sc->sc_usbc;
2628644267aSskrll 	uint32_t v;
2638644267aSskrll 
2648644267aSskrll 	sc->sc_iftype = IMXUSBC_IF_UTMI_WIDE;
2658644267aSskrll 
2668644267aSskrll 	v = bus_space_read_4(usbc->sc_iot, usbc->sc_ioh_usbnc, USBNC_USB_UH1_CTRL);
2678644267aSskrll 	v |= USBNC_USB_UH1_CTRL_OVER_CUR_POL;
2688644267aSskrll 	v |= USBNC_USB_UH1_CTRL_OVER_CUR_DIS;
2698644267aSskrll 	bus_space_write_4(usbc->sc_iot, usbc->sc_ioh_usbnc, USBNC_USB_UH1_CTRL, v);
2708644267aSskrll 
2718644267aSskrll 	/* do reset */
2728644267aSskrll 	imxehci_reset(sc);
2738644267aSskrll 
2748644267aSskrll 	/* set mode */
2758644267aSskrll 	v = bus_space_read_4(usbc->sc_iot, usbc->sc_ioh, USBC_UH1_USBMODE);
2768644267aSskrll 	v &= ~USBC_UH_USBMODE_CM;
2778644267aSskrll 	v |= __SHIFTIN(USBC_UH_USBMODE_CM, USBC_UH_USBMODE_CM_HOST_CONTROLLER);
2788644267aSskrll 	bus_space_write_4(usbc->sc_iot, usbc->sc_ioh, USBC_UH1_USBMODE, v);
2798644267aSskrll }
2808644267aSskrll 
2818644267aSskrll static void *
imx6_usb_intr_establish(struct imxehci_softc * sc,uintptr_t data)282*1b53f886Sbouyer imx6_usb_intr_establish(struct imxehci_softc *sc, uintptr_t data)
2838644267aSskrll {
2848644267aSskrll 	struct imxusbc_fdt_softc *ifsc = (struct imxusbc_fdt_softc *)sc->sc_usbc;
2858644267aSskrll 	ehci_softc_t *hsc = &sc->sc_hsc;
2868644267aSskrll 	void *ih;
2878644267aSskrll 
2888644267aSskrll 	char intrstr[128];
2898644267aSskrll 	if (!fdtbus_intr_str(ifsc->sc_phandle, 0, intrstr, sizeof(intrstr))) {
2908644267aSskrll 		aprint_error_dev(sc->sc_dev, "failed to decode interrupt\n");
2918644267aSskrll 		return NULL;
2928644267aSskrll 	}
29382b8374aSjmcneill 	ih = fdtbus_intr_establish_xname(ifsc->sc_phandle, 0, IPL_USB,
29482b8374aSjmcneill 	    FDT_INTR_MPSAFE, ehci_intr, hsc, device_xname(sc->sc_dev));
2958644267aSskrll 	if (ih == NULL) {
2968644267aSskrll 		aprint_error_dev(sc->sc_dev, "failed to establish interrupt on %s\n",
2978644267aSskrll 		    intrstr);
2988644267aSskrll 		return NULL;
2998644267aSskrll 	}
3008644267aSskrll 	aprint_normal_dev(sc->sc_dev, "interrupting on %s\n", intrstr);
3018644267aSskrll 
3028644267aSskrll 	return ih;
3038644267aSskrll }
304