aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBirger Koblitz <git@birger-koblitz.de>2021-12-11 20:14:47 +0100
committerDaniel Golle <daniel@makrotopia.org>2022-02-17 15:21:46 +0000
commit63a0a4d85bc900464c5b046b13808a582345f8c8 (patch)
treeab0f66a93a119bf1d72a98ef02ad1ff26af05f0e
parent48dd4466147de7b1f143c42d23fa4d4780496f40 (diff)
downloadupstream-63a0a4d85bc900464c5b046b13808a582345f8c8.tar.gz
upstream-63a0a4d85bc900464c5b046b13808a582345f8c8.tar.bz2
upstream-63a0a4d85bc900464c5b046b13808a582345f8c8.zip
realtek: Add support for RTL9300/RTL9310 I2C controller
This adds support for the RTL9300 and RTL9310 I2C controller. The controller implements the SMBus protocol for SMBus transfers over an I2C bus. The driver supports selecting one of the 2 possible SCL pins and any of the 8 possible SDA pins. Bus speeds of 100kHz (standard speed) and 400kHz (high speed I2C) are supported. Signed-off-by: Birger Koblitz <git@birger-koblitz.de>
-rw-r--r--target/linux/realtek/config-5.102
-rw-r--r--target/linux/realtek/files-5.10/drivers/i2c/busses/i2c-rtl9300.c488
-rw-r--r--target/linux/realtek/files-5.10/drivers/i2c/busses/i2c-rtl9300.h62
-rw-r--r--target/linux/realtek/patches-5.10/310-add-i2c-rtl9300-support.patch29
4 files changed, 581 insertions, 0 deletions
diff --git a/target/linux/realtek/config-5.10 b/target/linux/realtek/config-5.10
index 71028da77f..b62f7eb30e 100644
--- a/target/linux/realtek/config-5.10
+++ b/target/linux/realtek/config-5.10
@@ -82,7 +82,9 @@ CONFIG_HZ_PERIODIC=y
CONFIG_I2C=y
CONFIG_I2C_ALGOBIT=y
CONFIG_I2C_BOARDINFO=y
+CONFIG_I2C_CHARDEV=y
CONFIG_I2C_GPIO=y
+CONFIG_I2C_RTL9300=y
CONFIG_INITRAMFS_SOURCE=""
CONFIG_IRQCHIP=y
CONFIG_IRQ_DOMAIN=y
diff --git a/target/linux/realtek/files-5.10/drivers/i2c/busses/i2c-rtl9300.c b/target/linux/realtek/files-5.10/drivers/i2c/busses/i2c-rtl9300.c
new file mode 100644
index 0000000000..b3392ba5d5
--- /dev/null
+++ b/target/linux/realtek/files-5.10/drivers/i2c/busses/i2c-rtl9300.c
@@ -0,0 +1,488 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include "i2c-rtl9300.h"
+
+#define REG(x) (i2c->base + x + (i2c->scl_num ? i2c->mst2_offset : 0))
+#define REG_MASK(clear, set, reg) \
+ writel((readl(REG(reg)) & ~(clear)) | (set), REG(reg))
+
+struct i2c_drv_data {
+ int scl0_pin;
+ int scl1_pin;
+ int sda0_pin;
+ struct i2c_algorithm *algo;
+ int (*read)(struct rtl9300_i2c *i2c, u8 *buf, int len);
+ int (*write)(struct rtl9300_i2c *i2c, u8 *buf, int len);
+ void (*reg_addr_set)(struct rtl9300_i2c *i2c, u32 reg, u16 len);
+ int (*config_xfer)(struct rtl9300_i2c *i2c, u16 addr, u16 len);
+ int (*execute_xfer)(struct rtl9300_i2c *i2c, char read_write, int size,
+ union i2c_smbus_data * data, int len);
+ void (*writel)(struct rtl9300_i2c *i2c, u32 data);
+ void (*config_io)(struct rtl9300_i2c *i2c, int scl_num, int sda_num);
+ u32 mst2_offset;
+};
+
+DEFINE_MUTEX(i2c_lock);
+
+static void rtl9300_i2c_reg_addr_set(struct rtl9300_i2c *i2c, u32 reg, u16 len)
+{
+ // Set register address width
+ REG_MASK(0x3 << RTL9300_I2C_CTRL2_MADDR_WIDTH, len << RTL9300_I2C_CTRL2_MADDR_WIDTH,
+ RTL9300_I2C_CTRL2);
+
+ // Set register address
+ REG_MASK(0xffffff << RTL9300_I2C_CTRL1_MEM_ADDR, reg << RTL9300_I2C_CTRL1_MEM_ADDR,
+ RTL9300_I2C_CTRL1);
+}
+
+static void rtl9310_i2c_reg_addr_set(struct rtl9300_i2c *i2c, u32 reg, u16 len)
+{
+ // Set register address width
+ REG_MASK(0x3 << RTL9310_I2C_CTRL_MADDR_WIDTH, len << RTL9310_I2C_CTRL_MADDR_WIDTH,
+ RTL9310_I2C_CTRL);
+
+ // Set register address
+ writel(reg, REG(RTL9310_I2C_MEMADDR));
+}
+
+static void rtl9300_i2c_config_io(struct rtl9300_i2c *i2c, int scl_num, int sda_num)
+{
+ u32 v;
+
+ // Set SCL pin
+ REG_MASK(0, BIT(RTL9300_I2C_CTRL1_GPIO8_SCL_SEL), RTL9300_I2C_CTRL1);
+
+ // Set SDA pin
+ REG_MASK(0x7 << RTL9300_I2C_CTRL1_SDA_OUT_SEL,
+ i2c->sda_num << RTL9300_I2C_CTRL1_SDA_OUT_SEL, RTL9300_I2C_CTRL1);
+
+ // Set SDA pin to I2C functionality
+ v = readl(i2c->base + RTL9300_I2C_MST_GLB_CTRL);
+ v |= BIT(i2c->sda_num);
+ writel(v, i2c->base + RTL9300_I2C_MST_GLB_CTRL);
+}
+
+static void rtl9310_i2c_config_io(struct rtl9300_i2c *i2c, int scl_num, int sda_num)
+{
+ u32 v;
+
+ // Set SCL pin
+ REG_MASK(0, BIT(RTL9310_I2C_MST_IF_SEL_GPIO_SCL_SEL + scl_num), RTL9310_I2C_MST_IF_SEL);
+
+ // Set SDA pin
+ REG_MASK(0x7 << RTL9310_I2C_CTRL_SDA_OUT_SEL,
+ i2c->sda_num << RTL9310_I2C_CTRL_SDA_OUT_SEL, RTL9310_I2C_CTRL);
+
+ // Set SDA pin to I2C functionality
+ v = readl(i2c->base + RTL9310_I2C_MST_IF_SEL);
+ v |= BIT(i2c->sda_num);
+ writel(v, i2c->base + RTL9310_I2C_MST_IF_SEL);
+}
+
+static int rtl9300_i2c_config_xfer(struct rtl9300_i2c *i2c, u16 addr, u16 len)
+{
+ // Set bus frequency
+ REG_MASK(0x3 << RTL9300_I2C_CTRL2_SCL_FREQ,
+ i2c->bus_freq << RTL9300_I2C_CTRL2_SCL_FREQ, RTL9300_I2C_CTRL2);
+
+ // Set slave device address
+ REG_MASK(0x7f << RTL9300_I2C_CTRL2_DEV_ADDR,
+ addr << RTL9300_I2C_CTRL2_DEV_ADDR, RTL9300_I2C_CTRL2);
+
+ // Set data length
+ REG_MASK(0xf << RTL9300_I2C_CTRL2_DATA_WIDTH,
+ ((len - 1) & 0xf) << RTL9300_I2C_CTRL2_DATA_WIDTH, RTL9300_I2C_CTRL2);
+
+ // Set read mode to random
+ REG_MASK(0x1 << RTL9300_I2C_CTRL2_READ_MODE, 0, RTL9300_I2C_CTRL2);
+
+ return 0;
+}
+
+static int rtl9310_i2c_config_xfer(struct rtl9300_i2c *i2c, u16 addr, u16 len)
+{
+ // Set bus frequency
+ REG_MASK(0x3 << RTL9310_I2C_CTRL_SCL_FREQ,
+ i2c->bus_freq << RTL9310_I2C_CTRL_SCL_FREQ, RTL9310_I2C_CTRL);
+
+ // Set slave device address
+ REG_MASK(0x7f << RTL9310_I2C_CTRL_DEV_ADDR,
+ addr << RTL9310_I2C_CTRL_DEV_ADDR, RTL9310_I2C_CTRL);
+
+ // Set data length
+ REG_MASK(0xf << RTL9310_I2C_CTRL_DATA_WIDTH,
+ ((len - 1) & 0xf) << RTL9310_I2C_CTRL_DATA_WIDTH, RTL9310_I2C_CTRL);
+
+ // Set read mode to random
+ REG_MASK(0x1 << RTL9310_I2C_CTRL_READ_MODE, 0, RTL9310_I2C_CTRL);
+
+ return 0;
+}
+
+static int i2c_read(void __iomem *r0, u8 *buf, int len)
+{
+ int i;
+ u32 v;
+
+ if (len > 16)
+ return -EIO;
+
+ for (i = 0; i < len; i++) {
+ if (i % 4 == 0)
+ v = readl(r0 + i);
+ buf[i] = v;
+ v >>= 8;
+ }
+
+ return len;
+}
+
+static int i2c_write(void __iomem *r0, u8 *buf, int len)
+{
+ u32 v;
+ int i;
+
+ if (len > 16)
+ return -EIO;
+
+ for (i = 0; i < len; i++) {
+ if (! (i % 4))
+ v = 0;
+ v <<= 8;
+ v |= buf[i];
+ if (i % 4 == 3 || i == len - 1)
+ writel(v, r0 + (i / 4) * 4);
+ }
+
+ return len;
+}
+
+static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, int len)
+{
+ return i2c_read(REG(RTL9300_I2C_DATA_WORD0), buf, len);
+}
+
+static int rtl9300_i2c_write(struct rtl9300_i2c *i2c, u8 *buf, int len)
+{
+ return i2c_write(REG(RTL9300_I2C_DATA_WORD0), buf, len);
+}
+
+static int rtl9310_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, int len)
+{
+ return i2c_read(REG(RTL9310_I2C_DATA), buf, len);
+}
+
+static int rtl9310_i2c_write(struct rtl9300_i2c *i2c, u8 *buf, int len)
+{
+ return i2c_write(REG(RTL9310_I2C_DATA), buf, len);
+}
+
+static void rtl9300_writel(struct rtl9300_i2c *i2c, u32 data)
+{
+ writel(data, REG(RTL9300_I2C_DATA_WORD0));
+}
+
+static void rtl9310_writel(struct rtl9300_i2c *i2c, u32 data)
+{
+ writel(data, REG(RTL9310_I2C_DATA));
+}
+
+
+static int rtl9300_execute_xfer(struct rtl9300_i2c *i2c, char read_write,
+ int size, union i2c_smbus_data * data, int len)
+{
+ u32 v;
+
+ if (read_write == I2C_SMBUS_READ)
+ REG_MASK(BIT(RTL9300_I2C_CTRL1_RWOP), 0, RTL9300_I2C_CTRL1);
+ else
+ REG_MASK(0, BIT(RTL9300_I2C_CTRL1_RWOP), RTL9300_I2C_CTRL1);
+
+ REG_MASK(0, BIT(RTL9300_I2C_CTRL1_I2C_TRIG), RTL9300_I2C_CTRL1);
+ do {
+ v = readl(REG(RTL9300_I2C_CTRL1));
+ } while (v & BIT(RTL9300_I2C_CTRL1_I2C_TRIG));
+
+ if (v & BIT(RTL9300_I2C_CTRL1_I2C_FAIL))
+ return -EIO;
+
+ if (read_write == I2C_SMBUS_READ) {
+ if (size == I2C_SMBUS_BYTE || size == I2C_SMBUS_BYTE_DATA){
+ data->byte = readl(REG(RTL9300_I2C_DATA_WORD0));
+ } else if (size == I2C_SMBUS_WORD_DATA) {
+ data->word = readl(REG(RTL9300_I2C_DATA_WORD0));
+ } else if (len > 0) {
+ rtl9300_i2c_read(i2c, &data->block[0], len);
+ }
+ }
+
+ return 0;
+}
+
+static int rtl9310_execute_xfer(struct rtl9300_i2c *i2c, char read_write,
+ int size, union i2c_smbus_data * data, int len)
+{
+ u32 v;
+
+ if (read_write == I2C_SMBUS_READ)
+ REG_MASK(BIT(RTL9310_I2C_CTRL_RWOP), 0, RTL9310_I2C_CTRL);
+ else
+ REG_MASK(0, BIT(RTL9310_I2C_CTRL_RWOP), RTL9310_I2C_CTRL);
+
+ REG_MASK(0, BIT(RTL9310_I2C_CTRL_I2C_TRIG), RTL9310_I2C_CTRL);
+ do {
+ v = readl(REG(RTL9310_I2C_CTRL));
+ } while (v & BIT(RTL9310_I2C_CTRL_I2C_TRIG));
+
+ if (v & BIT(RTL9310_I2C_CTRL_I2C_FAIL))
+ return -EIO;
+
+ if (read_write == I2C_SMBUS_READ) {
+ if (size == I2C_SMBUS_BYTE || size == I2C_SMBUS_BYTE_DATA){
+ data->byte = readl(REG(RTL9310_I2C_DATA));
+ } else if (size == I2C_SMBUS_WORD_DATA) {
+ data->word = readl(REG(RTL9310_I2C_DATA));
+ } else if (len > 0) {
+ rtl9310_i2c_read(i2c, &data->block[0], len);
+ }
+ }
+
+ 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 *i2c = i2c_get_adapdata(adap);
+ struct i2c_drv_data *drv_data = (struct i2c_drv_data *)device_get_match_data(i2c->dev);
+ int len = 0, ret;
+
+ mutex_lock(&i2c_lock);
+ switch (size) {
+ case I2C_SMBUS_QUICK:
+ drv_data->config_xfer(i2c, addr, 0);
+ drv_data->reg_addr_set(i2c, 0, 0);
+ break;
+
+ case I2C_SMBUS_BYTE:
+ if (read_write == I2C_SMBUS_WRITE) {
+ drv_data->config_xfer(i2c, addr, 0);
+ drv_data->reg_addr_set(i2c, command, 1);
+ } else {
+ drv_data->config_xfer(i2c, addr, 1);
+ drv_data->reg_addr_set(i2c, 0, 0);
+ }
+ break;
+
+ case I2C_SMBUS_BYTE_DATA:
+ pr_debug("I2C_SMBUS_BYTE_DATA %02x, read %d cmd %02x\n", addr, read_write, command);
+ drv_data->reg_addr_set(i2c, command, 1);
+ drv_data->config_xfer(i2c, addr, 1);
+
+ if (read_write == I2C_SMBUS_WRITE) {
+ pr_debug("--> data %02x\n", data->byte);
+ drv_data->writel(i2c, data->byte);
+ }
+ break;
+
+ case I2C_SMBUS_WORD_DATA:
+ pr_debug("I2C_SMBUS_WORD %02x, read %d\n", addr, read_write);
+ drv_data->reg_addr_set(i2c, command, 1);
+ drv_data->config_xfer(i2c, addr, 2);
+ if (read_write == I2C_SMBUS_WRITE)
+ drv_data->writel(i2c, data->word);
+ break;
+
+ case I2C_SMBUS_BLOCK_DATA:
+ pr_debug("I2C_SMBUS_BLOCK_DATA %02x, read %d, len %d\n",
+ addr, read_write, data->block[0]);
+ drv_data->reg_addr_set(i2c, command, 1);
+ drv_data->config_xfer(i2c, addr, data->block[0]);
+ if (read_write == I2C_SMBUS_WRITE)
+ drv_data->write(i2c, &data->block[1], data->block[0]);
+ len = data->block[0];
+ break;
+
+ default:
+ dev_warn(&adap->dev, "Unsupported transaction %d\n", size);
+ return -EOPNOTSUPP;
+ }
+
+ ret = drv_data->execute_xfer(i2c, read_write, size, data, len);
+
+ 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,
+};
+
+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 resource *res;
+ struct rtl9300_i2c *i2c;
+ struct i2c_adapter *adap;
+ struct i2c_drv_data *drv_data;
+ struct device_node *node = pdev->dev.of_node;
+ u32 clock_freq, pin;
+ int ret = 0;
+
+ pr_info("%s probing I2C adapter\n", __func__);
+
+ if (!node) {
+ dev_err(i2c->dev, "No DT found\n");
+ return -EINVAL;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ drv_data = (struct i2c_drv_data *) device_get_match_data(&pdev->dev);
+
+ i2c = devm_kzalloc(&pdev->dev, sizeof(struct rtl9300_i2c), GFP_KERNEL);
+ if (!i2c)
+ return -ENOMEM;
+
+ i2c->base = devm_ioremap_resource(&pdev->dev, res);
+ i2c->mst2_offset = drv_data->mst2_offset;
+ if (IS_ERR(i2c->base))
+ return PTR_ERR(i2c->base);
+
+ pr_debug("%s base memory %08x\n", __func__, (u32)i2c->base);
+ i2c->dev = &pdev->dev;
+
+ if (of_property_read_u32(node, "clock-frequency", &clock_freq)) {
+ clock_freq = I2C_MAX_STANDARD_MODE_FREQ;
+ }
+ switch(clock_freq) {
+ case I2C_MAX_STANDARD_MODE_FREQ:
+ i2c->bus_freq = RTL9300_I2C_STD_FREQ;
+ break;
+
+ case I2C_MAX_FAST_MODE_FREQ:
+ i2c->bus_freq = RTL9300_I2C_FAST_FREQ;
+ break;
+ default:
+ dev_warn(i2c->dev, "clock-frequency %d not supported\n", clock_freq);
+ return -EINVAL;
+ }
+
+ dev_info(&pdev->dev, "SCL speed %d, mode is %d\n", clock_freq, i2c->bus_freq);
+
+ if (of_property_read_u32(node, "scl-pin", &pin)) {
+ dev_warn(i2c->dev, "SCL pin not found in DT, using default\n");
+ pin = drv_data->scl0_pin;
+ }
+ if (!(pin == drv_data->scl0_pin || pin == drv_data->scl1_pin)) {
+ dev_warn(i2c->dev, "SCL pin %d not supported\n", pin);
+ return -EINVAL;
+ }
+ i2c->scl_num = pin == drv_data->scl0_pin ? 0 : 1;
+ pr_info("%s scl_num %d\n", __func__, i2c->scl_num);
+
+ if (of_property_read_u32(node, "sda-pin", &pin)) {
+ dev_warn(i2c->dev, "SDA pin not found in DT, using default \n");
+ pin = drv_data->sda0_pin;
+ }
+ i2c->sda_num = pin - drv_data->sda0_pin;
+ if (i2c->sda_num < 0 || i2c->sda_num > 7) {
+ dev_warn(i2c->dev, "SDA pin %d not supported\n", pin);
+ return -EINVAL;
+ }
+ pr_info("%s sda_num %d\n", __func__, i2c->sda_num);
+
+ adap = &i2c->adap;
+ adap->owner = THIS_MODULE;
+ adap->algo = &rtl9300_i2c_algo;
+ adap->retries = 3;
+ adap->dev.parent = &pdev->dev;
+ i2c_set_adapdata(adap, i2c);
+ adap->dev.of_node = node;
+ strlcpy(adap->name, dev_name(&pdev->dev), sizeof(adap->name));
+
+ platform_set_drvdata(pdev, i2c);
+
+ drv_data->config_io(i2c, i2c->scl_num, i2c->sda_num);
+
+ ret = i2c_add_adapter(adap);
+
+ return ret;
+}
+
+static int rtl9300_i2c_remove(struct platform_device *pdev)
+{
+ struct rtl9300_i2c *i2c = platform_get_drvdata(pdev);
+
+ i2c_del_adapter(&i2c->adap);
+
+ return 0;
+}
+
+struct i2c_drv_data rtl9300_i2c_drv_data = {
+ .scl0_pin = 8,
+ .scl1_pin = 17,
+ .sda0_pin = 9,
+ .read = rtl9300_i2c_read,
+ .read = rtl9300_i2c_write,
+ .reg_addr_set = rtl9300_i2c_reg_addr_set,
+ .config_xfer = rtl9300_i2c_config_xfer,
+ .execute_xfer = rtl9300_execute_xfer,
+ .writel = rtl9300_writel,
+ .config_io = rtl9300_i2c_config_io,
+ .mst2_offset = 0x1c,
+};
+
+struct i2c_drv_data rtl9310_i2c_drv_data = {
+ .scl0_pin = 13,
+ .scl1_pin = 14,
+ .sda0_pin = 0,
+ .read = rtl9310_i2c_read,
+ .read = rtl9310_i2c_write,
+ .reg_addr_set = rtl9310_i2c_reg_addr_set,
+ .config_xfer = rtl9310_i2c_config_xfer,
+ .execute_xfer = rtl9310_execute_xfer,
+ .writel = rtl9310_writel,
+ .config_io = rtl9310_i2c_config_io,
+ .mst2_offset = 0x18,
+};
+
+static const struct of_device_id i2c_rtl9300_dt_ids[] = {
+ { .compatible = "realtek,rtl9300-i2c", .data = (void *) &rtl9300_i2c_drv_data },
+ { .compatible = "realtek,rtl9310-i2c", .data = (void *) &rtl9310_i2c_drv_data },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rtl838x_eth_of_ids);
+
+static struct platform_driver rtl9300_i2c_driver = {
+ .probe = rtl9300_i2c_probe,
+ .remove = rtl9300_i2c_remove,
+ .driver = {
+ .name = "i2c-rtl9300",
+ .pm = NULL,
+ .of_match_table = i2c_rtl9300_dt_ids,
+ },
+};
+
+module_platform_driver(rtl9300_i2c_driver);
+
+MODULE_AUTHOR("Birger Koblitz");
+MODULE_DESCRIPTION("RTL9300 I2C host driver");
+MODULE_LICENSE("GPL v2");
diff --git a/target/linux/realtek/files-5.10/drivers/i2c/busses/i2c-rtl9300.h b/target/linux/realtek/files-5.10/drivers/i2c/busses/i2c-rtl9300.h
new file mode 100644
index 0000000000..b10c38c91c
--- /dev/null
+++ b/target/linux/realtek/files-5.10/drivers/i2c/busses/i2c-rtl9300.h
@@ -0,0 +1,62 @@
+#ifndef I2C_RTL9300_H
+#define I2C_RTL9300_H
+
+#include <linux/i2c.h>
+
+#define RTL9300_I2C_CTRL1 0x00
+#define RTL9300_I2C_CTRL1_MEM_ADDR 8
+#define RTL9300_I2C_CTRL1_SDA_OUT_SEL 4
+#define RTL9300_I2C_CTRL1_GPIO8_SCL_SEL 3
+#define RTL9300_I2C_CTRL1_RWOP 2
+#define RTL9300_I2C_CTRL1_I2C_FAIL 1
+#define RTL9300_I2C_CTRL1_I2C_TRIG 0
+
+#define RTL9300_I2C_CTRL2 0x04
+#define RTL9300_I2C_CTRL2_DRIVE_ACK_DELAY 20
+#define RTL9300_I2C_CTRL2_CHECK_ACK_DELAY 16
+#define RTL9300_I2C_CTRL2_READ_MODE 15
+#define RTL9300_I2C_CTRL2_DEV_ADDR 8
+#define RTL9300_I2C_CTRL2_DATA_WIDTH 4
+#define RTL9300_I2C_CTRL2_MADDR_WIDTH 2
+#define RTL9300_I2C_CTRL2_SCL_FREQ 0
+
+#define RTL9300_I2C_DATA_WORD0 0x08
+
+#define RTL9300_I2C_MST_GLB_CTRL 0x18
+
+#define RTL9310_I2C_MST_IF_CTRL 0x00
+
+#define RTL9310_I2C_MST_IF_SEL 0x04
+#define RTL9310_I2C_MST_IF_SEL_GPIO_SCL_SEL 12
+
+#define RTL9310_I2C_CTRL 0x08
+#define RTL9310_I2C_CTRL_SCL_FREQ 30
+#define RTL9310_I2C_CTRL_CHECK_ACK_DELAY 26
+#define RTL9310_I2C_CTRL_DRIVE_ACK_DELAY 22
+#define RTL9310_I2C_CTRL_SDA_OUT_SEL 18
+#define RTL9310_I2C_CTRL_DEV_ADDR 11
+#define RTL9310_I2C_CTRL_MADDR_WIDTH 9
+#define RTL9310_I2C_CTRL_DATA_WIDTH 5
+#define RTL9310_I2C_CTRL_READ_MODE 4
+#define RTL9310_I2C_CTRL_RWOP 2
+#define RTL9310_I2C_CTRL_I2C_FAIL 1
+#define RTL9310_I2C_CTRL_I2C_TRIG 0
+
+#define RTL9310_I2C_MEMADDR 0x0c
+
+#define RTL9310_I2C_DATA 0x10
+
+#define RTL9300_I2C_STD_FREQ 0
+#define RTL9300_I2C_FAST_FREQ 1
+
+struct rtl9300_i2c {
+ void __iomem *base;
+ u32 mst2_offset;
+ struct device *dev;
+ struct i2c_adapter adap;
+ u8 bus_freq;
+ u8 sda_num; // SDA channel number
+ u8 scl_num; // SCL channel, mapping to master 1 or 2
+};
+
+#endif
diff --git a/target/linux/realtek/patches-5.10/310-add-i2c-rtl9300-support.patch b/target/linux/realtek/patches-5.10/310-add-i2c-rtl9300-support.patch
new file mode 100644
index 0000000000..c4d23d90e1
--- /dev/null
+++ b/target/linux/realtek/patches-5.10/310-add-i2c-rtl9300-support.patch
@@ -0,0 +1,29 @@
+--- a/drivers/i2c/busses/Kconfig
++++ b/drivers/i2c/busses/Kconfig
+@@ -954,6 +954,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 adapter"
++ depends on OF
++ help
++ Say Y here to include support for the I2C adapter in Realtek RTL9300
++ and RTL9310 SoCs.
++
++ This driver can also be built as a module. If so, the module will
++ be called i2c-rtl9300.
++
+ config HAVE_S3C2410_I2C
+ bool
+ help
+--- a/drivers/i2c/busses/Makefile
++++ b/drivers/i2c/busses/Makefile
+@@ -95,6 +95,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_S3C2410) += i2c-s3c2410.o
+ obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o
+ obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o