diff options
-rw-r--r-- | drivers/net/dsa/mv88e6123_61_65.c | 31 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6171.c | 70 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6352.c | 39 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx.c | 479 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx.h | 46 | ||||
-rw-r--r-- | include/net/dsa.h | 6 | ||||
-rw-r--r-- | net/dsa/slave.c | 102 |
7 files changed, 679 insertions, 94 deletions
diff --git a/drivers/net/dsa/mv88e6123_61_65.c b/drivers/net/dsa/mv88e6123_61_65.c index e9c736e1cef3..2d7e1ffe9fdc 100644 --- a/drivers/net/dsa/mv88e6123_61_65.c +++ b/drivers/net/dsa/mv88e6123_61_65.c | |||
@@ -222,28 +222,6 @@ static int mv88e6123_61_65_setup_port(struct dsa_switch *ds, int p) | |||
222 | val |= 0x000c; | 222 | val |= 0x000c; |
223 | REG_WRITE(addr, 0x04, val); | 223 | REG_WRITE(addr, 0x04, val); |
224 | 224 | ||
225 | /* Port Control 1: disable trunking. Also, if this is the | ||
226 | * CPU port, enable learn messages to be sent to this port. | ||
227 | */ | ||
228 | REG_WRITE(addr, 0x05, dsa_is_cpu_port(ds, p) ? 0x8000 : 0x0000); | ||
229 | |||
230 | /* Port based VLAN map: give each port its own address | ||
231 | * database, allow the CPU port to talk to each of the 'real' | ||
232 | * ports, and allow each of the 'real' ports to only talk to | ||
233 | * the upstream port. | ||
234 | */ | ||
235 | val = (p & 0xf) << 12; | ||
236 | if (dsa_is_cpu_port(ds, p)) | ||
237 | val |= ds->phys_port_mask; | ||
238 | else | ||
239 | val |= 1 << dsa_upstream_port(ds); | ||
240 | REG_WRITE(addr, 0x06, val); | ||
241 | |||
242 | /* Default VLAN ID and priority: don't set a default VLAN | ||
243 | * ID, and set the default packet priority to zero. | ||
244 | */ | ||
245 | REG_WRITE(addr, 0x07, 0x0000); | ||
246 | |||
247 | /* Port Control 2: don't force a good FCS, set the maximum | 225 | /* Port Control 2: don't force a good FCS, set the maximum |
248 | * frame size to 10240 bytes, don't let the switch add or | 226 | * frame size to 10240 bytes, don't let the switch add or |
249 | * strip 802.1q tags, don't discard tagged or untagged frames | 227 | * strip 802.1q tags, don't discard tagged or untagged frames |
@@ -288,18 +266,17 @@ static int mv88e6123_61_65_setup_port(struct dsa_switch *ds, int p) | |||
288 | */ | 266 | */ |
289 | REG_WRITE(addr, 0x19, 0x7654); | 267 | REG_WRITE(addr, 0x19, 0x7654); |
290 | 268 | ||
291 | return 0; | 269 | return mv88e6xxx_setup_port_common(ds, p); |
292 | } | 270 | } |
293 | 271 | ||
294 | static int mv88e6123_61_65_setup(struct dsa_switch *ds) | 272 | static int mv88e6123_61_65_setup(struct dsa_switch *ds) |
295 | { | 273 | { |
296 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | ||
297 | int i; | 274 | int i; |
298 | int ret; | 275 | int ret; |
299 | 276 | ||
300 | mutex_init(&ps->smi_mutex); | 277 | ret = mv88e6xxx_setup_common(ds); |
301 | mutex_init(&ps->stats_mutex); | 278 | if (ret < 0) |
302 | mutex_init(&ps->phy_mutex); | 279 | return ret; |
303 | 280 | ||
304 | ret = mv88e6123_61_65_switch_reset(ds); | 281 | ret = mv88e6123_61_65_switch_reset(ds); |
305 | if (ret < 0) | 282 | if (ret < 0) |
diff --git a/drivers/net/dsa/mv88e6171.c b/drivers/net/dsa/mv88e6171.c index 9808c860a797..18cfead83dc9 100644 --- a/drivers/net/dsa/mv88e6171.c +++ b/drivers/net/dsa/mv88e6171.c | |||
@@ -17,6 +17,10 @@ | |||
17 | #include <net/dsa.h> | 17 | #include <net/dsa.h> |
18 | #include "mv88e6xxx.h" | 18 | #include "mv88e6xxx.h" |
19 | 19 | ||
20 | /* Switch product IDs */ | ||
21 | #define ID_6171 0x1710 | ||
22 | #define ID_6172 0x1720 | ||
23 | |||
20 | static char *mv88e6171_probe(struct device *host_dev, int sw_addr) | 24 | static char *mv88e6171_probe(struct device *host_dev, int sw_addr) |
21 | { | 25 | { |
22 | struct mii_bus *bus = dsa_host_dev_to_mii_bus(host_dev); | 26 | struct mii_bus *bus = dsa_host_dev_to_mii_bus(host_dev); |
@@ -27,9 +31,9 @@ static char *mv88e6171_probe(struct device *host_dev, int sw_addr) | |||
27 | 31 | ||
28 | ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), 0x03); | 32 | ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), 0x03); |
29 | if (ret >= 0) { | 33 | if (ret >= 0) { |
30 | if ((ret & 0xfff0) == 0x1710) | 34 | if ((ret & 0xfff0) == ID_6171) |
31 | return "Marvell 88E6171"; | 35 | return "Marvell 88E6171"; |
32 | if ((ret & 0xfff0) == 0x1720) | 36 | if ((ret & 0xfff0) == ID_6172) |
33 | return "Marvell 88E6172"; | 37 | return "Marvell 88E6172"; |
34 | } | 38 | } |
35 | 39 | ||
@@ -221,28 +225,6 @@ static int mv88e6171_setup_port(struct dsa_switch *ds, int p) | |||
221 | val |= 0x000c; | 225 | val |= 0x000c; |
222 | REG_WRITE(addr, 0x04, val); | 226 | REG_WRITE(addr, 0x04, val); |
223 | 227 | ||
224 | /* Port Control 1: disable trunking. Also, if this is the | ||
225 | * CPU port, enable learn messages to be sent to this port. | ||
226 | */ | ||
227 | REG_WRITE(addr, 0x05, dsa_is_cpu_port(ds, p) ? 0x8000 : 0x0000); | ||
228 | |||
229 | /* Port based VLAN map: give each port its own address | ||
230 | * database, allow the CPU port to talk to each of the 'real' | ||
231 | * ports, and allow each of the 'real' ports to only talk to | ||
232 | * the upstream port. | ||
233 | */ | ||
234 | val = (p & 0xf) << 12; | ||
235 | if (dsa_is_cpu_port(ds, p)) | ||
236 | val |= ds->phys_port_mask; | ||
237 | else | ||
238 | val |= 1 << dsa_upstream_port(ds); | ||
239 | REG_WRITE(addr, 0x06, val); | ||
240 | |||
241 | /* Default VLAN ID and priority: don't set a default VLAN | ||
242 | * ID, and set the default packet priority to zero. | ||
243 | */ | ||
244 | REG_WRITE(addr, 0x07, 0x0000); | ||
245 | |||
246 | /* Port Control 2: don't force a good FCS, set the maximum | 228 | /* Port Control 2: don't force a good FCS, set the maximum |
247 | * frame size to 10240 bytes, don't let the switch add or | 229 | * frame size to 10240 bytes, don't let the switch add or |
248 | * strip 802.1q tags, don't discard tagged or untagged frames | 230 | * strip 802.1q tags, don't discard tagged or untagged frames |
@@ -287,17 +269,17 @@ static int mv88e6171_setup_port(struct dsa_switch *ds, int p) | |||
287 | */ | 269 | */ |
288 | REG_WRITE(addr, 0x19, 0x7654); | 270 | REG_WRITE(addr, 0x19, 0x7654); |
289 | 271 | ||
290 | return 0; | 272 | return mv88e6xxx_setup_port_common(ds, p); |
291 | } | 273 | } |
292 | 274 | ||
293 | static int mv88e6171_setup(struct dsa_switch *ds) | 275 | static int mv88e6171_setup(struct dsa_switch *ds) |
294 | { | 276 | { |
295 | struct mv88e6xxx_priv_state *ps = (void *)(ds + 1); | ||
296 | int i; | 277 | int i; |
297 | int ret; | 278 | int ret; |
298 | 279 | ||
299 | mutex_init(&ps->smi_mutex); | 280 | ret = mv88e6xxx_setup_common(ds); |
300 | mutex_init(&ps->stats_mutex); | 281 | if (ret < 0) |
282 | return ret; | ||
301 | 283 | ||
302 | ret = mv88e6171_switch_reset(ds); | 284 | ret = mv88e6171_switch_reset(ds); |
303 | if (ret < 0) | 285 | if (ret < 0) |
@@ -318,8 +300,6 @@ static int mv88e6171_setup(struct dsa_switch *ds) | |||
318 | return ret; | 300 | return ret; |
319 | } | 301 | } |
320 | 302 | ||
321 | mutex_init(&ps->phy_mutex); | ||
322 | |||
323 | return 0; | 303 | return 0; |
324 | } | 304 | } |
325 | 305 | ||
@@ -410,6 +390,28 @@ static int mv88e6171_get_sset_count(struct dsa_switch *ds) | |||
410 | return ARRAY_SIZE(mv88e6171_hw_stats); | 390 | return ARRAY_SIZE(mv88e6171_hw_stats); |
411 | } | 391 | } |
412 | 392 | ||
393 | static int mv88e6171_get_eee(struct dsa_switch *ds, int port, | ||
394 | struct ethtool_eee *e) | ||
395 | { | ||
396 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | ||
397 | |||
398 | if (ps->id == ID_6172) | ||
399 | return mv88e6xxx_get_eee(ds, port, e); | ||
400 | |||
401 | return -EOPNOTSUPP; | ||
402 | } | ||
403 | |||
404 | static int mv88e6171_set_eee(struct dsa_switch *ds, int port, | ||
405 | struct phy_device *phydev, struct ethtool_eee *e) | ||
406 | { | ||
407 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | ||
408 | |||
409 | if (ps->id == ID_6172) | ||
410 | return mv88e6xxx_set_eee(ds, port, phydev, e); | ||
411 | |||
412 | return -EOPNOTSUPP; | ||
413 | } | ||
414 | |||
413 | struct dsa_switch_driver mv88e6171_switch_driver = { | 415 | struct dsa_switch_driver mv88e6171_switch_driver = { |
414 | .tag_protocol = DSA_TAG_PROTO_EDSA, | 416 | .tag_protocol = DSA_TAG_PROTO_EDSA, |
415 | .priv_size = sizeof(struct mv88e6xxx_priv_state), | 417 | .priv_size = sizeof(struct mv88e6xxx_priv_state), |
@@ -422,11 +424,19 @@ struct dsa_switch_driver mv88e6171_switch_driver = { | |||
422 | .get_strings = mv88e6171_get_strings, | 424 | .get_strings = mv88e6171_get_strings, |
423 | .get_ethtool_stats = mv88e6171_get_ethtool_stats, | 425 | .get_ethtool_stats = mv88e6171_get_ethtool_stats, |
424 | .get_sset_count = mv88e6171_get_sset_count, | 426 | .get_sset_count = mv88e6171_get_sset_count, |
427 | .set_eee = mv88e6171_set_eee, | ||
428 | .get_eee = mv88e6171_get_eee, | ||
425 | #ifdef CONFIG_NET_DSA_HWMON | 429 | #ifdef CONFIG_NET_DSA_HWMON |
426 | .get_temp = mv88e6xxx_get_temp, | 430 | .get_temp = mv88e6xxx_get_temp, |
427 | #endif | 431 | #endif |
428 | .get_regs_len = mv88e6xxx_get_regs_len, | 432 | .get_regs_len = mv88e6xxx_get_regs_len, |
429 | .get_regs = mv88e6xxx_get_regs, | 433 | .get_regs = mv88e6xxx_get_regs, |
434 | .port_join_bridge = mv88e6xxx_join_bridge, | ||
435 | .port_leave_bridge = mv88e6xxx_leave_bridge, | ||
436 | .port_stp_update = mv88e6xxx_port_stp_update, | ||
437 | .fdb_add = mv88e6xxx_port_fdb_add, | ||
438 | .fdb_del = mv88e6xxx_port_fdb_del, | ||
439 | .fdb_getnext = mv88e6xxx_port_fdb_getnext, | ||
430 | }; | 440 | }; |
431 | 441 | ||
432 | MODULE_ALIAS("platform:mv88e6171"); | 442 | MODULE_ALIAS("platform:mv88e6171"); |
diff --git a/drivers/net/dsa/mv88e6352.c b/drivers/net/dsa/mv88e6352.c index 7bc5998384c6..41fe3a6a72d1 100644 --- a/drivers/net/dsa/mv88e6352.c +++ b/drivers/net/dsa/mv88e6352.c | |||
@@ -215,28 +215,6 @@ static int mv88e6352_setup_port(struct dsa_switch *ds, int p) | |||
215 | val |= 0x000c; | 215 | val |= 0x000c; |
216 | REG_WRITE(addr, 0x04, val); | 216 | REG_WRITE(addr, 0x04, val); |
217 | 217 | ||
218 | /* Port Control 1: disable trunking. Also, if this is the | ||
219 | * CPU port, enable learn messages to be sent to this port. | ||
220 | */ | ||
221 | REG_WRITE(addr, 0x05, dsa_is_cpu_port(ds, p) ? 0x8000 : 0x0000); | ||
222 | |||
223 | /* Port based VLAN map: give each port its own address | ||
224 | * database, allow the CPU port to talk to each of the 'real' | ||
225 | * ports, and allow each of the 'real' ports to only talk to | ||
226 | * the upstream port. | ||
227 | */ | ||
228 | val = (p & 0xf) << 12; | ||
229 | if (dsa_is_cpu_port(ds, p)) | ||
230 | val |= ds->phys_port_mask; | ||
231 | else | ||
232 | val |= 1 << dsa_upstream_port(ds); | ||
233 | REG_WRITE(addr, 0x06, val); | ||
234 | |||
235 | /* Default VLAN ID and priority: don't set a default VLAN | ||
236 | * ID, and set the default packet priority to zero. | ||
237 | */ | ||
238 | REG_WRITE(addr, 0x07, 0x0000); | ||
239 | |||
240 | /* Port Control 2: don't force a good FCS, set the maximum | 218 | /* Port Control 2: don't force a good FCS, set the maximum |
241 | * frame size to 10240 bytes, don't let the switch add or | 219 | * frame size to 10240 bytes, don't let the switch add or |
242 | * strip 802.1q tags, don't discard tagged or untagged frames | 220 | * strip 802.1q tags, don't discard tagged or untagged frames |
@@ -281,7 +259,7 @@ static int mv88e6352_setup_port(struct dsa_switch *ds, int p) | |||
281 | */ | 259 | */ |
282 | REG_WRITE(addr, 0x19, 0x7654); | 260 | REG_WRITE(addr, 0x19, 0x7654); |
283 | 261 | ||
284 | return 0; | 262 | return mv88e6xxx_setup_port_common(ds, p); |
285 | } | 263 | } |
286 | 264 | ||
287 | #ifdef CONFIG_NET_DSA_HWMON | 265 | #ifdef CONFIG_NET_DSA_HWMON |
@@ -385,12 +363,11 @@ static int mv88e6352_setup(struct dsa_switch *ds) | |||
385 | int ret; | 363 | int ret; |
386 | int i; | 364 | int i; |
387 | 365 | ||
388 | mutex_init(&ps->smi_mutex); | 366 | ret = mv88e6xxx_setup_common(ds); |
389 | mutex_init(&ps->stats_mutex); | 367 | if (ret < 0) |
390 | mutex_init(&ps->phy_mutex); | 368 | return ret; |
391 | mutex_init(&ps->eeprom_mutex); | ||
392 | 369 | ||
393 | ps->id = REG_READ(REG_PORT(0), 0x03) & 0xfff0; | 370 | mutex_init(&ps->eeprom_mutex); |
394 | 371 | ||
395 | ret = mv88e6352_switch_reset(ds); | 372 | ret = mv88e6352_switch_reset(ds); |
396 | if (ret < 0) | 373 | if (ret < 0) |
@@ -729,6 +706,12 @@ struct dsa_switch_driver mv88e6352_switch_driver = { | |||
729 | .set_eeprom = mv88e6352_set_eeprom, | 706 | .set_eeprom = mv88e6352_set_eeprom, |
730 | .get_regs_len = mv88e6xxx_get_regs_len, | 707 | .get_regs_len = mv88e6xxx_get_regs_len, |
731 | .get_regs = mv88e6xxx_get_regs, | 708 | .get_regs = mv88e6xxx_get_regs, |
709 | .port_join_bridge = mv88e6xxx_join_bridge, | ||
710 | .port_leave_bridge = mv88e6xxx_leave_bridge, | ||
711 | .port_stp_update = mv88e6xxx_port_stp_update, | ||
712 | .fdb_add = mv88e6xxx_port_fdb_add, | ||
713 | .fdb_del = mv88e6xxx_port_fdb_del, | ||
714 | .fdb_getnext = mv88e6xxx_port_fdb_getnext, | ||
732 | }; | 715 | }; |
733 | 716 | ||
734 | MODULE_ALIAS("platform:mv88e6352"); | 717 | MODULE_ALIAS("platform:mv88e6352"); |
diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c index c18ffc98aacc..13572cc24c6d 100644 --- a/drivers/net/dsa/mv88e6xxx.c +++ b/drivers/net/dsa/mv88e6xxx.c | |||
@@ -9,6 +9,8 @@ | |||
9 | */ | 9 | */ |
10 | 10 | ||
11 | #include <linux/delay.h> | 11 | #include <linux/delay.h> |
12 | #include <linux/etherdevice.h> | ||
13 | #include <linux/if_bridge.h> | ||
12 | #include <linux/jiffies.h> | 14 | #include <linux/jiffies.h> |
13 | #include <linux/list.h> | 15 | #include <linux/list.h> |
14 | #include <linux/module.h> | 16 | #include <linux/module.h> |
@@ -72,19 +74,16 @@ int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg) | |||
72 | return ret & 0xffff; | 74 | return ret & 0xffff; |
73 | } | 75 | } |
74 | 76 | ||
75 | int mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg) | 77 | /* Must be called with SMI mutex held */ |
78 | static int _mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg) | ||
76 | { | 79 | { |
77 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | ||
78 | struct mii_bus *bus = dsa_host_dev_to_mii_bus(ds->master_dev); | 80 | struct mii_bus *bus = dsa_host_dev_to_mii_bus(ds->master_dev); |
79 | int ret; | 81 | int ret; |
80 | 82 | ||
81 | if (bus == NULL) | 83 | if (bus == NULL) |
82 | return -EINVAL; | 84 | return -EINVAL; |
83 | 85 | ||
84 | mutex_lock(&ps->smi_mutex); | ||
85 | ret = __mv88e6xxx_reg_read(bus, ds->pd->sw_addr, addr, reg); | 86 | ret = __mv88e6xxx_reg_read(bus, ds->pd->sw_addr, addr, reg); |
86 | mutex_unlock(&ps->smi_mutex); | ||
87 | |||
88 | if (ret < 0) | 87 | if (ret < 0) |
89 | return ret; | 88 | return ret; |
90 | 89 | ||
@@ -94,6 +93,18 @@ int mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg) | |||
94 | return ret; | 93 | return ret; |
95 | } | 94 | } |
96 | 95 | ||
96 | int mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg) | ||
97 | { | ||
98 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | ||
99 | int ret; | ||
100 | |||
101 | mutex_lock(&ps->smi_mutex); | ||
102 | ret = _mv88e6xxx_reg_read(ds, addr, reg); | ||
103 | mutex_unlock(&ps->smi_mutex); | ||
104 | |||
105 | return ret; | ||
106 | } | ||
107 | |||
97 | int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr, | 108 | int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr, |
98 | int reg, u16 val) | 109 | int reg, u16 val) |
99 | { | 110 | { |
@@ -125,11 +136,11 @@ int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr, | |||
125 | return 0; | 136 | return 0; |
126 | } | 137 | } |
127 | 138 | ||
128 | int mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, u16 val) | 139 | /* Must be called with SMI mutex held */ |
140 | static int _mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, | ||
141 | u16 val) | ||
129 | { | 142 | { |
130 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | ||
131 | struct mii_bus *bus = dsa_host_dev_to_mii_bus(ds->master_dev); | 143 | struct mii_bus *bus = dsa_host_dev_to_mii_bus(ds->master_dev); |
132 | int ret; | ||
133 | 144 | ||
134 | if (bus == NULL) | 145 | if (bus == NULL) |
135 | return -EINVAL; | 146 | return -EINVAL; |
@@ -137,8 +148,16 @@ int mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, u16 val) | |||
137 | dev_dbg(ds->master_dev, "-> addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n", | 148 | dev_dbg(ds->master_dev, "-> addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n", |
138 | addr, reg, val); | 149 | addr, reg, val); |
139 | 150 | ||
151 | return __mv88e6xxx_reg_write(bus, ds->pd->sw_addr, addr, reg, val); | ||
152 | } | ||
153 | |||
154 | int mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, u16 val) | ||
155 | { | ||
156 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | ||
157 | int ret; | ||
158 | |||
140 | mutex_lock(&ps->smi_mutex); | 159 | mutex_lock(&ps->smi_mutex); |
141 | ret = __mv88e6xxx_reg_write(bus, ds->pd->sw_addr, addr, reg, val); | 160 | ret = _mv88e6xxx_reg_write(ds, addr, reg, val); |
142 | mutex_unlock(&ps->smi_mutex); | 161 | mutex_unlock(&ps->smi_mutex); |
143 | 162 | ||
144 | return ret; | 163 | return ret; |
@@ -627,6 +646,31 @@ int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds) | |||
627 | return mv88e6xxx_wait(ds, REG_GLOBAL2, 0x14, 0x8000); | 646 | return mv88e6xxx_wait(ds, REG_GLOBAL2, 0x14, 0x8000); |
628 | } | 647 | } |
629 | 648 | ||
649 | /* Must be called with SMI lock held */ | ||
650 | static int _mv88e6xxx_wait(struct dsa_switch *ds, int reg, int offset, u16 mask) | ||
651 | { | ||
652 | unsigned long timeout = jiffies + HZ / 10; | ||
653 | |||
654 | while (time_before(jiffies, timeout)) { | ||
655 | int ret; | ||
656 | |||
657 | ret = _mv88e6xxx_reg_read(ds, reg, offset); | ||
658 | if (ret < 0) | ||
659 | return ret; | ||
660 | if (!(ret & mask)) | ||
661 | return 0; | ||
662 | |||
663 | usleep_range(1000, 2000); | ||
664 | } | ||
665 | return -ETIMEDOUT; | ||
666 | } | ||
667 | |||
668 | /* Must be called with SMI lock held */ | ||
669 | static int _mv88e6xxx_atu_wait(struct dsa_switch *ds) | ||
670 | { | ||
671 | return _mv88e6xxx_wait(ds, REG_GLOBAL, 0x0b, ATU_BUSY); | ||
672 | } | ||
673 | |||
630 | int mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int addr, int regnum) | 674 | int mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int addr, int regnum) |
631 | { | 675 | { |
632 | int ret; | 676 | int ret; |
@@ -700,6 +744,423 @@ int mv88e6xxx_set_eee(struct dsa_switch *ds, int port, | |||
700 | return 0; | 744 | return 0; |
701 | } | 745 | } |
702 | 746 | ||
747 | static int _mv88e6xxx_atu_cmd(struct dsa_switch *ds, int fid, u16 cmd) | ||
748 | { | ||
749 | int ret; | ||
750 | |||
751 | ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x01, fid); | ||
752 | if (ret < 0) | ||
753 | return ret; | ||
754 | |||
755 | ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x0b, cmd); | ||
756 | if (ret < 0) | ||
757 | return ret; | ||
758 | |||
759 | return _mv88e6xxx_atu_wait(ds); | ||
760 | } | ||
761 | |||
762 | static int _mv88e6xxx_flush_fid(struct dsa_switch *ds, int fid) | ||
763 | { | ||
764 | int ret; | ||
765 | |||
766 | ret = _mv88e6xxx_atu_wait(ds); | ||
767 | if (ret < 0) | ||
768 | return ret; | ||
769 | |||
770 | return _mv88e6xxx_atu_cmd(ds, fid, ATU_CMD_FLUSH_NONSTATIC_FID); | ||
771 | } | ||
772 | |||
773 | static int mv88e6xxx_set_port_state(struct dsa_switch *ds, int port, u8 state) | ||
774 | { | ||
775 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | ||
776 | int reg, ret; | ||
777 | u8 oldstate; | ||
778 | |||
779 | mutex_lock(&ps->smi_mutex); | ||
780 | |||
781 | reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), 0x04); | ||
782 | if (reg < 0) | ||
783 | goto abort; | ||
784 | |||
785 | oldstate = reg & PSTATE_MASK; | ||
786 | if (oldstate != state) { | ||
787 | /* Flush forwarding database if we're moving a port | ||
788 | * from Learning or Forwarding state to Disabled or | ||
789 | * Blocking or Listening state. | ||
790 | */ | ||
791 | if (oldstate >= PSTATE_LEARNING && state <= PSTATE_BLOCKING) { | ||
792 | ret = _mv88e6xxx_flush_fid(ds, ps->fid[port]); | ||
793 | if (ret) | ||
794 | goto abort; | ||
795 | } | ||
796 | reg = (reg & ~PSTATE_MASK) | state; | ||
797 | ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), 0x04, reg); | ||
798 | } | ||
799 | |||
800 | abort: | ||
801 | mutex_unlock(&ps->smi_mutex); | ||
802 | return ret; | ||
803 | } | ||
804 | |||
805 | /* Must be called with smi lock held */ | ||
806 | static int _mv88e6xxx_update_port_config(struct dsa_switch *ds, int port) | ||
807 | { | ||
808 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | ||
809 | u8 fid = ps->fid[port]; | ||
810 | u16 reg = fid << 12; | ||
811 | |||
812 | if (dsa_is_cpu_port(ds, port)) | ||
813 | reg |= ds->phys_port_mask; | ||
814 | else | ||
815 | reg |= (ps->bridge_mask[fid] | | ||
816 | (1 << dsa_upstream_port(ds))) & ~(1 << port); | ||
817 | |||
818 | return _mv88e6xxx_reg_write(ds, REG_PORT(port), 0x06, reg); | ||
819 | } | ||
820 | |||
821 | /* Must be called with smi lock held */ | ||
822 | static int _mv88e6xxx_update_bridge_config(struct dsa_switch *ds, int fid) | ||
823 | { | ||
824 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | ||
825 | int port; | ||
826 | u32 mask; | ||
827 | int ret; | ||
828 | |||
829 | mask = ds->phys_port_mask; | ||
830 | while (mask) { | ||
831 | port = __ffs(mask); | ||
832 | mask &= ~(1 << port); | ||
833 | if (ps->fid[port] != fid) | ||
834 | continue; | ||
835 | |||
836 | ret = _mv88e6xxx_update_port_config(ds, port); | ||
837 | if (ret) | ||
838 | return ret; | ||
839 | } | ||
840 | |||
841 | return _mv88e6xxx_flush_fid(ds, fid); | ||
842 | } | ||
843 | |||
844 | /* Bridge handling functions */ | ||
845 | |||
846 | int mv88e6xxx_join_bridge(struct dsa_switch *ds, int port, u32 br_port_mask) | ||
847 | { | ||
848 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | ||
849 | int ret = 0; | ||
850 | u32 nmask; | ||
851 | int fid; | ||
852 | |||
853 | /* If the bridge group is not empty, join that group. | ||
854 | * Otherwise create a new group. | ||
855 | */ | ||
856 | fid = ps->fid[port]; | ||
857 | nmask = br_port_mask & ~(1 << port); | ||
858 | if (nmask) | ||
859 | fid = ps->fid[__ffs(nmask)]; | ||
860 | |||
861 | nmask = ps->bridge_mask[fid] | (1 << port); | ||
862 | if (nmask != br_port_mask) { | ||
863 | netdev_err(ds->ports[port], | ||
864 | "join: Bridge port mask mismatch fid=%d mask=0x%x expected 0x%x\n", | ||
865 | fid, br_port_mask, nmask); | ||
866 | return -EINVAL; | ||
867 | } | ||
868 | |||
869 | mutex_lock(&ps->smi_mutex); | ||
870 | |||
871 | ps->bridge_mask[fid] = br_port_mask; | ||
872 | |||
873 | if (fid != ps->fid[port]) { | ||
874 | ps->fid_mask |= 1 << ps->fid[port]; | ||
875 | ps->fid[port] = fid; | ||
876 | ret = _mv88e6xxx_update_bridge_config(ds, fid); | ||
877 | } | ||
878 | |||
879 | mutex_unlock(&ps->smi_mutex); | ||
880 | |||
881 | return ret; | ||
882 | } | ||
883 | |||
884 | int mv88e6xxx_leave_bridge(struct dsa_switch *ds, int port, u32 br_port_mask) | ||
885 | { | ||
886 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | ||
887 | u8 fid, newfid; | ||
888 | int ret; | ||
889 | |||
890 | fid = ps->fid[port]; | ||
891 | |||
892 | if (ps->bridge_mask[fid] != br_port_mask) { | ||
893 | netdev_err(ds->ports[port], | ||
894 | "leave: Bridge port mask mismatch fid=%d mask=0x%x expected 0x%x\n", | ||
895 | fid, br_port_mask, ps->bridge_mask[fid]); | ||
896 | return -EINVAL; | ||
897 | } | ||
898 | |||
899 | /* If the port was the last port of a bridge, we are done. | ||
900 | * Otherwise assign a new fid to the port, and fix up | ||
901 | * the bridge configuration. | ||
902 | */ | ||
903 | if (br_port_mask == (1 << port)) | ||
904 | return 0; | ||
905 | |||
906 | mutex_lock(&ps->smi_mutex); | ||
907 | |||
908 | newfid = __ffs(ps->fid_mask); | ||
909 | ps->fid[port] = newfid; | ||
910 | ps->fid_mask &= (1 << newfid); | ||
911 | ps->bridge_mask[fid] &= ~(1 << port); | ||
912 | ps->bridge_mask[newfid] = 1 << port; | ||
913 | |||
914 | ret = _mv88e6xxx_update_bridge_config(ds, fid); | ||
915 | if (!ret) | ||
916 | ret = _mv88e6xxx_update_bridge_config(ds, newfid); | ||
917 | |||
918 | mutex_unlock(&ps->smi_mutex); | ||
919 | |||
920 | return ret; | ||
921 | } | ||
922 | |||
923 | int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state) | ||
924 | { | ||
925 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | ||
926 | int stp_state; | ||
927 | |||
928 | switch (state) { | ||
929 | case BR_STATE_DISABLED: | ||
930 | stp_state = PSTATE_DISABLED; | ||
931 | break; | ||
932 | case BR_STATE_BLOCKING: | ||
933 | case BR_STATE_LISTENING: | ||
934 | stp_state = PSTATE_BLOCKING; | ||
935 | break; | ||
936 | case BR_STATE_LEARNING: | ||
937 | stp_state = PSTATE_LEARNING; | ||
938 | break; | ||
939 | case BR_STATE_FORWARDING: | ||
940 | default: | ||
941 | stp_state = PSTATE_FORWARDING; | ||
942 | break; | ||
943 | } | ||
944 | |||
945 | netdev_dbg(ds->ports[port], "port state %d [%d]\n", state, stp_state); | ||
946 | |||
947 | /* mv88e6xxx_port_stp_update may be called with softirqs disabled, | ||
948 | * so we can not update the port state directly but need to schedule it. | ||
949 | */ | ||
950 | ps->port_state[port] = stp_state; | ||
951 | set_bit(port, &ps->port_state_update_mask); | ||
952 | schedule_work(&ps->bridge_work); | ||
953 | |||
954 | return 0; | ||
955 | } | ||
956 | |||
957 | static int __mv88e6xxx_write_addr(struct dsa_switch *ds, | ||
958 | const unsigned char *addr) | ||
959 | { | ||
960 | int i, ret; | ||
961 | |||
962 | for (i = 0; i < 3; i++) { | ||
963 | ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x0d + i, | ||
964 | (addr[i * 2] << 8) | addr[i * 2 + 1]); | ||
965 | if (ret < 0) | ||
966 | return ret; | ||
967 | } | ||
968 | |||
969 | return 0; | ||
970 | } | ||
971 | |||
972 | static int __mv88e6xxx_read_addr(struct dsa_switch *ds, unsigned char *addr) | ||
973 | { | ||
974 | int i, ret; | ||
975 | |||
976 | for (i = 0; i < 3; i++) { | ||
977 | ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, 0x0d + i); | ||
978 | if (ret < 0) | ||
979 | return ret; | ||
980 | addr[i * 2] = ret >> 8; | ||
981 | addr[i * 2 + 1] = ret & 0xff; | ||
982 | } | ||
983 | |||
984 | return 0; | ||
985 | } | ||
986 | |||
987 | static int __mv88e6xxx_port_fdb_cmd(struct dsa_switch *ds, int port, | ||
988 | const unsigned char *addr, int state) | ||
989 | { | ||
990 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | ||
991 | u8 fid = ps->fid[port]; | ||
992 | int ret; | ||
993 | |||
994 | ret = _mv88e6xxx_atu_wait(ds); | ||
995 | if (ret < 0) | ||
996 | return ret; | ||
997 | |||
998 | ret = __mv88e6xxx_write_addr(ds, addr); | ||
999 | if (ret < 0) | ||
1000 | return ret; | ||
1001 | |||
1002 | ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x0c, | ||
1003 | (0x10 << port) | state); | ||
1004 | if (ret) | ||
1005 | return ret; | ||
1006 | |||
1007 | ret = _mv88e6xxx_atu_cmd(ds, fid, ATU_CMD_LOAD_FID); | ||
1008 | |||
1009 | return ret; | ||
1010 | } | ||
1011 | |||
1012 | int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, | ||
1013 | const unsigned char *addr, u16 vid) | ||
1014 | { | ||
1015 | int state = is_multicast_ether_addr(addr) ? | ||
1016 | FDB_STATE_MC_STATIC : FDB_STATE_STATIC; | ||
1017 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | ||
1018 | int ret; | ||
1019 | |||
1020 | mutex_lock(&ps->smi_mutex); | ||
1021 | ret = __mv88e6xxx_port_fdb_cmd(ds, port, addr, state); | ||
1022 | mutex_unlock(&ps->smi_mutex); | ||
1023 | |||
1024 | return ret; | ||
1025 | } | ||
1026 | |||
1027 | int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, | ||
1028 | const unsigned char *addr, u16 vid) | ||
1029 | { | ||
1030 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | ||
1031 | int ret; | ||
1032 | |||
1033 | mutex_lock(&ps->smi_mutex); | ||
1034 | ret = __mv88e6xxx_port_fdb_cmd(ds, port, addr, FDB_STATE_UNUSED); | ||
1035 | mutex_unlock(&ps->smi_mutex); | ||
1036 | |||
1037 | return ret; | ||
1038 | } | ||
1039 | |||
1040 | static int __mv88e6xxx_port_getnext(struct dsa_switch *ds, int port, | ||
1041 | unsigned char *addr, bool *is_static) | ||
1042 | { | ||
1043 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | ||
1044 | u8 fid = ps->fid[port]; | ||
1045 | int ret, state; | ||
1046 | |||
1047 | ret = _mv88e6xxx_atu_wait(ds); | ||
1048 | if (ret < 0) | ||
1049 | return ret; | ||
1050 | |||
1051 | ret = __mv88e6xxx_write_addr(ds, addr); | ||
1052 | if (ret < 0) | ||
1053 | return ret; | ||
1054 | |||
1055 | do { | ||
1056 | ret = _mv88e6xxx_atu_cmd(ds, fid, ATU_CMD_GETNEXT_FID); | ||
1057 | if (ret < 0) | ||
1058 | return ret; | ||
1059 | |||
1060 | ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, 0x0c); | ||
1061 | if (ret < 0) | ||
1062 | return ret; | ||
1063 | state = ret & FDB_STATE_MASK; | ||
1064 | if (state == FDB_STATE_UNUSED) | ||
1065 | return -ENOENT; | ||
1066 | } while (!(((ret >> 4) & 0xff) & (1 << port))); | ||
1067 | |||
1068 | ret = __mv88e6xxx_read_addr(ds, addr); | ||
1069 | if (ret < 0) | ||
1070 | return ret; | ||
1071 | |||
1072 | *is_static = state == (is_multicast_ether_addr(addr) ? | ||
1073 | FDB_STATE_MC_STATIC : FDB_STATE_STATIC); | ||
1074 | |||
1075 | return 0; | ||
1076 | } | ||
1077 | |||
1078 | /* get next entry for port */ | ||
1079 | int mv88e6xxx_port_fdb_getnext(struct dsa_switch *ds, int port, | ||
1080 | unsigned char *addr, bool *is_static) | ||
1081 | { | ||
1082 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | ||
1083 | int ret; | ||
1084 | |||
1085 | mutex_lock(&ps->smi_mutex); | ||
1086 | ret = __mv88e6xxx_port_getnext(ds, port, addr, is_static); | ||
1087 | mutex_unlock(&ps->smi_mutex); | ||
1088 | |||
1089 | return ret; | ||
1090 | } | ||
1091 | |||
1092 | static void mv88e6xxx_bridge_work(struct work_struct *work) | ||
1093 | { | ||
1094 | struct mv88e6xxx_priv_state *ps; | ||
1095 | struct dsa_switch *ds; | ||
1096 | int port; | ||
1097 | |||
1098 | ps = container_of(work, struct mv88e6xxx_priv_state, bridge_work); | ||
1099 | ds = ((struct dsa_switch *)ps) - 1; | ||
1100 | |||
1101 | while (ps->port_state_update_mask) { | ||
1102 | port = __ffs(ps->port_state_update_mask); | ||
1103 | clear_bit(port, &ps->port_state_update_mask); | ||
1104 | mv88e6xxx_set_port_state(ds, port, ps->port_state[port]); | ||
1105 | } | ||
1106 | } | ||
1107 | |||
1108 | int mv88e6xxx_setup_port_common(struct dsa_switch *ds, int port) | ||
1109 | { | ||
1110 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | ||
1111 | int ret, fid; | ||
1112 | |||
1113 | mutex_lock(&ps->smi_mutex); | ||
1114 | |||
1115 | /* Port Control 1: disable trunking, disable sending | ||
1116 | * learning messages to this port. | ||
1117 | */ | ||
1118 | ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), 0x05, 0x0000); | ||
1119 | if (ret) | ||
1120 | goto abort; | ||
1121 | |||
1122 | /* Port based VLAN map: give each port its own address | ||
1123 | * database, allow the CPU port to talk to each of the 'real' | ||
1124 | * ports, and allow each of the 'real' ports to only talk to | ||
1125 | * the upstream port. | ||
1126 | */ | ||
1127 | fid = __ffs(ps->fid_mask); | ||
1128 | ps->fid[port] = fid; | ||
1129 | ps->fid_mask &= ~(1 << fid); | ||
1130 | |||
1131 | if (!dsa_is_cpu_port(ds, port)) | ||
1132 | ps->bridge_mask[fid] = 1 << port; | ||
1133 | |||
1134 | ret = _mv88e6xxx_update_port_config(ds, port); | ||
1135 | if (ret) | ||
1136 | goto abort; | ||
1137 | |||
1138 | /* Default VLAN ID and priority: don't set a default VLAN | ||
1139 | * ID, and set the default packet priority to zero. | ||
1140 | */ | ||
1141 | ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), 0x07, 0x0000); | ||
1142 | abort: | ||
1143 | mutex_unlock(&ps->smi_mutex); | ||
1144 | return ret; | ||
1145 | } | ||
1146 | |||
1147 | int mv88e6xxx_setup_common(struct dsa_switch *ds) | ||
1148 | { | ||
1149 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | ||
1150 | |||
1151 | mutex_init(&ps->smi_mutex); | ||
1152 | mutex_init(&ps->stats_mutex); | ||
1153 | mutex_init(&ps->phy_mutex); | ||
1154 | |||
1155 | ps->id = REG_READ(REG_PORT(0), 0x03) & 0xfff0; | ||
1156 | |||
1157 | ps->fid_mask = (1 << DSA_MAX_PORTS) - 1; | ||
1158 | |||
1159 | INIT_WORK(&ps->bridge_work, mv88e6xxx_bridge_work); | ||
1160 | |||
1161 | return 0; | ||
1162 | } | ||
1163 | |||
703 | static int __init mv88e6xxx_init(void) | 1164 | static int __init mv88e6xxx_init(void) |
704 | { | 1165 | { |
705 | #if IS_ENABLED(CONFIG_NET_DSA_MV88E6131) | 1166 | #if IS_ENABLED(CONFIG_NET_DSA_MV88E6131) |
diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h index 5fd42ced9011..aaf239aba726 100644 --- a/drivers/net/dsa/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx.h | |||
@@ -15,6 +15,30 @@ | |||
15 | #define REG_GLOBAL 0x1b | 15 | #define REG_GLOBAL 0x1b |
16 | #define REG_GLOBAL2 0x1c | 16 | #define REG_GLOBAL2 0x1c |
17 | 17 | ||
18 | /* ATU commands */ | ||
19 | |||
20 | #define ATU_BUSY 0x8000 | ||
21 | |||
22 | #define ATU_CMD_LOAD_FID (ATU_BUSY | 0x3000) | ||
23 | #define ATU_CMD_GETNEXT_FID (ATU_BUSY | 0x4000) | ||
24 | #define ATU_CMD_FLUSH_NONSTATIC_FID (ATU_BUSY | 0x6000) | ||
25 | |||
26 | /* port states */ | ||
27 | |||
28 | #define PSTATE_MASK 0x03 | ||
29 | #define PSTATE_DISABLED 0x00 | ||
30 | #define PSTATE_BLOCKING 0x01 | ||
31 | #define PSTATE_LEARNING 0x02 | ||
32 | #define PSTATE_FORWARDING 0x03 | ||
33 | |||
34 | /* FDB states */ | ||
35 | |||
36 | #define FDB_STATE_MASK 0x0f | ||
37 | |||
38 | #define FDB_STATE_UNUSED 0x00 | ||
39 | #define FDB_STATE_MC_STATIC 0x07 /* static multicast */ | ||
40 | #define FDB_STATE_STATIC 0x0e /* static unicast */ | ||
41 | |||
18 | struct mv88e6xxx_priv_state { | 42 | struct mv88e6xxx_priv_state { |
19 | /* When using multi-chip addressing, this mutex protects | 43 | /* When using multi-chip addressing, this mutex protects |
20 | * access to the indirect access registers. (In single-chip | 44 | * access to the indirect access registers. (In single-chip |
@@ -49,6 +73,17 @@ struct mv88e6xxx_priv_state { | |||
49 | struct mutex eeprom_mutex; | 73 | struct mutex eeprom_mutex; |
50 | 74 | ||
51 | int id; /* switch product id */ | 75 | int id; /* switch product id */ |
76 | |||
77 | /* hw bridging */ | ||
78 | |||
79 | u32 fid_mask; | ||
80 | u8 fid[DSA_MAX_PORTS]; | ||
81 | u16 bridge_mask[DSA_MAX_PORTS]; | ||
82 | |||
83 | unsigned long port_state_update_mask; | ||
84 | u8 port_state[DSA_MAX_PORTS]; | ||
85 | |||
86 | struct work_struct bridge_work; | ||
52 | }; | 87 | }; |
53 | 88 | ||
54 | struct mv88e6xxx_hw_stat { | 89 | struct mv88e6xxx_hw_stat { |
@@ -57,6 +92,8 @@ struct mv88e6xxx_hw_stat { | |||
57 | int reg; | 92 | int reg; |
58 | }; | 93 | }; |
59 | 94 | ||
95 | int mv88e6xxx_setup_port_common(struct dsa_switch *ds, int port); | ||
96 | int mv88e6xxx_setup_common(struct dsa_switch *ds); | ||
60 | int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg); | 97 | int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg); |
61 | int mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg); | 98 | int mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg); |
62 | int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr, | 99 | int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr, |
@@ -91,6 +128,15 @@ int mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int addr, int regnum, | |||
91 | int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e); | 128 | int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e); |
92 | int mv88e6xxx_set_eee(struct dsa_switch *ds, int port, | 129 | int mv88e6xxx_set_eee(struct dsa_switch *ds, int port, |
93 | struct phy_device *phydev, struct ethtool_eee *e); | 130 | struct phy_device *phydev, struct ethtool_eee *e); |
131 | int mv88e6xxx_join_bridge(struct dsa_switch *ds, int port, u32 br_port_mask); | ||
132 | int mv88e6xxx_leave_bridge(struct dsa_switch *ds, int port, u32 br_port_mask); | ||
133 | int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state); | ||
134 | int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, | ||
135 | const unsigned char *addr, u16 vid); | ||
136 | int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, | ||
137 | const unsigned char *addr, u16 vid); | ||
138 | int mv88e6xxx_port_fdb_getnext(struct dsa_switch *ds, int port, | ||
139 | unsigned char *addr, bool *is_static); | ||
94 | 140 | ||
95 | extern struct dsa_switch_driver mv88e6131_switch_driver; | 141 | extern struct dsa_switch_driver mv88e6131_switch_driver; |
96 | extern struct dsa_switch_driver mv88e6123_61_65_switch_driver; | 142 | extern struct dsa_switch_driver mv88e6123_61_65_switch_driver; |
diff --git a/include/net/dsa.h b/include/net/dsa.h index 47917e5e1e12..fbca63ba8f73 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h | |||
@@ -296,6 +296,12 @@ struct dsa_switch_driver { | |||
296 | u32 br_port_mask); | 296 | u32 br_port_mask); |
297 | int (*port_stp_update)(struct dsa_switch *ds, int port, | 297 | int (*port_stp_update)(struct dsa_switch *ds, int port, |
298 | u8 state); | 298 | u8 state); |
299 | int (*fdb_add)(struct dsa_switch *ds, int port, | ||
300 | const unsigned char *addr, u16 vid); | ||
301 | int (*fdb_del)(struct dsa_switch *ds, int port, | ||
302 | const unsigned char *addr, u16 vid); | ||
303 | int (*fdb_getnext)(struct dsa_switch *ds, int port, | ||
304 | unsigned char *addr, bool *is_static); | ||
299 | }; | 305 | }; |
300 | 306 | ||
301 | void register_switch_driver(struct dsa_switch_driver *type); | 307 | void register_switch_driver(struct dsa_switch_driver *type); |
diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 39555f3f263b..3597724ec3d8 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c | |||
@@ -201,6 +201,105 @@ out: | |||
201 | return 0; | 201 | return 0; |
202 | } | 202 | } |
203 | 203 | ||
204 | static int dsa_slave_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], | ||
205 | struct net_device *dev, | ||
206 | const unsigned char *addr, u16 vid, u16 nlm_flags) | ||
207 | { | ||
208 | struct dsa_slave_priv *p = netdev_priv(dev); | ||
209 | struct dsa_switch *ds = p->parent; | ||
210 | int ret = -EOPNOTSUPP; | ||
211 | |||
212 | if (ds->drv->fdb_add) | ||
213 | ret = ds->drv->fdb_add(ds, p->port, addr, vid); | ||
214 | |||
215 | return ret; | ||
216 | } | ||
217 | |||
218 | static int dsa_slave_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], | ||
219 | struct net_device *dev, | ||
220 | const unsigned char *addr, u16 vid) | ||
221 | { | ||
222 | struct dsa_slave_priv *p = netdev_priv(dev); | ||
223 | struct dsa_switch *ds = p->parent; | ||
224 | int ret = -EOPNOTSUPP; | ||
225 | |||
226 | if (ds->drv->fdb_del) | ||
227 | ret = ds->drv->fdb_del(ds, p->port, addr, vid); | ||
228 | |||
229 | return ret; | ||
230 | } | ||
231 | |||
232 | static int dsa_slave_fill_info(struct net_device *dev, struct sk_buff *skb, | ||
233 | const unsigned char *addr, u16 vid, | ||
234 | bool is_static, | ||
235 | u32 portid, u32 seq, int type, | ||
236 | unsigned int flags) | ||
237 | { | ||
238 | struct nlmsghdr *nlh; | ||
239 | struct ndmsg *ndm; | ||
240 | |||
241 | nlh = nlmsg_put(skb, portid, seq, type, sizeof(*ndm), flags); | ||
242 | if (!nlh) | ||
243 | return -EMSGSIZE; | ||
244 | |||
245 | ndm = nlmsg_data(nlh); | ||
246 | ndm->ndm_family = AF_BRIDGE; | ||
247 | ndm->ndm_pad1 = 0; | ||
248 | ndm->ndm_pad2 = 0; | ||
249 | ndm->ndm_flags = NTF_EXT_LEARNED; | ||
250 | ndm->ndm_type = 0; | ||
251 | ndm->ndm_ifindex = dev->ifindex; | ||
252 | ndm->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE; | ||
253 | |||
254 | if (nla_put(skb, NDA_LLADDR, ETH_ALEN, addr)) | ||
255 | goto nla_put_failure; | ||
256 | |||
257 | if (vid && nla_put_u16(skb, NDA_VLAN, vid)) | ||
258 | goto nla_put_failure; | ||
259 | |||
260 | nlmsg_end(skb, nlh); | ||
261 | return 0; | ||
262 | |||
263 | nla_put_failure: | ||
264 | nlmsg_cancel(skb, nlh); | ||
265 | return -EMSGSIZE; | ||
266 | } | ||
267 | |||
268 | /* Dump information about entries, in response to GETNEIGH */ | ||
269 | static int dsa_slave_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, | ||
270 | struct net_device *dev, | ||
271 | struct net_device *filter_dev, int idx) | ||
272 | { | ||
273 | struct dsa_slave_priv *p = netdev_priv(dev); | ||
274 | struct dsa_switch *ds = p->parent; | ||
275 | unsigned char addr[ETH_ALEN] = { 0 }; | ||
276 | int ret; | ||
277 | |||
278 | if (!ds->drv->fdb_getnext) | ||
279 | return -EOPNOTSUPP; | ||
280 | |||
281 | for (; ; idx++) { | ||
282 | bool is_static; | ||
283 | |||
284 | ret = ds->drv->fdb_getnext(ds, p->port, addr, &is_static); | ||
285 | if (ret < 0) | ||
286 | break; | ||
287 | |||
288 | if (idx < cb->args[0]) | ||
289 | continue; | ||
290 | |||
291 | ret = dsa_slave_fill_info(dev, skb, addr, 0, | ||
292 | is_static, | ||
293 | NETLINK_CB(cb->skb).portid, | ||
294 | cb->nlh->nlmsg_seq, | ||
295 | RTM_NEWNEIGH, NLM_F_MULTI); | ||
296 | if (ret < 0) | ||
297 | break; | ||
298 | } | ||
299 | |||
300 | return idx; | ||
301 | } | ||
302 | |||
204 | static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) | 303 | static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) |
205 | { | 304 | { |
206 | struct dsa_slave_priv *p = netdev_priv(dev); | 305 | struct dsa_slave_priv *p = netdev_priv(dev); |
@@ -572,6 +671,9 @@ static const struct net_device_ops dsa_slave_netdev_ops = { | |||
572 | .ndo_change_rx_flags = dsa_slave_change_rx_flags, | 671 | .ndo_change_rx_flags = dsa_slave_change_rx_flags, |
573 | .ndo_set_rx_mode = dsa_slave_set_rx_mode, | 672 | .ndo_set_rx_mode = dsa_slave_set_rx_mode, |
574 | .ndo_set_mac_address = dsa_slave_set_mac_address, | 673 | .ndo_set_mac_address = dsa_slave_set_mac_address, |
674 | .ndo_fdb_add = dsa_slave_fdb_add, | ||
675 | .ndo_fdb_del = dsa_slave_fdb_del, | ||
676 | .ndo_fdb_dump = dsa_slave_fdb_dump, | ||
575 | .ndo_do_ioctl = dsa_slave_ioctl, | 677 | .ndo_do_ioctl = dsa_slave_ioctl, |
576 | }; | 678 | }; |
577 | 679 | ||