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 }; 37*54108d04SKever Yang #ifdef CONFIG_RKIMG_BOOTLOADER 3899d14b01SJoseph Chen extern void gmac_set_rgmii(struct udevice *dev, u32 tx_delay, u32 rx_delay); 39*54108d04SKever Yang #endif 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 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 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 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 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 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++) { 146*54108d04SKever Yang #ifdef CONFIG_RKIMG_BOOTLOADER 14799d14b01SJoseph Chen if (delay_test) 14899d14b01SJoseph Chen gmac_set_rgmii(current, i, j); 149*54108d04SKever 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 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 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 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 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; 25399d14b01SJoseph Chen char cmd_eth[512] = {0}; 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) { 26299d14b01SJoseph Chen case 3: 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; 27399d14b01SJoseph Chen case 4: 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; 28099d14b01SJoseph Chen case 5: 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); 284*54108d04SKever Yang #ifdef CONFIG_RKIMG_BOOTLOADER 28599d14b01SJoseph Chen gmac_set_rgmii(current, tx_delay, rx_delay); 286*54108d04SKever Yang #endif 28799d14b01SJoseph Chen return 0; 28899d14b01SJoseph Chen } 28999d14b01SJoseph Chen break; 29099d14b01SJoseph Chen default: 29199d14b01SJoseph Chen break; 29299d14b01SJoseph Chen } 29399d14b01SJoseph Chen 29499d14b01SJoseph Chen for (i = 2; i < argc; i++) { 29599d14b01SJoseph Chen strncat(cmd_eth, argv[i], sizeof(cmd_eth)); 29699d14b01SJoseph Chen if (i < argc - 1) 29799d14b01SJoseph Chen strncat(cmd_eth, " ", sizeof(" ")); 29899d14b01SJoseph Chen } 29999d14b01SJoseph Chen 30099d14b01SJoseph Chen /* run dhcp/tftp test */ 30199d14b01SJoseph Chen ret = run_command(cmd_eth, 0); 30299d14b01SJoseph Chen if (ret < 0) { 30399d14b01SJoseph Chen printf("DHCP test error: %d\n", ret); 30499d14b01SJoseph Chen return ret; 30599d14b01SJoseph Chen } 30699d14b01SJoseph Chen 30799d14b01SJoseph Chen return 0; 30899d14b01SJoseph Chen } 30999d14b01SJoseph Chen #endif 31099d14b01SJoseph Chen 31199d14b01SJoseph Chen static cmd_tbl_t sub_cmd[] = { 31299d14b01SJoseph Chen #ifdef CONFIG_GMAC_ROCKCHIP 31399d14b01SJoseph Chen UNIT_CMD_DEFINE(eth, 0), 31499d14b01SJoseph Chen #endif 31599d14b01SJoseph Chen }; 31699d14b01SJoseph Chen 31799d14b01SJoseph Chen static const char sub_cmd_help[] = 31899d14b01SJoseph Chen #ifdef CONFIG_GMAC_ROCKCHIP 31999d14b01SJoseph Chen " [i] rktest eth - test ethernet\n" 32099d14b01SJoseph Chen #else 32199d14b01SJoseph Chen "" 32299d14b01SJoseph Chen #endif 32399d14b01SJoseph Chen ; 32499d14b01SJoseph Chen 32599d14b01SJoseph Chen struct cmd_group cmd_grp_net = { 32699d14b01SJoseph Chen .id = TEST_ID_NET, 32799d14b01SJoseph Chen .help = sub_cmd_help, 32899d14b01SJoseph Chen .cmd = sub_cmd, 32999d14b01SJoseph Chen .cmd_n = ARRAY_SIZE(sub_cmd), 33099d14b01SJoseph Chen }; 331