aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/dsa
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 /drivers/net/dsa
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>
Diffstat (limited to 'drivers/net/dsa')
-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