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