1*c7fb772bSthorpej /* $NetBSD: ingenic_ehci.c,v 1.8 2021/08/07 16:18:59 thorpej Exp $ */
2e3ba4f1cSmacallan
3e3ba4f1cSmacallan /*-
4e3ba4f1cSmacallan * Copyright (c) 2015 Michael Lorenz
5e3ba4f1cSmacallan * All rights reserved.
6e3ba4f1cSmacallan *
7e3ba4f1cSmacallan * Redistribution and use in source and binary forms, with or without
8e3ba4f1cSmacallan * modification, are permitted provided that the following conditions
9e3ba4f1cSmacallan * are met:
10e3ba4f1cSmacallan * 1. Redistributions of source code must retain the above copyright
11e3ba4f1cSmacallan * notice, this list of conditions and the following disclaimer.
12e3ba4f1cSmacallan * 2. Redistributions in binary form must reproduce the above copyright
13e3ba4f1cSmacallan * notice, this list of conditions and the following disclaimer in the
14e3ba4f1cSmacallan * documentation and/or other materials provided with the distribution.
15e3ba4f1cSmacallan *
16e3ba4f1cSmacallan * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17e3ba4f1cSmacallan * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18e3ba4f1cSmacallan * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19e3ba4f1cSmacallan * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20e3ba4f1cSmacallan * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21e3ba4f1cSmacallan * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22e3ba4f1cSmacallan * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23e3ba4f1cSmacallan * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24e3ba4f1cSmacallan * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25e3ba4f1cSmacallan * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26e3ba4f1cSmacallan * POSSIBILITY OF SUCH DAMAGE.
27e3ba4f1cSmacallan */
28e3ba4f1cSmacallan
29e3ba4f1cSmacallan #include <sys/cdefs.h>
30*c7fb772bSthorpej __KERNEL_RCSID(0, "$NetBSD: ingenic_ehci.c,v 1.8 2021/08/07 16:18:59 thorpej Exp $");
31e3ba4f1cSmacallan
32e3ba4f1cSmacallan #include <sys/param.h>
33e3ba4f1cSmacallan #include <sys/systm.h>
34e3ba4f1cSmacallan #include <sys/device.h>
35e3ba4f1cSmacallan #include <sys/mutex.h>
36e3ba4f1cSmacallan #include <sys/bus.h>
37e3ba4f1cSmacallan #include <sys/workqueue.h>
38e3ba4f1cSmacallan
39e3ba4f1cSmacallan #include <mips/ingenic/ingenic_var.h>
40e3ba4f1cSmacallan #include <mips/ingenic/ingenic_regs.h>
41e3ba4f1cSmacallan
42e3ba4f1cSmacallan #include <dev/usb/usb.h>
43e3ba4f1cSmacallan #include <dev/usb/usbdi.h>
44e3ba4f1cSmacallan #include <dev/usb/usbdivar.h>
45e3ba4f1cSmacallan #include <dev/usb/usb_mem.h>
46e3ba4f1cSmacallan
47e3ba4f1cSmacallan #include <dev/usb/ehcireg.h>
48e3ba4f1cSmacallan #include <dev/usb/ehcivar.h>
49e3ba4f1cSmacallan
504f8ab635Smacallan #include <dev/usb/usbdevs.h>
514f8ab635Smacallan
52e3ba4f1cSmacallan #include "opt_ingenic.h"
53e3ba4f1cSmacallan #include "ohci.h"
54e3ba4f1cSmacallan
55e3ba4f1cSmacallan static int ingenic_ehci_match(device_t, struct cfdata *, void *);
56e3ba4f1cSmacallan static void ingenic_ehci_attach(device_t, device_t, void *);
57e3ba4f1cSmacallan
58e3ba4f1cSmacallan CFATTACH_DECL_NEW(ingenic_ehci, sizeof(struct ehci_softc),
59e3ba4f1cSmacallan ingenic_ehci_match, ingenic_ehci_attach, NULL, NULL);
60e3ba4f1cSmacallan
61e3ba4f1cSmacallan #if NOHCI > 0
62e3ba4f1cSmacallan extern device_t ingenic_ohci;
63e3ba4f1cSmacallan #endif
64e3ba4f1cSmacallan
65e3ba4f1cSmacallan /* ARGSUSED */
66e3ba4f1cSmacallan static int
ingenic_ehci_match(device_t parent,struct cfdata * match,void * aux)67e3ba4f1cSmacallan ingenic_ehci_match(device_t parent, struct cfdata *match, void *aux)
68e3ba4f1cSmacallan {
69e3ba4f1cSmacallan struct apbus_attach_args *aa = aux;
70e3ba4f1cSmacallan
71e3ba4f1cSmacallan if (strcmp(aa->aa_name, "ehci") != 0)
72e3ba4f1cSmacallan return 0;
73e3ba4f1cSmacallan
74e3ba4f1cSmacallan return 1;
75e3ba4f1cSmacallan }
76e3ba4f1cSmacallan
771ae1c233Smacallan static int
ingenic_ehci_enable(struct ehci_softc * sc)781ae1c233Smacallan ingenic_ehci_enable(struct ehci_softc *sc)
791ae1c233Smacallan {
801ae1c233Smacallan uint32_t reg;
811ae1c233Smacallan
821ae1c233Smacallan /* Togle VBUS pin */
831ae1c233Smacallan gpio_set(5, 15, 0);
841ae1c233Smacallan delay(250000);
851ae1c233Smacallan gpio_set(5, 15, 1);
861ae1c233Smacallan delay(250000);
871ae1c233Smacallan
881ae1c233Smacallan /* Enable OTG, should not be necessary since we use PLL clock */
891ae1c233Smacallan reg = readreg(JZ_USBPCR);
901ae1c233Smacallan reg &= ~(PCR_OTG_DISABLE);
911ae1c233Smacallan writereg(JZ_USBPCR, reg);
921ae1c233Smacallan
931ae1c233Smacallan /* Select CORE as PLL reference */
941ae1c233Smacallan reg = readreg(JZ_USBPCR1);
951ae1c233Smacallan reg |= PCR_REFCLK_CORE;
961ae1c233Smacallan writereg(JZ_USBPCR1, reg);
971ae1c233Smacallan
981ae1c233Smacallan /* Configure OTG PHY clock frequency */
991ae1c233Smacallan reg = readreg(JZ_USBPCR1);
1001ae1c233Smacallan reg &= ~PCR_CLK_M;
1011ae1c233Smacallan reg |= PCR_CLK_48;
1021ae1c233Smacallan writereg(JZ_USBPCR1, reg);
1031ae1c233Smacallan
1041ae1c233Smacallan /* Do not force port1 to suspend mode */
1051ae1c233Smacallan reg = readreg(JZ_OPCR);
1061ae1c233Smacallan reg |= OPCR_SPENDN1;
1071ae1c233Smacallan writereg(JZ_OPCR, reg);
1081ae1c233Smacallan
1091ae1c233Smacallan /* D- pulldown */
1101ae1c233Smacallan reg = readreg(JZ_USBPCR1);
1111ae1c233Smacallan reg |= PCR_DMPD1;
1121ae1c233Smacallan writereg(JZ_USBPCR1, reg);
1131ae1c233Smacallan
1141ae1c233Smacallan /* D+ pulldown */
1151ae1c233Smacallan reg = readreg(JZ_USBPCR1);
1161ae1c233Smacallan reg |= PCR_DPPD1;
1171ae1c233Smacallan writereg(JZ_USBPCR1, reg);
1181ae1c233Smacallan
1191ae1c233Smacallan /* 16 bit bus witdth for port 1 (and 0) */
1201ae1c233Smacallan reg = readreg(JZ_USBPCR1);
1211ae1c233Smacallan reg |= PCR_WORD_I_F1 | PCR_WORD_I_F0;
1221ae1c233Smacallan writereg(JZ_USBPCR1, reg);
1231ae1c233Smacallan
1241ae1c233Smacallan /* Reset USB */
1251ae1c233Smacallan reg = readreg(JZ_USBPCR);
1261ae1c233Smacallan reg |= PCR_POR;
1271ae1c233Smacallan writereg(JZ_USBPCR, reg);
1281ae1c233Smacallan delay(1);
1291ae1c233Smacallan reg = readreg(JZ_USBPCR);
1301ae1c233Smacallan reg &= ~(PCR_POR);
1311ae1c233Smacallan writereg(JZ_USBPCR, reg);
1321ae1c233Smacallan
1331ae1c233Smacallan /* Soft-reset USB */
1341ae1c233Smacallan reg = readreg(JZ_SRBC);
1351ae1c233Smacallan reg |= (1 << 14);
1361ae1c233Smacallan writereg(JZ_SRBC, reg);
1371ae1c233Smacallan /* 300ms */
1381ae1c233Smacallan delay(300000);
1391ae1c233Smacallan
1401ae1c233Smacallan reg = readreg(JZ_SRBC);
1411ae1c233Smacallan reg &= ~(1 << 14);
1421ae1c233Smacallan writereg(JZ_SRBC, reg);
1431ae1c233Smacallan
1441ae1c233Smacallan /* 300ms */
1451ae1c233Smacallan delay(300000);
1461ae1c233Smacallan
1471ae1c233Smacallan return (0);
1481ae1c233Smacallan }
1491ae1c233Smacallan
150e3ba4f1cSmacallan /* ARGSUSED */
151e3ba4f1cSmacallan static void
ingenic_ehci_attach(device_t parent,device_t self,void * aux)152e3ba4f1cSmacallan ingenic_ehci_attach(device_t parent, device_t self, void *aux)
153e3ba4f1cSmacallan {
154e3ba4f1cSmacallan struct ehci_softc *sc = device_private(self);
155e3ba4f1cSmacallan struct apbus_attach_args *aa = aux;
156e3ba4f1cSmacallan void *ih;
1574e8e6643Sskrll int error;
158e3ba4f1cSmacallan uint32_t reg;
159e3ba4f1cSmacallan
160e3ba4f1cSmacallan sc->sc_dev = self;
161e3ba4f1cSmacallan
162e3ba4f1cSmacallan sc->iot = aa->aa_bst;
1634e8e6643Sskrll sc->sc_bus.ub_dmatag = aa->aa_dmat;
1644e8e6643Sskrll sc->sc_bus.ub_hcpriv = sc;
165e3ba4f1cSmacallan sc->sc_size = 0x1000;
1664e8e6643Sskrll sc->sc_bus.ub_revision = USBREV_2_0;
167e3ba4f1cSmacallan
168e3ba4f1cSmacallan if (aa->aa_addr == 0)
169e3ba4f1cSmacallan aa->aa_addr = JZ_EHCI_BASE;
170e3ba4f1cSmacallan
171e3ba4f1cSmacallan error = bus_space_map(aa->aa_bst, aa->aa_addr, 0x1000, 0, &sc->ioh);
172e3ba4f1cSmacallan if (error) {
173e3ba4f1cSmacallan aprint_error_dev(self,
174e3ba4f1cSmacallan "can't map registers for %s: %d\n", aa->aa_name, error);
175e3ba4f1cSmacallan return;
176e3ba4f1cSmacallan }
177e3ba4f1cSmacallan
178e3ba4f1cSmacallan aprint_naive(": EHCI USB controller\n");
179e3ba4f1cSmacallan aprint_normal(": EHCI USB controller\n");
180e3ba4f1cSmacallan
1811ae1c233Smacallan ingenic_ehci_enable(sc);
182e3ba4f1cSmacallan
183e3ba4f1cSmacallan /* Disable EHCI interrupts */
184e3ba4f1cSmacallan bus_space_write_4(sc->iot, sc->ioh, EHCI_USBINTR, 0);
185e3ba4f1cSmacallan
186613f5923Smacallan ih = evbmips_intr_establish(aa->aa_irq, ehci_intr, sc);
187e3ba4f1cSmacallan
188e3ba4f1cSmacallan if (ih == NULL) {
189e3ba4f1cSmacallan aprint_error_dev(self, "failed to establish interrupt %d\n",
190613f5923Smacallan aa->aa_irq);
191e3ba4f1cSmacallan goto fail;
192e3ba4f1cSmacallan }
193e3ba4f1cSmacallan
194e3ba4f1cSmacallan #if NOHCI > 0
195e3ba4f1cSmacallan if (ingenic_ohci != NULL) {
196e3ba4f1cSmacallan sc->sc_ncomp = 1;
197e3ba4f1cSmacallan sc->sc_comps[0] = ingenic_ohci;
198e3ba4f1cSmacallan } else
199e3ba4f1cSmacallan sc->sc_ncomp = 0;
200e3ba4f1cSmacallan #else
201e3ba4f1cSmacallan sc->sc_ncomp = 0;
2021ae1c233Smacallan sc->sc_npcomp = 0;
203e3ba4f1cSmacallan #endif
204e3ba4f1cSmacallan
2054e8e6643Sskrll error = ehci_init(sc);
2064e8e6643Sskrll if (error) {
2074e8e6643Sskrll aprint_error_dev(self, "init failed, error=%d\n", error);
208e3ba4f1cSmacallan goto fail;
209e3ba4f1cSmacallan }
210e3ba4f1cSmacallan
2111ae1c233Smacallan /*
2121ae1c233Smacallan * voodoo from the linux driver:
2131ae1c233Smacallan * select utmi data bus width of controller to 16bit
2141ae1c233Smacallan */
2151ae1c233Smacallan reg = bus_space_read_4(sc->iot, sc->ioh, 0xb0);
2161ae1c233Smacallan reg |= 1 << 6;
2171ae1c233Smacallan bus_space_write_4(sc->iot, sc->ioh, 0xb0, reg);
2181ae1c233Smacallan
219e3ba4f1cSmacallan /* Attach USB device */
220*c7fb772bSthorpej sc->sc_child = config_found(self, &sc->sc_bus, usbctlprint, CFARGS_NONE);
221e3ba4f1cSmacallan
222e3ba4f1cSmacallan return;
223e3ba4f1cSmacallan
224e3ba4f1cSmacallan fail:
225e3ba4f1cSmacallan if (ih) {
226e3ba4f1cSmacallan evbmips_intr_disestablish(ih);
227e3ba4f1cSmacallan }
228e3ba4f1cSmacallan bus_space_unmap(sc->iot, sc->ioh, 0x1000);
229e3ba4f1cSmacallan }
230