OpenWrt – Blame information for rev 3
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* |
2 | * Marvell 88E61xx switch driver |
||
3 | * |
||
4 | * Copyright (c) 2014 Claudio Leite <leitec@staticky.com> |
||
5 | * Copyright (c) 2014 Nikita Nazarenko <nnazarenko@radiofid.com> |
||
6 | * |
||
7 | * Based on code (c) 2008 Felix Fietkau <nbd@nbd.name> |
||
8 | * |
||
9 | * This program is free software; you can redistribute it and/or modify it |
||
10 | * under the terms of the GNU General Public License v2 as published by the |
||
11 | * Free Software Foundation |
||
12 | */ |
||
13 | |||
14 | #include <linux/kernel.h> |
||
15 | #include <linux/module.h> |
||
16 | #include <linux/init.h> |
||
17 | #include <linux/list.h> |
||
18 | #include <linux/mii.h> |
||
19 | #include <linux/phy.h> |
||
20 | #include <linux/of.h> |
||
21 | #include <linux/of_mdio.h> |
||
22 | #include <linux/delay.h> |
||
23 | #include <linux/switch.h> |
||
24 | #include <linux/device.h> |
||
25 | #include <linux/platform_device.h> |
||
26 | |||
27 | #include "mvsw61xx.h" |
||
28 | |||
29 | MODULE_DESCRIPTION("Marvell 88E61xx Switch driver"); |
||
30 | MODULE_AUTHOR("Claudio Leite <leitec@staticky.com>"); |
||
31 | MODULE_AUTHOR("Nikita Nazarenko <nnazarenko@radiofid.com>"); |
||
32 | MODULE_LICENSE("GPL v2"); |
||
33 | MODULE_ALIAS("platform:mvsw61xx"); |
||
34 | |||
35 | /* |
||
36 | * Register access is done through direct or indirect addressing, |
||
37 | * depending on how the switch is physically connected. |
||
38 | * |
||
39 | * Direct addressing: all port and global registers directly |
||
40 | * accessible via an address/register pair |
||
41 | * |
||
42 | * Indirect addressing: switch is mapped at a single address, |
||
43 | * port and global registers accessible via a single command/data |
||
44 | * register pair |
||
45 | */ |
||
46 | |||
47 | static int |
||
48 | mvsw61xx_wait_mask_raw(struct mii_bus *bus, int addr, |
||
49 | int reg, u16 mask, u16 val) |
||
50 | { |
||
51 | int i = 100; |
||
52 | u16 r; |
||
53 | |||
54 | do { |
||
55 | r = bus->read(bus, addr, reg); |
||
56 | if ((r & mask) == val) |
||
57 | return 0; |
||
58 | } while (--i > 0); |
||
59 | |||
60 | return -ETIMEDOUT; |
||
61 | } |
||
62 | |||
63 | static u16 |
||
64 | r16(struct mii_bus *bus, bool indirect, int base_addr, int addr, int reg) |
||
65 | { |
||
66 | u16 ind_addr; |
||
67 | |||
68 | if (!indirect) |
||
69 | return bus->read(bus, addr, reg); |
||
70 | |||
71 | /* Indirect read: First, make sure switch is free */ |
||
72 | mvsw61xx_wait_mask_raw(bus, base_addr, MV_INDIRECT_REG_CMD, |
||
73 | MV_INDIRECT_INPROGRESS, 0); |
||
74 | |||
75 | /* Load address and request read */ |
||
76 | ind_addr = MV_INDIRECT_READ | (addr << MV_INDIRECT_ADDR_S) | reg; |
||
77 | bus->write(bus, base_addr, MV_INDIRECT_REG_CMD, |
||
78 | ind_addr); |
||
79 | |||
80 | /* Wait until it's ready */ |
||
81 | mvsw61xx_wait_mask_raw(bus, base_addr, MV_INDIRECT_REG_CMD, |
||
82 | MV_INDIRECT_INPROGRESS, 0); |
||
83 | |||
84 | /* Read the requested data */ |
||
85 | return bus->read(bus, base_addr, MV_INDIRECT_REG_DATA); |
||
86 | } |
||
87 | |||
88 | static void |
||
89 | w16(struct mii_bus *bus, bool indirect, int base_addr, int addr, |
||
90 | int reg, u16 val) |
||
91 | { |
||
92 | u16 ind_addr; |
||
93 | |||
94 | if (!indirect) { |
||
95 | bus->write(bus, addr, reg, val); |
||
96 | return; |
||
97 | } |
||
98 | |||
99 | /* Indirect write: First, make sure switch is free */ |
||
100 | mvsw61xx_wait_mask_raw(bus, base_addr, MV_INDIRECT_REG_CMD, |
||
101 | MV_INDIRECT_INPROGRESS, 0); |
||
102 | |||
103 | /* Load the data to be written */ |
||
104 | bus->write(bus, base_addr, MV_INDIRECT_REG_DATA, val); |
||
105 | |||
106 | /* Wait again for switch to be free */ |
||
107 | mvsw61xx_wait_mask_raw(bus, base_addr, MV_INDIRECT_REG_CMD, |
||
108 | MV_INDIRECT_INPROGRESS, 0); |
||
109 | |||
110 | /* Load address, and issue write command */ |
||
111 | ind_addr = MV_INDIRECT_WRITE | (addr << MV_INDIRECT_ADDR_S) | reg; |
||
112 | bus->write(bus, base_addr, MV_INDIRECT_REG_CMD, |
||
113 | ind_addr); |
||
114 | } |
||
115 | |||
116 | /* swconfig support */ |
||
117 | |||
118 | static inline u16 |
||
119 | sr16(struct switch_dev *dev, int addr, int reg) |
||
120 | { |
||
121 | struct mvsw61xx_state *state = get_state(dev); |
||
122 | |||
123 | return r16(state->bus, state->is_indirect, state->base_addr, addr, reg); |
||
124 | } |
||
125 | |||
126 | static inline void |
||
127 | sw16(struct switch_dev *dev, int addr, int reg, u16 val) |
||
128 | { |
||
129 | struct mvsw61xx_state *state = get_state(dev); |
||
130 | |||
131 | w16(state->bus, state->is_indirect, state->base_addr, addr, reg, val); |
||
132 | } |
||
133 | |||
134 | static int |
||
135 | mvsw61xx_wait_mask_s(struct switch_dev *dev, int addr, |
||
136 | int reg, u16 mask, u16 val) |
||
137 | { |
||
138 | int i = 100; |
||
139 | u16 r; |
||
140 | |||
141 | do { |
||
142 | r = sr16(dev, addr, reg) & mask; |
||
143 | if (r == val) |
||
144 | return 0; |
||
145 | } while (--i > 0); |
||
146 | |||
147 | return -ETIMEDOUT; |
||
148 | } |
||
149 | |||
150 | static int |
||
151 | mvsw61xx_mdio_read(struct switch_dev *dev, int addr, int reg) |
||
152 | { |
||
153 | sw16(dev, MV_GLOBAL2REG(SMI_OP), |
||
154 | MV_INDIRECT_READ | (addr << MV_INDIRECT_ADDR_S) | reg); |
||
155 | |||
156 | if (mvsw61xx_wait_mask_s(dev, MV_GLOBAL2REG(SMI_OP), |
||
157 | MV_INDIRECT_INPROGRESS, 0) < 0) |
||
158 | return -ETIMEDOUT; |
||
159 | |||
160 | return sr16(dev, MV_GLOBAL2REG(SMI_DATA)); |
||
161 | } |
||
162 | |||
163 | static int |
||
164 | mvsw61xx_mdio_write(struct switch_dev *dev, int addr, int reg, u16 val) |
||
165 | { |
||
166 | sw16(dev, MV_GLOBAL2REG(SMI_DATA), val); |
||
167 | |||
168 | sw16(dev, MV_GLOBAL2REG(SMI_OP), |
||
169 | MV_INDIRECT_WRITE | (addr << MV_INDIRECT_ADDR_S) | reg); |
||
170 | |||
171 | return mvsw61xx_wait_mask_s(dev, MV_GLOBAL2REG(SMI_OP), |
||
172 | MV_INDIRECT_INPROGRESS, 0) < 0; |
||
173 | } |
||
174 | |||
175 | static int |
||
176 | mvsw61xx_mdio_page_read(struct switch_dev *dev, int port, int page, int reg) |
||
177 | { |
||
178 | int ret; |
||
179 | |||
180 | mvsw61xx_mdio_write(dev, port, MII_MV_PAGE, page); |
||
181 | ret = mvsw61xx_mdio_read(dev, port, reg); |
||
182 | mvsw61xx_mdio_write(dev, port, MII_MV_PAGE, 0); |
||
183 | |||
184 | return ret; |
||
185 | } |
||
186 | |||
187 | static void |
||
188 | mvsw61xx_mdio_page_write(struct switch_dev *dev, int port, int page, int reg, |
||
189 | u16 val) |
||
190 | { |
||
191 | mvsw61xx_mdio_write(dev, port, MII_MV_PAGE, page); |
||
192 | mvsw61xx_mdio_write(dev, port, reg, val); |
||
193 | mvsw61xx_mdio_write(dev, port, MII_MV_PAGE, 0); |
||
194 | } |
||
195 | |||
196 | static int |
||
197 | mvsw61xx_get_port_mask(struct switch_dev *dev, |
||
198 | const struct switch_attr *attr, struct switch_val *val) |
||
199 | { |
||
200 | struct mvsw61xx_state *state = get_state(dev); |
||
201 | char *buf = state->buf; |
||
202 | int port, len, i; |
||
203 | u16 reg; |
||
204 | |||
205 | port = val->port_vlan; |
||
206 | reg = sr16(dev, MV_PORTREG(VLANMAP, port)) & MV_PORTS_MASK; |
||
207 | |||
208 | len = sprintf(buf, "0x%04x: ", reg); |
||
209 | |||
210 | for (i = 0; i < MV_PORTS; i++) { |
||
211 | if (reg & (1 << i)) |
||
212 | len += sprintf(buf + len, "%d ", i); |
||
213 | else if (i == port) |
||
214 | len += sprintf(buf + len, "(%d) ", i); |
||
215 | } |
||
216 | |||
217 | val->value.s = buf; |
||
218 | |||
219 | return 0; |
||
220 | } |
||
221 | |||
222 | static int |
||
223 | mvsw61xx_get_port_qmode(struct switch_dev *dev, |
||
224 | const struct switch_attr *attr, struct switch_val *val) |
||
225 | { |
||
226 | struct mvsw61xx_state *state = get_state(dev); |
||
227 | |||
228 | val->value.i = state->ports[val->port_vlan].qmode; |
||
229 | |||
230 | return 0; |
||
231 | } |
||
232 | |||
233 | static int |
||
234 | mvsw61xx_set_port_qmode(struct switch_dev *dev, |
||
235 | const struct switch_attr *attr, struct switch_val *val) |
||
236 | { |
||
237 | struct mvsw61xx_state *state = get_state(dev); |
||
238 | |||
239 | state->ports[val->port_vlan].qmode = val->value.i; |
||
240 | |||
241 | return 0; |
||
242 | } |
||
243 | |||
244 | static int |
||
245 | mvsw61xx_get_port_pvid(struct switch_dev *dev, int port, int *val) |
||
246 | { |
||
247 | struct mvsw61xx_state *state = get_state(dev); |
||
248 | |||
249 | *val = state->ports[port].pvid; |
||
250 | |||
251 | return 0; |
||
252 | } |
||
253 | |||
254 | static int |
||
255 | mvsw61xx_set_port_pvid(struct switch_dev *dev, int port, int val) |
||
256 | { |
||
257 | struct mvsw61xx_state *state = get_state(dev); |
||
258 | |||
259 | if (val < 0 || val >= MV_VLANS) |
||
260 | return -EINVAL; |
||
261 | |||
262 | state->ports[port].pvid = (u16)val; |
||
263 | |||
264 | return 0; |
||
265 | } |
||
266 | |||
267 | static int |
||
268 | mvsw61xx_get_port_link(struct switch_dev *dev, int port, |
||
269 | struct switch_port_link *link) |
||
270 | { |
||
271 | u16 status, speed; |
||
272 | |||
273 | status = sr16(dev, MV_PORTREG(STATUS, port)); |
||
274 | |||
275 | link->link = status & MV_PORT_STATUS_LINK; |
||
276 | if (!link->link) |
||
277 | return 0; |
||
278 | |||
279 | link->duplex = status & MV_PORT_STATUS_FDX; |
||
280 | |||
281 | speed = (status & MV_PORT_STATUS_SPEED_MASK) >> |
||
282 | MV_PORT_STATUS_SPEED_SHIFT; |
||
283 | |||
284 | switch (speed) { |
||
285 | case MV_PORT_STATUS_SPEED_10: |
||
286 | link->speed = SWITCH_PORT_SPEED_10; |
||
287 | break; |
||
288 | case MV_PORT_STATUS_SPEED_100: |
||
289 | link->speed = SWITCH_PORT_SPEED_100; |
||
290 | break; |
||
291 | case MV_PORT_STATUS_SPEED_1000: |
||
292 | link->speed = SWITCH_PORT_SPEED_1000; |
||
293 | break; |
||
294 | } |
||
295 | |||
296 | return 0; |
||
297 | } |
||
298 | |||
299 | static int mvsw61xx_get_vlan_ports(struct switch_dev *dev, |
||
300 | struct switch_val *val) |
||
301 | { |
||
302 | struct mvsw61xx_state *state = get_state(dev); |
||
303 | int i, j, mode, vno; |
||
304 | |||
305 | vno = val->port_vlan; |
||
306 | |||
307 | if (vno <= 0 || vno >= dev->vlans) |
||
308 | return -EINVAL; |
||
309 | |||
310 | for (i = 0, j = 0; i < dev->ports; i++) { |
||
311 | if (state->vlans[vno].mask & (1 << i)) { |
||
312 | val->value.ports[j].id = i; |
||
313 | |||
314 | mode = (state->vlans[vno].port_mode >> (i * 4)) & 0xf; |
||
315 | if (mode == MV_VTUCTL_EGRESS_TAGGED) |
||
316 | val->value.ports[j].flags = |
||
317 | (1 << SWITCH_PORT_FLAG_TAGGED); |
||
318 | else |
||
319 | val->value.ports[j].flags = 0; |
||
320 | |||
321 | j++; |
||
322 | } |
||
323 | } |
||
324 | |||
325 | val->len = j; |
||
326 | |||
327 | return 0; |
||
328 | } |
||
329 | |||
330 | static int mvsw61xx_set_vlan_ports(struct switch_dev *dev, |
||
331 | struct switch_val *val) |
||
332 | { |
||
333 | struct mvsw61xx_state *state = get_state(dev); |
||
334 | int i, mode, pno, vno; |
||
335 | |||
336 | vno = val->port_vlan; |
||
337 | |||
338 | if (vno <= 0 || vno >= dev->vlans) |
||
339 | return -EINVAL; |
||
340 | |||
341 | state->vlans[vno].mask = 0; |
||
342 | state->vlans[vno].port_mode = 0; |
||
343 | state->vlans[vno].port_sstate = 0; |
||
344 | |||
345 | if(state->vlans[vno].vid == 0) |
||
346 | state->vlans[vno].vid = vno; |
||
347 | |||
348 | for (i = 0; i < val->len; i++) { |
||
349 | pno = val->value.ports[i].id; |
||
350 | |||
351 | state->vlans[vno].mask |= (1 << pno); |
||
352 | if (val->value.ports[i].flags & |
||
353 | (1 << SWITCH_PORT_FLAG_TAGGED)) |
||
354 | mode = MV_VTUCTL_EGRESS_TAGGED; |
||
355 | else |
||
356 | mode = MV_VTUCTL_EGRESS_UNTAGGED; |
||
357 | |||
358 | state->vlans[vno].port_mode |= mode << (pno * 4); |
||
359 | state->vlans[vno].port_sstate |= |
||
360 | MV_STUCTL_STATE_FORWARDING << (pno * 4 + 2); |
||
361 | } |
||
362 | |||
363 | /* |
||
364 | * DISCARD is nonzero, so it must be explicitly |
||
365 | * set on ports not in the VLAN. |
||
366 | */ |
||
367 | for (i = 0; i < dev->ports; i++) |
||
368 | if (!(state->vlans[vno].mask & (1 << i))) |
||
369 | state->vlans[vno].port_mode |= |
||
370 | MV_VTUCTL_DISCARD << (i * 4); |
||
371 | |||
372 | return 0; |
||
373 | } |
||
374 | |||
375 | static int mvsw61xx_get_vlan_port_based(struct switch_dev *dev, |
||
376 | const struct switch_attr *attr, struct switch_val *val) |
||
377 | { |
||
378 | struct mvsw61xx_state *state = get_state(dev); |
||
379 | int vno = val->port_vlan; |
||
380 | |||
381 | if (vno <= 0 || vno >= dev->vlans) |
||
382 | return -EINVAL; |
||
383 | |||
384 | if (state->vlans[vno].port_based) |
||
385 | val->value.i = 1; |
||
386 | else |
||
387 | val->value.i = 0; |
||
388 | |||
389 | return 0; |
||
390 | } |
||
391 | |||
392 | static int mvsw61xx_set_vlan_port_based(struct switch_dev *dev, |
||
393 | const struct switch_attr *attr, struct switch_val *val) |
||
394 | { |
||
395 | struct mvsw61xx_state *state = get_state(dev); |
||
396 | int vno = val->port_vlan; |
||
397 | |||
398 | if (vno <= 0 || vno >= dev->vlans) |
||
399 | return -EINVAL; |
||
400 | |||
401 | if (val->value.i == 1) |
||
402 | state->vlans[vno].port_based = true; |
||
403 | else |
||
404 | state->vlans[vno].port_based = false; |
||
405 | |||
406 | return 0; |
||
407 | } |
||
408 | |||
409 | static int mvsw61xx_get_vid(struct switch_dev *dev, |
||
410 | const struct switch_attr *attr, struct switch_val *val) |
||
411 | { |
||
412 | struct mvsw61xx_state *state = get_state(dev); |
||
413 | int vno = val->port_vlan; |
||
414 | |||
415 | if (vno <= 0 || vno >= dev->vlans) |
||
416 | return -EINVAL; |
||
417 | |||
418 | val->value.i = state->vlans[vno].vid; |
||
419 | |||
420 | return 0; |
||
421 | } |
||
422 | |||
423 | static int mvsw61xx_set_vid(struct switch_dev *dev, |
||
424 | const struct switch_attr *attr, struct switch_val *val) |
||
425 | { |
||
426 | struct mvsw61xx_state *state = get_state(dev); |
||
427 | int vno = val->port_vlan; |
||
428 | |||
429 | if (vno <= 0 || vno >= dev->vlans) |
||
430 | return -EINVAL; |
||
431 | |||
432 | state->vlans[vno].vid = val->value.i; |
||
433 | |||
434 | return 0; |
||
435 | } |
||
436 | |||
437 | static int mvsw61xx_get_enable_vlan(struct switch_dev *dev, |
||
438 | const struct switch_attr *attr, struct switch_val *val) |
||
439 | { |
||
440 | struct mvsw61xx_state *state = get_state(dev); |
||
441 | |||
442 | val->value.i = state->vlan_enabled; |
||
443 | |||
444 | return 0; |
||
445 | } |
||
446 | |||
447 | static int mvsw61xx_set_enable_vlan(struct switch_dev *dev, |
||
448 | const struct switch_attr *attr, struct switch_val *val) |
||
449 | { |
||
450 | struct mvsw61xx_state *state = get_state(dev); |
||
451 | |||
452 | state->vlan_enabled = val->value.i; |
||
453 | |||
454 | return 0; |
||
455 | } |
||
456 | |||
457 | static int mvsw61xx_vtu_program(struct switch_dev *dev) |
||
458 | { |
||
459 | struct mvsw61xx_state *state = get_state(dev); |
||
460 | u16 v1, v2, s1, s2; |
||
461 | int i; |
||
462 | |||
463 | /* Flush */ |
||
464 | mvsw61xx_wait_mask_s(dev, MV_GLOBALREG(VTU_OP), |
||
465 | MV_VTUOP_INPROGRESS, 0); |
||
466 | sw16(dev, MV_GLOBALREG(VTU_OP), |
||
467 | MV_VTUOP_INPROGRESS | MV_VTUOP_PURGE); |
||
468 | |||
469 | /* Write VLAN table */ |
||
470 | for (i = 1; i < dev->vlans; i++) { |
||
471 | if (state->vlans[i].mask == 0 || |
||
472 | state->vlans[i].vid == 0 || |
||
473 | state->vlans[i].port_based == true) |
||
474 | continue; |
||
475 | |||
476 | mvsw61xx_wait_mask_s(dev, MV_GLOBALREG(VTU_OP), |
||
477 | MV_VTUOP_INPROGRESS, 0); |
||
478 | |||
479 | /* Write per-VLAN port state into STU */ |
||
480 | s1 = (u16) (state->vlans[i].port_sstate & 0xffff); |
||
481 | s2 = (u16) ((state->vlans[i].port_sstate >> 16) & 0xffff); |
||
482 | |||
483 | sw16(dev, MV_GLOBALREG(VTU_VID), MV_VTU_VID_VALID); |
||
484 | sw16(dev, MV_GLOBALREG(VTU_SID), i); |
||
485 | sw16(dev, MV_GLOBALREG(VTU_DATA1), s1); |
||
486 | sw16(dev, MV_GLOBALREG(VTU_DATA2), s2); |
||
487 | sw16(dev, MV_GLOBALREG(VTU_DATA3), 0); |
||
488 | |||
489 | sw16(dev, MV_GLOBALREG(VTU_OP), |
||
490 | MV_VTUOP_INPROGRESS | MV_VTUOP_STULOAD); |
||
491 | mvsw61xx_wait_mask_s(dev, MV_GLOBALREG(VTU_OP), |
||
492 | MV_VTUOP_INPROGRESS, 0); |
||
493 | |||
494 | /* Write VLAN information into VTU */ |
||
495 | v1 = (u16) (state->vlans[i].port_mode & 0xffff); |
||
496 | v2 = (u16) ((state->vlans[i].port_mode >> 16) & 0xffff); |
||
497 | |||
498 | sw16(dev, MV_GLOBALREG(VTU_VID), |
||
499 | MV_VTU_VID_VALID | state->vlans[i].vid); |
||
500 | sw16(dev, MV_GLOBALREG(VTU_SID), i); |
||
501 | sw16(dev, MV_GLOBALREG(VTU_FID), i); |
||
502 | sw16(dev, MV_GLOBALREG(VTU_DATA1), v1); |
||
503 | sw16(dev, MV_GLOBALREG(VTU_DATA2), v2); |
||
504 | sw16(dev, MV_GLOBALREG(VTU_DATA3), 0); |
||
505 | |||
506 | sw16(dev, MV_GLOBALREG(VTU_OP), |
||
507 | MV_VTUOP_INPROGRESS | MV_VTUOP_LOAD); |
||
508 | mvsw61xx_wait_mask_s(dev, MV_GLOBALREG(VTU_OP), |
||
509 | MV_VTUOP_INPROGRESS, 0); |
||
510 | } |
||
511 | |||
512 | return 0; |
||
513 | } |
||
514 | |||
515 | static void mvsw61xx_vlan_port_config(struct switch_dev *dev, int vno) |
||
516 | { |
||
517 | struct mvsw61xx_state *state = get_state(dev); |
||
518 | int i, mode; |
||
519 | |||
520 | for (i = 0; i < dev->ports; i++) { |
||
521 | if (!(state->vlans[vno].mask & (1 << i))) |
||
522 | continue; |
||
523 | |||
524 | mode = (state->vlans[vno].port_mode >> (i * 4)) & 0xf; |
||
525 | |||
526 | if(mode != MV_VTUCTL_EGRESS_TAGGED) |
||
527 | state->ports[i].pvid = state->vlans[vno].vid; |
||
528 | |||
529 | if (state->vlans[vno].port_based) { |
||
530 | state->ports[i].mask |= state->vlans[vno].mask; |
||
531 | state->ports[i].fdb = vno; |
||
532 | } |
||
533 | else |
||
534 | state->ports[i].qmode = MV_8021Q_MODE_SECURE; |
||
535 | } |
||
536 | } |
||
537 | |||
538 | static int mvsw61xx_update_state(struct switch_dev *dev) |
||
539 | { |
||
540 | struct mvsw61xx_state *state = get_state(dev); |
||
541 | int i; |
||
542 | u16 reg; |
||
543 | |||
544 | if (!state->registered) |
||
545 | return -EINVAL; |
||
546 | |||
547 | /* |
||
548 | * Set 802.1q-only mode if vlan_enabled is true. |
||
549 | * |
||
550 | * Without this, even if 802.1q is enabled for |
||
551 | * a port/VLAN, it still depends on the port-based |
||
552 | * VLAN mask being set. |
||
553 | * |
||
554 | * With this setting, port-based VLANs are still |
||
555 | * functional, provided the VID is not in the VTU. |
||
556 | */ |
||
557 | reg = sr16(dev, MV_GLOBAL2REG(SDET_POLARITY)); |
||
558 | |||
559 | if (state->vlan_enabled) |
||
560 | reg |= MV_8021Q_VLAN_ONLY; |
||
561 | else |
||
562 | reg &= ~MV_8021Q_VLAN_ONLY; |
||
563 | |||
564 | sw16(dev, MV_GLOBAL2REG(SDET_POLARITY), reg); |
||
565 | |||
566 | /* |
||
567 | * Set port-based VLAN masks on each port |
||
568 | * based only on VLAN definitions known to |
||
569 | * the driver (i.e. in state). |
||
570 | * |
||
571 | * This means any pre-existing port mapping is |
||
572 | * wiped out once our driver is initialized. |
||
573 | */ |
||
574 | for (i = 0; i < dev->ports; i++) { |
||
575 | state->ports[i].mask = 0; |
||
576 | state->ports[i].qmode = MV_8021Q_MODE_DISABLE; |
||
577 | } |
||
578 | |||
579 | for (i = 0; i < dev->vlans; i++) |
||
580 | mvsw61xx_vlan_port_config(dev, i); |
||
581 | |||
582 | for (i = 0; i < dev->ports; i++) { |
||
583 | reg = sr16(dev, MV_PORTREG(VLANID, i)) & ~MV_PVID_MASK; |
||
584 | reg |= state->ports[i].pvid; |
||
585 | sw16(dev, MV_PORTREG(VLANID, i), reg); |
||
586 | |||
587 | state->ports[i].mask &= ~(1 << i); |
||
588 | |||
589 | /* set default forwarding DB number and port mask */ |
||
590 | reg = sr16(dev, MV_PORTREG(CONTROL1, i)) & ~MV_FDB_HI_MASK; |
||
591 | reg |= (state->ports[i].fdb >> MV_FDB_HI_SHIFT) & |
||
592 | MV_FDB_HI_MASK; |
||
593 | sw16(dev, MV_PORTREG(CONTROL1, i), reg); |
||
594 | |||
595 | reg = ((state->ports[i].fdb & 0xf) << MV_FDB_LO_SHIFT) | |
||
596 | state->ports[i].mask; |
||
597 | sw16(dev, MV_PORTREG(VLANMAP, i), reg); |
||
598 | |||
599 | reg = sr16(dev, MV_PORTREG(CONTROL2, i)) & |
||
600 | ~MV_8021Q_MODE_MASK; |
||
601 | reg |= state->ports[i].qmode << MV_8021Q_MODE_SHIFT; |
||
602 | sw16(dev, MV_PORTREG(CONTROL2, i), reg); |
||
603 | } |
||
604 | |||
605 | mvsw61xx_vtu_program(dev); |
||
606 | |||
607 | return 0; |
||
608 | } |
||
609 | |||
610 | static int mvsw61xx_apply(struct switch_dev *dev) |
||
611 | { |
||
612 | return mvsw61xx_update_state(dev); |
||
613 | } |
||
614 | |||
615 | static void mvsw61xx_enable_serdes(struct switch_dev *dev) |
||
616 | { |
||
617 | int bmcr = mvsw61xx_mdio_page_read(dev, MV_REG_FIBER_SERDES, |
||
618 | MV_PAGE_FIBER_SERDES, MII_BMCR); |
||
619 | if (bmcr < 0) |
||
620 | return; |
||
621 | |||
622 | if (bmcr & BMCR_PDOWN) |
||
623 | mvsw61xx_mdio_page_write(dev, MV_REG_FIBER_SERDES, |
||
624 | MV_PAGE_FIBER_SERDES, MII_BMCR, |
||
625 | bmcr & ~BMCR_PDOWN); |
||
626 | } |
||
627 | |||
628 | static int _mvsw61xx_reset(struct switch_dev *dev, bool full) |
||
629 | { |
||
630 | struct mvsw61xx_state *state = get_state(dev); |
||
631 | int i; |
||
632 | u16 reg; |
||
633 | |||
634 | /* Disable all ports before reset */ |
||
635 | for (i = 0; i < dev->ports; i++) { |
||
636 | reg = sr16(dev, MV_PORTREG(CONTROL, i)) & |
||
637 | ~MV_PORTCTRL_FORWARDING; |
||
638 | sw16(dev, MV_PORTREG(CONTROL, i), reg); |
||
639 | } |
||
640 | |||
641 | reg = sr16(dev, MV_GLOBALREG(CONTROL)) | MV_CONTROL_RESET; |
||
642 | |||
643 | sw16(dev, MV_GLOBALREG(CONTROL), reg); |
||
644 | if (mvsw61xx_wait_mask_s(dev, MV_GLOBALREG(CONTROL), |
||
645 | MV_CONTROL_RESET, 0) < 0) |
||
646 | return -ETIMEDOUT; |
||
647 | |||
648 | for (i = 0; i < dev->ports; i++) { |
||
649 | state->ports[i].fdb = 0; |
||
650 | state->ports[i].qmode = 0; |
||
651 | state->ports[i].mask = 0; |
||
652 | state->ports[i].pvid = 0; |
||
653 | |||
654 | /* Force flow control off */ |
||
655 | reg = sr16(dev, MV_PORTREG(PHYCTL, i)) & ~MV_PHYCTL_FC_MASK; |
||
656 | reg |= MV_PHYCTL_FC_DISABLE; |
||
657 | sw16(dev, MV_PORTREG(PHYCTL, i), reg); |
||
658 | |||
659 | /* Set port association vector */ |
||
660 | sw16(dev, MV_PORTREG(ASSOC, i), (1 << i)); |
||
661 | |||
662 | /* power up phys */ |
||
663 | if (full && i < 5) { |
||
664 | mvsw61xx_mdio_write(dev, i, MII_MV_SPEC_CTRL, |
||
665 | MV_SPEC_MDI_CROSS_AUTO | |
||
666 | MV_SPEC_ENERGY_DETECT | |
||
667 | MV_SPEC_DOWNSHIFT_COUNTER); |
||
668 | mvsw61xx_mdio_write(dev, i, MII_BMCR, BMCR_RESET | |
||
669 | BMCR_ANENABLE | BMCR_FULLDPLX | |
||
670 | BMCR_SPEED1000); |
||
671 | } |
||
672 | |||
673 | /* enable SerDes if necessary */ |
||
674 | if (full && i >= 5 && state->model == MV_IDENT_VALUE_6176) { |
||
675 | u16 sts = sr16(dev, MV_PORTREG(STATUS, i)); |
||
676 | u16 mode = sts & MV_PORT_STATUS_CMODE_MASK; |
||
677 | |||
678 | if (mode == MV_PORT_STATUS_CMODE_100BASE_X || |
||
679 | mode == MV_PORT_STATUS_CMODE_1000BASE_X || |
||
680 | mode == MV_PORT_STATUS_CMODE_SGMII) { |
||
681 | mvsw61xx_enable_serdes(dev); |
||
682 | } |
||
683 | } |
||
684 | } |
||
685 | |||
686 | for (i = 0; i < dev->vlans; i++) { |
||
687 | state->vlans[i].port_based = false; |
||
688 | state->vlans[i].mask = 0; |
||
689 | state->vlans[i].vid = 0; |
||
690 | state->vlans[i].port_mode = 0; |
||
691 | state->vlans[i].port_sstate = 0; |
||
692 | } |
||
693 | |||
694 | state->vlan_enabled = 0; |
||
695 | |||
696 | mvsw61xx_update_state(dev); |
||
697 | |||
698 | /* Re-enable ports */ |
||
699 | for (i = 0; i < dev->ports; i++) { |
||
700 | reg = sr16(dev, MV_PORTREG(CONTROL, i)) | |
||
701 | MV_PORTCTRL_FORWARDING; |
||
702 | sw16(dev, MV_PORTREG(CONTROL, i), reg); |
||
703 | } |
||
704 | |||
705 | return 0; |
||
706 | } |
||
707 | |||
708 | static int mvsw61xx_reset(struct switch_dev *dev) |
||
709 | { |
||
710 | return _mvsw61xx_reset(dev, false); |
||
711 | } |
||
712 | |||
713 | enum { |
||
3 | office | 714 | MVSW61XX_ENABLE_VLAN, |
715 | }; |
||
716 | |||
717 | enum { |
||
1 | office | 718 | MVSW61XX_VLAN_PORT_BASED, |
719 | MVSW61XX_VLAN_ID, |
||
720 | }; |
||
721 | |||
722 | enum { |
||
723 | MVSW61XX_PORT_MASK, |
||
724 | MVSW61XX_PORT_QMODE, |
||
725 | }; |
||
726 | |||
727 | static const struct switch_attr mvsw61xx_global[] = { |
||
3 | office | 728 | [MVSW61XX_ENABLE_VLAN] = { |
729 | .id = MVSW61XX_ENABLE_VLAN, |
||
1 | office | 730 | .type = SWITCH_TYPE_INT, |
731 | .name = "enable_vlan", |
||
732 | .description = "Enable 802.1q VLAN support", |
||
733 | .get = mvsw61xx_get_enable_vlan, |
||
734 | .set = mvsw61xx_set_enable_vlan, |
||
735 | }, |
||
736 | }; |
||
737 | |||
738 | static const struct switch_attr mvsw61xx_vlan[] = { |
||
739 | [MVSW61XX_VLAN_PORT_BASED] = { |
||
740 | .id = MVSW61XX_VLAN_PORT_BASED, |
||
741 | .type = SWITCH_TYPE_INT, |
||
742 | .name = "port_based", |
||
743 | .description = "Use port-based (non-802.1q) VLAN only", |
||
744 | .get = mvsw61xx_get_vlan_port_based, |
||
745 | .set = mvsw61xx_set_vlan_port_based, |
||
746 | }, |
||
747 | [MVSW61XX_VLAN_ID] = { |
||
748 | .id = MVSW61XX_VLAN_ID, |
||
749 | .type = SWITCH_TYPE_INT, |
||
750 | .name = "vid", |
||
751 | .description = "Get/set VLAN ID", |
||
752 | .get = mvsw61xx_get_vid, |
||
753 | .set = mvsw61xx_set_vid, |
||
754 | }, |
||
755 | }; |
||
756 | |||
757 | static const struct switch_attr mvsw61xx_port[] = { |
||
758 | [MVSW61XX_PORT_MASK] = { |
||
759 | .id = MVSW61XX_PORT_MASK, |
||
760 | .type = SWITCH_TYPE_STRING, |
||
761 | .description = "Port-based VLAN mask", |
||
762 | .name = "mask", |
||
763 | .get = mvsw61xx_get_port_mask, |
||
764 | .set = NULL, |
||
765 | }, |
||
766 | [MVSW61XX_PORT_QMODE] = { |
||
767 | .id = MVSW61XX_PORT_QMODE, |
||
768 | .type = SWITCH_TYPE_INT, |
||
769 | .description = "802.1q mode: 0=off/1=fallback/2=check/3=secure", |
||
770 | .name = "qmode", |
||
771 | .get = mvsw61xx_get_port_qmode, |
||
772 | .set = mvsw61xx_set_port_qmode, |
||
773 | }, |
||
774 | }; |
||
775 | |||
776 | static const struct switch_dev_ops mvsw61xx_ops = { |
||
777 | .attr_global = { |
||
778 | .attr = mvsw61xx_global, |
||
779 | .n_attr = ARRAY_SIZE(mvsw61xx_global), |
||
780 | }, |
||
781 | .attr_vlan = { |
||
782 | .attr = mvsw61xx_vlan, |
||
783 | .n_attr = ARRAY_SIZE(mvsw61xx_vlan), |
||
784 | }, |
||
785 | .attr_port = { |
||
786 | .attr = mvsw61xx_port, |
||
787 | .n_attr = ARRAY_SIZE(mvsw61xx_port), |
||
788 | }, |
||
789 | .get_port_link = mvsw61xx_get_port_link, |
||
790 | .get_port_pvid = mvsw61xx_get_port_pvid, |
||
791 | .set_port_pvid = mvsw61xx_set_port_pvid, |
||
792 | .get_vlan_ports = mvsw61xx_get_vlan_ports, |
||
793 | .set_vlan_ports = mvsw61xx_set_vlan_ports, |
||
794 | .apply_config = mvsw61xx_apply, |
||
795 | .reset_switch = mvsw61xx_reset, |
||
796 | }; |
||
797 | |||
798 | /* end swconfig stuff */ |
||
799 | |||
800 | static int mvsw61xx_probe(struct platform_device *pdev) |
||
801 | { |
||
802 | struct mvsw61xx_state *state; |
||
803 | struct device_node *np = pdev->dev.of_node; |
||
804 | struct device_node *mdio; |
||
805 | char *model_str; |
||
806 | u32 val; |
||
807 | int err; |
||
808 | |||
809 | state = kzalloc(sizeof(*state), GFP_KERNEL); |
||
810 | if (!state) |
||
811 | return -ENOMEM; |
||
812 | |||
813 | mdio = of_parse_phandle(np, "mii-bus", 0); |
||
814 | if (!mdio) { |
||
815 | dev_err(&pdev->dev, "Couldn't get MII bus handle\n"); |
||
816 | err = -ENODEV; |
||
817 | goto out_err; |
||
818 | } |
||
819 | |||
820 | state->bus = of_mdio_find_bus(mdio); |
||
821 | if (!state->bus) { |
||
822 | dev_err(&pdev->dev, "Couldn't find MII bus from handle\n"); |
||
823 | err = -ENODEV; |
||
824 | goto out_err; |
||
825 | } |
||
826 | |||
827 | state->is_indirect = of_property_read_bool(np, "is-indirect"); |
||
828 | |||
829 | if (state->is_indirect) { |
||
830 | if (of_property_read_u32(np, "reg", &val)) { |
||
831 | dev_err(&pdev->dev, "Switch address not specified\n"); |
||
832 | err = -ENODEV; |
||
833 | goto out_err; |
||
834 | } |
||
835 | |||
836 | state->base_addr = val; |
||
837 | } else { |
||
838 | state->base_addr = MV_BASE; |
||
839 | } |
||
840 | |||
841 | state->model = r16(state->bus, state->is_indirect, state->base_addr, |
||
842 | MV_PORTREG(IDENT, 0)) & MV_IDENT_MASK; |
||
843 | |||
844 | switch(state->model) { |
||
845 | case MV_IDENT_VALUE_6171: |
||
846 | model_str = MV_IDENT_STR_6171; |
||
847 | break; |
||
848 | case MV_IDENT_VALUE_6172: |
||
849 | model_str = MV_IDENT_STR_6172; |
||
850 | break; |
||
851 | case MV_IDENT_VALUE_6176: |
||
852 | model_str = MV_IDENT_STR_6176; |
||
853 | break; |
||
854 | case MV_IDENT_VALUE_6352: |
||
855 | model_str = MV_IDENT_STR_6352; |
||
856 | break; |
||
857 | default: |
||
858 | dev_err(&pdev->dev, "No compatible switch found at 0x%02x\n", |
||
859 | state->base_addr); |
||
860 | err = -ENODEV; |
||
861 | goto out_err; |
||
862 | } |
||
863 | |||
864 | platform_set_drvdata(pdev, state); |
||
865 | dev_info(&pdev->dev, "Found %s at %s:%02x\n", model_str, |
||
866 | state->bus->id, state->base_addr); |
||
867 | |||
868 | dev_info(&pdev->dev, "Using %sdirect addressing\n", |
||
869 | (state->is_indirect ? "in" : "")); |
||
870 | |||
871 | if (of_property_read_u32(np, "cpu-port-0", &val)) { |
||
872 | dev_err(&pdev->dev, "CPU port not set\n"); |
||
873 | err = -ENODEV; |
||
874 | goto out_err; |
||
875 | } |
||
876 | |||
877 | state->cpu_port0 = val; |
||
878 | |||
879 | if (!of_property_read_u32(np, "cpu-port-1", &val)) |
||
880 | state->cpu_port1 = val; |
||
881 | else |
||
882 | state->cpu_port1 = -1; |
||
883 | |||
884 | state->dev.vlans = MV_VLANS; |
||
885 | state->dev.cpu_port = state->cpu_port0; |
||
886 | state->dev.ports = MV_PORTS; |
||
887 | state->dev.name = model_str; |
||
888 | state->dev.ops = &mvsw61xx_ops; |
||
889 | state->dev.alias = dev_name(&pdev->dev); |
||
890 | |||
891 | _mvsw61xx_reset(&state->dev, true); |
||
892 | |||
893 | err = register_switch(&state->dev, NULL); |
||
894 | if (err < 0) |
||
895 | goto out_err; |
||
896 | |||
897 | state->registered = true; |
||
898 | |||
899 | return 0; |
||
900 | out_err: |
||
901 | kfree(state); |
||
902 | return err; |
||
903 | } |
||
904 | |||
905 | static int |
||
906 | mvsw61xx_remove(struct platform_device *pdev) |
||
907 | { |
||
908 | struct mvsw61xx_state *state = platform_get_drvdata(pdev); |
||
909 | |||
910 | if (state->registered) |
||
911 | unregister_switch(&state->dev); |
||
912 | |||
913 | kfree(state); |
||
914 | |||
915 | return 0; |
||
916 | } |
||
917 | |||
918 | static const struct of_device_id mvsw61xx_match[] = { |
||
919 | { .compatible = "marvell,88e6171" }, |
||
920 | { .compatible = "marvell,88e6172" }, |
||
921 | { .compatible = "marvell,88e6176" }, |
||
922 | { .compatible = "marvell,88e6352" }, |
||
923 | { } |
||
924 | }; |
||
925 | MODULE_DEVICE_TABLE(of, mvsw61xx_match); |
||
926 | |||
927 | static struct platform_driver mvsw61xx_driver = { |
||
928 | .probe = mvsw61xx_probe, |
||
929 | .remove = mvsw61xx_remove, |
||
930 | .driver = { |
||
931 | .name = "mvsw61xx", |
||
932 | .of_match_table = of_match_ptr(mvsw61xx_match), |
||
933 | .owner = THIS_MODULE, |
||
934 | }, |
||
935 | }; |
||
936 | |||
937 | static int __init mvsw61xx_module_init(void) |
||
938 | { |
||
939 | return platform_driver_register(&mvsw61xx_driver); |
||
940 | } |
||
941 | late_initcall(mvsw61xx_module_init); |
||
942 | |||
943 | static void __exit mvsw61xx_module_exit(void) |
||
944 | { |
||
945 | platform_driver_unregister(&mvsw61xx_driver); |
||
946 | } |
||
947 | module_exit(mvsw61xx_module_exit); |