diff options
-rw-r--r-- | drivers/net/dsa/bcm_sf2.c | 155 | ||||
-rw-r--r-- | drivers/net/dsa/bcm_sf2.h | 2 | ||||
-rw-r--r-- | drivers/net/dsa/bcm_sf2_regs.h | 15 |
3 files changed, 171 insertions, 1 deletions
diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 4daffb284931..cedb572bf25a 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/of_address.h> | 23 | #include <linux/of_address.h> |
24 | #include <net/dsa.h> | 24 | #include <net/dsa.h> |
25 | #include <linux/ethtool.h> | 25 | #include <linux/ethtool.h> |
26 | #include <linux/if_bridge.h> | ||
26 | 27 | ||
27 | #include "bcm_sf2.h" | 28 | #include "bcm_sf2.h" |
28 | #include "bcm_sf2_regs.h" | 29 | #include "bcm_sf2_regs.h" |
@@ -299,10 +300,14 @@ static int bcm_sf2_port_setup(struct dsa_switch *ds, int port, | |||
299 | if (port == 7) | 300 | if (port == 7) |
300 | intrl2_1_mask_clear(priv, P_IRQ_MASK(P7_IRQ_OFF)); | 301 | intrl2_1_mask_clear(priv, P_IRQ_MASK(P7_IRQ_OFF)); |
301 | 302 | ||
302 | /* Set this port, and only this one to be in the default VLAN */ | 303 | /* Set this port, and only this one to be in the default VLAN, |
304 | * if member of a bridge, restore its membership prior to | ||
305 | * bringing down this port. | ||
306 | */ | ||
303 | reg = core_readl(priv, CORE_PORT_VLAN_CTL_PORT(port)); | 307 | reg = core_readl(priv, CORE_PORT_VLAN_CTL_PORT(port)); |
304 | reg &= ~PORT_VLAN_CTRL_MASK; | 308 | reg &= ~PORT_VLAN_CTRL_MASK; |
305 | reg |= (1 << port); | 309 | reg |= (1 << port); |
310 | reg |= priv->port_sts[port].vlan_ctl_mask; | ||
306 | core_writel(priv, reg, CORE_PORT_VLAN_CTL_PORT(port)); | 311 | core_writel(priv, reg, CORE_PORT_VLAN_CTL_PORT(port)); |
307 | 312 | ||
308 | bcm_sf2_imp_vlan_setup(ds, cpu_port); | 313 | bcm_sf2_imp_vlan_setup(ds, cpu_port); |
@@ -400,6 +405,151 @@ static int bcm_sf2_sw_set_eee(struct dsa_switch *ds, int port, | |||
400 | return 0; | 405 | return 0; |
401 | } | 406 | } |
402 | 407 | ||
408 | /* Fast-ageing of ARL entries for a given port, equivalent to an ARL | ||
409 | * flush for that port. | ||
410 | */ | ||
411 | static int bcm_sf2_sw_fast_age_port(struct dsa_switch *ds, int port) | ||
412 | { | ||
413 | struct bcm_sf2_priv *priv = ds_to_priv(ds); | ||
414 | unsigned int timeout = 1000; | ||
415 | u32 reg; | ||
416 | |||
417 | core_writel(priv, port, CORE_FAST_AGE_PORT); | ||
418 | |||
419 | reg = core_readl(priv, CORE_FAST_AGE_CTRL); | ||
420 | reg |= EN_AGE_PORT | FAST_AGE_STR_DONE; | ||
421 | core_writel(priv, reg, CORE_FAST_AGE_CTRL); | ||
422 | |||
423 | do { | ||
424 | reg = core_readl(priv, CORE_FAST_AGE_CTRL); | ||
425 | if (!(reg & FAST_AGE_STR_DONE)) | ||
426 | break; | ||
427 | |||
428 | cpu_relax(); | ||
429 | } while (timeout--); | ||
430 | |||
431 | if (!timeout) | ||
432 | return -ETIMEDOUT; | ||
433 | |||
434 | return 0; | ||
435 | } | ||
436 | |||
437 | static int bcm_sf2_sw_br_join(struct dsa_switch *ds, int port, | ||
438 | u32 br_port_mask) | ||
439 | { | ||
440 | struct bcm_sf2_priv *priv = ds_to_priv(ds); | ||
441 | unsigned int i; | ||
442 | u32 reg, p_ctl; | ||
443 | |||
444 | p_ctl = core_readl(priv, CORE_PORT_VLAN_CTL_PORT(port)); | ||
445 | |||
446 | for (i = 0; i < priv->hw_params.num_ports; i++) { | ||
447 | if (!((1 << i) & br_port_mask)) | ||
448 | continue; | ||
449 | |||
450 | /* Add this local port to the remote port VLAN control | ||
451 | * membership and update the remote port bitmask | ||
452 | */ | ||
453 | reg = core_readl(priv, CORE_PORT_VLAN_CTL_PORT(i)); | ||
454 | reg |= 1 << port; | ||
455 | core_writel(priv, reg, CORE_PORT_VLAN_CTL_PORT(i)); | ||
456 | priv->port_sts[i].vlan_ctl_mask = reg; | ||
457 | |||
458 | p_ctl |= 1 << i; | ||
459 | } | ||
460 | |||
461 | /* Configure the local port VLAN control membership to include | ||
462 | * remote ports and update the local port bitmask | ||
463 | */ | ||
464 | core_writel(priv, p_ctl, CORE_PORT_VLAN_CTL_PORT(port)); | ||
465 | priv->port_sts[port].vlan_ctl_mask = p_ctl; | ||
466 | |||
467 | return 0; | ||
468 | } | ||
469 | |||
470 | static int bcm_sf2_sw_br_leave(struct dsa_switch *ds, int port, | ||
471 | u32 br_port_mask) | ||
472 | { | ||
473 | struct bcm_sf2_priv *priv = ds_to_priv(ds); | ||
474 | unsigned int i; | ||
475 | u32 reg, p_ctl; | ||
476 | |||
477 | p_ctl = core_readl(priv, CORE_PORT_VLAN_CTL_PORT(port)); | ||
478 | |||
479 | for (i = 0; i < priv->hw_params.num_ports; i++) { | ||
480 | /* Don't touch the remaining ports */ | ||
481 | if (!((1 << i) & br_port_mask)) | ||
482 | continue; | ||
483 | |||
484 | reg = core_readl(priv, CORE_PORT_VLAN_CTL_PORT(i)); | ||
485 | reg &= ~(1 << port); | ||
486 | core_writel(priv, reg, CORE_PORT_VLAN_CTL_PORT(i)); | ||
487 | priv->port_sts[port].vlan_ctl_mask = reg; | ||
488 | |||
489 | /* Prevent self removal to preserve isolation */ | ||
490 | if (port != i) | ||
491 | p_ctl &= ~(1 << i); | ||
492 | } | ||
493 | |||
494 | core_writel(priv, p_ctl, CORE_PORT_VLAN_CTL_PORT(port)); | ||
495 | priv->port_sts[port].vlan_ctl_mask = p_ctl; | ||
496 | |||
497 | return 0; | ||
498 | } | ||
499 | |||
500 | static int bcm_sf2_sw_br_set_stp_state(struct dsa_switch *ds, int port, | ||
501 | u8 state) | ||
502 | { | ||
503 | struct bcm_sf2_priv *priv = ds_to_priv(ds); | ||
504 | u8 hw_state, cur_hw_state; | ||
505 | int ret = 0; | ||
506 | u32 reg; | ||
507 | |||
508 | reg = core_readl(priv, CORE_G_PCTL_PORT(port)); | ||
509 | cur_hw_state = reg >> G_MISTP_STATE_SHIFT; | ||
510 | |||
511 | switch (state) { | ||
512 | case BR_STATE_DISABLED: | ||
513 | hw_state = G_MISTP_DIS_STATE; | ||
514 | break; | ||
515 | case BR_STATE_LISTENING: | ||
516 | hw_state = G_MISTP_LISTEN_STATE; | ||
517 | break; | ||
518 | case BR_STATE_LEARNING: | ||
519 | hw_state = G_MISTP_LEARN_STATE; | ||
520 | break; | ||
521 | case BR_STATE_FORWARDING: | ||
522 | hw_state = G_MISTP_FWD_STATE; | ||
523 | break; | ||
524 | case BR_STATE_BLOCKING: | ||
525 | hw_state = G_MISTP_BLOCK_STATE; | ||
526 | break; | ||
527 | default: | ||
528 | pr_err("%s: invalid STP state: %d\n", __func__, state); | ||
529 | return -EINVAL; | ||
530 | } | ||
531 | |||
532 | /* Fast-age ARL entries if we are moving a port from Learning or | ||
533 | * Forwarding state to Disabled, Blocking or Listening state | ||
534 | */ | ||
535 | if (cur_hw_state != hw_state) { | ||
536 | if (cur_hw_state & 4 && !(hw_state & 4)) { | ||
537 | ret = bcm_sf2_sw_fast_age_port(ds, port); | ||
538 | if (ret) { | ||
539 | pr_err("%s: fast-ageing failed\n", __func__); | ||
540 | return ret; | ||
541 | } | ||
542 | } | ||
543 | } | ||
544 | |||
545 | reg = core_readl(priv, CORE_G_PCTL_PORT(port)); | ||
546 | reg &= ~(G_MISTP_STATE_MASK << G_MISTP_STATE_SHIFT); | ||
547 | reg |= hw_state; | ||
548 | core_writel(priv, reg, CORE_G_PCTL_PORT(port)); | ||
549 | |||
550 | return 0; | ||
551 | } | ||
552 | |||
403 | static irqreturn_t bcm_sf2_switch_0_isr(int irq, void *dev_id) | 553 | static irqreturn_t bcm_sf2_switch_0_isr(int irq, void *dev_id) |
404 | { | 554 | { |
405 | struct bcm_sf2_priv *priv = dev_id; | 555 | struct bcm_sf2_priv *priv = dev_id; |
@@ -916,6 +1066,9 @@ static struct dsa_switch_driver bcm_sf2_switch_driver = { | |||
916 | .port_disable = bcm_sf2_port_disable, | 1066 | .port_disable = bcm_sf2_port_disable, |
917 | .get_eee = bcm_sf2_sw_get_eee, | 1067 | .get_eee = bcm_sf2_sw_get_eee, |
918 | .set_eee = bcm_sf2_sw_set_eee, | 1068 | .set_eee = bcm_sf2_sw_set_eee, |
1069 | .port_join_bridge = bcm_sf2_sw_br_join, | ||
1070 | .port_leave_bridge = bcm_sf2_sw_br_leave, | ||
1071 | .port_stp_update = bcm_sf2_sw_br_set_stp_state, | ||
919 | }; | 1072 | }; |
920 | 1073 | ||
921 | static int __init bcm_sf2_init(void) | 1074 | static int __init bcm_sf2_init(void) |
diff --git a/drivers/net/dsa/bcm_sf2.h b/drivers/net/dsa/bcm_sf2.h index ee9f650d5026..0f217e99904f 100644 --- a/drivers/net/dsa/bcm_sf2.h +++ b/drivers/net/dsa/bcm_sf2.h | |||
@@ -46,6 +46,8 @@ struct bcm_sf2_port_status { | |||
46 | unsigned int link; | 46 | unsigned int link; |
47 | 47 | ||
48 | struct ethtool_eee eee; | 48 | struct ethtool_eee eee; |
49 | |||
50 | u32 vlan_ctl_mask; | ||
49 | }; | 51 | }; |
50 | 52 | ||
51 | struct bcm_sf2_priv { | 53 | struct bcm_sf2_priv { |
diff --git a/drivers/net/dsa/bcm_sf2_regs.h b/drivers/net/dsa/bcm_sf2_regs.h index cabdfa5e217a..fa4e6e78c9ea 100644 --- a/drivers/net/dsa/bcm_sf2_regs.h +++ b/drivers/net/dsa/bcm_sf2_regs.h | |||
@@ -163,6 +163,21 @@ | |||
163 | #define EN_CHIP_RST (1 << 6) | 163 | #define EN_CHIP_RST (1 << 6) |
164 | #define EN_SW_RESET (1 << 4) | 164 | #define EN_SW_RESET (1 << 4) |
165 | 165 | ||
166 | #define CORE_FAST_AGE_CTRL 0x00220 | ||
167 | #define EN_FAST_AGE_STATIC (1 << 0) | ||
168 | #define EN_AGE_DYNAMIC (1 << 1) | ||
169 | #define EN_AGE_PORT (1 << 2) | ||
170 | #define EN_AGE_VLAN (1 << 3) | ||
171 | #define EN_AGE_SPT (1 << 4) | ||
172 | #define EN_AGE_MCAST (1 << 5) | ||
173 | #define FAST_AGE_STR_DONE (1 << 7) | ||
174 | |||
175 | #define CORE_FAST_AGE_PORT 0x00224 | ||
176 | #define AGE_PORT_MASK 0xf | ||
177 | |||
178 | #define CORE_FAST_AGE_VID 0x00228 | ||
179 | #define AGE_VID_MASK 0x3fff | ||
180 | |||
166 | #define CORE_LNKSTS 0x00400 | 181 | #define CORE_LNKSTS 0x00400 |
167 | #define LNK_STS_MASK 0x1ff | 182 | #define LNK_STS_MASK 0x1ff |
168 | 183 | ||