+++ /dev/null
-From 42d20a6a61b8fccbb57d80df1ccde7dd82d5bbd6 Mon Sep 17 00:00:00 2001
-From: Chris Packham <chris.packham@alliedtelesis.co.nz>
-Date: Wed, 16 Oct 2024 11:54:34 +1300
-Subject: [PATCH] spi: spi-mem: Add Realtek SPI-NAND controller
-
-Add a driver for the SPI-NAND controller on the RTL9300 family of
-devices.
-
-The controller supports
-* Serial/Dual/Quad data with
-* PIO and DMA data read/write operation
-* Configurable flash access timing
-
-There is a separate ECC controller on the RTL9300 which isn't currently
-supported (instead we rely on the on-die ECC supported by most SPI-NAND
-chips).
-
-Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
-Link: https://patch.msgid.link/20241015225434.3970360-4-chris.packham@alliedtelesis.co.nz
-Signed-off-by: Mark Brown <broonie@kernel.org>
----
- MAINTAINERS | 6 +
- drivers/spi/Kconfig | 11 +
- drivers/spi/Makefile | 1 +
- drivers/spi/spi-realtek-rtl-snand.c | 405 ++++++++++++++++++++++++++++
- 4 files changed, 423 insertions(+)
- create mode 100644 drivers/spi/spi-realtek-rtl-snand.c
-
---- a/MAINTAINERS
-+++ b/MAINTAINERS
-@@ -19507,6 +19507,12 @@ S: Maintained
- F: Documentation/devicetree/bindings/net/dsa/realtek.yaml
- F: drivers/net/dsa/realtek/*
-
-+REALTEK SPI-NAND
-+M: Chris Packham <chris.packham@alliedtelesis.co.nz>
-+S: Maintained
-+F: Documentation/devicetree/bindings/spi/realtek,rtl9301-snand.yaml
-+F: drivers/spi/spi-realtek-rtl-snand.c
-+
- REALTEK WIRELESS DRIVER (rtlwifi family)
- M: Ping-Ke Shih <pkshih@realtek.com>
- L: linux-wireless@vger.kernel.org
---- a/drivers/spi/Kconfig
-+++ b/drivers/spi/Kconfig
-@@ -843,6 +843,17 @@ config SPI_PXA2XX
- config SPI_PXA2XX_PCI
- def_tristate SPI_PXA2XX && PCI && COMMON_CLK
-
-+config SPI_REALTEK_SNAND
-+ tristate "Realtek SPI-NAND Flash Controller"
-+ depends on MACH_REALTEK_RTL || COMPILE_TEST
-+ select REGMAP
-+ help
-+ This enables support for the SPI-NAND Flash controller on
-+ Realtek SoCs.
-+
-+ This driver does not support generic SPI. The implementation
-+ only supports the spi-mem interface.
-+
- config SPI_ROCKCHIP
- tristate "Rockchip SPI controller driver"
- depends on ARCH_ROCKCHIP || COMPILE_TEST
---- a/drivers/spi/Makefile
-+++ b/drivers/spi/Makefile
-@@ -120,6 +120,7 @@ obj-$(CONFIG_SPI_ROCKCHIP) += spi-rockc
- obj-$(CONFIG_SPI_ROCKCHIP_SFC) += spi-rockchip-sfc.o
- obj-$(CONFIG_SPI_RB4XX) += spi-rb4xx.o
- obj-$(CONFIG_MACH_REALTEK_RTL) += spi-realtek-rtl.o
-+obj-$(CONFIG_SPI_REALTEK_SNAND) += spi-realtek-rtl-snand.o
- obj-$(CONFIG_SPI_RPCIF) += spi-rpc-if.o
- obj-$(CONFIG_SPI_RSPI) += spi-rspi.o
- obj-$(CONFIG_SPI_RZV2M_CSI) += spi-rzv2m-csi.o
---- /dev/null
-+++ b/drivers/spi/spi-realtek-rtl-snand.c
-@@ -0,0 +1,405 @@
-+// SPDX-License-Identifier: GPL-2.0-only
-+
-+#include <linux/completion.h>
-+#include <linux/dma-mapping.h>
-+#include <linux/interrupt.h>
-+#include <linux/mod_devicetable.h>
-+#include <linux/platform_device.h>
-+#include <linux/regmap.h>
-+#include <linux/spi/spi.h>
-+#include <linux/spi/spi-mem.h>
-+
-+#define SNAFCFR 0x00
-+#define SNAFCFR_DMA_IE BIT(20)
-+#define SNAFCCR 0x04
-+#define SNAFWCMR 0x08
-+#define SNAFRCMR 0x0c
-+#define SNAFRDR 0x10
-+#define SNAFWDR 0x14
-+#define SNAFDTR 0x18
-+#define SNAFDRSAR 0x1c
-+#define SNAFDIR 0x20
-+#define SNAFDIR_DMA_IP BIT(0)
-+#define SNAFDLR 0x24
-+#define SNAFSR 0x40
-+#define SNAFSR_NFCOS BIT(3)
-+#define SNAFSR_NFDRS BIT(2)
-+#define SNAFSR_NFDWS BIT(1)
-+
-+#define CMR_LEN(len) ((len) - 1)
-+#define CMR_WID(width) (((width) >> 1) << 28)
-+
-+struct rtl_snand {
-+ struct device *dev;
-+ struct regmap *regmap;
-+ struct completion comp;
-+};
-+
-+static irqreturn_t rtl_snand_irq(int irq, void *data)
-+{
-+ struct rtl_snand *snand = data;
-+ u32 val = 0;
-+
-+ regmap_read(snand->regmap, SNAFSR, &val);
-+ if (val & (SNAFSR_NFCOS | SNAFSR_NFDRS | SNAFSR_NFDWS))
-+ return IRQ_NONE;
-+
-+ regmap_write(snand->regmap, SNAFDIR, SNAFDIR_DMA_IP);
-+ complete(&snand->comp);
-+
-+ return IRQ_HANDLED;
-+}
-+
-+static bool rtl_snand_supports_op(struct spi_mem *mem,
-+ const struct spi_mem_op *op)
-+{
-+ if (!spi_mem_default_supports_op(mem, op))
-+ return false;
-+ if (op->cmd.nbytes != 1 || op->cmd.buswidth != 1)
-+ return false;
-+ return true;
-+}
-+
-+static void rtl_snand_set_cs(struct rtl_snand *snand, int cs, bool active)
-+{
-+ u32 val;
-+
-+ if (active)
-+ val = ~(1 << (4 * cs));
-+ else
-+ val = ~0;
-+
-+ regmap_write(snand->regmap, SNAFCCR, val);
-+}
-+
-+static int rtl_snand_wait_ready(struct rtl_snand *snand)
-+{
-+ u32 val;
-+
-+ return regmap_read_poll_timeout(snand->regmap, SNAFSR, val, !(val & SNAFSR_NFCOS),
-+ 0, 2 * USEC_PER_MSEC);
-+}
-+
-+static int rtl_snand_xfer_head(struct rtl_snand *snand, int cs, const struct spi_mem_op *op)
-+{
-+ int ret;
-+ u32 val, len = 0;
-+
-+ rtl_snand_set_cs(snand, cs, true);
-+
-+ val = op->cmd.opcode << 24;
-+ len = 1;
-+ if (op->addr.nbytes && op->addr.buswidth == 1) {
-+ val |= op->addr.val << ((3 - op->addr.nbytes) * 8);
-+ len += op->addr.nbytes;
-+ }
-+
-+ ret = rtl_snand_wait_ready(snand);
-+ if (ret)
-+ return ret;
-+
-+ ret = regmap_write(snand->regmap, SNAFWCMR, CMR_LEN(len));
-+ if (ret)
-+ return ret;
-+
-+ ret = regmap_write(snand->regmap, SNAFWDR, val);
-+ if (ret)
-+ return ret;
-+
-+ ret = rtl_snand_wait_ready(snand);
-+ if (ret)
-+ return ret;
-+
-+ if (op->addr.buswidth > 1) {
-+ val = op->addr.val << ((3 - op->addr.nbytes) * 8);
-+ len = op->addr.nbytes;
-+
-+ ret = regmap_write(snand->regmap, SNAFWCMR,
-+ CMR_WID(op->addr.buswidth) | CMR_LEN(len));
-+ if (ret)
-+ return ret;
-+
-+ ret = regmap_write(snand->regmap, SNAFWDR, val);
-+ if (ret)
-+ return ret;
-+
-+ ret = rtl_snand_wait_ready(snand);
-+ if (ret)
-+ return ret;
-+ }
-+
-+ if (op->dummy.nbytes) {
-+ val = 0;
-+
-+ ret = regmap_write(snand->regmap, SNAFWCMR,
-+ CMR_WID(op->dummy.buswidth) | CMR_LEN(op->dummy.nbytes));
-+ if (ret)
-+ return ret;
-+
-+ ret = regmap_write(snand->regmap, SNAFWDR, val);
-+ if (ret)
-+ return ret;
-+
-+ ret = rtl_snand_wait_ready(snand);
-+ if (ret)
-+ return ret;
-+ }
-+
-+ return 0;
-+}
-+
-+static void rtl_snand_xfer_tail(struct rtl_snand *snand, int cs)
-+{
-+ rtl_snand_set_cs(snand, cs, false);
-+}
-+
-+static int rtl_snand_xfer(struct rtl_snand *snand, int cs, const struct spi_mem_op *op)
-+{
-+ unsigned int pos, nbytes;
-+ int ret;
-+ u32 val, len = 0;
-+
-+ ret = rtl_snand_xfer_head(snand, cs, op);
-+ if (ret)
-+ goto out_deselect;
-+
-+ if (op->data.dir == SPI_MEM_DATA_IN) {
-+ pos = 0;
-+ len = op->data.nbytes;
-+
-+ while (pos < len) {
-+ nbytes = len - pos;
-+ if (nbytes > 4)
-+ nbytes = 4;
-+
-+ ret = rtl_snand_wait_ready(snand);
-+ if (ret)
-+ goto out_deselect;
-+
-+ ret = regmap_write(snand->regmap, SNAFRCMR,
-+ CMR_WID(op->data.buswidth) | CMR_LEN(nbytes));
-+ if (ret)
-+ goto out_deselect;
-+
-+ ret = rtl_snand_wait_ready(snand);
-+ if (ret)
-+ goto out_deselect;
-+
-+ ret = regmap_read(snand->regmap, SNAFRDR, &val);
-+ if (ret)
-+ goto out_deselect;
-+
-+ memcpy(op->data.buf.in + pos, &val, nbytes);
-+
-+ pos += nbytes;
-+ }
-+ } else if (op->data.dir == SPI_MEM_DATA_OUT) {
-+ pos = 0;
-+ len = op->data.nbytes;
-+
-+ while (pos < len) {
-+ nbytes = len - pos;
-+ if (nbytes > 4)
-+ nbytes = 4;
-+
-+ memcpy(&val, op->data.buf.out + pos, nbytes);
-+
-+ pos += nbytes;
-+
-+ ret = regmap_write(snand->regmap, SNAFWCMR, CMR_LEN(nbytes));
-+ if (ret)
-+ goto out_deselect;
-+
-+ ret = regmap_write(snand->regmap, SNAFWDR, val);
-+ if (ret)
-+ goto out_deselect;
-+
-+ ret = rtl_snand_wait_ready(snand);
-+ if (ret)
-+ goto out_deselect;
-+ }
-+ }
-+
-+out_deselect:
-+ rtl_snand_xfer_tail(snand, cs);
-+
-+ if (ret)
-+ dev_err(snand->dev, "transfer failed %d\n", ret);
-+
-+ return ret;
-+}
-+
-+static int rtl_snand_dma_xfer(struct rtl_snand *snand, int cs, const struct spi_mem_op *op)
-+{
-+ int ret;
-+ dma_addr_t buf_dma;
-+ enum dma_data_direction dir;
-+ u32 trig;
-+
-+ ret = rtl_snand_xfer_head(snand, cs, op);
-+ if (ret)
-+ goto out_deselect;
-+
-+ if (op->data.dir == SPI_MEM_DATA_IN) {
-+ dir = DMA_FROM_DEVICE;
-+ trig = 0;
-+ } else if (op->data.dir == SPI_MEM_DATA_OUT) {
-+ dir = DMA_TO_DEVICE;
-+ trig = 1;
-+ } else {
-+ ret = -EOPNOTSUPP;
-+ goto out_deselect;
-+ }
-+
-+ buf_dma = dma_map_single(snand->dev, op->data.buf.in, op->data.nbytes, dir);
-+ ret = dma_mapping_error(snand->dev, buf_dma);
-+ if (ret)
-+ goto out_deselect;
-+
-+ ret = regmap_write(snand->regmap, SNAFDIR, SNAFDIR_DMA_IP);
-+ if (ret)
-+ goto out_unmap;
-+
-+ ret = regmap_update_bits(snand->regmap, SNAFCFR, SNAFCFR_DMA_IE, SNAFCFR_DMA_IE);
-+ if (ret)
-+ goto out_unmap;
-+
-+ reinit_completion(&snand->comp);
-+
-+ ret = regmap_write(snand->regmap, SNAFDRSAR, buf_dma);
-+ if (ret)
-+ goto out_disable_int;
-+
-+ ret = regmap_write(snand->regmap, SNAFDLR,
-+ CMR_WID(op->data.buswidth) | (op->data.nbytes & 0xffff));
-+ if (ret)
-+ goto out_disable_int;
-+
-+ ret = regmap_write(snand->regmap, SNAFDTR, trig);
-+ if (ret)
-+ goto out_disable_int;
-+
-+ if (!wait_for_completion_timeout(&snand->comp, usecs_to_jiffies(20000)))
-+ ret = -ETIMEDOUT;
-+
-+ if (ret)
-+ goto out_disable_int;
-+
-+out_disable_int:
-+ regmap_update_bits(snand->regmap, SNAFCFR, SNAFCFR_DMA_IE, 0);
-+out_unmap:
-+ dma_unmap_single(snand->dev, buf_dma, op->data.nbytes, dir);
-+out_deselect:
-+ rtl_snand_xfer_tail(snand, cs);
-+
-+ if (ret)
-+ dev_err(snand->dev, "transfer failed %d\n", ret);
-+
-+ return ret;
-+}
-+
-+static bool rtl_snand_dma_op(const struct spi_mem_op *op)
-+{
-+ switch (op->data.dir) {
-+ case SPI_MEM_DATA_IN:
-+ case SPI_MEM_DATA_OUT:
-+ return op->data.nbytes > 32;
-+ default:
-+ return false;
-+ }
-+}
-+
-+static int rtl_snand_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
-+{
-+ struct rtl_snand *snand = spi_controller_get_devdata(mem->spi->controller);
-+ int cs = spi_get_chipselect(mem->spi, 0);
-+
-+ dev_dbg(snand->dev, "cs %d op cmd %02x %d:%d, dummy %d:%d, addr %08llx@%d:%d, data %d:%d\n",
-+ cs, op->cmd.opcode,
-+ op->cmd.buswidth, op->cmd.nbytes, op->dummy.buswidth,
-+ op->dummy.nbytes, op->addr.val, op->addr.buswidth,
-+ op->addr.nbytes, op->data.buswidth, op->data.nbytes);
-+
-+ if (rtl_snand_dma_op(op))
-+ return rtl_snand_dma_xfer(snand, cs, op);
-+ else
-+ return rtl_snand_xfer(snand, cs, op);
-+}
-+
-+static const struct spi_controller_mem_ops rtl_snand_mem_ops = {
-+ .supports_op = rtl_snand_supports_op,
-+ .exec_op = rtl_snand_exec_op,
-+};
-+
-+static const struct of_device_id rtl_snand_match[] = {
-+ { .compatible = "realtek,rtl9301-snand" },
-+ { .compatible = "realtek,rtl9302b-snand" },
-+ { .compatible = "realtek,rtl9302c-snand" },
-+ { .compatible = "realtek,rtl9303-snand" },
-+ {},
-+};
-+MODULE_DEVICE_TABLE(of, rtl_snand_match);
-+
-+static int rtl_snand_probe(struct platform_device *pdev)
-+{
-+ struct rtl_snand *snand;
-+ struct device *dev = &pdev->dev;
-+ struct spi_controller *ctrl;
-+ void __iomem *base;
-+ const struct regmap_config rc = {
-+ .reg_bits = 32,
-+ .val_bits = 32,
-+ .reg_stride = 4,
-+ .cache_type = REGCACHE_NONE,
-+ };
-+ int irq, ret;
-+
-+ ctrl = devm_spi_alloc_host(dev, sizeof(*snand));
-+ if (!ctrl)
-+ return -ENOMEM;
-+
-+ snand = spi_controller_get_devdata(ctrl);
-+ snand->dev = dev;
-+
-+ base = devm_platform_ioremap_resource(pdev, 0);
-+ if (IS_ERR(base))
-+ return PTR_ERR(base);
-+
-+ snand->regmap = devm_regmap_init_mmio(dev, base, &rc);
-+ if (IS_ERR(snand->regmap))
-+ return PTR_ERR(snand->regmap);
-+
-+ init_completion(&snand->comp);
-+
-+ irq = platform_get_irq(pdev, 0);
-+ if (irq < 0)
-+ return irq;
-+
-+ ret = dma_set_mask(snand->dev, DMA_BIT_MASK(32));
-+ if (ret)
-+ return dev_err_probe(dev, ret, "failed to set DMA mask\n");
-+
-+ ret = devm_request_irq(dev, irq, rtl_snand_irq, 0, "rtl-snand", snand);
-+ if (ret)
-+ return dev_err_probe(dev, ret, "failed to request irq\n");
-+
-+ ctrl->num_chipselect = 2;
-+ ctrl->mem_ops = &rtl_snand_mem_ops;
-+ ctrl->bits_per_word_mask = SPI_BPW_MASK(8);
-+ ctrl->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD | SPI_TX_DUAL | SPI_TX_QUAD;
-+ device_set_node(&ctrl->dev, dev_fwnode(dev));
-+
-+ return devm_spi_register_controller(dev, ctrl);
-+}
-+
-+static struct platform_driver rtl_snand_driver = {
-+ .driver = {
-+ .name = "realtek-rtl-snand",
-+ .of_match_table = rtl_snand_match,
-+ },
-+ .probe = rtl_snand_probe,
-+};
-+module_platform_driver(rtl_snand_driver);
-+
-+MODULE_DESCRIPTION("Realtek SPI-NAND Flash Controller Driver");
-+MODULE_LICENSE("GPL");
+++ /dev/null
-From 25d284715845a465a1a3693a09cf8b6ab8bd9caf Mon Sep 17 00:00:00 2001
-From: Chris Packham <chris.packham@alliedtelesis.co.nz>
-Date: Thu, 31 Oct 2024 08:49:20 +1300
-Subject: [PATCH] spi: spi-mem: rtl-snand: Correctly handle DMA transfers
-
-The RTL9300 has some limitations on the maximum DMA transfers possible.
-For reads this is 2080 bytes (520*4) for writes this is 520 bytes. Deal
-with this by splitting transfers into appropriately sized parts.
-
-Fixes: 42d20a6a61b8 ("spi: spi-mem: Add Realtek SPI-NAND controller")
-Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
-Link: https://patch.msgid.link/20241030194920.3202282-1-chris.packham@alliedtelesis.co.nz
-Signed-off-by: Mark Brown <broonie@kernel.org>
----
- drivers/spi/spi-realtek-rtl-snand.c | 46 +++++++++++++++++++----------
- 1 file changed, 30 insertions(+), 16 deletions(-)
-
---- a/drivers/spi/spi-realtek-rtl-snand.c
-+++ b/drivers/spi/spi-realtek-rtl-snand.c
-@@ -231,19 +231,22 @@ out_deselect:
-
- static int rtl_snand_dma_xfer(struct rtl_snand *snand, int cs, const struct spi_mem_op *op)
- {
-+ unsigned int pos, nbytes;
- int ret;
- dma_addr_t buf_dma;
- enum dma_data_direction dir;
-- u32 trig;
-+ u32 trig, len, maxlen;
-
- ret = rtl_snand_xfer_head(snand, cs, op);
- if (ret)
- goto out_deselect;
-
- if (op->data.dir == SPI_MEM_DATA_IN) {
-+ maxlen = 2080;
- dir = DMA_FROM_DEVICE;
- trig = 0;
- } else if (op->data.dir == SPI_MEM_DATA_OUT) {
-+ maxlen = 520;
- dir = DMA_TO_DEVICE;
- trig = 1;
- } else {
-@@ -264,26 +267,37 @@ static int rtl_snand_dma_xfer(struct rtl
- if (ret)
- goto out_unmap;
-
-- reinit_completion(&snand->comp);
-+ pos = 0;
-+ len = op->data.nbytes;
-
-- ret = regmap_write(snand->regmap, SNAFDRSAR, buf_dma);
-- if (ret)
-- goto out_disable_int;
-+ while (pos < len) {
-+ nbytes = len - pos;
-+ if (nbytes > maxlen)
-+ nbytes = maxlen;
-
-- ret = regmap_write(snand->regmap, SNAFDLR,
-- CMR_WID(op->data.buswidth) | (op->data.nbytes & 0xffff));
-- if (ret)
-- goto out_disable_int;
-+ reinit_completion(&snand->comp);
-
-- ret = regmap_write(snand->regmap, SNAFDTR, trig);
-- if (ret)
-- goto out_disable_int;
-+ ret = regmap_write(snand->regmap, SNAFDRSAR, buf_dma + pos);
-+ if (ret)
-+ goto out_disable_int;
-
-- if (!wait_for_completion_timeout(&snand->comp, usecs_to_jiffies(20000)))
-- ret = -ETIMEDOUT;
-+ pos += nbytes;
-
-- if (ret)
-- goto out_disable_int;
-+ ret = regmap_write(snand->regmap, SNAFDLR,
-+ CMR_WID(op->data.buswidth) | nbytes);
-+ if (ret)
-+ goto out_disable_int;
-+
-+ ret = regmap_write(snand->regmap, SNAFDTR, trig);
-+ if (ret)
-+ goto out_disable_int;
-+
-+ if (!wait_for_completion_timeout(&snand->comp, usecs_to_jiffies(20000)))
-+ ret = -ETIMEDOUT;
-+
-+ if (ret)
-+ goto out_disable_int;
-+ }
-
- out_disable_int:
- regmap_update_bits(snand->regmap, SNAFCFR, SNAFCFR_DMA_IE, 0);
+++ /dev/null
-From c5eda0333076e031197816454998a918f1de0831 Mon Sep 17 00:00:00 2001
-From: Chris Packham <chris.packham@alliedtelesis.co.nz>
-Date: Fri, 1 Nov 2024 09:03:46 +1300
-Subject: [PATCH] dt-bindings: i2c: Add Realtek RTL I2C Controller
-
-Add dt-schema for the I2C controller on the RTL9300 Ethernet switch
-with integrated SoC.
-
-Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
-Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
-Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
-
---- /dev/null
-+++ b/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml
-@@ -0,0 +1,69 @@
-+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
-+%YAML 1.2
-+---
-+$id: http://devicetree.org/schemas/i2c/realtek,rtl9301-i2c.yaml#
-+$schema: http://devicetree.org/meta-schemas/core.yaml#
-+
-+title: Realtek RTL I2C Controller
-+
-+maintainers:
-+ - Chris Packham <chris.packham@alliedtelesis.co.nz>
-+
-+description:
-+ The RTL9300 SoC has two I2C controllers. Each of these has an SCL line (which
-+ if not-used for SCL can be a GPIO). There are 8 common SDA lines that can be
-+ assigned to either I2C controller.
-+
-+properties:
-+ compatible:
-+ oneOf:
-+ - items:
-+ - enum:
-+ - realtek,rtl9302b-i2c
-+ - realtek,rtl9302c-i2c
-+ - realtek,rtl9303-i2c
-+ - const: realtek,rtl9301-i2c
-+ - const: realtek,rtl9301-i2c
-+
-+ reg:
-+ description: Register offset and size this I2C controller.
-+
-+ "#address-cells":
-+ const: 1
-+
-+ "#size-cells":
-+ const: 0
-+
-+patternProperties:
-+ '^i2c@[0-7]$':
-+ $ref: /schemas/i2c/i2c-controller.yaml
-+ unevaluatedProperties: false
-+
-+ properties:
-+ reg:
-+ description: The SDA pin associated with the I2C bus.
-+ maxItems: 1
-+
-+ required:
-+ - reg
-+
-+required:
-+ - compatible
-+ - reg
-+
-+additionalProperties: false
-+
-+examples:
-+ - |
-+ i2c@36c {
-+ compatible = "realtek,rtl9301-i2c";
-+ reg = <0x36c 0x14>;
-+ #address-cells = <1>;
-+ #size-cells = <0>;
-+
-+ i2c@2 {
-+ reg = <2>;
-+ #address-cells = <1>;
-+ #size-cells = <0>;
-+ };
-+ };
+++ /dev/null
-From c366be720235301fdadf67e6f1ea6ff32669c074 Mon Sep 17 00:00:00 2001
-From: Chris Packham <chris.packham@alliedtelesis.co.nz>
-Date: Wed, 6 Nov 2024 13:18:35 +1300
-Subject: [PATCH] i2c: Add driver for the RTL9300 I2C controller
-
-Add support for the I2C controller on the RTL9300 SoC. There are two I2C
-controllers in the RTL9300 that are part of the Ethernet switch register
-block. Each of these controllers owns a SCL pin (GPIO8 for the fiorst
-I2C controller, GPIO17 for the second). There are 8 possible SDA pins
-(GPIO9-16) that can be assigned to either I2C controller. This
-relationship is represented in the device tree with a child node for
-each SDA line in use.
-
-This is based on the openwrt implementation[1] but has been
-significantly modified
-
-[1] - https://git.openwrt.org/?p=openwrt/openwrt.git;a=blob;f=target/linux/realtek/files-5.15/drivers/i2c/busses/i2c-rtl9300.c
-
-Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
-Reviewed-by: Andi Shyti <andi.shyti@kernel.org>
-Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
-
---- a/MAINTAINERS
-+++ b/MAINTAINERS
-@@ -20164,6 +20164,13 @@ S: Maintained
- T: git https://github.com/pkshih/rtw.git
- F: drivers/net/wireless/realtek/rtl8xxxu/
-
-+RTL9300 I2C DRIVER (rtl9300-i2c)
-+M: Chris Packham <chris.packham@alliedtelesis.co.nz>
-+L: linux-i2c@vger.kernel.org
-+S: Maintained
-+F: Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml
-+F: drivers/i2c/busses/i2c-rtl9300.c
-+
- RTRS TRANSPORT DRIVERS
- M: Md. Haris Iqbal <haris.iqbal@ionos.com>
- M: Jack Wang <jinpu.wang@ionos.com>
---- a/drivers/i2c/busses/Kconfig
-+++ b/drivers/i2c/busses/Kconfig
-@@ -1062,6 +1062,16 @@ config I2C_RK3X
- This driver can also be built as a module. If so, the module will
- be called i2c-rk3x.
-
-+config I2C_RTL9300
-+ tristate "Realtek RTL9300 I2C controller"
-+ depends on MACH_REALTEK_RTL || COMPILE_TEST
-+ help
-+ Say Y here to include support for the I2C controller in Realtek
-+ RTL9300 SoCs.
-+
-+ This driver can also be built as a module. If so, the module will
-+ be called i2c-rtl9300.
-+
- config I2C_RZV2M
- tristate "Renesas RZ/V2M adapter"
- depends on ARCH_RENESAS || COMPILE_TEST
---- a/drivers/i2c/busses/Makefile
-+++ b/drivers/i2c/busses/Makefile
-@@ -103,6 +103,7 @@ obj-$(CONFIG_I2C_QCOM_GENI) += i2c-qcom-
- obj-$(CONFIG_I2C_QUP) += i2c-qup.o
- obj-$(CONFIG_I2C_RIIC) += i2c-riic.o
- obj-$(CONFIG_I2C_RK3X) += i2c-rk3x.o
-+obj-$(CONFIG_I2C_RTL9300) += i2c-rtl9300.o
- obj-$(CONFIG_I2C_RZV2M) += i2c-rzv2m.o
- obj-$(CONFIG_I2C_S3C2410) += i2c-s3c2410.o
- obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o
---- /dev/null
-+++ b/drivers/i2c/busses/i2c-rtl9300.c
-@@ -0,0 +1,423 @@
-+// SPDX-License-Identifier: GPL-2.0-only
-+
-+#include <linux/bits.h>
-+#include <linux/i2c.h>
-+#include <linux/i2c-mux.h>
-+#include <linux/mod_devicetable.h>
-+#include <linux/mfd/syscon.h>
-+#include <linux/mutex.h>
-+#include <linux/platform_device.h>
-+#include <linux/regmap.h>
-+
-+enum rtl9300_bus_freq {
-+ RTL9300_I2C_STD_FREQ,
-+ RTL9300_I2C_FAST_FREQ,
-+};
-+
-+struct rtl9300_i2c;
-+
-+struct rtl9300_i2c_chan {
-+ struct i2c_adapter adap;
-+ struct rtl9300_i2c *i2c;
-+ enum rtl9300_bus_freq bus_freq;
-+ u8 sda_pin;
-+};
-+
-+#define RTL9300_I2C_MUX_NCHAN 8
-+
-+struct rtl9300_i2c {
-+ struct regmap *regmap;
-+ struct device *dev;
-+ struct rtl9300_i2c_chan chans[RTL9300_I2C_MUX_NCHAN];
-+ u32 reg_base;
-+ u8 sda_pin;
-+ struct mutex lock;
-+};
-+
-+#define RTL9300_I2C_MST_CTRL1 0x0
-+#define RTL9300_I2C_MST_CTRL1_MEM_ADDR_OFS 8
-+#define RTL9300_I2C_MST_CTRL1_MEM_ADDR_MASK GENMASK(31, 8)
-+#define RTL9300_I2C_MST_CTRL1_SDA_OUT_SEL_OFS 4
-+#define RTL9300_I2C_MST_CTRL1_SDA_OUT_SEL_MASK GENMASK(6, 4)
-+#define RTL9300_I2C_MST_CTRL1_GPIO_SCL_SEL BIT(3)
-+#define RTL9300_I2C_MST_CTRL1_RWOP BIT(2)
-+#define RTL9300_I2C_MST_CTRL1_I2C_FAIL BIT(1)
-+#define RTL9300_I2C_MST_CTRL1_I2C_TRIG BIT(0)
-+#define RTL9300_I2C_MST_CTRL2 0x4
-+#define RTL9300_I2C_MST_CTRL2_RD_MODE BIT(15)
-+#define RTL9300_I2C_MST_CTRL2_DEV_ADDR_OFS 8
-+#define RTL9300_I2C_MST_CTRL2_DEV_ADDR_MASK GENMASK(14, 8)
-+#define RTL9300_I2C_MST_CTRL2_DATA_WIDTH_OFS 4
-+#define RTL9300_I2C_MST_CTRL2_DATA_WIDTH_MASK GENMASK(7, 4)
-+#define RTL9300_I2C_MST_CTRL2_MEM_ADDR_WIDTH_OFS 2
-+#define RTL9300_I2C_MST_CTRL2_MEM_ADDR_WIDTH_MASK GENMASK(3, 2)
-+#define RTL9300_I2C_MST_CTRL2_SCL_FREQ_OFS 0
-+#define RTL9300_I2C_MST_CTRL2_SCL_FREQ_MASK GENMASK(1, 0)
-+#define RTL9300_I2C_MST_DATA_WORD0 0x8
-+#define RTL9300_I2C_MST_DATA_WORD1 0xc
-+#define RTL9300_I2C_MST_DATA_WORD2 0x10
-+#define RTL9300_I2C_MST_DATA_WORD3 0x14
-+
-+#define RTL9300_I2C_MST_GLB_CTRL 0x384
-+
-+static int rtl9300_i2c_reg_addr_set(struct rtl9300_i2c *i2c, u32 reg, u16 len)
-+{
-+ u32 val, mask;
-+ int ret;
-+
-+ val = len << RTL9300_I2C_MST_CTRL2_MEM_ADDR_WIDTH_OFS;
-+ mask = RTL9300_I2C_MST_CTRL2_MEM_ADDR_WIDTH_MASK;
-+
-+ ret = regmap_update_bits(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL2, mask, val);
-+ if (ret)
-+ return ret;
-+
-+ val = reg << RTL9300_I2C_MST_CTRL1_MEM_ADDR_OFS;
-+ mask = RTL9300_I2C_MST_CTRL1_MEM_ADDR_MASK;
-+
-+ return regmap_update_bits(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL1, mask, val);
-+}
-+
-+static int rtl9300_i2c_config_io(struct rtl9300_i2c *i2c, u8 sda_pin)
-+{
-+ int ret;
-+ u32 val, mask;
-+
-+ ret = regmap_update_bits(i2c->regmap, RTL9300_I2C_MST_GLB_CTRL, BIT(sda_pin), BIT(sda_pin));
-+ if (ret)
-+ return ret;
-+
-+ val = (sda_pin << RTL9300_I2C_MST_CTRL1_SDA_OUT_SEL_OFS) |
-+ RTL9300_I2C_MST_CTRL1_GPIO_SCL_SEL;
-+ mask = RTL9300_I2C_MST_CTRL1_SDA_OUT_SEL_MASK | RTL9300_I2C_MST_CTRL1_GPIO_SCL_SEL;
-+
-+ return regmap_update_bits(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL1, mask, val);
-+}
-+
-+static int rtl9300_i2c_config_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan,
-+ u16 addr, u16 len)
-+{
-+ u32 val, mask;
-+
-+ val = chan->bus_freq << RTL9300_I2C_MST_CTRL2_SCL_FREQ_OFS;
-+ mask = RTL9300_I2C_MST_CTRL2_SCL_FREQ_MASK;
-+
-+ val |= addr << RTL9300_I2C_MST_CTRL2_DEV_ADDR_OFS;
-+ mask |= RTL9300_I2C_MST_CTRL2_DEV_ADDR_MASK;
-+
-+ val |= ((len - 1) & 0xf) << RTL9300_I2C_MST_CTRL2_DATA_WIDTH_OFS;
-+ mask |= RTL9300_I2C_MST_CTRL2_DATA_WIDTH_MASK;
-+
-+ mask |= RTL9300_I2C_MST_CTRL2_RD_MODE;
-+
-+ return regmap_update_bits(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL2, mask, val);
-+}
-+
-+static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, int len)
-+{
-+ u32 vals[4] = {};
-+ int i, ret;
-+
-+ if (len > 16)
-+ return -EIO;
-+
-+ ret = regmap_bulk_read(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0,
-+ vals, ARRAY_SIZE(vals));
-+ if (ret)
-+ return ret;
-+
-+ for (i = 0; i < len; i++) {
-+ buf[i] = vals[i/4] & 0xff;
-+ vals[i/4] >>= 8;
-+ }
-+
-+ return 0;
-+}
-+
-+static int rtl9300_i2c_write(struct rtl9300_i2c *i2c, u8 *buf, int len)
-+{
-+ u32 vals[4] = {};
-+ int i;
-+
-+ if (len > 16)
-+ return -EIO;
-+
-+ for (i = 0; i < len; i++) {
-+ if (i % 4 == 0)
-+ vals[i/4] = 0;
-+ vals[i/4] <<= 8;
-+ vals[i/4] |= buf[i];
-+ }
-+
-+ return regmap_bulk_write(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0,
-+ vals, ARRAY_SIZE(vals));
-+}
-+
-+static int rtl9300_i2c_writel(struct rtl9300_i2c *i2c, u32 data)
-+{
-+ return regmap_write(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0, data);
-+}
-+
-+static int rtl9300_i2c_execute_xfer(struct rtl9300_i2c *i2c, char read_write,
-+ int size, union i2c_smbus_data *data, int len)
-+{
-+ u32 val, mask;
-+ int ret;
-+
-+ val = read_write == I2C_SMBUS_WRITE ? RTL9300_I2C_MST_CTRL1_RWOP : 0;
-+ mask = RTL9300_I2C_MST_CTRL1_RWOP;
-+
-+ val |= RTL9300_I2C_MST_CTRL1_I2C_TRIG;
-+ mask |= RTL9300_I2C_MST_CTRL1_I2C_TRIG;
-+
-+ ret = regmap_update_bits(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL1, mask, val);
-+ if (ret)
-+ return ret;
-+
-+ ret = regmap_read_poll_timeout(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL1,
-+ val, !(val & RTL9300_I2C_MST_CTRL1_I2C_TRIG), 100, 2000);
-+ if (ret)
-+ return ret;
-+
-+ if (val & RTL9300_I2C_MST_CTRL1_I2C_FAIL)
-+ return -EIO;
-+
-+ if (read_write == I2C_SMBUS_READ) {
-+ if (size == I2C_SMBUS_BYTE || size == I2C_SMBUS_BYTE_DATA) {
-+ ret = regmap_read(i2c->regmap,
-+ i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0, &val);
-+ if (ret)
-+ return ret;
-+ data->byte = val & 0xff;
-+ } else if (size == I2C_SMBUS_WORD_DATA) {
-+ ret = regmap_read(i2c->regmap,
-+ i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0, &val);
-+ if (ret)
-+ return ret;
-+ data->word = val & 0xffff;
-+ } else {
-+ ret = rtl9300_i2c_read(i2c, &data->block[0], len);
-+ if (ret)
-+ return ret;
-+ }
-+ }
-+
-+ return 0;
-+}
-+
-+static int rtl9300_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned short flags,
-+ char read_write, u8 command, int size,
-+ union i2c_smbus_data *data)
-+{
-+ struct rtl9300_i2c_chan *chan = i2c_get_adapdata(adap);
-+ struct rtl9300_i2c *i2c = chan->i2c;
-+ int len = 0, ret;
-+
-+ mutex_lock(&i2c->lock);
-+ if (chan->sda_pin != i2c->sda_pin) {
-+ ret = rtl9300_i2c_config_io(i2c, chan->sda_pin);
-+ if (ret)
-+ goto out_unlock;
-+ i2c->sda_pin = chan->sda_pin;
-+ }
-+
-+ switch (size) {
-+ case I2C_SMBUS_QUICK:
-+ ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 0);
-+ if (ret)
-+ goto out_unlock;
-+ ret = rtl9300_i2c_reg_addr_set(i2c, 0, 0);
-+ if (ret)
-+ goto out_unlock;
-+ break;
-+
-+ case I2C_SMBUS_BYTE:
-+ if (read_write == I2C_SMBUS_WRITE) {
-+ ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 0);
-+ if (ret)
-+ goto out_unlock;
-+ ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
-+ if (ret)
-+ goto out_unlock;
-+ } else {
-+ ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 1);
-+ if (ret)
-+ goto out_unlock;
-+ ret = rtl9300_i2c_reg_addr_set(i2c, 0, 0);
-+ if (ret)
-+ goto out_unlock;
-+ }
-+ break;
-+
-+ case I2C_SMBUS_BYTE_DATA:
-+ ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
-+ if (ret)
-+ goto out_unlock;
-+ ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 1);
-+ if (ret)
-+ goto out_unlock;
-+ if (read_write == I2C_SMBUS_WRITE) {
-+ ret = rtl9300_i2c_writel(i2c, data->byte);
-+ if (ret)
-+ goto out_unlock;
-+ }
-+ break;
-+
-+ case I2C_SMBUS_WORD_DATA:
-+ ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
-+ if (ret)
-+ goto out_unlock;
-+ ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 2);
-+ if (ret)
-+ goto out_unlock;
-+ if (read_write == I2C_SMBUS_WRITE) {
-+ ret = rtl9300_i2c_writel(i2c, data->word);
-+ if (ret)
-+ goto out_unlock;
-+ }
-+ break;
-+
-+ case I2C_SMBUS_BLOCK_DATA:
-+ ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
-+ if (ret)
-+ goto out_unlock;
-+ ret = rtl9300_i2c_config_xfer(i2c, chan, addr, data->block[0]);
-+ if (ret)
-+ goto out_unlock;
-+ if (read_write == I2C_SMBUS_WRITE) {
-+ ret = rtl9300_i2c_write(i2c, &data->block[1], data->block[0]);
-+ if (ret)
-+ goto out_unlock;
-+ }
-+ len = data->block[0];
-+ break;
-+
-+ default:
-+ dev_err(&adap->dev, "Unsupported transaction %d\n", size);
-+ ret = -EOPNOTSUPP;
-+ goto out_unlock;
-+ }
-+
-+ ret = rtl9300_i2c_execute_xfer(i2c, read_write, size, data, len);
-+
-+out_unlock:
-+ mutex_unlock(&i2c->lock);
-+
-+ return ret;
-+}
-+
-+static u32 rtl9300_i2c_func(struct i2c_adapter *a)
-+{
-+ return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
-+ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
-+ I2C_FUNC_SMBUS_BLOCK_DATA;
-+}
-+
-+static const struct i2c_algorithm rtl9300_i2c_algo = {
-+ .smbus_xfer = rtl9300_i2c_smbus_xfer,
-+ .functionality = rtl9300_i2c_func,
-+};
-+
-+static struct i2c_adapter_quirks rtl9300_i2c_quirks = {
-+ .flags = I2C_AQ_NO_CLK_STRETCH,
-+ .max_read_len = 16,
-+ .max_write_len = 16,
-+};
-+
-+static int rtl9300_i2c_probe(struct platform_device *pdev)
-+{
-+ struct device *dev = &pdev->dev;
-+ struct rtl9300_i2c *i2c;
-+ u32 clock_freq, sda_pin;
-+ int ret, i = 0;
-+ struct fwnode_handle *child;
-+
-+ i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
-+ if (!i2c)
-+ return -ENOMEM;
-+
-+ i2c->regmap = syscon_node_to_regmap(dev->parent->of_node);
-+ if (IS_ERR(i2c->regmap))
-+ return PTR_ERR(i2c->regmap);
-+ i2c->dev = dev;
-+
-+ mutex_init(&i2c->lock);
-+
-+ ret = device_property_read_u32(dev, "reg", &i2c->reg_base);
-+ if (ret)
-+ return ret;
-+
-+ platform_set_drvdata(pdev, i2c);
-+
-+ if (device_get_child_node_count(dev) >= RTL9300_I2C_MUX_NCHAN)
-+ return dev_err_probe(dev, -EINVAL, "Too many channels\n");
-+
-+ device_for_each_child_node(dev, child) {
-+ struct rtl9300_i2c_chan *chan = &i2c->chans[i];
-+ struct i2c_adapter *adap = &chan->adap;
-+
-+ ret = fwnode_property_read_u32(child, "reg", &sda_pin);
-+ if (ret)
-+ return ret;
-+
-+ ret = fwnode_property_read_u32(child, "clock-frequency", &clock_freq);
-+ if (ret)
-+ clock_freq = I2C_MAX_STANDARD_MODE_FREQ;
-+
-+ switch (clock_freq) {
-+ case I2C_MAX_STANDARD_MODE_FREQ:
-+ chan->bus_freq = RTL9300_I2C_STD_FREQ;
-+ break;
-+
-+ case I2C_MAX_FAST_MODE_FREQ:
-+ chan->bus_freq = RTL9300_I2C_FAST_FREQ;
-+ break;
-+ default:
-+ dev_warn(i2c->dev, "SDA%d clock-frequency %d not supported using default\n",
-+ sda_pin, clock_freq);
-+ break;
-+ }
-+
-+ chan->sda_pin = sda_pin;
-+ chan->i2c = i2c;
-+ adap = &i2c->chans[i].adap;
-+ adap->owner = THIS_MODULE;
-+ adap->algo = &rtl9300_i2c_algo;
-+ adap->quirks = &rtl9300_i2c_quirks;
-+ adap->retries = 3;
-+ adap->dev.parent = dev;
-+ i2c_set_adapdata(adap, chan);
-+ adap->dev.of_node = to_of_node(child);
-+ snprintf(adap->name, sizeof(adap->name), "%s SDA%d\n", dev_name(dev), sda_pin);
-+ i++;
-+
-+ ret = devm_i2c_add_adapter(dev, adap);
-+ if (ret)
-+ return ret;
-+ }
-+ i2c->sda_pin = 0xff;
-+
-+ return 0;
-+}
-+
-+static const struct of_device_id i2c_rtl9300_dt_ids[] = {
-+ { .compatible = "realtek,rtl9301-i2c" },
-+ { .compatible = "realtek,rtl9302b-i2c" },
-+ { .compatible = "realtek,rtl9302c-i2c" },
-+ { .compatible = "realtek,rtl9303-i2c" },
-+ {}
-+};
-+MODULE_DEVICE_TABLE(of, i2c_rtl9300_dt_ids);
-+
-+static struct platform_driver rtl9300_i2c_driver = {
-+ .probe = rtl9300_i2c_probe,
-+ .driver = {
-+ .name = "i2c-rtl9300",
-+ .of_match_table = i2c_rtl9300_dt_ids,
-+ },
-+};
-+
-+module_platform_driver(rtl9300_i2c_driver);
-+
-+MODULE_DESCRIPTION("RTL9300 I2C controller driver");
-+MODULE_LICENSE("GPL");
+++ /dev/null
-From 5f05fc6e2218db7ecc52c60eb34b707fe69262c2 Mon Sep 17 00:00:00 2001
-From: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
-Date: Wed, 2 Jul 2025 08:15:31 +0200
-Subject: [PATCH] dt-bindings: i2c: realtek,rtl9301: Fix missing 'reg'
- constraint
-
-Lists should have fixed amount if items, so add missing constraint to
-the 'reg' property (only one address space entry).
-
-Fixes: c5eda0333076 ("dt-bindings: i2c: Add Realtek RTL I2C Controller")
-Cc: <stable@vger.kernel.org> # v6.13+
-Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
-Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
-Link: https://lore.kernel.org/r/20250702061530.6940-2-krzysztof.kozlowski@linaro.org
-
---- a/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml
-+++ b/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml
-@@ -26,7 +26,8 @@ properties:
- - const: realtek,rtl9301-i2c
-
- reg:
-- description: Register offset and size this I2C controller.
-+ items:
-+ - description: Register offset and size this I2C controller.
-
- "#address-cells":
- const: 1
+++ /dev/null
-From 57f312b955938fc4663f430cb57a71f2414f601b Mon Sep 17 00:00:00 2001
-From: Alex Guo <alexguo1023@gmail.com>
-Date: Sun, 10 Aug 2025 20:05:13 +0200
-Subject: [PATCH] i2c: rtl9300: Fix out-of-bounds bug in rtl9300_i2c_smbus_xfer
-
-The data->block[0] variable comes from user. Without proper check,
-the variable may be very large to cause an out-of-bounds bug.
-
-Fix this bug by checking the value of data->block[0] first.
-
-1. commit 39244cc75482 ("i2c: ismt: Fix an out-of-bounds bug in
- ismt_access()")
-2. commit 92fbb6d1296f ("i2c: xgene-slimpro: Fix out-of-bounds bug in
- xgene_slimpro_i2c_xfer()")
-
-Fixes: c366be720235 ("i2c: Add driver for the RTL9300 I2C controller")
-Signed-off-by: Alex Guo <alexguo1023@gmail.com>
-Cc: <stable@vger.kernel.org> # v6.13+
-Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
-Tested-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
-Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
-Signed-off-by: Sven Eckelmann <sven@narfation.org>
-Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
-Link: https://lore.kernel.org/r/20250810-i2c-rtl9300-multi-byte-v5-1-cd9dca0db722@narfation.org
-
---- a/drivers/i2c/busses/i2c-rtl9300.c
-+++ b/drivers/i2c/busses/i2c-rtl9300.c
-@@ -281,6 +281,10 @@ static int rtl9300_i2c_smbus_xfer(struct
- ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
- if (ret)
- goto out_unlock;
-+ if (data->block[0] < 1 || data->block[0] > I2C_SMBUS_BLOCK_MAX) {
-+ ret = -EINVAL;
-+ goto out_unlock;
-+ }
- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, data->block[0]);
- if (ret)
- goto out_unlock;
+++ /dev/null
-From d67b740b9edfa46310355e2b68050f79ebf05a4c Mon Sep 17 00:00:00 2001
-From: Harshal Gohel <hg@simonwunderlich.de>
-Date: Sun, 10 Aug 2025 20:05:14 +0200
-Subject: [PATCH] i2c: rtl9300: Fix multi-byte I2C write
-
-The RTL93xx I2C controller has 4 32 bit registers to store the bytes for
-the upcoming I2C transmission. The first byte is stored in the
-least-significant byte of the first register. And the last byte in the most
-significant byte of the last register. A map of the transferred bytes to
-their order in the registers is:
-
-reg 0: 0x04_03_02_01
-reg 1: 0x08_07_06_05
-reg 2: 0x0c_0b_0a_09
-reg 3: 0x10_0f_0e_0d
-
-The i2c_read() function basically demonstrates how the hardware would pick
-up bytes from this register set. But the i2c_write() function was just
-pushing bytes one after another to the least significant byte of a register
-AFTER shifting the last one to the next more significant byte position.
-
-If you would then have tried to send a buffer with numbers 1-11 using
-i2c_write(), you would have ended up with following register content:
-
-reg 0: 0x01_02_03_04
-reg 1: 0x05_06_07_08
-reg 2: 0x00_09_0a_0b
-reg 3: 0x00_00_00_00
-
-On the wire, you would then have seen:
-
- Sr Addr Wr [A] 04 A 03 A 02 A 01 A 08 A 07 A 06 A 05 A 0b A 0a A 09 A P
-
-But the correct data transmission was expected to be
-
- Sr Addr Wr [A] 01 A 02 A 03 A 04 A 05 A 06 A 07 A 08 A 09 A 0a A 0b A P
-
-Because of this multi-byte ordering problem, only single byte i2c_write()
-operations were executed correctly (on the wire).
-
-By shifting the byte directly to the correct end position in the register,
-it is possible to avoid this incorrect byte ordering and fix multi-byte
-transmissions.
-
-The second initialization (to 0) of vals was also be dropped because this
-array is initialized to 0 on the stack by using `= {};`. This makes the
-fix a lot more readable.
-
-Fixes: c366be720235 ("i2c: Add driver for the RTL9300 I2C controller")
-Signed-off-by: Harshal Gohel <hg@simonwunderlich.de>
-Cc: <stable@vger.kernel.org> # v6.13+
-Co-developed-by: Sven Eckelmann <sven@narfation.org>
-Signed-off-by: Sven Eckelmann <sven@narfation.org>
-Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
-Tested-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
-Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
-Link: https://lore.kernel.org/r/20250810-i2c-rtl9300-multi-byte-v5-2-cd9dca0db722@narfation.org
-
---- a/drivers/i2c/busses/i2c-rtl9300.c
-+++ b/drivers/i2c/busses/i2c-rtl9300.c
-@@ -143,10 +143,10 @@ static int rtl9300_i2c_write(struct rtl9
- return -EIO;
-
- for (i = 0; i < len; i++) {
-- if (i % 4 == 0)
-- vals[i/4] = 0;
-- vals[i/4] <<= 8;
-- vals[i/4] |= buf[i];
-+ unsigned int shift = (i % 4) * 8;
-+ unsigned int reg = i / 4;
-+
-+ vals[reg] |= buf[i] << shift;
- }
-
- return regmap_bulk_write(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0,
+++ /dev/null
-From ceee7776c010c5f09d30985c9e5223b363a6172a Mon Sep 17 00:00:00 2001
-From: Sven Eckelmann <sven@narfation.org>
-Date: Sun, 10 Aug 2025 20:05:15 +0200
-Subject: [PATCH] i2c: rtl9300: Increase timeout for transfer polling
-
-The timeout for transfers was only set to 2ms. Because of this relatively
-low limit, 12-byte read operations to the frontend MCU of a RTL8239 POE PSE
-chip cluster was consistently resulting in a timeout.
-
-The original OpenWrt downstream driver [1] was not using any timeout limit
-at all. This is also possible by setting the timeout_us parameter of
-regmap_read_poll_timeout() to 0. But since the driver currently implements
-the ETIMEDOUT error, it is more sensible to increase the timeout in such a
-way that communication with the (quite common) Realtek I2C-connected POE
-management solution is possible.
-
-[1] https://git.openwrt.org/?p=openwrt/openwrt.git;a=blob;f=target/linux/realtek/files-6.12/drivers/i2c/busses/i2c-rtl9300.c;h=c4d973195ef39dc56d6207e665d279745525fcac#l202
-
-Fixes: c366be720235 ("i2c: Add driver for the RTL9300 I2C controller")
-Signed-off-by: Sven Eckelmann <sven@narfation.org>
-Cc: <stable@vger.kernel.org> # v6.13+
-Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
-Tested-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
-Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
-Link: https://lore.kernel.org/r/20250810-i2c-rtl9300-multi-byte-v5-3-cd9dca0db722@narfation.org
-
---- a/drivers/i2c/busses/i2c-rtl9300.c
-+++ b/drivers/i2c/busses/i2c-rtl9300.c
-@@ -175,7 +175,7 @@ static int rtl9300_i2c_execute_xfer(stru
- return ret;
-
- ret = regmap_read_poll_timeout(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL1,
-- val, !(val & RTL9300_I2C_MST_CTRL1_I2C_TRIG), 100, 2000);
-+ val, !(val & RTL9300_I2C_MST_CTRL1_I2C_TRIG), 100, 100000);
- if (ret)
- return ret;
-
+++ /dev/null
-From 82b350dd8185ce790e61555c436f90b6501af23c Mon Sep 17 00:00:00 2001
-From: Sven Eckelmann <sven@narfation.org>
-Date: Sun, 10 Aug 2025 20:05:16 +0200
-Subject: [PATCH] i2c: rtl9300: Add missing count byte for SMBus Block Ops
-
-The expected on-wire format of an SMBus Block Write is
-
- S Addr Wr [A] Comm [A] Count [A] Data [A] Data [A] ... [A] Data [A] P
-
-Everything starting from the Count byte is provided by the I2C subsystem in
-the array data->block. But the driver was skipping the Count byte
-(data->block[0]) when sending it to the RTL93xx I2C controller.
-
-Only the actual data could be seen on the wire:
-
- S Addr Wr [A] Comm [A] Data [A] Data [A] ... [A] Data [A] P
-
-This wire format is not SMBus Block Write compatible but matches the format
-of an I2C Block Write. Simply adding the count byte to the buffer for the
-I2C controller is enough to fix the transmission.
-
-This also affects read because the I2C controller must receive the count
-byte + $count * data bytes.
-
-Fixes: c366be720235 ("i2c: Add driver for the RTL9300 I2C controller")
-Signed-off-by: Sven Eckelmann <sven@narfation.org>
-Cc: <stable@vger.kernel.org> # v6.13+
-Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
-Tested-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
-Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
-Link: https://lore.kernel.org/r/20250810-i2c-rtl9300-multi-byte-v5-4-cd9dca0db722@narfation.org
-
---- a/drivers/i2c/busses/i2c-rtl9300.c
-+++ b/drivers/i2c/busses/i2c-rtl9300.c
-@@ -285,15 +285,15 @@ static int rtl9300_i2c_smbus_xfer(struct
- ret = -EINVAL;
- goto out_unlock;
- }
-- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, data->block[0]);
-+ ret = rtl9300_i2c_config_xfer(i2c, chan, addr, data->block[0] + 1);
- if (ret)
- goto out_unlock;
- if (read_write == I2C_SMBUS_WRITE) {
-- ret = rtl9300_i2c_write(i2c, &data->block[1], data->block[0]);
-+ ret = rtl9300_i2c_write(i2c, &data->block[0], data->block[0] + 1);
- if (ret)
- goto out_unlock;
- }
-- len = data->block[0];
-+ len = data->block[0] + 1;
- break;
-
- default:
+++ /dev/null
-From cd6c956fbc13156bcbcca084b46a8380caebc2a8 Mon Sep 17 00:00:00 2001
-From: Jonas Jelonek <jelonek.jonas@gmail.com>
-Date: Sun, 31 Aug 2025 10:04:46 +0000
-Subject: [PATCH] i2c: rtl9300: fix channel number bound check
-
-Fix the current check for number of channels (child nodes in the device
-tree). Before, this was:
-
-if (device_get_child_node_count(dev) >= RTL9300_I2C_MUX_NCHAN)
-
-RTL9300_I2C_MUX_NCHAN gives the maximum number of channels so checking
-with '>=' isn't correct because it doesn't allow the last channel
-number. Thus, fix it to:
-
-if (device_get_child_node_count(dev) > RTL9300_I2C_MUX_NCHAN)
-
-Issue occured on a TP-Link TL-ST1008F v2.0 device (8 SFP+ ports) and fix
-is tested there.
-
-Fixes: c366be720235 ("i2c: Add driver for the RTL9300 I2C controller")
-Cc: stable@vger.kernel.org # v6.13+
-Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
-Tested-by: Sven Eckelmann <sven@narfation.org>
-Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
-Tested-by: Chris Packham <chris.packham@alliedtelesis.co.nz> # On RTL9302C based board
-Tested-by: Markus Stockhausen <markus.stockhausen@gmx.de>
-Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
-Link: https://lore.kernel.org/r/20250831100457.3114-2-jelonek.jonas@gmail.com
-
---- a/drivers/i2c/busses/i2c-rtl9300.c
-+++ b/drivers/i2c/busses/i2c-rtl9300.c
-@@ -353,7 +353,7 @@ static int rtl9300_i2c_probe(struct plat
-
- platform_set_drvdata(pdev, i2c);
-
-- if (device_get_child_node_count(dev) >= RTL9300_I2C_MUX_NCHAN)
-+ if (device_get_child_node_count(dev) > RTL9300_I2C_MUX_NCHAN)
- return dev_err_probe(dev, -EINVAL, "Too many channels\n");
-
- device_for_each_child_node(dev, child) {
+++ /dev/null
-From 06418cb5a1a542a003fdb4ad8e76ea542d57cfba Mon Sep 17 00:00:00 2001
-From: Jonas Jelonek <jelonek.jonas@gmail.com>
-Date: Sun, 31 Aug 2025 10:04:47 +0000
-Subject: [PATCH] i2c: rtl9300: ensure data length is within supported range
-
-Add an explicit check for the xfer length to 'rtl9300_i2c_config_xfer'
-to ensure the data length isn't within the supported range. In
-particular a data length of 0 is not supported by the hardware and
-causes unintended or destructive behaviour.
-
-This limitation becomes obvious when looking at the register
-documentation [1]. 4 bits are reserved for DATA_WIDTH and the value
-of these 4 bits is used as N + 1, allowing a data length range of
-1 <= len <= 16.
-
-Affected by this is the SMBus Quick Operation which works with a data
-length of 0. Passing 0 as the length causes an underflow of the value
-due to:
-
-(len - 1) & 0xf
-
-and effectively specifying a transfer length of 16 via the registers.
-This causes a 16-byte write operation instead of a Quick Write. For
-example, on SFP modules without write-protected EEPROM this soft-bricks
-them by overwriting some initial bytes.
-
-For completeness, also add a quirk for the zero length.
-
-[1] https://svanheule.net/realtek/longan/register/i2c_mst1_ctrl2
-
-Fixes: c366be720235 ("i2c: Add driver for the RTL9300 I2C controller")
-Cc: stable@vger.kernel.org # v6.13+
-Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
-Tested-by: Sven Eckelmann <sven@narfation.org>
-Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
-Tested-by: Chris Packham <chris.packham@alliedtelesis.co.nz> # On RTL9302C based board
-Tested-by: Markus Stockhausen <markus.stockhausen@gmx.de>
-Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
-Link: https://lore.kernel.org/r/20250831100457.3114-3-jelonek.jonas@gmail.com
-
---- a/drivers/i2c/busses/i2c-rtl9300.c
-+++ b/drivers/i2c/busses/i2c-rtl9300.c
-@@ -99,6 +99,9 @@ static int rtl9300_i2c_config_xfer(struc
- {
- u32 val, mask;
-
-+ if (len < 1 || len > 16)
-+ return -EINVAL;
-+
- val = chan->bus_freq << RTL9300_I2C_MST_CTRL2_SCL_FREQ_OFS;
- mask = RTL9300_I2C_MST_CTRL2_SCL_FREQ_MASK;
-
-@@ -323,7 +326,7 @@ static const struct i2c_algorithm rtl930
- };
-
- static struct i2c_adapter_quirks rtl9300_i2c_quirks = {
-- .flags = I2C_AQ_NO_CLK_STRETCH,
-+ .flags = I2C_AQ_NO_CLK_STRETCH | I2C_AQ_NO_ZERO_LEN,
- .max_read_len = 16,
- .max_write_len = 16,
- };
+++ /dev/null
-From ede965fd555ac2536cf651893a998dbfd8e57b86 Mon Sep 17 00:00:00 2001
-From: Jonas Jelonek <jelonek.jonas@gmail.com>
-Date: Sun, 31 Aug 2025 10:04:48 +0000
-Subject: [PATCH] i2c: rtl9300: remove broken SMBus Quick operation support
-
-Remove the SMBus Quick operation from this driver because it is not
-natively supported by the hardware and is wrongly implemented in the
-driver.
-
-The I2C controllers in Realtek RTL9300 and RTL9310 are SMBus-compliant
-but there doesn't seem to be native support for the SMBus Quick
-operation. It is not explicitly mentioned in the documentation but
-looking at the registers which configure an SMBus transaction, one can
-see that the data length cannot be set to 0. This suggests that the
-hardware doesn't allow any SMBus message without data bytes (except for
-those it does on it's own, see SMBus Block Read).
-
-The current implementation of SMBus Quick operation passes a length of
-0 (which is actually invalid). Before the fix of a bug in a previous
-commit, this led to a read operation of 16 bytes from any register (the
-one of a former transaction or any other value.
-
-This caused issues like soft-bricked SFP modules after a simple probe
-with i2cdetect which uses Quick by default. Running this with SFP
-modules whose EEPROM isn't write-protected, some of the initial bytes
-are overwritten because a 16-byte write operation is executed instead of
-a Quick Write. (This temporarily soft-bricked one of my DAC cables.)
-
-Because SMBus Quick operation is obviously not supported on these
-controllers (because a length of 0 cannot be set, even when no register
-address is set), remove that instead of claiming there is support. There
-also shouldn't be any kind of emulated 'Quick' which just does another
-kind of operation in the background. Otherwise, specific issues occur
-in case of a 'Quick' Write which actually writes unknown data to an
-unknown register.
-
-Fixes: c366be720235 ("i2c: Add driver for the RTL9300 I2C controller")
-Cc: stable@vger.kernel.org # v6.13+
-Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
-Tested-by: Sven Eckelmann <sven@narfation.org>
-Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
-Tested-by: Chris Packham <chris.packham@alliedtelesis.co.nz> # On RTL9302C based board
-Tested-by: Markus Stockhausen <markus.stockhausen@gmx.de>
-Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
-Link: https://lore.kernel.org/r/20250831100457.3114-4-jelonek.jonas@gmail.com
-
---- a/drivers/i2c/busses/i2c-rtl9300.c
-+++ b/drivers/i2c/busses/i2c-rtl9300.c
-@@ -225,15 +225,6 @@ static int rtl9300_i2c_smbus_xfer(struct
- }
-
- switch (size) {
-- case I2C_SMBUS_QUICK:
-- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 0);
-- if (ret)
-- goto out_unlock;
-- ret = rtl9300_i2c_reg_addr_set(i2c, 0, 0);
-- if (ret)
-- goto out_unlock;
-- break;
--
- case I2C_SMBUS_BYTE:
- if (read_write == I2C_SMBUS_WRITE) {
- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 0);
-@@ -315,9 +306,9 @@ out_unlock:
-
- static u32 rtl9300_i2c_func(struct i2c_adapter *a)
- {
-- return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
-- I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
-- I2C_FUNC_SMBUS_BLOCK_DATA;
-+ return I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
-+ I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA |
-+ I2C_FUNC_SMBUS_I2C_BLOCK;
- }
-
- static const struct i2c_algorithm rtl9300_i2c_algo = {
+++ /dev/null
-From 095530512152e6811278de9c30f170f0ac9705eb Mon Sep 17 00:00:00 2001
-From: Sven Eckelmann <sven@narfation.org>
-Date: Sat, 27 Sep 2025 11:52:16 +0200
-Subject: [PATCH] i2c: rtl9300: Drop unsupported I2C_FUNC_SMBUS_I2C_BLOCK
-
-While applying the patch for commit ede965fd555a ("i2c: rtl9300: remove
-broken SMBus Quick operation support"), a conflict was incorrectly solved
-by adding the I2C_FUNC_SMBUS_I2C_BLOCK feature flag. But the code to handle
-I2C_SMBUS_I2C_BLOCK_DATA requests will be added by a separate commit.
-
-Fixes: ede965fd555a ("i2c: rtl9300: remove broken SMBus Quick operation support")
-Signed-off-by: Sven Eckelmann <sven@narfation.org>
-Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
-
---- a/drivers/i2c/busses/i2c-rtl9300.c
-+++ b/drivers/i2c/busses/i2c-rtl9300.c
-@@ -307,8 +307,7 @@ out_unlock:
- static u32 rtl9300_i2c_func(struct i2c_adapter *a)
- {
- return I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
-- I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA |
-- I2C_FUNC_SMBUS_I2C_BLOCK;
-+ I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA;
- }
-
- static const struct i2c_algorithm rtl9300_i2c_algo = {
+++ /dev/null
-From 415216ae3196e67bdb9515519f219d553bd38d3a Mon Sep 17 00:00:00 2001
-From: Harshal Gohel <hg@simonwunderlich.de>
-Date: Sat, 27 Sep 2025 11:52:17 +0200
-Subject: [PATCH] i2c: rtl9300: Implement I2C block read and write
-
-It was noticed that the original implementation of SMBus Block Write in the
-driver was actually an I2C Block Write. Both differ only in the Count byte
-before the actual data:
-
- S Addr Wr [A] Comm [A] Count [A] Data [A] Data [A] ... [A] Data [A] P
-
-The I2C Block Write is just skipping this Count byte and starts directly
-with the data:
-
- S Addr Wr [A] Comm [A] Data [A] Data [A] ... [A] Data [A] P
-
-The I2C controller of RTL93xx doesn't handle this Count byte special and it
-is simply another one of (16 possible) data bytes. Adding support for the
-I2C Block Write therefore only requires skipping the count byte (0) in
-data->block.
-
-It is similar for reads. The SMBUS Block read is having a Count byte before
-the data:
-
- S Addr Wr [A] Comm [A]
- Sr Addr Rd [A] [Count] A [Data] A [Data] A ... A [Data] NA P
-
-And the I2C Block Read is directly starting with the actual data:
-
- S Addr Wr [A] Comm [A]
- Sr Addr Rd [A] [Data] A [Data] A ... A [Data] NA P
-
-The I2C controller is also not handling this byte in a special way. It
-simply provides every byte after the Rd marker + Ack as part of the 16 byte
-receive buffer (registers). The content of this buffer just has to be
-copied to the right position in the receive data->block.
-
-Signed-off-by: Harshal Gohel <hg@simonwunderlich.de>
-Co-developed-by: Sven Eckelmann <sven@narfation.org>
-Signed-off-by: Sven Eckelmann <sven@narfation.org>
-Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
-Tested-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
-Reviewed-by: Jonas Jelonek <jelonek.jonas@gmail.com>
-Tested-by: Jonas Jelonek <jelonek.jonas@gmail.com>
-
---- a/drivers/i2c/busses/i2c-rtl9300.c
-+++ b/drivers/i2c/busses/i2c-rtl9300.c
-@@ -186,22 +186,32 @@ static int rtl9300_i2c_execute_xfer(stru
- return -EIO;
-
- if (read_write == I2C_SMBUS_READ) {
-- if (size == I2C_SMBUS_BYTE || size == I2C_SMBUS_BYTE_DATA) {
-+ switch (size) {
-+ case I2C_SMBUS_BYTE:
-+ case I2C_SMBUS_BYTE_DATA:
- ret = regmap_read(i2c->regmap,
- i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0, &val);
- if (ret)
- return ret;
- data->byte = val & 0xff;
-- } else if (size == I2C_SMBUS_WORD_DATA) {
-+ break;
-+ case I2C_SMBUS_WORD_DATA:
- ret = regmap_read(i2c->regmap,
- i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0, &val);
- if (ret)
- return ret;
- data->word = val & 0xffff;
-- } else {
-+ break;
-+ case I2C_SMBUS_I2C_BLOCK_DATA:
-+ ret = rtl9300_i2c_read(i2c, &data->block[1], len);
-+ if (ret)
-+ return ret;
-+ break;
-+ default:
- ret = rtl9300_i2c_read(i2c, &data->block[0], len);
- if (ret)
- return ret;
-+ break;
- }
- }
-
-@@ -290,6 +300,25 @@ static int rtl9300_i2c_smbus_xfer(struct
- len = data->block[0] + 1;
- break;
-
-+ case I2C_SMBUS_I2C_BLOCK_DATA:
-+ ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
-+ if (ret)
-+ goto out_unlock;
-+ if (data->block[0] < 1 || data->block[0] > I2C_SMBUS_BLOCK_MAX) {
-+ ret = -EINVAL;
-+ goto out_unlock;
-+ }
-+ ret = rtl9300_i2c_config_xfer(i2c, chan, addr, data->block[0]);
-+ if (ret)
-+ goto out_unlock;
-+ if (read_write == I2C_SMBUS_WRITE) {
-+ ret = rtl9300_i2c_write(i2c, &data->block[1], data->block[0]);
-+ if (ret)
-+ goto out_unlock;
-+ }
-+ len = data->block[0];
-+ break;
-+
- default:
- dev_err(&adap->dev, "Unsupported transaction %d\n", size);
- ret = -EOPNOTSUPP;
-@@ -307,7 +336,8 @@ out_unlock:
- static u32 rtl9300_i2c_func(struct i2c_adapter *a)
- {
- return I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
-- I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA;
-+ I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA |
-+ I2C_FUNC_SMBUS_I2C_BLOCK;
- }
-
- static const struct i2c_algorithm rtl9300_i2c_algo = {
+++ /dev/null
-From 5a6ecb27435ef7a67d7bec4543f0c6303f34e8a6 Mon Sep 17 00:00:00 2001
-From: Jonas Jelonek <jelonek.jonas@gmail.com>
-Date: Sat, 27 Sep 2025 10:19:23 +0000
-Subject: [PATCH] i2c: rtl9300: use regmap fields and API for registers
-
-Adapt the RTL9300 I2C controller driver to use more of the regmap
-API, especially make use of reg_field and regmap_field instead of macros
-to represent registers. Most register operations are performed through
-regmap_field_* API then.
-
-Handle SCL selection using separate chip-specific functions since this
-is already known to differ between the Realtek SoC families in such a
-way that this cannot be properly handled using just a different
-reg_field.
-
-This makes it easier to add support for newer generations or to handle
-differences between specific revisions within a series. Just by
-defining a separate driver data structure with the corresponding
-register field definitions and linking it to a new compatible.
-
-Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
-Tested-by: Sven Eckelmann <sven@narfation.org>
-Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
-Tested-by: Chris Packham <chris.packham@alliedtelesis.co.nz> # On RTL9302C based board
-Tested-by: Markus Stockhausen <markus.stockhausen@gmx.de>
-Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
-Link: https://lore.kernel.org/r/20250927101931.71575-2-jelonek.jonas@gmail.com
-
---- a/drivers/i2c/busses/i2c-rtl9300.c
-+++ b/drivers/i2c/busses/i2c-rtl9300.c
-@@ -23,97 +23,117 @@ struct rtl9300_i2c_chan {
- u8 sda_pin;
- };
-
-+enum rtl9300_i2c_reg_scope {
-+ REG_SCOPE_GLOBAL,
-+ REG_SCOPE_MASTER,
-+};
-+
-+struct rtl9300_i2c_reg_field {
-+ struct reg_field field;
-+ enum rtl9300_i2c_reg_scope scope;
-+};
-+
-+enum rtl9300_i2c_reg_fields {
-+ F_DATA_WIDTH = 0,
-+ F_DEV_ADDR,
-+ F_I2C_FAIL,
-+ F_I2C_TRIG,
-+ F_MEM_ADDR,
-+ F_MEM_ADDR_WIDTH,
-+ F_RD_MODE,
-+ F_RWOP,
-+ F_SCL_FREQ,
-+ F_SCL_SEL,
-+ F_SDA_OUT_SEL,
-+ F_SDA_SEL,
-+
-+ /* keep last */
-+ F_NUM_FIELDS
-+};
-+
-+struct rtl9300_i2c_drv_data {
-+ struct rtl9300_i2c_reg_field field_desc[F_NUM_FIELDS];
-+ int (*select_scl)(struct rtl9300_i2c *i2c, u8 scl);
-+ u32 data_reg;
-+ u8 max_nchan;
-+};
-+
- #define RTL9300_I2C_MUX_NCHAN 8
-
- struct rtl9300_i2c {
- struct regmap *regmap;
- struct device *dev;
- struct rtl9300_i2c_chan chans[RTL9300_I2C_MUX_NCHAN];
-+ struct regmap_field *fields[F_NUM_FIELDS];
- u32 reg_base;
-+ u32 data_reg;
- u8 sda_pin;
- struct mutex lock;
- };
-
- #define RTL9300_I2C_MST_CTRL1 0x0
--#define RTL9300_I2C_MST_CTRL1_MEM_ADDR_OFS 8
--#define RTL9300_I2C_MST_CTRL1_MEM_ADDR_MASK GENMASK(31, 8)
--#define RTL9300_I2C_MST_CTRL1_SDA_OUT_SEL_OFS 4
--#define RTL9300_I2C_MST_CTRL1_SDA_OUT_SEL_MASK GENMASK(6, 4)
--#define RTL9300_I2C_MST_CTRL1_GPIO_SCL_SEL BIT(3)
--#define RTL9300_I2C_MST_CTRL1_RWOP BIT(2)
--#define RTL9300_I2C_MST_CTRL1_I2C_FAIL BIT(1)
--#define RTL9300_I2C_MST_CTRL1_I2C_TRIG BIT(0)
- #define RTL9300_I2C_MST_CTRL2 0x4
--#define RTL9300_I2C_MST_CTRL2_RD_MODE BIT(15)
--#define RTL9300_I2C_MST_CTRL2_DEV_ADDR_OFS 8
--#define RTL9300_I2C_MST_CTRL2_DEV_ADDR_MASK GENMASK(14, 8)
--#define RTL9300_I2C_MST_CTRL2_DATA_WIDTH_OFS 4
--#define RTL9300_I2C_MST_CTRL2_DATA_WIDTH_MASK GENMASK(7, 4)
--#define RTL9300_I2C_MST_CTRL2_MEM_ADDR_WIDTH_OFS 2
--#define RTL9300_I2C_MST_CTRL2_MEM_ADDR_WIDTH_MASK GENMASK(3, 2)
--#define RTL9300_I2C_MST_CTRL2_SCL_FREQ_OFS 0
--#define RTL9300_I2C_MST_CTRL2_SCL_FREQ_MASK GENMASK(1, 0)
- #define RTL9300_I2C_MST_DATA_WORD0 0x8
- #define RTL9300_I2C_MST_DATA_WORD1 0xc
- #define RTL9300_I2C_MST_DATA_WORD2 0x10
- #define RTL9300_I2C_MST_DATA_WORD3 0x14
--
- #define RTL9300_I2C_MST_GLB_CTRL 0x384
-
- static int rtl9300_i2c_reg_addr_set(struct rtl9300_i2c *i2c, u32 reg, u16 len)
- {
-- u32 val, mask;
- int ret;
-
-- val = len << RTL9300_I2C_MST_CTRL2_MEM_ADDR_WIDTH_OFS;
-- mask = RTL9300_I2C_MST_CTRL2_MEM_ADDR_WIDTH_MASK;
--
-- ret = regmap_update_bits(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL2, mask, val);
-+ ret = regmap_field_write(i2c->fields[F_MEM_ADDR_WIDTH], len);
- if (ret)
- return ret;
-
-- val = reg << RTL9300_I2C_MST_CTRL1_MEM_ADDR_OFS;
-- mask = RTL9300_I2C_MST_CTRL1_MEM_ADDR_MASK;
-+ return regmap_field_write(i2c->fields[F_MEM_ADDR], reg);
-+}
-
-- return regmap_update_bits(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL1, mask, val);
-+static int rtl9300_i2c_select_scl(struct rtl9300_i2c *i2c, u8 scl)
-+{
-+ return regmap_field_write(i2c->fields[F_SCL_SEL], 1);
- }
-
- static int rtl9300_i2c_config_io(struct rtl9300_i2c *i2c, u8 sda_pin)
- {
-+ struct rtl9300_i2c_drv_data *drv_data;
- int ret;
-- u32 val, mask;
-
-- ret = regmap_update_bits(i2c->regmap, RTL9300_I2C_MST_GLB_CTRL, BIT(sda_pin), BIT(sda_pin));
-+ drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
-+
-+ ret = regmap_field_update_bits(i2c->fields[F_SDA_SEL], BIT(sda_pin), BIT(sda_pin));
- if (ret)
- return ret;
-
-- val = (sda_pin << RTL9300_I2C_MST_CTRL1_SDA_OUT_SEL_OFS) |
-- RTL9300_I2C_MST_CTRL1_GPIO_SCL_SEL;
-- mask = RTL9300_I2C_MST_CTRL1_SDA_OUT_SEL_MASK | RTL9300_I2C_MST_CTRL1_GPIO_SCL_SEL;
-+ ret = regmap_field_write(i2c->fields[F_SDA_OUT_SEL], sda_pin);
-+ if (ret)
-+ return ret;
-
-- return regmap_update_bits(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL1, mask, val);
-+ return drv_data->select_scl(i2c, 0);
- }
-
- static int rtl9300_i2c_config_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan,
- u16 addr, u16 len)
- {
-- u32 val, mask;
-+ int ret;
-
- if (len < 1 || len > 16)
- return -EINVAL;
-
-- val = chan->bus_freq << RTL9300_I2C_MST_CTRL2_SCL_FREQ_OFS;
-- mask = RTL9300_I2C_MST_CTRL2_SCL_FREQ_MASK;
--
-- val |= addr << RTL9300_I2C_MST_CTRL2_DEV_ADDR_OFS;
-- mask |= RTL9300_I2C_MST_CTRL2_DEV_ADDR_MASK;
-+ ret = regmap_field_write(i2c->fields[F_SCL_FREQ], chan->bus_freq);
-+ if (ret)
-+ return ret;
-
-- val |= ((len - 1) & 0xf) << RTL9300_I2C_MST_CTRL2_DATA_WIDTH_OFS;
-- mask |= RTL9300_I2C_MST_CTRL2_DATA_WIDTH_MASK;
-+ ret = regmap_field_write(i2c->fields[F_DEV_ADDR], addr);
-+ if (ret)
-+ return ret;
-
-- mask |= RTL9300_I2C_MST_CTRL2_RD_MODE;
-+ ret = regmap_field_write(i2c->fields[F_DATA_WIDTH], (len - 1) & 0xf);
-+ if (ret)
-+ return ret;
-
-- return regmap_update_bits(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL2, mask, val);
-+ return regmap_field_write(i2c->fields[F_RD_MODE], 0);
- }
-
- static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, int len)
-@@ -124,8 +144,7 @@ static int rtl9300_i2c_read(struct rtl93
- if (len > 16)
- return -EIO;
-
-- ret = regmap_bulk_read(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0,
-- vals, ARRAY_SIZE(vals));
-+ ret = regmap_bulk_read(i2c->regmap, i2c->data_reg, vals, ARRAY_SIZE(vals));
- if (ret)
- return ret;
-
-@@ -152,52 +171,49 @@ static int rtl9300_i2c_write(struct rtl9
- vals[reg] |= buf[i] << shift;
- }
-
-- return regmap_bulk_write(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0,
-- vals, ARRAY_SIZE(vals));
-+ return regmap_bulk_write(i2c->regmap, i2c->data_reg, vals, ARRAY_SIZE(vals));
- }
-
- static int rtl9300_i2c_writel(struct rtl9300_i2c *i2c, u32 data)
- {
-- return regmap_write(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0, data);
-+ return regmap_write(i2c->regmap, i2c->data_reg, data);
- }
-
- static int rtl9300_i2c_execute_xfer(struct rtl9300_i2c *i2c, char read_write,
- int size, union i2c_smbus_data *data, int len)
- {
-- u32 val, mask;
-+ u32 val;
- int ret;
-
-- val = read_write == I2C_SMBUS_WRITE ? RTL9300_I2C_MST_CTRL1_RWOP : 0;
-- mask = RTL9300_I2C_MST_CTRL1_RWOP;
--
-- val |= RTL9300_I2C_MST_CTRL1_I2C_TRIG;
-- mask |= RTL9300_I2C_MST_CTRL1_I2C_TRIG;
-+ ret = regmap_field_write(i2c->fields[F_RWOP], read_write == I2C_SMBUS_WRITE);
-+ if (ret)
-+ return ret;
-
-- ret = regmap_update_bits(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL1, mask, val);
-+ ret = regmap_field_write(i2c->fields[F_I2C_TRIG], 1);
- if (ret)
- return ret;
-
-- ret = regmap_read_poll_timeout(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL1,
-- val, !(val & RTL9300_I2C_MST_CTRL1_I2C_TRIG), 100, 100000);
-+ ret = regmap_field_read_poll_timeout(i2c->fields[F_I2C_TRIG], val, !val, 100, 100000);
- if (ret)
- return ret;
-
-- if (val & RTL9300_I2C_MST_CTRL1_I2C_FAIL)
-+ ret = regmap_field_read(i2c->fields[F_I2C_FAIL], &val);
-+ if (ret)
-+ return ret;
-+ if (val)
- return -EIO;
-
- if (read_write == I2C_SMBUS_READ) {
- switch (size) {
- case I2C_SMBUS_BYTE:
- case I2C_SMBUS_BYTE_DATA:
-- ret = regmap_read(i2c->regmap,
-- i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0, &val);
-+ ret = regmap_read(i2c->regmap, i2c->data_reg, &val);
- if (ret)
- return ret;
- data->byte = val & 0xff;
- break;
- case I2C_SMBUS_WORD_DATA:
-- ret = regmap_read(i2c->regmap,
-- i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0, &val);
-+ ret = regmap_read(i2c->regmap, i2c->data_reg, &val);
- if (ret)
- return ret;
- data->word = val & 0xffff;
-@@ -355,9 +371,11 @@ static int rtl9300_i2c_probe(struct plat
- {
- struct device *dev = &pdev->dev;
- struct rtl9300_i2c *i2c;
-+ struct fwnode_handle *child;
-+ struct rtl9300_i2c_drv_data *drv_data;
-+ struct reg_field fields[F_NUM_FIELDS];
- u32 clock_freq, sda_pin;
- int ret, i = 0;
-- struct fwnode_handle *child;
-
- i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
- if (!i2c)
-@@ -376,9 +394,22 @@ static int rtl9300_i2c_probe(struct plat
-
- platform_set_drvdata(pdev, i2c);
-
-- if (device_get_child_node_count(dev) > RTL9300_I2C_MUX_NCHAN)
-+ drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
-+ if (device_get_child_node_count(dev) > drv_data->max_nchan)
- return dev_err_probe(dev, -EINVAL, "Too many channels\n");
-
-+ i2c->data_reg = i2c->reg_base + drv_data->data_reg;
-+ for (i = 0; i < F_NUM_FIELDS; i++) {
-+ fields[i] = drv_data->field_desc[i].field;
-+ if (drv_data->field_desc[i].scope == REG_SCOPE_MASTER)
-+ fields[i].reg += i2c->reg_base;
-+ }
-+ ret = devm_regmap_field_bulk_alloc(dev, i2c->regmap, i2c->fields,
-+ fields, F_NUM_FIELDS);
-+ if (ret)
-+ return ret;
-+
-+ i = 0;
- device_for_each_child_node(dev, child) {
- struct rtl9300_i2c_chan *chan = &i2c->chans[i];
- struct i2c_adapter *adap = &chan->adap;
-@@ -395,7 +426,6 @@ static int rtl9300_i2c_probe(struct plat
- case I2C_MAX_STANDARD_MODE_FREQ:
- chan->bus_freq = RTL9300_I2C_STD_FREQ;
- break;
--
- case I2C_MAX_FAST_MODE_FREQ:
- chan->bus_freq = RTL9300_I2C_FAST_FREQ;
- break;
-@@ -427,11 +457,37 @@ static int rtl9300_i2c_probe(struct plat
- return 0;
- }
-
-+#define GLB_REG_FIELD(reg, msb, lsb) \
-+ { .field = REG_FIELD(reg, msb, lsb), .scope = REG_SCOPE_GLOBAL }
-+#define MST_REG_FIELD(reg, msb, lsb) \
-+ { .field = REG_FIELD(reg, msb, lsb), .scope = REG_SCOPE_MASTER }
-+
-+static const struct rtl9300_i2c_drv_data rtl9300_i2c_drv_data = {
-+ .field_desc = {
-+ [F_MEM_ADDR] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 8, 31),
-+ [F_SDA_OUT_SEL] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 4, 6),
-+ [F_SCL_SEL] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 3, 3),
-+ [F_RWOP] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 2, 2),
-+ [F_I2C_FAIL] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 1, 1),
-+ [F_I2C_TRIG] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 0, 0),
-+ [F_RD_MODE] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL2, 15, 15),
-+ [F_DEV_ADDR] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL2, 8, 14),
-+ [F_DATA_WIDTH] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL2, 4, 7),
-+ [F_MEM_ADDR_WIDTH] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL2, 2, 3),
-+ [F_SCL_FREQ] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL2, 0, 1),
-+ [F_SDA_SEL] = GLB_REG_FIELD(RTL9300_I2C_MST_GLB_CTRL, 0, 7),
-+ },
-+ .select_scl = rtl9300_i2c_select_scl,
-+ .data_reg = RTL9300_I2C_MST_DATA_WORD0,
-+ .max_nchan = RTL9300_I2C_MUX_NCHAN,
-+};
-+
-+
- static const struct of_device_id i2c_rtl9300_dt_ids[] = {
-- { .compatible = "realtek,rtl9301-i2c" },
-- { .compatible = "realtek,rtl9302b-i2c" },
-- { .compatible = "realtek,rtl9302c-i2c" },
-- { .compatible = "realtek,rtl9303-i2c" },
-+ { .compatible = "realtek,rtl9301-i2c", .data = (void *) &rtl9300_i2c_drv_data },
-+ { .compatible = "realtek,rtl9302b-i2c", .data = (void *) &rtl9300_i2c_drv_data },
-+ { .compatible = "realtek,rtl9302c-i2c", .data = (void *) &rtl9300_i2c_drv_data },
-+ { .compatible = "realtek,rtl9303-i2c", .data = (void *) &rtl9300_i2c_drv_data },
- {}
- };
- MODULE_DEVICE_TABLE(of, i2c_rtl9300_dt_ids);
+++ /dev/null
-From 80f3e37d5e734bbfe891592bb669ceb5e8b314dc Mon Sep 17 00:00:00 2001
-From: Jonas Jelonek <jelonek.jonas@gmail.com>
-Date: Sat, 27 Sep 2025 10:19:24 +0000
-Subject: [PATCH] dt-bindings: i2c: realtek,rtl9301-i2c: fix wording and typos
-
-Fix wording of binding description to use plural because there is not
-only a single RTL9300 SoC. RTL9300 describes a whole family of Realtek
-SoCs.
-
-Add missing word 'of' in description of reg property.
-
-Change 'SDA pin' to 'SDA line number' because the property must contain
-the SDA (channel) number ranging from 0-7 instead of a real pin number.
-
-Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
-Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
-Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
-Tested-by: Chris Packham <chris.packham@alliedtelesis.co.nz> # On RTL9302C based board
-Tested-by: Markus Stockhausen <markus.stockhausen@gmx.de>
-Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
-Link: https://lore.kernel.org/r/20250927101931.71575-3-jelonek.jonas@gmail.com
-
---- a/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml
-+++ b/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml
-@@ -10,7 +10,7 @@ maintainers:
- - Chris Packham <chris.packham@alliedtelesis.co.nz>
-
- description:
-- The RTL9300 SoC has two I2C controllers. Each of these has an SCL line (which
-+ RTL9300 SoCs have two I2C controllers. Each of these has an SCL line (which
- if not-used for SCL can be a GPIO). There are 8 common SDA lines that can be
- assigned to either I2C controller.
-
-@@ -27,7 +27,7 @@ properties:
-
- reg:
- items:
-- - description: Register offset and size this I2C controller.
-+ - description: Register offset and size of this I2C controller.
-
- "#address-cells":
- const: 1
-@@ -42,7 +42,7 @@ patternProperties:
-
- properties:
- reg:
-- description: The SDA pin associated with the I2C bus.
-+ description: The SDA line number associated with the I2C bus.
- maxItems: 1
-
- required:
+++ /dev/null
-From 8ff3819d7edcd56e4c533b9391a156cd607048fa Mon Sep 17 00:00:00 2001
-From: Jonas Jelonek <jelonek.jonas@gmail.com>
-Date: Sat, 27 Sep 2025 10:19:25 +0000
-Subject: [PATCH] i2c: rtl9300: rename internal sda_pin to sda_num
-
-Rename the internally used 'sda_pin' to 'sda_num' to make it clear that
-this is NOT the actual pin number of the GPIO pin but rather the logical
-SDA channel number. Although the alternate function SDA_Y is sometimes
-given with the GPIO number, this is not always the case. Thus, avoid any
-confusion or misconfiguration by giving the variable the correct name.
-
-This follows the description change in the devicetree bindings.
-
-Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
-Tested-by: Sven Eckelmann <sven@narfation.org>
-Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
-Tested-by: Chris Packham <chris.packham@alliedtelesis.co.nz> # On RTL9302C based board
-Tested-by: Markus Stockhausen <markus.stockhausen@gmx.de>
-Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
-Link: https://lore.kernel.org/r/20250927101931.71575-4-jelonek.jonas@gmail.com
-
---- a/drivers/i2c/busses/i2c-rtl9300.c
-+++ b/drivers/i2c/busses/i2c-rtl9300.c
-@@ -20,7 +20,7 @@ struct rtl9300_i2c_chan {
- struct i2c_adapter adap;
- struct rtl9300_i2c *i2c;
- enum rtl9300_bus_freq bus_freq;
-- u8 sda_pin;
-+ u8 sda_num;
- };
-
- enum rtl9300_i2c_reg_scope {
-@@ -67,7 +67,7 @@ struct rtl9300_i2c {
- struct regmap_field *fields[F_NUM_FIELDS];
- u32 reg_base;
- u32 data_reg;
-- u8 sda_pin;
-+ u8 sda_num;
- struct mutex lock;
- };
-
-@@ -102,11 +102,11 @@ static int rtl9300_i2c_config_io(struct
-
- drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
-
-- ret = regmap_field_update_bits(i2c->fields[F_SDA_SEL], BIT(sda_pin), BIT(sda_pin));
-+ ret = regmap_field_update_bits(i2c->fields[F_SDA_SEL], BIT(sda_num), BIT(sda_num));
- if (ret)
- return ret;
-
-- ret = regmap_field_write(i2c->fields[F_SDA_OUT_SEL], sda_pin);
-+ ret = regmap_field_write(i2c->fields[F_SDA_OUT_SEL], sda_num);
- if (ret)
- return ret;
-
-@@ -243,11 +243,11 @@ static int rtl9300_i2c_smbus_xfer(struct
- int len = 0, ret;
-
- mutex_lock(&i2c->lock);
-- if (chan->sda_pin != i2c->sda_pin) {
-+ if (chan->sda_num != i2c->sda_num) {
- ret = rtl9300_i2c_config_io(i2c, chan->sda_pin);
- if (ret)
- goto out_unlock;
-- i2c->sda_pin = chan->sda_pin;
-+ i2c->sda_num = chan->sda_num;
- }
-
- switch (size) {
-@@ -374,7 +374,7 @@ static int rtl9300_i2c_probe(struct plat
- struct fwnode_handle *child;
- struct rtl9300_i2c_drv_data *drv_data;
- struct reg_field fields[F_NUM_FIELDS];
-- u32 clock_freq, sda_pin;
-+ u32 clock_freq, sda_num;
- int ret, i = 0;
-
- i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
-@@ -414,7 +414,7 @@ static int rtl9300_i2c_probe(struct plat
- struct rtl9300_i2c_chan *chan = &i2c->chans[i];
- struct i2c_adapter *adap = &chan->adap;
-
-- ret = fwnode_property_read_u32(child, "reg", &sda_pin);
-+ ret = fwnode_property_read_u32(child, "reg", &sda_num);
- if (ret)
- return ret;
-
-@@ -431,11 +431,11 @@ static int rtl9300_i2c_probe(struct plat
- break;
- default:
- dev_warn(i2c->dev, "SDA%d clock-frequency %d not supported using default\n",
-- sda_pin, clock_freq);
-+ sda_num, clock_freq);
- break;
- }
-
-- chan->sda_pin = sda_pin;
-+ chan->sda_num = sda_num;
- chan->i2c = i2c;
- adap = &i2c->chans[i].adap;
- adap->owner = THIS_MODULE;
-@@ -445,14 +445,14 @@ static int rtl9300_i2c_probe(struct plat
- adap->dev.parent = dev;
- i2c_set_adapdata(adap, chan);
- adap->dev.of_node = to_of_node(child);
-- snprintf(adap->name, sizeof(adap->name), "%s SDA%d\n", dev_name(dev), sda_pin);
-+ snprintf(adap->name, sizeof(adap->name), "%s SDA%d\n", dev_name(dev), sda_num);
- i++;
-
- ret = devm_i2c_add_adapter(dev, adap);
- if (ret)
- return ret;
- }
-- i2c->sda_pin = 0xff;
-+ i2c->sda_num = 0xff;
-
- return 0;
- }
+++ /dev/null
-From 6b0549abc8582a81425f89a436def8e28d8d7dce Mon Sep 17 00:00:00 2001
-From: Jonas Jelonek <jelonek.jonas@gmail.com>
-Date: Sat, 27 Sep 2025 10:19:26 +0000
-Subject: [PATCH] i2c: rtl9300: move setting SCL frequency to config_io
-
-Move the register operation to set the SCL frequency to the
-rtl9300_i2c_config_io function instead of the rtl9300_i2c_config_xfer
-function. This rather belongs there next to selecting the current SDA
-output line.
-
-Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
-Tested-by: Sven Eckelmann <sven@narfation.org>
-Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
-Tested-by: Chris Packham <chris.packham@alliedtelesis.co.nz> # On RTL9302C based board
-Tested-by: Markus Stockhausen <markus.stockhausen@gmx.de>
-Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
-Link: https://lore.kernel.org/r/20250927101931.71575-5-jelonek.jonas@gmail.com
-
---- a/drivers/i2c/busses/i2c-rtl9300.c
-+++ b/drivers/i2c/busses/i2c-rtl9300.c
-@@ -95,18 +95,23 @@ static int rtl9300_i2c_select_scl(struct
- return regmap_field_write(i2c->fields[F_SCL_SEL], 1);
- }
-
--static int rtl9300_i2c_config_io(struct rtl9300_i2c *i2c, u8 sda_pin)
-+static int rtl9300_i2c_config_io(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan)
- {
- struct rtl9300_i2c_drv_data *drv_data;
- int ret;
-
- drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
-
-- ret = regmap_field_update_bits(i2c->fields[F_SDA_SEL], BIT(sda_num), BIT(sda_num));
-+ ret = regmap_field_update_bits(i2c->fields[F_SDA_SEL], BIT(chan->sda_num),
-+ BIT(chan->sda_num));
- if (ret)
- return ret;
-
-- ret = regmap_field_write(i2c->fields[F_SDA_OUT_SEL], sda_num);
-+ ret = regmap_field_write(i2c->fields[F_SDA_OUT_SEL], chan->sda_num);
-+ if (ret)
-+ return ret;
-+
-+ ret = regmap_field_write(i2c->fields[F_SCL_FREQ], chan->bus_freq);
- if (ret)
- return ret;
-
-@@ -121,10 +126,6 @@ static int rtl9300_i2c_config_xfer(struc
- if (len < 1 || len > 16)
- return -EINVAL;
-
-- ret = regmap_field_write(i2c->fields[F_SCL_FREQ], chan->bus_freq);
-- if (ret)
-- return ret;
--
- ret = regmap_field_write(i2c->fields[F_DEV_ADDR], addr);
- if (ret)
- return ret;
-@@ -244,7 +245,7 @@ static int rtl9300_i2c_smbus_xfer(struct
-
- mutex_lock(&i2c->lock);
- if (chan->sda_num != i2c->sda_num) {
-- ret = rtl9300_i2c_config_io(i2c, chan->sda_pin);
-+ ret = rtl9300_i2c_config_io(i2c, chan);
- if (ret)
- goto out_unlock;
- i2c->sda_num = chan->sda_num;
+++ /dev/null
-From 0cb24186d0ebd7dd12c070fed9d782bf7c6dfb1e Mon Sep 17 00:00:00 2001
-From: Jonas Jelonek <jelonek.jonas@gmail.com>
-Date: Sat, 27 Sep 2025 10:19:27 +0000
-Subject: [PATCH] i2c: rtl9300: do not set read mode on every transfer
-
-Move the operation to set the read mode from config_xfer to probe.
-
-The I2C controller of RTL9300 and RTL9310 support a legacy message mode
-for READs with 'Read Address Data' instead of the standard format 'Write
-Address ; Read Data'. There is no way to pass that via smbus_xfer, thus
-there is no point in supported this in the driver and moreover no point
-in setting this on every transaction. Setting this once in the probe
-call is sufficient.
-
-Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
-Tested-by: Sven Eckelmann <sven@narfation.org>
-Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
-Tested-by: Chris Packham <chris.packham@alliedtelesis.co.nz> # On RTL9302C based board
-Tested-by: Markus Stockhausen <markus.stockhausen@gmx.de>
-Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
-Link: https://lore.kernel.org/r/20250927101931.71575-6-jelonek.jonas@gmail.com
-
---- a/drivers/i2c/busses/i2c-rtl9300.c
-+++ b/drivers/i2c/busses/i2c-rtl9300.c
-@@ -130,11 +130,7 @@ static int rtl9300_i2c_config_xfer(struc
- if (ret)
- return ret;
-
-- ret = regmap_field_write(i2c->fields[F_DATA_WIDTH], (len - 1) & 0xf);
-- if (ret)
-- return ret;
--
-- return regmap_field_write(i2c->fields[F_RD_MODE], 0);
-+ return regmap_field_write(i2c->fields[F_DATA_WIDTH], (len - 1) & 0xf);
- }
-
- static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, int len)
-@@ -455,6 +451,11 @@ static int rtl9300_i2c_probe(struct plat
- }
- i2c->sda_num = 0xff;
-
-+ /* only use standard read format */
-+ ret = regmap_field_write(i2c->fields[F_RD_MODE], 0);
-+ if (ret)
-+ return ret;
-+
- return 0;
- }
-
+++ /dev/null
-From 447dd46f95014eb4ea94f6164963bf23ce05b927 Mon Sep 17 00:00:00 2001
-From: Jonas Jelonek <jelonek.jonas@gmail.com>
-Date: Sat, 27 Sep 2025 10:19:28 +0000
-Subject: [PATCH] i2c: rtl9300: separate xfer configuration and execution
-
-So far, the rtl9300_i2c_smbus_xfer code is quite a mess with function
-calls distributed over the whole function setting different values in
-different cases. Calls to rtl9300_i2c_config_xfer and
-rtl9300_i2c_reg_addr_set are used in every case-block with varying
-values whose meaning is not instantly obvious. In some cases, there are
-additional calls within these case-blocks doing more things.
-
-This is in general a bad design and especially really bad for
-readability and maintainability because it distributes changes or
-issues to multiple locations due to the same function being called with
-different hardcoded values in different places.
-
-To have a good structure, setting different parameters based on the
-desired operation should not be interleaved with applying these
-parameters to the hardware registers. Or in different words, the
-parameter site should be mixed with the call site.
-
-Thus, separate configuration and execution of an SMBus xfer within
-rtl9300_i2c_smbus_xfer to improve readability and maintainability. Add a
-new 'struct rtl9300_i2c_xfer' to carry the required parameters for an
-xfer which are configured based on the input parameters within a single
-switch-case block, without having any function calls within this block.
-
-The function calls to actually apply these values to the hardware
-registers then appear below in a single place and just operate on the
-passed instance of 'struct rtl9300_i2c_xfer'. These are
-'rtl9300_i2c_prepare_xfer' which combines applying all parameters of the
-xfer to the corresponding register, and 'rtl9300_i2c_do_xfer' which
-actually executes the xfer and does post-processing if needed.
-
-Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
-Tested-by: Sven Eckelmann <sven@narfation.org>
-Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
-Tested-by: Chris Packham <chris.packham@alliedtelesis.co.nz> # On RTL9302C based board
-Tested-by: Markus Stockhausen <markus.stockhausen@gmx.de>
-Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
-Link: https://lore.kernel.org/r/20250927101931.71575-7-jelonek.jonas@gmail.com
-
---- a/drivers/i2c/busses/i2c-rtl9300.c
-+++ b/drivers/i2c/busses/i2c-rtl9300.c
-@@ -8,6 +8,7 @@
- #include <linux/mutex.h>
- #include <linux/platform_device.h>
- #include <linux/regmap.h>
-+#include <linux/unaligned.h>
-
- enum rtl9300_bus_freq {
- RTL9300_I2C_STD_FREQ,
-@@ -71,6 +72,22 @@ struct rtl9300_i2c {
- struct mutex lock;
- };
-
-+enum rtl9300_i2c_xfer_type {
-+ RTL9300_I2C_XFER_BYTE,
-+ RTL9300_I2C_XFER_WORD,
-+ RTL9300_I2C_XFER_BLOCK,
-+};
-+
-+struct rtl9300_i2c_xfer {
-+ enum rtl9300_i2c_xfer_type type;
-+ u16 dev_addr;
-+ u8 reg_addr;
-+ u8 reg_addr_len;
-+ u8 *data;
-+ u8 data_len;
-+ bool write;
-+};
-+
- #define RTL9300_I2C_MST_CTRL1 0x0
- #define RTL9300_I2C_MST_CTRL2 0x4
- #define RTL9300_I2C_MST_DATA_WORD0 0x8
-@@ -95,45 +112,37 @@ static int rtl9300_i2c_select_scl(struct
- return regmap_field_write(i2c->fields[F_SCL_SEL], 1);
- }
-
--static int rtl9300_i2c_config_io(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan)
-+static int rtl9300_i2c_config_chan(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan)
- {
- struct rtl9300_i2c_drv_data *drv_data;
- int ret;
-
-- drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
-+ if (i2c->sda_num == chan->sda_num)
-+ return 0;
-
-- ret = regmap_field_update_bits(i2c->fields[F_SDA_SEL], BIT(chan->sda_num),
-- BIT(chan->sda_num));
-+ ret = regmap_field_write(i2c->fields[F_SCL_FREQ], chan->bus_freq);
- if (ret)
- return ret;
-
-- ret = regmap_field_write(i2c->fields[F_SDA_OUT_SEL], chan->sda_num);
-+ drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
-+ ret = drv_data->select_scl(i2c, 0);
- if (ret)
- return ret;
-
-- ret = regmap_field_write(i2c->fields[F_SCL_FREQ], chan->bus_freq);
-+ ret = regmap_field_update_bits(i2c->fields[F_SDA_SEL], BIT(chan->sda_num),
-+ BIT(chan->sda_num));
- if (ret)
- return ret;
-
-- return drv_data->select_scl(i2c, 0);
--}
--
--static int rtl9300_i2c_config_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan,
-- u16 addr, u16 len)
--{
-- int ret;
--
-- if (len < 1 || len > 16)
-- return -EINVAL;
--
-- ret = regmap_field_write(i2c->fields[F_DEV_ADDR], addr);
-+ ret = regmap_field_write(i2c->fields[F_SDA_OUT_SEL], chan->sda_num);
- if (ret)
- return ret;
-
-- return regmap_field_write(i2c->fields[F_DATA_WIDTH], (len - 1) & 0xf);
-+ i2c->sda_num = chan->sda_num;
-+ return 0;
- }
-
--static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, int len)
-+static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, u8 len)
- {
- u32 vals[4] = {};
- int i, ret;
-@@ -153,7 +162,7 @@ static int rtl9300_i2c_read(struct rtl93
- return 0;
- }
-
--static int rtl9300_i2c_write(struct rtl9300_i2c *i2c, u8 *buf, int len)
-+static int rtl9300_i2c_write(struct rtl9300_i2c *i2c, u8 *buf, u8 len)
- {
- u32 vals[4] = {};
- int i;
-@@ -176,16 +185,51 @@ static int rtl9300_i2c_writel(struct rtl
- return regmap_write(i2c->regmap, i2c->data_reg, data);
- }
-
--static int rtl9300_i2c_execute_xfer(struct rtl9300_i2c *i2c, char read_write,
-- int size, union i2c_smbus_data *data, int len)
-+static int rtl9300_i2c_prepare_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_xfer *xfer)
- {
-- u32 val;
- int ret;
-
-- ret = regmap_field_write(i2c->fields[F_RWOP], read_write == I2C_SMBUS_WRITE);
-+ if (xfer->data_len < 1 || xfer->data_len > 16)
-+ return -EINVAL;
-+
-+ ret = regmap_field_write(i2c->fields[F_DEV_ADDR], xfer->dev_addr);
- if (ret)
- return ret;
-
-+ ret = rtl9300_i2c_reg_addr_set(i2c, xfer->reg_addr, xfer->reg_addr_len);
-+ if (ret)
-+ return ret;
-+
-+ ret = regmap_field_write(i2c->fields[F_RWOP], xfer->write);
-+ if (ret)
-+ return ret;
-+
-+ ret = regmap_field_write(i2c->fields[F_DATA_WIDTH], (xfer->data_len - 1) & 0xf);
-+ if (ret)
-+ return ret;
-+
-+ if (xfer->write) {
-+ switch (xfer->type) {
-+ case RTL9300_I2C_XFER_BYTE:
-+ ret = rtl9300_i2c_writel(i2c, *xfer->data);
-+ break;
-+ case RTL9300_I2C_XFER_WORD:
-+ ret = rtl9300_i2c_writel(i2c, get_unaligned((const u16 *)xfer->data));
-+ break;
-+ default:
-+ ret = rtl9300_i2c_write(i2c, xfer->data, xfer->data_len);
-+ break;
-+ }
-+ }
-+
-+ return ret;
-+}
-+
-+static int rtl9300_i2c_do_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_xfer *xfer)
-+{
-+ u32 val;
-+ int ret;
-+
- ret = regmap_field_write(i2c->fields[F_I2C_TRIG], 1);
- if (ret)
- return ret;
-@@ -200,28 +244,24 @@ static int rtl9300_i2c_execute_xfer(stru
- if (val)
- return -EIO;
-
-- if (read_write == I2C_SMBUS_READ) {
-- switch (size) {
-- case I2C_SMBUS_BYTE:
-- case I2C_SMBUS_BYTE_DATA:
-+ if (!xfer->write) {
-+ switch (xfer->type) {
-+ case RTL9300_I2C_XFER_BYTE:
- ret = regmap_read(i2c->regmap, i2c->data_reg, &val);
- if (ret)
- return ret;
-- data->byte = val & 0xff;
-+
-+ *xfer->data = val & 0xff;
- break;
-- case I2C_SMBUS_WORD_DATA:
-+ case RTL9300_I2C_XFER_WORD:
- ret = regmap_read(i2c->regmap, i2c->data_reg, &val);
- if (ret)
- return ret;
-- data->word = val & 0xffff;
-- break;
-- case I2C_SMBUS_I2C_BLOCK_DATA:
-- ret = rtl9300_i2c_read(i2c, &data->block[1], len);
-- if (ret)
-- return ret;
-+
-+ put_unaligned(val & 0xffff, (u16*)xfer->data);
- break;
- default:
-- ret = rtl9300_i2c_read(i2c, &data->block[0], len);
-+ ret = rtl9300_i2c_read(i2c, xfer->data, xfer->data_len);
- if (ret)
- return ret;
- break;
-@@ -237,108 +277,62 @@ static int rtl9300_i2c_smbus_xfer(struct
- {
- struct rtl9300_i2c_chan *chan = i2c_get_adapdata(adap);
- struct rtl9300_i2c *i2c = chan->i2c;
-- int len = 0, ret;
-+ struct rtl9300_i2c_xfer xfer = {0};
-+ int ret;
-+
-+ if (addr > 0x7f)
-+ return -EINVAL;
-
- mutex_lock(&i2c->lock);
-- if (chan->sda_num != i2c->sda_num) {
-- ret = rtl9300_i2c_config_io(i2c, chan);
-- if (ret)
-- goto out_unlock;
-- i2c->sda_num = chan->sda_num;
-- }
-+
-+ ret = rtl9300_i2c_config_chan(i2c, chan);
-+ if (ret)
-+ goto out_unlock;
-+
-+ xfer.dev_addr = addr & 0x7f;
-+ xfer.write = (read_write == I2C_SMBUS_WRITE);
-+ xfer.reg_addr = command;
-+ xfer.reg_addr_len = 1;
-
- switch (size) {
- case I2C_SMBUS_BYTE:
-- if (read_write == I2C_SMBUS_WRITE) {
-- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 0);
-- if (ret)
-- goto out_unlock;
-- ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
-- if (ret)
-- goto out_unlock;
-- } else {
-- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 1);
-- if (ret)
-- goto out_unlock;
-- ret = rtl9300_i2c_reg_addr_set(i2c, 0, 0);
-- if (ret)
-- goto out_unlock;
-- }
-+ xfer.data = (read_write == I2C_SMBUS_READ) ? &data->byte : &command;
-+ xfer.data_len = 1;
-+ xfer.reg_addr = 0;
-+ xfer.reg_addr_len = 0;
-+ xfer.type = RTL9300_I2C_XFER_BYTE;
- break;
--
- case I2C_SMBUS_BYTE_DATA:
-- ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
-- if (ret)
-- goto out_unlock;
-- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 1);
-- if (ret)
-- goto out_unlock;
-- if (read_write == I2C_SMBUS_WRITE) {
-- ret = rtl9300_i2c_writel(i2c, data->byte);
-- if (ret)
-- goto out_unlock;
-- }
-+ xfer.data = &data->byte;
-+ xfer.data_len = 1;
-+ xfer.type = RTL9300_I2C_XFER_BYTE;
- break;
--
- case I2C_SMBUS_WORD_DATA:
-- ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
-- if (ret)
-- goto out_unlock;
-- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 2);
-- if (ret)
-- goto out_unlock;
-- if (read_write == I2C_SMBUS_WRITE) {
-- ret = rtl9300_i2c_writel(i2c, data->word);
-- if (ret)
-- goto out_unlock;
-- }
-+ xfer.data = (u8 *)&data->word;
-+ xfer.data_len = 2;
-+ xfer.type = RTL9300_I2C_XFER_WORD;
- break;
--
- case I2C_SMBUS_BLOCK_DATA:
-- ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
-- if (ret)
-- goto out_unlock;
-- if (data->block[0] < 1 || data->block[0] > I2C_SMBUS_BLOCK_MAX) {
-- ret = -EINVAL;
-- goto out_unlock;
-- }
-- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, data->block[0] + 1);
-- if (ret)
-- goto out_unlock;
-- if (read_write == I2C_SMBUS_WRITE) {
-- ret = rtl9300_i2c_write(i2c, &data->block[0], data->block[0] + 1);
-- if (ret)
-- goto out_unlock;
-- }
-- len = data->block[0] + 1;
-+ xfer.data = &data->block[0];
-+ xfer.data_len = data->block[0] + 1;
-+ xfer.type = RTL9300_I2C_XFER_BLOCK;
- break;
--
- case I2C_SMBUS_I2C_BLOCK_DATA:
-- ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
-- if (ret)
-- goto out_unlock;
-- if (data->block[0] < 1 || data->block[0] > I2C_SMBUS_BLOCK_MAX) {
-- ret = -EINVAL;
-- goto out_unlock;
-- }
-- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, data->block[0]);
-- if (ret)
-- goto out_unlock;
-- if (read_write == I2C_SMBUS_WRITE) {
-- ret = rtl9300_i2c_write(i2c, &data->block[1], data->block[0]);
-- if (ret)
-- goto out_unlock;
-- }
-- len = data->block[0];
-+ xfer.data = &data->block[1];
-+ xfer.data_len = data->block[0];
-+ xfer.type = RTL9300_I2C_XFER_BLOCK;
- break;
--
- default:
- dev_err(&adap->dev, "Unsupported transaction %d\n", size);
- ret = -EOPNOTSUPP;
- goto out_unlock;
- }
-
-- ret = rtl9300_i2c_execute_xfer(i2c, read_write, size, data, len);
-+ ret = rtl9300_i2c_prepare_xfer(i2c, &xfer);
-+ if (ret)
-+ goto out_unlock;
-+
-+ ret = rtl9300_i2c_do_xfer(i2c, &xfer);
-
- out_unlock:
- mutex_unlock(&i2c->lock);
+++ /dev/null
-From bcd5f0da57e6c47a884dcad94ad6b0e32cce8705 Mon Sep 17 00:00:00 2001
-From: Jonas Jelonek <jelonek.jonas@gmail.com>
-Date: Sat, 27 Sep 2025 10:19:29 +0000
-Subject: [PATCH] i2c: rtl9300: use scoped guard instead of explicit
- lock/unlock
-
-Use the scoped guard infrastructure which unlocks a mutex automatically
-when the guard goes out of scope, instead of explicit lock and unlock.
-This simplifies the code and control flow in rtl9300_i2c_smbus_xfer and
-removes the need of using goto in error cases to unlock before
-returning.
-
-Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
-Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
-Tested-by: Chris Packham <chris.packham@alliedtelesis.co.nz> # On RTL9302C based board
-Tested-by: Markus Stockhausen <markus.stockhausen@gmx.de>
-Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
-Link: https://lore.kernel.org/r/20250927101931.71575-8-jelonek.jonas@gmail.com
-
---- a/drivers/i2c/busses/i2c-rtl9300.c
-+++ b/drivers/i2c/busses/i2c-rtl9300.c
-@@ -72,6 +72,8 @@ struct rtl9300_i2c {
- struct mutex lock;
- };
-
-+DEFINE_GUARD(rtl9300_i2c, struct rtl9300_i2c *, mutex_lock(&_T->lock), mutex_unlock(&_T->lock))
-+
- enum rtl9300_i2c_xfer_type {
- RTL9300_I2C_XFER_BYTE,
- RTL9300_I2C_XFER_WORD,
-@@ -283,11 +285,11 @@ static int rtl9300_i2c_smbus_xfer(struct
- if (addr > 0x7f)
- return -EINVAL;
-
-- mutex_lock(&i2c->lock);
-+ guard(rtl9300_i2c)(i2c);
-
- ret = rtl9300_i2c_config_chan(i2c, chan);
- if (ret)
-- goto out_unlock;
-+ return ret;
-
- xfer.dev_addr = addr & 0x7f;
- xfer.write = (read_write == I2C_SMBUS_WRITE);
-@@ -324,20 +326,14 @@ static int rtl9300_i2c_smbus_xfer(struct
- break;
- default:
- dev_err(&adap->dev, "Unsupported transaction %d\n", size);
-- ret = -EOPNOTSUPP;
-- goto out_unlock;
-+ return -EOPNOTSUPP;
- }
-
- ret = rtl9300_i2c_prepare_xfer(i2c, &xfer);
- if (ret)
-- goto out_unlock;
--
-- ret = rtl9300_i2c_do_xfer(i2c, &xfer);
--
--out_unlock:
-- mutex_unlock(&i2c->lock);
-+ return ret;
-
-- return ret;
-+ return rtl9300_i2c_do_xfer(i2c, &xfer);
- }
-
- static u32 rtl9300_i2c_func(struct i2c_adapter *a)
+++ /dev/null
-From 17689aafb793599a862617a127429dd3d6f675c9 Mon Sep 17 00:00:00 2001
-From: Jonas Jelonek <jelonek.jonas@gmail.com>
-Date: Sat, 27 Sep 2025 10:19:30 +0000
-Subject: [PATCH] dt-bindings: i2c: realtek,rtl9301-i2c: extend for RTL9310
- support
-
-Adjust the regex for child-node address to account for the fact that
-RTL9310 supports 12 instead of only 8 SDA lines. Also, narrow this per
-variant.
-
-Add a vendor-specific property to explicitly specify the SCL line number
-of the defined I2C controller/master. This is required, in particular
-for RTL9310, to operate on the correct SCL for each controller. Require
-this property to be specified for RTL9310.
-
-Add compatibles for known SoC variants RTL9311, RTL9312 and RTL9313.
-
-Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
-Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
-Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
-Tested-by: Markus Stockhausen <markus.stockhausen@gmx.de>
-Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
-Link: https://lore.kernel.org/r/20250927101931.71575-9-jelonek.jonas@gmail.com
-
---- a/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml
-+++ b/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml
-@@ -13,6 +13,8 @@ description:
- RTL9300 SoCs have two I2C controllers. Each of these has an SCL line (which
- if not-used for SCL can be a GPIO). There are 8 common SDA lines that can be
- assigned to either I2C controller.
-+ RTL9310 SoCs have equal capabilities but support 12 common SDA lines which
-+ can be assigned to either I2C controller.
-
- properties:
- compatible:
-@@ -23,7 +25,15 @@ properties:
- - realtek,rtl9302c-i2c
- - realtek,rtl9303-i2c
- - const: realtek,rtl9301-i2c
-- - const: realtek,rtl9301-i2c
-+ - items:
-+ - enum:
-+ - realtek,rtl9311-i2c
-+ - realtek,rtl9312-i2c
-+ - realtek,rtl9313-i2c
-+ - const: realtek,rtl9310-i2c
-+ - enum:
-+ - realtek,rtl9301-i2c
-+ - realtek,rtl9310-i2c
-
- reg:
- items:
-@@ -35,8 +45,14 @@ properties:
- "#size-cells":
- const: 0
-
-+ realtek,scl:
-+ $ref: /schemas/types.yaml#/definitions/uint32
-+ description:
-+ The SCL line number of this I2C controller.
-+ enum: [ 0, 1 ]
-+
- patternProperties:
-- '^i2c@[0-7]$':
-+ '^i2c@[0-9ab]$':
- $ref: /schemas/i2c/i2c-controller.yaml
- unevaluatedProperties: false
-
-@@ -48,6 +64,25 @@ patternProperties:
- required:
- - reg
-
-+
-+allOf:
-+ - if:
-+ properties:
-+ compatible:
-+ contains:
-+ const: realtek,rtl9310-i2c
-+ then:
-+ required:
-+ - realtek,scl
-+ - if:
-+ properties:
-+ compatible:
-+ contains:
-+ const: realtek,rtl9301-i2c
-+ then:
-+ patternProperties:
-+ '^i2c@[89ab]$': false
-+
- required:
- - compatible
- - reg
+++ /dev/null
-From 8d43287120ce6437e7a77e735d99137f3fdb3ae9 Mon Sep 17 00:00:00 2001
-From: Jonas Jelonek <jelonek.jonas@gmail.com>
-Date: Sat, 27 Sep 2025 10:19:31 +0000
-Subject: [PATCH] i2c: rtl9300: add support for RTL9310 I2C controller
-
-Add support for the internal I2C controllers of RTL9310 series based
-SoCs to the driver for RTL9300. Add register definitions, chip-specific
-functions and compatible strings for known RTL9310-based SoCs RTL9311,
-RTL9312 and RTL9313.
-
-Make use of a new device tree property 'realtek,scl' which needs to be
-specified in case both or only the second master is used. This is
-required due how the register layout changed in contrast to RTL9300,
-which has SCL selection in a global register instead of a
-master-specific one.
-
-Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
-Tested-by: Sven Eckelmann <sven@narfation.org>
-Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
-Tested-by: Markus Stockhausen <markus.stockhausen@gmx.de>
-Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
-Link: https://lore.kernel.org/r/20250927101931.71575-10-jelonek.jonas@gmail.com
-
---- a/drivers/i2c/busses/i2c-rtl9300.c
-+++ b/drivers/i2c/busses/i2c-rtl9300.c
-@@ -60,14 +60,16 @@ struct rtl9300_i2c_drv_data {
- };
-
- #define RTL9300_I2C_MUX_NCHAN 8
-+#define RTL9310_I2C_MUX_NCHAN 12
-
- struct rtl9300_i2c {
- struct regmap *regmap;
- struct device *dev;
-- struct rtl9300_i2c_chan chans[RTL9300_I2C_MUX_NCHAN];
-+ struct rtl9300_i2c_chan chans[RTL9310_I2C_MUX_NCHAN];
- struct regmap_field *fields[F_NUM_FIELDS];
- u32 reg_base;
- u32 data_reg;
-+ u8 scl_num;
- u8 sda_num;
- struct mutex lock;
- };
-@@ -98,6 +100,12 @@ struct rtl9300_i2c_xfer {
- #define RTL9300_I2C_MST_DATA_WORD3 0x14
- #define RTL9300_I2C_MST_GLB_CTRL 0x384
-
-+#define RTL9310_I2C_MST_IF_CTRL 0x1004
-+#define RTL9310_I2C_MST_IF_SEL 0x1008
-+#define RTL9310_I2C_MST_CTRL 0x0
-+#define RTL9310_I2C_MST_MEMADDR_CTRL 0x4
-+#define RTL9310_I2C_MST_DATA_CTRL 0x8
-+
- static int rtl9300_i2c_reg_addr_set(struct rtl9300_i2c *i2c, u32 reg, u16 len)
- {
- int ret;
-@@ -114,6 +122,11 @@ static int rtl9300_i2c_select_scl(struct
- return regmap_field_write(i2c->fields[F_SCL_SEL], 1);
- }
-
-+static int rtl9310_i2c_select_scl(struct rtl9300_i2c *i2c, u8 scl)
-+{
-+ return regmap_field_update_bits(i2c->fields[F_SCL_SEL], BIT(scl), BIT(scl));
-+}
-+
- static int rtl9300_i2c_config_chan(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan)
- {
- struct rtl9300_i2c_drv_data *drv_data;
-@@ -127,7 +140,7 @@ static int rtl9300_i2c_config_chan(struc
- return ret;
-
- drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
-- ret = drv_data->select_scl(i2c, 0);
-+ ret = drv_data->select_scl(i2c, i2c->scl_num);
- if (ret)
- return ret;
-
-@@ -361,7 +374,7 @@ static int rtl9300_i2c_probe(struct plat
- struct fwnode_handle *child;
- struct rtl9300_i2c_drv_data *drv_data;
- struct reg_field fields[F_NUM_FIELDS];
-- u32 clock_freq, sda_num;
-+ u32 clock_freq, scl_num, sda_num;
- int ret, i = 0;
-
- i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
-@@ -379,6 +392,11 @@ static int rtl9300_i2c_probe(struct plat
- if (ret)
- return ret;
-
-+ ret = device_property_read_u32(dev, "realtek,scl", &scl_num);
-+ if (ret || scl_num != 1)
-+ scl_num = 0;
-+ i2c->scl_num = (u8)scl_num;
-+
- platform_set_drvdata(pdev, i2c);
-
- drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
-@@ -474,12 +492,35 @@ static const struct rtl9300_i2c_drv_data
- .max_nchan = RTL9300_I2C_MUX_NCHAN,
- };
-
-+static const struct rtl9300_i2c_drv_data rtl9310_i2c_drv_data = {
-+ .field_desc = {
-+ [F_SCL_SEL] = GLB_REG_FIELD(RTL9310_I2C_MST_IF_SEL, 12, 13),
-+ [F_SDA_SEL] = GLB_REG_FIELD(RTL9310_I2C_MST_IF_SEL, 0, 11),
-+ [F_SCL_FREQ] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 30, 31),
-+ [F_DEV_ADDR] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 11, 17),
-+ [F_SDA_OUT_SEL] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 18, 21),
-+ [F_MEM_ADDR_WIDTH] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 9, 10),
-+ [F_DATA_WIDTH] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 5, 8),
-+ [F_RD_MODE] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 4, 4),
-+ [F_RWOP] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 2, 2),
-+ [F_I2C_FAIL] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 1, 1),
-+ [F_I2C_TRIG] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 0, 0),
-+ [F_MEM_ADDR] = MST_REG_FIELD(RTL9310_I2C_MST_MEMADDR_CTRL, 0, 23),
-+ },
-+ .select_scl = rtl9310_i2c_select_scl,
-+ .data_reg = RTL9310_I2C_MST_DATA_CTRL,
-+ .max_nchan = RTL9310_I2C_MUX_NCHAN,
-+};
-
- static const struct of_device_id i2c_rtl9300_dt_ids[] = {
- { .compatible = "realtek,rtl9301-i2c", .data = (void *) &rtl9300_i2c_drv_data },
- { .compatible = "realtek,rtl9302b-i2c", .data = (void *) &rtl9300_i2c_drv_data },
- { .compatible = "realtek,rtl9302c-i2c", .data = (void *) &rtl9300_i2c_drv_data },
- { .compatible = "realtek,rtl9303-i2c", .data = (void *) &rtl9300_i2c_drv_data },
-+ { .compatible = "realtek,rtl9310-i2c", .data = (void *) &rtl9310_i2c_drv_data },
-+ { .compatible = "realtek,rtl9311-i2c", .data = (void *) &rtl9310_i2c_drv_data },
-+ { .compatible = "realtek,rtl9312-i2c", .data = (void *) &rtl9310_i2c_drv_data },
-+ { .compatible = "realtek,rtl9313-i2c", .data = (void *) &rtl9310_i2c_drv_data },
- {}
- };
- MODULE_DEVICE_TABLE(of, i2c_rtl9300_dt_ids);
+++ /dev/null
-From 85e27f218121bdaa8e8afd68674262aa154d2cb4 Mon Sep 17 00:00:00 2001
-From: Markus Stockhausen <markus.stockhausen@gmx.de>
-Date: Mon, 4 Aug 2025 04:03:26 -0400
-Subject: clocksource/drivers/timer-rtl-otto: Drop set_counter function
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-The current counter value is a read only register. It will be
-reset when writing a new target timer value with rttm_set_period().
-rttm_set_counter() is essentially a noop. Drop it.
-
-While this makes rttm_start_timer() and rttm_enable_timer() the
-same functions keep both to make the established abstraction layers
-for register and control functions active.
-
-Downstream has already tested and confirmed a patch. See
-https://github.com/openwrt/openwrt/pull/19468
-https://forum.openwrt.org/t/support-for-rtl838x-based-managed-switches/57875/3788
-
-Tested-by: Stephen Howell <howels@allthatwemight.be>
-Tested-by: Bjørn Mork <bjorn@mork.no>
-Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
-Link: https://lore.kernel.org/r/20250804080328.2609287-3-markus.stockhausen@gmx.de
-Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
----
- drivers/clocksource/timer-rtl-otto.c | 6 ------
- 1 file changed, 6 deletions(-)
-
---- a/drivers/clocksource/timer-rtl-otto.c
-+++ b/drivers/clocksource/timer-rtl-otto.c
-@@ -56,11 +56,6 @@ struct rttm_cs {
- };
-
- /* Simple internal register functions */
--static inline void rttm_set_counter(void __iomem *base, unsigned int counter)
--{
-- iowrite32(counter, base + RTTM_CNT);
--}
--
- static inline unsigned int rttm_get_counter(void __iomem *base)
- {
- return ioread32(base + RTTM_CNT);
-@@ -137,7 +132,6 @@ static void rttm_stop_timer(void __iomem
-
- static void rttm_start_timer(struct timer_of *to, u32 mode)
- {
-- rttm_set_counter(to->of_base.base, 0);
- rttm_enable_timer(to->of_base.base, mode, to->of_clk.rate / RTTM_TICKS_PER_SEC);
- }
-
+++ /dev/null
-From 3148d0e5b1c5733d69ec51b70c8280e46488750a Mon Sep 17 00:00:00 2001
-From: Markus Stockhausen <markus.stockhausen@gmx.de>
-Date: Fri, 19 Sep 2025 03:52:01 -0400
-Subject: mtd: nand: realtek-ecc: Add Realtek external ECC engine support
-
-The Realtek RTl93xx switch SoC series has a built in ECC controller
-that can provide BCH6 or BCH12 over 512 data and 6 tag bytes. It
-generates 10 (BCH6) or 20 (BCH12) bytes of parity.
-
-This engine will most likely work in conjunction with the Realtek
-spi-mem based NAND controller but can work on its own. Therefore
-the initial implementation will be of type external.
-
-Remark! The engine can support any data blocks that are multiples
-of 512 bytes. For now limit it to data+oob layouts that have been
-analyzed from existing devices. This way it keeps compatibility
-and pre-existing vendor data can be read.
-
-Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
-Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
----
- drivers/mtd/nand/Kconfig | 8 +
- drivers/mtd/nand/Makefile | 1 +
- drivers/mtd/nand/ecc-realtek.c | 464 +++++++++++++++++++++++++++++++++++++++++
- 3 files changed, 473 insertions(+)
- create mode 100644 drivers/mtd/nand/ecc-realtek.c
-
---- a/drivers/mtd/nand/Kconfig
-+++ b/drivers/mtd/nand/Kconfig
-@@ -65,6 +65,14 @@ config MTD_NAND_ECC_MEDIATEK
- help
- This enables support for the hardware ECC engine from Mediatek.
-
-+config MTD_NAND_ECC_REALTEK
-+ tristate "Realtek RTL93xx hardware ECC engine"
-+ depends on HAS_IOMEM
-+ depends on MACH_REALTEK_RTL || COMPILE_TEST
-+ select MTD_NAND_ECC
-+ help
-+ This enables support for the hardware ECC engine from Realtek.
-+
- endmenu
-
- endmenu
---- a/drivers/mtd/nand/Makefile
-+++ b/drivers/mtd/nand/Makefile
-@@ -2,6 +2,7 @@
-
- nandcore-objs := core.o bbt.o
- obj-$(CONFIG_MTD_NAND_CORE) += nandcore.o
-+obj-$(CONFIG_MTD_NAND_ECC_REALTEK) += ecc-realtek.o
- obj-$(CONFIG_MTD_NAND_ECC_MEDIATEK) += ecc-mtk.o
- obj-$(CONFIG_MTD_NAND_MTK_BMT) += mtk_bmt.o mtk_bmt_v2.o mtk_bmt_bbt.o mtk_bmt_nmbm.o
- obj-$(CONFIG_SPI_QPIC_SNAND) += qpic_common.o
---- /dev/null
-+++ b/drivers/mtd/nand/ecc-realtek.c
-@@ -0,0 +1,464 @@
-+// SPDX-License-Identifier: GPL-2.0
-+/*
-+ * Support for Realtek hardware ECC engine in RTL93xx SoCs
-+ */
-+
-+#include <linux/bitfield.h>
-+#include <linux/dma-mapping.h>
-+#include <linux/mtd/nand.h>
-+#include <linux/mutex.h>
-+#include <linux/platform_device.h>
-+#include <linux/regmap.h>
-+
-+/*
-+ * The Realtek ECC engine has two operation modes.
-+ *
-+ * - BCH6 : Generate 10 ECC bytes from 512 data bytes plus 6 free bytes
-+ * - BCH12 : Generate 20 ECC bytes from 512 data bytes plus 6 free bytes
-+ *
-+ * It can run for arbitrary NAND flash chips with different block and OOB sizes. Currently there
-+ * are only two known devices in the wild that have NAND flash and make use of this ECC engine
-+ * (Linksys LGS328C & LGS352C). To keep compatibility with vendor firmware, new modes can only
-+ * be added when new data layouts have been analyzed. For now allow BCH6 on flash with 2048 byte
-+ * blocks and 64 bytes oob.
-+ *
-+ * This driver aligns with kernel ECC naming conventions. Neverthless a short notice on the
-+ * Realtek naming conventions for the different structures in the OOB area.
-+ *
-+ * - BBI : Bad block indicator. The first two bytes of OOB. Protected by ECC!
-+ * - tag : 6 User/free bytes. First tag "contains" 2 bytes BBI. Protected by ECC!
-+ * - syndrome : ECC/parity bytes
-+ *
-+ * Altogether this gives currently the following block layout.
-+ *
-+ * +------+------+------+------+-----+------+------+------+------+-----+-----+-----+-----+
-+ * | 512 | 512 | 512 | 512 | 2 | 4 | 6 | 6 | 6 | 10 | 10 | 10 | 10 |
-+ * +------+------+------+------+-----+------+------+------+------+-----+-----+-----+-----+
-+ * | data | data | data | data | BBI | free | free | free | free | ECC | ECC | ECC | ECC |
-+ * +------+------+------+------+-----+------+------+------+------+-----+-----+-----+-----+
-+ */
-+
-+#define RTL_ECC_ALLOWED_PAGE_SIZE 2048
-+#define RTL_ECC_ALLOWED_OOB_SIZE 64
-+#define RTL_ECC_ALLOWED_STRENGTH 6
-+
-+#define RTL_ECC_BLOCK_SIZE 512
-+#define RTL_ECC_FREE_SIZE 6
-+#define RTL_ECC_PARITY_SIZE_BCH6 10
-+#define RTL_ECC_PARITY_SIZE_BCH12 20
-+
-+/*
-+ * The engine is fed with two DMA regions. One for data (always 512 bytes) and one for free bytes
-+ * and parity (either 16 bytes for BCH6 or 26 bytes for BCH12). Start and length of each must be
-+ * aligned to a multiple of 4.
-+ */
-+
-+#define RTL_ECC_DMA_FREE_PARITY_SIZE ALIGN(RTL_ECC_FREE_SIZE + RTL_ECC_PARITY_SIZE_BCH12, 4)
-+#define RTL_ECC_DMA_SIZE (RTL_ECC_BLOCK_SIZE + RTL_ECC_DMA_FREE_PARITY_SIZE)
-+
-+#define RTL_ECC_CFG 0x00
-+#define RTL_ECC_BCH6 0
-+#define RTL_ECC_BCH12 BIT(28)
-+#define RTL_ECC_DMA_PRECISE BIT(12)
-+#define RTL_ECC_BURST_128 GENMASK(1, 0)
-+#define RTL_ECC_DMA_TRIGGER 0x08
-+#define RTL_ECC_OP_DECODE 0
-+#define RTL_ECC_OP_ENCODE BIT(0)
-+#define RTL_ECC_DMA_START 0x0c
-+#define RTL_ECC_DMA_TAG 0x10
-+#define RTL_ECC_STATUS 0x14
-+#define RTL_ECC_CORR_COUNT GENMASK(19, 12)
-+#define RTL_ECC_RESULT BIT(8)
-+#define RTL_ECC_ALL_ONE BIT(4)
-+#define RTL_ECC_OP_STATUS BIT(0)
-+
-+struct rtl_ecc_engine {
-+ struct device *dev;
-+ struct nand_ecc_engine engine;
-+ struct mutex lock;
-+ char *buf;
-+ dma_addr_t buf_dma;
-+ struct regmap *regmap;
-+};
-+
-+struct rtl_ecc_ctx {
-+ struct rtl_ecc_engine * rtlc;
-+ struct nand_ecc_req_tweak_ctx req_ctx;
-+ int steps;
-+ int bch_mode;
-+ int strength;
-+ int parity_size;
-+};
-+
-+static const struct regmap_config rtl_ecc_regmap_config = {
-+ .reg_bits = 32,
-+ .val_bits = 32,
-+ .reg_stride = 4,
-+};
-+
-+static inline void *nand_to_ctx(struct nand_device *nand)
-+{
-+ return nand->ecc.ctx.priv;
-+}
-+
-+static inline struct rtl_ecc_engine *nand_to_rtlc(struct nand_device *nand)
-+{
-+ struct nand_ecc_engine *eng = nand->ecc.engine;
-+
-+ return container_of(eng, struct rtl_ecc_engine, engine);
-+}
-+
-+static int rtl_ecc_ooblayout_ecc(struct mtd_info *mtd, int section,
-+ struct mtd_oob_region *oobregion)
-+{
-+ struct nand_device *nand = mtd_to_nanddev(mtd);
-+ struct rtl_ecc_ctx *ctx = nand_to_ctx(nand);
-+
-+ if (section < 0 || section >= ctx->steps)
-+ return -ERANGE;
-+
-+ oobregion->offset = ctx->steps * RTL_ECC_FREE_SIZE + section * ctx->parity_size;
-+ oobregion->length = ctx->parity_size;
-+
-+ return 0;
-+}
-+
-+static int rtl_ecc_ooblayout_free(struct mtd_info *mtd, int section,
-+ struct mtd_oob_region *oobregion)
-+{
-+ struct nand_device *nand = mtd_to_nanddev(mtd);
-+ struct rtl_ecc_ctx *ctx = nand_to_ctx(nand);
-+ int bbm;
-+
-+ if (section < 0 || section >= ctx->steps)
-+ return -ERANGE;
-+
-+ /* reserve 2 BBM bytes in first block */
-+ bbm = section ? 0 : 2;
-+ oobregion->offset = section * RTL_ECC_FREE_SIZE + bbm;
-+ oobregion->length = RTL_ECC_FREE_SIZE - bbm;
-+
-+ return 0;
-+}
-+
-+static const struct mtd_ooblayout_ops rtl_ecc_ooblayout_ops = {
-+ .ecc = rtl_ecc_ooblayout_ecc,
-+ .free = rtl_ecc_ooblayout_free,
-+};
-+
-+static void rtl_ecc_kick_engine(struct rtl_ecc_ctx *ctx, int operation)
-+{
-+ struct rtl_ecc_engine *rtlc = ctx->rtlc;
-+
-+ regmap_write(rtlc->regmap, RTL_ECC_CFG,
-+ ctx->bch_mode | RTL_ECC_BURST_128 | RTL_ECC_DMA_PRECISE);
-+
-+ regmap_write(rtlc->regmap, RTL_ECC_DMA_START, rtlc->buf_dma);
-+ regmap_write(rtlc->regmap, RTL_ECC_DMA_TAG, rtlc->buf_dma + RTL_ECC_BLOCK_SIZE);
-+ regmap_write(rtlc->regmap, RTL_ECC_DMA_TRIGGER, operation);
-+}
-+
-+static int rtl_ecc_wait_for_engine(struct rtl_ecc_ctx *ctx)
-+{
-+ struct rtl_ecc_engine *rtlc = ctx->rtlc;
-+ int ret, status, bitflips;
-+ bool all_one;
-+
-+ /*
-+ * The ECC engine needs 6-8 us to encode/decode a BCH6 syndrome for 512 bytes of data
-+ * and 6 free bytes. In case the NAND area has been erased and all data and oob is
-+ * set to 0xff, decoding takes 30us (reason unknown). Although the engine can trigger
-+ * interrupts when finished, use active polling for now. 12 us maximum wait time has
-+ * proven to be a good tradeoff between performance and overhead.
-+ */
-+
-+ ret = regmap_read_poll_timeout(rtlc->regmap, RTL_ECC_STATUS, status,
-+ !(status & RTL_ECC_OP_STATUS), 12, 1000000);
-+ if (ret)
-+ return ret;
-+
-+ ret = FIELD_GET(RTL_ECC_RESULT, status);
-+ all_one = FIELD_GET(RTL_ECC_ALL_ONE, status);
-+ bitflips = FIELD_GET(RTL_ECC_CORR_COUNT, status);
-+
-+ /* For erased blocks (all bits one) error status can be ignored */
-+ if (all_one)
-+ ret = 0;
-+
-+ return ret ? -EBADMSG : bitflips;
-+}
-+
-+static int rtl_ecc_run_engine(struct rtl_ecc_ctx *ctx, char *data, char *free,
-+ char *parity, int operation)
-+{
-+ struct rtl_ecc_engine *rtlc = ctx->rtlc;
-+ char *buf_parity = rtlc->buf + RTL_ECC_BLOCK_SIZE + RTL_ECC_FREE_SIZE;
-+ char *buf_free = rtlc->buf + RTL_ECC_BLOCK_SIZE;
-+ char *buf_data = rtlc->buf;
-+ int ret;
-+
-+ mutex_lock(&rtlc->lock);
-+
-+ memcpy(buf_data, data, RTL_ECC_BLOCK_SIZE);
-+ memcpy(buf_free, free, RTL_ECC_FREE_SIZE);
-+ memcpy(buf_parity, parity, ctx->parity_size);
-+
-+ dma_sync_single_for_device(rtlc->dev, rtlc->buf_dma, RTL_ECC_DMA_SIZE, DMA_TO_DEVICE);
-+ rtl_ecc_kick_engine(ctx, operation);
-+ ret = rtl_ecc_wait_for_engine(ctx);
-+ dma_sync_single_for_cpu(rtlc->dev, rtlc->buf_dma, RTL_ECC_DMA_SIZE, DMA_FROM_DEVICE);
-+
-+ if (ret >= 0) {
-+ memcpy(data, buf_data, RTL_ECC_BLOCK_SIZE);
-+ memcpy(free, buf_free, RTL_ECC_FREE_SIZE);
-+ memcpy(parity, buf_parity, ctx->parity_size);
-+ }
-+
-+ mutex_unlock(&rtlc->lock);
-+
-+ return ret;
-+}
-+
-+static int rtl_ecc_prepare_io_req(struct nand_device *nand, struct nand_page_io_req *req)
-+{
-+ struct rtl_ecc_engine *rtlc = nand_to_rtlc(nand);
-+ struct rtl_ecc_ctx *ctx = nand_to_ctx(nand);
-+ char *data, *free, *parity;
-+ int ret = 0;
-+
-+ if (req->mode == MTD_OPS_RAW)
-+ return 0;
-+
-+ nand_ecc_tweak_req(&ctx->req_ctx, req);
-+
-+ if (req->type == NAND_PAGE_READ)
-+ return 0;
-+
-+ free = req->oobbuf.in;
-+ data = req->databuf.in;
-+ parity = req->oobbuf.in + ctx->steps * RTL_ECC_FREE_SIZE;
-+
-+ for (int i = 0; i < ctx->steps; i++) {
-+ ret |= rtl_ecc_run_engine(ctx, data, free, parity, RTL_ECC_OP_ENCODE);
-+
-+ free += RTL_ECC_FREE_SIZE;
-+ data += RTL_ECC_BLOCK_SIZE;
-+ parity += ctx->parity_size;
-+ }
-+
-+ if (unlikely(ret))
-+ dev_dbg(rtlc->dev, "ECC calculation failed\n");
-+
-+ return ret ? -EBADMSG : 0;
-+}
-+
-+static int rtl_ecc_finish_io_req(struct nand_device *nand, struct nand_page_io_req *req)
-+{
-+ struct rtl_ecc_engine *rtlc = nand_to_rtlc(nand);
-+ struct rtl_ecc_ctx *ctx = nand_to_ctx(nand);
-+ struct mtd_info *mtd = nanddev_to_mtd(nand);
-+ char *data, *free, *parity;
-+ bool failure = false;
-+ int bitflips = 0;
-+
-+ if (req->mode == MTD_OPS_RAW)
-+ return 0;
-+
-+ if (req->type == NAND_PAGE_WRITE) {
-+ nand_ecc_restore_req(&ctx->req_ctx, req);
-+ return 0;
-+ }
-+
-+ free = req->oobbuf.in;
-+ data = req->databuf.in;
-+ parity = req->oobbuf.in + ctx->steps * RTL_ECC_FREE_SIZE;
-+
-+ for (int i = 0 ; i < ctx->steps; i++) {
-+ int ret = rtl_ecc_run_engine(ctx, data, free, parity, RTL_ECC_OP_DECODE);
-+
-+ if (unlikely(ret < 0))
-+ /* ECC totally fails for bitflips in erased blocks */
-+ ret = nand_check_erased_ecc_chunk(data, RTL_ECC_BLOCK_SIZE,
-+ parity, ctx->parity_size,
-+ free, RTL_ECC_FREE_SIZE,
-+ ctx->strength);
-+ if (unlikely(ret < 0)) {
-+ failure = true;
-+ mtd->ecc_stats.failed++;
-+ } else {
-+ mtd->ecc_stats.corrected += ret;
-+ bitflips = max_t(unsigned int, bitflips, ret);
-+ }
-+
-+ free += RTL_ECC_FREE_SIZE;
-+ data += RTL_ECC_BLOCK_SIZE;
-+ parity += ctx->parity_size;
-+ }
-+
-+ nand_ecc_restore_req(&ctx->req_ctx, req);
-+
-+ if (unlikely(failure))
-+ dev_dbg(rtlc->dev, "ECC correction failed\n");
-+ else if (unlikely(bitflips > 2))
-+ dev_dbg(rtlc->dev, "%d bitflips detected\n", bitflips);
-+
-+ return failure ? -EBADMSG : bitflips;
-+}
-+
-+static int rtl_ecc_check_support(struct nand_device *nand)
-+{
-+ struct mtd_info *mtd = nanddev_to_mtd(nand);
-+ struct device *dev = nand->ecc.engine->dev;
-+
-+ if (mtd->oobsize != RTL_ECC_ALLOWED_OOB_SIZE ||
-+ mtd->writesize != RTL_ECC_ALLOWED_PAGE_SIZE) {
-+ dev_err(dev, "only flash geometry data=%d, oob=%d supported\n",
-+ RTL_ECC_ALLOWED_PAGE_SIZE, RTL_ECC_ALLOWED_OOB_SIZE);
-+ return -EINVAL;
-+ }
-+
-+ if (nand->ecc.user_conf.algo != NAND_ECC_ALGO_BCH ||
-+ nand->ecc.user_conf.strength != RTL_ECC_ALLOWED_STRENGTH ||
-+ nand->ecc.user_conf.placement != NAND_ECC_PLACEMENT_OOB ||
-+ nand->ecc.user_conf.step_size != RTL_ECC_BLOCK_SIZE) {
-+ dev_err(dev, "only algo=bch, strength=%d, placement=oob, step=%d supported\n",
-+ RTL_ECC_ALLOWED_STRENGTH, RTL_ECC_BLOCK_SIZE);
-+ return -EINVAL;
-+ }
-+
-+ return 0;
-+}
-+
-+static int rtl_ecc_init_ctx(struct nand_device *nand)
-+{
-+ struct nand_ecc_props *conf = &nand->ecc.ctx.conf;
-+ struct rtl_ecc_engine *rtlc = nand_to_rtlc(nand);
-+ struct mtd_info *mtd = nanddev_to_mtd(nand);
-+ int strength = nand->ecc.user_conf.strength;
-+ struct device *dev = nand->ecc.engine->dev;
-+ struct rtl_ecc_ctx *ctx;
-+ int ret;
-+
-+ ret = rtl_ecc_check_support(nand);
-+ if (ret)
-+ return ret;
-+
-+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
-+ if (!ctx)
-+ return -ENOMEM;
-+
-+ nand->ecc.ctx.priv = ctx;
-+ mtd_set_ooblayout(mtd, &rtl_ecc_ooblayout_ops);
-+
-+ conf->algo = NAND_ECC_ALGO_BCH;
-+ conf->strength = strength;
-+ conf->step_size = RTL_ECC_BLOCK_SIZE;
-+ conf->engine_type = NAND_ECC_ENGINE_TYPE_ON_HOST;
-+
-+ ctx->rtlc = rtlc;
-+ ctx->steps = mtd->writesize / RTL_ECC_BLOCK_SIZE;
-+ ctx->strength = strength;
-+ ctx->bch_mode = strength == 6 ? RTL_ECC_BCH6 : RTL_ECC_BCH12;
-+ ctx->parity_size = strength == 6 ? RTL_ECC_PARITY_SIZE_BCH6 : RTL_ECC_PARITY_SIZE_BCH12;
-+
-+ ret = nand_ecc_init_req_tweaking(&ctx->req_ctx, nand);
-+ if (ret)
-+ return ret;
-+
-+ dev_dbg(dev, "using bch%d with geometry data=%dx%d, free=%dx6, parity=%dx%d",
-+ conf->strength, ctx->steps, conf->step_size,
-+ ctx->steps, ctx->steps, ctx->parity_size);
-+
-+ return 0;
-+}
-+
-+static void rtl_ecc_cleanup_ctx(struct nand_device *nand)
-+{
-+ struct rtl_ecc_ctx *ctx = nand_to_ctx(nand);
-+
-+ if (ctx)
-+ nand_ecc_cleanup_req_tweaking(&ctx->req_ctx);
-+}
-+
-+static struct nand_ecc_engine_ops rtl_ecc_engine_ops = {
-+ .init_ctx = rtl_ecc_init_ctx,
-+ .cleanup_ctx = rtl_ecc_cleanup_ctx,
-+ .prepare_io_req = rtl_ecc_prepare_io_req,
-+ .finish_io_req = rtl_ecc_finish_io_req,
-+};
-+
-+static int rtl_ecc_probe(struct platform_device *pdev)
-+{
-+ struct device *dev = &pdev->dev;
-+ struct rtl_ecc_engine *rtlc;
-+ void __iomem *base;
-+ int ret;
-+
-+ rtlc = devm_kzalloc(dev, sizeof(*rtlc), GFP_KERNEL);
-+ if (!rtlc)
-+ return -ENOMEM;
-+
-+ base = devm_platform_ioremap_resource(pdev, 0);
-+ if (IS_ERR(base))
-+ return PTR_ERR(base);
-+
-+ ret = devm_mutex_init(dev, &rtlc->lock);
-+ if (ret)
-+ return ret;
-+
-+ rtlc->regmap = devm_regmap_init_mmio(dev, base, &rtl_ecc_regmap_config);
-+ if (IS_ERR(rtlc->regmap))
-+ return PTR_ERR(rtlc->regmap);
-+
-+ /*
-+ * Focus on simplicity and use a preallocated DMA buffer for data exchange with the
-+ * engine. For now make it a noncoherent memory model as invalidating/flushing caches
-+ * is faster than reading/writing uncached memory on the known architectures.
-+ */
-+
-+ rtlc->buf = dma_alloc_noncoherent(dev, RTL_ECC_DMA_SIZE, &rtlc->buf_dma,
-+ DMA_BIDIRECTIONAL, GFP_KERNEL);
-+ if (IS_ERR(rtlc->buf))
-+ return PTR_ERR(rtlc->buf);
-+
-+ rtlc->dev = dev;
-+ rtlc->engine.dev = dev;
-+ rtlc->engine.ops = &rtl_ecc_engine_ops;
-+ rtlc->engine.integration = NAND_ECC_ENGINE_INTEGRATION_EXTERNAL;
-+
-+ nand_ecc_register_on_host_hw_engine(&rtlc->engine);
-+
-+ platform_set_drvdata(pdev, rtlc);
-+
-+ return 0;
-+}
-+
-+static void rtl_ecc_remove(struct platform_device *pdev)
-+{
-+ struct rtl_ecc_engine *rtlc = platform_get_drvdata(pdev);
-+
-+ nand_ecc_unregister_on_host_hw_engine(&rtlc->engine);
-+ dma_free_noncoherent(rtlc->dev, RTL_ECC_DMA_SIZE, rtlc->buf, rtlc->buf_dma,
-+ DMA_BIDIRECTIONAL);
-+}
-+
-+static const struct of_device_id rtl_ecc_of_ids[] = {
-+ {
-+ .compatible = "realtek,rtl9301-ecc",
-+ },
-+ { /* sentinel */ },
-+};
-+
-+static struct platform_driver rtl_ecc_driver = {
-+ .driver = {
-+ .name = "rtl-nand-ecc-engine",
-+ .of_match_table = rtl_ecc_of_ids,
-+ },
-+ .probe = rtl_ecc_probe,
-+ .remove = rtl_ecc_remove,
-+};
-+module_platform_driver(rtl_ecc_driver);
-+
-+MODULE_LICENSE("GPL");
-+MODULE_AUTHOR("Markus Stockhausen <markus.stockhausen@gmx.de>");
-+MODULE_DESCRIPTION("Realtek NAND hardware ECC controller");
+++ /dev/null
-From 2250db8628a0d8293ad2e0671138b848a185fba1 Mon Sep 17 00:00:00 2001
-From: Markus Stockhausen <markus.stockhausen@gmx.de>
-Date: Sat, 21 Jun 2025 01:49:51 -0400
-Subject: irqchip/mips-gic: Allow forced affinity
-
-Devices of the Realtek MIPS Otto platform use the official rtl-otto-timer
-as clock event generator and CPU clocksource. It is registered for each CPU
-startup via cpuhp_setup_state() and forces the affinity of the clockevent
-interrupts to the appropriate CPU via irq_force_affinity().
-
-On the "smaller" devices with a vendor specific interrupt controller
-(supported by irq-realtek-rtl) the registration works fine. The "larger"
-RTL931x series is based on a MIPS interAptiv dual core with a MIPS GIC
-controller. Interrupt routing setup is cancelled because gic_set_affinity()
-does not accept the current (not yet online) CPU as a target.
-
-Relax the checks by evaluating the force parameter that is provided for
-exactly this purpose like in other drivers. With this the affinity can be
-set as follows:
-
- - force = false: allow to set affinity to any online cpu
- - force = true: allow to set affinity to any cpu
-
-Co-developed-by: Sebastian Gottschall <s.gottschall@dd-wrt.com>
-Signed-off-by: Sebastian Gottschall <s.gottschall@dd-wrt.com>
-Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
-Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-Link: https://lore.kernel.org/all/20250621054952.380374-1-markus.stockhausen@gmx.de
----
- drivers/irqchip/irq-mips-gic.c | 8 ++++++--
- 1 file changed, 6 insertions(+), 2 deletions(-)
-
---- a/drivers/irqchip/irq-mips-gic.c
-+++ b/drivers/irqchip/irq-mips-gic.c
-@@ -263,7 +263,11 @@ static int gic_set_affinity(struct irq_d
- unsigned long flags;
- unsigned int cpu;
-
-- cpu = cpumask_first_and(cpumask, cpu_online_mask);
-+ if (force)
-+ cpu = cpumask_first(cpumask);
-+ else
-+ cpu = cpumask_first_and(cpumask, cpu_online_mask);
-+
- if (cpu >= NR_CPUS)
- return -EINVAL;
-