Commit 0a16ea593355d1a0fd415286089961f9c9ad1614
Committed by
Prafulla Wadaskar
1 parent
136846d77f
Exists in
master
and in
54 other branches
mv88e61xx: refactor PHY and SWITCH level-code
Signed-off-by: Albert ARIBAUD <albert.u.boot@aribaud.net>
Showing 3 changed files with 358 additions and 197 deletions Side-by-side Diff
drivers/net/phy/mv88e61xx.c
... | ... | @@ -26,6 +26,14 @@ |
26 | 26 | #include <netdev.h> |
27 | 27 | #include "mv88e61xx.h" |
28 | 28 | |
29 | +/* | |
30 | + * Uncomment either of the following line for local debug control; | |
31 | + * otherwise global debug control will apply. | |
32 | + */ | |
33 | + | |
34 | +/* #undef DEBUG */ | |
35 | +/* #define DEBUG */ | |
36 | + | |
29 | 37 | #ifdef CONFIG_MV88E61XX_MULTICHIP_ADRMODE |
30 | 38 | /* Chip Address mode |
31 | 39 | * The Switch support two modes of operation |
... | ... | @@ -52,7 +60,8 @@ |
52 | 60 | return 0; |
53 | 61 | } |
54 | 62 | |
55 | -static void mv88e61xx_wr_phy(char *name, u32 phy_adr, u32 reg_ofs, u16 data) | |
63 | +static void mv88e61xx_switch_write(char *name, u32 phy_adr, | |
64 | + u32 reg_ofs, u16 data) | |
56 | 65 | { |
57 | 66 | u16 mii_dev_addr; |
58 | 67 | |
... | ... | @@ -70,7 +79,8 @@ |
70 | 79 | 15)); |
71 | 80 | } |
72 | 81 | |
73 | -static void mv88e61xx_rd_phy(char *name, u32 phy_adr, u32 reg_ofs, u16 * data) | |
82 | +static void mv88e61xx_switch_read(char *name, u32 phy_adr, | |
83 | + u32 reg_ofs, u16 *data) | |
74 | 84 | { |
75 | 85 | u16 mii_dev_addr; |
76 | 86 | |
77 | 87 | |
78 | 88 | |
79 | 89 | |
... | ... | @@ -90,112 +100,53 @@ |
90 | 100 | } |
91 | 101 | #endif /* CONFIG_MV88E61XX_MULTICHIP_ADRMODE */ |
92 | 102 | |
93 | -static void mv88e61xx_port_vlan_config(struct mv88e61xx_config *swconfig, | |
94 | - u32 max_prtnum, u32 ports_ofs) | |
95 | -{ | |
96 | - u32 prt; | |
97 | - u16 reg; | |
98 | - char *name = swconfig->name; | |
99 | - u32 cpu_port = swconfig->cpuport; | |
100 | - u32 port_mask = swconfig->ports_enabled; | |
101 | - enum mv88e61xx_cfg_vlan vlancfg = swconfig->vlancfg; | |
103 | +/* | |
104 | + * Convenience macros for switch device/port reads/writes | |
105 | + * These macros output valid 'mv88e61xx' U_BOOT_CMDs | |
106 | + */ | |
102 | 107 | |
103 | - /* be sure all ports are disabled */ | |
104 | - for (prt = 0; prt < max_prtnum; prt++) { | |
105 | - RD_PHY(name, ports_ofs + prt, MV88E61XX_PRT_CTRL_REG, ®); | |
106 | - reg &= ~0x3; | |
107 | - WR_PHY(name, ports_ofs + prt, MV88E61XX_PRT_CTRL_REG, reg); | |
108 | - | |
109 | - if (!(cpu_port & (1 << prt))) | |
110 | - continue; | |
111 | - /* Set CPU port VID to 0x1 */ | |
112 | - RD_PHY(name, (ports_ofs + prt), MV88E61XX_PRT_VID_REG, ®); | |
113 | - reg &= ~0xfff; | |
114 | - reg |= 0x1; | |
115 | - WR_PHY(name, (ports_ofs + prt), MV88E61XX_PRT_VID_REG, reg); | |
116 | - } | |
117 | - | |
118 | - /* Setting Port default priority for all ports to zero */ | |
119 | - for (prt = 0; prt < max_prtnum; prt++) { | |
120 | - RD_PHY(name, ports_ofs + prt, MV88E61XX_PRT_VID_REG, ®); | |
121 | - reg &= ~0xc000; | |
122 | - WR_PHY(name, ports_ofs + prt, MV88E61XX_PRT_VID_REG, reg); | |
123 | - } | |
124 | - /* Setting VID and VID map for all ports except CPU port */ | |
125 | - for (prt = 0; prt < max_prtnum; prt++) { | |
126 | - /* only for enabled ports */ | |
127 | - if ((1 << prt) & port_mask) { | |
128 | - /* skip CPU port */ | |
129 | - if ((1 << prt) & cpu_port) { | |
130 | - /* | |
131 | - * Set Vlan map table for cpu_port to see | |
132 | - * all ports | |
133 | - */ | |
134 | - RD_PHY(name, (ports_ofs + prt), | |
135 | - MV88E61XX_PRT_VMAP_REG, ®); | |
136 | - reg &= ~((1 << max_prtnum) - 1); | |
137 | - reg |= port_mask & ~(1 << prt); | |
138 | - WR_PHY(name, (ports_ofs + prt), | |
139 | - MV88E61XX_PRT_VMAP_REG, reg); | |
140 | - } else { | |
141 | - | |
142 | - /* | |
143 | - * set Ports VLAN Mapping. | |
144 | - * port prt <--> cpu_port VLAN #prt+1. | |
145 | - */ | |
146 | - RD_PHY(name, ports_ofs + prt, | |
147 | - MV88E61XX_PRT_VID_REG, ®); | |
148 | - reg &= ~0x0fff; | |
149 | - reg |= (prt + 1); | |
150 | - WR_PHY(name, ports_ofs + prt, | |
151 | - MV88E61XX_PRT_VID_REG, reg); | |
152 | - | |
153 | - RD_PHY(name, ports_ofs + prt, | |
154 | - MV88E61XX_PRT_VMAP_REG, ®); | |
155 | - if (vlancfg == MV88E61XX_VLANCFG_DEFAULT) { | |
156 | - /* | |
157 | - * all any port can send frames to all other ports | |
158 | - * ref: sec 3.2.1.1 of datasheet | |
159 | - */ | |
160 | - reg |= 0x03f; | |
161 | - reg &= ~(1 << prt); | |
162 | - } else if (vlancfg == MV88E61XX_VLANCFG_ROUTER) { | |
163 | - /* | |
164 | - * all other ports can send frames to CPU port only | |
165 | - * ref: sec 3.2.1.2 of datasheet | |
166 | - */ | |
167 | - reg &= ~((1 << max_prtnum) - 1); | |
168 | - reg |= cpu_port; | |
169 | - } | |
170 | - WR_PHY(name, ports_ofs + prt, | |
171 | - MV88E61XX_PRT_VMAP_REG, reg); | |
172 | - } | |
173 | - } | |
174 | - } | |
175 | - | |
176 | - /* | |
177 | - * enable only appropriate ports to forwarding mode | |
178 | - * and disable the others | |
179 | - */ | |
180 | - for (prt = 0; prt < max_prtnum; prt++) { | |
181 | - if ((1 << prt) & port_mask) { | |
182 | - RD_PHY(name, ports_ofs + prt, | |
183 | - MV88E61XX_PRT_CTRL_REG, ®); | |
184 | - reg |= 0x3; | |
185 | - WR_PHY(name, ports_ofs + prt, | |
186 | - MV88E61XX_PRT_CTRL_REG, reg); | |
187 | - } else { | |
188 | - /* Disable port */ | |
189 | - RD_PHY(name, ports_ofs + prt, | |
190 | - MV88E61XX_PRT_CTRL_REG, ®); | |
191 | - reg &= ~0x3; | |
192 | - WR_PHY(name, ports_ofs + prt, | |
193 | - MV88E61XX_PRT_CTRL_REG, reg); | |
194 | - } | |
195 | - } | |
108 | +#ifndef DEBUG | |
109 | +#define WR_SWITCH_REG wr_switch_reg | |
110 | +#define RD_SWITCH_REG rd_switch_reg | |
111 | +#define WR_SWITCH_PORT_REG(n, p, r, d) \ | |
112 | + WR_SWITCH_REG(n, (MV88E61XX_PRT_OFST+p), r, d) | |
113 | +#define RD_SWITCH_PORT_REG(n, p, r, d) \ | |
114 | + RD_SWITCH_REG(n, (MV88E61XX_PRT_OFST+p), r, d) | |
115 | +#else | |
116 | +static void WR_SWITCH_REG(char *name, u32 dev_adr, u32 reg_ofs, u16 data) | |
117 | +{ | |
118 | + printf("mv88e61xx %s dev %02x reg %02x write %04x\n", | |
119 | + name, dev_adr, reg_ofs, data); | |
120 | + wr_switch_reg(name, dev_adr, reg_ofs, data); | |
196 | 121 | } |
122 | +static void RD_SWITCH_REG(char *name, u32 dev_adr, u32 reg_ofs, u16 *data) | |
123 | +{ | |
124 | + rd_switch_reg(name, dev_adr, reg_ofs, data); | |
125 | + printf("mv88e61xx %s dev %02x reg %02x read %04x\n", | |
126 | + name, dev_adr, reg_ofs, *data); | |
127 | +} | |
128 | +static void WR_SWITCH_PORT_REG(char *name, u32 prt_adr, u32 reg_ofs, | |
129 | + u16 data) | |
130 | +{ | |
131 | + printf("mv88e61xx %s port %02x reg %02x write %04x\n", | |
132 | + name, prt_adr, reg_ofs, data); | |
133 | + wr_switch_reg(name, (MV88E61XX_PRT_OFST+prt_adr), reg_ofs, data); | |
134 | +} | |
135 | +static void RD_SWITCH_PORT_REG(char *name, u32 prt_adr, u32 reg_ofs, | |
136 | + u16 *data) | |
137 | +{ | |
138 | + rd_switch_reg(name, (MV88E61XX_PRT_OFST+prt_adr), reg_ofs, data); | |
139 | + printf("mv88e61xx %s port %02x reg %02x read %04x\n", | |
140 | + name, prt_adr, reg_ofs, *data); | |
141 | +} | |
142 | +#endif | |
197 | 143 | |
198 | 144 | /* |
145 | + * Local functions to read/write registers on the switch PHYs. | |
146 | + * NOTE! This goes through switch, not direct miiphy, writes and reads! | |
147 | + */ | |
148 | + | |
149 | +/* | |
199 | 150 | * Make sure SMIBusy bit cleared before another |
200 | 151 | * SMI operation can take place |
201 | 152 | */ |
... | ... | @@ -204,7 +155,7 @@ |
204 | 155 | u16 reg = 0; |
205 | 156 | u32 timeout = MV88E61XX_PHY_TIMEOUT; |
206 | 157 | do { |
207 | - RD_PHY(name, MV88E61XX_GLB2REG_DEVADR, | |
158 | + rd_switch_reg(name, MV88E61XX_GLB2REG_DEVADR, | |
208 | 159 | MV88E61XX_PHY_CMD, ®); |
209 | 160 | if (timeout-- == 0) { |
210 | 161 | printf("SMI busy timeout\n"); |
211 | 162 | |
212 | 163 | |
213 | 164 | |
214 | 165 | |
215 | 166 | |
... | ... | @@ -214,34 +165,110 @@ |
214 | 165 | return 0; |
215 | 166 | } |
216 | 167 | |
168 | +static inline int mv88e61xx_switch_miiphy_write(char *name, u32 phy, | |
169 | + u32 reg, u16 data) | |
170 | +{ | |
171 | + /* write switch data reg then cmd reg then check completion */ | |
172 | + wr_switch_reg(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_DATA, | |
173 | + data); | |
174 | + wr_switch_reg(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_CMD, | |
175 | + (MV88E61XX_PHY_WRITE_CMD | (phy << 5) | reg)); | |
176 | + return mv88e61xx_busychk(name); | |
177 | +} | |
178 | + | |
179 | +static inline int mv88e61xx_switch_miiphy_read(char *name, u32 phy, | |
180 | + u32 reg, u16 *data) | |
181 | +{ | |
182 | + /* write switch cmd reg, check for completion */ | |
183 | + wr_switch_reg(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_CMD, | |
184 | + (MV88E61XX_PHY_READ_CMD | (phy << 5) | reg)); | |
185 | + if (mv88e61xx_busychk(name)) | |
186 | + return -1; | |
187 | + /* read switch data reg and return success */ | |
188 | + rd_switch_reg(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_DATA, data); | |
189 | + return 0; | |
190 | +} | |
191 | + | |
217 | 192 | /* |
193 | + * Convenience macros for switch PHY reads/writes | |
194 | + */ | |
195 | + | |
196 | +#ifndef DEBUG | |
197 | +#define WR_SWITCH_PHY_REG mv88e61xx_switch_miiphy_write | |
198 | +#define RD_SWITCH_PHY_REG mv88e61xx_switch_miiphy_read | |
199 | +#else | |
200 | +static inline int WR_SWITCH_PHY_REG(char *name, u32 phy_adr, | |
201 | + u32 reg_ofs, u16 data) | |
202 | +{ | |
203 | + int r = mv88e61xx_switch_miiphy_write(name, phy_adr, reg_ofs, data); | |
204 | + if (r) | |
205 | + printf("** ERROR writing mv88e61xx %s phy %02x reg %02x\n", | |
206 | + name, phy_adr, reg_ofs); | |
207 | + else | |
208 | + printf("mv88e61xx %s phy %02x reg %02x write %04x\n", | |
209 | + name, phy_adr, reg_ofs, data); | |
210 | + return r; | |
211 | +} | |
212 | +static inline int RD_SWITCH_PHY_REG(char *name, u32 phy_adr, | |
213 | + u32 reg_ofs, u16 *data) | |
214 | +{ | |
215 | + int r = mv88e61xx_switch_miiphy_read(name, phy_adr, reg_ofs, data); | |
216 | + if (r) | |
217 | + printf("** ERROR reading mv88e61xx %s phy %02x reg %02x\n", | |
218 | + name, phy_adr, reg_ofs); | |
219 | + else | |
220 | + printf("mv88e61xx %s phy %02x reg %02x read %04x\n", | |
221 | + name, phy_adr, reg_ofs, *data); | |
222 | + return r; | |
223 | +} | |
224 | +#endif | |
225 | + | |
226 | +static void mv88e61xx_port_vlan_config(struct mv88e61xx_config *swconfig) | |
227 | +{ | |
228 | + u32 prt; | |
229 | + u16 reg; | |
230 | + char *name = swconfig->name; | |
231 | + u32 port_mask = swconfig->ports_enabled; | |
232 | + | |
233 | + /* apply internal vlan config */ | |
234 | + for (prt = 0; prt < MV88E61XX_MAX_PORTS_NUM; prt++) { | |
235 | + /* only for enabled ports */ | |
236 | + if ((1 << prt) & port_mask) { | |
237 | + /* take vlan map from swconfig */ | |
238 | + u8 vlanmap = swconfig->vlancfg[prt]; | |
239 | + /* remove disabled ports from vlan map */ | |
240 | + vlanmap &= swconfig->ports_enabled; | |
241 | + /* apply vlan map to port */ | |
242 | + RD_SWITCH_PORT_REG(name, prt, | |
243 | + MV88E61XX_PRT_VMAP_REG, ®); | |
244 | + reg &= ~((1 << MV88E61XX_MAX_PORTS_NUM) - 1); | |
245 | + reg |= vlanmap; | |
246 | + WR_SWITCH_PORT_REG(name, prt, | |
247 | + MV88E61XX_PRT_VMAP_REG, reg); | |
248 | + } | |
249 | + } | |
250 | +} | |
251 | + | |
252 | +/* | |
218 | 253 | * Power up the specified port and reset PHY |
219 | 254 | */ |
220 | -static int mv88361xx_powerup(struct mv88e61xx_config *swconfig, u32 prt) | |
255 | +static int mv88361xx_powerup(struct mv88e61xx_config *swconfig, u32 phy) | |
221 | 256 | { |
222 | 257 | char *name = swconfig->name; |
223 | 258 | |
224 | - /* Write Copper Specific control reg1 (0x14) for- | |
259 | + /* Write Copper Specific control reg1 (0x10) for- | |
225 | 260 | * Enable Phy power up |
226 | 261 | * Energy Detect on (sense&Xmit NLP Periodically |
227 | 262 | * reset other settings default |
228 | 263 | */ |
229 | - WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_DATA, 0x3360); | |
230 | - WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, | |
231 | - MV88E61XX_PHY_CMD, (0x9410 | (prt << 5))); | |
232 | - | |
233 | - if (mv88e61xx_busychk(name)) | |
264 | + if (WR_SWITCH_PHY_REG(name, phy, 0x10, 0x3360)) | |
234 | 265 | return -1; |
235 | 266 | |
236 | 267 | /* Write PHY ctrl reg (0x0) to apply |
237 | 268 | * Phy reset (set bit 15 low) |
238 | 269 | * reset other default values |
239 | 270 | */ |
240 | - WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_DATA, 0x1140); | |
241 | - WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, | |
242 | - MV88E61XX_PHY_CMD, (0x9400 | (prt << 5))); | |
243 | - | |
244 | - if (mv88e61xx_busychk(name)) | |
271 | + if (WR_SWITCH_PHY_REG(name, phy, 0x00, 0x9140)) | |
245 | 272 | return -1; |
246 | 273 | |
247 | 274 | return 0; |
248 | 275 | |
249 | 276 | |
250 | 277 | |
251 | 278 | |
... | ... | @@ -256,48 +283,26 @@ |
256 | 283 | * to setup PHY LEDs default configuration to detect 10/100/1000Mb/s |
257 | 284 | * Link status |
258 | 285 | */ |
259 | -static int mv88361xx_led_init(struct mv88e61xx_config *swconfig, u32 prt) | |
286 | +static int mv88361xx_led_init(struct mv88e61xx_config *swconfig, u32 phy) | |
260 | 287 | { |
261 | 288 | char *name = swconfig->name; |
262 | - u16 reg; | |
263 | 289 | |
264 | 290 | if (swconfig->led_init != MV88E61XX_LED_INIT_EN) |
265 | 291 | return 0; |
266 | 292 | |
267 | 293 | /* set page address to 3 */ |
268 | - reg = 3; | |
269 | - WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_DATA, reg); | |
270 | - WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, | |
271 | - MV88E61XX_PHY_CMD, (1 << MV88E61XX_BUSY_OFST | | |
272 | - 1 << MV88E61XX_MODE_OFST | | |
273 | - 1 << MV88E61XX_OP_OFST | | |
274 | - prt << MV88E61XX_ADDR_OFST | 22)); | |
275 | - | |
276 | - if (mv88e61xx_busychk(name)) | |
294 | + if (WR_SWITCH_PHY_REG(name, phy, 0x16, 0x0003)) | |
277 | 295 | return -1; |
278 | 296 | |
279 | - /* set LED Func Ctrl reg */ | |
280 | - reg = 1; /* LED[0] On-Link, Blink-Activity, Off-NoLink */ | |
281 | - WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_DATA, reg); | |
282 | - WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, | |
283 | - MV88E61XX_PHY_CMD, (1 << MV88E61XX_BUSY_OFST | | |
284 | - 1 << MV88E61XX_MODE_OFST | | |
285 | - 1 << MV88E61XX_OP_OFST | | |
286 | - prt << MV88E61XX_ADDR_OFST | 16)); | |
287 | - | |
288 | - if (mv88e61xx_busychk(name)) | |
297 | + /* | |
298 | + * set LED Func Ctrl reg | |
299 | + * value 0x0001 = LED[0] On-Link, Blink-Activity, Off-NoLink | |
300 | + */ | |
301 | + if (WR_SWITCH_PHY_REG(name, phy, 0x10, 0x0001)) | |
289 | 302 | return -1; |
290 | 303 | |
291 | 304 | /* set page address to 0 */ |
292 | - reg = 0; | |
293 | - WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_DATA, reg); | |
294 | - WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, | |
295 | - MV88E61XX_PHY_CMD, (1 << MV88E61XX_BUSY_OFST | | |
296 | - 1 << MV88E61XX_MODE_OFST | | |
297 | - 1 << MV88E61XX_OP_OFST | | |
298 | - prt << MV88E61XX_ADDR_OFST | 22)); | |
299 | - | |
300 | - if (mv88e61xx_busychk(name)) | |
305 | + if (WR_SWITCH_PHY_REG(name, phy, 0x16, 0x0000)) | |
301 | 306 | return -1; |
302 | 307 | |
303 | 308 | return 0; |
304 | 309 | |
305 | 310 | |
... | ... | @@ -312,23 +317,15 @@ |
312 | 317 | * This is optional settings may be needed on some boards |
313 | 318 | * for PHY<->magnetics h/w tuning |
314 | 319 | */ |
315 | -static int mv88361xx_reverse_mdipn(struct mv88e61xx_config *swconfig, u32 prt) | |
320 | +static int mv88361xx_reverse_mdipn(struct mv88e61xx_config *swconfig, u32 phy) | |
316 | 321 | { |
317 | 322 | char *name = swconfig->name; |
318 | - u16 reg; | |
319 | 323 | |
320 | 324 | if (swconfig->mdip != MV88E61XX_MDIP_REVERSE) |
321 | 325 | return 0; |
322 | 326 | |
323 | - reg = 0x0f; /*Reverse MDIP/N[3:0] bits */ | |
324 | - WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_DATA, reg); | |
325 | - WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, | |
326 | - MV88E61XX_PHY_CMD, (1 << MV88E61XX_BUSY_OFST | | |
327 | - 1 << MV88E61XX_MODE_OFST | | |
328 | - 1 << MV88E61XX_OP_OFST | | |
329 | - prt << MV88E61XX_ADDR_OFST | 20)); | |
330 | - | |
331 | - if (mv88e61xx_busychk(name)) | |
327 | + /*Reverse MDIP/N[3:0] bits */ | |
328 | + if (WR_SWITCH_PHY_REG(name, phy, 0x14, 0x000f)) | |
332 | 329 | return -1; |
333 | 330 | |
334 | 331 | return 0; |
... | ... | @@ -343,6 +340,7 @@ |
343 | 340 | u16 reg; |
344 | 341 | char *idstr; |
345 | 342 | char *name = swconfig->name; |
343 | + int time; | |
346 | 344 | |
347 | 345 | if (miiphy_set_current_dev(name)) { |
348 | 346 | printf("%s failed\n", __FUNCTION__); |
... | ... | @@ -354,7 +352,7 @@ |
354 | 352 | printf("Invalid cpu port config, using default port5\n"); |
355 | 353 | } |
356 | 354 | |
357 | - RD_PHY(name, MV88E61XX_PRT_OFST, MII_PHYSID2, ®); | |
355 | + RD_SWITCH_PORT_REG(name, 0, MII_PHYSID2, ®); | |
358 | 356 | switch (reg &= 0xfff0) { |
359 | 357 | case 0x1610: |
360 | 358 | idstr = "88E6161"; |
361 | 359 | |
362 | 360 | |
363 | 361 | |
364 | 362 | |
365 | 363 | |
366 | 364 | |
367 | 365 | |
368 | 366 | |
369 | 367 | |
... | ... | @@ -373,47 +371,184 @@ |
373 | 371 | break; |
374 | 372 | } |
375 | 373 | |
376 | - /* Port based VLANs configuration */ | |
377 | - if ((swconfig->vlancfg == MV88E61XX_VLANCFG_DEFAULT) | |
378 | - || (swconfig->vlancfg == MV88E61XX_VLANCFG_ROUTER)) | |
379 | - mv88e61xx_port_vlan_config(swconfig, MV88E61XX_MAX_PORTS_NUM, | |
380 | - MV88E61XX_PRT_OFST); | |
381 | - else { | |
382 | - printf("Unsupported mode %s failed\n", __FUNCTION__); | |
383 | - return -1; | |
374 | + /* be sure all ports are disabled */ | |
375 | + for (prt = 0; prt < MV88E61XX_MAX_PORTS_NUM; prt++) { | |
376 | + RD_SWITCH_PORT_REG(name, prt, MV88E61XX_PRT_CTRL_REG, ®); | |
377 | + reg &= ~0x3; | |
378 | + WR_SWITCH_PORT_REG(name, prt, MV88E61XX_PRT_CTRL_REG, reg); | |
384 | 379 | } |
385 | 380 | |
381 | + /* wait 2 ms for queues to drain */ | |
382 | + udelay(2000); | |
383 | + | |
384 | + /* reset switch */ | |
385 | + RD_SWITCH_REG(name, MV88E61XX_GLBREG_DEVADR, MV88E61XX_SGCR, ®); | |
386 | + reg |= 0x8000; | |
387 | + WR_SWITCH_REG(name, MV88E61XX_GLBREG_DEVADR, MV88E61XX_SGCR, reg); | |
388 | + | |
389 | + /* wait up to 1 second for switch reset complete */ | |
390 | + for (time = 1000; time; time--) { | |
391 | + RD_SWITCH_REG(name, MV88E61XX_GLBREG_DEVADR, MV88E61XX_SGSR, | |
392 | + ®); | |
393 | + if ((reg & 0xc800) == 0xc800) | |
394 | + break; | |
395 | + udelay(1000); | |
396 | + } | |
397 | + if (!time) | |
398 | + return -1; | |
399 | + | |
400 | + /* Port based VLANs configuration */ | |
401 | + mv88e61xx_port_vlan_config(swconfig); | |
402 | + | |
386 | 403 | if (swconfig->rgmii_delay == MV88E61XX_RGMII_DELAY_EN) { |
387 | 404 | /* |
388 | 405 | * Enable RGMII delay on Tx and Rx for CPU port |
389 | 406 | * Ref: sec 9.5 of chip datasheet-02 |
390 | 407 | */ |
391 | - WR_PHY(name, MV88E61XX_PRT_OFST + 5, | |
392 | - MV88E61XX_RGMII_TIMECTRL_REG, 0x18); | |
393 | - WR_PHY(name, MV88E61XX_PRT_OFST + 4, | |
394 | - MV88E61XX_RGMII_TIMECTRL_REG, 0xc1e7); | |
408 | + /*Force port link down */ | |
409 | + WR_SWITCH_PORT_REG(name, 5, MV88E61XX_PCS_CTRL_REG, 0x10); | |
410 | + /* configure port RGMII delay */ | |
411 | + WR_SWITCH_PORT_REG(name, 4, | |
412 | + MV88E61XX_RGMII_TIMECTRL_REG, 0x81e7); | |
413 | + RD_SWITCH_PORT_REG(name, 5, | |
414 | + MV88E61XX_RGMII_TIMECTRL_REG, ®); | |
415 | + WR_SWITCH_PORT_REG(name, 5, | |
416 | + MV88E61XX_RGMII_TIMECTRL_REG, reg | 0x18); | |
417 | + WR_SWITCH_PORT_REG(name, 4, | |
418 | + MV88E61XX_RGMII_TIMECTRL_REG, 0xc1e7); | |
419 | + /* Force port to RGMII FDX 1000Base then up */ | |
420 | + WR_SWITCH_PORT_REG(name, 5, MV88E61XX_PCS_CTRL_REG, 0x1e); | |
421 | + WR_SWITCH_PORT_REG(name, 5, MV88E61XX_PCS_CTRL_REG, 0x3e); | |
395 | 422 | } |
396 | 423 | |
397 | 424 | for (prt = 0; prt < MV88E61XX_MAX_PORTS_NUM; prt++) { |
398 | - if (!((1 << prt) & swconfig->cpuport)) { | |
399 | 425 | |
400 | - if (mv88361xx_led_init(swconfig, prt)) | |
426 | + /* configure port's PHY */ | |
427 | + if (!((1 << prt) & swconfig->cpuport)) { | |
428 | + /* port 4 has phy 6, not 4 */ | |
429 | + int phy = (prt == 4) ? 6 : prt; | |
430 | + if (mv88361xx_powerup(swconfig, phy)) | |
401 | 431 | return -1; |
402 | - if (mv88361xx_reverse_mdipn(swconfig, prt)) | |
432 | + if (mv88361xx_reverse_mdipn(swconfig, phy)) | |
403 | 433 | return -1; |
404 | - if (mv88361xx_powerup(swconfig, prt)) | |
434 | + if (mv88361xx_led_init(swconfig, phy)) | |
405 | 435 | return -1; |
406 | 436 | } |
407 | 437 | |
438 | + /* set port VID to port+1 except for cpu port */ | |
439 | + if (!((1 << prt) & swconfig->cpuport)) { | |
440 | + RD_SWITCH_PORT_REG(name, prt, | |
441 | + MV88E61XX_PRT_VID_REG, ®); | |
442 | + WR_SWITCH_PORT_REG(name, prt, | |
443 | + MV88E61XX_PRT_VID_REG, | |
444 | + (reg & ~1023) | (prt+1)); | |
445 | + } | |
446 | + | |
408 | 447 | /*Program port state */ |
409 | - RD_PHY(name, MV88E61XX_PRT_OFST + prt, | |
410 | - MV88E61XX_PRT_CTRL_REG, ®); | |
411 | - WR_PHY(name, MV88E61XX_PRT_OFST + prt, | |
412 | - MV88E61XX_PRT_CTRL_REG, | |
413 | - reg | (swconfig->portstate & 0x03)); | |
448 | + RD_SWITCH_PORT_REG(name, prt, | |
449 | + MV88E61XX_PRT_CTRL_REG, ®); | |
450 | + WR_SWITCH_PORT_REG(name, prt, | |
451 | + MV88E61XX_PRT_CTRL_REG, | |
452 | + reg | (swconfig->portstate & 0x03)); | |
453 | + | |
414 | 454 | } |
415 | 455 | |
416 | 456 | printf("%s Initialized on %s\n", idstr, name); |
417 | 457 | return 0; |
418 | 458 | } |
459 | + | |
460 | +#ifdef CONFIG_MV88E61XX_CMD | |
461 | +static int | |
462 | +do_switch(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) | |
463 | +{ | |
464 | + char *name, *endp; | |
465 | + int write = 0; | |
466 | + enum { dev, prt, phy } target = dev; | |
467 | + u32 addrlo, addrhi, addr; | |
468 | + u32 reglo, reghi, reg; | |
469 | + u16 data, rdata; | |
470 | + | |
471 | + if (argc < 7) | |
472 | + return -1; | |
473 | + | |
474 | + name = argv[1]; | |
475 | + | |
476 | + if (strcmp(argv[2], "phy") == 0) | |
477 | + target = phy; | |
478 | + else if (strcmp(argv[2], "port") == 0) | |
479 | + target = prt; | |
480 | + else if (strcmp(argv[2], "dev") != 0) | |
481 | + return 1; | |
482 | + | |
483 | + addrlo = simple_strtoul(argv[3], &endp, 16); | |
484 | + | |
485 | + if (!*endp) { | |
486 | + addrhi = addrlo; | |
487 | + } else { | |
488 | + while (*endp < '0' || *endp > '9') | |
489 | + endp++; | |
490 | + addrhi = simple_strtoul(endp, NULL, 16); | |
491 | + } | |
492 | + | |
493 | + reglo = simple_strtoul(argv[5], &endp, 16); | |
494 | + if (!*endp) { | |
495 | + reghi = reglo; | |
496 | + } else { | |
497 | + while (*endp < '0' || *endp > '9') | |
498 | + endp++; | |
499 | + reghi = simple_strtoul(endp, NULL, 16); | |
500 | + } | |
501 | + | |
502 | + if (strcmp(argv[6], "write") == 0) | |
503 | + write = 1; | |
504 | + else if (strcmp(argv[6], "read") != 0) | |
505 | + return 1; | |
506 | + | |
507 | + data = simple_strtoul(argv[7], NULL, 16); | |
508 | + | |
509 | + for (addr = addrlo; addr <= addrhi; addr++) { | |
510 | + for (reg = reglo; reg <= reghi; reg++) { | |
511 | + if (write) { | |
512 | + if (target == phy) | |
513 | + mv88e61xx_switch_miiphy_write( | |
514 | + name, addr, reg, data); | |
515 | + else if (target == prt) | |
516 | + wr_switch_reg(name, | |
517 | + addr+MV88E61XX_PRT_OFST, | |
518 | + reg, data); | |
519 | + else | |
520 | + wr_switch_reg(name, addr, reg, data); | |
521 | + } else { | |
522 | + if (target == phy) | |
523 | + mv88e61xx_switch_miiphy_read( | |
524 | + name, addr, reg, &rdata); | |
525 | + else if (target == prt) | |
526 | + rd_switch_reg(name, | |
527 | + addr+MV88E61XX_PRT_OFST, | |
528 | + reg, &rdata); | |
529 | + else | |
530 | + rd_switch_reg(name, addr, reg, &rdata); | |
531 | + printf("%s %s %s %02x %s %02x %s %04x\n", | |
532 | + argv[0], argv[1], argv[2], addr, | |
533 | + argv[4], reg, argv[6], rdata); | |
534 | + if (write && argc == 7 && rdata != data) | |
535 | + return 1; | |
536 | + } | |
537 | + } | |
538 | + } | |
539 | + return 0; | |
540 | +} | |
541 | + | |
542 | +U_BOOT_CMD(mv88e61xx, 8, 0, do_switch, | |
543 | + "Read or write mv88e61xx switch registers", | |
544 | + "<ethdevice> dev|port|phy <addr> reg <reg> write <data>\n" | |
545 | + "<ethdevice> dev|port|phy <addr> reg <reg> read [<data>]\n" | |
546 | + " - read/write switch device, port or phy at (addr,reg)\n" | |
547 | + " addr=0..0x1C for dev, 0..5 for port or phy.\n" | |
548 | + " reg=0..0x1F.\n" | |
549 | + " data=0..0xFFFF (tested if present against actual read).\n" | |
550 | + " All numeric parameters are assumed to be hex.\n" | |
551 | + " <addr> and <<reg> arguments can be ranges (x..y)" | |
552 | +); | |
553 | +#endif /* CONFIG_MV88E61XX_CMD */ |
drivers/net/phy/mv88e61xx.h
... | ... | @@ -28,35 +28,50 @@ |
28 | 28 | #include <miiphy.h> |
29 | 29 | |
30 | 30 | #define MV88E61XX_CPU_PORT 0x5 |
31 | -#define MV88E61XX_MAX_PORTS_NUM 0x6 | |
32 | 31 | |
33 | 32 | #define MV88E61XX_PHY_TIMEOUT 100000 |
34 | 33 | |
35 | -#define MV88E61XX_PRT_STS_REG 0x1 | |
34 | +/* port dev-addr (= port + 0x10) */ | |
35 | +#define MV88E61XX_PRT_OFST 0x10 | |
36 | +/* port registers */ | |
37 | +#define MV88E61XX_PCS_CTRL_REG 0x1 | |
36 | 38 | #define MV88E61XX_PRT_CTRL_REG 0x4 |
37 | 39 | #define MV88E61XX_PRT_VMAP_REG 0x6 |
38 | 40 | #define MV88E61XX_PRT_VID_REG 0x7 |
41 | +#define MV88E61XX_RGMII_TIMECTRL_REG 0x1A | |
39 | 42 | |
40 | -#define MV88E61XX_PRT_OFST 0x10 | |
43 | +/* global registers dev-addr */ | |
44 | +#define MV88E61XX_GLBREG_DEVADR 0x1B | |
45 | +/* global registers */ | |
46 | +#define MV88E61XX_SGSR 0x00 | |
47 | +#define MV88E61XX_SGCR 0x04 | |
48 | + | |
49 | +/* global 2 registers dev-addr */ | |
50 | +#define MV88E61XX_GLB2REG_DEVADR 0x1C | |
51 | +/* global 2 registers */ | |
41 | 52 | #define MV88E61XX_PHY_CMD 0x18 |
42 | 53 | #define MV88E61XX_PHY_DATA 0x19 |
43 | -#define MV88E61XX_RGMII_TIMECTRL_REG 0x1A | |
44 | -#define MV88E61XX_GLB2REG_DEVADR 0x1C | |
54 | +/* global 2 phy commands */ | |
55 | +#define MV88E61XX_PHY_WRITE_CMD 0x9400 | |
56 | +#define MV88E61XX_PHY_READ_CMD 0x9800 | |
45 | 57 | |
46 | 58 | #define MV88E61XX_BUSY_OFST 15 |
47 | 59 | #define MV88E61XX_MODE_OFST 12 |
48 | -#define MV88E61XX_OP_OFST 10 | |
60 | +#define MV88E61XX_OP_OFST 10 | |
49 | 61 | #define MV88E61XX_ADDR_OFST 5 |
50 | 62 | |
51 | 63 | #ifdef CONFIG_MV88E61XX_MULTICHIP_ADRMODE |
52 | 64 | static int mv88e61xx_busychk_multic(char *name, u32 devaddr); |
53 | -static void mv88e61xx_wr_phy(char *name, u32 phy_adr, u32 reg_ofs, u16 data); | |
54 | -static void mv88e61xx_rd_phy(char *name, u32 phy_adr, u32 reg_ofs, u16 * data); | |
55 | -#define WR_PHY mv88e61xx_wr_phy | |
56 | -#define RD_PHY mv88e61xx_rd_phy | |
65 | +static void mv88e61xx_switch_write(char *name, u32 phy_adr, | |
66 | + u32 reg_ofs, u16 data); | |
67 | +static void mv88e61xx_switch_read(char *name, u32 phy_adr, | |
68 | + u32 reg_ofs, u16 *data); | |
69 | +#define wr_switch_reg mv88e61xx_switch_write | |
70 | +#define rd_switch_reg mv88e61xx_switch_read | |
57 | 71 | #else |
58 | -#define WR_PHY miiphy_write | |
59 | -#define RD_PHY miiphy_read | |
72 | +/* switch appears a s simple PHY and can thus use miiphy */ | |
73 | +#define wr_switch_reg miiphy_write | |
74 | +#define rd_switch_reg miiphy_read | |
60 | 75 | #endif /* CONFIG_MV88E61XX_MULTICHIP_ADRMODE */ |
61 | 76 | |
62 | 77 | #endif /* _MV88E61XX_H */ |
include/netdev.h
... | ... | @@ -163,11 +163,10 @@ |
163 | 163 | * the stuct and enums here are used to specify switch configuration params |
164 | 164 | */ |
165 | 165 | #if defined(CONFIG_MV88E61XX_SWITCH) |
166 | -enum mv88e61xx_cfg_vlan { | |
167 | - MV88E61XX_VLANCFG_DEFAULT, | |
168 | - MV88E61XX_VLANCFG_ROUTER | |
169 | -}; | |
170 | 166 | |
167 | +/* constants for any 88E61xx switch */ | |
168 | +#define MV88E61XX_MAX_PORTS_NUM 6 | |
169 | + | |
171 | 170 | enum mv88e61xx_cfg_mdip { |
172 | 171 | MV88E61XX_MDIP_NOCHANGE, |
173 | 172 | MV88E61XX_MDIP_REVERSE |
... | ... | @@ -192,7 +191,7 @@ |
192 | 191 | |
193 | 192 | struct mv88e61xx_config { |
194 | 193 | char *name; |
195 | - enum mv88e61xx_cfg_vlan vlancfg; | |
194 | + u8 vlancfg[MV88E61XX_MAX_PORTS_NUM]; | |
196 | 195 | enum mv88e61xx_cfg_rgmiid rgmii_delay; |
197 | 196 | enum mv88e61xx_cfg_prtstt portstate; |
198 | 197 | enum mv88e61xx_cfg_ledinit led_init; |
... | ... | @@ -200,6 +199,18 @@ |
200 | 199 | u32 ports_enabled; |
201 | 200 | u8 cpuport; |
202 | 201 | }; |
202 | + | |
203 | +/* | |
204 | + * Common mappings for Internal VLANs | |
205 | + * These mappings consider that all ports are useable; the driver | |
206 | + * will mask inexistent/unused ports. | |
207 | + */ | |
208 | + | |
209 | +/* Switch mode : routes any port to any port */ | |
210 | +#define MV88E61XX_VLANCFG_SWITCH { 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F } | |
211 | + | |
212 | +/* Router mode: routes only CPU port 5 to/from non-CPU ports 0-4 */ | |
213 | +#define MV88E61XX_VLANCFG_ROUTER { 0x20, 0x20, 0x20, 0x20, 0x20, 0x1F } | |
203 | 214 | |
204 | 215 | int mv88e61xx_switch_initialize(struct mv88e61xx_config *swconfig); |
205 | 216 | #endif /* CONFIG_MV88E61XX_SWITCH */ |