]>
Commit | Line | Data |
---|---|---|
6f51deb7 PW |
1 | /* |
2 | * (C) Copyright 2009 | |
3 | * Marvell Semiconductor <www.marvell.com> | |
4 | * Prafulla Wadaskar <prafulla@marvell.com> | |
5 | * | |
1a459660 | 6 | * SPDX-License-Identifier: GPL-2.0+ |
6f51deb7 PW |
7 | */ |
8 | ||
9 | #include <common.h> | |
10 | #include <netdev.h> | |
11 | #include "mv88e61xx.h" | |
12 | ||
0a16ea59 AA |
13 | /* |
14 | * Uncomment either of the following line for local debug control; | |
15 | * otherwise global debug control will apply. | |
16 | */ | |
17 | ||
18 | /* #undef DEBUG */ | |
19 | /* #define DEBUG */ | |
20 | ||
6f51deb7 PW |
21 | #ifdef CONFIG_MV88E61XX_MULTICHIP_ADRMODE |
22 | /* Chip Address mode | |
23 | * The Switch support two modes of operation | |
24 | * 1. single chip mode and | |
25 | * 2. Multi-chip mode | |
26 | * Refer section 9.2 &9.3 in chip datasheet-02 for more details | |
27 | * | |
28 | * By default single chip mode is configured | |
29 | * multichip mode operation can be configured in board header | |
30 | */ | |
443ce4ac | 31 | static int mv88e61xx_busychk_multic(char *name, u32 devaddr) |
6f51deb7 | 32 | { |
08c2df33 | 33 | u16 reg = 0; |
6f51deb7 PW |
34 | u32 timeout = MV88E61XX_PHY_TIMEOUT; |
35 | ||
36 | /* Poll till SMIBusy bit is clear */ | |
37 | do { | |
38 | miiphy_read(name, devaddr, 0x0, ®); | |
39 | if (timeout-- == 0) { | |
40 | printf("SMI busy timeout\n"); | |
41 | return -1; | |
42 | } | |
43 | } while (reg & (1 << 15)); | |
44 | return 0; | |
45 | } | |
46 | ||
0a16ea59 AA |
47 | static void mv88e61xx_switch_write(char *name, u32 phy_adr, |
48 | u32 reg_ofs, u16 data) | |
6f51deb7 | 49 | { |
08c2df33 | 50 | u16 mii_dev_addr; |
6f51deb7 PW |
51 | |
52 | /* command to read PHY dev address */ | |
443ce4ac | 53 | if (miiphy_read(name, 0xEE, 0xEE, &mii_dev_addr)) { |
6f51deb7 PW |
54 | printf("Error..could not read PHY dev address\n"); |
55 | return; | |
56 | } | |
443ce4ac | 57 | mv88e61xx_busychk_multic(name, mii_dev_addr); |
6f51deb7 PW |
58 | /* Write data to Switch indirect data register */ |
59 | miiphy_write(name, mii_dev_addr, 0x1, data); | |
60 | /* Write command to Switch indirect command register (write) */ | |
61 | miiphy_write(name, mii_dev_addr, 0x0, | |
62 | reg_ofs | (phy_adr << 5) | (1 << 10) | (1 << 12) | (1 << | |
63 | 15)); | |
64 | } | |
65 | ||
0a16ea59 AA |
66 | static void mv88e61xx_switch_read(char *name, u32 phy_adr, |
67 | u32 reg_ofs, u16 *data) | |
6f51deb7 | 68 | { |
08c2df33 | 69 | u16 mii_dev_addr; |
6f51deb7 PW |
70 | |
71 | /* command to read PHY dev address */ | |
443ce4ac | 72 | if (miiphy_read(name, 0xEE, 0xEE, &mii_dev_addr)) { |
6f51deb7 PW |
73 | printf("Error..could not read PHY dev address\n"); |
74 | return; | |
75 | } | |
443ce4ac | 76 | mv88e61xx_busychk_multic(name, mii_dev_addr); |
6f51deb7 PW |
77 | /* Write command to Switch indirect command register (read) */ |
78 | miiphy_write(name, mii_dev_addr, 0x0, | |
443ce4ac | 79 | reg_ofs | (phy_adr << 5) | (1 << 11) | (1 << 12) | (1 << |
6f51deb7 | 80 | 15)); |
443ce4ac | 81 | mv88e61xx_busychk_multic(name, mii_dev_addr); |
6f51deb7 | 82 | /* Read data from Switch indirect data register */ |
443ce4ac | 83 | miiphy_read(name, mii_dev_addr, 0x1, data); |
6f51deb7 PW |
84 | } |
85 | #endif /* CONFIG_MV88E61XX_MULTICHIP_ADRMODE */ | |
86 | ||
0a16ea59 AA |
87 | /* |
88 | * Convenience macros for switch device/port reads/writes | |
89 | * These macros output valid 'mv88e61xx' U_BOOT_CMDs | |
90 | */ | |
6f51deb7 | 91 | |
0a16ea59 AA |
92 | #ifndef DEBUG |
93 | #define WR_SWITCH_REG wr_switch_reg | |
94 | #define RD_SWITCH_REG rd_switch_reg | |
95 | #define WR_SWITCH_PORT_REG(n, p, r, d) \ | |
96 | WR_SWITCH_REG(n, (MV88E61XX_PRT_OFST+p), r, d) | |
97 | #define RD_SWITCH_PORT_REG(n, p, r, d) \ | |
98 | RD_SWITCH_REG(n, (MV88E61XX_PRT_OFST+p), r, d) | |
99 | #else | |
100 | static void WR_SWITCH_REG(char *name, u32 dev_adr, u32 reg_ofs, u16 data) | |
101 | { | |
102 | printf("mv88e61xx %s dev %02x reg %02x write %04x\n", | |
103 | name, dev_adr, reg_ofs, data); | |
104 | wr_switch_reg(name, dev_adr, reg_ofs, data); | |
6f51deb7 | 105 | } |
0a16ea59 AA |
106 | static void RD_SWITCH_REG(char *name, u32 dev_adr, u32 reg_ofs, u16 *data) |
107 | { | |
108 | rd_switch_reg(name, dev_adr, reg_ofs, data); | |
109 | printf("mv88e61xx %s dev %02x reg %02x read %04x\n", | |
110 | name, dev_adr, reg_ofs, *data); | |
111 | } | |
112 | static void WR_SWITCH_PORT_REG(char *name, u32 prt_adr, u32 reg_ofs, | |
113 | u16 data) | |
114 | { | |
115 | printf("mv88e61xx %s port %02x reg %02x write %04x\n", | |
116 | name, prt_adr, reg_ofs, data); | |
117 | wr_switch_reg(name, (MV88E61XX_PRT_OFST+prt_adr), reg_ofs, data); | |
118 | } | |
119 | static void RD_SWITCH_PORT_REG(char *name, u32 prt_adr, u32 reg_ofs, | |
120 | u16 *data) | |
121 | { | |
122 | rd_switch_reg(name, (MV88E61XX_PRT_OFST+prt_adr), reg_ofs, data); | |
123 | printf("mv88e61xx %s port %02x reg %02x read %04x\n", | |
124 | name, prt_adr, reg_ofs, *data); | |
125 | } | |
126 | #endif | |
127 | ||
128 | /* | |
129 | * Local functions to read/write registers on the switch PHYs. | |
130 | * NOTE! This goes through switch, not direct miiphy, writes and reads! | |
131 | */ | |
6f51deb7 PW |
132 | |
133 | /* | |
134 | * Make sure SMIBusy bit cleared before another | |
135 | * SMI operation can take place | |
136 | */ | |
137 | static int mv88e61xx_busychk(char *name) | |
138 | { | |
32e7f239 | 139 | u16 reg = 0; |
6f51deb7 PW |
140 | u32 timeout = MV88E61XX_PHY_TIMEOUT; |
141 | do { | |
0a16ea59 | 142 | rd_switch_reg(name, MV88E61XX_GLB2REG_DEVADR, |
32e7f239 | 143 | MV88E61XX_PHY_CMD, ®); |
6f51deb7 PW |
144 | if (timeout-- == 0) { |
145 | printf("SMI busy timeout\n"); | |
146 | return -1; | |
147 | } | |
443ce4ac | 148 | } while (reg & 1 << 15); /* busy mask */ |
6f51deb7 PW |
149 | return 0; |
150 | } | |
151 | ||
0a16ea59 AA |
152 | static inline int mv88e61xx_switch_miiphy_write(char *name, u32 phy, |
153 | u32 reg, u16 data) | |
154 | { | |
155 | /* write switch data reg then cmd reg then check completion */ | |
156 | wr_switch_reg(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_DATA, | |
157 | data); | |
158 | wr_switch_reg(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_CMD, | |
159 | (MV88E61XX_PHY_WRITE_CMD | (phy << 5) | reg)); | |
160 | return mv88e61xx_busychk(name); | |
161 | } | |
162 | ||
163 | static inline int mv88e61xx_switch_miiphy_read(char *name, u32 phy, | |
164 | u32 reg, u16 *data) | |
165 | { | |
166 | /* write switch cmd reg, check for completion */ | |
167 | wr_switch_reg(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_CMD, | |
168 | (MV88E61XX_PHY_READ_CMD | (phy << 5) | reg)); | |
169 | if (mv88e61xx_busychk(name)) | |
170 | return -1; | |
171 | /* read switch data reg and return success */ | |
172 | rd_switch_reg(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_DATA, data); | |
173 | return 0; | |
174 | } | |
175 | ||
176 | /* | |
177 | * Convenience macros for switch PHY reads/writes | |
178 | */ | |
179 | ||
180 | #ifndef DEBUG | |
181 | #define WR_SWITCH_PHY_REG mv88e61xx_switch_miiphy_write | |
182 | #define RD_SWITCH_PHY_REG mv88e61xx_switch_miiphy_read | |
183 | #else | |
184 | static inline int WR_SWITCH_PHY_REG(char *name, u32 phy_adr, | |
185 | u32 reg_ofs, u16 data) | |
186 | { | |
187 | int r = mv88e61xx_switch_miiphy_write(name, phy_adr, reg_ofs, data); | |
188 | if (r) | |
189 | printf("** ERROR writing mv88e61xx %s phy %02x reg %02x\n", | |
190 | name, phy_adr, reg_ofs); | |
191 | else | |
192 | printf("mv88e61xx %s phy %02x reg %02x write %04x\n", | |
193 | name, phy_adr, reg_ofs, data); | |
194 | return r; | |
195 | } | |
196 | static inline int RD_SWITCH_PHY_REG(char *name, u32 phy_adr, | |
197 | u32 reg_ofs, u16 *data) | |
198 | { | |
199 | int r = mv88e61xx_switch_miiphy_read(name, phy_adr, reg_ofs, data); | |
200 | if (r) | |
201 | printf("** ERROR reading mv88e61xx %s phy %02x reg %02x\n", | |
202 | name, phy_adr, reg_ofs); | |
203 | else | |
204 | printf("mv88e61xx %s phy %02x reg %02x read %04x\n", | |
205 | name, phy_adr, reg_ofs, *data); | |
206 | return r; | |
207 | } | |
208 | #endif | |
209 | ||
210 | static void mv88e61xx_port_vlan_config(struct mv88e61xx_config *swconfig) | |
211 | { | |
212 | u32 prt; | |
213 | u16 reg; | |
214 | char *name = swconfig->name; | |
215 | u32 port_mask = swconfig->ports_enabled; | |
216 | ||
217 | /* apply internal vlan config */ | |
218 | for (prt = 0; prt < MV88E61XX_MAX_PORTS_NUM; prt++) { | |
219 | /* only for enabled ports */ | |
220 | if ((1 << prt) & port_mask) { | |
221 | /* take vlan map from swconfig */ | |
222 | u8 vlanmap = swconfig->vlancfg[prt]; | |
223 | /* remove disabled ports from vlan map */ | |
224 | vlanmap &= swconfig->ports_enabled; | |
225 | /* apply vlan map to port */ | |
226 | RD_SWITCH_PORT_REG(name, prt, | |
227 | MV88E61XX_PRT_VMAP_REG, ®); | |
228 | reg &= ~((1 << MV88E61XX_MAX_PORTS_NUM) - 1); | |
229 | reg |= vlanmap; | |
230 | WR_SWITCH_PORT_REG(name, prt, | |
231 | MV88E61XX_PRT_VMAP_REG, reg); | |
232 | } | |
233 | } | |
234 | } | |
235 | ||
6f51deb7 PW |
236 | /* |
237 | * Power up the specified port and reset PHY | |
238 | */ | |
0a16ea59 | 239 | static int mv88361xx_powerup(struct mv88e61xx_config *swconfig, u32 phy) |
6f51deb7 PW |
240 | { |
241 | char *name = swconfig->name; | |
242 | ||
0a16ea59 | 243 | /* Write Copper Specific control reg1 (0x10) for- |
6f51deb7 PW |
244 | * Enable Phy power up |
245 | * Energy Detect on (sense&Xmit NLP Periodically | |
246 | * reset other settings default | |
247 | */ | |
0a16ea59 | 248 | if (WR_SWITCH_PHY_REG(name, phy, 0x10, 0x3360)) |
6f51deb7 PW |
249 | return -1; |
250 | ||
251 | /* Write PHY ctrl reg (0x0) to apply | |
252 | * Phy reset (set bit 15 low) | |
253 | * reset other default values | |
254 | */ | |
0a16ea59 | 255 | if (WR_SWITCH_PHY_REG(name, phy, 0x00, 0x9140)) |
6f51deb7 PW |
256 | return -1; |
257 | ||
258 | return 0; | |
259 | } | |
260 | ||
261 | /* | |
262 | * Default Setup for LED[0]_Control (ref: Table 46 Datasheet-3) | |
263 | * is set to "On-1000Mb/s Link, Off Else" | |
264 | * This function sets it to "On-Link, Blink-Activity, Off-NoLink" | |
265 | * | |
266 | * This is optional settings may be needed on some boards | |
267 | * to setup PHY LEDs default configuration to detect 10/100/1000Mb/s | |
268 | * Link status | |
269 | */ | |
0a16ea59 | 270 | static int mv88361xx_led_init(struct mv88e61xx_config *swconfig, u32 phy) |
6f51deb7 PW |
271 | { |
272 | char *name = swconfig->name; | |
6f51deb7 PW |
273 | |
274 | if (swconfig->led_init != MV88E61XX_LED_INIT_EN) | |
275 | return 0; | |
276 | ||
277 | /* set page address to 3 */ | |
0a16ea59 | 278 | if (WR_SWITCH_PHY_REG(name, phy, 0x16, 0x0003)) |
6f51deb7 PW |
279 | return -1; |
280 | ||
0a16ea59 AA |
281 | /* |
282 | * set LED Func Ctrl reg | |
283 | * value 0x0001 = LED[0] On-Link, Blink-Activity, Off-NoLink | |
284 | */ | |
285 | if (WR_SWITCH_PHY_REG(name, phy, 0x10, 0x0001)) | |
6f51deb7 PW |
286 | return -1; |
287 | ||
288 | /* set page address to 0 */ | |
0a16ea59 | 289 | if (WR_SWITCH_PHY_REG(name, phy, 0x16, 0x0000)) |
6f51deb7 PW |
290 | return -1; |
291 | ||
292 | return 0; | |
293 | } | |
294 | ||
295 | /* | |
296 | * Reverse Transmit polarity for Media Dependent Interface | |
297 | * Pins (MDIP) bits in Copper Specific Control Register 3 | |
298 | * (Page 0, Reg 20 for each phy (except cpu port) | |
299 | * Reference: Section 1.1 Switch datasheet-3 | |
300 | * | |
301 | * This is optional settings may be needed on some boards | |
302 | * for PHY<->magnetics h/w tuning | |
303 | */ | |
0a16ea59 | 304 | static int mv88361xx_reverse_mdipn(struct mv88e61xx_config *swconfig, u32 phy) |
6f51deb7 PW |
305 | { |
306 | char *name = swconfig->name; | |
6f51deb7 PW |
307 | |
308 | if (swconfig->mdip != MV88E61XX_MDIP_REVERSE) | |
309 | return 0; | |
310 | ||
0a16ea59 AA |
311 | /*Reverse MDIP/N[3:0] bits */ |
312 | if (WR_SWITCH_PHY_REG(name, phy, 0x14, 0x000f)) | |
6f51deb7 PW |
313 | return -1; |
314 | ||
315 | return 0; | |
316 | } | |
317 | ||
318 | /* | |
319 | * Marvell 88E61XX Switch initialization | |
320 | */ | |
321 | int mv88e61xx_switch_initialize(struct mv88e61xx_config *swconfig) | |
322 | { | |
323 | u32 prt; | |
324 | u16 reg; | |
325 | char *idstr; | |
326 | char *name = swconfig->name; | |
0a16ea59 | 327 | int time; |
6f51deb7 PW |
328 | |
329 | if (miiphy_set_current_dev(name)) { | |
330 | printf("%s failed\n", __FUNCTION__); | |
331 | return -1; | |
332 | } | |
333 | ||
334 | if (!(swconfig->cpuport & ((1 << 4) | (1 << 5)))) { | |
335 | swconfig->cpuport = (1 << 5); | |
336 | printf("Invalid cpu port config, using default port5\n"); | |
337 | } | |
338 | ||
0a16ea59 | 339 | RD_SWITCH_PORT_REG(name, 0, MII_PHYSID2, ®); |
08c2df33 PW |
340 | switch (reg &= 0xfff0) { |
341 | case 0x1610: | |
6f51deb7 | 342 | idstr = "88E6161"; |
08c2df33 PW |
343 | break; |
344 | case 0x1650: | |
6f51deb7 | 345 | idstr = "88E6165"; |
08c2df33 PW |
346 | break; |
347 | case 0x1210: | |
6f51deb7 PW |
348 | idstr = "88E6123"; |
349 | /* ports 2,3,4 not available */ | |
350 | swconfig->ports_enabled &= 0x023; | |
08c2df33 PW |
351 | break; |
352 | default: | |
353 | /* Could not detect switch id */ | |
354 | idstr = "88E61??"; | |
355 | break; | |
6f51deb7 PW |
356 | } |
357 | ||
0a16ea59 AA |
358 | /* be sure all ports are disabled */ |
359 | for (prt = 0; prt < MV88E61XX_MAX_PORTS_NUM; prt++) { | |
360 | RD_SWITCH_PORT_REG(name, prt, MV88E61XX_PRT_CTRL_REG, ®); | |
361 | reg &= ~0x3; | |
362 | WR_SWITCH_PORT_REG(name, prt, MV88E61XX_PRT_CTRL_REG, reg); | |
6f51deb7 PW |
363 | } |
364 | ||
0a16ea59 AA |
365 | /* wait 2 ms for queues to drain */ |
366 | udelay(2000); | |
367 | ||
368 | /* reset switch */ | |
369 | RD_SWITCH_REG(name, MV88E61XX_GLBREG_DEVADR, MV88E61XX_SGCR, ®); | |
370 | reg |= 0x8000; | |
371 | WR_SWITCH_REG(name, MV88E61XX_GLBREG_DEVADR, MV88E61XX_SGCR, reg); | |
372 | ||
373 | /* wait up to 1 second for switch reset complete */ | |
374 | for (time = 1000; time; time--) { | |
375 | RD_SWITCH_REG(name, MV88E61XX_GLBREG_DEVADR, MV88E61XX_SGSR, | |
376 | ®); | |
377 | if ((reg & 0xc800) == 0xc800) | |
378 | break; | |
379 | udelay(1000); | |
380 | } | |
381 | if (!time) | |
382 | return -1; | |
383 | ||
384 | /* Port based VLANs configuration */ | |
385 | mv88e61xx_port_vlan_config(swconfig); | |
386 | ||
6f51deb7 PW |
387 | if (swconfig->rgmii_delay == MV88E61XX_RGMII_DELAY_EN) { |
388 | /* | |
389 | * Enable RGMII delay on Tx and Rx for CPU port | |
390 | * Ref: sec 9.5 of chip datasheet-02 | |
391 | */ | |
0a16ea59 AA |
392 | /*Force port link down */ |
393 | WR_SWITCH_PORT_REG(name, 5, MV88E61XX_PCS_CTRL_REG, 0x10); | |
394 | /* configure port RGMII delay */ | |
395 | WR_SWITCH_PORT_REG(name, 4, | |
396 | MV88E61XX_RGMII_TIMECTRL_REG, 0x81e7); | |
397 | RD_SWITCH_PORT_REG(name, 5, | |
398 | MV88E61XX_RGMII_TIMECTRL_REG, ®); | |
399 | WR_SWITCH_PORT_REG(name, 5, | |
400 | MV88E61XX_RGMII_TIMECTRL_REG, reg | 0x18); | |
401 | WR_SWITCH_PORT_REG(name, 4, | |
402 | MV88E61XX_RGMII_TIMECTRL_REG, 0xc1e7); | |
403 | /* Force port to RGMII FDX 1000Base then up */ | |
404 | WR_SWITCH_PORT_REG(name, 5, MV88E61XX_PCS_CTRL_REG, 0x1e); | |
405 | WR_SWITCH_PORT_REG(name, 5, MV88E61XX_PCS_CTRL_REG, 0x3e); | |
6f51deb7 PW |
406 | } |
407 | ||
408 | for (prt = 0; prt < MV88E61XX_MAX_PORTS_NUM; prt++) { | |
6f51deb7 | 409 | |
0a16ea59 AA |
410 | /* configure port's PHY */ |
411 | if (!((1 << prt) & swconfig->cpuport)) { | |
412 | /* port 4 has phy 6, not 4 */ | |
413 | int phy = (prt == 4) ? 6 : prt; | |
414 | if (mv88361xx_powerup(swconfig, phy)) | |
6f51deb7 | 415 | return -1; |
0a16ea59 | 416 | if (mv88361xx_reverse_mdipn(swconfig, phy)) |
6f51deb7 | 417 | return -1; |
0a16ea59 | 418 | if (mv88361xx_led_init(swconfig, phy)) |
6f51deb7 PW |
419 | return -1; |
420 | } | |
421 | ||
0a16ea59 AA |
422 | /* set port VID to port+1 except for cpu port */ |
423 | if (!((1 << prt) & swconfig->cpuport)) { | |
424 | RD_SWITCH_PORT_REG(name, prt, | |
425 | MV88E61XX_PRT_VID_REG, ®); | |
426 | WR_SWITCH_PORT_REG(name, prt, | |
427 | MV88E61XX_PRT_VID_REG, | |
428 | (reg & ~1023) | (prt+1)); | |
429 | } | |
430 | ||
6f51deb7 | 431 | /*Program port state */ |
0a16ea59 AA |
432 | RD_SWITCH_PORT_REG(name, prt, |
433 | MV88E61XX_PRT_CTRL_REG, ®); | |
434 | WR_SWITCH_PORT_REG(name, prt, | |
435 | MV88E61XX_PRT_CTRL_REG, | |
436 | reg | (swconfig->portstate & 0x03)); | |
437 | ||
6f51deb7 PW |
438 | } |
439 | ||
440 | printf("%s Initialized on %s\n", idstr, name); | |
441 | return 0; | |
442 | } | |
0a16ea59 AA |
443 | |
444 | #ifdef CONFIG_MV88E61XX_CMD | |
445 | static int | |
446 | do_switch(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) | |
447 | { | |
448 | char *name, *endp; | |
449 | int write = 0; | |
450 | enum { dev, prt, phy } target = dev; | |
451 | u32 addrlo, addrhi, addr; | |
452 | u32 reglo, reghi, reg; | |
453 | u16 data, rdata; | |
454 | ||
455 | if (argc < 7) | |
456 | return -1; | |
457 | ||
458 | name = argv[1]; | |
459 | ||
460 | if (strcmp(argv[2], "phy") == 0) | |
461 | target = phy; | |
462 | else if (strcmp(argv[2], "port") == 0) | |
463 | target = prt; | |
464 | else if (strcmp(argv[2], "dev") != 0) | |
465 | return 1; | |
466 | ||
467 | addrlo = simple_strtoul(argv[3], &endp, 16); | |
468 | ||
469 | if (!*endp) { | |
470 | addrhi = addrlo; | |
471 | } else { | |
472 | while (*endp < '0' || *endp > '9') | |
473 | endp++; | |
474 | addrhi = simple_strtoul(endp, NULL, 16); | |
475 | } | |
476 | ||
477 | reglo = simple_strtoul(argv[5], &endp, 16); | |
478 | if (!*endp) { | |
479 | reghi = reglo; | |
480 | } else { | |
481 | while (*endp < '0' || *endp > '9') | |
482 | endp++; | |
483 | reghi = simple_strtoul(endp, NULL, 16); | |
484 | } | |
485 | ||
486 | if (strcmp(argv[6], "write") == 0) | |
487 | write = 1; | |
488 | else if (strcmp(argv[6], "read") != 0) | |
489 | return 1; | |
490 | ||
491 | data = simple_strtoul(argv[7], NULL, 16); | |
492 | ||
493 | for (addr = addrlo; addr <= addrhi; addr++) { | |
494 | for (reg = reglo; reg <= reghi; reg++) { | |
495 | if (write) { | |
496 | if (target == phy) | |
497 | mv88e61xx_switch_miiphy_write( | |
498 | name, addr, reg, data); | |
499 | else if (target == prt) | |
500 | wr_switch_reg(name, | |
501 | addr+MV88E61XX_PRT_OFST, | |
502 | reg, data); | |
503 | else | |
504 | wr_switch_reg(name, addr, reg, data); | |
505 | } else { | |
506 | if (target == phy) | |
507 | mv88e61xx_switch_miiphy_read( | |
508 | name, addr, reg, &rdata); | |
509 | else if (target == prt) | |
510 | rd_switch_reg(name, | |
511 | addr+MV88E61XX_PRT_OFST, | |
512 | reg, &rdata); | |
513 | else | |
514 | rd_switch_reg(name, addr, reg, &rdata); | |
515 | printf("%s %s %s %02x %s %02x %s %04x\n", | |
516 | argv[0], argv[1], argv[2], addr, | |
517 | argv[4], reg, argv[6], rdata); | |
518 | if (write && argc == 7 && rdata != data) | |
519 | return 1; | |
520 | } | |
521 | } | |
522 | } | |
523 | return 0; | |
524 | } | |
525 | ||
526 | U_BOOT_CMD(mv88e61xx, 8, 0, do_switch, | |
527 | "Read or write mv88e61xx switch registers", | |
528 | "<ethdevice> dev|port|phy <addr> reg <reg> write <data>\n" | |
529 | "<ethdevice> dev|port|phy <addr> reg <reg> read [<data>]\n" | |
530 | " - read/write switch device, port or phy at (addr,reg)\n" | |
531 | " addr=0..0x1C for dev, 0..5 for port or phy.\n" | |
532 | " reg=0..0x1F.\n" | |
533 | " data=0..0xFFFF (tested if present against actual read).\n" | |
534 | " All numeric parameters are assumed to be hex.\n" | |
535 | " <addr> and <<reg> arguments can be ranges (x..y)" | |
536 | ); | |
537 | #endif /* CONFIG_MV88E61XX_CMD */ |