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