xref: /openbsd/sys/dev/fdt/amlpciephy.c (revision 9fdf0c62)
1 /*	$OpenBSD: amlpciephy.c,v 1.5 2021/10/24 17:52:26 mpi Exp $	*/
2 /*
3  * Copyright (c) 2019 Mark Kettenis <kettenis@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/device.h>
21 
22 #include <machine/intr.h>
23 #include <machine/bus.h>
24 #include <machine/fdt.h>
25 
26 #include <dev/ofw/openfirm.h>
27 #include <dev/ofw/ofw_clock.h>
28 #include <dev/ofw/ofw_misc.h>
29 #include <dev/ofw/fdt.h>
30 
31 #define PHY_R0		0x00
32 #define  PHY_R0_PCIE_POWER_MASK	(0x1f << 0)
33 #define  PHY_R0_PCIE_POWER_ON	(0x1c << 0)
34 #define  PHY_R0_PCIE_POWER_OFF	(0x1d << 0)
35 #define  PHY_R0_MODE_MASK	(0x3 << 5)
36 #define  PHY_R0_MODE_USB3	(0x3 << 5)
37 #define PHY_R4		0x10
38 #define  PHY_R4_PHY_CR_WRITE	(1 << 0)
39 #define  PHY_R4_PHY_CR_READ	(1 << 1)
40 #define  PHY_R4_PHY_CR_CAP_DATA	(1 << 18)
41 #define  PHY_R4_PHY_CR_CAP_ADDR	(1 << 19)
42 #define PHY_R5		0x14
43 #define  PHY_R5_PHY_CR_ACK	(1 << 16)
44 
45 #define HREAD4(sc, reg)							\
46 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
47 #define HWRITE4(sc, reg, val)						\
48 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
49 #define HSET4(sc, reg, bits)						\
50 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
51 #define HCLR4(sc, reg, bits)						\
52 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
53 
54 struct amlpciephy_softc {
55 	struct device		sc_dev;
56 	bus_space_tag_t		sc_iot;
57 	bus_space_handle_t	sc_ioh;
58 
59 	struct phy_device	sc_pd;
60 };
61 
62 int amlpciephy_match(struct device *, void *, void *);
63 void amlpciephy_attach(struct device *, struct device *, void *);
64 
65 const struct cfattach	amlpciephy_ca = {
66 	sizeof (struct amlpciephy_softc), amlpciephy_match, amlpciephy_attach
67 };
68 
69 struct cfdriver amlpciephy_cd = {
70 	NULL, "amlpciephy", DV_DULL
71 };
72 
73 int	amlpciephy_enable(void *, uint32_t *);
74 uint16_t amlpciephy_read(struct amlpciephy_softc *, bus_addr_t);
75 void	amlpciephy_write(struct amlpciephy_softc *, bus_addr_t, uint16_t);
76 
77 int
amlpciephy_match(struct device * parent,void * match,void * aux)78 amlpciephy_match(struct device *parent, void *match, void *aux)
79 {
80 	struct fdt_attach_args *faa = aux;
81 
82 	return OF_is_compatible(faa->fa_node, "amlogic,g12a-usb3-pcie-phy");
83 }
84 
85 void
amlpciephy_attach(struct device * parent,struct device * self,void * aux)86 amlpciephy_attach(struct device *parent, struct device *self, void *aux)
87 {
88 	struct amlpciephy_softc *sc = (struct amlpciephy_softc *)self;
89 	struct fdt_attach_args *faa = aux;
90 
91 	if (faa->fa_nreg < 1) {
92 		printf(": no registers\n");
93 		return;
94 	}
95 
96 	sc->sc_iot = faa->fa_iot;
97 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
98 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
99 		printf(": can't map registers\n");
100 		return;
101 	}
102 
103 	printf("\n");
104 
105 	sc->sc_pd.pd_node = faa->fa_node;
106 	sc->sc_pd.pd_cookie = sc;
107 	sc->sc_pd.pd_enable = amlpciephy_enable;
108 	phy_register(&sc->sc_pd);
109 }
110 
111 int
amlpciephy_enable(void * cookie,uint32_t * cells)112 amlpciephy_enable(void *cookie, uint32_t *cells)
113 {
114 	struct amlpciephy_softc *sc = cookie;
115 	int node = sc->sc_pd.pd_node;
116 	uint32_t type = cells[0];
117 	uint32_t reg;
118 
119 	/* Hardware can be switched between PCIe 2.0 and USB 3.0 mode. */
120 	if (type != PHY_TYPE_PCIE && type != PHY_TYPE_USB3)
121 		return -1;
122 
123 	clock_set_assigned(node);
124 	clock_enable_all(node);
125 
126 	switch (type) {
127 	case PHY_TYPE_PCIE:
128 		/* Power on. */
129 		reg = HREAD4(sc, PHY_R0);
130 		reg &= ~PHY_R0_PCIE_POWER_MASK;
131 		reg |= PHY_R0_PCIE_POWER_ON;
132 		HWRITE4(sc, PHY_R0, reg);
133 
134 		reset_assert_all(node);
135 		delay(500);
136 		reset_deassert_all(node);
137 		delay(500);
138 
139 		break;
140 	case PHY_TYPE_USB3:
141 		reset_assert_all(node);
142 		delay(10);
143 		reset_deassert_all(node);
144 
145 		/* Switch to USB 3.0 mode. */
146 		reg = HREAD4(sc, PHY_R0);
147 		reg &= ~PHY_R0_MODE_MASK;
148 		reg |= PHY_R0_MODE_USB3;
149 		HWRITE4(sc, PHY_R0, reg);
150 
151 		/* Workaround for SuperSpeed PHY suspend bug. */
152 		reg = amlpciephy_read(sc, 0x102d);
153 		reg |= (1 << 7);
154 		amlpciephy_write(sc, 0x102d, reg);
155 
156 		reg = amlpciephy_read(sc, 0x1010);
157 		reg &= ~0xff0;
158 		reg |= 0x10;
159 		amlpciephy_write(sc, 0x1010, reg);
160 
161 		/* Rx equalization magic. */
162 		reg = amlpciephy_read(sc, 0x1006);
163 		reg &= (1 << 6);
164 		reg |= (1 << 7);
165 		reg &= ~(0x7 << 8);
166 		reg |= (0x3 << 8);
167 		reg |= (1 << 11);
168 		amlpciephy_write(sc, 0x1006, reg);
169 
170 		/* Tx equalization magic. */
171 		reg = amlpciephy_read(sc, 0x1002);
172 		reg &= ~0x3f80;
173 		reg |= (0x16 << 7);
174 		reg &= ~0x7f;
175 		reg |= (0x7f | (1 << 14));
176 		amlpciephy_write(sc, 0x1002, reg);
177 
178 		/* MPLL loop magic. */
179 		reg = amlpciephy_read(sc, 0x30);
180 		reg &= ~(0xf << 4);
181 		reg |= (8 << 4);
182 		amlpciephy_write(sc, 0x30, reg);
183 
184 		break;
185 	}
186 
187 	return 0;
188 }
189 
190 void
amlpciephy_addr(struct amlpciephy_softc * sc,bus_addr_t addr)191 amlpciephy_addr(struct amlpciephy_softc *sc, bus_addr_t addr)
192 {
193 	int timo;
194 
195 	HWRITE4(sc, PHY_R4, addr << 2);
196 	HWRITE4(sc, PHY_R4, addr << 2);
197 	HWRITE4(sc, PHY_R4, (addr << 2) | PHY_R4_PHY_CR_CAP_ADDR);
198 	for (timo = 200; timo > 0; timo--) {
199 		if (HREAD4(sc, PHY_R5) & PHY_R5_PHY_CR_ACK)
200 			break;
201 		delay(5);
202 	}
203 	if (timo == 0) {
204 		printf("%s: timeout\n", __func__);
205 		return;
206 	}
207 	HWRITE4(sc, PHY_R4, addr << 2);
208 	for (timo = 200; timo > 0; timo--) {
209 		if ((HREAD4(sc, PHY_R5) & PHY_R5_PHY_CR_ACK) == 0)
210 			break;
211 		delay(5);
212 	}
213 	if (timo == 0) {
214 		printf("%s: timeout\n", __func__);
215 		return;
216 	}
217 }
218 
219 uint16_t
amlpciephy_read(struct amlpciephy_softc * sc,bus_addr_t addr)220 amlpciephy_read(struct amlpciephy_softc *sc, bus_addr_t addr)
221 {
222 	uint32_t reg;
223 	int timo;
224 
225 	amlpciephy_addr(sc, addr);
226 	HWRITE4(sc, PHY_R4, 0);
227 	HWRITE4(sc, PHY_R4, PHY_R4_PHY_CR_READ);
228 	for (timo = 200; timo > 0; timo--) {
229 		if (HREAD4(sc, PHY_R5) & PHY_R5_PHY_CR_ACK)
230 			break;
231 		delay(5);
232 	}
233 	if (timo == 0) {
234 		printf("%s: timeout\n", __func__);
235 		return 0;
236 	}
237 	reg = HREAD4(sc, PHY_R5);
238 	HWRITE4(sc, PHY_R4, 0);
239 	for (timo = 200; timo > 0; timo--) {
240 		if ((HREAD4(sc, PHY_R5) & PHY_R5_PHY_CR_ACK) == 0)
241 			break;
242 		delay(5);
243 	}
244 	if (timo == 0) {
245 		printf("%s: timeout\n", __func__);
246 		return 0;
247 	}
248 	return reg;
249 }
250 
251 void
amlpciephy_write(struct amlpciephy_softc * sc,bus_addr_t addr,uint16_t data)252 amlpciephy_write(struct amlpciephy_softc *sc, bus_addr_t addr, uint16_t data)
253 {
254 	int timo;
255 
256 	amlpciephy_addr(sc, addr);
257 	HWRITE4(sc, PHY_R4, data << 2);
258 	HWRITE4(sc, PHY_R4, data << 2);
259 	HWRITE4(sc, PHY_R4, data << 2 | PHY_R4_PHY_CR_CAP_DATA);
260 	for (timo = 200; timo > 0; timo--) {
261 		if (HREAD4(sc, PHY_R5) & PHY_R5_PHY_CR_ACK)
262 			break;
263 		delay(5);
264 	}
265 	if (timo == 0) {
266 		printf("%s: timeout\n", __func__);
267 		return;
268 	}
269 	HWRITE4(sc, PHY_R4, data << 2);
270 	for (timo = 200; timo > 0; timo--) {
271 		if ((HREAD4(sc, PHY_R5) & PHY_R5_PHY_CR_ACK) == 0)
272 			break;
273 		delay(5);
274 	}
275 	if (timo == 0) {
276 		printf("%s: timeout\n", __func__);
277 		return;
278 	}
279 
280 	HWRITE4(sc, PHY_R4, data << 2);
281 	HWRITE4(sc, PHY_R4, data << 2 | PHY_R4_PHY_CR_WRITE);
282 	for (timo = 200; timo > 0; timo--) {
283 		if (HREAD4(sc, PHY_R5) & PHY_R5_PHY_CR_ACK)
284 			break;
285 		delay(5);
286 	}
287 	if (timo == 0) {
288 		printf("%s: timeout\n", __func__);
289 		return;
290 	}
291 	HWRITE4(sc, PHY_R4, data << 2);
292 	for (timo = 200; timo > 0; timo--) {
293 		if ((HREAD4(sc, PHY_R5) & PHY_R5_PHY_CR_ACK) == 0)
294 			break;
295 		delay(5);
296 	}
297 	if (timo == 0) {
298 		printf("%s: timeout\n", __func__);
299 		return;
300 	}
301 }
302