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