1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * (C) Copyright 2019 Rockchip Electronics Co., Ltd
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * SPDX-License-Identifier: GPL-2.0+
5*4882a593Smuzhiyun */
6*4882a593Smuzhiyun
7*4882a593Smuzhiyun #include <common.h>
8*4882a593Smuzhiyun #include <dm.h>
9*4882a593Smuzhiyun #include <fdtdec.h>
10*4882a593Smuzhiyun #include <malloc.h>
11*4882a593Smuzhiyun #include <miiphy.h>
12*4882a593Smuzhiyun #include <net.h>
13*4882a593Smuzhiyun #include <phy.h>
14*4882a593Smuzhiyun #include <asm/io.h>
15*4882a593Smuzhiyun #include "test-rockchip.h"
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun #ifdef CONFIG_GMAC_ROCKCHIP
18*4882a593Smuzhiyun #define LOOPBACK_TEST_HDR_SIZE 14
19*4882a593Smuzhiyun #define LOOPBACK_TEST_DATA_SIZE 1500
20*4882a593Smuzhiyun #define LOOPBACK_TEST_FRAME_SIZE (14 + 1500)
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun #define MAX_TX_DELAY_LINE 0X7F
23*4882a593Smuzhiyun #define MAX_RX_DELAY_LINE 0X7F
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun /* MAC configuration register definitions */
26*4882a593Smuzhiyun #define FRAMEBURSTENABLE (1 << 21)
27*4882a593Smuzhiyun #define MII_PORTSELECT (1 << 15)
28*4882a593Smuzhiyun #define FES_100 (1 << 14)
29*4882a593Smuzhiyun #define DISABLERXOWN (1 << 13)
30*4882a593Smuzhiyun #define FULLDPLXMODE (1 << 11)
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun enum loopback_speed {
33*4882a593Smuzhiyun LOOPBACK_SPEED_10 = 10,
34*4882a593Smuzhiyun LOOPBACK_SPEED_100 = 100,
35*4882a593Smuzhiyun LOOPBACK_SPEED_1000 = 1000
36*4882a593Smuzhiyun };
37*4882a593Smuzhiyun #ifdef CONFIG_RKIMG_BOOTLOADER
38*4882a593Smuzhiyun extern void gmac_set_rgmii(struct udevice *dev, u32 tx_delay, u32 rx_delay);
39*4882a593Smuzhiyun #endif
get_current_phydev(void)40*4882a593Smuzhiyun static struct phy_device *get_current_phydev(void)
41*4882a593Smuzhiyun {
42*4882a593Smuzhiyun struct mii_dev *bus = mdio_get_current_dev();
43*4882a593Smuzhiyun int i;
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun for (i = 0; i < PHY_MAX_ADDR; i++) {
46*4882a593Smuzhiyun if (bus->phymap[i])
47*4882a593Smuzhiyun return bus->phymap[i];
48*4882a593Smuzhiyun }
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun return NULL;
51*4882a593Smuzhiyun }
52*4882a593Smuzhiyun
create_lbtest_frame(uchar * data,unsigned int frame_size)53*4882a593Smuzhiyun static void create_lbtest_frame(uchar *data, unsigned int frame_size)
54*4882a593Smuzhiyun {
55*4882a593Smuzhiyun memset(data, 0xFF, frame_size);
56*4882a593Smuzhiyun frame_size &= ~1;
57*4882a593Smuzhiyun memset(data + (frame_size / 2), 0xAA, frame_size / 2 - 1);
58*4882a593Smuzhiyun }
59*4882a593Smuzhiyun
alter_lbtest_frame(uchar * data,unsigned int frame_size,unsigned int tx,unsigned int rx)60*4882a593Smuzhiyun static void alter_lbtest_frame(uchar *data, unsigned int frame_size,
61*4882a593Smuzhiyun unsigned int tx, unsigned int rx)
62*4882a593Smuzhiyun {
63*4882a593Smuzhiyun frame_size &= ~1;
64*4882a593Smuzhiyun memset(data + (frame_size / 2 + tx), 0xBE, 1);
65*4882a593Smuzhiyun memset(data + (frame_size / 2 + rx), 0xAF, 1);
66*4882a593Smuzhiyun }
67*4882a593Smuzhiyun
check_lbtest_frame(uchar * tx_data,uchar * rx_data,unsigned int frame_size)68*4882a593Smuzhiyun static int check_lbtest_frame(uchar *tx_data, uchar *rx_data,
69*4882a593Smuzhiyun unsigned int frame_size)
70*4882a593Smuzhiyun {
71*4882a593Smuzhiyun int i;
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun for (i = 0; i < frame_size; i++) {
74*4882a593Smuzhiyun if (tx_data[i] != rx_data[i])
75*4882a593Smuzhiyun return 13;
76*4882a593Smuzhiyun }
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun return 0;
79*4882a593Smuzhiyun }
80*4882a593Smuzhiyun
eth_setup_loopback_test(struct udevice * current,int speed)81*4882a593Smuzhiyun static void eth_setup_loopback_test(struct udevice *current, int speed)
82*4882a593Smuzhiyun {
83*4882a593Smuzhiyun struct phy_device *phydev = get_current_phydev();
84*4882a593Smuzhiyun struct eth_pdata *pdata = dev_get_platdata(current);
85*4882a593Smuzhiyun u32 conf;
86*4882a593Smuzhiyun int val;
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun if (!phydev) {
89*4882a593Smuzhiyun printf("%s, can't get phydev\n", __func__);
90*4882a593Smuzhiyun return;
91*4882a593Smuzhiyun }
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun /* set mac ctrl register */
94*4882a593Smuzhiyun conf = readl(pdata->iobase);
95*4882a593Smuzhiyun if (speed != LOOPBACK_SPEED_1000)
96*4882a593Smuzhiyun conf |= MII_PORTSELECT;
97*4882a593Smuzhiyun else
98*4882a593Smuzhiyun conf &= ~MII_PORTSELECT;
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun if (speed == LOOPBACK_SPEED_100)
101*4882a593Smuzhiyun conf |= FES_100;
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun if (phydev->duplex)
104*4882a593Smuzhiyun conf |= FULLDPLXMODE;
105*4882a593Smuzhiyun writel(conf, pdata->iobase);
106*4882a593Smuzhiyun
107*4882a593Smuzhiyun /* set phy ctrl register */
108*4882a593Smuzhiyun val = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
109*4882a593Smuzhiyun val &= ~(BMCR_ANENABLE | BMCR_PDOWN);
110*4882a593Smuzhiyun val |= BMCR_LOOPBACK;
111*4882a593Smuzhiyun if (speed == LOOPBACK_SPEED_1000) {
112*4882a593Smuzhiyun val |= BMCR_SPEED1000;
113*4882a593Smuzhiyun val &= ~BMCR_SPEED100;
114*4882a593Smuzhiyun } else if (speed == LOOPBACK_SPEED_100) {
115*4882a593Smuzhiyun val &= ~BMCR_SPEED1000;
116*4882a593Smuzhiyun val |= BMCR_SPEED100;
117*4882a593Smuzhiyun } else if (speed == LOOPBACK_SPEED_10) {
118*4882a593Smuzhiyun val &= ~BMCR_SPEED1000;
119*4882a593Smuzhiyun val &= ~BMCR_SPEED100;
120*4882a593Smuzhiyun }
121*4882a593Smuzhiyun val |= BMCR_FULLDPLX;
122*4882a593Smuzhiyun phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, val);
123*4882a593Smuzhiyun }
124*4882a593Smuzhiyun
eth_run_loopback_test(struct udevice * current,int speed,int delay_test)125*4882a593Smuzhiyun static int eth_run_loopback_test(struct udevice *current, int speed, int delay_test)
126*4882a593Smuzhiyun {
127*4882a593Smuzhiyun int flags = ETH_RECV_CHECK_DEVICE;
128*4882a593Smuzhiyun uchar *tx_pkt, *rx_packet;
129*4882a593Smuzhiyun int ret, length, success = 0;
130*4882a593Smuzhiyun u32 i, j;
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun /* make sure the net_tx_packet is initialized (net_init() was called) */
133*4882a593Smuzhiyun assert(net_tx_packet);
134*4882a593Smuzhiyun if (!net_tx_packet)
135*4882a593Smuzhiyun return -EINVAL;
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun net_set_ether(net_tx_packet, net_bcast_ethaddr, LOOPBACK_TEST_DATA_SIZE);
138*4882a593Smuzhiyun tx_pkt = (uchar *)net_tx_packet + LOOPBACK_TEST_HDR_SIZE;
139*4882a593Smuzhiyun create_lbtest_frame(tx_pkt, LOOPBACK_TEST_DATA_SIZE);
140*4882a593Smuzhiyun udelay(50000);
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun for (i = 0x0; i < MAX_TX_DELAY_LINE; i++) {
143*4882a593Smuzhiyun if (delay_test)
144*4882a593Smuzhiyun printf("[0x%02x]:", i);
145*4882a593Smuzhiyun for (j = 0x0; j < MAX_RX_DELAY_LINE; j++) {
146*4882a593Smuzhiyun #ifdef CONFIG_RKIMG_BOOTLOADER
147*4882a593Smuzhiyun if (delay_test)
148*4882a593Smuzhiyun gmac_set_rgmii(current, i, j);
149*4882a593Smuzhiyun #endif
150*4882a593Smuzhiyun alter_lbtest_frame(tx_pkt, LOOPBACK_TEST_DATA_SIZE, i, j);
151*4882a593Smuzhiyun net_send_packet(net_tx_packet, LOOPBACK_TEST_FRAME_SIZE);
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun /*
154*4882a593Smuzhiyun * Make sure that mac have enough delay time to
155*4882a593Smuzhiyun * receive packet.
156*4882a593Smuzhiyun */
157*4882a593Smuzhiyun if (speed == LOOPBACK_SPEED_10)
158*4882a593Smuzhiyun udelay(2000);
159*4882a593Smuzhiyun else if (speed == LOOPBACK_SPEED_100)
160*4882a593Smuzhiyun udelay(2000);
161*4882a593Smuzhiyun else
162*4882a593Smuzhiyun /* The default is 1000M speed */
163*4882a593Smuzhiyun udelay(200);
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun length = eth_get_ops(current)->recv(current, flags, &rx_packet);
166*4882a593Smuzhiyun if (length > 0) {
167*4882a593Smuzhiyun if (!check_lbtest_frame(net_tx_packet, rx_packet,
168*4882a593Smuzhiyun LOOPBACK_TEST_FRAME_SIZE)) {
169*4882a593Smuzhiyun printf("*");
170*4882a593Smuzhiyun success++;
171*4882a593Smuzhiyun ret = 0;
172*4882a593Smuzhiyun } else {
173*4882a593Smuzhiyun printf("x");
174*4882a593Smuzhiyun ret = -EINVAL;
175*4882a593Smuzhiyun }
176*4882a593Smuzhiyun } else if (length == 0) {
177*4882a593Smuzhiyun ret = -EBUSY;
178*4882a593Smuzhiyun printf("?");
179*4882a593Smuzhiyun } else {
180*4882a593Smuzhiyun ret = length;
181*4882a593Smuzhiyun printf(" ");
182*4882a593Smuzhiyun }
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun if (length >= 0 && eth_get_ops(current)->free_pkt)
185*4882a593Smuzhiyun eth_get_ops(current)->free_pkt(current, rx_packet,
186*4882a593Smuzhiyun length);
187*4882a593Smuzhiyun
188*4882a593Smuzhiyun /* Only run loopback test once */
189*4882a593Smuzhiyun if (!delay_test) {
190*4882a593Smuzhiyun printf("\n");
191*4882a593Smuzhiyun return ret;
192*4882a593Smuzhiyun }
193*4882a593Smuzhiyun }
194*4882a593Smuzhiyun printf("\n");
195*4882a593Smuzhiyun }
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun if (delay_test && success > 0)
198*4882a593Smuzhiyun ret = 0;
199*4882a593Smuzhiyun
200*4882a593Smuzhiyun return ret;
201*4882a593Smuzhiyun }
202*4882a593Smuzhiyun
ethernet_init(void)203*4882a593Smuzhiyun static int ethernet_init(void)
204*4882a593Smuzhiyun {
205*4882a593Smuzhiyun int ret = -EINVAL;
206*4882a593Smuzhiyun
207*4882a593Smuzhiyun net_init();
208*4882a593Smuzhiyun eth_halt();
209*4882a593Smuzhiyun eth_set_current();
210*4882a593Smuzhiyun ret = eth_init();
211*4882a593Smuzhiyun if (ret < 0) {
212*4882a593Smuzhiyun eth_halt();
213*4882a593Smuzhiyun return ret;
214*4882a593Smuzhiyun }
215*4882a593Smuzhiyun
216*4882a593Smuzhiyun return ret;
217*4882a593Smuzhiyun }
218*4882a593Smuzhiyun
eth_loopback_test(int speed,int delay_test)219*4882a593Smuzhiyun static int eth_loopback_test(int speed, int delay_test)
220*4882a593Smuzhiyun {
221*4882a593Smuzhiyun struct udevice *current;
222*4882a593Smuzhiyun int ret;
223*4882a593Smuzhiyun
224*4882a593Smuzhiyun current = eth_get_dev();
225*4882a593Smuzhiyun if (!current || !device_active(current))
226*4882a593Smuzhiyun return -EINVAL;
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun eth_setup_loopback_test(current, speed);
229*4882a593Smuzhiyun ret = ethernet_init();
230*4882a593Smuzhiyun if (ret) {
231*4882a593Smuzhiyun printf("%s, ethernet_init error: %d\n", __func__, ret);
232*4882a593Smuzhiyun return ret;
233*4882a593Smuzhiyun }
234*4882a593Smuzhiyun
235*4882a593Smuzhiyun ret = eth_run_loopback_test(current, speed, delay_test);
236*4882a593Smuzhiyun
237*4882a593Smuzhiyun return ret;
238*4882a593Smuzhiyun }
239*4882a593Smuzhiyun
do_eth_help(void)240*4882a593Smuzhiyun static void do_eth_help(void)
241*4882a593Smuzhiyun {
242*4882a593Smuzhiyun printf("Usage:\n");
243*4882a593Smuzhiyun printf("rktest eth loopback speed - Test the phy loopback, speed is 1000/100/10, need to unplug the RJ45 cable\n");
244*4882a593Smuzhiyun printf("rktest eth delaytest - Get the loopback-passed tx_delay/rx_delay array, need to unplug the RJ45 cable\n");
245*4882a593Smuzhiyun printf("rktest eth delayline tx_delay rx_delay - Delay value is 0x00~0x7f\n");
246*4882a593Smuzhiyun printf("rktest eth dhcp address IP:file - Boot image via network using DHCP/TFTP protocol, example: rktest eth dhcp 0x62000000 192.168.1.100:Image\n");
247*4882a593Smuzhiyun }
248*4882a593Smuzhiyun
do_test_eth(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])249*4882a593Smuzhiyun int do_test_eth(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
250*4882a593Smuzhiyun {
251*4882a593Smuzhiyun struct udevice *current;
252*4882a593Smuzhiyun u32 tx_delay, rx_delay;
253*4882a593Smuzhiyun char cmd_eth[512] = "dhcp $kernel_addr_r 172.16.12.246:golden/arm/rootfs.cpio.gz";
254*4882a593Smuzhiyun int i, speed;
255*4882a593Smuzhiyun int ret;
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun current = eth_get_dev();
258*4882a593Smuzhiyun if (!current || !device_active(current))
259*4882a593Smuzhiyun return -EINVAL;
260*4882a593Smuzhiyun
261*4882a593Smuzhiyun switch (argc) {
262*4882a593Smuzhiyun case 2:
263*4882a593Smuzhiyun if (!strncmp(argv[2], "delaytest", sizeof("delaytest"))) {
264*4882a593Smuzhiyun /* Force 1000 speed test */
265*4882a593Smuzhiyun speed = LOOPBACK_SPEED_1000;
266*4882a593Smuzhiyun ret = eth_loopback_test(speed, 1);
267*4882a593Smuzhiyun return ret;
268*4882a593Smuzhiyun } else if (!strncmp(argv[2], "help", sizeof("help"))) {
269*4882a593Smuzhiyun do_eth_help();
270*4882a593Smuzhiyun return 0;
271*4882a593Smuzhiyun }
272*4882a593Smuzhiyun break;
273*4882a593Smuzhiyun case 3:
274*4882a593Smuzhiyun if (!strncmp(argv[2], "loopback", sizeof("loopback"))) {
275*4882a593Smuzhiyun speed = simple_strtoul(argv[3], NULL, 0);
276*4882a593Smuzhiyun ret = eth_loopback_test(speed, 0);
277*4882a593Smuzhiyun return ret;
278*4882a593Smuzhiyun }
279*4882a593Smuzhiyun break;
280*4882a593Smuzhiyun case 4:
281*4882a593Smuzhiyun if (!strncmp(argv[2], "delayline", sizeof("delayline"))) {
282*4882a593Smuzhiyun tx_delay = simple_strtoul(argv[3], NULL, 0);
283*4882a593Smuzhiyun rx_delay = simple_strtoul(argv[4], NULL, 0);
284*4882a593Smuzhiyun #ifdef CONFIG_RKIMG_BOOTLOADER
285*4882a593Smuzhiyun gmac_set_rgmii(current, tx_delay, rx_delay);
286*4882a593Smuzhiyun #endif
287*4882a593Smuzhiyun return 0;
288*4882a593Smuzhiyun }
289*4882a593Smuzhiyun break;
290*4882a593Smuzhiyun default:
291*4882a593Smuzhiyun break;
292*4882a593Smuzhiyun }
293*4882a593Smuzhiyun
294*4882a593Smuzhiyun for (i = 1; i < argc; i++) {
295*4882a593Smuzhiyun if (i == 1)
296*4882a593Smuzhiyun sprintf(cmd_eth, argv[i]);
297*4882a593Smuzhiyun else
298*4882a593Smuzhiyun strncat(cmd_eth, argv[i], sizeof(argv[i]));
299*4882a593Smuzhiyun if (i < argc - 1)
300*4882a593Smuzhiyun strncat(cmd_eth, " ", sizeof(" "));
301*4882a593Smuzhiyun }
302*4882a593Smuzhiyun
303*4882a593Smuzhiyun /* run dhcp/tftp test */
304*4882a593Smuzhiyun ret = run_command(cmd_eth, 0);
305*4882a593Smuzhiyun if (ret) {
306*4882a593Smuzhiyun printf("DHCP test error: %d\n", ret);
307*4882a593Smuzhiyun return ret;
308*4882a593Smuzhiyun }
309*4882a593Smuzhiyun
310*4882a593Smuzhiyun return 0;
311*4882a593Smuzhiyun }
312*4882a593Smuzhiyun #endif
313*4882a593Smuzhiyun
314*4882a593Smuzhiyun static cmd_tbl_t sub_cmd[] = {
315*4882a593Smuzhiyun #ifdef CONFIG_GMAC_ROCKCHIP
316*4882a593Smuzhiyun UNIT_CMD_DEFINE(eth, 0),
317*4882a593Smuzhiyun #endif
318*4882a593Smuzhiyun };
319*4882a593Smuzhiyun
320*4882a593Smuzhiyun static const char sub_cmd_help[] =
321*4882a593Smuzhiyun #ifdef CONFIG_GMAC_ROCKCHIP
322*4882a593Smuzhiyun " [i] rktest eth - test ethernet\n"
323*4882a593Smuzhiyun #else
324*4882a593Smuzhiyun ""
325*4882a593Smuzhiyun #endif
326*4882a593Smuzhiyun ;
327*4882a593Smuzhiyun
328*4882a593Smuzhiyun struct cmd_group cmd_grp_net = {
329*4882a593Smuzhiyun .id = TEST_ID_NET,
330*4882a593Smuzhiyun .help = sub_cmd_help,
331*4882a593Smuzhiyun .cmd = sub_cmd,
332*4882a593Smuzhiyun .cmd_n = ARRAY_SIZE(sub_cmd),
333*4882a593Smuzhiyun };
334