xref: /rk3399_rockchip-uboot/test/rockchip/test-net.c (revision 21a92a158bb238f14e906aae6475cf9fcb434db1)
199d14b01SJoseph Chen /*
299d14b01SJoseph Chen  * (C) Copyright 2019 Rockchip Electronics Co., Ltd
399d14b01SJoseph Chen  *
499d14b01SJoseph Chen  * SPDX-License-Identifier:     GPL-2.0+
599d14b01SJoseph Chen  */
699d14b01SJoseph Chen 
799d14b01SJoseph Chen #include <common.h>
899d14b01SJoseph Chen #include <dm.h>
999d14b01SJoseph Chen #include <fdtdec.h>
1099d14b01SJoseph Chen #include <malloc.h>
1199d14b01SJoseph Chen #include <miiphy.h>
1299d14b01SJoseph Chen #include <net.h>
1399d14b01SJoseph Chen #include <phy.h>
1499d14b01SJoseph Chen #include <asm/io.h>
1599d14b01SJoseph Chen #include "test-rockchip.h"
1699d14b01SJoseph Chen 
1799d14b01SJoseph Chen #ifdef CONFIG_GMAC_ROCKCHIP
1899d14b01SJoseph Chen #define LOOPBACK_TEST_HDR_SIZE		14
1999d14b01SJoseph Chen #define LOOPBACK_TEST_DATA_SIZE		1500
2099d14b01SJoseph Chen #define LOOPBACK_TEST_FRAME_SIZE	(14 + 1500)
2199d14b01SJoseph Chen 
2299d14b01SJoseph Chen #define MAX_TX_DELAY_LINE		0X7F
2399d14b01SJoseph Chen #define MAX_RX_DELAY_LINE		0X7F
2499d14b01SJoseph Chen 
2599d14b01SJoseph Chen /* MAC configuration register definitions */
2699d14b01SJoseph Chen #define FRAMEBURSTENABLE		(1 << 21)
2799d14b01SJoseph Chen #define MII_PORTSELECT			(1 << 15)
2899d14b01SJoseph Chen #define FES_100				(1 << 14)
2999d14b01SJoseph Chen #define DISABLERXOWN			(1 << 13)
3099d14b01SJoseph Chen #define FULLDPLXMODE			(1 << 11)
3199d14b01SJoseph Chen 
3299d14b01SJoseph Chen enum loopback_speed {
3399d14b01SJoseph Chen 	LOOPBACK_SPEED_10	= 10,
3499d14b01SJoseph Chen 	LOOPBACK_SPEED_100	= 100,
3599d14b01SJoseph Chen 	LOOPBACK_SPEED_1000	= 1000
3699d14b01SJoseph Chen };
3754108d04SKever Yang #ifdef CONFIG_RKIMG_BOOTLOADER
3899d14b01SJoseph Chen extern void gmac_set_rgmii(struct udevice *dev, u32 tx_delay, u32 rx_delay);
3954108d04SKever Yang #endif
get_current_phydev(void)4099d14b01SJoseph Chen static struct phy_device *get_current_phydev(void)
4199d14b01SJoseph Chen {
4299d14b01SJoseph Chen 	struct mii_dev *bus = mdio_get_current_dev();
4399d14b01SJoseph Chen 	int i;
4499d14b01SJoseph Chen 
4599d14b01SJoseph Chen 	for (i = 0; i < PHY_MAX_ADDR; i++) {
4699d14b01SJoseph Chen 		if (bus->phymap[i])
4799d14b01SJoseph Chen 			return bus->phymap[i];
4899d14b01SJoseph Chen 	}
4999d14b01SJoseph Chen 
5099d14b01SJoseph Chen 	return NULL;
5199d14b01SJoseph Chen }
5299d14b01SJoseph Chen 
create_lbtest_frame(uchar * data,unsigned int frame_size)5399d14b01SJoseph Chen static void create_lbtest_frame(uchar *data, unsigned int frame_size)
5499d14b01SJoseph Chen {
5599d14b01SJoseph Chen 	memset(data, 0xFF, frame_size);
5699d14b01SJoseph Chen 	frame_size &= ~1;
5799d14b01SJoseph Chen 	memset(data + (frame_size / 2), 0xAA, frame_size / 2 - 1);
5899d14b01SJoseph Chen }
5999d14b01SJoseph Chen 
alter_lbtest_frame(uchar * data,unsigned int frame_size,unsigned int tx,unsigned int rx)6099d14b01SJoseph Chen static void alter_lbtest_frame(uchar *data, unsigned int frame_size,
6199d14b01SJoseph Chen 			       unsigned int tx, unsigned int rx)
6299d14b01SJoseph Chen {
6399d14b01SJoseph Chen 	frame_size &= ~1;
6499d14b01SJoseph Chen 	memset(data + (frame_size / 2 + tx), 0xBE, 1);
6599d14b01SJoseph Chen 	memset(data + (frame_size / 2 + rx), 0xAF, 1);
6699d14b01SJoseph Chen }
6799d14b01SJoseph Chen 
check_lbtest_frame(uchar * tx_data,uchar * rx_data,unsigned int frame_size)6899d14b01SJoseph Chen static int check_lbtest_frame(uchar *tx_data, uchar *rx_data,
6999d14b01SJoseph Chen 			      unsigned int frame_size)
7099d14b01SJoseph Chen {
7199d14b01SJoseph Chen 	int i;
7299d14b01SJoseph Chen 
7399d14b01SJoseph Chen 	for (i = 0; i < frame_size; i++) {
7499d14b01SJoseph Chen 		if (tx_data[i] != rx_data[i])
7599d14b01SJoseph Chen 			return 13;
7699d14b01SJoseph Chen 	}
7799d14b01SJoseph Chen 
7899d14b01SJoseph Chen 	return 0;
7999d14b01SJoseph Chen }
8099d14b01SJoseph Chen 
eth_setup_loopback_test(struct udevice * current,int speed)8199d14b01SJoseph Chen static void eth_setup_loopback_test(struct udevice *current, int speed)
8299d14b01SJoseph Chen {
8399d14b01SJoseph Chen 	struct phy_device *phydev = get_current_phydev();
8499d14b01SJoseph Chen 	struct eth_pdata *pdata = dev_get_platdata(current);
8599d14b01SJoseph Chen 	u32 conf;
8699d14b01SJoseph Chen 	int val;
8799d14b01SJoseph Chen 
8899d14b01SJoseph Chen 	if (!phydev) {
8999d14b01SJoseph Chen 		printf("%s, can't get phydev\n", __func__);
9099d14b01SJoseph Chen 		return;
9199d14b01SJoseph Chen 	}
9299d14b01SJoseph Chen 
9399d14b01SJoseph Chen 	/* set mac ctrl register */
9499d14b01SJoseph Chen 	conf = readl(pdata->iobase);
9599d14b01SJoseph Chen 	if (speed != LOOPBACK_SPEED_1000)
9699d14b01SJoseph Chen 		conf |= MII_PORTSELECT;
9799d14b01SJoseph Chen 	else
9899d14b01SJoseph Chen 		conf &= ~MII_PORTSELECT;
9999d14b01SJoseph Chen 
10099d14b01SJoseph Chen 	if (speed == LOOPBACK_SPEED_100)
10199d14b01SJoseph Chen 		conf |= FES_100;
10299d14b01SJoseph Chen 
10399d14b01SJoseph Chen 	if (phydev->duplex)
10499d14b01SJoseph Chen 		conf |= FULLDPLXMODE;
10599d14b01SJoseph Chen 	writel(conf, pdata->iobase);
10699d14b01SJoseph Chen 
10799d14b01SJoseph Chen 	/* set phy ctrl register */
10899d14b01SJoseph Chen 	val = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
10999d14b01SJoseph Chen 	val &= ~(BMCR_ANENABLE | BMCR_PDOWN);
11099d14b01SJoseph Chen 	val |= BMCR_LOOPBACK;
11199d14b01SJoseph Chen 	if (speed == LOOPBACK_SPEED_1000) {
11299d14b01SJoseph Chen 		val |= BMCR_SPEED1000;
11399d14b01SJoseph Chen 		val &= ~BMCR_SPEED100;
11499d14b01SJoseph Chen 	} else if (speed == LOOPBACK_SPEED_100) {
11599d14b01SJoseph Chen 		val &= ~BMCR_SPEED1000;
11699d14b01SJoseph Chen 		val |= BMCR_SPEED100;
11799d14b01SJoseph Chen 	} else if (speed == LOOPBACK_SPEED_10) {
11899d14b01SJoseph Chen 		val &= ~BMCR_SPEED1000;
11999d14b01SJoseph Chen 		val &= ~BMCR_SPEED100;
12099d14b01SJoseph Chen 	}
12199d14b01SJoseph Chen 	val |= BMCR_FULLDPLX;
12299d14b01SJoseph Chen 	phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, val);
12399d14b01SJoseph Chen }
12499d14b01SJoseph Chen 
eth_run_loopback_test(struct udevice * current,int speed,int delay_test)12599d14b01SJoseph Chen static int eth_run_loopback_test(struct udevice *current, int speed, int delay_test)
12699d14b01SJoseph Chen {
12799d14b01SJoseph Chen 	int flags = ETH_RECV_CHECK_DEVICE;
12899d14b01SJoseph Chen 	uchar *tx_pkt, *rx_packet;
12999d14b01SJoseph Chen 	int ret, length, success = 0;
13099d14b01SJoseph Chen 	u32 i, j;
13199d14b01SJoseph Chen 
13299d14b01SJoseph Chen 	/* make sure the net_tx_packet is initialized (net_init() was called) */
13399d14b01SJoseph Chen 	assert(net_tx_packet);
13499d14b01SJoseph Chen 	if (!net_tx_packet)
13599d14b01SJoseph Chen 		return -EINVAL;
13699d14b01SJoseph Chen 
13799d14b01SJoseph Chen 	net_set_ether(net_tx_packet, net_bcast_ethaddr, LOOPBACK_TEST_DATA_SIZE);
13899d14b01SJoseph Chen 	tx_pkt = (uchar *)net_tx_packet + LOOPBACK_TEST_HDR_SIZE;
13999d14b01SJoseph Chen 	create_lbtest_frame(tx_pkt, LOOPBACK_TEST_DATA_SIZE);
14099d14b01SJoseph Chen 	udelay(50000);
14199d14b01SJoseph Chen 
14299d14b01SJoseph Chen 	for (i = 0x0; i < MAX_TX_DELAY_LINE; i++) {
14399d14b01SJoseph Chen 		if (delay_test)
14499d14b01SJoseph Chen 			printf("[0x%02x]:", i);
14599d14b01SJoseph Chen 		for (j = 0x0; j < MAX_RX_DELAY_LINE; j++) {
14654108d04SKever Yang #ifdef CONFIG_RKIMG_BOOTLOADER
14799d14b01SJoseph Chen 			if (delay_test)
14899d14b01SJoseph Chen 				gmac_set_rgmii(current, i, j);
14954108d04SKever Yang #endif
15099d14b01SJoseph Chen 			alter_lbtest_frame(tx_pkt, LOOPBACK_TEST_DATA_SIZE, i, j);
15199d14b01SJoseph Chen 			net_send_packet(net_tx_packet, LOOPBACK_TEST_FRAME_SIZE);
15299d14b01SJoseph Chen 
15399d14b01SJoseph Chen 			/*
15499d14b01SJoseph Chen 			 * Make sure that mac have enough delay time to
15599d14b01SJoseph Chen 			 * receive packet.
15699d14b01SJoseph Chen 			 */
15799d14b01SJoseph Chen 			if (speed == LOOPBACK_SPEED_10)
15899d14b01SJoseph Chen 				udelay(2000);
15999d14b01SJoseph Chen 			else if (speed == LOOPBACK_SPEED_100)
16099d14b01SJoseph Chen 				udelay(2000);
16199d14b01SJoseph Chen 			else
16299d14b01SJoseph Chen 				/* The default is 1000M speed */
16399d14b01SJoseph Chen 				udelay(200);
16499d14b01SJoseph Chen 
16599d14b01SJoseph Chen 			length = eth_get_ops(current)->recv(current, flags, &rx_packet);
16699d14b01SJoseph Chen 			if (length > 0) {
16799d14b01SJoseph Chen 				if (!check_lbtest_frame(net_tx_packet, rx_packet,
16899d14b01SJoseph Chen 							LOOPBACK_TEST_FRAME_SIZE)) {
16999d14b01SJoseph Chen 					printf("*");
17099d14b01SJoseph Chen 					success++;
17199d14b01SJoseph Chen 					ret = 0;
17299d14b01SJoseph Chen 				} else {
17399d14b01SJoseph Chen 					printf("x");
17499d14b01SJoseph Chen 					ret = -EINVAL;
17599d14b01SJoseph Chen 				}
17699d14b01SJoseph Chen 			} else if (length == 0) {
17799d14b01SJoseph Chen 				ret = -EBUSY;
17899d14b01SJoseph Chen 				printf("?");
17999d14b01SJoseph Chen 			} else {
18099d14b01SJoseph Chen 				ret = length;
18199d14b01SJoseph Chen 				printf(" ");
18299d14b01SJoseph Chen 			}
18399d14b01SJoseph Chen 
18499d14b01SJoseph Chen 			if (length >= 0 && eth_get_ops(current)->free_pkt)
18599d14b01SJoseph Chen 				eth_get_ops(current)->free_pkt(current, rx_packet,
18699d14b01SJoseph Chen 							       length);
18799d14b01SJoseph Chen 
18899d14b01SJoseph Chen 			/* Only run loopback test once */
18999d14b01SJoseph Chen 			if (!delay_test) {
19099d14b01SJoseph Chen 				printf("\n");
19199d14b01SJoseph Chen 				return ret;
19299d14b01SJoseph Chen 			}
19399d14b01SJoseph Chen 		}
19499d14b01SJoseph Chen 		printf("\n");
19599d14b01SJoseph Chen 	}
19699d14b01SJoseph Chen 
19799d14b01SJoseph Chen 	if (delay_test && success > 0)
19899d14b01SJoseph Chen 		ret = 0;
19999d14b01SJoseph Chen 
20099d14b01SJoseph Chen 	return ret;
20199d14b01SJoseph Chen }
20299d14b01SJoseph Chen 
ethernet_init(void)20399d14b01SJoseph Chen static int ethernet_init(void)
20499d14b01SJoseph Chen {
20599d14b01SJoseph Chen 	int ret = -EINVAL;
20699d14b01SJoseph Chen 
20799d14b01SJoseph Chen 	net_init();
20899d14b01SJoseph Chen 	eth_halt();
20999d14b01SJoseph Chen 	eth_set_current();
21099d14b01SJoseph Chen 	ret = eth_init();
21199d14b01SJoseph Chen 	if (ret < 0) {
21299d14b01SJoseph Chen 		eth_halt();
21399d14b01SJoseph Chen 		return ret;
21499d14b01SJoseph Chen 	}
21599d14b01SJoseph Chen 
21699d14b01SJoseph Chen 	return ret;
21799d14b01SJoseph Chen }
21899d14b01SJoseph Chen 
eth_loopback_test(int speed,int delay_test)21999d14b01SJoseph Chen static int eth_loopback_test(int speed, int delay_test)
22099d14b01SJoseph Chen {
22199d14b01SJoseph Chen 	struct udevice *current;
22299d14b01SJoseph Chen 	int ret;
22399d14b01SJoseph Chen 
22499d14b01SJoseph Chen 	current = eth_get_dev();
22599d14b01SJoseph Chen 	if (!current || !device_active(current))
22699d14b01SJoseph Chen 		return -EINVAL;
22799d14b01SJoseph Chen 
22899d14b01SJoseph Chen 	eth_setup_loopback_test(current, speed);
22999d14b01SJoseph Chen 	ret = ethernet_init();
23099d14b01SJoseph Chen 	if (ret) {
23199d14b01SJoseph Chen 		printf("%s, ethernet_init error: %d\n", __func__, ret);
23299d14b01SJoseph Chen 		return ret;
23399d14b01SJoseph Chen 	}
23499d14b01SJoseph Chen 
23599d14b01SJoseph Chen 	ret = eth_run_loopback_test(current, speed, delay_test);
23699d14b01SJoseph Chen 
23799d14b01SJoseph Chen 	return ret;
23899d14b01SJoseph Chen }
23999d14b01SJoseph Chen 
do_eth_help(void)24099d14b01SJoseph Chen static void do_eth_help(void)
24199d14b01SJoseph Chen {
24299d14b01SJoseph Chen 	printf("Usage:\n");
24399d14b01SJoseph Chen 	printf("rktest eth loopback speed - Test the phy loopback, speed is 1000/100/10, need to unplug the RJ45 cable\n");
24499d14b01SJoseph Chen 	printf("rktest eth delaytest - Get the loopback-passed tx_delay/rx_delay array, need to unplug the RJ45 cable\n");
24599d14b01SJoseph Chen 	printf("rktest eth delayline tx_delay rx_delay - Delay value is 0x00~0x7f\n");
24699d14b01SJoseph Chen 	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");
24799d14b01SJoseph Chen }
24899d14b01SJoseph Chen 
do_test_eth(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])24999d14b01SJoseph Chen int do_test_eth(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
25099d14b01SJoseph Chen {
25199d14b01SJoseph Chen 	struct udevice *current;
25299d14b01SJoseph Chen 	u32 tx_delay, rx_delay;
253*21a92a15SKever Yang 	char cmd_eth[512] = "dhcp $kernel_addr_r 172.16.12.246:golden/arm/rootfs.cpio.gz";
25499d14b01SJoseph Chen 	int i, speed;
25599d14b01SJoseph Chen 	int ret;
25699d14b01SJoseph Chen 
25799d14b01SJoseph Chen 	current = eth_get_dev();
25899d14b01SJoseph Chen 	if (!current || !device_active(current))
25999d14b01SJoseph Chen 		return -EINVAL;
26099d14b01SJoseph Chen 
26199d14b01SJoseph Chen 	switch (argc) {
262*21a92a15SKever Yang 	case 2:
26399d14b01SJoseph Chen 		if (!strncmp(argv[2], "delaytest", sizeof("delaytest"))) {
26499d14b01SJoseph Chen 			/* Force 1000 speed test */
26599d14b01SJoseph Chen 			speed = LOOPBACK_SPEED_1000;
26699d14b01SJoseph Chen 			ret = eth_loopback_test(speed, 1);
26799d14b01SJoseph Chen 			return ret;
26899d14b01SJoseph Chen 		} else if (!strncmp(argv[2], "help", sizeof("help"))) {
26999d14b01SJoseph Chen 			do_eth_help();
27099d14b01SJoseph Chen 			return 0;
27199d14b01SJoseph Chen 		}
27299d14b01SJoseph Chen 		break;
273*21a92a15SKever Yang 	case 3:
27499d14b01SJoseph Chen 		if (!strncmp(argv[2], "loopback", sizeof("loopback"))) {
27599d14b01SJoseph Chen 			speed = simple_strtoul(argv[3], NULL, 0);
27699d14b01SJoseph Chen 			ret = eth_loopback_test(speed, 0);
27799d14b01SJoseph Chen 			return ret;
27899d14b01SJoseph Chen 		}
27999d14b01SJoseph Chen 		break;
280*21a92a15SKever Yang 	case 4:
28199d14b01SJoseph Chen 		if (!strncmp(argv[2], "delayline", sizeof("delayline"))) {
28299d14b01SJoseph Chen 			tx_delay = simple_strtoul(argv[3], NULL, 0);
28399d14b01SJoseph Chen 			rx_delay = simple_strtoul(argv[4], NULL, 0);
28454108d04SKever Yang #ifdef CONFIG_RKIMG_BOOTLOADER
28599d14b01SJoseph Chen 			gmac_set_rgmii(current, tx_delay, rx_delay);
28654108d04SKever Yang #endif
28799d14b01SJoseph Chen 			return 0;
28899d14b01SJoseph Chen 		}
28999d14b01SJoseph Chen 		break;
29099d14b01SJoseph Chen 	default:
29199d14b01SJoseph Chen 		break;
29299d14b01SJoseph Chen 	}
29399d14b01SJoseph Chen 
294*21a92a15SKever Yang 	for (i = 1; i < argc; i++) {
295*21a92a15SKever Yang 		if (i == 1)
296*21a92a15SKever Yang 			sprintf(cmd_eth, argv[i]);
297*21a92a15SKever Yang 		else
298*21a92a15SKever Yang 			strncat(cmd_eth, argv[i], sizeof(argv[i]));
29999d14b01SJoseph Chen 		if (i < argc - 1)
30099d14b01SJoseph Chen 			strncat(cmd_eth, " ", sizeof(" "));
30199d14b01SJoseph Chen 	}
30299d14b01SJoseph Chen 
30399d14b01SJoseph Chen 	/* run dhcp/tftp test */
30499d14b01SJoseph Chen 	ret = run_command(cmd_eth, 0);
305*21a92a15SKever Yang 	if (ret) {
30699d14b01SJoseph Chen 		printf("DHCP test error: %d\n", ret);
30799d14b01SJoseph Chen 		return ret;
30899d14b01SJoseph Chen 	}
30999d14b01SJoseph Chen 
31099d14b01SJoseph Chen 	return 0;
31199d14b01SJoseph Chen }
31299d14b01SJoseph Chen #endif
31399d14b01SJoseph Chen 
31499d14b01SJoseph Chen static cmd_tbl_t sub_cmd[] = {
31599d14b01SJoseph Chen #ifdef CONFIG_GMAC_ROCKCHIP
31699d14b01SJoseph Chen 	UNIT_CMD_DEFINE(eth, 0),
31799d14b01SJoseph Chen #endif
31899d14b01SJoseph Chen };
31999d14b01SJoseph Chen 
32099d14b01SJoseph Chen static const char sub_cmd_help[] =
32199d14b01SJoseph Chen #ifdef CONFIG_GMAC_ROCKCHIP
32299d14b01SJoseph Chen "    [i] rktest eth                         - test ethernet\n"
32399d14b01SJoseph Chen #else
32499d14b01SJoseph Chen ""
32599d14b01SJoseph Chen #endif
32699d14b01SJoseph Chen ;
32799d14b01SJoseph Chen 
32899d14b01SJoseph Chen struct cmd_group cmd_grp_net = {
32999d14b01SJoseph Chen 	.id	= TEST_ID_NET,
33099d14b01SJoseph Chen 	.help	= sub_cmd_help,
33199d14b01SJoseph Chen 	.cmd	= sub_cmd,
33299d14b01SJoseph Chen 	.cmd_n	= ARRAY_SIZE(sub_cmd),
33399d14b01SJoseph Chen };
334