diff options
-rw-r--r-- | drivers/net/dsa/bcm_sf2.c | 236 | ||||
-rw-r--r-- | drivers/net/dsa/bcm_sf2.h | 56 | ||||
-rw-r--r-- | drivers/net/dsa/bcm_sf2_regs.h | 43 |
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 */ | ||
561 | static 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 | |||
577 | static 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 | |||
596 | static 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 | |||
629 | static 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 | |||
680 | static 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 | |||
688 | static 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 | |||
697 | static 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 | |||
705 | static 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 | |||
724 | static 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 | |||
735 | static 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 | |||
753 | static 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 | |||
558 | static irqreturn_t bcm_sf2_switch_0_isr(int irq, void *dev_id) | 790 | static 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 | ||
1081 | static int __init bcm_sf2_init(void) | 1317 | static 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 | ||
55 | struct 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 | |||
64 | static 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 | |||
72 | static 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 | |||
83 | static 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 | |||
95 | static 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 | |||
53 | struct bcm_sf2_priv { | 109 | struct 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 |