xref: /netbsd-src/sys/arch/arm/samsung/exynos_usbdrdphy.c (revision 6e54367a22fbc89a1139d033e95bec0c0cf0975b)
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