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