aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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