diff options
Diffstat (limited to 'drivers/net/dsa/mv88e6xxx.c')
-rw-r--r-- | drivers/net/dsa/mv88e6xxx.c | 204 |
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; |