aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFlorian Fainelli <f.fainelli@gmail.com>2015-10-23 14:38:07 -0400
committerDavid S. Miller <davem@davemloft.net>2015-10-26 21:14:02 -0400
commit680060d3e02d175516832e9af058ffe96ecc4cdc (patch)
tree7194e4b891e85bfb83ea559a9462c46de8fa1119
parentd1611c3aba11ffa281bdd027aace52f5a370b8c5 (diff)
net: dsa: bcm_sf2: Implement FDB operations
Add support for the FDB add, delete, and dump operations. The add and delete operations are implemented using directed ARL operations using the specified MAC address and consist in a read operation, write and readback operation. The dump operation consists in using the ARL search and software filtering entries which are not for the desired port. Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Reviewed-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/dsa/bcm_sf2.c236
-rw-r--r--drivers/net/dsa/bcm_sf2.h56
-rw-r--r--drivers/net/dsa/bcm_sf2_regs.h43
3 files changed, 335 insertions, 0 deletions
diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c
index 9d56515f4c4d..4f32b8a530bf 100644
--- a/drivers/net/dsa/bcm_sf2.c
+++ b/drivers/net/dsa/bcm_sf2.c
@@ -25,6 +25,8 @@
25#include <linux/ethtool.h> 25#include <linux/ethtool.h>
26#include <linux/if_bridge.h> 26#include <linux/if_bridge.h>
27#include <linux/brcmphy.h> 27#include <linux/brcmphy.h>
28#include <linux/etherdevice.h>
29#include <net/switchdev.h>
28 30
29#include "bcm_sf2.h" 31#include "bcm_sf2.h"
30#include "bcm_sf2_regs.h" 32#include "bcm_sf2_regs.h"
@@ -555,6 +557,236 @@ static int bcm_sf2_sw_br_set_stp_state(struct dsa_switch *ds, int port,
555 return 0; 557 return 0;
556} 558}
557 559
560/* Address Resolution Logic routines */
561static int bcm_sf2_arl_op_wait(struct bcm_sf2_priv *priv)
562{
563 unsigned int timeout = 10;
564 u32 reg;
565
566 do {
567 reg = core_readl(priv, CORE_ARLA_RWCTL);
568 if (!(reg & ARL_STRTDN))
569 return 0;
570
571 usleep_range(1000, 2000);
572 } while (timeout--);
573
574 return -ETIMEDOUT;
575}
576
577static int bcm_sf2_arl_rw_op(struct bcm_sf2_priv *priv, unsigned int op)
578{
579 u32 cmd;
580
581 if (op > ARL_RW)
582 return -EINVAL;
583
584 cmd = core_readl(priv, CORE_ARLA_RWCTL);
585 cmd &= ~IVL_SVL_SELECT;
586 cmd |= ARL_STRTDN;
587 if (op)
588 cmd |= ARL_RW;
589 else
590 cmd &= ~ARL_RW;
591 core_writel(priv, cmd, CORE_ARLA_RWCTL);
592
593 return bcm_sf2_arl_op_wait(priv);
594}
595
596static int bcm_sf2_arl_read(struct bcm_sf2_priv *priv, u64 mac,
597 u16 vid, struct bcm_sf2_arl_entry *ent, u8 *idx,
598 bool is_valid)
599{
600 unsigned int i;
601 int ret;
602
603 ret = bcm_sf2_arl_op_wait(priv);
604 if (ret)
605 return ret;
606
607 /* Read the 4 bins */
608 for (i = 0; i < 4; i++) {
609 u64 mac_vid;
610 u32 fwd_entry;
611
612 mac_vid = core_readq(priv, CORE_ARLA_MACVID_ENTRY(i));
613 fwd_entry = core_readl(priv, CORE_ARLA_FWD_ENTRY(i));
614 bcm_sf2_arl_to_entry(ent, mac_vid, fwd_entry);
615
616 if (ent->is_valid && is_valid) {
617 *idx = i;
618 return 0;
619 }
620
621 /* This is the MAC we just deleted */
622 if (!is_valid && (mac_vid & mac))
623 return 0;
624 }
625
626 return -ENOENT;
627}
628
629static int bcm_sf2_arl_op(struct bcm_sf2_priv *priv, int op, int port,
630 const unsigned char *addr, u16 vid, bool is_valid)
631{
632 struct bcm_sf2_arl_entry ent;
633 u32 fwd_entry;
634 u64 mac, mac_vid = 0;
635 u8 idx = 0;
636 int ret;
637
638 /* Convert the array into a 64-bit MAC */
639 mac = bcm_sf2_mac_to_u64(addr);
640
641 /* Perform a read for the given MAC and VID */
642 core_writeq(priv, mac, CORE_ARLA_MAC);
643 core_writel(priv, vid, CORE_ARLA_VID);
644
645 /* Issue a read operation for this MAC */
646 ret = bcm_sf2_arl_rw_op(priv, 1);
647 if (ret)
648 return ret;
649
650 ret = bcm_sf2_arl_read(priv, mac, vid, &ent, &idx, is_valid);
651 /* If this is a read, just finish now */
652 if (op)
653 return ret;
654
655 /* We could not find a matching MAC, so reset to a new entry */
656 if (ret) {
657 fwd_entry = 0;
658 idx = 0;
659 }
660
661 memset(&ent, 0, sizeof(ent));
662 ent.port = port;
663 ent.is_valid = is_valid;
664 ent.vid = vid;
665 ent.is_static = true;
666 memcpy(ent.mac, addr, ETH_ALEN);
667 bcm_sf2_arl_from_entry(&mac_vid, &fwd_entry, &ent);
668
669 core_writeq(priv, mac_vid, CORE_ARLA_MACVID_ENTRY(idx));
670 core_writel(priv, fwd_entry, CORE_ARLA_FWD_ENTRY(idx));
671
672 ret = bcm_sf2_arl_rw_op(priv, 0);
673 if (ret)
674 return ret;
675
676 /* Re-read the entry to check */
677 return bcm_sf2_arl_read(priv, mac, vid, &ent, &idx, is_valid);
678}
679
680static int bcm_sf2_sw_fdb_prepare(struct dsa_switch *ds, int port,
681 const struct switchdev_obj_port_fdb *fdb,
682 struct switchdev_trans *trans)
683{
684 /* We do not need to do anything specific here yet */
685 return 0;
686}
687
688static int bcm_sf2_sw_fdb_add(struct dsa_switch *ds, int port,
689 const struct switchdev_obj_port_fdb *fdb,
690 struct switchdev_trans *trans)
691{
692 struct bcm_sf2_priv *priv = ds_to_priv(ds);
693
694 return bcm_sf2_arl_op(priv, 0, port, fdb->addr, fdb->vid, true);
695}
696
697static int bcm_sf2_sw_fdb_del(struct dsa_switch *ds, int port,
698 const struct switchdev_obj_port_fdb *fdb)
699{
700 struct bcm_sf2_priv *priv = ds_to_priv(ds);
701
702 return bcm_sf2_arl_op(priv, 0, port, fdb->addr, fdb->vid, false);
703}
704
705static int bcm_sf2_arl_search_wait(struct bcm_sf2_priv *priv)
706{
707 unsigned timeout = 1000;
708 u32 reg;
709
710 do {
711 reg = core_readl(priv, CORE_ARLA_SRCH_CTL);
712 if (!(reg & ARLA_SRCH_STDN))
713 return 0;
714
715 if (reg & ARLA_SRCH_VLID)
716 return 0;
717
718 usleep_range(1000, 2000);
719 } while (timeout--);
720
721 return -ETIMEDOUT;
722}
723
724static void bcm_sf2_arl_search_rd(struct bcm_sf2_priv *priv, u8 idx,
725 struct bcm_sf2_arl_entry *ent)
726{
727 u64 mac_vid;
728 u32 fwd_entry;
729
730 mac_vid = core_readq(priv, CORE_ARLA_SRCH_RSLT_MACVID(idx));
731 fwd_entry = core_readl(priv, CORE_ARLA_SRCH_RSLT(idx));
732 bcm_sf2_arl_to_entry(ent, mac_vid, fwd_entry);
733}
734
735static int bcm_sf2_sw_fdb_copy(struct net_device *dev, int port,
736 const struct bcm_sf2_arl_entry *ent,
737 struct switchdev_obj_port_fdb *fdb,
738 int (*cb)(struct switchdev_obj *obj))
739{
740 if (!ent->is_valid)
741 return 0;
742
743 if (port != ent->port)
744 return 0;
745
746 ether_addr_copy(fdb->addr, ent->mac);
747 fdb->vid = ent->vid;
748 fdb->ndm_state = ent->is_static ? NUD_NOARP : NUD_REACHABLE;
749
750 return cb(&fdb->obj);
751}
752
753static int bcm_sf2_sw_fdb_dump(struct dsa_switch *ds, int port,
754 struct switchdev_obj_port_fdb *fdb,
755 int (*cb)(struct switchdev_obj *obj))
756{
757 struct bcm_sf2_priv *priv = ds_to_priv(ds);
758 struct net_device *dev = ds->ports[port];
759 struct bcm_sf2_arl_entry results[2];
760 unsigned int count = 0;
761 int ret;
762
763 /* Start search operation */
764 core_writel(priv, ARLA_SRCH_STDN, CORE_ARLA_SRCH_CTL);
765
766 do {
767 ret = bcm_sf2_arl_search_wait(priv);
768 if (ret)
769 return ret;
770
771 /* Read both entries, then return their values back */
772 bcm_sf2_arl_search_rd(priv, 0, &results[0]);
773 ret = bcm_sf2_sw_fdb_copy(dev, port, &results[0], fdb, cb);
774 if (ret)
775 return ret;
776
777 bcm_sf2_arl_search_rd(priv, 1, &results[1]);
778 ret = bcm_sf2_sw_fdb_copy(dev, port, &results[1], fdb, cb);
779 if (ret)
780 return ret;
781
782 if (!results[0].is_valid && !results[1].is_valid)
783 break;
784
785 } while (count++ < CORE_ARLA_NUM_ENTRIES);
786
787 return 0;
788}
789
558static irqreturn_t bcm_sf2_switch_0_isr(int irq, void *dev_id) 790static irqreturn_t bcm_sf2_switch_0_isr(int irq, void *dev_id)
559{ 791{
560 struct bcm_sf2_priv *priv = dev_id; 792 struct bcm_sf2_priv *priv = dev_id;
@@ -1076,6 +1308,10 @@ static struct dsa_switch_driver bcm_sf2_switch_driver = {
1076 .port_join_bridge = bcm_sf2_sw_br_join, 1308 .port_join_bridge = bcm_sf2_sw_br_join,
1077 .port_leave_bridge = bcm_sf2_sw_br_leave, 1309 .port_leave_bridge = bcm_sf2_sw_br_leave,
1078 .port_stp_update = bcm_sf2_sw_br_set_stp_state, 1310 .port_stp_update = bcm_sf2_sw_br_set_stp_state,
1311 .port_fdb_prepare = bcm_sf2_sw_fdb_prepare,
1312 .port_fdb_add = bcm_sf2_sw_fdb_add,
1313 .port_fdb_del = bcm_sf2_sw_fdb_del,
1314 .port_fdb_dump = bcm_sf2_sw_fdb_dump,
1079}; 1315};
1080 1316
1081static int __init bcm_sf2_init(void) 1317static int __init bcm_sf2_init(void)
diff --git a/drivers/net/dsa/bcm_sf2.h b/drivers/net/dsa/bcm_sf2.h
index 789d7b7737da..cc98abc0aaf3 100644
--- a/drivers/net/dsa/bcm_sf2.h
+++ b/drivers/net/dsa/bcm_sf2.h
@@ -19,6 +19,8 @@
19#include <linux/mutex.h> 19#include <linux/mutex.h>
20#include <linux/mii.h> 20#include <linux/mii.h>
21#include <linux/ethtool.h> 21#include <linux/ethtool.h>
22#include <linux/types.h>
23#include <linux/bitops.h>
22 24
23#include <net/dsa.h> 25#include <net/dsa.h>
24 26
@@ -50,6 +52,60 @@ struct bcm_sf2_port_status {
50 u32 vlan_ctl_mask; 52 u32 vlan_ctl_mask;
51}; 53};
52 54
55struct bcm_sf2_arl_entry {
56 u8 port;
57 u8 mac[ETH_ALEN];
58 u16 vid;
59 u8 is_valid:1;
60 u8 is_age:1;
61 u8 is_static:1;
62};
63
64static inline void bcm_sf2_mac_from_u64(u64 src, u8 *dst)
65{
66 unsigned int i;
67
68 for (i = 0; i < ETH_ALEN; i++)
69 dst[ETH_ALEN - 1 - i] = (src >> (8 * i)) & 0xff;
70}
71
72static inline u64 bcm_sf2_mac_to_u64(const u8 *src)
73{
74 unsigned int i;
75 u64 dst = 0;
76
77 for (i = 0; i < ETH_ALEN; i++)
78 dst |= (u64)src[ETH_ALEN - 1 - i] << (8 * i);
79
80 return dst;
81}
82
83static inline void bcm_sf2_arl_to_entry(struct bcm_sf2_arl_entry *ent,
84 u64 mac_vid, u32 fwd_entry)
85{
86 memset(ent, 0, sizeof(*ent));
87 ent->port = fwd_entry & PORTID_MASK;
88 ent->is_valid = !!(fwd_entry & ARL_VALID);
89 ent->is_age = !!(fwd_entry & ARL_AGE);
90 ent->is_static = !!(fwd_entry & ARL_STATIC);
91 bcm_sf2_mac_from_u64(mac_vid, ent->mac);
92 ent->vid = mac_vid >> VID_SHIFT;
93}
94
95static inline void bcm_sf2_arl_from_entry(u64 *mac_vid, u32 *fwd_entry,
96 const struct bcm_sf2_arl_entry *ent)
97{
98 *mac_vid = bcm_sf2_mac_to_u64(ent->mac);
99 *mac_vid |= (u64)(ent->vid & VID_MASK) << VID_SHIFT;
100 *fwd_entry = ent->port & PORTID_MASK;
101 if (ent->is_valid)
102 *fwd_entry |= ARL_VALID;
103 if (ent->is_static)
104 *fwd_entry |= ARL_STATIC;
105 if (ent->is_age)
106 *fwd_entry |= ARL_AGE;
107}
108
53struct bcm_sf2_priv { 109struct bcm_sf2_priv {
54 /* Base registers, keep those in order with BCM_SF2_REGS_NAME */ 110 /* Base registers, keep those in order with BCM_SF2_REGS_NAME */
55 void __iomem *core; 111 void __iomem *core;
diff --git a/drivers/net/dsa/bcm_sf2_regs.h b/drivers/net/dsa/bcm_sf2_regs.h
index fa4e6e78c9ea..97780d43b5c0 100644
--- a/drivers/net/dsa/bcm_sf2_regs.h
+++ b/drivers/net/dsa/bcm_sf2_regs.h
@@ -231,6 +231,49 @@
231#define CORE_BRCM_HDR_RX_DIS 0x0980 231#define CORE_BRCM_HDR_RX_DIS 0x0980
232#define CORE_BRCM_HDR_TX_DIS 0x0988 232#define CORE_BRCM_HDR_TX_DIS 0x0988
233 233
234#define CORE_ARLA_NUM_ENTRIES 1024
235
236#define CORE_ARLA_RWCTL 0x1400
237#define ARL_RW (1 << 0)
238#define IVL_SVL_SELECT (1 << 6)
239#define ARL_STRTDN (1 << 7)
240
241#define CORE_ARLA_MAC 0x1408
242#define CORE_ARLA_VID 0x1420
243#define ARLA_VIDTAB_INDX_MASK 0x1fff
244
245#define CORE_ARLA_MACVID0 0x1440
246#define MAC_MASK 0xffffffffff
247#define VID_SHIFT 48
248#define VID_MASK 0xfff
249
250#define CORE_ARLA_FWD_ENTRY0 0x1460
251#define PORTID_MASK 0x1ff
252#define ARL_CON_SHIFT 9
253#define ARL_CON_MASK 0x3
254#define ARL_PRI_SHIFT 11
255#define ARL_PRI_MASK 0x7
256#define ARL_AGE (1 << 14)
257#define ARL_STATIC (1 << 15)
258#define ARL_VALID (1 << 16)
259
260#define CORE_ARLA_MACVID_ENTRY(x) (CORE_ARLA_MACVID0 + ((x) * 0x40))
261#define CORE_ARLA_FWD_ENTRY(x) (CORE_ARLA_FWD_ENTRY0 + ((x) * 0x40))
262
263#define CORE_ARLA_SRCH_CTL 0x1540
264#define ARLA_SRCH_VLID (1 << 0)
265#define IVL_SVL_SELECT (1 << 6)
266#define ARLA_SRCH_STDN (1 << 7)
267
268#define CORE_ARLA_SRCH_ADR 0x1544
269#define ARLA_SRCH_ADR_VALID (1 << 15)
270
271#define CORE_ARLA_SRCH_RSLT_0_MACVID 0x1580
272#define CORE_ARLA_SRCH_RSLT_0 0x15a0
273
274#define CORE_ARLA_SRCH_RSLT_MACVID(x) (CORE_ARLA_SRCH_RSLT_0_MACVID + ((x) * 0x40))
275#define CORE_ARLA_SRCH_RSLT(x) (CORE_ARLA_SRCH_RSLT_0 + ((x) * 0x40))
276
234#define CORE_MEM_PSM_VDD_CTRL 0x2380 277#define CORE_MEM_PSM_VDD_CTRL 0x2380
235#define P_TXQ_PSM_VDD_SHIFT 2 278#define P_TXQ_PSM_VDD_SHIFT 2
236#define P_TXQ_PSM_VDD_MASK 0x3 279#define P_TXQ_PSM_VDD_MASK 0x3