exynos_usbdrdphy.c revision 1.3 1 /* $NetBSD: exynos_usbdrdphy.c,v 1.3 2021/01/18 02:35:49 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 2018 Jared McNeill <jmcneill (at) invisible.ca>
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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30
31 __KERNEL_RCSID(0, "$NetBSD: exynos_usbdrdphy.c,v 1.3 2021/01/18 02:35:49 thorpej Exp $");
32
33 #include <sys/param.h>
34 #include <sys/bus.h>
35 #include <sys/device.h>
36 #include <sys/intr.h>
37 #include <sys/systm.h>
38 #include <sys/kmem.h>
39
40 #include <dev/fdt/fdtvar.h>
41 #include <dev/fdt/syscon.h>
42
43 /*
44 * PHY Registers
45 */
46 #define PHY_LINK_SYSTEM 0x04
47 #define PHY_LINK_SYSTEM_XHCI_VERCTL __BIT(27)
48 #define PHY_LINK_SYSTEM_FLADJ __BITS(6,1)
49 #define PHY_UTMI 0x08
50 #define PHY_UTMI_OTGDISABLE __BIT(6)
51 #define PHY_CLK_RST 0x10
52 #define PHY_CLK_RST_SSC_REFCLKSEL __BITS(30,23)
53 #define PHY_CLK_RST_SSC_EN __BIT(20)
54 #define PHY_CLK_RST_REF_SSP_EN __BIT(19)
55 #define PHY_CLK_RST_MPLL_MULT __BITS(17,11)
56 #define PHY_CLK_RST_MPLL_MULT_24M 0x68
57 #define PHY_CLK_RST_FSEL __BITS(10,5)
58 #define PHY_CLK_RST_FSEL_24M 0x5
59 #define PHY_CLK_RST_RETENABLEN __BIT(4)
60 #define PHY_CLK_RST_REFCLKSEL __BITS(3,2)
61 #define PHY_CLK_RST_REFCLKSEL_EXT 3
62 #define PHY_CLK_RST_PORTRESET __BIT(1)
63 #define PHY_CLK_RST_COMMONONN __BIT(0)
64 #define PHY_REG0 0x14
65 #define PHY_PARAM0 0x1c
66 #define PHY_PARAM0_REF_USE_PAD __BIT(31)
67 #define PHY_PARAM0_REF_LOSLEVEL __BITS(30,26)
68 #define PHY_PARAM1 0x20
69 #define PHY_PARAM1_TXDEEMPH __BITS(4,0)
70 #define PHY_TEST 0x28
71 #define PHY_TEST_POWERDOWN_SSP __BIT(3)
72 #define PHY_TEST_POWERDOWN_HSP __BIT(2)
73 #define PHY_BATCHG 0x30
74 #define PHY_BATCHG_UTMI_CLKSEL __BIT(2)
75 #define PHY_RESUME 0x34
76
77 /*
78 * PMU Registers
79 */
80 #define USBDRD_PHY_CTRL(n) (0x704 + (n) * 4)
81 #define USBDRD_PHY_CTRL_EN __BIT(0)
82
83 static int exynos_usbdrdphy_match(device_t, cfdata_t, void *);
84 static void exynos_usbdrdphy_attach(device_t, device_t, void *);
85
86 enum {
87 PHY_ID_UTMI_PLUS = 0,
88 PHY_ID_PIPE3,
89 NPHY_ID
90 };
91
92 static const struct device_compatible_entry compat_data[] = {
93 { .compat = "samsung,exynos5420-usbdrd-phy" },
94
95 { 0 }
96 };
97
98 struct exynos_usbdrdphy_softc;
99
100 struct exynos_usbdrdphy {
101 struct exynos_usbdrdphy_softc *phy_sc;
102 u_int phy_index;
103 };
104
105 struct exynos_usbdrdphy_softc {
106 device_t sc_dev;
107 bus_space_tag_t sc_bst;
108 bus_space_handle_t sc_bsh;
109 int sc_phandle;
110 struct syscon *sc_pmureg;
111
112 struct exynos_usbdrdphy *sc_phy;
113 u_int sc_nphy;
114
115 struct fdtbus_gpio_pin *sc_gpio_id_det;
116 struct fdtbus_gpio_pin *sc_gpio_vbus_det;
117 };
118
119 #define PHY_READ(sc, reg) \
120 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
121 #define PHY_WRITE(sc, reg, val) \
122 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
123
124 CFATTACH_DECL_NEW(exynos_usbdrdphy, sizeof(struct exynos_usbdrdphy_softc),
125 exynos_usbdrdphy_match, exynos_usbdrdphy_attach, NULL, NULL);
126
127 static void *
128 exynos_usbdrdphy_acquire(device_t dev, const void *data, size_t len)
129 {
130 struct exynos_usbdrdphy_softc * const sc = device_private(dev);
131
132 if (len != 4)
133 return NULL;
134
135 const u_int index = be32dec(data);
136 if (index >= sc->sc_nphy)
137 return NULL;
138
139 return &sc->sc_phy[index];
140 }
141
142 static void
143 exynos_usbdrdphy_release(device_t dev, void *priv)
144 {
145 }
146
147 static int
148 exynos_usbdrdphy_enable(device_t dev, void *priv, bool enable)
149 {
150 struct exynos_usbdrdphy * const phy = priv;
151 struct exynos_usbdrdphy_softc * const sc = phy->phy_sc;
152 uint32_t val;
153
154 syscon_lock(sc->sc_pmureg);
155 val = syscon_read_4(sc->sc_pmureg, USBDRD_PHY_CTRL(phy->phy_index));
156 if (enable)
157 val |= USBDRD_PHY_CTRL_EN;
158 else
159 val &= ~USBDRD_PHY_CTRL_EN;
160 syscon_write_4(sc->sc_pmureg, USBDRD_PHY_CTRL(phy->phy_index), val);
161 syscon_unlock(sc->sc_pmureg);
162
163 return 0;
164 }
165
166 const struct fdtbus_phy_controller_func exynos_usbdrdphy_funcs = {
167 .acquire = exynos_usbdrdphy_acquire,
168 .release = exynos_usbdrdphy_release,
169 .enable = exynos_usbdrdphy_enable,
170 };
171
172 static void
173 exynos_usbdrdphy_init(struct exynos_usbdrdphy_softc *sc)
174 {
175 uint32_t val;
176
177 PHY_WRITE(sc, PHY_REG0, 0);
178
179 val = PHY_READ(sc, PHY_PARAM0);
180 val &= ~PHY_PARAM0_REF_USE_PAD;
181 val &= ~PHY_PARAM0_REF_LOSLEVEL;
182 val |= __SHIFTIN(9, PHY_PARAM0_REF_LOSLEVEL);
183 PHY_WRITE(sc, PHY_PARAM0, val);
184
185 PHY_WRITE(sc, PHY_RESUME, 0);
186
187 val = PHY_READ(sc, PHY_LINK_SYSTEM);
188 val |= PHY_LINK_SYSTEM_XHCI_VERCTL;
189 val &= ~PHY_LINK_SYSTEM_FLADJ;
190 val |= __SHIFTIN(0x20, PHY_LINK_SYSTEM_FLADJ);
191 PHY_WRITE(sc, PHY_LINK_SYSTEM, val);
192
193 val = PHY_READ(sc, PHY_PARAM1);
194 val &= ~PHY_PARAM1_TXDEEMPH;
195 val |= __SHIFTIN(0x1c, PHY_PARAM1_TXDEEMPH);
196 PHY_WRITE(sc, PHY_PARAM1, val);
197
198 val = PHY_READ(sc, PHY_BATCHG);
199 val |= PHY_BATCHG_UTMI_CLKSEL;
200 PHY_WRITE(sc, PHY_BATCHG, val);
201
202 val = PHY_READ(sc, PHY_TEST);
203 val &= ~PHY_TEST_POWERDOWN_SSP;
204 val &= ~PHY_TEST_POWERDOWN_HSP;
205 PHY_WRITE(sc, PHY_TEST, val);
206
207 PHY_WRITE(sc, PHY_UTMI, PHY_UTMI_OTGDISABLE);
208
209 val = __SHIFTIN(PHY_CLK_RST_REFCLKSEL_EXT, PHY_CLK_RST_REFCLKSEL);
210 val |= __SHIFTIN(PHY_CLK_RST_FSEL_24M, PHY_CLK_RST_FSEL);
211 val |= __SHIFTIN(PHY_CLK_RST_MPLL_MULT_24M, PHY_CLK_RST_MPLL_MULT);
212 val |= __SHIFTIN(0x88, PHY_CLK_RST_SSC_REFCLKSEL);
213 val |= PHY_CLK_RST_PORTRESET;
214 val |= PHY_CLK_RST_RETENABLEN;
215 val |= PHY_CLK_RST_REF_SSP_EN;
216 val |= PHY_CLK_RST_SSC_EN;
217 val |= PHY_CLK_RST_COMMONONN;
218 PHY_WRITE(sc, PHY_CLK_RST, val);
219
220 delay(50000);
221
222 val &= ~PHY_CLK_RST_PORTRESET;
223 PHY_WRITE(sc, PHY_CLK_RST, val);
224 }
225
226 static int
227 exynos_usbdrdphy_match(device_t parent, cfdata_t cf, void *aux)
228 {
229 struct fdt_attach_args * const faa = aux;
230
231 return of_match_compat_data(faa->faa_phandle, compat_data);
232 }
233
234 static void
235 exynos_usbdrdphy_attach(device_t parent, device_t self, void *aux)
236 {
237 struct exynos_usbdrdphy_softc * const sc = device_private(self);
238 struct fdt_attach_args * const faa = aux;
239 const int phandle = faa->faa_phandle;
240 struct clk *clk;
241 bus_addr_t addr;
242 bus_size_t size;
243 u_int n;
244
245 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
246 aprint_error(": couldn't get phy registers\n");
247 return;
248 }
249
250 sc->sc_dev = self;
251 sc->sc_phandle = phandle;
252 sc->sc_bst = faa->faa_bst;
253 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
254 aprint_error(": couldn't map phy registers\n");
255 return;
256 }
257
258 sc->sc_nphy = NPHY_ID;
259 sc->sc_phy = kmem_alloc(sizeof(*sc->sc_phy) * sc->sc_nphy, KM_SLEEP);
260 for (n = 0; n < sc->sc_nphy; n++) {
261 sc->sc_phy[n].phy_sc = sc;
262 sc->sc_phy[n].phy_index = n;
263 }
264
265 sc->sc_pmureg = fdtbus_syscon_acquire(phandle, "samsung,pmu-syscon");
266 if (sc->sc_pmureg == NULL) {
267 aprint_error(": couldn't acquire pmureg syscon\n");
268 return;
269 }
270
271 /* Enable clocks */
272 clk = fdtbus_clock_get(phandle, "phy");
273 if (clk == NULL || clk_enable(clk) != 0) {
274 aprint_error(": couldn't enable phy clock\n");
275 return;
276 }
277 clk = fdtbus_clock_get(phandle, "ref");
278 if (clk == NULL || clk_enable(clk) != 0) {
279 aprint_error(": couldn't enable ref clock\n");
280 return;
281 }
282
283 aprint_naive("\n");
284 aprint_normal(": USB DRD PHY\n");
285
286 exynos_usbdrdphy_init(sc);
287
288 fdtbus_register_phy_controller(self, phandle, &exynos_usbdrdphy_funcs);
289 }
290