]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm/freescale/imx/imx6_usbphy.c
MFV r344878:
[FreeBSD/FreeBSD.git] / sys / arm / freescale / imx / imx6_usbphy.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2013 Ian Lepore <ian@freebsd.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 /*
33  * USBPHY driver for Freescale i.MX6 family of SoCs.
34  */
35
36 #include "opt_bus.h"
37
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/module.h>
42 #include <sys/bus.h>
43 #include <sys/rman.h>
44
45 #include <dev/ofw/ofw_bus.h>
46 #include <dev/ofw/ofw_bus_subr.h>
47
48 #include <machine/bus.h>
49
50 #include <arm/freescale/imx/imx_ccmvar.h>
51 #include <arm/freescale/imx/imx6_anatopreg.h>
52 #include <arm/freescale/imx/imx6_anatopvar.h>
53
54 /*
55  * Hardware register defines.
56  */
57 #define PWD_REG                         0x0000
58 #define CTRL_STATUS_REG                 0x0030
59 #define CTRL_SET_REG                    0x0034
60 #define CTRL_CLR_REG                    0x0038
61 #define CTRL_TOGGLE_REG                 0x003c
62 #define   CTRL_SFTRST                     (1U << 31)
63 #define   CTRL_CLKGATE                    (1 << 30)
64 #define   CTRL_ENUTMILEVEL3               (1 << 15)
65 #define   CTRL_ENUTMILEVEL2               (1 << 14)
66
67 struct usbphy_softc {
68         device_t        dev;
69         struct resource *mem_res;
70         u_int           phy_num;
71 };
72
73 static struct ofw_compat_data compat_data[] = {
74         {"fsl,imx6q-usbphy",    true},
75         {"fsl,imx6ul-usbphy",   true},
76         {NULL,                  false}
77 };
78
79 static int
80 usbphy_detach(device_t dev)
81 {
82         struct usbphy_softc *sc;
83
84         sc = device_get_softc(dev);
85
86         if (sc->mem_res != NULL)
87                 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
88
89         return (0);
90 }
91
92 static int
93 usbphy_attach(device_t dev)
94 {
95         struct usbphy_softc *sc;
96         int err, regoff, rid;
97
98         sc = device_get_softc(dev);
99         err = 0;
100
101         /* Allocate bus_space resources. */
102         rid = 0;
103         sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
104             RF_ACTIVE);
105         if (sc->mem_res == NULL) {
106                 device_printf(dev, "Cannot allocate memory resources\n");
107                 err = ENXIO;
108                 goto out;
109         }
110
111         /*
112          * XXX Totally lame way to get the unit number (but not quite as lame as
113          * adding an ad-hoc property to the fdt data).  This works as long as
114          * this driver is used for imx6 only.
115          */
116         const uint32_t PWD_PHY1_REG_PHYSADDR = 0x020c9000;
117         if (BUS_SPACE_PHYSADDR(sc->mem_res, 0) == PWD_PHY1_REG_PHYSADDR) {
118                 sc->phy_num = 0;
119                 regoff = 0;
120         } else {
121                 sc->phy_num = 1;
122                 regoff = 0x60;
123         }
124
125         /*
126          * Based on a note in the u-boot source code, disable charger detection
127          * to avoid degrading the differential signaling on the DP line.  Note
128          * that this disables (by design) both charger detection and contact
129          * detection, because of the screwball mix of active-high and active-low
130          * bits in this register.
131          */
132         imx6_anatop_write_4(IMX6_ANALOG_USB1_CHRG_DETECT + regoff, 
133             IMX6_ANALOG_USB_CHRG_DETECT_N_ENABLE | 
134             IMX6_ANALOG_USB_CHRG_DETECT_N_CHK_CHRG);
135
136         imx6_anatop_write_4(IMX6_ANALOG_USB1_CHRG_DETECT + regoff, 
137             IMX6_ANALOG_USB_CHRG_DETECT_N_ENABLE | 
138             IMX6_ANALOG_USB_CHRG_DETECT_N_CHK_CHRG);
139
140         /* XXX Configure the overcurrent detection here. */
141
142         /*
143          * Turn on the phy clocks.
144          */
145         imx_ccm_usbphy_enable(dev);
146
147         /*
148          * Set the software reset bit, then clear both it and the clock gate bit
149          * to bring the device out of reset with the clock running.
150          */
151         bus_write_4(sc->mem_res, CTRL_SET_REG, CTRL_SFTRST);
152         bus_write_4(sc->mem_res, CTRL_CLR_REG, CTRL_SFTRST | CTRL_CLKGATE);
153
154         /* Set UTMI+ level 2+3 bits to enable low and full speed devices. */
155         bus_write_4(sc->mem_res, CTRL_SET_REG,
156             CTRL_ENUTMILEVEL2 | CTRL_ENUTMILEVEL3);
157
158         /* Power up: clear all bits in the powerdown register. */
159         bus_write_4(sc->mem_res, PWD_REG, 0);
160
161         err = 0;
162
163 out:
164
165         if (err != 0)
166                 usbphy_detach(dev);
167
168         return (err);
169 }
170
171 static int
172 usbphy_probe(device_t dev)
173 {
174
175         if (!ofw_bus_status_okay(dev))
176                 return (ENXIO);
177
178         if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
179                 return (ENXIO);
180
181         device_set_desc(dev, "Freescale i.MX6 USB PHY");
182
183         return (BUS_PROBE_DEFAULT);
184 }
185
186 static device_method_t usbphy_methods[] = {
187         /* Device interface */
188         DEVMETHOD(device_probe,  usbphy_probe),
189         DEVMETHOD(device_attach, usbphy_attach),
190         DEVMETHOD(device_detach, usbphy_detach),
191
192         DEVMETHOD_END
193 };
194
195 static driver_t usbphy_driver = {
196         "usbphy",
197         usbphy_methods,
198         sizeof(struct usbphy_softc)
199 };
200
201 static devclass_t usbphy_devclass;
202
203 /*
204  * This driver needs to start before the ehci driver, but later than the usual
205  * "special" drivers like clocks and cpu.  Ehci starts at DEFAULT so SUPPORTDEV
206  * is where this driver fits most.
207  */
208 EARLY_DRIVER_MODULE(usbphy, simplebus, usbphy_driver, usbphy_devclass, 0, 0,
209     BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE);
210