diff options
Diffstat (limited to 'drivers/net/dsa')
-rw-r--r-- | drivers/net/dsa/mv88e6xxx.c | 121 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx.h | 8 |
2 files changed, 86 insertions, 43 deletions
diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c index fa086e09d6b7..5e572b3510b9 100644 --- a/drivers/net/dsa/mv88e6xxx.c +++ b/drivers/net/dsa/mv88e6xxx.c @@ -2181,27 +2181,10 @@ int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *bridge) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); - u16 fid; - int i, err; + int i, err = 0; mutex_lock(&ps->smi_mutex); - /* Get or create the bridge FID and assign it to the port */ - for (i = 0; i < ps->num_ports; ++i) - if (ps->ports[i].bridge_dev == bridge) - break; - - if (i < ps->num_ports) - err = _mv88e6xxx_port_fid_get(ds, i, &fid); - else - err = _mv88e6xxx_fid_new(ds, &fid); - if (err) - goto unlock; - - err = _mv88e6xxx_port_fid_set(ds, port, fid); - if (err) - goto unlock; - /* Assign the bridge and remap each port's VLANTable */ ps->ports[port].bridge_dev = bridge; @@ -2213,7 +2196,6 @@ int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port, } } -unlock: mutex_unlock(&ps->smi_mutex); return err; @@ -2223,16 +2205,10 @@ void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); struct net_device *bridge = ps->ports[port].bridge_dev; - u16 fid; int i; mutex_lock(&ps->smi_mutex); - /* Give the port a fresh Filtering Information Database */ - if (_mv88e6xxx_fid_new(ds, &fid) || - _mv88e6xxx_port_fid_set(ds, port, fid)) - netdev_warn(ds->ports[port], "failed to assign a new FID\n"); - /* Unassign the bridge and remap each port's VLANTable */ ps->ports[port].bridge_dev = NULL; @@ -2264,6 +2240,57 @@ static void mv88e6xxx_bridge_work(struct work_struct *work) mutex_unlock(&ps->smi_mutex); } +static int _mv88e6xxx_phy_page_write(struct dsa_switch *ds, int port, int page, + int reg, int val) +{ + int ret; + + ret = _mv88e6xxx_phy_write_indirect(ds, port, 0x16, page); + if (ret < 0) + goto restore_page_0; + + ret = _mv88e6xxx_phy_write_indirect(ds, port, reg, val); +restore_page_0: + _mv88e6xxx_phy_write_indirect(ds, port, 0x16, 0x0); + + return ret; +} + +static int _mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, + int reg) +{ + int ret; + + ret = _mv88e6xxx_phy_write_indirect(ds, port, 0x16, page); + if (ret < 0) + goto restore_page_0; + + ret = _mv88e6xxx_phy_read_indirect(ds, port, reg); +restore_page_0: + _mv88e6xxx_phy_write_indirect(ds, port, 0x16, 0x0); + + return ret; +} + +static int mv88e6xxx_power_on_serdes(struct dsa_switch *ds) +{ + int ret; + + ret = _mv88e6xxx_phy_page_read(ds, REG_FIBER_SERDES, PAGE_FIBER_SERDES, + MII_BMCR); + if (ret < 0) + return ret; + + if (ret & BMCR_PDOWN) { + ret &= ~BMCR_PDOWN; + ret = _mv88e6xxx_phy_page_write(ds, REG_FIBER_SERDES, + PAGE_FIBER_SERDES, MII_BMCR, + ret); + } + + return ret; +} + static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) { struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); @@ -2367,6 +2394,23 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) goto abort; } + /* If this port is connected to a SerDes, make sure the SerDes is not + * powered down. + */ + if (mv88e6xxx_6352_family(ds)) { + ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_STATUS); + if (ret < 0) + goto abort; + ret &= PORT_STATUS_CMODE_MASK; + if ((ret == PORT_STATUS_CMODE_100BASE_X) || + (ret == PORT_STATUS_CMODE_1000BASE_X) || + (ret == PORT_STATUS_CMODE_SGMII)) { + ret = mv88e6xxx_power_on_serdes(ds); + if (ret < 0) + goto abort; + } + } + /* Port Control 2: don't force a good FCS, set the maximum frame size to * 10240 bytes, disable 802.1q tags checking, don't discard tagged or * untagged frames on this port, do a destination address lookup on all @@ -2408,9 +2452,9 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) * the other bits clear. */ reg = 1 << port; - /* Disable learning for DSA and CPU ports */ - if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) - reg = PORT_ASSOC_VECTOR_LOCKED_PORT; + /* Disable learning for CPU port */ + if (dsa_is_cpu_port(ds, port)) + reg = 0; ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_ASSOC_VECTOR, reg); if (ret) @@ -2490,11 +2534,11 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) if (ret) goto abort; - /* Port based VLAN map: give each port its own address + /* Port based VLAN map: give each port the same default address * database, and allow bidirectional communication between the * CPU and DSA port(s), and the other ports. */ - ret = _mv88e6xxx_port_fid_set(ds, port, port + 1); + ret = _mv88e6xxx_port_fid_set(ds, port, 0); if (ret) goto abort; @@ -2714,13 +2758,9 @@ int mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, int reg) int ret; mutex_lock(&ps->smi_mutex); - ret = _mv88e6xxx_phy_write_indirect(ds, port, 0x16, page); - if (ret < 0) - goto error; - ret = _mv88e6xxx_phy_read_indirect(ds, port, reg); -error: - _mv88e6xxx_phy_write_indirect(ds, port, 0x16, 0x0); + ret = _mv88e6xxx_phy_page_read(ds, port, page, reg); mutex_unlock(&ps->smi_mutex); + return ret; } @@ -2731,14 +2771,9 @@ int mv88e6xxx_phy_page_write(struct dsa_switch *ds, int port, int page, int ret; mutex_lock(&ps->smi_mutex); - ret = _mv88e6xxx_phy_write_indirect(ds, port, 0x16, page); - if (ret < 0) - goto error; - - ret = _mv88e6xxx_phy_write_indirect(ds, port, reg, val); -error: - _mv88e6xxx_phy_write_indirect(ds, port, 0x16, 0x0); + ret = _mv88e6xxx_phy_page_write(ds, port, page, reg, val); mutex_unlock(&ps->smi_mutex); + return ret; } diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h index 9a038aba48fb..26a424acd10f 100644 --- a/drivers/net/dsa/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx.h @@ -28,6 +28,10 @@ #define SMI_CMD_OP_45_READ_DATA_INC ((3 << 10) | SMI_CMD_BUSY) #define SMI_DATA 0x01 +/* Fiber/SERDES Registers are located at SMI address F, page 1 */ +#define REG_FIBER_SERDES 0x0f +#define PAGE_FIBER_SERDES 0x01 + #define REG_PORT(p) (0x10 + (p)) #define PORT_STATUS 0x00 #define PORT_STATUS_PAUSE_EN BIT(15) @@ -45,6 +49,10 @@ #define PORT_STATUS_MGMII BIT(6) /* 6185 */ #define PORT_STATUS_TX_PAUSED BIT(5) #define PORT_STATUS_FLOW_CTRL BIT(4) +#define PORT_STATUS_CMODE_MASK 0x0f +#define PORT_STATUS_CMODE_100BASE_X 0x8 +#define PORT_STATUS_CMODE_1000BASE_X 0x9 +#define PORT_STATUS_CMODE_SGMII 0xa #define PORT_PCS_CTRL 0x01 #define PORT_PCS_CTRL_RGMII_DELAY_RXCLK BIT(15) #define PORT_PCS_CTRL_RGMII_DELAY_TXCLK BIT(14) |