diff options
author | Guenter Roeck <linux@roeck-us.net> | 2015-03-26 21:36:38 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-03-29 16:23:54 -0400 |
commit | defb05b9b9b4376aeb9db366cdb6ffa5ef0fa176 (patch) | |
tree | 96414f84528f3dbec05d1280d878ebecb8c10945 /drivers | |
parent | 339d82626d225e9b876665e4e89b7eb123e96b3d (diff) |
net: dsa: mv88e6xxx: Add support for fdb_add, fdb_del, and fdb_getnext
No vlan support at this time.
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>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/dsa/mv88e6xxx.c | 136 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx.h | 16 |
2 files changed, 152 insertions, 0 deletions
diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c index 17aa74f64233..038802229e32 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/etherdevice.h> | ||
12 | #include <linux/if_bridge.h> | 13 | #include <linux/if_bridge.h> |
13 | #include <linux/jiffies.h> | 14 | #include <linux/jiffies.h> |
14 | #include <linux/list.h> | 15 | #include <linux/list.h> |
@@ -953,6 +954,141 @@ int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state) | |||
953 | return 0; | 954 | return 0; |
954 | } | 955 | } |
955 | 956 | ||
957 | static int __mv88e6xxx_write_addr(struct dsa_switch *ds, | ||
958 | const unsigned char *addr) | ||
959 | { | ||
960 | int i, ret; | ||
961 | |||
962 | for (i = 0; i < 3; i++) { | ||
963 | ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x0d + i, | ||
964 | (addr[i * 2] << 8) | addr[i * 2 + 1]); | ||
965 | if (ret < 0) | ||
966 | return ret; | ||
967 | } | ||
968 | |||
969 | return 0; | ||
970 | } | ||
971 | |||
972 | static int __mv88e6xxx_read_addr(struct dsa_switch *ds, unsigned char *addr) | ||
973 | { | ||
974 | int i, ret; | ||
975 | |||
976 | for (i = 0; i < 3; i++) { | ||
977 | ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, 0x0d + i); | ||
978 | if (ret < 0) | ||
979 | return ret; | ||
980 | addr[i * 2] = ret >> 8; | ||
981 | addr[i * 2 + 1] = ret & 0xff; | ||
982 | } | ||
983 | |||
984 | return 0; | ||
985 | } | ||
986 | |||
987 | static int __mv88e6xxx_port_fdb_cmd(struct dsa_switch *ds, int port, | ||
988 | const unsigned char *addr, int state) | ||
989 | { | ||
990 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | ||
991 | u8 fid = ps->fid[port]; | ||
992 | int ret; | ||
993 | |||
994 | ret = _mv88e6xxx_atu_wait(ds); | ||
995 | if (ret < 0) | ||
996 | return ret; | ||
997 | |||
998 | ret = __mv88e6xxx_write_addr(ds, addr); | ||
999 | if (ret < 0) | ||
1000 | return ret; | ||
1001 | |||
1002 | ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x0c, | ||
1003 | (0x10 << port) | state); | ||
1004 | if (ret) | ||
1005 | return ret; | ||
1006 | |||
1007 | ret = _mv88e6xxx_atu_cmd(ds, fid, ATU_CMD_LOAD_FID); | ||
1008 | |||
1009 | return ret; | ||
1010 | } | ||
1011 | |||
1012 | int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, | ||
1013 | const unsigned char *addr, u16 vid) | ||
1014 | { | ||
1015 | int state = is_multicast_ether_addr(addr) ? | ||
1016 | FDB_STATE_MC_STATIC : FDB_STATE_STATIC; | ||
1017 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | ||
1018 | int ret; | ||
1019 | |||
1020 | mutex_lock(&ps->smi_mutex); | ||
1021 | ret = __mv88e6xxx_port_fdb_cmd(ds, port, addr, state); | ||
1022 | mutex_unlock(&ps->smi_mutex); | ||
1023 | |||
1024 | return ret; | ||
1025 | } | ||
1026 | |||
1027 | int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, | ||
1028 | const unsigned char *addr, u16 vid) | ||
1029 | { | ||
1030 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | ||
1031 | int ret; | ||
1032 | |||
1033 | mutex_lock(&ps->smi_mutex); | ||
1034 | ret = __mv88e6xxx_port_fdb_cmd(ds, port, addr, FDB_STATE_UNUSED); | ||
1035 | mutex_unlock(&ps->smi_mutex); | ||
1036 | |||
1037 | return ret; | ||
1038 | } | ||
1039 | |||
1040 | static int __mv88e6xxx_port_getnext(struct dsa_switch *ds, int port, | ||
1041 | unsigned char *addr, bool *is_static) | ||
1042 | { | ||
1043 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | ||
1044 | u8 fid = ps->fid[port]; | ||
1045 | int ret, state; | ||
1046 | |||
1047 | ret = _mv88e6xxx_atu_wait(ds); | ||
1048 | if (ret < 0) | ||
1049 | return ret; | ||
1050 | |||
1051 | ret = __mv88e6xxx_write_addr(ds, addr); | ||
1052 | if (ret < 0) | ||
1053 | return ret; | ||
1054 | |||
1055 | do { | ||
1056 | ret = _mv88e6xxx_atu_cmd(ds, fid, ATU_CMD_GETNEXT_FID); | ||
1057 | if (ret < 0) | ||
1058 | return ret; | ||
1059 | |||
1060 | ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, 0x0c); | ||
1061 | if (ret < 0) | ||
1062 | return ret; | ||
1063 | state = ret & FDB_STATE_MASK; | ||
1064 | if (state == FDB_STATE_UNUSED) | ||
1065 | return -ENOENT; | ||
1066 | } while (!(((ret >> 4) & 0xff) & (1 << port))); | ||
1067 | |||
1068 | ret = __mv88e6xxx_read_addr(ds, addr); | ||
1069 | if (ret < 0) | ||
1070 | return ret; | ||
1071 | |||
1072 | *is_static = state == (is_multicast_ether_addr(addr) ? | ||
1073 | FDB_STATE_MC_STATIC : FDB_STATE_STATIC); | ||
1074 | |||
1075 | return 0; | ||
1076 | } | ||
1077 | |||
1078 | /* get next entry for port */ | ||
1079 | int mv88e6xxx_port_fdb_getnext(struct dsa_switch *ds, int port, | ||
1080 | unsigned char *addr, bool *is_static) | ||
1081 | { | ||
1082 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | ||
1083 | int ret; | ||
1084 | |||
1085 | mutex_lock(&ps->smi_mutex); | ||
1086 | ret = __mv88e6xxx_port_getnext(ds, port, addr, is_static); | ||
1087 | mutex_unlock(&ps->smi_mutex); | ||
1088 | |||
1089 | return ret; | ||
1090 | } | ||
1091 | |||
956 | static void mv88e6xxx_bridge_work(struct work_struct *work) | 1092 | static void mv88e6xxx_bridge_work(struct work_struct *work) |
957 | { | 1093 | { |
958 | struct mv88e6xxx_priv_state *ps; | 1094 | struct mv88e6xxx_priv_state *ps; |
diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h index 8e215ebc8d34..aaf239aba726 100644 --- a/drivers/net/dsa/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx.h | |||
@@ -19,6 +19,8 @@ | |||
19 | 19 | ||
20 | #define ATU_BUSY 0x8000 | 20 | #define ATU_BUSY 0x8000 |
21 | 21 | ||
22 | #define ATU_CMD_LOAD_FID (ATU_BUSY | 0x3000) | ||
23 | #define ATU_CMD_GETNEXT_FID (ATU_BUSY | 0x4000) | ||
22 | #define ATU_CMD_FLUSH_NONSTATIC_FID (ATU_BUSY | 0x6000) | 24 | #define ATU_CMD_FLUSH_NONSTATIC_FID (ATU_BUSY | 0x6000) |
23 | 25 | ||
24 | /* port states */ | 26 | /* port states */ |
@@ -29,6 +31,14 @@ | |||
29 | #define PSTATE_LEARNING 0x02 | 31 | #define PSTATE_LEARNING 0x02 |
30 | #define PSTATE_FORWARDING 0x03 | 32 | #define PSTATE_FORWARDING 0x03 |
31 | 33 | ||
34 | /* FDB states */ | ||
35 | |||
36 | #define FDB_STATE_MASK 0x0f | ||
37 | |||
38 | #define FDB_STATE_UNUSED 0x00 | ||
39 | #define FDB_STATE_MC_STATIC 0x07 /* static multicast */ | ||
40 | #define FDB_STATE_STATIC 0x0e /* static unicast */ | ||
41 | |||
32 | struct mv88e6xxx_priv_state { | 42 | struct mv88e6xxx_priv_state { |
33 | /* When using multi-chip addressing, this mutex protects | 43 | /* When using multi-chip addressing, this mutex protects |
34 | * access to the indirect access registers. (In single-chip | 44 | * access to the indirect access registers. (In single-chip |
@@ -121,6 +131,12 @@ int mv88e6xxx_set_eee(struct dsa_switch *ds, int port, | |||
121 | int mv88e6xxx_join_bridge(struct dsa_switch *ds, int port, u32 br_port_mask); | 131 | int mv88e6xxx_join_bridge(struct dsa_switch *ds, int port, u32 br_port_mask); |
122 | int mv88e6xxx_leave_bridge(struct dsa_switch *ds, int port, u32 br_port_mask); | 132 | int mv88e6xxx_leave_bridge(struct dsa_switch *ds, int port, u32 br_port_mask); |
123 | int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state); | 133 | int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state); |
134 | int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, | ||
135 | const unsigned char *addr, u16 vid); | ||
136 | int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, | ||
137 | const unsigned char *addr, u16 vid); | ||
138 | int mv88e6xxx_port_fdb_getnext(struct dsa_switch *ds, int port, | ||
139 | unsigned char *addr, bool *is_static); | ||
124 | 140 | ||
125 | extern struct dsa_switch_driver mv88e6131_switch_driver; | 141 | extern struct dsa_switch_driver mv88e6131_switch_driver; |
126 | extern struct dsa_switch_driver mv88e6123_61_65_switch_driver; | 142 | extern struct dsa_switch_driver mv88e6123_61_65_switch_driver; |