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