xref: /netbsd-src/sys/arch/arm/rockchip/rk3399_pcie_phy.c (revision ca5860d9ee8ae57e39e516ea0653e09e16fb2bdf)
1*ca5860d9Sskrll /*	$NetBSD: rk3399_pcie_phy.c,v 1.5 2024/11/19 08:24:47 skrll Exp $	*/
2e8b82306Sjakllsch /*	$OpenBSD: rkpcie.c,v 1.6 2018/08/28 09:33:18 jsg Exp $	*/
3e8b82306Sjakllsch /*
4e8b82306Sjakllsch  * Copyright (c) 2018 Mark Kettenis <kettenis@openbsd.org>
5e8b82306Sjakllsch  *
6e8b82306Sjakllsch  * Permission to use, copy, modify, and distribute this software for any
7e8b82306Sjakllsch  * purpose with or without fee is hereby granted, provided that the above
8e8b82306Sjakllsch  * copyright notice and this permission notice appear in all copies.
9e8b82306Sjakllsch  *
10e8b82306Sjakllsch  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11e8b82306Sjakllsch  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12e8b82306Sjakllsch  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13e8b82306Sjakllsch  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14e8b82306Sjakllsch  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15e8b82306Sjakllsch  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16e8b82306Sjakllsch  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17e8b82306Sjakllsch  */
18e8b82306Sjakllsch 
19e8b82306Sjakllsch #include <sys/cdefs.h>
20e8b82306Sjakllsch 
21*ca5860d9Sskrll __KERNEL_RCSID(1, "$NetBSD: rk3399_pcie_phy.c,v 1.5 2024/11/19 08:24:47 skrll Exp $");
22e8b82306Sjakllsch 
23e8b82306Sjakllsch #include <sys/param.h>
24e8b82306Sjakllsch #include <sys/systm.h>
25e8b82306Sjakllsch #include <sys/device.h>
26e8b82306Sjakllsch #include <sys/kmem.h>
27e8b82306Sjakllsch 
28e8b82306Sjakllsch #include <machine/intr.h>
29e8b82306Sjakllsch #include <sys/bus.h>
30e8b82306Sjakllsch #include <dev/fdt/fdtvar.h>
31e8b82306Sjakllsch #include <dev/fdt/syscon.h>
32e8b82306Sjakllsch 
33e8b82306Sjakllsch #include <sys/gpio.h>
34e8b82306Sjakllsch 
35e8b82306Sjakllsch #define RKPCIEPHY_MAXPHY 4
36e8b82306Sjakllsch 
37e8b82306Sjakllsch struct rkpciephy_softc {
38e8b82306Sjakllsch 	device_t		sc_dev;
39e8b82306Sjakllsch 	int			sc_phy_node;
40e8b82306Sjakllsch 	uint8_t			sc_phys[RKPCIEPHY_MAXPHY];
41e8b82306Sjakllsch 	u_int			sc_phys_on;
42e8b82306Sjakllsch };
43e8b82306Sjakllsch 
44e8b82306Sjakllsch static int rkpciephy_match(device_t, cfdata_t, void *);
45e8b82306Sjakllsch static void rkpciephy_attach(device_t, device_t, void *);
46e8b82306Sjakllsch 
47e8b82306Sjakllsch CFATTACH_DECL_NEW(rkpciephy, sizeof(struct rkpciephy_softc),
48e8b82306Sjakllsch         rkpciephy_match, rkpciephy_attach, NULL, NULL);
49e8b82306Sjakllsch 
506e54367aSthorpej static const struct device_compatible_entry compat_data[] = {
516e54367aSthorpej 	{ .compat = "rockchip,rk3399-pcie-phy" },
526e54367aSthorpej 	DEVICE_COMPAT_EOL
53e8b82306Sjakllsch };
54e8b82306Sjakllsch 
55e8b82306Sjakllsch static int
56e8b82306Sjakllsch rkpciephy_match(device_t parent, cfdata_t cf, void *aux)
57e8b82306Sjakllsch {
58e8b82306Sjakllsch 	struct fdt_attach_args *faa = aux;
59e8b82306Sjakllsch 
606e54367aSthorpej 	return of_compatible_match(faa->faa_phandle, compat_data);
61e8b82306Sjakllsch }
62e8b82306Sjakllsch 
63e8b82306Sjakllsch static void rkpcie_phy_poweron(struct rkpciephy_softc *, u_int);
64e8b82306Sjakllsch 
65e8b82306Sjakllsch static inline void
66e8b82306Sjakllsch clock_enable(int phandle, const char *name)
67e8b82306Sjakllsch {
68e8b82306Sjakllsch 	struct clk * clk = fdtbus_clock_get(phandle, name);
69e8b82306Sjakllsch 	if (clk == NULL)
70e8b82306Sjakllsch 		return;
71e8b82306Sjakllsch 	if (clk_enable(clk) != 0)
72e8b82306Sjakllsch 		return;
73e8b82306Sjakllsch }
74e8b82306Sjakllsch 
75e8b82306Sjakllsch static void
76e8b82306Sjakllsch reset_assert(int phandle, const char *name)
77e8b82306Sjakllsch {
78e8b82306Sjakllsch 	struct fdtbus_reset *rst;
79e8b82306Sjakllsch 
80e8b82306Sjakllsch 	rst = fdtbus_reset_get(phandle, name);
81e8b82306Sjakllsch 	fdtbus_reset_assert(rst);
82e8b82306Sjakllsch 	fdtbus_reset_put(rst);
83e8b82306Sjakllsch }
84e8b82306Sjakllsch 
85e8b82306Sjakllsch static void
86e8b82306Sjakllsch reset_deassert(int phandle, const char *name)
87e8b82306Sjakllsch {
88e8b82306Sjakllsch         struct fdtbus_reset *rst;
89e8b82306Sjakllsch 
90e8b82306Sjakllsch 	rst = fdtbus_reset_get(phandle, name);
91e8b82306Sjakllsch 	fdtbus_reset_deassert(rst);
92e8b82306Sjakllsch 	fdtbus_reset_put(rst);
93e8b82306Sjakllsch }
94e8b82306Sjakllsch 
95e8b82306Sjakllsch static void *
96e8b82306Sjakllsch rkpciephy_phy_acquire(device_t dev, const void *data, size_t len)
97e8b82306Sjakllsch {
98e8b82306Sjakllsch 	struct rkpciephy_softc * const sc = device_private(dev);
99e8b82306Sjakllsch 
100e8b82306Sjakllsch 	if (len != 4)
101e8b82306Sjakllsch 		return NULL;
102e8b82306Sjakllsch 
103e8b82306Sjakllsch 	const int phy_id = be32dec(data);
104e8b82306Sjakllsch 	if (phy_id >= RKPCIEPHY_MAXPHY)
105e8b82306Sjakllsch 		return NULL;
106e8b82306Sjakllsch //	device_printf(dev, "%s phy_id %d %d\n", __func__, phy_id, sc->sc_phys[phy_id]);
107e8b82306Sjakllsch 
108e8b82306Sjakllsch 	if (true /*XXX*/ || sc->sc_phys_on == 0) {
109e8b82306Sjakllsch 		clock_enable(sc->sc_phy_node, "refclk");
110e8b82306Sjakllsch 		reset_assert(sc->sc_phy_node, "phy");
111e8b82306Sjakllsch 	}
112e8b82306Sjakllsch 
113e8b82306Sjakllsch 	return &sc->sc_phys[phy_id];
114e8b82306Sjakllsch }
115e8b82306Sjakllsch 
116e8b82306Sjakllsch static int
117e8b82306Sjakllsch rkpciephy_phy_enable(device_t dev, void *priv, bool enable)
118e8b82306Sjakllsch {
119e8b82306Sjakllsch 	struct rkpciephy_softc * const sc = device_private(dev);
120e8b82306Sjakllsch 	uint8_t * const lane = priv;
121e8b82306Sjakllsch 
122e8b82306Sjakllsch //	device_printf(dev, "%s %u %u\n", __func__, *lane, enable);
123e8b82306Sjakllsch 
124e8b82306Sjakllsch 	if (enable) {
125e8b82306Sjakllsch 		rkpcie_phy_poweron(sc, *lane);
126e8b82306Sjakllsch 		sc->sc_phys_on |= 1U << *lane;
127e8b82306Sjakllsch 	} else {
128e8b82306Sjakllsch #if notyet
129e8b82306Sjakllsch 		sc->sc_phys_on &= ~(1U << *lane);
130e8b82306Sjakllsch #endif
131e8b82306Sjakllsch 	}
132e8b82306Sjakllsch 
133e8b82306Sjakllsch 	return 0;
134e8b82306Sjakllsch }
135e8b82306Sjakllsch 
136e8b82306Sjakllsch const struct fdtbus_phy_controller_func rkpciephy_phy_funcs = {
137e8b82306Sjakllsch  	.acquire = rkpciephy_phy_acquire,
138e8b82306Sjakllsch  	.release = (void *)voidop,
139e8b82306Sjakllsch  	.enable = rkpciephy_phy_enable,
140e8b82306Sjakllsch };
141e8b82306Sjakllsch 
142e8b82306Sjakllsch static void
143e8b82306Sjakllsch rkpciephy_attach(device_t parent, device_t self, void *aux)
144e8b82306Sjakllsch {
145e8b82306Sjakllsch 	struct rkpciephy_softc *sc = device_private(self);
146e8b82306Sjakllsch 	struct fdt_attach_args *faa = aux;
147e8b82306Sjakllsch 
148e8b82306Sjakllsch 	sc->sc_dev = self;
149e8b82306Sjakllsch 	sc->sc_phy_node = faa->faa_phandle;
150e8b82306Sjakllsch 
151e8b82306Sjakllsch 	aprint_naive("\n");
152e8b82306Sjakllsch 	aprint_normal(": RK3399 PCIe PHY\n");
153e8b82306Sjakllsch 
154e8b82306Sjakllsch 	for (size_t i = 0; i < RKPCIEPHY_MAXPHY; i++)
155e8b82306Sjakllsch 		sc->sc_phys[i] = i;
156e8b82306Sjakllsch 
157e8b82306Sjakllsch 	fdtbus_register_phy_controller(self, faa->faa_phandle, &rkpciephy_phy_funcs);
158e8b82306Sjakllsch }
159e8b82306Sjakllsch 
160e8b82306Sjakllsch /*
161e8b82306Sjakllsch  * PHY Support.
162e8b82306Sjakllsch  */
163e8b82306Sjakllsch 
164e8b82306Sjakllsch #define RK3399_GRF_SOC_CON5_PCIE	0xe214
165e8b82306Sjakllsch #define  RK3399_TX_ELEC_IDLE_OFF_MASK	((1 << 3) << 16)
166e8b82306Sjakllsch #define  RK3399_TX_ELEC_IDLE_OFF	(1 << 3)
167e8b82306Sjakllsch #define RK3399_GRF_SOC_CON8		0xe220
168e8b82306Sjakllsch #define  RK3399_PCIE_TEST_DATA_MASK	((0xf << 7) << 16)
169e8b82306Sjakllsch #define  RK3399_PCIE_TEST_DATA_SHIFT	7
170e8b82306Sjakllsch #define  RK3399_PCIE_TEST_ADDR_MASK	((0x3f << 1) << 16)
171e8b82306Sjakllsch #define  RK3399_PCIE_TEST_ADDR_SHIFT	1
172e8b82306Sjakllsch #define  RK3399_PCIE_TEST_WRITE_ENABLE	(((1 << 0) << 16) | (1 << 0))
173e8b82306Sjakllsch #define  RK3399_PCIE_TEST_WRITE_DISABLE	(((1 << 0) << 16) | (0 << 0))
174e8b82306Sjakllsch #define RK3399_GRF_SOC_STATUS1		0xe2a4
175e8b82306Sjakllsch #define  RK3399_PCIE_PHY_PLL_LOCKED	(1 << 9)
176e8b82306Sjakllsch #define  RK3399_PCIE_PHY_PLL_OUTPUT	(1 << 10)
177e8b82306Sjakllsch 
178e8b82306Sjakllsch #define RK3399_PCIE_PHY_CFG_PLL_LOCK	0x10
179e8b82306Sjakllsch #define RK3399_PCIE_PHY_CFG_CLK_TEST	0x10
180e8b82306Sjakllsch #define  RK3399_PCIE_PHY_CFG_SEPE_RATE	(1 << 3)
181e8b82306Sjakllsch #define RK3399_PCIE_PHY_CFG_CLK_SCC	0x12
182e8b82306Sjakllsch #define  RK3399_PCIE_PHY_CFG_PLL_100M	(1 << 3)
183e8b82306Sjakllsch 
184e8b82306Sjakllsch static void
185e8b82306Sjakllsch rkpcie_phy_write_conf(struct syscon *rm, uint8_t addr, uint8_t data)
186e8b82306Sjakllsch {
187e8b82306Sjakllsch 	syscon_write_4(rm, RK3399_GRF_SOC_CON8,
188e8b82306Sjakllsch 	    RK3399_PCIE_TEST_ADDR_MASK |
189e8b82306Sjakllsch 	    (addr << RK3399_PCIE_TEST_ADDR_SHIFT) |
190e8b82306Sjakllsch 	    RK3399_PCIE_TEST_DATA_MASK |
191e8b82306Sjakllsch 	    (data << RK3399_PCIE_TEST_DATA_SHIFT) |
192e8b82306Sjakllsch 	    RK3399_PCIE_TEST_WRITE_DISABLE);
193e8b82306Sjakllsch 	delay(1);
194e8b82306Sjakllsch 	syscon_write_4(rm, RK3399_GRF_SOC_CON8,
195e8b82306Sjakllsch 	    RK3399_PCIE_TEST_WRITE_ENABLE);
196e8b82306Sjakllsch 	delay(1);
197e8b82306Sjakllsch 	syscon_write_4(rm, RK3399_GRF_SOC_CON8,
198e8b82306Sjakllsch 	    RK3399_PCIE_TEST_WRITE_DISABLE);
199e8b82306Sjakllsch }
200e8b82306Sjakllsch 
201e8b82306Sjakllsch static void
202e8b82306Sjakllsch rkpcie_phy_poweron(struct rkpciephy_softc *sc, u_int lane)
203e8b82306Sjakllsch {
204e8b82306Sjakllsch 	struct syscon *rm;
205e8b82306Sjakllsch 	uint32_t status;
206e8b82306Sjakllsch 	int timo;
207e8b82306Sjakllsch 
208e8b82306Sjakllsch 	reset_deassert(sc->sc_phy_node, "phy");
209e8b82306Sjakllsch 
210e8b82306Sjakllsch 	rm = fdtbus_syscon_lookup(OF_parent(sc->sc_phy_node));
211e8b82306Sjakllsch 	if (rm == NULL)
212e8b82306Sjakllsch 		return;
213e8b82306Sjakllsch 
214e8b82306Sjakllsch 	syscon_lock(rm);
215e8b82306Sjakllsch 	syscon_write_4(rm, RK3399_GRF_SOC_CON8,
216e8b82306Sjakllsch 	    RK3399_PCIE_TEST_ADDR_MASK |
217e8b82306Sjakllsch 	    RK3399_PCIE_PHY_CFG_PLL_LOCK << RK3399_PCIE_TEST_ADDR_SHIFT);
218e8b82306Sjakllsch 	syscon_write_4(rm, RK3399_GRF_SOC_CON5_PCIE,
219e8b82306Sjakllsch 	    RK3399_TX_ELEC_IDLE_OFF_MASK << lane | 0);
220e8b82306Sjakllsch 	//printf("%s %x\n", __func__, syscon_read_4(rm, RK3399_GRF_SOC_CON5_PCIE));
221e8b82306Sjakllsch 
222e8b82306Sjakllsch 	for (timo = 50; timo > 0; timo--) {
223e8b82306Sjakllsch 		status = syscon_read_4(rm, RK3399_GRF_SOC_STATUS1);
224e8b82306Sjakllsch 		if (status & RK3399_PCIE_PHY_PLL_LOCKED)
225e8b82306Sjakllsch 			break;
226e8b82306Sjakllsch 		delay(20000);
227e8b82306Sjakllsch 	}
228e8b82306Sjakllsch 	if (timo == 0) {
229e8b82306Sjakllsch 		device_printf(sc->sc_dev, "PHY PLL lock timeout\n");
230e8b82306Sjakllsch 		syscon_unlock(rm);
231e8b82306Sjakllsch 		return;
232e8b82306Sjakllsch 	}
233e8b82306Sjakllsch 
234e8b82306Sjakllsch 	rkpcie_phy_write_conf(rm, RK3399_PCIE_PHY_CFG_CLK_TEST,
235e8b82306Sjakllsch 	    RK3399_PCIE_PHY_CFG_SEPE_RATE);
236e8b82306Sjakllsch 	rkpcie_phy_write_conf(rm, RK3399_PCIE_PHY_CFG_CLK_SCC,
237e8b82306Sjakllsch 	    RK3399_PCIE_PHY_CFG_PLL_100M);
238e8b82306Sjakllsch 
239e8b82306Sjakllsch 	for (timo = 50; timo > 0; timo--) {
240e8b82306Sjakllsch 		status = syscon_read_4(rm, RK3399_GRF_SOC_STATUS1);
241e8b82306Sjakllsch 		if ((status & RK3399_PCIE_PHY_PLL_OUTPUT) == 0)
242e8b82306Sjakllsch 			break;
243e8b82306Sjakllsch 		delay(20000);
244e8b82306Sjakllsch 	}
245e8b82306Sjakllsch 	if (timo == 0) {
246e8b82306Sjakllsch 		device_printf(sc->sc_dev, "PHY PLL output enable timeout\n");
247e8b82306Sjakllsch 		syscon_unlock(rm);
248e8b82306Sjakllsch 		return;
249e8b82306Sjakllsch 	}
250e8b82306Sjakllsch 
251e8b82306Sjakllsch 	syscon_write_4(rm, RK3399_GRF_SOC_CON8,
252e8b82306Sjakllsch 	    RK3399_PCIE_TEST_ADDR_MASK |
253e8b82306Sjakllsch 	    RK3399_PCIE_PHY_CFG_PLL_LOCK << RK3399_PCIE_TEST_ADDR_SHIFT);
254e8b82306Sjakllsch 
255e8b82306Sjakllsch 	for (timo = 50; timo > 0; timo--) {
256e8b82306Sjakllsch 		status = syscon_read_4(rm, RK3399_GRF_SOC_STATUS1);
257e8b82306Sjakllsch 		if (status & RK3399_PCIE_PHY_PLL_LOCKED)
258e8b82306Sjakllsch 			break;
259e8b82306Sjakllsch 		delay(20000);
260e8b82306Sjakllsch 	}
261e8b82306Sjakllsch 	if (timo == 0) {
262e8b82306Sjakllsch 		device_printf(sc->sc_dev, "PHY PLL relock timeout\n");
263e8b82306Sjakllsch 		syscon_unlock(rm);
264e8b82306Sjakllsch 		return;
265e8b82306Sjakllsch 	}
266e8b82306Sjakllsch 	syscon_unlock(rm);
267e8b82306Sjakllsch }
268