1/*
2 * Copyright (c) 2010  Genetec Corporation.  All rights reserved.
3 * Written by Hiroyuki Bessho for Genetec Corporation.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY GENETEC CORPORATION ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
16 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL GENETEC CORPORATION
18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 *
26 */
27#include <sys/cdefs.h>
28__KERNEL_RCSID(0, "$NetBSD: netwalker_usb.c,v 1.9 2024/02/22 23:16:10 andvar Exp $");
29
30#include "locators.h"
31
32#define	_INTR_PRIVATE
33
34#include <sys/param.h>
35#include <sys/systm.h>
36#include <sys/conf.h>
37#include <sys/kernel.h>
38#include <sys/device.h>
39#include <sys/intr.h>
40#include <sys/bus.h>
41#include <sys/gpio.h>
42
43#include <dev/usb/usb.h>
44#include <dev/usb/usbdi.h>
45#include <dev/usb/usbdivar.h>
46#include <dev/usb/usb_mem.h>
47
48#include <dev/usb/ehcireg.h>
49#include <dev/usb/ehcivar.h>
50
51#include <arm/imx/imx51reg.h>
52#include <arm/imx/imx51var.h>
53#include <arm/imx/imxusbreg.h>
54#include <arm/imx/imxusbvar.h>
55#include <arm/imx/imx51_iomuxreg.h>
56#include <arm/imx/imxgpiovar.h>
57
58struct netwalker_usbc_softc {
59	struct imxusbc_softc sc_imxusbc; /* Must be first */
60};
61
62static int	imxusbc_match(device_t, cfdata_t, void *);
63static void	imxusbc_attach(device_t, device_t, void *);
64static void	netwalker_usb_init(struct imxehci_softc *, uintptr_t);
65
66static void	init_otg(struct imxehci_softc *);
67static void	init_h1(struct imxehci_softc *);
68
69extern const struct iomux_conf iomux_usb1_config[];
70
71/* attach structures */
72CFATTACH_DECL_NEW(imxusbc_axi, sizeof(struct netwalker_usbc_softc),
73    imxusbc_match, imxusbc_attach, NULL, NULL);
74
75static int
76imxusbc_match(device_t parent, cfdata_t cf, void *aux)
77{
78	struct axi_attach_args *aa = aux;
79
80	if (aa->aa_addr == USBOH3_BASE)
81		return 1;
82
83	return 0;
84}
85
86static void
87imxusbc_attach(device_t parent, device_t self, void *aux)
88{
89	struct imxusbc_softc *sc = device_private(self);
90	struct axi_attach_args *aa = aux;
91
92	aprint_naive("\n");
93	aprint_normal(": Universal Serial Bus Controller\n");
94
95	if (aa->aa_size == AXICF_SIZE_DEFAULT)
96		aa->aa_size = USBOH3_SIZE;
97
98	sc->sc_init_md_hook = netwalker_usb_init;
99	sc->sc_intr_establish_md_hook = NULL;
100	sc->sc_setup_md_hook = NULL;
101
102	imxusbc_attach_common(parent, self, aa->aa_iot, aa->aa_addr, aa->aa_size);
103}
104
105static void
106netwalker_usb_init(struct imxehci_softc *sc, uintptr_t data)
107{
108	switch (sc->sc_unit) {
109	case 0:	/* OTG controller */
110		init_otg(sc);
111		break;
112	case 1:	/* EHCI Host 1 */
113		init_h1(sc);
114		break;
115	default:
116		aprint_error_dev(sc->sc_hsc.sc_dev, "unit %d not supported\n",
117		    sc->sc_unit);
118	}
119}
120
121static void
122init_otg(struct imxehci_softc *sc)
123{
124	struct imxusbc_softc *usbc = sc->sc_usbc;
125	uint32_t reg;
126
127	sc->sc_iftype = IMXUSBC_IF_UTMI;
128
129	imxehci_reset(sc);
130
131	reg = bus_space_read_4(usbc->sc_iot, usbc->sc_ioh, USBOH3_PHYCTRL0);
132	reg |= PHYCTRL0_OTG_OVER_CUR_DIS;
133	bus_space_write_4(usbc->sc_iot, usbc->sc_ioh, USBOH3_PHYCTRL0, reg);
134
135	reg = bus_space_read_4(usbc->sc_iot, usbc->sc_ioh, USBOH3_USBCTRL);
136	reg &= ~(USBCTRL_OWIR|USBCTRL_OPM);
137	bus_space_write_4(usbc->sc_iot, usbc->sc_ioh, USBOH3_USBCTRL, reg);
138
139	reg = bus_space_read_4(usbc->sc_iot, usbc->sc_ioh, USBOH3_PHYCTRL1);
140	reg = (reg & ~PHYCTRL1_PLLDIVVALUE_MASK) | PHYCTRL1_PLLDIVVALUE_24MHZ;
141	bus_space_write_4(usbc->sc_iot, usbc->sc_ioh, USBOH3_PHYCTRL1, reg);
142}
143
144static void
145init_h1(struct imxehci_softc *sc)
146{
147	struct imxusbc_softc *usbc = sc->sc_usbc;
148	uint32_t reg;
149
150	/* output HIGH to USBH1_STP */
151	imxgpio_data_write(GPIO_NO(1, 27), GPIO_PIN_HIGH);
152	imxgpio_set_direction(GPIO_NO(1, 27), GPIO_PIN_OUTPUT);
153
154	iomux_mux_config(iomux_usb1_config);
155
156	delay(100 * 1000);
157
158	/* XXX enable USB clock */
159
160	imxehci_reset(sc);
161
162	/* select external clock for Host 1 */
163	reg = bus_space_read_4(usbc->sc_iot, usbc->sc_ioh,
164			       USBOH3_USBCTRL1);
165	reg |= USBCTRL1_UH1_EXT_CLK_EN;
166	bus_space_write_4(usbc->sc_iot, usbc->sc_ioh,
167			  USBOH3_USBCTRL1, reg);
168
169
170	/* select ULPI interface for Host 1 */
171	sc->sc_iftype = IMXUSBC_IF_ULPI;
172
173	reg = bus_space_read_4(usbc->sc_iot, usbc->sc_ioh,
174			       USBOH3_USBCTRL);
175	reg &= ~(USBCTRL_H1PM);
176	reg |= USBCTRL_H1UIE|USBCTRL_H1WIE;
177	bus_space_write_4(usbc->sc_iot, usbc->sc_ioh,
178			  USBOH3_USBCTRL, reg);
179
180	iomux_set_function(MUX_PIN(USBH1_STP), IOMUX_CONFIG_ALT0);
181
182
183	/* HUB RESET release */
184	imxgpio_data_write(GPIO_NO(1, 7), GPIO_PIN_HIGH);
185	imxgpio_set_direction(GPIO_NO(1, 7), GPIO_PIN_OUTPUT);
186
187	/* Drive 26M_OSC_EN line high 3_1 */
188	imxgpio_data_write(GPIO_NO(3, 1), GPIO_PIN_HIGH);
189	imxgpio_set_direction(GPIO_NO(3, 1), GPIO_PIN_OUTPUT);
190
191	/* Drive USB_CLK_EN_B line low  2_1 */
192	imxgpio_data_write(GPIO_NO(2, 1), GPIO_PIN_LOW);
193	imxgpio_set_direction(GPIO_NO(2, 1), GPIO_PIN_INPUT);
194
195	/* MX51_PIN_EIM_D21 - De-assert USB PHY RESETB */
196	delay(10 * 1000);
197	imxgpio_data_write(GPIO_NO(2, 5), GPIO_PIN_HIGH);
198	imxgpio_set_direction(GPIO_NO(2, 5), GPIO_PIN_OUTPUT);
199	iomux_set_function(MUX_PIN(EIM_D21), IOMUX_CONFIG_ALT1);
200	delay(5 * 1000);
201}
202
203/*
204 * IOMUX setting for USB Host1
205 * taken from Linux driver
206 */
207const struct iomux_conf iomux_usb1_config[] = {
208
209	{
210		/* Initially setup this pin for GPIO, and change to
211		 * USBH1_STP later */
212		.pin = MUX_PIN(USBH1_STP),
213		.mux = IOMUX_CONFIG_ALT2,
214		.pad = (PAD_CTL_SRE | PAD_CTL_DSE_HIGH |
215		    PAD_CTL_KEEPER | PAD_CTL_HYS)
216	},
217
218	{
219		/* Clock */
220		.pin = MUX_PIN(USBH1_CLK),
221		.mux = IOMUX_CONFIG_ALT0,
222		.pad = (PAD_CTL_SRE | PAD_CTL_DSE_HIGH |
223		    PAD_CTL_KEEPER | PAD_CTL_HYS)
224	},
225	{
226		/* DIR */
227		.pin = MUX_PIN(USBH1_DIR),
228		.mux = IOMUX_CONFIG_ALT0,
229		.pad = (PAD_CTL_SRE | PAD_CTL_DSE_HIGH |
230		    PAD_CTL_KEEPER | PAD_CTL_HYS)
231	},
232
233	{
234		/* NXT */
235		.pin = MUX_PIN(USBH1_NXT),
236		.mux = IOMUX_CONFIG_ALT0,
237		.pad = (PAD_CTL_SRE | PAD_CTL_DSE_HIGH |
238		    PAD_CTL_KEEPER | PAD_CTL_HYS)
239	},
240
241#define	USBH1_DATA_CONFIG(n)					\
242	{							\
243		/* DATA n */					\
244		.pin = MUX_PIN(USBH1_DATA##n),			\
245		.mux = IOMUX_CONFIG_ALT0,			\
246		.pad = (PAD_CTL_SRE | PAD_CTL_DSE_HIGH |	\
247		    PAD_CTL_KEEPER | PAD_CTL_PUS_100K_PU |	\
248		    PAD_CTL_HYS),				\
249		/* XXX: what does 100K_PU with KEEPER ? */	\
250	}
251
252	USBH1_DATA_CONFIG(0),
253	USBH1_DATA_CONFIG(1),
254	USBH1_DATA_CONFIG(2),
255	USBH1_DATA_CONFIG(3),
256	USBH1_DATA_CONFIG(4),
257	USBH1_DATA_CONFIG(5),
258	USBH1_DATA_CONFIG(6),
259	USBH1_DATA_CONFIG(7),
260
261	{
262		/* USB_CLK_EN_B  GPIO2[1]*/
263		.pin = MUX_PIN(EIM_D17),
264		.mux = IOMUX_CONFIG_ALT1,
265		.pad = (PAD_CTL_DSE_HIGH | PAD_CTL_PKE | PAD_CTL_SRE),
266	},
267
268	{
269		/* USB PHY RESETB */
270		.pin = MUX_PIN(EIM_D21),
271		.mux = IOMUX_CONFIG_ALT1,
272		.pad = (PAD_CTL_DSE_HIGH | PAD_CTL_KEEPER |
273		    PAD_CTL_PUS_100K_PU | PAD_CTL_SRE)
274	},
275	{
276		/* USB HUB RESET */
277		.pin = MUX_PIN(GPIO1_7),
278		.mux = IOMUX_CONFIG_ALT0,
279		.pad = (PAD_CTL_DSE_HIGH | PAD_CTL_SRE),
280	},
281	{
282		/* 26M_OSC pin settings */
283		.pin = MUX_PIN(DI1_PIN12),
284		.mux = IOMUX_CONFIG_ALT4,
285		.pad = (PAD_CTL_DSE_HIGH | PAD_CTL_KEEPER |
286		    PAD_CTL_SRE),
287	},
288
289	/* end of table */
290	{.pin = IOMUX_CONF_EOT}
291};
292