aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFlorian Fainelli <f.fainelli@gmail.com>2015-02-24 16:15:34 -0500
committerDavid S. Miller <davem@davemloft.net>2015-02-25 17:03:38 -0500
commit12f460f23423e81d6dd3efeb78906ae634ad8fc9 (patch)
treed0a46201df407c4719abade5cd44bcb86041b195
parentb73adef67765b72f2a0d01ef15aff9d784dc85da (diff)
net: dsa: bcm_sf2: add HW bridging support
Implement the bridge join, leave and set_stp callbacks by making that we do the following: - when a port joins the bridge, all existing ports in the bridge get their VLAN control register updated with that joining port - the joining port is including all existing bridge ports in its own VLAN control register The leave operation is fairly similar, special care must be taken to make sure that port leaving the bridging is not removing itself from its own VLAN control register. Since the various BR_* states apply directly to our HW semantics, we just need to translate these constants into their corresponding HW settings, and voila! We make sure to trigger a fast-ageing process for ports that are joining/leaving the bridge and transition from incompatible states, this is equivalent to triggering an ARL flush for that port. Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/dsa/bcm_sf2.c155
-rw-r--r--drivers/net/dsa/bcm_sf2.h2
-rw-r--r--drivers/net/dsa/bcm_sf2_regs.h15
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 */
411static 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
437static 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
470static 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
500static 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
403static irqreturn_t bcm_sf2_switch_0_isr(int irq, void *dev_id) 553static 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
921static int __init bcm_sf2_init(void) 1074static 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
51struct bcm_sf2_priv { 53struct 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