1 /* $NetBSD: exynos_usbdrdphy.c,v 1.6 2021/01/27 03:10:19 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 2018 Jared McNeill <jmcneill@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.6 2021/01/27 03:10:19 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 DEVICE_COMPAT_EOL
95 };
96
97 struct exynos_usbdrdphy_softc;
98
99 struct exynos_usbdrdphy {
100 struct exynos_usbdrdphy_softc *phy_sc;
101 u_int phy_index;
102 };
103
104 struct exynos_usbdrdphy_softc {
105 device_t sc_dev;
106 bus_space_tag_t sc_bst;
107 bus_space_handle_t sc_bsh;
108 int sc_phandle;
109 struct syscon *sc_pmureg;
110
111 struct exynos_usbdrdphy *sc_phy;
112 u_int sc_nphy;
113
114 struct fdtbus_gpio_pin *sc_gpio_id_det;
115 struct fdtbus_gpio_pin *sc_gpio_vbus_det;
116 };
117
118 #define PHY_READ(sc, reg) \
119 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
120 #define PHY_WRITE(sc, reg, val) \
121 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
122
123 CFATTACH_DECL_NEW(exynos_usbdrdphy, sizeof(struct exynos_usbdrdphy_softc),
124 exynos_usbdrdphy_match, exynos_usbdrdphy_attach, NULL, NULL);
125
126 static void *
exynos_usbdrdphy_acquire(device_t dev,const void * data,size_t len)127 exynos_usbdrdphy_acquire(device_t dev, const void *data, size_t len)
128 {
129 struct exynos_usbdrdphy_softc * const sc = device_private(dev);
130
131 if (len != 4)
132 return NULL;
133
134 const u_int index = be32dec(data);
135 if (index >= sc->sc_nphy)
136 return NULL;
137
138 return &sc->sc_phy[index];
139 }
140
141 static void
exynos_usbdrdphy_release(device_t dev,void * priv)142 exynos_usbdrdphy_release(device_t dev, void *priv)
143 {
144 }
145
146 static int
exynos_usbdrdphy_enable(device_t dev,void * priv,bool enable)147 exynos_usbdrdphy_enable(device_t dev, void *priv, bool enable)
148 {
149 struct exynos_usbdrdphy * const phy = priv;
150 struct exynos_usbdrdphy_softc * const sc = phy->phy_sc;
151 uint32_t val;
152
153 syscon_lock(sc->sc_pmureg);
154 val = syscon_read_4(sc->sc_pmureg, USBDRD_PHY_CTRL(phy->phy_index));
155 if (enable)
156 val |= USBDRD_PHY_CTRL_EN;
157 else
158 val &= ~USBDRD_PHY_CTRL_EN;
159 syscon_write_4(sc->sc_pmureg, USBDRD_PHY_CTRL(phy->phy_index), val);
160 syscon_unlock(sc->sc_pmureg);
161
162 return 0;
163 }
164
165 const struct fdtbus_phy_controller_func exynos_usbdrdphy_funcs = {
166 .acquire = exynos_usbdrdphy_acquire,
167 .release = exynos_usbdrdphy_release,
168 .enable = exynos_usbdrdphy_enable,
169 };
170
171 static void
exynos_usbdrdphy_init(struct exynos_usbdrdphy_softc * sc)172 exynos_usbdrdphy_init(struct exynos_usbdrdphy_softc *sc)
173 {
174 uint32_t val;
175
176 PHY_WRITE(sc, PHY_REG0, 0);
177
178 val = PHY_READ(sc, PHY_PARAM0);
179 val &= ~PHY_PARAM0_REF_USE_PAD;
180 val &= ~PHY_PARAM0_REF_LOSLEVEL;
181 val |= __SHIFTIN(9, PHY_PARAM0_REF_LOSLEVEL);
182 PHY_WRITE(sc, PHY_PARAM0, val);
183
184 PHY_WRITE(sc, PHY_RESUME, 0);
185
186 val = PHY_READ(sc, PHY_LINK_SYSTEM);
187 val |= PHY_LINK_SYSTEM_XHCI_VERCTL;
188 val &= ~PHY_LINK_SYSTEM_FLADJ;
189 val |= __SHIFTIN(0x20, PHY_LINK_SYSTEM_FLADJ);
190 PHY_WRITE(sc, PHY_LINK_SYSTEM, val);
191
192 val = PHY_READ(sc, PHY_PARAM1);
193 val &= ~PHY_PARAM1_TXDEEMPH;
194 val |= __SHIFTIN(0x1c, PHY_PARAM1_TXDEEMPH);
195 PHY_WRITE(sc, PHY_PARAM1, val);
196
197 val = PHY_READ(sc, PHY_BATCHG);
198 val |= PHY_BATCHG_UTMI_CLKSEL;
199 PHY_WRITE(sc, PHY_BATCHG, val);
200
201 val = PHY_READ(sc, PHY_TEST);
202 val &= ~PHY_TEST_POWERDOWN_SSP;
203 val &= ~PHY_TEST_POWERDOWN_HSP;
204 PHY_WRITE(sc, PHY_TEST, val);
205
206 PHY_WRITE(sc, PHY_UTMI, PHY_UTMI_OTGDISABLE);
207
208 val = __SHIFTIN(PHY_CLK_RST_REFCLKSEL_EXT, PHY_CLK_RST_REFCLKSEL);
209 val |= __SHIFTIN(PHY_CLK_RST_FSEL_24M, PHY_CLK_RST_FSEL);
210 val |= __SHIFTIN(PHY_CLK_RST_MPLL_MULT_24M, PHY_CLK_RST_MPLL_MULT);
211 val |= __SHIFTIN(0x88, PHY_CLK_RST_SSC_REFCLKSEL);
212 val |= PHY_CLK_RST_PORTRESET;
213 val |= PHY_CLK_RST_RETENABLEN;
214 val |= PHY_CLK_RST_REF_SSP_EN;
215 val |= PHY_CLK_RST_SSC_EN;
216 val |= PHY_CLK_RST_COMMONONN;
217 PHY_WRITE(sc, PHY_CLK_RST, val);
218
219 delay(50000);
220
221 val &= ~PHY_CLK_RST_PORTRESET;
222 PHY_WRITE(sc, PHY_CLK_RST, val);
223 }
224
225 static int
exynos_usbdrdphy_match(device_t parent,cfdata_t cf,void * aux)226 exynos_usbdrdphy_match(device_t parent, cfdata_t cf, void *aux)
227 {
228 struct fdt_attach_args * const faa = aux;
229
230 return of_compatible_match(faa->faa_phandle, compat_data);
231 }
232
233 static void
exynos_usbdrdphy_attach(device_t parent,device_t self,void * aux)234 exynos_usbdrdphy_attach(device_t parent, device_t self, void *aux)
235 {
236 struct exynos_usbdrdphy_softc * const sc = device_private(self);
237 struct fdt_attach_args * const faa = aux;
238 const int phandle = faa->faa_phandle;
239 struct clk *clk;
240 bus_addr_t addr;
241 bus_size_t size;
242 u_int n;
243
244 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
245 aprint_error(": couldn't get phy registers\n");
246 return;
247 }
248
249 sc->sc_dev = self;
250 sc->sc_phandle = phandle;
251 sc->sc_bst = faa->faa_bst;
252 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
253 aprint_error(": couldn't map phy registers\n");
254 return;
255 }
256
257 sc->sc_nphy = NPHY_ID;
258 sc->sc_phy = kmem_alloc(sizeof(*sc->sc_phy) * sc->sc_nphy, KM_SLEEP);
259 for (n = 0; n < sc->sc_nphy; n++) {
260 sc->sc_phy[n].phy_sc = sc;
261 sc->sc_phy[n].phy_index = n;
262 }
263
264 sc->sc_pmureg = fdtbus_syscon_acquire(phandle, "samsung,pmu-syscon");
265 if (sc->sc_pmureg == NULL) {
266 aprint_error(": couldn't acquire pmureg syscon\n");
267 return;
268 }
269
270 /* Enable clocks */
271 clk = fdtbus_clock_get(phandle, "phy");
272 if (clk == NULL || clk_enable(clk) != 0) {
273 aprint_error(": couldn't enable phy clock\n");
274 return;
275 }
276 clk = fdtbus_clock_get(phandle, "ref");
277 if (clk == NULL || clk_enable(clk) != 0) {
278 aprint_error(": couldn't enable ref clock\n");
279 return;
280 }
281
282 aprint_naive("\n");
283 aprint_normal(": USB DRD PHY\n");
284
285 exynos_usbdrdphy_init(sc);
286
287 fdtbus_register_phy_controller(self, phandle, &exynos_usbdrdphy_funcs);
288 }
289