aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuenter Roeck <linux@roeck-us.net>2015-03-26 21:36:35 -0400
committerDavid S. Miller <davem@davemloft.net>2015-03-29 16:23:53 -0400
commitfacd95b2e0ec02ccf6d13f4d08c060628dca0862 (patch)
tree114bf401b08530583b16839c1c22f9fe220fa4de
parentb0019b70d02bae9757b5b9237f38fb8cf2899009 (diff)
net: dsa: mv88e6xxx: Add Hardware bridging support
Bridge support is similar for all chips supported by the mv88e6xxx code, so add the code there. Reviewed-by: Andrew Lunn <andrew@lunn.ch> Tested-by: Andrew Lunn <andrew@lunn.ch> Signed-off-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/dsa/mv88e6xxx.c271
-rw-r--r--drivers/net/dsa/mv88e6xxx.h28
2 files changed, 292 insertions, 7 deletions
diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c
index d8f13327a438..17aa74f64233 100644
--- a/drivers/net/dsa/mv88e6xxx.c
+++ b/drivers/net/dsa/mv88e6xxx.c
@@ -9,6 +9,7 @@
9 */ 9 */
10 10
11#include <linux/delay.h> 11#include <linux/delay.h>
12#include <linux/if_bridge.h>
12#include <linux/jiffies.h> 13#include <linux/jiffies.h>
13#include <linux/list.h> 14#include <linux/list.h>
14#include <linux/module.h> 15#include <linux/module.h>
@@ -644,6 +645,31 @@ int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds)
644 return mv88e6xxx_wait(ds, REG_GLOBAL2, 0x14, 0x8000); 645 return mv88e6xxx_wait(ds, REG_GLOBAL2, 0x14, 0x8000);
645} 646}
646 647
648/* Must be called with SMI lock held */
649static int _mv88e6xxx_wait(struct dsa_switch *ds, int reg, int offset, u16 mask)
650{
651 unsigned long timeout = jiffies + HZ / 10;
652
653 while (time_before(jiffies, timeout)) {
654 int ret;
655
656 ret = _mv88e6xxx_reg_read(ds, reg, offset);
657 if (ret < 0)
658 return ret;
659 if (!(ret & mask))
660 return 0;
661
662 usleep_range(1000, 2000);
663 }
664 return -ETIMEDOUT;
665}
666
667/* Must be called with SMI lock held */
668static int _mv88e6xxx_atu_wait(struct dsa_switch *ds)
669{
670 return _mv88e6xxx_wait(ds, REG_GLOBAL, 0x0b, ATU_BUSY);
671}
672
647int mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int addr, int regnum) 673int mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int addr, int regnum)
648{ 674{
649 int ret; 675 int ret;
@@ -717,10 +743,236 @@ int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
717 return 0; 743 return 0;
718} 744}
719 745
746static int _mv88e6xxx_atu_cmd(struct dsa_switch *ds, int fid, u16 cmd)
747{
748 int ret;
749
750 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x01, fid);
751 if (ret < 0)
752 return ret;
753
754 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x0b, cmd);
755 if (ret < 0)
756 return ret;
757
758 return _mv88e6xxx_atu_wait(ds);
759}
760
761static int _mv88e6xxx_flush_fid(struct dsa_switch *ds, int fid)
762{
763 int ret;
764
765 ret = _mv88e6xxx_atu_wait(ds);
766 if (ret < 0)
767 return ret;
768
769 return _mv88e6xxx_atu_cmd(ds, fid, ATU_CMD_FLUSH_NONSTATIC_FID);
770}
771
772static int mv88e6xxx_set_port_state(struct dsa_switch *ds, int port, u8 state)
773{
774 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
775 int reg, ret;
776 u8 oldstate;
777
778 mutex_lock(&ps->smi_mutex);
779
780 reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), 0x04);
781 if (reg < 0)
782 goto abort;
783
784 oldstate = reg & PSTATE_MASK;
785 if (oldstate != state) {
786 /* Flush forwarding database if we're moving a port
787 * from Learning or Forwarding state to Disabled or
788 * Blocking or Listening state.
789 */
790 if (oldstate >= PSTATE_LEARNING && state <= PSTATE_BLOCKING) {
791 ret = _mv88e6xxx_flush_fid(ds, ps->fid[port]);
792 if (ret)
793 goto abort;
794 }
795 reg = (reg & ~PSTATE_MASK) | state;
796 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), 0x04, reg);
797 }
798
799abort:
800 mutex_unlock(&ps->smi_mutex);
801 return ret;
802}
803
804/* Must be called with smi lock held */
805static int _mv88e6xxx_update_port_config(struct dsa_switch *ds, int port)
806{
807 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
808 u8 fid = ps->fid[port];
809 u16 reg = fid << 12;
810
811 if (dsa_is_cpu_port(ds, port))
812 reg |= ds->phys_port_mask;
813 else
814 reg |= (ps->bridge_mask[fid] |
815 (1 << dsa_upstream_port(ds))) & ~(1 << port);
816
817 return _mv88e6xxx_reg_write(ds, REG_PORT(port), 0x06, reg);
818}
819
820/* Must be called with smi lock held */
821static int _mv88e6xxx_update_bridge_config(struct dsa_switch *ds, int fid)
822{
823 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
824 int port;
825 u32 mask;
826 int ret;
827
828 mask = ds->phys_port_mask;
829 while (mask) {
830 port = __ffs(mask);
831 mask &= ~(1 << port);
832 if (ps->fid[port] != fid)
833 continue;
834
835 ret = _mv88e6xxx_update_port_config(ds, port);
836 if (ret)
837 return ret;
838 }
839
840 return _mv88e6xxx_flush_fid(ds, fid);
841}
842
843/* Bridge handling functions */
844
845int mv88e6xxx_join_bridge(struct dsa_switch *ds, int port, u32 br_port_mask)
846{
847 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
848 int ret = 0;
849 u32 nmask;
850 int fid;
851
852 /* If the bridge group is not empty, join that group.
853 * Otherwise create a new group.
854 */
855 fid = ps->fid[port];
856 nmask = br_port_mask & ~(1 << port);
857 if (nmask)
858 fid = ps->fid[__ffs(nmask)];
859
860 nmask = ps->bridge_mask[fid] | (1 << port);
861 if (nmask != br_port_mask) {
862 netdev_err(ds->ports[port],
863 "join: Bridge port mask mismatch fid=%d mask=0x%x expected 0x%x\n",
864 fid, br_port_mask, nmask);
865 return -EINVAL;
866 }
867
868 mutex_lock(&ps->smi_mutex);
869
870 ps->bridge_mask[fid] = br_port_mask;
871
872 if (fid != ps->fid[port]) {
873 ps->fid_mask |= 1 << ps->fid[port];
874 ps->fid[port] = fid;
875 ret = _mv88e6xxx_update_bridge_config(ds, fid);
876 }
877
878 mutex_unlock(&ps->smi_mutex);
879
880 return ret;
881}
882
883int mv88e6xxx_leave_bridge(struct dsa_switch *ds, int port, u32 br_port_mask)
884{
885 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
886 u8 fid, newfid;
887 int ret;
888
889 fid = ps->fid[port];
890
891 if (ps->bridge_mask[fid] != br_port_mask) {
892 netdev_err(ds->ports[port],
893 "leave: Bridge port mask mismatch fid=%d mask=0x%x expected 0x%x\n",
894 fid, br_port_mask, ps->bridge_mask[fid]);
895 return -EINVAL;
896 }
897
898 /* If the port was the last port of a bridge, we are done.
899 * Otherwise assign a new fid to the port, and fix up
900 * the bridge configuration.
901 */
902 if (br_port_mask == (1 << port))
903 return 0;
904
905 mutex_lock(&ps->smi_mutex);
906
907 newfid = __ffs(ps->fid_mask);
908 ps->fid[port] = newfid;
909 ps->fid_mask &= (1 << newfid);
910 ps->bridge_mask[fid] &= ~(1 << port);
911 ps->bridge_mask[newfid] = 1 << port;
912
913 ret = _mv88e6xxx_update_bridge_config(ds, fid);
914 if (!ret)
915 ret = _mv88e6xxx_update_bridge_config(ds, newfid);
916
917 mutex_unlock(&ps->smi_mutex);
918
919 return ret;
920}
921
922int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state)
923{
924 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
925 int stp_state;
926
927 switch (state) {
928 case BR_STATE_DISABLED:
929 stp_state = PSTATE_DISABLED;
930 break;
931 case BR_STATE_BLOCKING:
932 case BR_STATE_LISTENING:
933 stp_state = PSTATE_BLOCKING;
934 break;
935 case BR_STATE_LEARNING:
936 stp_state = PSTATE_LEARNING;
937 break;
938 case BR_STATE_FORWARDING:
939 default:
940 stp_state = PSTATE_FORWARDING;
941 break;
942 }
943
944 netdev_dbg(ds->ports[port], "port state %d [%d]\n", state, stp_state);
945
946 /* mv88e6xxx_port_stp_update may be called with softirqs disabled,
947 * so we can not update the port state directly but need to schedule it.
948 */
949 ps->port_state[port] = stp_state;
950 set_bit(port, &ps->port_state_update_mask);
951 schedule_work(&ps->bridge_work);
952
953 return 0;
954}
955
956static void mv88e6xxx_bridge_work(struct work_struct *work)
957{
958 struct mv88e6xxx_priv_state *ps;
959 struct dsa_switch *ds;
960 int port;
961
962 ps = container_of(work, struct mv88e6xxx_priv_state, bridge_work);
963 ds = ((struct dsa_switch *)ps) - 1;
964
965 while (ps->port_state_update_mask) {
966 port = __ffs(ps->port_state_update_mask);
967 clear_bit(port, &ps->port_state_update_mask);
968 mv88e6xxx_set_port_state(ds, port, ps->port_state[port]);
969 }
970}
971
720int mv88e6xxx_setup_port_common(struct dsa_switch *ds, int port) 972int mv88e6xxx_setup_port_common(struct dsa_switch *ds, int port)
721{ 973{
722 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); 974 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
723 int ret, reg; 975 int ret, fid;
724 976
725 mutex_lock(&ps->smi_mutex); 977 mutex_lock(&ps->smi_mutex);
726 978
@@ -736,13 +988,14 @@ int mv88e6xxx_setup_port_common(struct dsa_switch *ds, int port)
736 * ports, and allow each of the 'real' ports to only talk to 988 * ports, and allow each of the 'real' ports to only talk to
737 * the upstream port. 989 * the upstream port.
738 */ 990 */
739 reg = (port & 0xf) << 12; 991 fid = __ffs(ps->fid_mask);
740 if (dsa_is_cpu_port(ds, port)) 992 ps->fid[port] = fid;
741 reg |= ds->phys_port_mask; 993 ps->fid_mask &= ~(1 << fid);
742 else 994
743 reg |= 1 << dsa_upstream_port(ds); 995 if (!dsa_is_cpu_port(ds, port))
996 ps->bridge_mask[fid] = 1 << port;
744 997
745 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), 0x06, reg); 998 ret = _mv88e6xxx_update_port_config(ds, port);
746 if (ret) 999 if (ret)
747 goto abort; 1000 goto abort;
748 1001
@@ -763,6 +1016,10 @@ int mv88e6xxx_setup_common(struct dsa_switch *ds)
763 mutex_init(&ps->stats_mutex); 1016 mutex_init(&ps->stats_mutex);
764 mutex_init(&ps->phy_mutex); 1017 mutex_init(&ps->phy_mutex);
765 1018
1019 ps->fid_mask = (1 << DSA_MAX_PORTS) - 1;
1020
1021 INIT_WORK(&ps->bridge_work, mv88e6xxx_bridge_work);
1022
766 return 0; 1023 return 0;
767} 1024}
768 1025
diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h
index a4df4968594e..8e215ebc8d34 100644
--- a/drivers/net/dsa/mv88e6xxx.h
+++ b/drivers/net/dsa/mv88e6xxx.h
@@ -15,6 +15,20 @@
15#define REG_GLOBAL 0x1b 15#define REG_GLOBAL 0x1b
16#define REG_GLOBAL2 0x1c 16#define REG_GLOBAL2 0x1c
17 17
18/* ATU commands */
19
20#define ATU_BUSY 0x8000
21
22#define ATU_CMD_FLUSH_NONSTATIC_FID (ATU_BUSY | 0x6000)
23
24/* port states */
25
26#define PSTATE_MASK 0x03
27#define PSTATE_DISABLED 0x00
28#define PSTATE_BLOCKING 0x01
29#define PSTATE_LEARNING 0x02
30#define PSTATE_FORWARDING 0x03
31
18struct mv88e6xxx_priv_state { 32struct mv88e6xxx_priv_state {
19 /* When using multi-chip addressing, this mutex protects 33 /* When using multi-chip addressing, this mutex protects
20 * access to the indirect access registers. (In single-chip 34 * access to the indirect access registers. (In single-chip
@@ -49,6 +63,17 @@ struct mv88e6xxx_priv_state {
49 struct mutex eeprom_mutex; 63 struct mutex eeprom_mutex;
50 64
51 int id; /* switch product id */ 65 int id; /* switch product id */
66
67 /* hw bridging */
68
69 u32 fid_mask;
70 u8 fid[DSA_MAX_PORTS];
71 u16 bridge_mask[DSA_MAX_PORTS];
72
73 unsigned long port_state_update_mask;
74 u8 port_state[DSA_MAX_PORTS];
75
76 struct work_struct bridge_work;
52}; 77};
53 78
54struct mv88e6xxx_hw_stat { 79struct mv88e6xxx_hw_stat {
@@ -93,6 +118,9 @@ int mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int addr, int regnum,
93int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e); 118int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e);
94int mv88e6xxx_set_eee(struct dsa_switch *ds, int port, 119int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
95 struct phy_device *phydev, struct ethtool_eee *e); 120 struct phy_device *phydev, struct ethtool_eee *e);
121int mv88e6xxx_join_bridge(struct dsa_switch *ds, int port, u32 br_port_mask);
122int mv88e6xxx_leave_bridge(struct dsa_switch *ds, int port, u32 br_port_mask);
123int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state);
96 124
97extern struct dsa_switch_driver mv88e6131_switch_driver; 125extern struct dsa_switch_driver mv88e6131_switch_driver;
98extern struct dsa_switch_driver mv88e6123_61_65_switch_driver; 126extern struct dsa_switch_driver mv88e6123_61_65_switch_driver;