xref: /openbsd-src/sys/dev/fdt/rkpciephy.c (revision 5b23ee9f7fff40c3a5d303a0cc3360a43360e931)
1*5b23ee9fSpatrick /*	$OpenBSD: rkpciephy.c,v 1.3 2023/07/09 19:11:30 patrick Exp $	*/
2a4de7ae7Skettenis /*
3a4de7ae7Skettenis  * Copyright (c) 2023 Mark Kettenis <kettenis@openbsd.org>
4a4de7ae7Skettenis  *
5a4de7ae7Skettenis  * Permission to use, copy, modify, and distribute this software for any
6a4de7ae7Skettenis  * purpose with or without fee is hereby granted, provided that the above
7a4de7ae7Skettenis  * copyright notice and this permission notice appear in all copies.
8a4de7ae7Skettenis  *
9a4de7ae7Skettenis  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10a4de7ae7Skettenis  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11a4de7ae7Skettenis  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12a4de7ae7Skettenis  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13a4de7ae7Skettenis  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14a4de7ae7Skettenis  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15a4de7ae7Skettenis  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16a4de7ae7Skettenis  */
17a4de7ae7Skettenis 
18a4de7ae7Skettenis #include <sys/param.h>
19a4de7ae7Skettenis #include <sys/systm.h>
20a4de7ae7Skettenis #include <sys/device.h>
21a4de7ae7Skettenis 
22a4de7ae7Skettenis #include <machine/intr.h>
23a4de7ae7Skettenis #include <machine/bus.h>
24a4de7ae7Skettenis #include <machine/fdt.h>
25a4de7ae7Skettenis 
26a4de7ae7Skettenis #include <dev/ofw/openfirm.h>
27a4de7ae7Skettenis #include <dev/ofw/ofw_clock.h>
28a4de7ae7Skettenis #include <dev/ofw/ofw_misc.h>
29a4de7ae7Skettenis #include <dev/ofw/fdt.h>
30a4de7ae7Skettenis 
31be60217fSpatrick /* RK3568 GRF registers */
32a4de7ae7Skettenis #define GRF_PCIE30PHY_CON(idx)			((idx) * 4)
33a4de7ae7Skettenis /* CON1 */
34a4de7ae7Skettenis #define  GRF_PCIE30PHY_DA_OCM			0x80008000
35a4de7ae7Skettenis /* CON5 */
36a4de7ae7Skettenis #define  GRF_PCIE30PHY_LANE0_LINK_NUM_MASK	(0xf << 16)
37a4de7ae7Skettenis #define  GRF_PCIE30PHY_LANE0_LINK_NUM_SHIFT	0
38a4de7ae7Skettenis /* CON6 */
39a4de7ae7Skettenis #define  GRF_PCIE30PHY_LANE1_LINK_NUM_MASK	(0xf << 16)
40a4de7ae7Skettenis #define  GRF_PCIE30PHY_LANE1_LINK_NUM_SHIFT	0
41a4de7ae7Skettenis /* STATUS0 */
42a4de7ae7Skettenis #define GRF_PCIE30PHY_STATUS0			0x80
43a4de7ae7Skettenis #define  GRF_PCIE30PHY_SRAM_INIT_DONE		(1 << 14)
44a4de7ae7Skettenis 
45be60217fSpatrick /* RK3588 GRF registers */
46be60217fSpatrick #define RK3588_PCIE3PHY_GRF_CMN_CON(idx)	((idx) * 4)
47be60217fSpatrick #define  RK3588_GRF_PCIE3PHY_DA_OCM		((0x1 << 24) | (1 << 8))
48be60217fSpatrick #define  RK3588_GRF_PCIE3PHY_LANE_BIFURCATE_0_1	(1 << 0)
49be60217fSpatrick #define  RK3588_GRF_PCIE3PHY_LANE_BIFURCATE_2_3	(1 << 1)
50be60217fSpatrick #define  RK3588_GRF_PCIE3PHY_LANE_AGGREGATE	(1 << 2)
51be60217fSpatrick #define  RK3588_GRF_PCIE3PHY_LANE_MASK		(0x7 << 16)
52be60217fSpatrick #define RK3588_PCIE3PHY_GRF_PHY0_STATUS1	0x904
53be60217fSpatrick #define RK3588_PCIE3PHY_GRF_PHY1_STATUS1	0xa04
54be60217fSpatrick #define  RK3588_PCIE3PHY_SRAM_INIT_DONE		(1 << 0)
55be60217fSpatrick #define RK3588_PHP_GRF_PCIESEL_CON		0x100
56be60217fSpatrick #define  RK3588_PHP_GRF_PCIE0L0_PCIE3		(1 << 0)
57be60217fSpatrick #define  RK3588_PHP_GRF_PCIE0L1_PCIE3		(1 << 1)
58be60217fSpatrick #define  RK3588_PHP_GRF_PCIE0L0_MASK		(0x1 << 16)
59be60217fSpatrick #define  RK3588_PHP_GRF_PCIE0L1_MASK		(0x1 << 17)
60be60217fSpatrick 
61a4de7ae7Skettenis struct rkpciephy_softc {
62a4de7ae7Skettenis 	struct device		sc_dev;
63a4de7ae7Skettenis 	bus_space_tag_t		sc_iot;
64a4de7ae7Skettenis 	bus_space_handle_t	sc_ioh;
65a4de7ae7Skettenis 
66a4de7ae7Skettenis 	struct phy_device	sc_pd;
67a4de7ae7Skettenis };
68a4de7ae7Skettenis 
69a4de7ae7Skettenis int	rkpciephy_match(struct device *, void *, void *);
70a4de7ae7Skettenis void	rkpciephy_attach(struct device *, struct device *, void *);
71a4de7ae7Skettenis 
72a4de7ae7Skettenis const struct cfattach rkpciephy_ca = {
73a4de7ae7Skettenis 	sizeof (struct rkpciephy_softc), rkpciephy_match, rkpciephy_attach
74a4de7ae7Skettenis };
75a4de7ae7Skettenis 
76a4de7ae7Skettenis struct cfdriver rkpciephy_cd = {
77a4de7ae7Skettenis 	NULL, "rkpciephy", DV_DULL
78a4de7ae7Skettenis };
79a4de7ae7Skettenis 
80be60217fSpatrick int	rk3568_pciephy_enable(void *, uint32_t *);
81be60217fSpatrick int	rk3588_pciephy_enable(void *, uint32_t *);
82a4de7ae7Skettenis 
83a4de7ae7Skettenis int
rkpciephy_match(struct device * parent,void * match,void * aux)84a4de7ae7Skettenis rkpciephy_match(struct device *parent, void *match, void *aux)
85a4de7ae7Skettenis {
86a4de7ae7Skettenis 	struct fdt_attach_args *faa = aux;
87a4de7ae7Skettenis 
88be60217fSpatrick 	return (OF_is_compatible(faa->fa_node, "rockchip,rk3568-pcie3-phy") ||
89be60217fSpatrick 	    OF_is_compatible(faa->fa_node, "rockchip,rk3588-pcie3-phy"));
90a4de7ae7Skettenis }
91a4de7ae7Skettenis 
92a4de7ae7Skettenis void
rkpciephy_attach(struct device * parent,struct device * self,void * aux)93a4de7ae7Skettenis rkpciephy_attach(struct device *parent, struct device *self, void *aux)
94a4de7ae7Skettenis {
95a4de7ae7Skettenis 	struct rkpciephy_softc *sc = (struct rkpciephy_softc *)self;
96a4de7ae7Skettenis 	struct fdt_attach_args *faa = aux;
97a4de7ae7Skettenis 
98a4de7ae7Skettenis 	printf("\n");
99a4de7ae7Skettenis 
100a4de7ae7Skettenis 	sc->sc_pd.pd_node = faa->fa_node;
101a4de7ae7Skettenis 	sc->sc_pd.pd_cookie = sc;
102be60217fSpatrick 	if (OF_is_compatible(faa->fa_node, "rockchip,rk3568-pcie3-phy"))
103be60217fSpatrick 		sc->sc_pd.pd_enable = rk3568_pciephy_enable;
104be60217fSpatrick 	if (OF_is_compatible(faa->fa_node, "rockchip,rk3588-pcie3-phy"))
105be60217fSpatrick 		sc->sc_pd.pd_enable = rk3588_pciephy_enable;
106a4de7ae7Skettenis 	phy_register(&sc->sc_pd);
107a4de7ae7Skettenis }
108a4de7ae7Skettenis 
109a4de7ae7Skettenis int
rk3568_pciephy_enable(void * cookie,uint32_t * cells)110be60217fSpatrick rk3568_pciephy_enable(void *cookie, uint32_t *cells)
111a4de7ae7Skettenis {
112a4de7ae7Skettenis 	struct rkpciephy_softc *sc = cookie;
113a4de7ae7Skettenis 	struct regmap *rm;
114a4de7ae7Skettenis 	int node = sc->sc_pd.pd_node;
115a4de7ae7Skettenis 	uint32_t data_lanes[2] = { 0, 0 };
116a4de7ae7Skettenis 	uint32_t grf, stat;
117a4de7ae7Skettenis 	int timo;
118a4de7ae7Skettenis 
119a4de7ae7Skettenis 	grf = OF_getpropint(node, "rockchip,phy-grf", 0);
120a4de7ae7Skettenis 	rm = regmap_byphandle(grf);
121a4de7ae7Skettenis 	if (rm == NULL)
122a4de7ae7Skettenis 		return ENXIO;
123a4de7ae7Skettenis 
124a4de7ae7Skettenis 	clock_enable_all(node);
125a4de7ae7Skettenis 	reset_assert(node, "phy");
126a4de7ae7Skettenis 	delay(1);
127a4de7ae7Skettenis 
128a4de7ae7Skettenis 	regmap_write_4(rm, GRF_PCIE30PHY_CON(9), GRF_PCIE30PHY_DA_OCM);
129a4de7ae7Skettenis 
130a4de7ae7Skettenis 	OF_getpropintarray(node, "data-lanes", data_lanes, sizeof(data_lanes));
131a4de7ae7Skettenis 	if (data_lanes[0] > 0) {
132a4de7ae7Skettenis 		regmap_write_4(rm, GRF_PCIE30PHY_CON(5),
133a4de7ae7Skettenis 		    GRF_PCIE30PHY_LANE0_LINK_NUM_MASK |
134a4de7ae7Skettenis 		    (data_lanes[0] - 1) << GRF_PCIE30PHY_LANE0_LINK_NUM_SHIFT);
135a4de7ae7Skettenis 	}
136a4de7ae7Skettenis 	if (data_lanes[1] > 0) {
137a4de7ae7Skettenis 		regmap_write_4(rm, GRF_PCIE30PHY_CON(6),
138a4de7ae7Skettenis 		    GRF_PCIE30PHY_LANE1_LINK_NUM_MASK |
139a4de7ae7Skettenis 		    (data_lanes[1] - 1) << GRF_PCIE30PHY_LANE1_LINK_NUM_SHIFT);
140a4de7ae7Skettenis 	}
141a4de7ae7Skettenis 	if (data_lanes[0] > 1 || data_lanes[1] > 1)
142a4de7ae7Skettenis 		regmap_write_4(rm, GRF_PCIE30PHY_CON(1), GRF_PCIE30PHY_DA_OCM);
143a4de7ae7Skettenis 
144a4de7ae7Skettenis 	reset_deassert(node, "phy");
145a4de7ae7Skettenis 
146a4de7ae7Skettenis 	for (timo = 500; timo > 0; timo--) {
147a4de7ae7Skettenis 		stat = regmap_read_4(rm, GRF_PCIE30PHY_STATUS0);
148a4de7ae7Skettenis 		if (stat & GRF_PCIE30PHY_SRAM_INIT_DONE)
149a4de7ae7Skettenis 			break;
150a4de7ae7Skettenis 		delay(100);
151a4de7ae7Skettenis 	}
152a4de7ae7Skettenis 	if (timo == 0) {
153a4de7ae7Skettenis 		printf("%s: timeout\n", sc->sc_dev.dv_xname);
154a4de7ae7Skettenis 		return ETIMEDOUT;
155a4de7ae7Skettenis 	}
156a4de7ae7Skettenis 
157a4de7ae7Skettenis 	return 0;
158a4de7ae7Skettenis }
159be60217fSpatrick 
160be60217fSpatrick int
rk3588_pciephy_enable(void * cookie,uint32_t * cells)161be60217fSpatrick rk3588_pciephy_enable(void *cookie, uint32_t *cells)
162be60217fSpatrick {
163be60217fSpatrick 	struct rkpciephy_softc *sc = cookie;
164be60217fSpatrick 	struct regmap *phy, *pipe;
165be60217fSpatrick 	int node = sc->sc_pd.pd_node;
166be60217fSpatrick 	uint32_t data_lanes[4] = { 1, 1, 1, 1 };
167be60217fSpatrick 	uint32_t grf, reg, stat;
168be60217fSpatrick 	int num_lanes, timo;
169be60217fSpatrick 
170be60217fSpatrick 	grf = OF_getpropint(node, "rockchip,phy-grf", 0);
171be60217fSpatrick 	phy = regmap_byphandle(grf);
172be60217fSpatrick 	if (phy == NULL)
173be60217fSpatrick 		return ENXIO;
174be60217fSpatrick 
175be60217fSpatrick 	clock_enable_all(node);
176be60217fSpatrick 	reset_assert(node, "phy");
177be60217fSpatrick 	delay(1);
178be60217fSpatrick 
179be60217fSpatrick 	regmap_write_4(phy, RK3588_PCIE3PHY_GRF_CMN_CON(0),
180be60217fSpatrick 	    RK3588_GRF_PCIE3PHY_DA_OCM);
181be60217fSpatrick 
182be60217fSpatrick 	num_lanes = OF_getpropintarray(node, "data-lanes", data_lanes,
183be60217fSpatrick 	    sizeof(data_lanes));
184be60217fSpatrick 	/* Use default setting in case of missing properties. */
185be60217fSpatrick 	if (num_lanes <= 0)
186be60217fSpatrick 		num_lanes = sizeof(data_lanes);
187be60217fSpatrick 	num_lanes /= sizeof(uint32_t);
188be60217fSpatrick 
189be60217fSpatrick 	reg = RK3588_GRF_PCIE3PHY_LANE_MASK;
190be60217fSpatrick 	/* If all links go to the first, aggregate toward x4 */
191be60217fSpatrick 	if (num_lanes >= 4 &&
192be60217fSpatrick 	    data_lanes[0] == 1 && data_lanes[1] == 1 &&
193be60217fSpatrick 	    data_lanes[2] == 1 && data_lanes[3] == 1) {
194be60217fSpatrick 		reg |= RK3588_GRF_PCIE3PHY_LANE_AGGREGATE;
195be60217fSpatrick 	} else {
196be60217fSpatrick 		/* If lanes 0+1 are not towards the same controller, split. */
197be60217fSpatrick 		if (num_lanes >= 2 && data_lanes[0] != data_lanes[1])
198be60217fSpatrick 			reg |= RK3588_GRF_PCIE3PHY_LANE_BIFURCATE_0_1;
199be60217fSpatrick 		/* If lanes 2+3 are not towards the same controller, split. */
200be60217fSpatrick 		if (num_lanes >= 4 && data_lanes[2] != data_lanes[3])
201be60217fSpatrick 			reg |= RK3588_GRF_PCIE3PHY_LANE_BIFURCATE_2_3;
202be60217fSpatrick 	}
203be60217fSpatrick 	regmap_write_4(phy, RK3588_PCIE3PHY_GRF_CMN_CON(0), reg);
204be60217fSpatrick 
205*5b23ee9fSpatrick 	grf = OF_getpropint(node, "rockchip,pipe-grf", 0);
206be60217fSpatrick 	pipe = regmap_byphandle(grf);
207be60217fSpatrick 	if (pipe != NULL) {
208be60217fSpatrick 		reg = RK3588_PHP_GRF_PCIE0L0_MASK | RK3588_PHP_GRF_PCIE0L1_MASK;
209*5b23ee9fSpatrick 		/* If lane 1 goes to PCIe3_1L0, move from Combo to PCIE3 PHY */
210*5b23ee9fSpatrick 		if (num_lanes >= 2 && data_lanes[1] == 2)
211be60217fSpatrick 			reg |= RK3588_PHP_GRF_PCIE0L0_PCIE3;
212*5b23ee9fSpatrick 		/* If lane 3 goes to PCIe3_1L1, move from Combo to PCIE3 PHY */
213*5b23ee9fSpatrick 		if (num_lanes >= 4 && data_lanes[3] == 4)
214be60217fSpatrick 			reg |= RK3588_PHP_GRF_PCIE0L1_PCIE3;
215be60217fSpatrick 		regmap_write_4(pipe, RK3588_PHP_GRF_PCIESEL_CON, reg);
216be60217fSpatrick 	}
217be60217fSpatrick 
218be60217fSpatrick 	reset_deassert(node, "phy");
219be60217fSpatrick 
220be60217fSpatrick 	for (timo = 500; timo > 0; timo--) {
221be60217fSpatrick 		stat = regmap_read_4(phy, RK3588_PCIE3PHY_GRF_PHY0_STATUS1);
222be60217fSpatrick 		if (stat & RK3588_PCIE3PHY_SRAM_INIT_DONE)
223be60217fSpatrick 			break;
224be60217fSpatrick 		delay(100);
225be60217fSpatrick 	}
226be60217fSpatrick 	for (; timo > 0; timo--) {
227be60217fSpatrick 		stat = regmap_read_4(phy, RK3588_PCIE3PHY_GRF_PHY1_STATUS1);
228be60217fSpatrick 		if (stat & RK3588_PCIE3PHY_SRAM_INIT_DONE)
229be60217fSpatrick 			break;
230be60217fSpatrick 		delay(100);
231be60217fSpatrick 	}
232be60217fSpatrick 	if (timo == 0) {
233be60217fSpatrick 		printf("%s: timeout\n", sc->sc_dev.dv_xname);
234be60217fSpatrick 		return ETIMEDOUT;
235be60217fSpatrick 	}
236be60217fSpatrick 
237be60217fSpatrick 	return 0;
238be60217fSpatrick }
239