diff options
Diffstat (limited to 'drivers/net/dsa/mv88e6xxx')
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/chip.c | 160 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/chip.h | 11 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/smi.c | 158 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/smi.h | 59 |
5 files changed, 219 insertions, 170 deletions
diff --git a/drivers/net/dsa/mv88e6xxx/Makefile b/drivers/net/dsa/mv88e6xxx/Makefile index 50de304abe2f..e85755dde90b 100644 --- a/drivers/net/dsa/mv88e6xxx/Makefile +++ b/drivers/net/dsa/mv88e6xxx/Makefile @@ -12,3 +12,4 @@ mv88e6xxx-objs += phy.o mv88e6xxx-objs += port.o mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_PTP) += ptp.o mv88e6xxx-objs += serdes.o +mv88e6xxx-objs += smi.o diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index bad30be699bf..28414db979b0 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -43,6 +43,7 @@ #include "port.h" #include "ptp.h" #include "serdes.h" +#include "smi.h" static void assert_reg_lock(struct mv88e6xxx_chip *chip) { @@ -52,149 +53,6 @@ static void assert_reg_lock(struct mv88e6xxx_chip *chip) } } -/* 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_smi_read(struct mv88e6xxx_chip *chip, - int addr, int reg, u16 *val) -{ - if (!chip->smi_ops) - return -EOPNOTSUPP; - - return chip->smi_ops->read(chip, addr, reg, val); -} - -static int mv88e6xxx_smi_write(struct mv88e6xxx_chip *chip, - int addr, int reg, u16 val) -{ - if (!chip->smi_ops) - return -EOPNOTSUPP; - - return chip->smi_ops->write(chip, addr, reg, val); -} - -static int mv88e6xxx_smi_single_chip_read(struct mv88e6xxx_chip *chip, - int addr, int reg, u16 *val) -{ - int ret; - - ret = mdiobus_read_nested(chip->bus, addr, reg); - if (ret < 0) - return ret; - - *val = ret & 0xffff; - - return 0; -} - -static int mv88e6xxx_smi_single_chip_write(struct mv88e6xxx_chip *chip, - int addr, int reg, u16 val) -{ - int ret; - - ret = mdiobus_write_nested(chip->bus, addr, reg, val); - if (ret < 0) - return ret; - - return 0; -} - -static const struct mv88e6xxx_bus_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_chip *chip) -{ - int ret; - int i; - - for (i = 0; i < 16; i++) { - ret = mdiobus_read_nested(chip->bus, chip->sw_addr, SMI_CMD); - if (ret < 0) - return ret; - - if ((ret & SMI_CMD_BUSY) == 0) - return 0; - } - - return -ETIMEDOUT; -} - -static int mv88e6xxx_smi_multi_chip_read(struct mv88e6xxx_chip *chip, - int addr, int reg, u16 *val) -{ - int ret; - - /* Wait for the bus to become free. */ - ret = mv88e6xxx_smi_multi_chip_wait(chip); - if (ret < 0) - return ret; - - /* Transmit the read command. */ - ret = mdiobus_write_nested(chip->bus, chip->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_smi_multi_chip_wait(chip); - if (ret < 0) - return ret; - - /* Read the data. */ - ret = mdiobus_read_nested(chip->bus, chip->sw_addr, SMI_DATA); - if (ret < 0) - return ret; - - *val = ret & 0xffff; - - return 0; -} - -static int mv88e6xxx_smi_multi_chip_write(struct mv88e6xxx_chip *chip, - int addr, int reg, u16 val) -{ - int ret; - - /* Wait for the bus to become free. */ - ret = mv88e6xxx_smi_multi_chip_wait(chip); - if (ret < 0) - return ret; - - /* Transmit the data to write. */ - ret = mdiobus_write_nested(chip->bus, chip->sw_addr, SMI_DATA, val); - if (ret < 0) - return ret; - - /* Transmit the write command. */ - ret = mdiobus_write_nested(chip->bus, chip->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_smi_multi_chip_wait(chip); - if (ret < 0) - return ret; - - return 0; -} - -static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_multi_chip_ops = { - .read = mv88e6xxx_smi_multi_chip_read, - .write = mv88e6xxx_smi_multi_chip_write, -}; - int mv88e6xxx_read(struct mv88e6xxx_chip *chip, int addr, int reg, u16 *val) { int err; @@ -4645,22 +4503,6 @@ static struct mv88e6xxx_chip *mv88e6xxx_alloc_chip(struct device *dev) return chip; } -static int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip, - struct mii_bus *bus, int sw_addr) -{ - if (sw_addr == 0) - chip->smi_ops = &mv88e6xxx_smi_single_chip_ops; - else if (chip->info->multi_chip) - chip->smi_ops = &mv88e6xxx_smi_multi_chip_ops; - else - return -EINVAL; - - chip->bus = bus; - chip->sw_addr = sw_addr; - - return 0; -} - static enum dsa_tag_protocol mv88e6xxx_get_tag_protocol(struct dsa_switch *ds, int port) { diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index 19c07dff0440..faa3fa889f19 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -21,17 +21,6 @@ #include <linux/timecounter.h> #include <net/dsa.h> -#define SMI_CMD 0x00 -#define SMI_CMD_BUSY BIT(15) -#define SMI_CMD_CLAUSE_22 BIT(12) -#define SMI_CMD_OP_22_WRITE ((1 << 10) | SMI_CMD_BUSY | SMI_CMD_CLAUSE_22) -#define SMI_CMD_OP_22_READ ((2 << 10) | SMI_CMD_BUSY | SMI_CMD_CLAUSE_22) -#define SMI_CMD_OP_45_WRITE_ADDR ((0 << 10) | SMI_CMD_BUSY) -#define SMI_CMD_OP_45_WRITE_DATA ((1 << 10) | SMI_CMD_BUSY) -#define SMI_CMD_OP_45_READ_DATA ((2 << 10) | SMI_CMD_BUSY) -#define SMI_CMD_OP_45_READ_DATA_INC ((3 << 10) | SMI_CMD_BUSY) -#define SMI_DATA 0x01 - #define MV88E6XXX_N_FID 4096 /* PVT limits for 4-bit port and 5-bit switch */ diff --git a/drivers/net/dsa/mv88e6xxx/smi.c b/drivers/net/dsa/mv88e6xxx/smi.c new file mode 100644 index 000000000000..96f7d2685bdc --- /dev/null +++ b/drivers/net/dsa/mv88e6xxx/smi.c @@ -0,0 +1,158 @@ +/* + * Marvell 88E6xxx System Management Interface (SMI) support + * + * Copyright (c) 2008 Marvell Semiconductor + * + * Copyright (c) 2019 Vivien Didelot <vivien.didelot@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include "chip.h" +#include "smi.h" + +/* 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_smi_direct_read(struct mv88e6xxx_chip *chip, + int dev, int reg, u16 *data) +{ + int ret; + + ret = mdiobus_read_nested(chip->bus, dev, reg); + if (ret < 0) + return ret; + + *data = ret & 0xffff; + + return 0; +} + +static int mv88e6xxx_smi_direct_write(struct mv88e6xxx_chip *chip, + int dev, int reg, u16 data) +{ + int ret; + + ret = mdiobus_write_nested(chip->bus, dev, reg, data); + if (ret < 0) + return ret; + + return 0; +} + +static int mv88e6xxx_smi_direct_wait(struct mv88e6xxx_chip *chip, + int dev, int reg, int bit, int val) +{ + u16 data; + int err; + int i; + + for (i = 0; i < 16; i++) { + err = mv88e6xxx_smi_direct_read(chip, dev, reg, &data); + if (err) + return err; + + if (!!(data >> bit) == !!val) + return 0; + } + + return -ETIMEDOUT; +} + +static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_direct_ops = { + .read = mv88e6xxx_smi_direct_read, + .write = mv88e6xxx_smi_direct_write, +}; + +/* Offset 0x00: SMI Command Register + * Offset 0x01: SMI Data Register + */ + +static int mv88e6xxx_smi_indirect_read(struct mv88e6xxx_chip *chip, + int dev, int reg, u16 *data) +{ + int err; + + err = mv88e6xxx_smi_direct_wait(chip, chip->sw_addr, + MV88E6XXX_SMI_CMD, 15, 0); + if (err) + return err; + + err = mv88e6xxx_smi_direct_write(chip, chip->sw_addr, + MV88E6XXX_SMI_CMD, + MV88E6XXX_SMI_CMD_BUSY | + MV88E6XXX_SMI_CMD_MODE_22 | + MV88E6XXX_SMI_CMD_OP_22_READ | + (dev << 5) | reg); + if (err) + return err; + + err = mv88e6xxx_smi_direct_wait(chip, chip->sw_addr, + MV88E6XXX_SMI_CMD, 15, 0); + if (err) + return err; + + return mv88e6xxx_smi_direct_read(chip, chip->sw_addr, + MV88E6XXX_SMI_DATA, data); +} + +static int mv88e6xxx_smi_indirect_write(struct mv88e6xxx_chip *chip, + int dev, int reg, u16 data) +{ + int err; + + err = mv88e6xxx_smi_direct_wait(chip, chip->sw_addr, + MV88E6XXX_SMI_CMD, 15, 0); + if (err) + return err; + + err = mv88e6xxx_smi_direct_write(chip, chip->sw_addr, + MV88E6XXX_SMI_DATA, data); + if (err) + return err; + + err = mv88e6xxx_smi_direct_write(chip, chip->sw_addr, + MV88E6XXX_SMI_CMD, + MV88E6XXX_SMI_CMD_BUSY | + MV88E6XXX_SMI_CMD_MODE_22 | + MV88E6XXX_SMI_CMD_OP_22_WRITE | + (dev << 5) | reg); + if (err) + return err; + + return mv88e6xxx_smi_direct_wait(chip, chip->sw_addr, + MV88E6XXX_SMI_CMD, 15, 0); +} + +static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_indirect_ops = { + .read = mv88e6xxx_smi_indirect_read, + .write = mv88e6xxx_smi_indirect_write, +}; + +int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip, + struct mii_bus *bus, int sw_addr) +{ + if (sw_addr == 0) + chip->smi_ops = &mv88e6xxx_smi_direct_ops; + else if (chip->info->multi_chip) + chip->smi_ops = &mv88e6xxx_smi_indirect_ops; + else + return -EINVAL; + + chip->bus = bus; + chip->sw_addr = sw_addr; + + return 0; +} diff --git a/drivers/net/dsa/mv88e6xxx/smi.h b/drivers/net/dsa/mv88e6xxx/smi.h new file mode 100644 index 000000000000..35e6403b65dc --- /dev/null +++ b/drivers/net/dsa/mv88e6xxx/smi.h @@ -0,0 +1,59 @@ +/* + * Marvell 88E6xxx System Management Interface (SMI) support + * + * Copyright (c) 2008 Marvell Semiconductor + * + * Copyright (c) 2019 Vivien Didelot <vivien.didelot@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _MV88E6XXX_SMI_H +#define _MV88E6XXX_SMI_H + +#include "chip.h" + +/* Offset 0x00: SMI Command Register */ +#define MV88E6XXX_SMI_CMD 0x00 +#define MV88E6XXX_SMI_CMD_BUSY 0x8000 +#define MV88E6XXX_SMI_CMD_MODE_MASK 0x1000 +#define MV88E6XXX_SMI_CMD_MODE_45 0x0000 +#define MV88E6XXX_SMI_CMD_MODE_22 0x1000 +#define MV88E6XXX_SMI_CMD_OP_MASK 0x0c00 +#define MV88E6XXX_SMI_CMD_OP_22_WRITE 0x0400 +#define MV88E6XXX_SMI_CMD_OP_22_READ 0x0800 +#define MV88E6XXX_SMI_CMD_OP_45_WRITE_ADDR 0x0000 +#define MV88E6XXX_SMI_CMD_OP_45_WRITE_DATA 0x0400 +#define MV88E6XXX_SMI_CMD_OP_45_READ_DATA 0x0800 +#define MV88E6XXX_SMI_CMD_OP_45_READ_DATA_INC 0x0c00 +#define MV88E6XXX_SMI_CMD_DEV_ADDR_MASK 0x003e +#define MV88E6XXX_SMI_CMD_REG_ADDR_MASK 0x001f + +/* Offset 0x01: SMI Data Register */ +#define MV88E6XXX_SMI_DATA 0x01 + +int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip, + struct mii_bus *bus, int sw_addr); + +static inline int mv88e6xxx_smi_read(struct mv88e6xxx_chip *chip, + int dev, int reg, u16 *data) +{ + if (chip->smi_ops && chip->smi_ops->read) + return chip->smi_ops->read(chip, dev, reg, data); + + return -EOPNOTSUPP; +} + +static inline int mv88e6xxx_smi_write(struct mv88e6xxx_chip *chip, + int dev, int reg, u16 data) +{ + if (chip->smi_ops && chip->smi_ops->write) + return chip->smi_ops->write(chip, dev, reg, data); + + return -EOPNOTSUPP; +} + +#endif /* _MV88E6XXX_SMI_H */ |