[PATCH v2 05/11] net: dsa: ksz9477: switch to regmap_init_spi

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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





[Index of Archives]     [Linux Embedded]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux