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