mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH v2 00/11] net: dsa: ksz9477: use regmap to add I2C support next to SPI
@ 2023-01-11 13:29 Ahmad Fatoum
  2023-01-11 13:29 ` [PATCH v2 01/11] regmap: consolidate reg/val format into regmap_format Ahmad Fatoum
                   ` (11 more replies)
  0 siblings, 12 replies; 13+ messages in thread
From: Ahmad Fatoum @ 2023-01-11 13:29 UTC (permalink / raw)
  To: barebox

The Linux DSA driver supports both I2C and SPI as management interface
by using regmaps as abstraction. barebox regmaps were not quite that
powerful yet, so we rework our regmaps to support optional formatting
and then use that to make the KSZ driver work with either I2C or SPI
transports.

This series has been tested with both an I2C-connected KSZ8563 and
a SPI-connected KSZ9563, both of which are supported by the barebox
KSZ9477 driver.

v1 -> v2:
  - Use cdev access size encoded in flags to arrive at correct
    regmap to use (md -b / md -w works correctly now)
  - Changed regmap_init_multi prototype to not take an array
  - Added option for 64bit regmap in regmap_init_multi
  - Have regmap_init return -ENOSYS if formatted regmap support
    is needed, but is not compiled in (previously it crashed).

Ahmad Fatoum (11):
  regmap: consolidate reg/val format into regmap_format
  regmap: support formatted read and write
  regmap: port regmap_init_spi
  regmap: factor out regmap cdev size calculation
  net: dsa: ksz9477: switch to regmap_init_spi
  net: dsa: ksz9477: create regmap cdev for switch registers
  drivers: base: regmap: introduce REGMAP_I2C
  dev: add dev_bus_is_spi/i2c helpers
  net: dsa: ksz9477: refactor to prepare i2c support
  regmap: i2c: use formatted I/O
  net: ksz9477: add I2C support

 drivers/base/Kconfig               |   2 +
 drivers/base/regmap/Kconfig        |  14 +
 drivers/base/regmap/Makefile       |   5 +-
 drivers/base/regmap/internal.h     |  35 +-
 drivers/base/regmap/regmap-fmt.c   | 574 +++++++++++++++++++++++++++++
 drivers/base/regmap/regmap-i2c.c   |  50 ++-
 drivers/base/regmap/regmap-multi.c | 104 ++++++
 drivers/base/regmap/regmap-spi.c   |  42 +++
 drivers/base/regmap/regmap.c       |  66 +++-
 drivers/mfd/Kconfig                |   4 +
 drivers/net/Kconfig                |   4 +-
 drivers/net/ksz9477.c              | 190 +++-------
 drivers/net/ksz_common.h           | 154 ++++++++
 drivers/rtc/Kconfig                |   1 +
 drivers/video/ssd1307fb.c          |   4 +-
 include/i2c/i2c.h                  |   5 +
 include/regmap.h                   |  82 +++++
 include/spi/spi.h                  |   5 +
 18 files changed, 1166 insertions(+), 175 deletions(-)
 create mode 100644 drivers/base/regmap/Kconfig
 create mode 100644 drivers/base/regmap/regmap-fmt.c
 create mode 100644 drivers/base/regmap/regmap-multi.c
 create mode 100644 drivers/base/regmap/regmap-spi.c
 create mode 100644 drivers/net/ksz_common.h

-- 
2.30.2




^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v2 01/11] regmap: consolidate reg/val format into regmap_format
  2023-01-11 13:29 [PATCH v2 00/11] net: dsa: ksz9477: use regmap to add I2C support next to SPI Ahmad Fatoum
@ 2023-01-11 13:29 ` Ahmad Fatoum
  2023-01-11 13:29 ` [PATCH v2 02/11] regmap: support formatted read and write Ahmad Fatoum
                   ` (10 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Ahmad Fatoum @ 2023-01-11 13:29 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

To reduce difference to Linux v5.19 state of regmap, add a regmap_format
with identically named members and make use of this where appropriate.

No functional change.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/base/regmap/internal.h | 11 +++++++----
 drivers/base/regmap/regmap.c   | 23 ++++++++---------------
 2 files changed, 15 insertions(+), 19 deletions(-)

diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h
index f17456ebd2e7..30ac6d162d73 100644
--- a/drivers/base/regmap/internal.h
+++ b/drivers/base/regmap/internal.h
@@ -7,17 +7,20 @@
 
 struct regmap_bus;
 
+struct regmap_format {
+	size_t reg_bytes;
+	size_t pad_bytes;
+	size_t val_bytes;
+};
+
 struct regmap {
 	struct device *dev;
 	const struct regmap_bus *bus;
 	const char *name;
 	void *bus_context;
 	struct list_head list;
-	int reg_bits;
 	int reg_stride;
-	int pad_bits;
-	int val_bits;
-	int val_bytes;
+	struct regmap_format format;
 	unsigned int max_register;
 
 	struct cdev cdev;
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index 97662751be66..1b0985376a1f 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -86,13 +86,12 @@ struct regmap *regmap_init(struct device *dev,
 	map->bus = bus;
 	map->name = config->name;
 	map->bus_context = bus_context;
-	map->reg_bits = config->reg_bits;
+	map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8);
 	map->reg_stride = config->reg_stride;
 	if (!map->reg_stride)
 		map->reg_stride = 1;
-	map->pad_bits = config->pad_bits;
-	map->val_bits = config->val_bits;
-	map->val_bytes = DIV_ROUND_UP(config->val_bits, 8);
+	map->format.pad_bytes = config->pad_bits / 8;
+	map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8);
 	map->max_register = config->max_register;
 
 	list_add_tail(&map->list, &regmaps);
@@ -227,7 +226,7 @@ int regmap_write_bits(struct regmap *map, unsigned int reg,
 int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
 		     size_t val_len)
 {
-	size_t val_bytes = map->val_bytes;
+	size_t val_bytes = map->format.val_bytes;
 	size_t val_count = val_len / val_bytes;
 	unsigned int v;
 	int ret, i;
@@ -248,7 +247,7 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
 		if (ret != 0)
 			goto out;
 
-		switch (map->val_bytes) {
+		switch (map->format.val_bytes) {
 		case 4:
 			u32[i] = v;
 			break;
@@ -282,7 +281,7 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
 int regmap_bulk_write(struct regmap *map, unsigned int reg,
 		     const void *val, size_t val_len)
 {
-	size_t val_bytes = map->val_bytes;
+	size_t val_bytes = map->format.val_bytes;
 	size_t val_count = val_len / val_bytes;
 	int ret, i;
 
@@ -323,7 +322,7 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg,
 
 int regmap_get_val_bytes(struct regmap *map)
 {
-	return map->val_bytes;
+	return map->format.val_bytes;
 }
 
 int regmap_get_max_register(struct regmap *map)
@@ -338,13 +337,7 @@ int regmap_get_reg_stride(struct regmap *map)
 
 static int regmap_round_val_bytes(struct regmap *map)
 {
-	int val_bytes;
-
-	val_bytes = roundup_pow_of_two(map->val_bits) >> 3;
-	if (!val_bytes)
-		val_bytes = 1;
-
-	return val_bytes;
+	return map->format.val_bytes ?: 1;
 }
 
 static ssize_t regmap_cdev_read(struct cdev *cdev, void *buf, size_t count, loff_t offset,
-- 
2.30.2




^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v2 02/11] regmap: support formatted read and write
  2023-01-11 13:29 [PATCH v2 00/11] net: dsa: ksz9477: use regmap to add I2C support next to SPI Ahmad Fatoum
  2023-01-11 13:29 ` [PATCH v2 01/11] regmap: consolidate reg/val format into regmap_format Ahmad Fatoum
@ 2023-01-11 13:29 ` Ahmad Fatoum
  2023-01-11 13:29 ` [PATCH v2 03/11] regmap: port regmap_init_spi Ahmad Fatoum
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Ahmad Fatoum @ 2023-01-11 13:29 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

Regmap support in barebox compared to Linux is quite primitive.
For incoming SPI regmap support, we will want users of the API to just
populate their regmap config appropriately and have the core do the heavy
lifting of formatting register and value into the buffer correctly.

For this, import the formatted read/write functionality of Linux into
a new regmap-fmt.c file. Unlike Linux, we keep this functionally gated
behind a Kconfig option, so it's only selected when needed, e.g. via
REGMAP_SPI in a follow-up commit.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/base/Kconfig             |   2 +
 drivers/base/regmap/Kconfig      |   4 +
 drivers/base/regmap/Makefile     |   1 +
 drivers/base/regmap/internal.h   |  24 ++
 drivers/base/regmap/regmap-fmt.c | 574 +++++++++++++++++++++++++++++++
 drivers/base/regmap/regmap.c     |  34 +-
 include/regmap.h                 |  46 +++
 7 files changed, 683 insertions(+), 2 deletions(-)
 create mode 100644 drivers/base/regmap/Kconfig
 create mode 100644 drivers/base/regmap/regmap-fmt.c

diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index eebb60ce9193..3788231b6e82 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -5,3 +5,5 @@ config PM_GENERIC_DOMAINS
 
 config FEATURE_CONTROLLER
 	bool "Feature controller support" if COMPILE_TEST
