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
get_current_phydev(void)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
create_lbtest_frame(uchar * data,unsigned int frame_size)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
alter_lbtest_frame(uchar * data,unsigned int frame_size,unsigned int tx,unsigned int rx)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
check_lbtest_frame(uchar * tx_data,uchar * rx_data,unsigned int frame_size)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
eth_setup_loopback_test(struct udevice * current,int speed)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
eth_run_loopback_test(struct udevice * current,int speed,int delay_test)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
ethernet_init(void)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
eth_loopback_test(int speed,int delay_test)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
do_eth_help(void)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
do_test_eth(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])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