xref: /openbsd-src/sys/dev/fdt/amlpciephy.c (revision 9fdf0c627b1fec102f212f847a6f7676c1829e65)
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