xref: /netbsd-src/sys/arch/mips/ingenic/ingenic_ehci.c (revision c7fb772b85b2b5d4cfb282f868f454b4701534fd)
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