2 * Copyright 2014-2015 John Wehle <john@feith.com>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * Amlogic aml8726-m6 (and later) USB physical layer driver.
30 * Each USB physical interface has a dedicated register block.
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
36 #include <sys/param.h>
37 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/module.h>
43 #include <sys/mutex.h>
44 #include <sys/resource.h>
49 #include <machine/bus.h>
50 #include <machine/cpu.h>
52 #include <dev/ofw/ofw_bus.h>
53 #include <dev/ofw/ofw_bus_subr.h>
55 #include <arm/amlogic/aml8726/aml8726_soc.h>
59 struct aml8726_usb_phy_gpio {
65 struct aml8726_usb_phy_softc {
67 struct resource *res[1];
69 struct aml8726_usb_phy_gpio *pwr_en;
71 struct aml8726_usb_phy_gpio hub_rst;
74 static struct resource_spec aml8726_usb_phy_spec[] = {
75 { SYS_RES_MEMORY, 0, RF_ACTIVE },
79 #define AML_USB_PHY_CFG_REG 0
80 #define AML_USB_PHY_CFG_CLK_SEL_32K_ALT (1 << 15)
81 #define AML_USB_PHY_CFG_CLK_DIV_MASK (0x7f << 4)
82 #define AML_USB_PHY_CFG_CLK_DIV_SHIFT 4
83 #define AML_USB_PHY_CFG_CLK_SEL_MASK (7 << 1)
84 #define AML_USB_PHY_CFG_CLK_SEL_XTAL (0 << 1)
85 #define AML_USB_PHY_CFG_CLK_SEL_XTAL_DIV2 (1 << 1)
86 #define AML_USB_PHY_CFG_CLK_EN (1 << 0)
88 #define AML_USB_PHY_CTRL_REG 4
89 #define AML_USB_PHY_CTRL_FSEL_MASK (7 << 22)
90 #define AML_USB_PHY_CTRL_FSEL_12M (2 << 22)
91 #define AML_USB_PHY_CTRL_FSEL_24M (5 << 22)
92 #define AML_USB_PHY_CTRL_POR (1 << 15)
93 #define AML_USB_PHY_CTRL_CLK_DETECTED (1 << 8)
95 #define AML_USB_PHY_ADP_BC_REG 12
96 #define AML_USB_PHY_ADP_BC_ACA_FLOATING (1 << 26)
97 #define AML_USB_PHY_ADP_BC_ACA_EN (1 << 16)
99 #define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val))
100 #define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], reg)
101 #define CSR_BARRIER(sc, reg) bus_barrier((sc)->res[0], reg, 4, \
102 (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
104 #define PIN_ON_FLAG(pol) ((pol) == 0 ? \
105 GPIO_PIN_LOW : GPIO_PIN_HIGH)
106 #define PIN_OFF_FLAG(pol) ((pol) == 0 ? \
107 GPIO_PIN_HIGH : GPIO_PIN_LOW)
110 aml8726_usb_phy_probe(device_t dev)
113 if (!ofw_bus_status_okay(dev))
116 if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-m6-usb-phy") &&
117 !ofw_bus_is_compatible(dev, "amlogic,aml8726-m8-usb-phy"))
120 switch (aml8726_soc_hw_rev) {
121 case AML_SOC_HW_REV_M8:
122 case AML_SOC_HW_REV_M8B:
123 device_set_desc(dev, "Amlogic aml8726-m8 USB PHY");
126 device_set_desc(dev, "Amlogic aml8726-m6 USB PHY");
130 return (BUS_PROBE_DEFAULT);
134 aml8726_usb_phy_attach(device_t dev)
136 struct aml8726_usb_phy_softc *sc = device_get_softc(dev);
149 if (bus_alloc_resources(dev, aml8726_usb_phy_spec, sc->res)) {
150 device_printf(dev, "can not allocate resources for device\n");
154 node = ofw_bus_get_node(dev);
156 len = OF_getprop_alloc(node, "force-aca",
157 sizeof(char), (void **)&force_aca);
159 sc->force_aca = FALSE;
162 if (strncmp(force_aca, "true", len) == 0)
163 sc->force_aca = TRUE;
166 OF_prop_free(force_aca);
170 len = OF_getencprop_alloc(node, "usb-pwr-en",
171 3 * sizeof(pcell_t), (void **)&prop);
172 npwr_en = (len > 0) ? len : 0;
175 sc->pwr_en = (struct aml8726_usb_phy_gpio *)
176 malloc(npwr_en * sizeof (*sc->pwr_en), M_DEVBUF, M_WAITOK);
178 for (i = 0; i < npwr_en; i++) {
179 sc->pwr_en[i].dev = OF_device_from_xref(prop[i * 3]);
180 sc->pwr_en[i].pin = prop[i * 3 + 1];
181 sc->pwr_en[i].pol = prop[i * 3 + 2];
183 if (sc->pwr_en[i].dev == NULL) {
191 len = OF_getencprop_alloc(node, "usb-hub-rst",
192 3 * sizeof(pcell_t), (void **)&prop);
194 sc->hub_rst.dev = OF_device_from_xref(prop[0]);
195 sc->hub_rst.pin = prop[1];
196 sc->hub_rst.pol = prop[2];
198 if (len > 1 || sc->hub_rst.dev == NULL)
205 device_printf(dev, "unable to parse gpio\n");
209 /* Turn on power by setting pin and then enabling output driver. */
210 for (i = 0; i < npwr_en; i++) {
211 if (GPIO_PIN_SET(sc->pwr_en[i].dev, sc->pwr_en[i].pin,
212 PIN_ON_FLAG(sc->pwr_en[i].pol)) != 0 ||
213 GPIO_PIN_SETFLAGS(sc->pwr_en[i].dev, sc->pwr_en[i].pin,
214 GPIO_PIN_OUTPUT) != 0) {
216 "could not use gpio to control power\n");
224 * Configure the clock source and divider.
227 value = CSR_READ_4(sc, AML_USB_PHY_CFG_REG);
229 value &= ~(AML_USB_PHY_CFG_CLK_SEL_32K_ALT |
230 AML_USB_PHY_CFG_CLK_DIV_MASK |
231 AML_USB_PHY_CFG_CLK_SEL_MASK |
232 AML_USB_PHY_CFG_CLK_EN);
234 switch (aml8726_soc_hw_rev) {
235 case AML_SOC_HW_REV_M8:
236 case AML_SOC_HW_REV_M8B:
237 value |= AML_USB_PHY_CFG_CLK_SEL_32K_ALT;
241 value |= AML_USB_PHY_CFG_CLK_SEL_XTAL;
242 value |= ((div - 1) << AML_USB_PHY_CFG_CLK_DIV_SHIFT) &
243 AML_USB_PHY_CFG_CLK_DIV_MASK;
244 value |= AML_USB_PHY_CFG_CLK_EN;
248 CSR_WRITE_4(sc, AML_USB_PHY_CFG_REG, value);
250 CSR_BARRIER(sc, AML_USB_PHY_CFG_REG);
253 * Configure the clock frequency and issue a power on reset.
256 value = CSR_READ_4(sc, AML_USB_PHY_CTRL_REG);
258 value &= ~AML_USB_PHY_CTRL_FSEL_MASK;
260 switch (aml8726_soc_hw_rev) {
261 case AML_SOC_HW_REV_M8:
262 case AML_SOC_HW_REV_M8B:
263 value |= AML_USB_PHY_CTRL_FSEL_24M;
266 value |= AML_USB_PHY_CTRL_FSEL_12M;
270 value |= AML_USB_PHY_CTRL_POR;
272 CSR_WRITE_4(sc, AML_USB_PHY_CTRL_REG, value);
274 CSR_BARRIER(sc, AML_USB_PHY_CTRL_REG);
279 * Enable by clearing the power on reset.
282 value &= ~AML_USB_PHY_CTRL_POR;
284 CSR_WRITE_4(sc, AML_USB_PHY_CTRL_REG, value);
286 CSR_BARRIER(sc, AML_USB_PHY_CTRL_REG);
291 * Check if the clock was detected.
293 value = CSR_READ_4(sc, AML_USB_PHY_CTRL_REG);
294 if ((value & AML_USB_PHY_CTRL_CLK_DETECTED) == 0)
295 device_printf(dev, "PHY Clock not detected\n");
298 * If necessary enabled Accessory Charger Adaptor detection
299 * so that the port knows what mode to operate in.
302 value = CSR_READ_4(sc, AML_USB_PHY_ADP_BC_REG);
304 value |= AML_USB_PHY_ADP_BC_ACA_EN;
306 CSR_WRITE_4(sc, AML_USB_PHY_ADP_BC_REG, value);
308 CSR_BARRIER(sc, AML_USB_PHY_ADP_BC_REG);
312 value = CSR_READ_4(sc, AML_USB_PHY_ADP_BC_REG);
314 if ((value & AML_USB_PHY_ADP_BC_ACA_FLOATING) != 0) {
316 "force-aca requires newer silicon\n");
324 if (sc->hub_rst.dev != NULL) {
327 if (GPIO_PIN_SET(sc->hub_rst.dev, sc->hub_rst.pin,
328 PIN_ON_FLAG(sc->hub_rst.pol)) != 0 ||
329 GPIO_PIN_SETFLAGS(sc->hub_rst.dev, sc->hub_rst.pin,
330 GPIO_PIN_OUTPUT) != 0)
335 if (GPIO_PIN_SET(sc->hub_rst.dev, sc->hub_rst.pin,
336 PIN_OFF_FLAG(sc->hub_rst.pol)) != 0)
343 "could not use gpio to reset hub\n");
351 /* In the event of problems attempt to turn things back off. */
354 GPIO_PIN_SET(sc->pwr_en[i].dev, sc->pwr_en[i].pin,
355 PIN_OFF_FLAG(sc->pwr_en[i].pol));
358 free (sc->pwr_en, M_DEVBUF);
361 bus_release_resources(dev, aml8726_usb_phy_spec, sc->res);
367 aml8726_usb_phy_detach(device_t dev)
369 struct aml8726_usb_phy_softc *sc = device_get_softc(dev);
374 * Disable by issuing a power on reset.
377 value = CSR_READ_4(sc, AML_USB_PHY_CTRL_REG);
379 value |= AML_USB_PHY_CTRL_POR;
381 CSR_WRITE_4(sc, AML_USB_PHY_CTRL_REG, value);
383 CSR_BARRIER(sc, AML_USB_PHY_CTRL_REG);
388 GPIO_PIN_SET(sc->pwr_en[i].dev, sc->pwr_en[i].pin,
389 PIN_OFF_FLAG(sc->pwr_en[i].pol));
391 free (sc->pwr_en, M_DEVBUF);
394 bus_release_resources(dev, aml8726_usb_phy_spec, sc->res);
399 static device_method_t aml8726_usb_phy_methods[] = {
400 /* Device interface */
401 DEVMETHOD(device_probe, aml8726_usb_phy_probe),
402 DEVMETHOD(device_attach, aml8726_usb_phy_attach),
403 DEVMETHOD(device_detach, aml8726_usb_phy_detach),
408 static driver_t aml8726_usb_phy_driver = {
410 aml8726_usb_phy_methods,
411 sizeof(struct aml8726_usb_phy_softc),
414 static devclass_t aml8726_usb_phy_devclass;
416 DRIVER_MODULE(aml8726_m6usbphy, simplebus, aml8726_usb_phy_driver,
417 aml8726_usb_phy_devclass, 0, 0);
418 MODULE_DEPEND(aml8726_m6usbphy, aml8726_gpio, 1, 1, 1);