summaryrefslogtreecommitdiff
path: root/drivers/net/dsa/mv88e6xxx.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/dsa/mv88e6xxx.c')
-rw-r--r--drivers/net/dsa/mv88e6xxx.c204
1 files changed, 145 insertions, 59 deletions
diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c
index 789f938a7f36..9b116d8d4e23 100644
--- a/drivers/net/dsa/mv88e6xxx.c
+++ b/drivers/net/dsa/mv88e6xxx.c
@@ -38,21 +38,74 @@ static void assert_reg_lock(struct mv88e6xxx_priv_state *ps)
}
}
-/* If the switch's ADDR[4:0] strap pins are strapped to zero, it will
- * use all 32 SMI bus addresses on its SMI bus, and all switch registers
- * will be directly accessible on some {device address,register address}
- * pair. If the ADDR[4:0] pins are not strapped to zero, the switch
- * will only respond to SMI transactions to that specific address, and
- * an indirect addressing mechanism needs to be used to access its
- * registers.
+/* The switch ADDR[4:1] configuration pins define the chip SMI device address
+ * (ADDR[0] is always zero, thus only even SMI addresses can be strapped).
+ *
+ * When ADDR is all zero, the chip uses Single-chip Addressing Mode, assuming it
+ * is the only device connected to the SMI master. In this mode it responds to
+ * all 32 possible SMI addresses, and thus maps directly the internal devices.
+ *
+ * When ADDR is non-zero, the chip uses Multi-chip Addressing Mode, allowing
+ * multiple devices to share the SMI interface. In this mode it responds to only
+ * 2 registers, used to indirectly access the internal SMI devices.
*/
-static int mv88e6xxx_reg_wait_ready(struct mii_bus *bus, int sw_addr)
+
+static int mv88e6xxx_smi_read(struct mv88e6xxx_priv_state *ps,
+ int addr, int reg, u16 *val)
+{
+ if (!ps->smi_ops)
+ return -EOPNOTSUPP;
+
+ return ps->smi_ops->read(ps, addr, reg, val);
+}
+
+static int mv88e6xxx_smi_write(struct mv88e6xxx_priv_state *ps,
+ int addr, int reg, u16 val)
+{
+ if (!ps->smi_ops)
+ return -EOPNOTSUPP;
+
+ return ps->smi_ops->write(ps, addr, reg, val);
+}
+
+static int mv88e6xxx_smi_single_chip_read(struct mv88e6xxx_priv_state *ps,
+ int addr, int reg, u16 *val)
+{
+ int ret;
+
+ ret = mdiobus_read_nested(ps->bus, addr, reg);
+ if (ret < 0)
+ return ret;
+
+ *val = ret & 0xffff;
+
+ return 0;
+}
+
+static int mv88e6xxx_smi_single_chip_write(struct mv88e6xxx_priv_state *ps,
+ int addr, int reg, u16 val)
+{
+ int ret;
+
+ ret = mdiobus_write_nested(ps->bus, addr, reg, val);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct mv88e6xxx_ops mv88e6xxx_smi_single_chip_ops = {
+ .read = mv88e6xxx_smi_single_chip_read,
+ .write = mv88e6xxx_smi_single_chip_write,
+};
+
+static int mv88e6xxx_smi_multi_chip_wait(struct mv88e6xxx_priv_state *ps)
{
int ret;
int i;
for (i = 0; i < 16; i++) {
- ret = mdiobus_read_nested(bus, sw_addr, SMI_CMD);
+ ret = mdiobus_read_nested(ps->bus, ps->sw_addr, SMI_CMD);
if (ret < 0)
return ret;
@@ -63,108 +116,134 @@ static int mv88e6xxx_reg_wait_ready(struct mii_bus *bus, int sw_addr)
return -ETIMEDOUT;
}
-static int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr,
- int reg)
+static int mv88e6xxx_smi_multi_chip_read(struct mv88e6xxx_priv_state *ps,
+ int addr, int reg, u16 *val)
{
int ret;
- if (sw_addr == 0)
- return mdiobus_read_nested(bus, addr, reg);
-
/* Wait for the bus to become free. */
- ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
+ ret = mv88e6xxx_smi_multi_chip_wait(ps);
if (ret < 0)
return ret;
/* Transmit the read command. */
- ret = mdiobus_write_nested(bus, sw_addr, SMI_CMD,
+ ret = mdiobus_write_nested(ps->bus, ps->sw_addr, SMI_CMD,
SMI_CMD_OP_22_READ | (addr << 5) | reg);
if (ret < 0)
return ret;
/* Wait for the read command to complete. */
- ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
+ ret = mv88e6xxx_smi_multi_chip_wait(ps);
if (ret < 0)
return ret;
/* Read the data. */
- ret = mdiobus_read_nested(bus, sw_addr, SMI_DATA);
- if (ret < 0)
- return ret;
-
- return ret & 0xffff;
-}
-
-static int _mv88e6xxx_reg_read(struct mv88e6xxx_priv_state *ps,
- int addr, int reg)
-{
- int ret;
-
- assert_reg_lock(ps);
-
- ret = __mv88e6xxx_reg_read(ps->bus, ps->sw_addr, addr, reg);
+ ret = mdiobus_read_nested(ps->bus, ps->sw_addr, SMI_DATA);
if (ret < 0)
return ret;
- dev_dbg(ps->dev, "<- addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n",
- addr, reg, ret);
-
- return ret;
-}
+ *val = ret & 0xffff;
-static int mv88e6xxx_reg_read(struct mv88e6xxx_priv_state *ps, int addr,
- int reg)
-{
- int ret;
-
- mutex_lock(&ps->reg_lock);
- ret = _mv88e6xxx_reg_read(ps, addr, reg);
- mutex_unlock(&ps->reg_lock);
-
- return ret;
+ return 0;
}
-static int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr,
- int reg, u16 val)
+static int mv88e6xxx_smi_multi_chip_write(struct mv88e6xxx_priv_state *ps,
+ int addr, int reg, u16 val)
{
int ret;
- if (sw_addr == 0)
- return mdiobus_write_nested(bus, addr, reg, val);
-
/* Wait for the bus to become free. */
- ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
+ ret = mv88e6xxx_smi_multi_chip_wait(ps);
if (ret < 0)
return ret;
/* Transmit the data to write. */
- ret = mdiobus_write_nested(bus, sw_addr, SMI_DATA, val);
+ ret = mdiobus_write_nested(ps->bus, ps->sw_addr, SMI_DATA, val);
if (ret < 0)
return ret;
/* Transmit the write command. */
- ret = mdiobus_write_nested(bus, sw_addr, SMI_CMD,
+ ret = mdiobus_write_nested(ps->bus, ps->sw_addr, SMI_CMD,
SMI_CMD_OP_22_WRITE | (addr << 5) | reg);
if (ret < 0)
return ret;
/* Wait for the write command to complete. */
- ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
+ ret = mv88e6xxx_smi_multi_chip_wait(ps);
if (ret < 0)
return ret;
return 0;
}
-static int _mv88e6xxx_reg_write(struct mv88e6xxx_priv_state *ps, int addr,
- int reg, u16 val)
+static const struct mv88e6xxx_ops mv88e6xxx_smi_multi_chip_ops = {
+ .read = mv88e6xxx_smi_multi_chip_read,
+ .write = mv88e6xxx_smi_multi_chip_write,
+};
+
+static int mv88e6xxx_read(struct mv88e6xxx_priv_state *ps,
+ int addr, int reg, u16 *val)
+{
+ int err;
+
+ assert_reg_lock(ps);
+
+ err = mv88e6xxx_smi_read(ps, addr, reg, val);
+ if (err)
+ return err;
+
+ dev_dbg(ps->dev, "<- addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n",
+ addr, reg, *val);
+
+ return 0;
+}
+
+static int mv88e6xxx_write(struct mv88e6xxx_priv_state *ps,
+ int addr, int reg, u16 val)
{
+ int err;
+
assert_reg_lock(ps);
+ err = mv88e6xxx_smi_write(ps, addr, reg, val);
+ if (err)
+ return err;
+
dev_dbg(ps->dev, "-> addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n",
addr, reg, val);
- return __mv88e6xxx_reg_write(ps->bus, ps->sw_addr, addr, reg, val);
+ return 0;
+}
+
+static int _mv88e6xxx_reg_read(struct mv88e6xxx_priv_state *ps,
+ int addr, int reg)
+{
+ u16 val;
+ int err;
+
+ err = mv88e6xxx_read(ps, addr, reg, &val);
+ if (err)
+ return err;
+
+ return val;
+}
+
+static int mv88e6xxx_reg_read(struct mv88e6xxx_priv_state *ps, int addr,
+ int reg)
+{
+ int ret;
+
+ mutex_lock(&ps->reg_lock);
+ ret = _mv88e6xxx_reg_read(ps, addr, reg);
+ mutex_unlock(&ps->reg_lock);
+
+ return ret;
+}
+
+static int _mv88e6xxx_reg_write(struct mv88e6xxx_priv_state *ps, int addr,
+ int reg, u16 val)
+{
+ return mv88e6xxx_write(ps, addr, reg, val);
}
static int mv88e6xxx_reg_write(struct mv88e6xxx_priv_state *ps, int addr,
@@ -3666,6 +3745,13 @@ static int mv88e6xxx_smi_init(struct mv88e6xxx_priv_state *ps,
if (sw_addr & 0x1)
return -EINVAL;
+ if (sw_addr == 0)
+ ps->smi_ops = &mv88e6xxx_smi_single_chip_ops;
+ else if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_MULTI_CHIP))
+ ps->smi_ops = &mv88e6xxx_smi_multi_chip_ops;
+ else
+ return -EINVAL;
+
ps->bus = bus;
ps->sw_addr = sw_addr;