+
+source "drivers/base/regmap/Kconfig"
diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig
new file mode 100644
index 000000000000..4fb6b6bcecf8
--- /dev/null
+++ b/drivers/base/regmap/Kconfig
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config REGMAP_FORMATTED
+	bool
diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile
index b136a72409fc..ef6814a50277 100644
--- a/drivers/base/regmap/Makefile
+++ b/drivers/base/regmap/Makefile
@@ -1,4 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0-only
 obj-y	+= regmap.o
 obj-y	+= regmap-mmio.o
+obj-$(CONFIG_REGMAP_FORMATTED)	+= regmap-fmt.o
 obj-$(CONFIG_I2C)	+= regmap-i2c.o
diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h
index 30ac6d162d73..ac3f0d3c0f20 100644
--- a/drivers/base/regmap/internal.h
+++ b/drivers/base/regmap/internal.h
@@ -8,9 +8,15 @@
 struct regmap_bus;
 
 struct regmap_format {
+	size_t buf_size;
 	size_t reg_bytes;
 	size_t pad_bytes;
 	size_t val_bytes;
+	void (*format_write)(struct regmap *map,
+			     unsigned int reg, unsigned int val);
+	void (*format_reg)(void *buf, unsigned int reg, unsigned int shift);
+	void (*format_val)(void *buf, unsigned int val, unsigned int shift);
+	unsigned int (*parse_val)(const void *buf);
 };
 
 struct regmap {
@@ -20,14 +26,32 @@ struct regmap {
 	void *bus_context;
 	struct list_head list;
 	int reg_stride;
+	void *work_buf;     /* Scratch buffer used to format I/O */
 	struct regmap_format format;
+	unsigned int read_flag_mask;
+	unsigned int write_flag_mask;
+	int reg_shift;
 	unsigned int max_register;
 
 	struct cdev cdev;
+
+	int (*reg_read)(void *context, unsigned int reg,
+			unsigned int *val);
+	int (*reg_write)(void *context, unsigned int reg,
+			 unsigned int val);
 };
 
 enum regmap_endian regmap_get_val_endian(struct device *dev,
 					 const struct regmap_bus *bus,
 					 const struct regmap_config *config);
 
+#ifdef CONFIG_REGMAP_FORMATTED
+int regmap_formatted_init(struct regmap *map, const struct regmap_config *);
+#else
+static inline int regmap_formatted_init(struct regmap *map, const struct regmap_config *cfg)
+{
+	return -ENOSYS;
+}
+#endif
+
 #endif
diff --git a/drivers/base/regmap/regmap-fmt.c b/drivers/base/regmap/regmap-fmt.c
new file mode 100644
index 000000000000..47545f691901
--- /dev/null
+++ b/drivers/base/regmap/regmap-fmt.c
@@ -0,0 +1,574 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Formatted register map access API
+ *
+ * Copyright 2022 Ahmad Fatoum <a.fatoum@pengutronix.de>
+ *
+ * based on Kernel code:
+ *
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ */
+
+#include <common.h>
+#include <regmap.h>
+#include <linux/log2.h>
+#include <asm/unaligned.h>
+
+#include "internal.h"
+
+static void regmap_format_12_20_write(struct regmap *map,
+				     unsigned int reg, unsigned int val)
+{
+	u8 *out = map->work_buf;
+
+	out[0] = reg >> 4;
+	out[1] = (reg << 4) | (val >> 16);
+	out[2] = val >> 8;
+	out[3] = val;
+}
+
+
+static void regmap_format_2_6_write(struct regmap *map,
+				     unsigned int reg, unsigned int val)
+{
+	u8 *out = map->work_buf;
+
+	*out = (reg << 6) | val;
+}
+
+static void regmap_format_4_12_write(struct regmap *map,
+				     unsigned int reg, unsigned int val)
+{
+	__be16 *out = map->work_buf;
+	*out = cpu_to_be16((reg << 12) | val);
+}
+
+static void regmap_format_7_9_write(struct regmap *map,
+				    unsigned int reg, unsigned int val)
+{
+	__be16 *out = map->work_buf;
+	*out = cpu_to_be16((reg << 9) | val);
+}
+
+static void regmap_format_7_17_write(struct regmap *map,
+				    unsigned int reg, unsigned int val)
+{
+	u8 *out = map->work_buf;
+
+	out[2] = val;
+	out[1] = val >> 8;
+	out[0] = (val >> 16) | (reg << 1);
+}
+
+static void regmap_format_10_14_write(struct regmap *map,
+				    unsigned int reg, unsigned int val)
+{
+	u8 *out = map->work_buf;
+
+	out[2] = val;
+	out[1] = (val >> 8) | (reg << 6);
+	out[0] = reg >> 2;
+}
+
+static void regmap_format_8(void *buf, unsigned int val, unsigned int shift)
+{
+	u8 *b = buf;
+
+	b[0] = val << shift;
+}
+
+static void regmap_format_16_be(void *buf, unsigned int val, unsigned int shift)
+{
+	put_unaligned_be16(val << shift, buf);
+}
+
+static void regmap_format_16_le(void *buf, unsigned int val, unsigned int shift)
+{
+	put_unaligned_le16(val << shift, buf);
+}
+
+static void regmap_format_16_native(void *buf, unsigned int val,
+				    unsigned int shift)
+{
+	u16 v = val << shift;
+
+	memcpy(buf, &v, sizeof(v));
+}
+
+static void regmap_format_24(void *buf, unsigned int val, unsigned int shift)
+{
+	u8 *b = buf;
+
+	val <<= shift;
+
+	b[0] = val >> 16;
+	b[1] = val >> 8;
+	b[2] = val;
+}
+
+static void regmap_format_32_be(void *buf, unsigned int val, unsigned int shift)
+{
+	put_unaligned_be32(val << shift, buf);
+}
+
+static void regmap_format_32_le(void *buf, unsigned int val, unsigned int shift)
+{
+	put_unaligned_le32(val << shift, buf);
+}
+
+static void regmap_format_32_native(void *buf, unsigned int val,
+				    unsigned int shift)
+{
+	u32 v = val << shift;
+
+	memcpy(buf, &v, sizeof(v));
+}
+
+#ifdef CONFIG_64BIT
+static void regmap_format_64_be(void *buf, unsigned int val, unsigned int shift)
+{
+	put_unaligned_be64((u64) val << shift, buf);
+}
+
+static void regmap_format_64_le(void *buf, unsigned int val, unsigned int shift)
+{
+	put_unaligned_le64((u64) val << shift, buf);
+}
+
+static void regmap_format_64_native(void *buf, unsigned int val,
+				    unsigned int shift)
+{
+	u64 v = (u64) val << shift;
+
+	memcpy(buf, &v, sizeof(v));
+}
+#endif
+
+static unsigned int regmap_parse_8(const void *buf)
+{
+	const u8 *b = buf;
+
+	return b[0];
+}
+
+static unsigned int regmap_parse_16_be(const void *buf)
+{
+	return get_unaligned_be16(buf);
+}
+
+static unsigned int regmap_parse_16_le(const void *buf)
+{
+	return get_unaligned_le16(buf);
+}
+
+static unsigned int regmap_parse_16_native(const void *buf)
+{
+	u16 v;
+
+	memcpy(&v, buf, sizeof(v));
+	return v;
+}
+
+static unsigned int regmap_parse_24(const void *buf)
+{
+	const u8 *b = buf;
+	unsigned int ret = b[2];
+	ret |= ((unsigned int)b[1]) << 8;
+	ret |= ((unsigned int)b[0]) << 16;
+
+	return ret;
+}
+
+static unsigned int regmap_parse_32_be(const void *buf)
+{
+	return get_unaligned_be32(buf);
+}
+
+static unsigned int regmap_parse_32_le(const void *buf)
+{
+	return get_unaligned_le32(buf);
+}
+
+static unsigned int regmap_parse_32_native(const void *buf)
+{
+	u32 v;
+
+	memcpy(&v, buf, sizeof(v));
+	return v;
+}
+
+#ifdef CONFIG_64BIT
+static unsigned int regmap_parse_64_be(const void *buf)
+{
+	return get_unaligned_be64(buf);
+}
+
+static unsigned int regmap_parse_64_le(const void *buf)
+{
+	return get_unaligned_le64(buf);
+}
+
+static unsigned int regmap_parse_64_native(const void *buf)
+{
+	u64 v;
+
+	memcpy(&v, buf, sizeof(v));
+	return v;
+}
+#endif
+
+
+static enum regmap_endian regmap_get_reg_endian(const struct regmap_bus *bus,
+					const struct regmap_config *config)
+{
+	enum regmap_endian endian;
+
+	/* Retrieve the endianness specification from the regmap config */
+	endian = config->reg_format_endian;
+
+	/* If the regmap config specified a non-default value, use that */
+	if (endian != REGMAP_ENDIAN_DEFAULT)
+		return endian;
+
+	/* Retrieve the endianness specification from the bus config */
+	if (bus && bus->reg_format_endian_default)
+		endian = bus->reg_format_endian_default;
+
+	/* If the bus specified a non-default value, use that */
+	if (endian != REGMAP_ENDIAN_DEFAULT)
+		return endian;
+
+	/* Use this if no other value was found */
+	return REGMAP_ENDIAN_BIG;
+}
+
+static void regmap_set_work_buf_flag_mask(struct regmap *map, int max_bytes,
+					  unsigned long mask)
+{
+	u8 *buf;
+	int i;
+
+	if (!mask || !map->work_buf)
+		return;
+
+	buf = map->work_buf;
+
+	for (i = 0; i < max_bytes; i++)
+		buf[i] |= (mask >> (8 * i)) & 0xff;
+}
+
+static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
+			    unsigned int val_len, bool noinc)
+{
+	const struct regmap_bus *bus = map->bus;
+
+	if (!bus->read)
+		return -EINVAL;
+
+	map->format.format_reg(map->work_buf, reg, map->reg_shift);
+	regmap_set_work_buf_flag_mask(map, map->format.reg_bytes,
+				      map->read_flag_mask);
+
+	return bus->read(map->bus_context, map->work_buf,
+			 map->format.reg_bytes + map->format.pad_bytes,
+			 val, val_len);
+
+}
+
+static int _regmap_bus_read(void *context, unsigned int reg,
+			    unsigned int *val)
+{
+	int ret;
+	struct regmap *map = context;
+	void *work_val = map->work_buf + map->format.reg_bytes +
+		map->format.pad_bytes;
+
+	if (!map->format.parse_val)
+		return -EINVAL;
+
+	ret = _regmap_raw_read(map, reg, work_val, map->format.val_bytes, false);
+	if (ret == 0)
+		*val = map->format.parse_val(work_val);
+
+	return ret;
+}
+
+static int _regmap_bus_formatted_write(void *context, unsigned int reg,
+				       unsigned int val)
+{
+	struct regmap *map = context;
+
+	map->format.format_write(map, reg, val);
+
+	return map->bus->write(map->bus_context, map->work_buf,
+			      map->format.buf_size);
+}
+
+static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg,
+				  const void *val, size_t val_len, bool noinc)
+{
+	void *work_val = map->work_buf + map->format.reg_bytes +
+		map->format.pad_bytes;
+
+	map->format.format_reg(map->work_buf, reg, map->reg_shift);
+	regmap_set_work_buf_flag_mask(map, map->format.reg_bytes,
+				      map->write_flag_mask);
+
+	/*
+	 * Essentially all I/O mechanisms will be faster with a single
+	 * buffer to write.  Since register syncs often generate raw
+	 * writes of single registers optimise that case.
+	 */
+	if (val != work_val && val_len == map->format.val_bytes) {
+		memcpy(work_val, val, map->format.val_bytes);
+		val = work_val;
+	}
+
+	/* If we're doing a single register write we can probably just
+	 * send the work_buf directly, otherwise try to do a gather
+	 * write.
+	 */
+	return map->bus->write(map->bus_context, map->work_buf,
+			       map->format.reg_bytes +
+			       map->format.pad_bytes +
+			       val_len);
+
+}
+
+static int _regmap_bus_raw_write(void *context, unsigned int reg,
+				 unsigned int val)
+{
+	struct regmap *map = context;
+
+	WARN_ON(!map->format.format_val);
+
+	map->format.format_val(map->work_buf + map->format.reg_bytes
+			       + map->format.pad_bytes, val, 0);
+	return _regmap_raw_write_impl(map, reg,
+				      map->work_buf +
+				      map->format.reg_bytes +
+				      map->format.pad_bytes,
+				      map->format.val_bytes,
+				      false);
+}
+
+int regmap_formatted_init(struct regmap *map, const struct regmap_config *config)
+{
+	enum regmap_endian reg_endian, val_endian;
+	const struct regmap_bus *bus = map->bus;
+
+	map->format.buf_size = DIV_ROUND_UP(config->reg_bits +
+			config->val_bits + config->pad_bits, 8);
+
+	map->work_buf = xzalloc(map->format.buf_size);
+
+	if (config->read_flag_mask || config->write_flag_mask) {
+		map->read_flag_mask = config->read_flag_mask;
+		map->write_flag_mask = config->write_flag_mask;
+	} else {
+		map->read_flag_mask = bus->read_flag_mask;
+	}
+
+	map->reg_read = _regmap_bus_read;
+
+	reg_endian = regmap_get_reg_endian(bus, config);
+	val_endian = regmap_get_val_endian(map->dev, bus, config);
+
+	switch (config->reg_bits + config->pad_bits % 8) {
+	case 2:
+		switch (config->val_bits) {
+		case 6:
+			map->format.format_write = regmap_format_2_6_write;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+
+	case 4:
+		switch (config->val_bits) {
+		case 12:
+			map->format.format_write = regmap_format_4_12_write;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+
+	case 7:
+		switch (config->val_bits) {
+		case 9:
+			map->format.format_write = regmap_format_7_9_write;
+			break;
+		case 17:
+			map->format.format_write = regmap_format_7_17_write;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+
+	case 10:
+		switch (config->val_bits) {
+		case 14:
+			map->format.format_write = regmap_format_10_14_write;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+
+	case 12:
+		switch (config->val_bits) {
+		case 20:
+			map->format.format_write = regmap_format_12_20_write;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+
+	case 8:
+		map->format.format_reg = regmap_format_8;
+		break;
+
+	case 16:
+		switch (reg_endian) {
+		case REGMAP_ENDIAN_BIG:
+			map->format.format_reg = regmap_format_16_be;
+			break;
+		case REGMAP_ENDIAN_LITTLE:
+			map->format.format_reg = regmap_format_16_le;
+			break;
+		case REGMAP_ENDIAN_NATIVE:
+			map->format.format_reg = regmap_format_16_native;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+
+	case 24:
+		if (reg_endian != REGMAP_ENDIAN_BIG)
+			return -EINVAL;
+		map->format.format_reg = regmap_format_24;
+		break;
+
+	case 32:
+		switch (reg_endian) {
+		case REGMAP_ENDIAN_BIG:
+			map->format.format_reg = regmap_format_32_be;
+			break;
+		case REGMAP_ENDIAN_LITTLE:
+			map->format.format_reg = regmap_format_32_le;
+			break;
+		case REGMAP_ENDIAN_NATIVE:
+			map->format.format_reg = regmap_format_32_native;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+
+#ifdef CONFIG_64BIT
+	case 64:
+		switch (reg_endian) {
+		case REGMAP_ENDIAN_BIG:
+			map->format.format_reg = regmap_format_64_be;
+			break;
+		case REGMAP_ENDIAN_LITTLE:
+			map->format.format_reg = regmap_format_64_le;
+			break;
+		case REGMAP_ENDIAN_NATIVE:
+			map->format.format_reg = regmap_format_64_native;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+#endif
+
+	default:
+		return -EINVAL;
+	}
+
+	switch (config->val_bits) {
+	case 8:
+		map->format.format_val = regmap_format_8;
+		map->format.parse_val = regmap_parse_8;
+		break;
+	case 16:
+		switch (val_endian) {
+		case REGMAP_ENDIAN_BIG:
+			map->format.format_val = regmap_format_16_be;
+			map->format.parse_val = regmap_parse_16_be;
+			break;
+		case REGMAP_ENDIAN_LITTLE:
+			map->format.format_val = regmap_format_16_le;
+			map->format.parse_val = regmap_parse_16_le;
+			break;
+		case REGMAP_ENDIAN_NATIVE:
+			map->format.format_val = regmap_format_16_native;
+			map->format.parse_val = regmap_parse_16_native;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	case 24:
+		if (val_endian != REGMAP_ENDIAN_BIG)
+			return -EINVAL;
+		map->format.format_val = regmap_format_24;
+		map->format.parse_val = regmap_parse_24;
+		break;
+	case 32:
+		switch (val_endian) {
+		case REGMAP_ENDIAN_BIG:
+			map->format.format_val = regmap_format_32_be;
+			map->format.parse_val = regmap_parse_32_be;
+			break;
+		case REGMAP_ENDIAN_LITTLE:
+			map->format.format_val = regmap_format_32_le;
+			map->format.parse_val = regmap_parse_32_le;
+			break;
+		case REGMAP_ENDIAN_NATIVE:
+			map->format.format_val = regmap_format_32_native;
+			map->format.parse_val = regmap_parse_32_native;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+#ifdef CONFIG_64BIT
+	case 64:
+		switch (val_endian) {
+		case REGMAP_ENDIAN_BIG:
+			map->format.format_val = regmap_format_64_be;
+			map->format.parse_val = regmap_parse_64_be;
+			break;
+		case REGMAP_ENDIAN_LITTLE:
+			map->format.format_val = regmap_format_64_le;
+			map->format.parse_val = regmap_parse_64_le;
+			break;
+		case REGMAP_ENDIAN_NATIVE:
+			map->format.format_val = regmap_format_64_native;
+			map->format.parse_val = regmap_parse_64_native;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+#endif
+	}
+
+	if (map->format.format_write)
+		map->reg_write = _regmap_bus_formatted_write;
+	else if (map->format.format_val)
+		map->reg_write = _regmap_bus_raw_write;
+	else
+		return -EOPNOTSUPP;
+
+	return 0;
+}
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index 1b0985376a1f..19f5356105f6 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -64,6 +64,23 @@ enum regmap_endian regmap_get_val_endian(struct device *dev,
 }
 EXPORT_SYMBOL_GPL(regmap_get_val_endian);
 
+static int _regmap_bus_reg_read(void *context, unsigned int reg,
+				unsigned int *val)
+{
+	struct regmap *map = context;
+
+	return map->bus->reg_read(map->bus_context, reg, val);
+}
+
+
+static int _regmap_bus_reg_write(void *context, unsigned int reg,
+				 unsigned int val)
+{
+	struct regmap *map = context;
+
+	return map->bus->reg_write(map->bus_context, reg, val);
+}
+
 /*
  * regmap_init - initialize and register a regmap
  *
@@ -80,6 +97,7 @@ struct regmap *regmap_init(struct device *dev,
 			     const struct regmap_config *config)
 {
 	struct regmap *map;
+	int ret;
 
 	map = xzalloc(sizeof(*map));
 	map->dev = dev;
@@ -92,8 +110,20 @@ struct regmap *regmap_init(struct device *dev,
 		map->reg_stride = 1;
 	map->format.pad_bytes = config->pad_bits / 8;
 	map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8);
+	map->reg_shift = config->pad_bits % 8;
 	map->max_register = config->max_register;
 
+	if (!bus->read || !bus->write) {
+		map->reg_read = _regmap_bus_reg_read;
+		map->reg_write = _regmap_bus_reg_write;
+	} else  {
+		ret = regmap_formatted_init(map, config);
+		if (ret) {
+			free(map);
+			return ERR_PTR(ret);
+		}
+	}
+
 	list_add_tail(&map->list, &regmaps);
 
 	return map;
@@ -139,7 +169,7 @@ struct device *regmap_get_device(struct regmap *map)
  */
 int regmap_write(struct regmap *map, unsigned int reg, unsigned int val)
 {
-	return map->bus->reg_write(map->bus_context, reg, val);
+	return map->reg_write(map, reg, val);
 }
 
 /*
@@ -153,7 +183,7 @@ int regmap_write(struct regmap *map, unsigned int reg, unsigned int val)
  */
 int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val)
 {
-	return map->bus->reg_read(map->bus_context, reg, val);
+	return map->reg_read(map, reg, val);
 }
 
 /**
diff --git a/include/regmap.h b/include/regmap.h
index 44dd8f38c8af..36a75eb34e03 100644
--- a/include/regmap.h
+++ b/include/regmap.h
@@ -2,6 +2,9 @@
 #ifndef __REGMAP_H
 #define __REGMAP_H
 
+#include <linux/compiler.h>
+#include <linux/types.h>
+
 enum regmap_endian {
 	/* Unspecified -> 0 -> Backwards compatible default */
 	REGMAP_ENDIAN_DEFAULT = 0,
@@ -23,7 +26,14 @@ enum regmap_endian {
  * @pad_bits: Number of bits of padding between register and value.
  * @val_bits: Number of bits in a register value, mandatory.
  *
+ * @write: Write operation.
+ * @read: Read operation.  Data is returned in the buffer used to transmit
+ *         data.
+ *
  * @max_register: Optional, specifies the maximum valid register index.
+ *
+ * @read_flag_mask: Mask to be set in the top byte of the register when doing
+ *                  a read.
  */
 struct regmap_config {
 	const char *name;
@@ -37,16 +47,52 @@ struct regmap_config {
 
 	enum regmap_endian reg_format_endian;
 	enum regmap_endian val_format_endian;
+
+	unsigned int read_flag_mask;
+	unsigned int write_flag_mask;
 };
 
+typedef int (*regmap_hw_write)(void *context, const void *data,
+			       size_t count);
+typedef int (*regmap_hw_read)(void *context,
+			      const void *reg_buf, size_t reg_size,
+			      void *val_buf, size_t val_size);
 typedef int (*regmap_hw_reg_read)(void *context, unsigned int reg,
 				  unsigned int *val);
 typedef int (*regmap_hw_reg_write)(void *context, unsigned int reg,
 				   unsigned int val);
 
+/**
+ * struct regmap_bus - Description of a hardware bus for the register map
+ *                     infrastructure.
+ *
+ * @reg_write: Write a single register value to the given register address. This
+ *             write operation has to complete when returning from the function.
+ * @reg_read: Read a single register value from a given register address.
+ * @read: Read operation.  Data is returned in the buffer used to transmit
+ *         data.
+ * @write: Write operation.
+ * @read_flag_mask: Mask to be set in the top byte of the register when doing
+ *                  a read.
+ * @reg_format_endian_default: Default endianness for formatted register
+ *     addresses. Used when the regmap_config specifies DEFAULT. If this is
+ *     DEFAULT, BIG is assumed.
+ * @val_format_endian_default: Default endianness for formatted register
+ *     values. Used when the regmap_config specifies DEFAULT. If this is
+ *     DEFAULT, BIG is assumed.
+ */
 struct regmap_bus {
 	regmap_hw_reg_write reg_write;
 	regmap_hw_reg_read reg_read;
+
+	int (*read)(void *context,
+		    const void *reg_buf, size_t reg_size,
+		    void *val_buf, size_t val_size);
+	int (*write)(void *context, const void *data,
+		     size_t count);
+
+	u8 read_flag_mask;
+
 	enum regmap_endian reg_format_endian_default;
 	enum regmap_endian val_format_endian_default;
 };
-- 
2.30.2




^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v2 03/11] regmap: port regmap_init_spi
  2023-01-11 13:29 [PATCH v2 00/11] net: dsa: ksz9477: use regmap to add I2C support next to SPI Ahmad Fatoum
  2023-01-11 13:29 ` [PATCH v2 01/11] regmap: consolidate reg/val format into regmap_format Ahmad Fatoum
  2023-01-11 13:29 ` [PATCH v2 02/11] regmap: support formatted read and write Ahmad Fatoum
@ 2023-01-11 13:29 ` Ahmad Fatoum
  2023-01-11 13:29 ` [PATCH v2 04/11] regmap: factor out regmap cdev size calculation Ahmad Fatoum
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Ahmad Fatoum @ 2023-01-11 13:29 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

We already have regmap_init_i2c, so add regmap_init_spi as well. Unlike
regmap_init_i2c, this one makes full use of the formatted regmap API.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/base/regmap/Kconfig      |  5 ++++
 drivers/base/regmap/Makefile     |  1 +
 drivers/base/regmap/regmap-spi.c | 42 ++++++++++++++++++++++++++++++++
 include/regmap.h                 | 13 ++++++++++
 4 files changed, 61 insertions(+)
 create mode 100644 drivers/base/regmap/regmap-spi.c

diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig
index 4fb6b6bcecf8..afe59a538c87 100644
--- a/drivers/base/regmap/Kconfig
+++ b/drivers/base/regmap/Kconfig
@@ -2,3 +2,8 @@
 
 config REGMAP_FORMATTED
 	bool
+
+config REGMAP_SPI
+	bool "SPI regmaps" if COMPILE_TEST
+	depends on SPI
+	select REGMAP_FORMATTED
diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile
index ef6814a50277..d99db4277149 100644
--- a/drivers/base/regmap/Makefile
+++ b/drivers/base/regmap/Makefile
@@ -3,3 +3,4 @@ obj-y	+= regmap.o
 obj-y	+= regmap-mmio.o
 obj-$(CONFIG_REGMAP_FORMATTED)	+= regmap-fmt.o
 obj-$(CONFIG_I2C)	+= regmap-i2c.o
+obj-$(CONFIG_REGMAP_SPI)	+= regmap-spi.o
diff --git a/drivers/base/regmap/regmap-spi.c b/drivers/base/regmap/regmap-spi.c
new file mode 100644
index 000000000000..5a0e418065b8
--- /dev/null
+++ b/drivers/base/regmap/regmap-spi.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Register map access API - SPI support
+//
+// Copyright 2011 Wolfson Microelectronics plc
+//
+// Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+
+#include <regmap.h>
+#include <spi/spi.h>
+
+static int regmap_spi_write(void *context, const void *data, size_t count)
+{
+	struct device_d *dev = context;
+	struct spi_device *spi = to_spi_device(dev);
+
+	return spi_write(spi, data, count);
+}
+
+static int regmap_spi_read(void *context,
+			   const void *reg, size_t reg_size,
+			   void *val, size_t val_size)
+{
+	struct device_d *dev = context;
+	struct spi_device *spi = to_spi_device(dev);
+
+	return spi_write_then_read(spi, reg, reg_size, val, val_size);
+}
+
+static const struct regmap_bus regmap_spi = {
+	.write = regmap_spi_write,
+	.read = regmap_spi_read,
+	.read_flag_mask = 0x80,
+	.reg_format_endian_default = REGMAP_ENDIAN_BIG,
+	.val_format_endian_default = REGMAP_ENDIAN_BIG,
+};
+
+struct regmap *regmap_init_spi(struct spi_device *spi,
+			       const struct regmap_config *config)
+{
+	return regmap_init(&spi->dev, &regmap_spi, &spi->dev, config);
+}
diff --git a/include/regmap.h b/include/regmap.h
index 36a75eb34e03..8f191c87cfcb 100644
--- a/include/regmap.h
+++ b/include/regmap.h
@@ -138,6 +138,19 @@ struct regmap *regmap_init_i2c(struct i2c_client *i2c,
 struct regmap *regmap_init_i2c_smbus(struct i2c_client *client,
 			       const struct regmap_config *config);
 
+/**
+ * regmap_init_spi() - Initialise spi register map
+ *
+ * @spi: Device that will be interacted with
+ * @config: Configuration for register map
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer
+ * to a struct regmap.
+ */
+struct spi_device;
+struct regmap *regmap_init_spi(struct spi_device *dev,
+			       const struct regmap_config *config);
+
 /**
  * regmap_init_mmio() - Initialise register map
  *
-- 
2.30.2




^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v2 04/11] regmap: factor out regmap cdev size calculation
  2023-01-11 13:29 [PATCH v2 00/11] net: dsa: ksz9477: use regmap to add I2C support next to SPI Ahmad Fatoum
                   ` (2 preceding siblings ...)
  2023-01-11 13:29 ` [PATCH v2 03/11] regmap: port regmap_init_spi Ahmad Fatoum
@ 2023-01-11 13:29 ` Ahmad Fatoum
  2023-01-11 13:29 ` [PATCH v2 05/11] net: dsa: ksz9477: switch to regmap_init_spi Ahmad Fatoum
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Ahmad Fatoum @ 2023-01-11 13:29 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

We will add a new user of this calculate in a follow-up commit, so
make it available as a global function.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/base/regmap/regmap.c | 9 +++++++--
 include/regmap.h             | 2 ++
 2 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index 19f5356105f6..da20dc05828d 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -401,6 +401,12 @@ static struct cdev_operations regmap_fops = {
 	.write	= regmap_cdev_write,
 };
 
+size_t regmap_size_bytes(struct regmap *map)
+{
+	return regmap_round_val_bytes(map) * (map->max_register + 1) /
+		map->reg_stride;
+}
+
 /*
  * regmap_register_cdev - register a devfs file for a regmap
  *
@@ -428,8 +434,7 @@ int regmap_register_cdev(struct regmap *map, const char *name)
 			map->cdev.name = xstrdup(dev_name(map->dev));
 	}
 
-	map->cdev.size = regmap_round_val_bytes(map) * (map->max_register + 1) /
-			map->reg_stride;
+	map->cdev.size = regmap_size_bytes(map);
 	map->cdev.dev = map->dev;
 	map->cdev.ops = &regmap_fops;
 
diff --git a/include/regmap.h b/include/regmap.h
index 8f191c87cfcb..986ed391ab28 100644
--- a/include/regmap.h
+++ b/include/regmap.h
@@ -204,6 +204,8 @@ static inline int regmap_clear_bits(struct regmap *map,
 	return regmap_update_bits(map, reg, bits, 0);
 }
 
+size_t regmap_size_bytes(struct regmap *map);
+
 /**
  * regmap_read_poll_timeout - Poll until a condition is met or a timeout occurs
  *
-- 
2.30.2




^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v2 05/11] net: dsa: ksz9477: switch to regmap_init_spi
  2023-01-11 13:29 [PATCH v2 00/11] net: dsa: ksz9477: use regmap to add I2C support next to SPI Ahmad Fatoum
                   ` (3 preceding siblings ...)
  2023-01-11 13:29 ` [PATCH v2 04/11] regmap: factor out regmap cdev size calculation Ahmad Fatoum
@ 2023-01-11 13:29 ` Ahmad Fatoum
  2023-01-11 13:29 ` [PATCH v2 06/11] net: dsa: ksz9477: create regmap cdev for switch registers Ahmad Fatoum
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Ahmad Fatoum @ 2023-01-11 13:29 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

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@pengutronix.de>
---
 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




^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v2 06/11] net: dsa: ksz9477: create regmap cdev for switch registers
  2023-01-11 13:29 [PATCH v2 00/11] net: dsa: ksz9477: use regmap to add I2C support next to SPI Ahmad Fatoum
                   ` (4 preceding siblings ...)
  2023-01-11 13:29 ` [PATCH v2 05/11] net: dsa: ksz9477: switch to regmap_init_spi Ahmad Fatoum
@ 2023-01-11 13:29 ` Ahmad Fatoum
  2023-01-11 13:29 ` [PATCH v2 07/11] drivers: base: regmap: introduce REGMAP_I2C Ahmad Fatoum
                   ` (5 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Ahmad Fatoum @ 2023-01-11 13:29 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

Now that we use regmap for the KSZ9477 driver, we can make the register
map available for introspection as a device file. As the KSZ driver has
a separate regmap for each of the three access sizes, we add a new
regmap_multi_register_cdev abstraction that multiplexes device file
access to the regmap with the best matching alignment.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/base/regmap/Makefile       |   1 +
 drivers/base/regmap/regmap-multi.c | 104 +++++++++++++++++++++++++++++
 drivers/net/ksz9477.c              |   7 +-
 include/regmap.h                   |  21 ++++++
 4 files changed, 132 insertions(+), 1 deletion(-)
 create mode 100644 drivers/base/regmap/regmap-multi.c

diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile
index d99db4277149..fe5beaaaa382 100644
--- a/drivers/base/regmap/Makefile
+++ b/drivers/base/regmap/Makefile
@@ -1,5 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0-only
 obj-y	+= regmap.o
+obj-y	+= regmap-multi.o
 obj-y	+= regmap-mmio.o
 obj-$(CONFIG_REGMAP_FORMATTED)	+= regmap-fmt.o
 obj-$(CONFIG_I2C)	+= regmap-i2c.o
diff --git a/drivers/base/regmap/regmap-multi.c b/drivers/base/regmap/regmap-multi.c
new file mode 100644
index 000000000000..e3f5b9aec1e4
--- /dev/null
+++ b/drivers/base/regmap/regmap-multi.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2022 Ahmad Fatoum <a.fatoum@pengutronix.de>
+ */
+
+#include <common.h>
+#include <fcntl.h>
+#include <regmap.h>
+#include <linux/bitfield.h>
+#include <linux/export.h>
+
+#include "internal.h"
+
+enum { MULTI_MAP_8, MULTI_MAP_16, MULTI_MAP_32, MULTI_MAP_64, MULTI_MAP_COUNT };
+struct regmap_multi {
+	struct cdev cdev;
+	struct regmap *map[MULTI_MAP_COUNT];
+};
+
+static struct regmap *regmap_multi_cdev_get_map(struct cdev *cdev, unsigned rwsize)
+{
+	struct regmap_multi *multi = container_of(cdev, struct regmap_multi, cdev);
+
+	switch (rwsize) {
+		case 8:
+			return multi->map[MULTI_MAP_64];
+		case 4:
+			return multi->map[MULTI_MAP_32];
+		case 2:
+			return multi->map[MULTI_MAP_16];
+		case 1:
+			return multi->map[MULTI_MAP_8];
+	}
+
+	return NULL;
+}
+
+static ssize_t regmap_multi_cdev_read(struct cdev *cdev, void *buf, size_t count,
+				      loff_t offset, unsigned long flags)
+{
+	unsigned rwsize = FIELD_GET(O_RWSIZE_MASK, flags);
+	struct regmap *map;
+
+	map = regmap_multi_cdev_get_map(cdev, rwsize);
+	if (!map)
+		return -EINVAL;
+
+	count = ALIGN_DOWN(count, rwsize);
+	return regmap_bulk_read(map, offset, buf, count) ?: count;
+}
+
+static ssize_t regmap_multi_cdev_write(struct cdev *cdev, const void *buf, size_t count,
+				       loff_t offset, unsigned long flags)
+{
+	unsigned rwsize = FIELD_GET(O_RWSIZE_MASK, flags);
+	struct regmap *map;
+
+	map = regmap_multi_cdev_get_map(cdev, rwsize);
+	if (!map)
+		return -EINVAL;
+
+	count = ALIGN_DOWN(count, rwsize);
+	return regmap_bulk_write(map, offset, buf, count) ?: count;
+}
+
+static struct cdev_operations regmap_multi_fops = {
+	.read	= regmap_multi_cdev_read,
+	.write	= regmap_multi_cdev_write,
+};
+
+int regmap_multi_register_cdev(struct regmap *map8,
+			       struct regmap *map16,
+			       struct regmap *map32,
+			       struct regmap *map64)
+{
+	struct regmap *maps[MULTI_MAP_COUNT] = { map8, map16, map32, map64 };
+	struct regmap_multi *multi;
+	struct cdev *cdev;
+	int i;
+
+	multi = xzalloc(sizeof(*multi));
+	cdev = &multi->cdev;
+
+	cdev->ops = &regmap_multi_fops;
+	cdev->size = LLONG_MAX;
+
+	for (i = 0; i < MULTI_MAP_COUNT; i++) {
+		if (!maps[i])
+			continue;
+
+		multi->map[i] = maps[i];
+		cdev->size = min_t(loff_t, regmap_size_bytes(maps[i]), cdev->size);
+		cdev->dev = cdev->dev ?: maps[i]->dev;
+	}
+
+	if (!cdev->dev) {
+		free(multi);
+		return -EINVAL;
+	}
+
+	cdev->name = xstrdup(dev_name(cdev->dev));
+
+	return devfs_create(cdev);
+}
diff --git a/drivers/net/ksz9477.c b/drivers/net/ksz9477.c
index a980735e8e3a..92e08f45bf03 100644
--- a/drivers/net/ksz9477.c
+++ b/drivers/net/ksz9477.c
@@ -450,7 +450,12 @@ static int microchip_switch_probe(struct device *dev)
 
 	ksz_default_setup(priv);
 
-	return dsa_register_switch(ds);
+	ret = dsa_register_switch(ds);
+	if (ret)
+		return ret;
+
+	return regmap_multi_register_cdev(priv->regmap[0], priv->regmap[1],
+					  priv->regmap[2], NULL);
 }
 
 static const struct of_device_id microchip_switch_dt_ids[] = {
diff --git a/include/regmap.h b/include/regmap.h
index 986ed391ab28..98ac90523b07 100644
--- a/include/regmap.h
+++ b/include/regmap.h
@@ -175,6 +175,27 @@ struct device *regmap_get_device(struct regmap *map);
 
 int regmap_register_cdev(struct regmap *map, const char *name);
 
+/**
+ * regmap_multi_register_cdev() - Initialize cdev backed by multiple regmaps
+ *
+ * @map8:  regmap for  8-bit wide accesses. NULL if such access
+ *         should fail with -EINVAL
+ * @map16: regmap for 16-bit wide accesses. NULL if such access
+ *         should fail with -EINVAL
+ * @map32: regmap for 32-bit wide accesses. NULL if such access
+ *         should fail with -EINVAL
+ * @map64: regmap for 64-bit wide accesses. NULL if such access
+ *         should fail with -EINVAL
+ *
+ * Registers a cdev that demultiplexes cdev accesses to one
+ * of the underlying regmaps according to the access size
+ * (e.g. mw -b => map8, mw -l => map32)
+ */
+int regmap_multi_register_cdev(struct regmap *map8,
+			       struct regmap *map16,
+			       struct regmap *map32,
+			       struct regmap *map64);
+
 int regmap_write(struct regmap *map, unsigned int reg, unsigned int val);
 int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val);
 
-- 
2.30.2




^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v2 07/11] drivers: base: regmap: introduce REGMAP_I2C
  2023-01-11 13:29 [PATCH v2 00/11] net: dsa: ksz9477: use regmap to add I2C support next to SPI Ahmad Fatoum
                   ` (5 preceding siblings ...)
  2023-01-11 13:29 ` [PATCH v2 06/11] net: dsa: ksz9477: create regmap cdev for switch registers Ahmad Fatoum
@ 2023-01-11 13:29 ` Ahmad Fatoum
  2023-01-11 13:29 ` [PATCH v2 08/11] dev: add dev_bus_is_spi/i2c helpers Ahmad Fatoum
                   ` (4 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Ahmad Fatoum @ 2023-01-11 13:29 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

regmap_init_i2c is quite limited right now and only supports 8-bit
registers and values. In future, we may want to expand this further, but
that would require us to format the regmap_config appropriately, pulling
in more code that's not required in the general case, where reg_read and
reg_write can be used directly. Add a new Kconfig symbol and select it
where appropriate to allow us to split formatted regmap handling from
the more basic handling we currently have.

We intentionally don't provide a stub function, so out-of-tree users
without the select fail to link and can be fixed.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/base/regmap/Kconfig  | 4 ++++
 drivers/base/regmap/Makefile | 2 +-
 drivers/mfd/Kconfig          | 4 ++++
 drivers/rtc/Kconfig          | 1 +
 4 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig
index afe59a538c87..78a00a085f05 100644
--- a/drivers/base/regmap/Kconfig
+++ b/drivers/base/regmap/Kconfig
@@ -3,6 +3,10 @@
 config REGMAP_FORMATTED
 	bool
 
+config REGMAP_I2C
+	bool "I2C regmaps" if COMPILE_TEST
+	depends on I2C
+
 config REGMAP_SPI
 	bool "SPI regmaps" if COMPILE_TEST
 	depends on SPI
diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile
index fe5beaaaa382..6911e07f0e62 100644
--- a/drivers/base/regmap/Makefile
+++ b/drivers/base/regmap/Makefile
@@ -3,5 +3,5 @@ obj-y	+= regmap.o
 obj-y	+= regmap-multi.o
 obj-y	+= regmap-mmio.o
 obj-$(CONFIG_REGMAP_FORMATTED)	+= regmap-fmt.o
-obj-$(CONFIG_I2C)	+= regmap-i2c.o
+obj-$(CONFIG_REGMAP_I2C)	+= regmap-i2c.o
 obj-$(CONFIG_REGMAP_SPI)	+= regmap-spi.o
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 5fd1a0aaa84e..c33fa262236c 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -64,12 +64,14 @@ config RAVE_SP_CORE
 
 config MFD_STPMIC1
 	depends on I2C
+	select REGMAP_I2C
 	bool "STPMIC1 MFD driver"
 	help
 	  Select this to support communication with the STPMIC1.
 
 config MFD_RN568PMIC
 	depends on I2C
+	select REGMAP_I2C
 	bool "Ricoh RN5T568 MFD driver"
 	help
 	  Select this to support communication with the Ricoh RN5T568 PMIC.
@@ -111,6 +113,7 @@ config MFD_ATMEL_FLEXCOM
 config MFD_RK808
 	tristate "Rockchip RK805/RK808/RK809/RK817/RK818 Power Management Chip"
 	depends on I2C && OFDEVICE
+	select REGMAP_I2C
 	help
 	  If you say yes here you get support for the RK805, RK808, RK809,
 	  RK817 and RK818 Power Management chips.
@@ -120,6 +123,7 @@ config MFD_RK808
 config MFD_AXP20X_I2C
 	tristate "X-Powers AXP series PMICs with I2C"
 	depends on I2C && OFDEVICE
+	select REGMAP_I2C
 	help
 	  If you say Y here you get support for the X-Powers AXP series power
 	  management ICs (PMICs) controlled with I2C.
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index db87aee1f320..98e58da89bd8 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -40,6 +40,7 @@ config RTC_DRV_ABRACON
 config RTC_DRV_PCF85363
 	tristate "NXP PCF85363"
 	depends on I2C
+	select REGMAP_I2C
 	help
 	  If you say yes here you get support for the PCF85363 RTC chip.
 
-- 
2.30.2




^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v2 08/11] dev: add dev_bus_is_spi/i2c helpers
  2023-01-11 13:29 [PATCH v2 00/11] net: dsa: ksz9477: use regmap to add I2C support next to SPI Ahmad Fatoum
                   ` (6 preceding siblings ...)
  2023-01-11 13:29 ` [PATCH v2 07/11] drivers: base: regmap: introduce REGMAP_I2C Ahmad Fatoum
@ 2023-01-11 13:29 ` Ahmad Fatoum
  2023-01-11 13:29 ` [PATCH v2 09/11] net: dsa: ksz9477: refactor to prepare i2c support Ahmad Fatoum
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Ahmad Fatoum @ 2023-01-11 13:29 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

These will simplify writing drivers that can probe via either I2C or SPI
like the SSD1307FB and upcoming KSZ I2C switch support.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/video/ssd1307fb.c | 4 ++--
 include/i2c/i2c.h         | 5 +++++
 include/spi/spi.h         | 5 +++++
 3 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/drivers/video/ssd1307fb.c b/drivers/video/ssd1307fb.c
index c2b5cd9b3481..7d2769a228af 100644
--- a/drivers/video/ssd1307fb.c
+++ b/drivers/video/ssd1307fb.c
@@ -451,12 +451,12 @@ static int ssd1307fb_probe(struct device *dev)
 
 	par->device_info = (struct ssd1307fb_deviceinfo *)match->data;
 
-	if (IS_ENABLED(CONFIG_I2C) && dev->bus == &i2c_bus) {
+	if (dev_bus_is_i2c(dev)) {
 		par->client = to_i2c_client(dev);
 		i2c_set_clientdata(par->client, par);
 		par->write_array = ssd1307fb_i2c_write_array;
 	}
-	if (IS_ENABLED(CONFIG_SPI) && dev->bus == &spi_bus) {
+	if (dev_bus_is_spi(dev)) {
 		par->spi = to_spi_device(dev);
 		par->dc = of_get_named_gpio(node, "dc-gpios", 0);
 		if (!gpio_is_valid(par->dc)) {
diff --git a/include/i2c/i2c.h b/include/i2c/i2c.h
index 5cca73a2f86d..b1ab72850c75 100644
--- a/include/i2c/i2c.h
+++ b/include/i2c/i2c.h
@@ -330,6 +330,11 @@ extern int i2c_write_reg(struct i2c_client *client, u32 addr, const u8 *buf, u16
 
 extern struct bus_type i2c_bus;
 
+static inline bool dev_bus_is_i2c(struct device *dev)
+{
+	return IS_ENABLED(CONFIG_I2C) && dev->bus == &i2c_bus;
+}
+
 static inline int i2c_driver_register(struct driver *drv)
 {
 	drv->bus = &i2c_bus;
diff --git a/include/spi/spi.h b/include/spi/spi.h
index 7591d0ff3b26..5071dd69650b 100644
--- a/include/spi/spi.h
+++ b/include/spi/spi.h
@@ -556,6 +556,11 @@ static inline ssize_t spi_w8r8(struct spi_device *spi, u8 cmd)
 
 extern struct bus_type spi_bus;
 
+static inline bool dev_bus_is_spi(struct device_d *dev)
+{
+	return IS_ENABLED(CONFIG_SPI) && dev->bus == &spi_bus;
+}
+
 struct spi_controller *spi_get_controller(int bus);
 
 static inline int spi_driver_register(struct driver *drv)
-- 
2.30.2




^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v2 09/11] net: dsa: ksz9477: refactor to prepare i2c support
  2023-01-11 13:29 [PATCH v2 00/11] net: dsa: ksz9477: use regmap to add I2C support next to SPI Ahmad Fatoum
                   ` (7 preceding siblings ...)
  2023-01-11 13:29 ` [PATCH v2 08/11] dev: add dev_bus_is_spi/i2c helpers Ahmad Fatoum
@ 2023-01-11 13:29 ` Ahmad Fatoum
  2023-01-11 13:29 ` [PATCH v2 10/11] regmap: i2c: use formatted I/O Ahmad Fatoum
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Ahmad Fatoum @ 2023-01-11 13:29 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

With the move to regmap, it's straight-forward to add i2c support.
Prepare for this by making it possible to turn off the SPI parts.

No functional change.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/net/Kconfig   |  2 +-
 drivers/net/ksz9477.c | 19 +++++++++++--------
 2 files changed, 12 insertions(+), 9 deletions(-)

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index e881b671d027..a6c820690f00 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -309,7 +309,7 @@ config DRIVER_NET_KSZ8873
 config DRIVER_NET_KSZ9477
 	bool "KSZ9477 switch driver"
 	depends on SPI
-	select REGMAP_SPI
+	select REGMAP_SPI if 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 92e08f45bf03..acf1e949fbe1 100644
--- a/drivers/net/ksz9477.c
+++ b/drivers/net/ksz9477.c
@@ -403,6 +403,7 @@ static int microchip_switch_regmap_init(struct ksz_switch *priv)
 
 static int microchip_switch_probe(struct device *dev)
 {
+	struct device *hw_dev;
 	struct ksz_switch *priv;
 	int ret = 0, gpio;
 	struct dsa_switch *ds;
@@ -412,9 +413,12 @@ static int microchip_switch_probe(struct device *dev)
 	dev->priv = priv;
 	priv->dev = dev;
 
-	priv->spi = (struct spi_device *)dev->type_data;
-	priv->spi->mode = SPI_MODE_0;
-	priv->spi->bits_per_word = 8;
+	if (dev_bus_is_spi(dev)) {
+		priv->spi = (struct spi_device *)dev->type_data;
+		priv->spi->mode = SPI_MODE_0;
+		priv->spi->bits_per_word = 8;
+		hw_dev = &priv->spi->dev;
+	}
 
 	ret = microchip_switch_regmap_init(priv);
 	if (ret)
@@ -430,8 +434,7 @@ static int microchip_switch_probe(struct device *dev)
 
 	ret = ksz9477_switch_detect(dev->priv);
 	if (ret) {
-		dev_err(&priv->spi->dev, "error detecting KSZ9477: %s\n",
-			strerror(-ret));
+		dev_err(hw_dev, "error detecting KSZ9477: %pe\n", ERR_PTR(ret));
 		return -ENODEV;
 	}
 
@@ -465,10 +468,10 @@ static const struct of_device_id microchip_switch_dt_ids[] = {
 	{ }
 };
 
-static struct driver microchip_switch_driver = {
-	.name		= "ksz9477",
+static struct driver microchip_switch_spi_driver = {
+	.name		= "ksz9477-spi",
 	.probe		= microchip_switch_probe,
 	.of_compatible	= DRV_OF_COMPAT(microchip_switch_dt_ids),
 };
+device_spi_driver(microchip_switch_spi_driver);
 
-device_spi_driver(microchip_switch_driver);
-- 
2.30.2




^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v2 10/11] regmap: i2c: use formatted I/O
  2023-01-11 13:29 [PATCH v2 00/11] net: dsa: ksz9477: use regmap to add I2C support next to SPI Ahmad Fatoum
                   ` (8 preceding siblings ...)
  2023-01-11 13:29 ` [PATCH v2 09/11] net: dsa: ksz9477: refactor to prepare i2c support Ahmad Fatoum
@ 2023-01-11 13:29 ` Ahmad Fatoum
  2023-01-11 13:29 ` [PATCH v2 11/11] net: ksz9477: add I2C support Ahmad Fatoum
  2023-01-12 14:58 ` [PATCH v2 00/11] net: dsa: ksz9477: use regmap to add I2C support next to SPI Sascha Hauer
  11 siblings, 0 replies; 13+ messages in thread
From: Ahmad Fatoum @ 2023-01-11 13:29 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

Switching to formatted I/O will enable consumers to customize access
sizes, which we'll need to support KSZ switch I2C communication.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/base/regmap/Kconfig      |  1 +
 drivers/base/regmap/regmap-i2c.c | 50 ++++++++++++++++++++++----------
 2 files changed, 36 insertions(+), 15 deletions(-)

diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig
index 78a00a085f05..c76908952a79 100644
--- a/drivers/base/regmap/Kconfig
+++ b/drivers/base/regmap/Kconfig
@@ -6,6 +6,7 @@ config REGMAP_FORMATTED
 config REGMAP_I2C
 	bool "I2C regmaps" if COMPILE_TEST
 	depends on I2C
+	select REGMAP_FORMATTED
 
 config REGMAP_SPI
 	bool "SPI regmaps" if COMPILE_TEST
diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c
index 756bc224cc3b..583bfffc1bb0 100644
--- a/drivers/base/regmap/regmap-i2c.c
+++ b/drivers/base/regmap/regmap-i2c.c
@@ -7,34 +7,54 @@
 #include <regmap.h>
 
 
-static int regmap_i2c_reg_read(void *client, unsigned int reg, unsigned int *val)
+static int regmap_i2c_read(void *context,
+			   const void *reg, size_t reg_size,
+			   void *val, size_t val_size)
 {
-	u8 buf[1];
+	struct device_d *dev = context;
+	struct i2c_client *i2c = to_i2c_client(dev);
+	struct i2c_msg xfer[2];
 	int ret;
 
-	ret = i2c_read_reg(client, reg, buf, 1);
-	if (ret != 1)
-		return ret;
+	xfer[0].addr = i2c->addr;
+	xfer[0].flags = 0;
+	xfer[0].len = reg_size;
+	xfer[0].buf = (void *)reg;
 
-	*val = buf[0];
-	return 0;
+	xfer[1].addr = i2c->addr;
+	xfer[1].flags = I2C_M_RD;
+	xfer[1].len = val_size;
+	xfer[1].buf = val;
+
+	ret = i2c_transfer(i2c->adapter, xfer, 2);
+	if (ret == 2)
+		return 0;
+	else if (ret < 0)
+		return ret;
+	else
+		return -EIO;
 }
 
-static int regmap_i2c_reg_write(void *client, unsigned int reg, unsigned int val)
+static int regmap_i2c_write(void *context, const void *data, size_t count)
 {
-	u8 buf[] = { val & 0xff };
+	struct device_d *dev = context;
+	struct i2c_client *i2c = to_i2c_client(dev);
 	int ret;
 
-	ret = i2c_write_reg(client, reg, buf, 1);
-	if (ret != 1)
+	ret = i2c_master_send(i2c, data, count);
+	if (ret == count)
+		return 0;
+	else if (ret < 0)
 		return ret;
-
-	return 0;
+	else
+		return -EIO;
 }
 
 static const struct regmap_bus regmap_regmap_i2c_bus = {
-	.reg_write = regmap_i2c_reg_write,
-	.reg_read = regmap_i2c_reg_read,
+	.write = regmap_i2c_write,
+	.read = regmap_i2c_read,
+	.reg_format_endian_default = REGMAP_ENDIAN_BIG,
+	.val_format_endian_default = REGMAP_ENDIAN_BIG,
 };
 
 struct regmap *regmap_init_i2c(struct i2c_client *client,
-- 
2.30.2




^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v2 11/11] net: ksz9477: add I2C support
  2023-01-11 13:29 [PATCH v2 00/11] net: dsa: ksz9477: use regmap to add I2C support next to SPI Ahmad Fatoum
                   ` (9 preceding siblings ...)
  2023-01-11 13:29 ` [PATCH v2 10/11] regmap: i2c: use formatted I/O Ahmad Fatoum
@ 2023-01-11 13:29 ` Ahmad Fatoum
  2023-01-12 14:58 ` [PATCH v2 00/11] net: dsa: ksz9477: use regmap to add I2C support next to SPI Sascha Hauer
  11 siblings, 0 replies; 13+ messages in thread
From: Ahmad Fatoum @ 2023-01-11 13:29 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

With regmap formatting support now in place, adding I2C support is quite
trivial, so let's do it.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/net/Kconfig      |  3 ++-
 drivers/net/ksz9477.c    | 18 ++++++++++++++++--
 drivers/net/ksz_common.h |  1 +
 3 files changed, 19 insertions(+), 3 deletions(-)

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index a6c820690f00..2622f64651d2 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -308,8 +308,9 @@ config DRIVER_NET_KSZ8873
 
 config DRIVER_NET_KSZ9477
 	bool "KSZ9477 switch driver"
-	depends on SPI
+	depends on SPI || I2C
 	select REGMAP_SPI if SPI
+	select REGMAP_I2C if I2C
 	help
 	  This option enables support for the Microchip KSZ9477
 	  switch chip.
diff --git a/drivers/net/ksz9477.c b/drivers/net/ksz9477.c
index acf1e949fbe1..af394554056c 100644
--- a/drivers/net/ksz9477.c
+++ b/drivers/net/ksz9477.c
@@ -7,6 +7,7 @@
 #include <net.h>
 #include <platform_data/ksz9477_reg.h>
 #include <spi/spi.h>
+#include <i2c/i2c.h>
 #include "ksz_common.h"
 
 /* SPI frame opcodes */
@@ -22,6 +23,7 @@
 
 KSZ_REGMAP_TABLE(ksz9477_spi, 32, SPI_ADDR_SHIFT,
 		 SPI_TURNAROUND_SHIFT, SPI_ADDR_ALIGN);
+KSZ_REGMAP_TABLE(ksz9477_i2c, not_used, 16, 0, 0);
 
 static int ksz9477_phy_read16(struct dsa_switch *ds, int addr, int reg)
 {
@@ -388,10 +390,13 @@ static int microchip_switch_regmap_init(struct ksz_switch *priv)
 	const struct regmap_config *cfg;
 	int i;
 
-	cfg = ksz9477_spi_regmap_config;
+	cfg = priv->spi ? ksz9477_spi_regmap_config : ksz9477_i2c_regmap_config;
 
 	for (i = 0; i < KSZ_REGMAP_ENTRY_COUNT; i++) {
-		priv->regmap[i] = regmap_init_spi(priv->spi, &cfg[i]);
+		if (priv->spi)
+			priv->regmap[i] = regmap_init_spi(priv->spi, &cfg[i]);
+		else
+			priv->regmap[i] = regmap_init_i2c(priv->i2c, &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",
@@ -418,6 +423,9 @@ static int microchip_switch_probe(struct device *dev)
 		priv->spi->mode = SPI_MODE_0;
 		priv->spi->bits_per_word = 8;
 		hw_dev = &priv->spi->dev;
+	} else if (dev_bus_is_i2c(dev)) {
+		priv->i2c = dev->type_data;
+		hw_dev = &priv->i2c->dev;
 	}
 
 	ret = microchip_switch_regmap_init(priv);
@@ -475,3 +483,9 @@ static struct driver microchip_switch_spi_driver = {
 };
 device_spi_driver(microchip_switch_spi_driver);
 
+static struct driver microchip_switch_i2c_driver = {
+	.name		= "ksz9477-i2c",
+	.probe		= microchip_switch_probe,
+	.of_compatible	= DRV_OF_COMPAT(microchip_switch_dt_ids),
+};
+device_i2c_driver(microchip_switch_i2c_driver);
diff --git a/drivers/net/ksz_common.h b/drivers/net/ksz_common.h
index 01447b61419e..995054d6e8c6 100644
--- a/drivers/net/ksz_common.h
+++ b/drivers/net/ksz_common.h
@@ -9,6 +9,7 @@
 
 struct ksz_switch {
 	struct spi_device *spi;
+	struct i2c_client *i2c;
 	struct dsa_switch ds;
 	struct device *dev;
 	int phy_port_cnt;
-- 
2.30.2




^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v2 00/11] net: dsa: ksz9477: use regmap to add I2C support next to SPI
  2023-01-11 13:29 [PATCH v2 00/11] net: dsa: ksz9477: use regmap to add I2C support next to SPI Ahmad Fatoum
                   ` (10 preceding siblings ...)
  2023-01-11 13:29 ` [PATCH v2 11/11] net: ksz9477: add I2C support Ahmad Fatoum
@ 2023-01-12 14:58 ` Sascha Hauer
  11 siblings, 0 replies; 13+ messages in thread
From: Sascha Hauer @ 2023-01-12 14:58 UTC (permalink / raw)
  To: Ahmad Fatoum; +Cc: barebox

On Wed, Jan 11, 2023 at 02:29:45PM +0100, Ahmad Fatoum wrote:
> The Linux DSA driver supports both I2C and SPI as management interface
> by using regmaps as abstraction. barebox regmaps were not quite that
> powerful yet, so we rework our regmaps to support optional formatting
> and then use that to make the KSZ driver work with either I2C or SPI
> transports.
> 
> This series has been tested with both an I2C-connected KSZ8563 and
> a SPI-connected KSZ9563, both of which are supported by the barebox
> KSZ9477 driver.
> 
> v1 -> v2:
>   - Use cdev access size encoded in flags to arrive at correct
>     regmap to use (md -b / md -w works correctly now)
>   - Changed regmap_init_multi prototype to not take an array
>   - Added option for 64bit regmap in regmap_init_multi
>   - Have regmap_init return -ENOSYS if formatted regmap support
>     is needed, but is not compiled in (previously it crashed).
> 
> Ahmad Fatoum (11):
>   regmap: consolidate reg/val format into regmap_format
>   regmap: support formatted read and write
>   regmap: port regmap_init_spi
>   regmap: factor out regmap cdev size calculation
>   net: dsa: ksz9477: switch to regmap_init_spi
>   net: dsa: ksz9477: create regmap cdev for switch registers
>   drivers: base: regmap: introduce REGMAP_I2C
>   dev: add dev_bus_is_spi/i2c helpers
>   net: dsa: ksz9477: refactor to prepare i2c support
>   regmap: i2c: use formatted I/O
>   net: ksz9477: add I2C support

Applied, thanks

Sascha

> 
>  drivers/base/Kconfig               |   2 +
>  drivers/base/regmap/Kconfig        |  14 +
>  drivers/base/regmap/Makefile       |   5 +-
>  drivers/base/regmap/internal.h     |  35 +-
>  drivers/base/regmap/regmap-fmt.c   | 574 +++++++++++++++++++++++++++++
>  drivers/base/regmap/regmap-i2c.c   |  50 ++-
>  drivers/base/regmap/regmap-multi.c | 104 ++++++
>  drivers/base/regmap/regmap-spi.c   |  42 +++
>  drivers/base/regmap/regmap.c       |  66 +++-
>  drivers/mfd/Kconfig                |   4 +
>  drivers/net/Kconfig                |   4 +-
>  drivers/net/ksz9477.c              | 190 +++-------
>  drivers/net/ksz_common.h           | 154 ++++++++
>  drivers/rtc/Kconfig                |   1 +
>  drivers/video/ssd1307fb.c          |   4 +-
>  include/i2c/i2c.h                  |   5 +
>  include/regmap.h                   |  82 +++++
>  include/spi/spi.h                  |   5 +
>  18 files changed, 1166 insertions(+), 175 deletions(-)
>  create mode 100644 drivers/base/regmap/Kconfig
>  create mode 100644 drivers/base/regmap/regmap-fmt.c
>  create mode 100644 drivers/base/regmap/regmap-multi.c
>  create mode 100644 drivers/base/regmap/regmap-spi.c
>  create mode 100644 drivers/net/ksz_common.h
> 
> -- 
> 2.30.2
> 
> 
> 

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |



^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2023-01-12 14:59 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-01-11 13:29 [PATCH v2 00/11] net: dsa: ksz9477: use regmap to add I2C support next to SPI Ahmad Fatoum
2023-01-11 13:29 ` [PATCH v2 01/11] regmap: consolidate reg/val format into regmap_format Ahmad Fatoum
2023-01-11 13:29 ` [PATCH v2 02/11] regmap: support formatted read and write Ahmad Fatoum
2023-01-11 13:29 ` [PATCH v2 03/11] regmap: port regmap_init_spi Ahmad Fatoum
2023-01-11 13:29 ` [PATCH v2 04/11] regmap: factor out regmap cdev size calculation Ahmad Fatoum
2023-01-11 13:29 ` [PATCH v2 05/11] net: dsa: ksz9477: switch to regmap_init_spi Ahmad Fatoum
2023-01-11 13:29 ` [PATCH v2 06/11] net: dsa: ksz9477: create regmap cdev for switch registers Ahmad Fatoum
2023-01-11 13:29 ` [PATCH v2 07/11] drivers: base: regmap: introduce REGMAP_I2C Ahmad Fatoum
2023-01-11 13:29 ` [PATCH v2 08/11] dev: add dev_bus_is_spi/i2c helpers Ahmad Fatoum
2023-01-11 13:29 ` [PATCH v2 09/11] net: dsa: ksz9477: refactor to prepare i2c support Ahmad Fatoum
2023-01-11 13:29 ` [PATCH v2 10/11] regmap: i2c: use formatted I/O Ahmad Fatoum
2023-01-11 13:29 ` [PATCH v2 11/11] net: ksz9477: add I2C support Ahmad Fatoum
2023-01-12 14:58 ` [PATCH v2 00/11] net: dsa: ksz9477: use regmap to add I2C support next to SPI Sascha Hauer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox