30 |
|
*/ |
31 |
|
|
32 |
|
#include <sys/cdefs.h> |
33 |
< |
__FBSDID("$FreeBSD: stable/10/sys/arm/allwinner/a10_ehci.c 308402 2016-11-07 09:19:04Z hselasky $"); |
33 |
> |
__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/a10_ehci.c 346524 2019-04-22 04:56:41Z ian $"); |
34 |
|
|
35 |
|
#include "opt_bus.h" |
36 |
|
|
41 |
|
#include <sys/condvar.h> |
42 |
|
#include <sys/kernel.h> |
43 |
|
#include <sys/module.h> |
44 |
– |
#include <sys/gpio.h> |
44 |
|
|
45 |
|
#include <machine/bus.h> |
46 |
< |
#include <dev/ofw/ofw_bus.h> |
46 |
> |
#include <dev/ofw/ofw_bus.h> |
47 |
|
#include <dev/ofw/ofw_bus_subr.h> |
48 |
|
|
49 |
< |
#include <dev/usb/usb.h> |
49 |
> |
#include <dev/usb/usb.h> |
50 |
|
#include <dev/usb/usbdi.h> |
51 |
|
|
52 |
|
#include <dev/usb/usb_core.h> |
59 |
|
#include <dev/usb/controller/ehci.h> |
60 |
|
#include <dev/usb/controller/ehcireg.h> |
61 |
|
|
62 |
< |
#include "gpio_if.h" |
62 |
> |
#include <arm/allwinner/aw_machdep.h> |
63 |
> |
#include <dev/extres/clk/clk.h> |
64 |
> |
#include <dev/extres/hwreset/hwreset.h> |
65 |
> |
#include <dev/extres/phy/phy.h> |
66 |
|
|
65 |
– |
#include "a10_clk.h" |
66 |
– |
|
67 |
|
#define EHCI_HC_DEVSTR "Allwinner Integrated USB 2.0 controller" |
68 |
|
|
69 |
|
#define SW_USB_PMU_IRQ_ENABLE 0x800 |
76 |
|
#define SW_AHB_INCRX_ALIGN (1 << 8) |
77 |
|
#define SW_AHB_INCR4 (1 << 9) |
78 |
|
#define SW_AHB_INCR8 (1 << 10) |
79 |
– |
#define GPIO_USB1_PWR 230 |
80 |
– |
#define GPIO_USB2_PWR 227 |
79 |
|
|
80 |
+ |
#define USB_CONF(d) \ |
81 |
+ |
(void *)ofw_bus_search_compatible((d), compat_data)->ocd_data |
82 |
+ |
|
83 |
|
#define A10_READ_4(sc, reg) \ |
84 |
|
bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, reg) |
85 |
|
|
89 |
|
static device_attach_t a10_ehci_attach; |
90 |
|
static device_detach_t a10_ehci_detach; |
91 |
|
|
92 |
< |
bs_r_1_proto(reversed); |
93 |
< |
bs_w_1_proto(reversed); |
92 |
> |
struct aw_ehci_softc { |
93 |
> |
ehci_softc_t sc; |
94 |
> |
clk_t clk; |
95 |
> |
hwreset_t rst; |
96 |
> |
phy_t phy; |
97 |
> |
}; |
98 |
|
|
99 |
+ |
struct aw_ehci_conf { |
100 |
+ |
bool sdram_init; |
101 |
+ |
}; |
102 |
+ |
|
103 |
+ |
static const struct aw_ehci_conf a10_ehci_conf = { |
104 |
+ |
.sdram_init = true, |
105 |
+ |
}; |
106 |
+ |
|
107 |
+ |
static const struct aw_ehci_conf a31_ehci_conf = { |
108 |
+ |
.sdram_init = false, |
109 |
+ |
}; |
110 |
+ |
|
111 |
+ |
static struct ofw_compat_data compat_data[] = { |
112 |
+ |
{ "allwinner,sun4i-a10-ehci", (uintptr_t)&a10_ehci_conf }, |
113 |
+ |
{ "allwinner,sun5i-a13-ehci", (uintptr_t)&a10_ehci_conf }, |
114 |
+ |
{ "allwinner,sun6i-a31-ehci", (uintptr_t)&a31_ehci_conf }, |
115 |
+ |
{ "allwinner,sun7i-a20-ehci", (uintptr_t)&a10_ehci_conf }, |
116 |
+ |
{ "allwinner,sun8i-a83t-ehci", (uintptr_t)&a31_ehci_conf }, |
117 |
+ |
{ "allwinner,sun8i-h3-ehci", (uintptr_t)&a31_ehci_conf }, |
118 |
+ |
{ NULL, (uintptr_t)NULL } |
119 |
+ |
}; |
120 |
+ |
|
121 |
|
static int |
122 |
|
a10_ehci_probe(device_t self) |
123 |
|
{ |
125 |
|
if (!ofw_bus_status_okay(self)) |
126 |
|
return (ENXIO); |
127 |
|
|
128 |
< |
if (!ofw_bus_is_compatible(self, "allwinner,usb-ehci")) |
128 |
> |
if (ofw_bus_search_compatible(self, compat_data)->ocd_data == 0) |
129 |
|
return (ENXIO); |
130 |
|
|
131 |
|
device_set_desc(self, EHCI_HC_DEVSTR); |
136 |
|
static int |
137 |
|
a10_ehci_attach(device_t self) |
138 |
|
{ |
139 |
< |
ehci_softc_t *sc = device_get_softc(self); |
139 |
> |
struct aw_ehci_softc *aw_sc = device_get_softc(self); |
140 |
> |
ehci_softc_t *sc = &aw_sc->sc; |
141 |
> |
const struct aw_ehci_conf *conf; |
142 |
|
bus_space_handle_t bsh; |
114 |
– |
device_t sc_gpio_dev; |
143 |
|
int err; |
144 |
|
int rid; |
145 |
|
uint32_t reg_value = 0; |
146 |
|
|
147 |
+ |
conf = USB_CONF(self); |
148 |
+ |
|
149 |
|
/* initialise some bus fields */ |
150 |
|
sc->sc_bus.parent = self; |
151 |
|
sc->sc_bus.devices = sc->sc_devices; |
195 |
|
|
196 |
|
sprintf(sc->sc_vendor, "Allwinner"); |
197 |
|
|
168 |
– |
/* Get the GPIO device, we need this to give power to USB */ |
169 |
– |
sc_gpio_dev = devclass_get_device(devclass_find("gpio"), 0); |
170 |
– |
if (sc_gpio_dev == NULL) { |
171 |
– |
device_printf(self, "Error: failed to get the GPIO device\n"); |
172 |
– |
goto error; |
173 |
– |
} |
174 |
– |
|
198 |
|
err = bus_setup_intr(self, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, |
199 |
|
NULL, (driver_intr_t *)ehci_interrupt, sc, &sc->sc_intr_hdl); |
200 |
|
if (err) { |
205 |
|
|
206 |
|
sc->sc_flags |= EHCI_SCFLG_DONTRESET; |
207 |
|
|
208 |
+ |
/* De-assert reset */ |
209 |
+ |
if (hwreset_get_by_ofw_idx(self, 0, 0, &aw_sc->rst) == 0) { |
210 |
+ |
err = hwreset_deassert(aw_sc->rst); |
211 |
+ |
if (err != 0) { |
212 |
+ |
device_printf(self, "Could not de-assert reset\n"); |
213 |
+ |
goto error; |
214 |
+ |
} |
215 |
+ |
} |
216 |
+ |
|
217 |
|
/* Enable clock for USB */ |
218 |
< |
a10_clk_usb_activate(); |
218 |
> |
err = clk_get_by_ofw_index(self, 0, 0, &aw_sc->clk); |
219 |
> |
if (err != 0) { |
220 |
> |
device_printf(self, "Could not get clock\n"); |
221 |
> |
goto error; |
222 |
> |
} |
223 |
> |
err = clk_enable(aw_sc->clk); |
224 |
> |
if (err != 0) { |
225 |
> |
device_printf(self, "Could not enable clock\n"); |
226 |
> |
goto error; |
227 |
> |
} |
228 |
|
|
229 |
< |
/* Give power to USB */ |
230 |
< |
GPIO_PIN_SETFLAGS(sc_gpio_dev, GPIO_USB2_PWR, GPIO_PIN_OUTPUT); |
231 |
< |
GPIO_PIN_SET(sc_gpio_dev, GPIO_USB2_PWR, GPIO_PIN_HIGH); |
229 |
> |
/* Enable USB PHY */ |
230 |
> |
err = phy_get_by_ofw_name(self, 0, "usb", &aw_sc->phy); |
231 |
> |
if (err != 0) { |
232 |
> |
device_printf(self, "Could not get phy\n"); |
233 |
> |
goto error; |
234 |
> |
} |
235 |
> |
err = phy_enable(aw_sc->phy); |
236 |
> |
if (err != 0) { |
237 |
> |
device_printf(self, "Could not enable phy\n"); |
238 |
> |
goto error; |
239 |
> |
} |
240 |
|
|
192 |
– |
/* Give power to USB */ |
193 |
– |
GPIO_PIN_SETFLAGS(sc_gpio_dev, GPIO_USB1_PWR, GPIO_PIN_OUTPUT); |
194 |
– |
GPIO_PIN_SET(sc_gpio_dev, GPIO_USB1_PWR, GPIO_PIN_HIGH); |
195 |
– |
|
241 |
|
/* Enable passby */ |
242 |
|
reg_value = A10_READ_4(sc, SW_USB_PMU_IRQ_ENABLE); |
243 |
|
reg_value |= SW_AHB_INCR8; /* AHB INCR8 enable */ |
247 |
|
A10_WRITE_4(sc, SW_USB_PMU_IRQ_ENABLE, reg_value); |
248 |
|
|
249 |
|
/* Configure port */ |
250 |
< |
reg_value = A10_READ_4(sc, SW_SDRAM_REG_HPCR_USB2); |
251 |
< |
reg_value |= SW_SDRAM_BP_HPCR_ACCESS; |
252 |
< |
A10_WRITE_4(sc, SW_SDRAM_REG_HPCR_USB2, reg_value); |
250 |
> |
if (conf->sdram_init) { |
251 |
> |
reg_value = A10_READ_4(sc, SW_SDRAM_REG_HPCR_USB2); |
252 |
> |
reg_value |= SW_SDRAM_BP_HPCR_ACCESS; |
253 |
> |
A10_WRITE_4(sc, SW_SDRAM_REG_HPCR_USB2, reg_value); |
254 |
> |
} |
255 |
|
|
256 |
|
err = ehci_init(sc); |
257 |
|
if (!err) { |
264 |
|
return (0); |
265 |
|
|
266 |
|
error: |
267 |
+ |
if (aw_sc->clk != NULL) { |
268 |
+ |
clk_disable(aw_sc->clk); |
269 |
+ |
clk_release(aw_sc->clk); |
270 |
+ |
} |
271 |
|
a10_ehci_detach(self); |
272 |
|
return (ENXIO); |
273 |
|
} |
275 |
|
static int |
276 |
|
a10_ehci_detach(device_t self) |
277 |
|
{ |
278 |
< |
ehci_softc_t *sc = device_get_softc(self); |
278 |
> |
struct aw_ehci_softc *aw_sc = device_get_softc(self); |
279 |
> |
ehci_softc_t *sc = &aw_sc->sc; |
280 |
> |
const struct aw_ehci_conf *conf; |
281 |
|
int err; |
282 |
|
uint32_t reg_value = 0; |
283 |
|
|
284 |
+ |
conf = USB_CONF(self); |
285 |
+ |
|
286 |
|
/* during module unload there are lots of children leftover */ |
287 |
|
device_delete_children(self); |
288 |
|
|
313 |
|
usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); |
314 |
|
|
315 |
|
/* Disable configure port */ |
316 |
< |
reg_value = A10_READ_4(sc, SW_SDRAM_REG_HPCR_USB2); |
317 |
< |
reg_value &= ~SW_SDRAM_BP_HPCR_ACCESS; |
318 |
< |
A10_WRITE_4(sc, SW_SDRAM_REG_HPCR_USB2, reg_value); |
316 |
> |
if (conf->sdram_init) { |
317 |
> |
reg_value = A10_READ_4(sc, SW_SDRAM_REG_HPCR_USB2); |
318 |
> |
reg_value &= ~SW_SDRAM_BP_HPCR_ACCESS; |
319 |
> |
A10_WRITE_4(sc, SW_SDRAM_REG_HPCR_USB2, reg_value); |
320 |
> |
} |
321 |
|
|
322 |
|
/* Disable passby */ |
323 |
|
reg_value = A10_READ_4(sc, SW_USB_PMU_IRQ_ENABLE); |
328 |
|
A10_WRITE_4(sc, SW_USB_PMU_IRQ_ENABLE, reg_value); |
329 |
|
|
330 |
|
/* Disable clock for USB */ |
331 |
< |
a10_clk_usb_deactivate(); |
331 |
> |
if (aw_sc->clk != NULL) { |
332 |
> |
clk_disable(aw_sc->clk); |
333 |
> |
clk_release(aw_sc->clk); |
334 |
> |
} |
335 |
|
|
336 |
+ |
/* Assert reset */ |
337 |
+ |
if (aw_sc->rst != NULL) { |
338 |
+ |
hwreset_assert(aw_sc->rst); |
339 |
+ |
hwreset_release(aw_sc->rst); |
340 |
+ |
} |
341 |
+ |
|
342 |
|
return (0); |
343 |
|
} |
344 |
|
|
357 |
|
static driver_t ehci_driver = { |
358 |
|
.name = "ehci", |
359 |
|
.methods = ehci_methods, |
360 |
< |
.size = sizeof(ehci_softc_t), |
360 |
> |
.size = sizeof(struct aw_ehci_softc), |
361 |
|
}; |
362 |
|
|
363 |
|
static devclass_t ehci_devclass; |
364 |
|
|
365 |
< |
DRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); |
366 |
< |
MODULE_DEPEND(ehci, usb, 1, 1, 1); |
365 |
> |
DRIVER_MODULE(a10_ehci, simplebus, ehci_driver, ehci_devclass, 0, 0); |
366 |
> |
MODULE_DEPEND(a10_ehci, usb, 1, 1, 1); |