Linux uses three regmaps for the KSZ9477 DSA driver, one for each of the three access sizes supported by the chip. While this increases overhead a bit, it'll allow us in future to extend the driver seamlessly for i2c support. Signed-off-by: Ahmad Fatoum <a.fatoum@xxxxxxxxxxxxxx> --- drivers/net/Kconfig | 1 + drivers/net/ksz9477.c | 150 +++++++------------------------------- drivers/net/ksz_common.h | 153 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 180 insertions(+), 124 deletions(-) create mode 100644 drivers/net/ksz_common.h diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 2dafd9c7a8b9..e881b671d027 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -309,6 +309,7 @@ config DRIVER_NET_KSZ8873 config DRIVER_NET_KSZ9477 bool "KSZ9477 switch driver" depends on SPI + select REGMAP_SPI help This option enables support for the Microchip KSZ9477 switch chip. diff --git a/drivers/net/ksz9477.c b/drivers/net/ksz9477.c index d9186b1177b8..a980735e8e3a 100644 --- a/drivers/net/ksz9477.c +++ b/drivers/net/ksz9477.c @@ -7,13 +7,12 @@ #include <net.h> #include <platform_data/ksz9477_reg.h> #include <spi/spi.h> +#include "ksz_common.h" /* SPI frame opcodes */ -#define KS_SPIOP_RD 3 -#define KS_SPIOP_WR 2 #define SPI_ADDR_SHIFT 24 -#define SPI_ADDR_MASK (BIT(SPI_ADDR_SHIFT) - 1) +#define SPI_ADDR_ALIGN 3 #define SPI_TURNAROUND_SHIFT 5 #define GBIT_SUPPORT BIT(0) @@ -21,127 +20,8 @@ #define IS_9893 BIT(2) #define KSZ9477_PHY_ERRATA BIT(3) -struct ksz_switch { - struct spi_device *spi; - struct dsa_switch ds; - struct device *dev; - int phy_port_cnt; - u32 chip_id; - u8 features; -}; - -static int ksz9477_spi_read_reg(struct spi_device *spi, u32 reg, u8 *val, - unsigned int len) -{ - u32 txbuf; - int ret; - - txbuf = reg & SPI_ADDR_MASK; - txbuf |= KS_SPIOP_RD << SPI_ADDR_SHIFT; - txbuf <<= SPI_TURNAROUND_SHIFT; - txbuf = cpu_to_be32(txbuf); - - ret = spi_write_then_read(spi, &txbuf, 4, val, len); - - return ret; -} - -static int ksz9477_spi_write_reg(struct spi_device *spi, u32 reg, u8 *val, - unsigned int len) -{ - u32 txbuf[2]; - - txbuf[0] = reg & SPI_ADDR_MASK; - txbuf[0] |= (KS_SPIOP_WR << SPI_ADDR_SHIFT); - txbuf[0] <<= SPI_TURNAROUND_SHIFT; - txbuf[0] = cpu_to_be32(*txbuf); - memcpy(&txbuf[1], val, len); - - return spi_write(spi, txbuf, 4 + len); -} - -static int ksz_read8(struct ksz_switch *priv, u32 reg, u8 *val) -{ - return ksz9477_spi_read_reg(priv->spi, reg, val, 1); -} - -static int ksz_write8(struct ksz_switch *priv, u32 reg, u8 value) -{ - return ksz9477_spi_write_reg(priv->spi, reg, &value, 1); -} - -static int ksz_read16(struct ksz_switch *priv, u32 reg, u16 *val) -{ - int ret = ksz9477_spi_read_reg(priv->spi, reg, (u8 *)val, 2); - - if (!ret) - *val = be16_to_cpu(*val); - - return ret; -} - -static int ksz_write16(struct ksz_switch *priv, u32 reg, u16 value) -{ - struct spi_device *spi = priv->spi; - - value = cpu_to_be16(value); - return ksz9477_spi_write_reg(spi, reg, (u8 *)&value, 2); -} - -static int ksz_read32(struct ksz_switch *priv, u32 reg, u32 *val) -{ - int ret = ksz9477_spi_read_reg(priv->spi, reg, (u8 *)val, 4); - - if (!ret) - *val = be32_to_cpu(*val); - - return ret; -} - -static int ksz_write32(struct ksz_switch *priv, u32 reg, u32 value) -{ - struct spi_device *spi = priv->spi; - - value = cpu_to_be32(value); - return ksz9477_spi_write_reg(spi, reg, (u8 *)&value, 4); -} - -static void ksz_cfg(struct ksz_switch *priv, u32 addr, u8 bits, bool set) -{ - u8 data; - - ksz_read8(priv, addr, &data); - if (set) - data |= bits; - else - data &= ~bits; - ksz_write8(priv, addr, data); -} - -static int ksz_pread8(struct ksz_switch *priv, int port, int reg, u8 *val) -{ - return ksz_read8(priv, PORT_CTRL_ADDR(port, reg), val); -} - -static int ksz_pwrite8(struct ksz_switch *priv, int port, int reg, u8 val) -{ - return ksz_write8(priv, PORT_CTRL_ADDR(port, reg), val); -} - -static int ksz_pread16(struct ksz_switch *priv, int port, int reg, u16 *val) -{ - return ksz_read16(priv, PORT_CTRL_ADDR(port, reg), val); -} - -static int ksz_pwrite16(struct ksz_switch *priv, int port, int reg, u16 val) -{ - return ksz_write16(priv, PORT_CTRL_ADDR(port, reg), val); -} - -static int ksz_pwrite32(struct ksz_switch *priv, int port, int reg, u32 val) -{ - return ksz_write32(priv, PORT_CTRL_ADDR(port, reg), val); -} +KSZ_REGMAP_TABLE(ksz9477_spi, 32, SPI_ADDR_SHIFT, + SPI_TURNAROUND_SHIFT, SPI_ADDR_ALIGN); static int ksz9477_phy_read16(struct dsa_switch *ds, int addr, int reg) { @@ -503,6 +383,24 @@ static int ksz_default_setup(struct ksz_switch *priv) return 0; } +static int microchip_switch_regmap_init(struct ksz_switch *priv) +{ + const struct regmap_config *cfg; + int i; + + cfg = ksz9477_spi_regmap_config; + + for (i = 0; i < KSZ_REGMAP_ENTRY_COUNT; i++) { + priv->regmap[i] = regmap_init_spi(priv->spi, &cfg[i]); + if (IS_ERR(priv->regmap[i])) + return dev_err_probe(priv->dev, PTR_ERR(priv->regmap[i]), + "Failed to initialize regmap%i\n", + cfg[i].val_bits); + } + + return 0; +} + static int microchip_switch_probe(struct device *dev) { struct ksz_switch *priv; @@ -518,6 +416,10 @@ static int microchip_switch_probe(struct device *dev) priv->spi->mode = SPI_MODE_0; priv->spi->bits_per_word = 8; + ret = microchip_switch_regmap_init(priv); + if (ret) + return ret; + gpio = gpiod_get(dev, "reset", GPIOF_OUT_INIT_ACTIVE); if (gpio_is_valid(gpio)) { mdelay(1); diff --git a/drivers/net/ksz_common.h b/drivers/net/ksz_common.h new file mode 100644 index 000000000000..01447b61419e --- /dev/null +++ b/drivers/net/ksz_common.h @@ -0,0 +1,153 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef NET_KSZ_COMMON_H_ +#define NET_KSZ_COMMON_H_ + +#include <linux/swab.h> +#include <regmap.h> +#include <linux/bitops.h> +#include <platform_data/ksz9477_reg.h> + +struct ksz_switch { + struct spi_device *spi; + struct dsa_switch ds; + struct device *dev; + int phy_port_cnt; + u32 chip_id; + u8 features; + struct regmap *regmap[3]; +}; + +static inline int ksz_read8(struct ksz_switch *priv, u32 reg, u8 *val) +{ + unsigned int value; + int ret = regmap_read(priv->regmap[0], reg, &value); + + *val = value; + return ret; +} + +static inline int ksz_read16(struct ksz_switch *priv, u32 reg, u16 *val) +{ + unsigned int value; + int ret = regmap_read(priv->regmap[1], reg, &value); + + *val = value; + return ret; +} + +static inline int ksz_read32(struct ksz_switch *priv, u32 reg, u32 *val) +{ + unsigned int value; + int ret = regmap_read(priv->regmap[2], reg, &value); + + *val = value; + return ret; +} + +static inline int ksz_read64(struct ksz_switch *priv, u32 reg, u64 *val) +{ + u32 value[2]; + int ret; + + ret = regmap_bulk_read(priv->regmap[2], reg, value, 2); + if (!ret) + *val = (u64)value[0] << 32 | value[1]; + + return ret; +} + +static inline int ksz_write8(struct ksz_switch *priv, u32 reg, u8 value) +{ + return regmap_write(priv->regmap[0], reg, value); +} + +static inline int ksz_write16(struct ksz_switch *priv, u32 reg, u16 value) +{ + return regmap_write(priv->regmap[1], reg, value); +} + +static inline int ksz_write32(struct ksz_switch *priv, u32 reg, u32 value) +{ + return regmap_write(priv->regmap[2], reg, value); +} + +static inline int ksz_write64(struct ksz_switch *priv, u32 reg, u64 value) +{ + u32 val[2]; + + /* Ick! ToDo: Add 64bit R/W to regmap on 32bit systems */ + value = swab64(value); + val[0] = swab32(value & 0xffffffffULL); + val[1] = swab32(value >> 32ULL); + + return regmap_bulk_write(priv->regmap[2], reg, val, 2); +} + +static inline int ksz_pread8(struct ksz_switch *priv, int port, int reg, u8 *val) +{ + return ksz_read8(priv, PORT_CTRL_ADDR(port, reg), val); +} + +static inline int ksz_pwrite8(struct ksz_switch *priv, int port, int reg, u8 val) +{ + return ksz_write8(priv, PORT_CTRL_ADDR(port, reg), val); +} + +static inline int ksz_pread16(struct ksz_switch *priv, int port, int reg, u16 *val) +{ + return ksz_read16(priv, PORT_CTRL_ADDR(port, reg), val); +} + +static inline int ksz_pwrite16(struct ksz_switch *priv, int port, int reg, u16 val) +{ + return ksz_write16(priv, PORT_CTRL_ADDR(port, reg), val); +} + +static inline int ksz_pwrite32(struct ksz_switch *priv, int port, int reg, u32 val) +{ + return ksz_write32(priv, PORT_CTRL_ADDR(port, reg), val); +} + +static void ksz_cfg(struct ksz_switch *priv, u32 addr, u8 bits, bool set) +{ + regmap_update_bits(priv->regmap[0], addr, bits, set ? bits : 0); +} + +/* Regmap tables generation */ +#define KSZ_SPI_OP_RD 3 +#define KSZ_SPI_OP_WR 2 + +#define swabnot_used(x) 0 + +#define KSZ_SPI_OP_FLAG_MASK(opcode, swp, regbits, regpad) \ + swab##swp((opcode) << ((regbits) + (regpad))) + +#define KSZ_REGMAP_ENTRY_COUNT 3 + +#define KSZ_REGMAP_ENTRY(width, swp, regbits, regpad, regalign) \ + { \ + .name = #width, \ + .val_bits = (width), \ + .reg_stride = 1, \ + .reg_bits = (regbits) + (regalign), \ + .pad_bits = (regpad), \ + .max_register = BIT(regbits) - 1, \ + .read_flag_mask = \ + KSZ_SPI_OP_FLAG_MASK(KSZ_SPI_OP_RD, swp, \ + regbits, regpad), \ + .write_flag_mask = \ + KSZ_SPI_OP_FLAG_MASK(KSZ_SPI_OP_WR, swp, \ + regbits, regpad), \ + .reg_format_endian = REGMAP_ENDIAN_BIG, \ + .val_format_endian = REGMAP_ENDIAN_BIG \ + } + +#define KSZ_REGMAP_TABLE(ksz, swp, regbits, regpad, regalign) \ + static const struct regmap_config ksz##_regmap_config[KSZ_REGMAP_ENTRY_COUNT] = { \ + KSZ_REGMAP_ENTRY(8, swp, (regbits), (regpad), (regalign)), \ + KSZ_REGMAP_ENTRY(16, swp, (regbits), (regpad), (regalign)), \ + KSZ_REGMAP_ENTRY(32, swp, (regbits), (regpad), (regalign)), \ + } + + +#endif -- 2.30.2