xref: /freebsd/sys/dev/etherswitch/ar40xx/ar40xx_hw_psgmii.c (revision a57ca37dd1848cd42844d9082c4a74c2ed57f68a)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2022 Adrian Chadd <adrian@FreeBSD.org>.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/param.h>
29 #include <sys/bus.h>
30 #include <sys/errno.h>
31 #include <sys/kernel.h>
32 #include <sys/malloc.h>
33 #include <sys/module.h>
34 #include <sys/socket.h>
35 #include <sys/sockio.h>
36 #include <sys/sysctl.h>
37 #include <sys/systm.h>
38 
39 #include <net/if.h>
40 #include <net/if_var.h>
41 #include <net/if_arp.h>
42 #include <net/ethernet.h>
43 #include <net/if_dl.h>
44 #include <net/if_media.h>
45 #include <net/if_types.h>
46 
47 #include <machine/bus.h>
48 #include <dev/iicbus/iic.h>
49 #include <dev/iicbus/iiconf.h>
50 #include <dev/iicbus/iicbus.h>
51 #include <dev/mii/mii.h>
52 #include <dev/mii/miivar.h>
53 #include <dev/mdio/mdio.h>
54 #include <dev/extres/clk/clk.h>
55 #include <dev/extres/hwreset/hwreset.h>
56 
57 #include <dev/fdt/fdt_common.h>
58 #include <dev/ofw/ofw_bus.h>
59 #include <dev/ofw/ofw_bus_subr.h>
60 
61 #include <dev/etherswitch/etherswitch.h>
62 
63 #include <dev/etherswitch/ar40xx/ar40xx_var.h>
64 #include <dev/etherswitch/ar40xx/ar40xx_reg.h>
65 #include <dev/etherswitch/ar40xx/ar40xx_hw.h>
66 #include <dev/etherswitch/ar40xx/ar40xx_phy.h>
67 #include <dev/etherswitch/ar40xx/ar40xx_hw_atu.h>
68 #include <dev/etherswitch/ar40xx/ar40xx_hw_mdio.h>
69 #include <dev/etherswitch/ar40xx/ar40xx_hw_psgmii.h>
70 
71 #include "mdio_if.h"
72 #include "miibus_if.h"
73 #include "etherswitch_if.h"
74 
75 /*
76  * Routines that control the ess-psgmii block - the interconnect
77  * between the ess-switch and the external multi-port PHY
78  * (eg Maple.)
79  */
80 
81 static void
82 ar40xx_hw_psgmii_reg_write(struct ar40xx_softc *sc, uint32_t reg,
83     uint32_t val)
84 {
85 	bus_space_write_4(sc->sc_psgmii_mem_tag, sc->sc_psgmii_mem_handle,
86 	    reg, val);
87 	bus_space_barrier(sc->sc_psgmii_mem_tag, sc->sc_psgmii_mem_handle,
88 	    0, sc->sc_psgmii_mem_size, BUS_SPACE_BARRIER_WRITE);
89 }
90 
91 static int
92 ar40xx_hw_psgmii_reg_read(struct ar40xx_softc *sc, uint32_t reg)
93 {
94 	int ret;
95 
96 	bus_space_barrier(sc->sc_psgmii_mem_tag, sc->sc_psgmii_mem_handle,
97 	    0, sc->sc_psgmii_mem_size, BUS_SPACE_BARRIER_READ);
98 	ret = bus_space_read_4(sc->sc_psgmii_mem_tag, sc->sc_psgmii_mem_handle,
99 	    reg);
100 
101 	return (ret);
102 }
103 
104 int
105 ar40xx_hw_psgmii_set_mac_mode(struct ar40xx_softc *sc, uint32_t mac_mode)
106 {
107 	if (mac_mode == PORT_WRAPPER_PSGMII) {
108 		ar40xx_hw_psgmii_reg_write(sc, AR40XX_PSGMII_MODE_CONTROL,
109 		    0x2200);
110 		ar40xx_hw_psgmii_reg_write(sc, AR40XX_PSGMIIPHY_TX_CONTROL,
111 		    0x8380);
112 	} else {
113 		device_printf(sc->sc_dev, "WARNING: unknown MAC_MODE=%u\n",
114 		    mac_mode);
115 	}
116 
117 	return (0);
118 }
119 
120 int
121 ar40xx_hw_psgmii_single_phy_testing(struct ar40xx_softc *sc, int phy)
122 {
123 	int j;
124 	uint32_t tx_ok, tx_error;
125 	uint32_t rx_ok, rx_error;
126 	uint32_t tx_ok_high16;
127 	uint32_t rx_ok_high16;
128 	uint32_t tx_all_ok, rx_all_ok;
129 
130 	MDIO_WRITEREG(sc->sc_mdio_dev, phy, 0x0, 0x9000);
131 	MDIO_WRITEREG(sc->sc_mdio_dev, phy, 0x0, 0x4140);
132 
133 	for (j = 0; j < AR40XX_PSGMII_CALB_NUM; j++) {
134 		uint16_t status;
135 
136 	status = MDIO_READREG(sc->sc_mdio_dev, phy, 0x11);
137 		if (status & AR40XX_PHY_SPEC_STATUS_LINK)
138 			break;
139 			/*
140 			 * the polling interval to check if the PHY link up
141 			 * or not
142 			 * maxwait_timer: 750 ms +/-10 ms
143 			 * minwait_timer : 1 us +/- 0.1us
144 			 * time resides in minwait_timer ~ maxwait_timer
145 			 * see IEEE 802.3 section 40.4.5.2
146 			 */
147 		DELAY(8 * 1000);
148 	}
149 
150 	/* enable check */
151 	ar40xx_hw_phy_mmd_write(sc, phy, 7, 0x8029, 0x0000);
152 	ar40xx_hw_phy_mmd_write(sc, phy, 7, 0x8029, 0x0003);
153 
154 	/* start traffic */
155 	ar40xx_hw_phy_mmd_write(sc, phy, 7, 0x8020, 0xa000);
156 	/*
157 	 *wait for all traffic end
158 	 * 4096(pkt num)*1524(size)*8ns(125MHz)=49.9ms
159 	 */
160 	DELAY(60 * 1000);
161 
162 	/* check counter */
163 	tx_ok = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802e);
164 	tx_ok_high16 = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802d);
165 	tx_error = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802f);
166 	rx_ok = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802b);
167 	rx_ok_high16 = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802a);
168 	rx_error = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802c);
169 	tx_all_ok = tx_ok + (tx_ok_high16 << 16);
170 	rx_all_ok = rx_ok + (rx_ok_high16 << 16);
171 
172 	if (tx_all_ok == 0x1000 && tx_error == 0) {
173 		/* success */
174 		sc->sc_psgmii.phy_t_status &= ~(1U << phy);
175 	} else {
176 		device_printf(sc->sc_dev, "TX_OK=%d, tx_error=%d RX_OK=%d"
177 		    " rx_error=%d\n",
178 		    tx_all_ok, tx_error, rx_all_ok, rx_error);
179 		device_printf(sc->sc_dev,
180 		    "PHY %d single test PSGMII issue happen!\n", phy);
181 		sc->sc_psgmii.phy_t_status |= BIT(phy);
182 	}
183 
184 	MDIO_WRITEREG(sc->sc_mdio_dev, phy, 0x0, 0x1840);
185 	return (0);
186 }
187 
188 int
189 ar40xx_hw_psgmii_all_phy_testing(struct ar40xx_softc *sc)
190 {
191 	int phy, j;
192 
193 	MDIO_WRITEREG(sc->sc_mdio_dev, 0x1f, 0x0, 0x9000);
194 	MDIO_WRITEREG(sc->sc_mdio_dev, 0x1f, 0x0, 0x4140);
195 
196 	for (j = 0; j < AR40XX_PSGMII_CALB_NUM; j++) {
197 		for (phy = 0; phy < AR40XX_NUM_PORTS - 1; phy++) {
198 			uint16_t status;
199 
200 			status = MDIO_READREG(sc->sc_mdio_dev, phy, 0x11);
201 			if (!(status & (1U << 10)))
202 				break;
203 		}
204 
205 		if (phy >= (AR40XX_NUM_PORTS - 1))
206 			break;
207 		/* The polling interval to check if the PHY link up or not */
208 		DELAY(8*1000);
209 	}
210 
211 	/* enable check */
212 	ar40xx_hw_phy_mmd_write(sc, 0x1f, 7, 0x8029, 0x0000);
213 	ar40xx_hw_phy_mmd_write(sc, 0x1f, 7, 0x8029, 0x0003);
214 
215 	/* start traffic */
216 	ar40xx_hw_phy_mmd_write(sc, 0x1f, 7, 0x8020, 0xa000);
217 	/*
218 	 * wait for all traffic end
219 	 * 4096(pkt num)*1524(size)*8ns(125MHz)=49.9ms
220 	 */
221 	DELAY(60*1000); /* was 50ms */
222 
223 	for (phy = 0; phy < AR40XX_NUM_PORTS - 1; phy++) {
224 		uint32_t tx_ok, tx_error;
225 		uint32_t rx_ok, rx_error;
226 		uint32_t tx_ok_high16;
227 		uint32_t rx_ok_high16;
228 		uint32_t tx_all_ok, rx_all_ok;
229 
230 		/* check counter */
231 		tx_ok = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802e);
232 		tx_ok_high16 = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802d);
233 		tx_error = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802f);
234 		rx_ok = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802b);
235 		rx_ok_high16 = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802a);
236 		rx_error = ar40xx_hw_phy_mmd_read(sc, phy, 7, 0x802c);
237 
238 		tx_all_ok = tx_ok + (tx_ok_high16<<16);
239 		rx_all_ok = rx_ok + (rx_ok_high16<<16);
240 		if (tx_all_ok == 0x1000 && tx_error == 0) {
241 			/* success */
242 			sc->sc_psgmii.phy_t_status &= ~(1U << (phy + 8));
243 		} else {
244 			device_printf(sc->sc_dev,
245 			    "PHY%d test see issue! (tx_all_ok=%u,"
246 			    " rx_all_ok=%u, tx_error=%u, rx_error=%u)\n",
247 			    phy, tx_all_ok, rx_all_ok, tx_error, rx_error);
248 			sc->sc_psgmii.phy_t_status |= (1U << (phy + 8));
249 		}
250 	}
251 
252 	device_printf(sc->sc_dev, "PHY all test 0x%x\n",
253 	    sc->sc_psgmii.phy_t_status);
254 	return (0);
255 }
256 
257 /*
258  * Reset PSGMII in the Malibu PHY.
259  */
260 int
261 ar40xx_hw_malibu_psgmii_ess_reset(struct ar40xx_softc *sc)
262 {
263 	device_printf(sc->sc_dev, "%s: called\n", __func__);
264 	uint32_t i;
265 
266 	/* reset phy psgmii */
267 	/* fix phy psgmii RX 20bit */
268 	MDIO_WRITEREG(sc->sc_mdio_dev, 5, 0x0, 0x005b);
269 	/* reset phy psgmii */
270 	MDIO_WRITEREG(sc->sc_mdio_dev, 5, 0x0, 0x001b);
271 	/* release reset phy psgmii */
272 	MDIO_WRITEREG(sc->sc_mdio_dev, 5, 0x0, 0x005b);
273 
274 	for (i = 0; i < AR40XX_PSGMII_CALB_NUM; i++) {
275 		uint32_t status;
276 
277 		status = ar40xx_hw_phy_mmd_read(sc, 5, 1, 0x28);
278 		if (status & (1U << 0))
279 			break;
280 		/*
281 		 * Polling interval to check PSGMII PLL in malibu is ready
282 		 * the worst time is 8.67ms
283 		 * for 25MHz reference clock
284 		 * [512+(128+2048)*49]*80ns+100us
285 		 */
286 		DELAY(2000);
287 	}
288 	/* XXX TODO ;see if it timed out? */
289 
290 	/*check malibu psgmii calibration done end..*/
291 
292 	/*freeze phy psgmii RX CDR*/
293 	MDIO_WRITEREG(sc->sc_mdio_dev, 5, 0x1a, 0x2230);
294 
295 	ar40xx_hw_ess_reset(sc);
296 
297 	/*check psgmii calibration done start*/
298 	for (i = 0; i < AR40XX_PSGMII_CALB_NUM; i++) {
299 		uint32_t status;
300 
301 		status = ar40xx_hw_psgmii_reg_read(sc, 0xa0);
302 		if (status & (1U << 0))
303 			break;
304 		/* Polling interval to check PSGMII PLL in ESS is ready */
305 		DELAY(2000);
306 	}
307 	/* XXX TODO ;see if it timed out? */
308 
309 	/* check dakota psgmii calibration done end..*/
310 
311 	/* release phy psgmii RX CDR */
312 	MDIO_WRITEREG(sc->sc_mdio_dev, 5, 0x1a, 0x3230);
313 	/* release phy psgmii RX 20bit */
314 	MDIO_WRITEREG(sc->sc_mdio_dev, 5, 0x0, 0x005f);
315 
316 	return (0);
317 }
318 
319 int
320 ar40xx_hw_psgmii_self_test(struct ar40xx_softc *sc)
321 {
322 	uint32_t i, phy, reg;
323 
324 	device_printf(sc->sc_dev, "%s: called\n", __func__);
325 
326 	ar40xx_hw_malibu_psgmii_ess_reset(sc);
327 
328 	/* switch to access MII reg for copper */
329 	MDIO_WRITEREG(sc->sc_mdio_dev, 4, 0x1f, 0x8500);
330 	for (phy = 0; phy < AR40XX_NUM_PORTS - 1; phy++) {
331 		/*enable phy mdio broadcast write*/
332 		ar40xx_hw_phy_mmd_write(sc, phy, 7, 0x8028, 0x801f);
333 	}
334 
335 	/* force no link by power down */
336 	MDIO_WRITEREG(sc->sc_mdio_dev, 0x1f, 0x0, 0x1840);
337 
338 	/* packet number*/
339 	ar40xx_hw_phy_mmd_write(sc, 0x1f, 7, 0x8021, 0x1000);
340 	ar40xx_hw_phy_mmd_write(sc, 0x1f, 7, 0x8062, 0x05e0);
341 
342 	/* fix mdi status */
343 	MDIO_WRITEREG(sc->sc_mdio_dev, 0x1f, 0x10, 0x6800);
344 	for (i = 0; i < AR40XX_PSGMII_CALB_NUM; i++) {
345 		sc->sc_psgmii.phy_t_status = 0;
346 
347 		for (phy = 0; phy < AR40XX_NUM_PORTS - 1; phy++) {
348 			/* Enable port loopback for testing */
349 			AR40XX_REG_BARRIER_READ(sc);
350 			reg = AR40XX_REG_READ(sc,
351 			    AR40XX_REG_PORT_LOOKUP(phy + 1));
352 			reg |= AR40XX_PORT_LOOKUP_LOOPBACK;
353 			AR40XX_REG_WRITE(sc,
354 			    AR40XX_REG_PORT_LOOKUP(phy + 1), reg);
355 			AR40XX_REG_BARRIER_WRITE(sc);
356 		}
357 
358 		for (phy = 0; phy < AR40XX_NUM_PORTS - 1; phy++)
359 			ar40xx_hw_psgmii_single_phy_testing(sc, phy);
360 
361 		ar40xx_hw_psgmii_all_phy_testing(sc);
362 
363 		if (sc->sc_psgmii.phy_t_status)
364 			ar40xx_hw_malibu_psgmii_ess_reset(sc);
365 		else
366 			break;
367 	}
368 
369 	if (i >= AR40XX_PSGMII_CALB_NUM)
370 		device_printf(sc->sc_dev, "PSGMII cannot recover\n");
371 	else
372 		device_printf(sc->sc_dev,
373 		    "PSGMII recovered after %d times reset\n", i);
374 
375 	/* configuration recover */
376 	/* packet number */
377 	ar40xx_hw_phy_mmd_write(sc, 0x1f, 7, 0x8021, 0x0);
378 	/* disable check */
379 	ar40xx_hw_phy_mmd_write(sc, 0x1f, 7, 0x8029, 0x0);
380 	/* disable traffic */
381 	ar40xx_hw_phy_mmd_write(sc, 0x1f, 7, 0x8020, 0x0);
382 
383 	return (0);
384 }
385 
386 int
387 ar40xx_hw_psgmii_self_test_clean(struct ar40xx_softc *sc)
388 {
389 	uint32_t reg;
390 	int phy;
391 
392 	device_printf(sc->sc_dev, "%s: called\n", __func__);
393 
394 	/* disable phy internal loopback */
395 	MDIO_WRITEREG(sc->sc_mdio_dev, 0x1f, 0x10, 0x6860);
396 	MDIO_WRITEREG(sc->sc_mdio_dev, 0x1f, 0x0, 0x9040);
397 
398         for (phy = 0; phy < AR40XX_NUM_PORTS - 1; phy++) {
399 		/* disable mac loop back */
400 		reg = AR40XX_REG_READ(sc, AR40XX_REG_PORT_LOOKUP(phy + 1));
401 		reg &= ~AR40XX_PORT_LOOKUP_LOOPBACK;
402 		AR40XX_REG_WRITE(sc, AR40XX_REG_PORT_LOOKUP(phy + 1), reg);
403 		AR40XX_REG_BARRIER_WRITE(sc);
404 
405 		/* disable phy mdio broadcast write */
406 		ar40xx_hw_phy_mmd_write(sc, phy, 7, 0x8028, 0x001f);
407 	}
408 
409 	/* clear fdb entry */
410 	ar40xx_hw_atu_flush_all(sc);
411 
412 	return (0);
413 }
414 
415 int
416 ar40xx_hw_psgmii_init_config(struct ar40xx_softc *sc)
417 {
418 	uint32_t reg;
419 
420 	/*
421 	 * This is based on what I found in uboot - it configures
422 	 * the initial ESS interconnect to either be PSGMII
423 	 * or RGMII.
424 	 */
425 
426 	/* For now, just assume PSGMII and fix it in post. */
427 	/* PSGMIIPHY_PLL_VCO_RELATED_CTRL */
428 	reg = ar40xx_hw_psgmii_reg_read(sc, 0x78c);
429 	device_printf(sc->sc_dev,
430 	    "%s: PSGMIIPHY_PLL_VCO_RELATED_CTRL=0x%08x\n", __func__, reg);
431 	/* PSGMIIPHY_VCO_CALIBRATION_CTRL */
432 	reg = ar40xx_hw_psgmii_reg_read(sc, 0x09c);
433 	device_printf(sc->sc_dev,
434 	    "%s: PSGMIIPHY_VCO_CALIBRATION_CTRL=0x%08x\n", __func__, reg);
435 
436 	return (0);
437 }
438