]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm/amlogic/aml8726/aml8726_usb_phy-m6.c
Import lua 5.3.4 to contrib
[FreeBSD/FreeBSD.git] / sys / arm / amlogic / aml8726 / aml8726_usb_phy-m6.c
1 /*-
2  * Copyright 2014-2015 John Wehle <john@feith.com>
3  * All rights reserved.
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 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
24  * SUCH DAMAGE.
25  */
26
27 /*
28  * Amlogic aml8726-m6 (and later) USB physical layer driver.
29  *
30  * Each USB physical interface has a dedicated register block.
31  */
32
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/conf.h>
39 #include <sys/bus.h>
40 #include <sys/kernel.h>
41 #include <sys/module.h>
42 #include <sys/lock.h>
43 #include <sys/mutex.h>
44 #include <sys/resource.h>
45 #include <sys/rman.h>
46
47 #include <sys/gpio.h>
48
49 #include <machine/bus.h>
50 #include <machine/cpu.h>
51
52 #include <dev/ofw/ofw_bus.h>
53 #include <dev/ofw/ofw_bus_subr.h>
54
55 #include <arm/amlogic/aml8726/aml8726_soc.h>
56
57 #include "gpio_if.h"
58
59 struct aml8726_usb_phy_gpio {
60         device_t        dev;
61         uint32_t        pin;
62         uint32_t        pol;
63 };
64
65 struct aml8726_usb_phy_softc {
66         device_t                        dev;
67         struct resource                 *res[1];
68         uint32_t                        npwr_en;
69         struct aml8726_usb_phy_gpio     *pwr_en;
70         boolean_t                       force_aca;
71         struct aml8726_usb_phy_gpio     hub_rst;
72 };
73
74 static struct resource_spec aml8726_usb_phy_spec[] = {
75         { SYS_RES_MEMORY,       0,      RF_ACTIVE },
76         { -1, 0 }
77 };
78
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)
87
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)
94
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)
98
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))
103
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)
108
109 static int
110 aml8726_usb_phy_probe(device_t dev)
111 {
112
113         if (!ofw_bus_status_okay(dev))
114                 return (ENXIO);
115
116         if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-m6-usb-phy") &&
117             !ofw_bus_is_compatible(dev, "amlogic,aml8726-m8-usb-phy"))
118                 return (ENXIO);
119
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");
124                 break;
125         default:
126                 device_set_desc(dev, "Amlogic aml8726-m6 USB PHY");
127                 break;
128         }
129
130         return (BUS_PROBE_DEFAULT);
131 }
132
133 static int
134 aml8726_usb_phy_attach(device_t dev)
135 {
136         struct aml8726_usb_phy_softc *sc = device_get_softc(dev);
137         char *force_aca;
138         int err;
139         int npwr_en;
140         pcell_t *prop;
141         phandle_t node;
142         ssize_t len;
143         uint32_t div;
144         uint32_t i;
145         uint32_t value;
146
147         sc->dev = dev;
148
149         if (bus_alloc_resources(dev, aml8726_usb_phy_spec, sc->res)) {
150                 device_printf(dev, "can not allocate resources for device\n");
151                 return (ENXIO);
152         }
153
154         node = ofw_bus_get_node(dev);
155
156         len = OF_getprop_alloc(node, "force-aca",
157             sizeof(char), (void **)&force_aca);
158
159         sc->force_aca = FALSE;
160
161         if (len > 0) {
162                 if (strncmp(force_aca, "true", len) == 0)
163                         sc->force_aca = TRUE;
164         }
165
166         OF_prop_free(force_aca);
167
168         err = 0;
169
170         len = OF_getencprop_alloc(node, "usb-pwr-en",
171             3 * sizeof(pcell_t), (void **)&prop);
172         npwr_en = (len > 0) ? len : 0;
173
174         sc->npwr_en = 0;
175         sc->pwr_en = (struct aml8726_usb_phy_gpio *)
176             malloc(npwr_en * sizeof (*sc->pwr_en), M_DEVBUF, M_WAITOK);
177
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];
182
183                 if (sc->pwr_en[i].dev == NULL) {
184                         err = 1;
185                         break;
186                 }
187         }
188
189         OF_prop_free(prop);
190
191         len = OF_getencprop_alloc(node, "usb-hub-rst",
192             3 * sizeof(pcell_t), (void **)&prop);
193         if (len > 0) {
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];
197
198                 if (len > 1 || sc->hub_rst.dev == NULL)
199                         err = 1;
200         }
201
202         OF_prop_free(prop);
203
204         if (err) {
205                 device_printf(dev, "unable to parse gpio\n");
206                 goto fail;
207         }
208
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) {
215                         device_printf(dev,
216                             "could not use gpio to control power\n");
217                         goto fail;
218                 }
219
220                 sc->npwr_en++;
221         }
222
223         /*
224          * Configure the clock source and divider.
225          */
226
227         value = CSR_READ_4(sc, AML_USB_PHY_CFG_REG);
228
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);
233
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;
238                 break;
239         default:
240                 div = 2;
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;
245                 break;
246         }
247
248         CSR_WRITE_4(sc, AML_USB_PHY_CFG_REG, value);
249
250         CSR_BARRIER(sc, AML_USB_PHY_CFG_REG);
251
252         /*
253          * Configure the clock frequency and issue a power on reset.
254          */
255
256         value = CSR_READ_4(sc, AML_USB_PHY_CTRL_REG);
257
258         value &= ~AML_USB_PHY_CTRL_FSEL_MASK;
259
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;
264                 break;
265         default:
266                 value |= AML_USB_PHY_CTRL_FSEL_12M;
267                 break;
268         }
269
270         value |= AML_USB_PHY_CTRL_POR;
271
272         CSR_WRITE_4(sc, AML_USB_PHY_CTRL_REG, value);
273
274         CSR_BARRIER(sc, AML_USB_PHY_CTRL_REG);
275
276         DELAY(500);
277
278         /*
279          * Enable by clearing the power on reset.
280          */
281
282         value &= ~AML_USB_PHY_CTRL_POR;
283
284         CSR_WRITE_4(sc, AML_USB_PHY_CTRL_REG, value);
285
286         CSR_BARRIER(sc, AML_USB_PHY_CTRL_REG);
287
288         DELAY(1000);
289
290         /*
291          * Check if the clock was detected.
292          */
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");
296
297         /*
298          * If necessary enabled Accessory Charger Adaptor detection
299          * so that the port knows what mode to operate in.
300          */
301         if (sc->force_aca) {
302                 value = CSR_READ_4(sc, AML_USB_PHY_ADP_BC_REG);
303
304                 value |= AML_USB_PHY_ADP_BC_ACA_EN;
305
306                 CSR_WRITE_4(sc, AML_USB_PHY_ADP_BC_REG, value);
307
308                 CSR_BARRIER(sc, AML_USB_PHY_ADP_BC_REG);
309
310                 DELAY(50);
311
312                 value = CSR_READ_4(sc, AML_USB_PHY_ADP_BC_REG);
313
314                 if ((value & AML_USB_PHY_ADP_BC_ACA_FLOATING) != 0) {
315                         device_printf(dev,
316                             "force-aca requires newer silicon\n");
317                         goto fail;
318                 }
319         }
320
321         /*
322          * Reset the hub.
323          */
324         if (sc->hub_rst.dev != NULL) {
325                 err = 0;
326
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)
331                         err = 1;
332
333                 DELAY(30);
334
335                 if (GPIO_PIN_SET(sc->hub_rst.dev, sc->hub_rst.pin,
336                     PIN_OFF_FLAG(sc->hub_rst.pol)) != 0)
337                         err = 1;
338
339                 DELAY(60000);
340
341                 if (err) {
342                         device_printf(dev,
343                             "could not use gpio to reset hub\n");
344                         goto fail;
345                 }
346         }
347
348         return (0);
349
350 fail:
351         /* In the event of problems attempt to turn things back off. */
352         i = sc->npwr_en;
353         while (i-- != 0) {
354                 GPIO_PIN_SET(sc->pwr_en[i].dev, sc->pwr_en[i].pin,
355                     PIN_OFF_FLAG(sc->pwr_en[i].pol));
356         }
357
358         free (sc->pwr_en, M_DEVBUF);
359         sc->pwr_en = NULL;
360
361         bus_release_resources(dev, aml8726_usb_phy_spec, sc->res);
362
363         return (ENXIO);
364 }
365
366 static int
367 aml8726_usb_phy_detach(device_t dev)
368 {
369         struct aml8726_usb_phy_softc *sc = device_get_softc(dev);
370         uint32_t i;
371         uint32_t value;
372
373         /*
374          * Disable by issuing a power on reset.
375          */
376
377         value = CSR_READ_4(sc, AML_USB_PHY_CTRL_REG);
378
379         value |= AML_USB_PHY_CTRL_POR;
380
381         CSR_WRITE_4(sc, AML_USB_PHY_CTRL_REG, value);
382
383         CSR_BARRIER(sc, AML_USB_PHY_CTRL_REG);
384
385         /* Turn off power */
386         i = sc->npwr_en;
387         while (i-- != 0) {
388                 GPIO_PIN_SET(sc->pwr_en[i].dev, sc->pwr_en[i].pin,
389                     PIN_OFF_FLAG(sc->pwr_en[i].pol));
390         }
391         free (sc->pwr_en, M_DEVBUF);
392         sc->pwr_en = NULL;
393
394         bus_release_resources(dev, aml8726_usb_phy_spec, sc->res);
395
396         return (0);
397 }
398
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),
404
405         DEVMETHOD_END
406 };
407
408 static driver_t aml8726_usb_phy_driver = {
409         "usbphy",
410         aml8726_usb_phy_methods,
411         sizeof(struct aml8726_usb_phy_softc),
412 };
413
414 static devclass_t aml8726_usb_phy_devclass;
415
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);