diff options
author | Florian Fainelli <f.fainelli@gmail.com> | 2016-06-09 20:42:08 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-06-10 01:12:49 -0400 |
commit | 9c57a77182c89e1bf773008f904f4a2e9ea30bd5 (patch) | |
tree | bdcbc21f19e56e036f83545f60347eb35ba50f52 /drivers/net/dsa/bcm_sf2.c | |
parent | 064523ff786093d81ae967959196c723d30f3da5 (diff) |
net: dsa: bcm_sf2: Add VLAN support
Add support for configuring VLANs on the Broadcom Starfigther2 switch.
This is all done through the bridge vlan facility just like other DSA
drivers.
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/dsa/bcm_sf2.c')
-rw-r--r-- | drivers/net/dsa/bcm_sf2.c | 266 |
1 files changed, 265 insertions, 1 deletions
diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index d726f5906ef9..cd1d630ae3a9 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c | |||
@@ -467,7 +467,7 @@ static int bcm_sf2_fast_age_op(struct bcm_sf2_priv *priv) | |||
467 | u32 reg; | 467 | u32 reg; |
468 | 468 | ||
469 | reg = core_readl(priv, CORE_FAST_AGE_CTRL); | 469 | reg = core_readl(priv, CORE_FAST_AGE_CTRL); |
470 | reg |= EN_AGE_PORT | EN_AGE_DYNAMIC | FAST_AGE_STR_DONE; | 470 | reg |= EN_AGE_PORT | EN_AGE_VLAN | EN_AGE_DYNAMIC | FAST_AGE_STR_DONE; |
471 | core_writel(priv, reg, CORE_FAST_AGE_CTRL); | 471 | core_writel(priv, reg, CORE_FAST_AGE_CTRL); |
472 | 472 | ||
473 | do { | 473 | do { |
@@ -498,13 +498,86 @@ static int bcm_sf2_sw_fast_age_port(struct dsa_switch *ds, int port) | |||
498 | return bcm_sf2_fast_age_op(priv); | 498 | return bcm_sf2_fast_age_op(priv); |
499 | } | 499 | } |
500 | 500 | ||
501 | static int bcm_sf2_sw_fast_age_vlan(struct bcm_sf2_priv *priv, u16 vid) | ||
502 | { | ||
503 | core_writel(priv, vid, CORE_FAST_AGE_VID); | ||
504 | |||
505 | return bcm_sf2_fast_age_op(priv); | ||
506 | } | ||
507 | |||
508 | static int bcm_sf2_vlan_op_wait(struct bcm_sf2_priv *priv) | ||
509 | { | ||
510 | unsigned int timeout = 10; | ||
511 | u32 reg; | ||
512 | |||
513 | do { | ||
514 | reg = core_readl(priv, CORE_ARLA_VTBL_RWCTRL); | ||
515 | if (!(reg & ARLA_VTBL_STDN)) | ||
516 | return 0; | ||
517 | |||
518 | usleep_range(1000, 2000); | ||
519 | } while (timeout--); | ||
520 | |||
521 | return -ETIMEDOUT; | ||
522 | } | ||
523 | |||
524 | static int bcm_sf2_vlan_op(struct bcm_sf2_priv *priv, u8 op) | ||
525 | { | ||
526 | core_writel(priv, ARLA_VTBL_STDN | op, CORE_ARLA_VTBL_RWCTRL); | ||
527 | |||
528 | return bcm_sf2_vlan_op_wait(priv); | ||
529 | } | ||
530 | |||
531 | static void bcm_sf2_set_vlan_entry(struct bcm_sf2_priv *priv, u16 vid, | ||
532 | struct bcm_sf2_vlan *vlan) | ||
533 | { | ||
534 | int ret; | ||
535 | |||
536 | core_writel(priv, vid & VTBL_ADDR_INDEX_MASK, CORE_ARLA_VTBL_ADDR); | ||
537 | core_writel(priv, vlan->untag << UNTAG_MAP_SHIFT | vlan->members, | ||
538 | CORE_ARLA_VTBL_ENTRY); | ||
539 | |||
540 | ret = bcm_sf2_vlan_op(priv, ARLA_VTBL_CMD_WRITE); | ||
541 | if (ret) | ||
542 | pr_err("failed to write VLAN entry\n"); | ||
543 | } | ||
544 | |||
545 | static int bcm_sf2_get_vlan_entry(struct bcm_sf2_priv *priv, u16 vid, | ||
546 | struct bcm_sf2_vlan *vlan) | ||
547 | { | ||
548 | u32 entry; | ||
549 | int ret; | ||
550 | |||
551 | core_writel(priv, vid & VTBL_ADDR_INDEX_MASK, CORE_ARLA_VTBL_ADDR); | ||
552 | |||
553 | ret = bcm_sf2_vlan_op(priv, ARLA_VTBL_CMD_READ); | ||
554 | if (ret) | ||
555 | return ret; | ||
556 | |||
557 | entry = core_readl(priv, CORE_ARLA_VTBL_ENTRY); | ||
558 | vlan->members = entry & FWD_MAP_MASK; | ||
559 | vlan->untag = (entry >> UNTAG_MAP_SHIFT) & UNTAG_MAP_MASK; | ||
560 | |||
561 | return 0; | ||
562 | } | ||
563 | |||
501 | static int bcm_sf2_sw_br_join(struct dsa_switch *ds, int port, | 564 | static int bcm_sf2_sw_br_join(struct dsa_switch *ds, int port, |
502 | struct net_device *bridge) | 565 | struct net_device *bridge) |
503 | { | 566 | { |
504 | struct bcm_sf2_priv *priv = ds_to_priv(ds); | 567 | struct bcm_sf2_priv *priv = ds_to_priv(ds); |
568 | s8 cpu_port = ds->dst->cpu_port; | ||
505 | unsigned int i; | 569 | unsigned int i; |
506 | u32 reg, p_ctl; | 570 | u32 reg, p_ctl; |
507 | 571 | ||
572 | /* Make this port leave the all VLANs join since we will have proper | ||
573 | * VLAN entries from now on | ||
574 | */ | ||
575 | reg = core_readl(priv, CORE_JOIN_ALL_VLAN_EN); | ||
576 | reg &= ~BIT(port); | ||
577 | if ((reg & BIT(cpu_port)) == BIT(cpu_port)) | ||
578 | reg &= ~BIT(cpu_port); | ||
579 | core_writel(priv, reg, CORE_JOIN_ALL_VLAN_EN); | ||
580 | |||
508 | priv->port_sts[port].bridge_dev = bridge; | 581 | priv->port_sts[port].bridge_dev = bridge; |
509 | p_ctl = core_readl(priv, CORE_PORT_VLAN_CTL_PORT(port)); | 582 | p_ctl = core_readl(priv, CORE_PORT_VLAN_CTL_PORT(port)); |
510 | 583 | ||
@@ -536,6 +609,7 @@ static void bcm_sf2_sw_br_leave(struct dsa_switch *ds, int port) | |||
536 | { | 609 | { |
537 | struct bcm_sf2_priv *priv = ds_to_priv(ds); | 610 | struct bcm_sf2_priv *priv = ds_to_priv(ds); |
538 | struct net_device *bridge = priv->port_sts[port].bridge_dev; | 611 | struct net_device *bridge = priv->port_sts[port].bridge_dev; |
612 | s8 cpu_port = ds->dst->cpu_port; | ||
539 | unsigned int i; | 613 | unsigned int i; |
540 | u32 reg, p_ctl; | 614 | u32 reg, p_ctl; |
541 | 615 | ||
@@ -559,6 +633,13 @@ static void bcm_sf2_sw_br_leave(struct dsa_switch *ds, int port) | |||
559 | core_writel(priv, p_ctl, CORE_PORT_VLAN_CTL_PORT(port)); | 633 | core_writel(priv, p_ctl, CORE_PORT_VLAN_CTL_PORT(port)); |
560 | priv->port_sts[port].vlan_ctl_mask = p_ctl; | 634 | priv->port_sts[port].vlan_ctl_mask = p_ctl; |
561 | priv->port_sts[port].bridge_dev = NULL; | 635 | priv->port_sts[port].bridge_dev = NULL; |
636 | |||
637 | /* Make this port join all VLANs without VLAN entries */ | ||
638 | reg = core_readl(priv, CORE_JOIN_ALL_VLAN_EN); | ||
639 | reg |= BIT(port); | ||
640 | if (!(reg & BIT(cpu_port))) | ||
641 | reg |= BIT(cpu_port); | ||
642 | core_writel(priv, reg, CORE_JOIN_ALL_VLAN_EN); | ||
562 | } | 643 | } |
563 | 644 | ||
564 | static void bcm_sf2_sw_br_set_stp_state(struct dsa_switch *ds, int port, | 645 | static void bcm_sf2_sw_br_set_stp_state(struct dsa_switch *ds, int port, |
@@ -1312,6 +1393,182 @@ static int bcm_sf2_sw_set_wol(struct dsa_switch *ds, int port, | |||
1312 | return p->ethtool_ops->set_wol(p, wol); | 1393 | return p->ethtool_ops->set_wol(p, wol); |
1313 | } | 1394 | } |
1314 | 1395 | ||
1396 | static void bcm_sf2_enable_vlan(struct bcm_sf2_priv *priv, bool enable) | ||
1397 | { | ||
1398 | u32 mgmt, vc0, vc1, vc4, vc5; | ||
1399 | |||
1400 | mgmt = core_readl(priv, CORE_SWMODE); | ||
1401 | vc0 = core_readl(priv, CORE_VLAN_CTRL0); | ||
1402 | vc1 = core_readl(priv, CORE_VLAN_CTRL1); | ||
1403 | vc4 = core_readl(priv, CORE_VLAN_CTRL4); | ||
1404 | vc5 = core_readl(priv, CORE_VLAN_CTRL5); | ||
1405 | |||
1406 | mgmt &= ~SW_FWDG_MODE; | ||
1407 | |||
1408 | if (enable) { | ||
1409 | vc0 |= VLAN_EN | VLAN_LEARN_MODE_IVL; | ||
1410 | vc1 |= EN_RSV_MCAST_UNTAG | EN_RSV_MCAST_FWDMAP; | ||
1411 | vc4 &= ~(INGR_VID_CHK_MASK << INGR_VID_CHK_SHIFT); | ||
1412 | vc4 |= INGR_VID_CHK_DROP; | ||
1413 | vc5 |= DROP_VTABLE_MISS | EN_VID_FFF_FWD; | ||
1414 | } else { | ||
1415 | vc0 &= ~(VLAN_EN | VLAN_LEARN_MODE_IVL); | ||
1416 | vc1 &= ~(EN_RSV_MCAST_UNTAG | EN_RSV_MCAST_FWDMAP); | ||
1417 | vc4 &= ~(INGR_VID_CHK_MASK << INGR_VID_CHK_SHIFT); | ||
1418 | vc5 &= ~(DROP_VTABLE_MISS | EN_VID_FFF_FWD); | ||
1419 | vc4 |= INGR_VID_CHK_VID_VIOL_IMP; | ||
1420 | } | ||
1421 | |||
1422 | core_writel(priv, vc0, CORE_VLAN_CTRL0); | ||
1423 | core_writel(priv, vc1, CORE_VLAN_CTRL1); | ||
1424 | core_writel(priv, 0, CORE_VLAN_CTRL3); | ||
1425 | core_writel(priv, vc4, CORE_VLAN_CTRL4); | ||
1426 | core_writel(priv, vc5, CORE_VLAN_CTRL5); | ||
1427 | core_writel(priv, mgmt, CORE_SWMODE); | ||
1428 | } | ||
1429 | |||
1430 | static void bcm_sf2_sw_configure_vlan(struct dsa_switch *ds) | ||
1431 | { | ||
1432 | struct bcm_sf2_priv *priv = ds_to_priv(ds); | ||
1433 | unsigned int port; | ||
1434 | |||
1435 | /* Clear all VLANs */ | ||
1436 | bcm_sf2_vlan_op(priv, ARLA_VTBL_CMD_CLEAR); | ||
1437 | |||
1438 | for (port = 0; port < priv->hw_params.num_ports; port++) { | ||
1439 | if (!((1 << port) & ds->enabled_port_mask)) | ||
1440 | continue; | ||
1441 | |||
1442 | core_writel(priv, 1, CORE_DEFAULT_1Q_TAG_P(port)); | ||
1443 | } | ||
1444 | } | ||
1445 | |||
1446 | static int bcm_sf2_sw_vlan_filtering(struct dsa_switch *ds, int port, | ||
1447 | bool vlan_filtering) | ||
1448 | { | ||
1449 | return 0; | ||
1450 | } | ||
1451 | |||
1452 | static int bcm_sf2_sw_vlan_prepare(struct dsa_switch *ds, int port, | ||
1453 | const struct switchdev_obj_port_vlan *vlan, | ||
1454 | struct switchdev_trans *trans) | ||
1455 | { | ||
1456 | struct bcm_sf2_priv *priv = ds_to_priv(ds); | ||
1457 | |||
1458 | bcm_sf2_enable_vlan(priv, true); | ||
1459 | |||
1460 | return 0; | ||
1461 | } | ||
1462 | |||
1463 | static void bcm_sf2_sw_vlan_add(struct dsa_switch *ds, int port, | ||
1464 | const struct switchdev_obj_port_vlan *vlan, | ||
1465 | struct switchdev_trans *trans) | ||
1466 | { | ||
1467 | struct bcm_sf2_priv *priv = ds_to_priv(ds); | ||
1468 | bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; | ||
1469 | bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; | ||
1470 | s8 cpu_port = ds->dst->cpu_port; | ||
1471 | struct bcm_sf2_vlan *vl; | ||
1472 | u16 vid; | ||
1473 | |||
1474 | for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) { | ||
1475 | vl = &priv->vlans[vid]; | ||
1476 | |||
1477 | bcm_sf2_get_vlan_entry(priv, vid, vl); | ||
1478 | |||
1479 | vl->members |= BIT(port) | BIT(cpu_port); | ||
1480 | if (untagged) | ||
1481 | vl->untag |= BIT(port) | BIT(cpu_port); | ||
1482 | else | ||
1483 | vl->untag &= ~(BIT(port) | BIT(cpu_port)); | ||
1484 | |||
1485 | bcm_sf2_set_vlan_entry(priv, vid, vl); | ||
1486 | bcm_sf2_sw_fast_age_vlan(priv, vid); | ||
1487 | } | ||
1488 | |||
1489 | if (pvid) { | ||
1490 | core_writel(priv, vlan->vid_end, CORE_DEFAULT_1Q_TAG_P(port)); | ||
1491 | core_writel(priv, vlan->vid_end, | ||
1492 | CORE_DEFAULT_1Q_TAG_P(cpu_port)); | ||
1493 | bcm_sf2_sw_fast_age_vlan(priv, vid); | ||
1494 | } | ||
1495 | } | ||
1496 | |||
1497 | static int bcm_sf2_sw_vlan_del(struct dsa_switch *ds, int port, | ||
1498 | const struct switchdev_obj_port_vlan *vlan) | ||
1499 | { | ||
1500 | struct bcm_sf2_priv *priv = ds_to_priv(ds); | ||
1501 | bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; | ||
1502 | s8 cpu_port = ds->dst->cpu_port; | ||
1503 | struct bcm_sf2_vlan *vl; | ||
1504 | u16 vid, pvid; | ||
1505 | int ret; | ||
1506 | |||
1507 | pvid = core_readl(priv, CORE_DEFAULT_1Q_TAG_P(port)); | ||
1508 | |||
1509 | for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) { | ||
1510 | vl = &priv->vlans[vid]; | ||
1511 | |||
1512 | ret = bcm_sf2_get_vlan_entry(priv, vid, vl); | ||
1513 | if (ret) | ||
1514 | return ret; | ||
1515 | |||
1516 | vl->members &= ~BIT(port); | ||
1517 | if ((vl->members & BIT(cpu_port)) == BIT(cpu_port)) | ||
1518 | vl->members = 0; | ||
1519 | if (pvid == vid) | ||
1520 | pvid = 0; | ||
1521 | if (untagged) { | ||
1522 | vl->untag &= ~BIT(port); | ||
1523 | if ((vl->untag & BIT(port)) == BIT(cpu_port)) | ||
1524 | vl->untag = 0; | ||
1525 | } | ||
1526 | |||
1527 | bcm_sf2_set_vlan_entry(priv, vid, vl); | ||
1528 | bcm_sf2_sw_fast_age_vlan(priv, vid); | ||
1529 | } | ||
1530 | |||
1531 | core_writel(priv, pvid, CORE_DEFAULT_1Q_TAG_P(port)); | ||
1532 | core_writel(priv, pvid, CORE_DEFAULT_1Q_TAG_P(cpu_port)); | ||
1533 | bcm_sf2_sw_fast_age_vlan(priv, vid); | ||
1534 | |||
1535 | return 0; | ||
1536 | } | ||
1537 | |||
1538 | static int bcm_sf2_sw_vlan_dump(struct dsa_switch *ds, int port, | ||
1539 | struct switchdev_obj_port_vlan *vlan, | ||
1540 | int (*cb)(struct switchdev_obj *obj)) | ||
1541 | { | ||
1542 | struct bcm_sf2_priv *priv = ds_to_priv(ds); | ||
1543 | struct bcm_sf2_port_status *p = &priv->port_sts[port]; | ||
1544 | struct bcm_sf2_vlan *vl; | ||
1545 | u16 vid, pvid; | ||
1546 | int err = 0; | ||
1547 | |||
1548 | pvid = core_readl(priv, CORE_DEFAULT_1Q_TAG_P(port)); | ||
1549 | |||
1550 | for (vid = 0; vid < VLAN_N_VID; vid++) { | ||
1551 | vl = &priv->vlans[vid]; | ||
1552 | |||
1553 | if (!(vl->members & BIT(port))) | ||
1554 | continue; | ||
1555 | |||
1556 | vlan->vid_begin = vlan->vid_end = vid; | ||
1557 | vlan->flags = 0; | ||
1558 | |||
1559 | if (vl->untag & BIT(port)) | ||
1560 | vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED; | ||
1561 | if (p->pvid == vid) | ||
1562 | vlan->flags |= BRIDGE_VLAN_INFO_PVID; | ||
1563 | |||
1564 | err = cb(&vlan->obj); | ||
1565 | if (err) | ||
1566 | break; | ||
1567 | } | ||
1568 | |||
1569 | return err; | ||
1570 | } | ||
1571 | |||
1315 | static int bcm_sf2_sw_setup(struct dsa_switch *ds) | 1572 | static int bcm_sf2_sw_setup(struct dsa_switch *ds) |
1316 | { | 1573 | { |
1317 | const char *reg_names[BCM_SF2_REGS_NUM] = BCM_SF2_REGS_NAME; | 1574 | const char *reg_names[BCM_SF2_REGS_NUM] = BCM_SF2_REGS_NAME; |
@@ -1403,6 +1660,8 @@ static int bcm_sf2_sw_setup(struct dsa_switch *ds) | |||
1403 | bcm_sf2_port_disable(ds, port, NULL); | 1660 | bcm_sf2_port_disable(ds, port, NULL); |
1404 | } | 1661 | } |
1405 | 1662 | ||
1663 | bcm_sf2_sw_configure_vlan(ds); | ||
1664 | |||
1406 | rev = reg_readl(priv, REG_SWITCH_REVISION); | 1665 | rev = reg_readl(priv, REG_SWITCH_REVISION); |
1407 | priv->hw_params.top_rev = (rev >> SWITCH_TOP_REV_SHIFT) & | 1666 | priv->hw_params.top_rev = (rev >> SWITCH_TOP_REV_SHIFT) & |
1408 | SWITCH_TOP_REV_MASK; | 1667 | SWITCH_TOP_REV_MASK; |
@@ -1457,6 +1716,11 @@ static struct dsa_switch_driver bcm_sf2_switch_driver = { | |||
1457 | .port_fdb_add = bcm_sf2_sw_fdb_add, | 1716 | .port_fdb_add = bcm_sf2_sw_fdb_add, |
1458 | .port_fdb_del = bcm_sf2_sw_fdb_del, | 1717 | .port_fdb_del = bcm_sf2_sw_fdb_del, |
1459 | .port_fdb_dump = bcm_sf2_sw_fdb_dump, | 1718 | .port_fdb_dump = bcm_sf2_sw_fdb_dump, |
1719 | .port_vlan_filtering = bcm_sf2_sw_vlan_filtering, | ||
1720 | .port_vlan_prepare = bcm_sf2_sw_vlan_prepare, | ||
1721 | .port_vlan_add = bcm_sf2_sw_vlan_add, | ||
1722 | .port_vlan_del = bcm_sf2_sw_vlan_del, | ||
1723 | .port_vlan_dump = bcm_sf2_sw_vlan_dump, | ||
1460 | }; | 1724 | }; |
1461 | 1725 | ||
1462 | static int __init bcm_sf2_init(void) | 1726 | static int __init bcm_sf2_init(void) |