This commit adds the driver to control the Advantech EIO I2C block, this
block is included in the Advantech EIO MFD.

Signed-off-by: Ramiro Oliveira <[email protected]>
---
 MAINTAINERS                  |    1 +
 drivers/i2c/busses/Kconfig   |    6 +
 drivers/i2c/busses/Makefile  |    1 +
 drivers/i2c/busses/i2c-eio.c | 1142 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 1150 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index fdd39b152f41..be9d3c4e1ce1 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -621,6 +621,7 @@ M:  Ramiro Oliveira <[email protected]>
 S:     Maintained
 F:     drivers/gpio/gpio-eio.c
 F:     drivers/hwmon/eio-hwmon.c
+F:     drivers/i2c/busses/i2c-eio.c
 F:     drivers/mfd/eio_core.c
 F:     include/linux/mfd/eio.h
 
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 09ba55bae1fa..e597c08414e4 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -656,6 +656,12 @@ config I2C_DIGICOLOR
          This driver can also be built as a module.  If so, the module
          will be called i2c-digicolor.
 
+config I2C_EIO
+       tristate "Advantech EIO I2C bus"
+       depends on MFD_EIO
+       help
+         Say Y or M to build support for Advantech EIO I2C block.
+
 config I2C_EG20T
        tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) I2C"
        depends on PCI && (X86_32 || MIPS || COMPILE_TEST)
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index fb985769f5ff..b65bb06b14c6 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -62,6 +62,7 @@ obj-$(CONFIG_I2C_DESIGNWARE_AMDISP) += i2c-designware-amdisp.o
 obj-$(CONFIG_I2C_DESIGNWARE_PCI)                       += i2c-designware-pci.o
 i2c-designware-pci-y                                   := 
i2c-designware-pcidrv.o
 obj-$(CONFIG_I2C_DIGICOLOR)    += i2c-digicolor.o
+obj-$(CONFIG_I2C_EIO)          += i2c-eio.o
 obj-$(CONFIG_I2C_EG20T)                += i2c-eg20t.o
 obj-$(CONFIG_I2C_EMEV2)                += i2c-emev2.o
 obj-$(CONFIG_I2C_EXYNOS5)      += i2c-exynos5.o
diff --git a/drivers/i2c/busses/i2c-eio.c b/drivers/i2c/busses/i2c-eio.c
new file mode 100644
index 000000000000..a867f24a4809
--- /dev/null
+++ b/drivers/i2c/busses/i2c-eio.c
@@ -0,0 +1,1142 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * I2C and SMBus driver of EIO embedded driver
+ *
+ * Copyright (C) 2025 Advantech Co., Ltd.
+ */
+
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/eio.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#define SUPPORTED_COMMON (I2C_FUNC_I2C | \
+                         I2C_FUNC_SMBUS_QUICK | \
+                         I2C_FUNC_SMBUS_BYTE | \
+                         I2C_FUNC_SMBUS_BYTE_DATA | \
+                         I2C_FUNC_SMBUS_WORD_DATA | \
+                         I2C_FUNC_SMBUS_I2C_BLOCK)
+#define SUPPORTED_SMB  (SUPPORTED_COMMON | I2C_FUNC_SMBUS_BLOCK_DATA)
+#define SUPPORTED_I2C  (SUPPORTED_COMMON | I2C_FUNC_10BIT_ADDR)
+
+#define MAX_I2C_SMB            4
+
+#define REG_PNP_INDEX          0x299
+#define REG_PNP_DATA           0x29A
+#define REG_SUB_PNP_INDEX      0x499
+#define REG_SUB_PNP_DATA       0x49A
+#define REG_EXT_MODE_ENTER     0x87
+#define REG_EXT_MODE_EXIT      0xAA
+#define REG_LDN                        0x07
+
+#define LDN_I2C0               0x20
+#define LDN_I2C1               0x21
+#define LDN_SMBUS0             0x22
+#define LDN_SMBUS1             0x23
+
+#define REG_BASE_HI            0x60
+#define REG_BASE_LO            0x61
+
+#define I2C_REG_CTRL           0x00
+#define I2C_CTRL_STOP          BIT(1)
+
+#define I2C_REG_STAT           0x01
+#define I2C_STAT_RXREADY       BIT(6)
+#define I2C_STAT_TXDONE                BIT(5)
+#define I2C_STAT_NAK_ERR       BIT(4)
+#define I2C_STAT_ARL_ERR       BIT(3)
+#define I2C_STAT_SLV_STP       BIT(2)
+#define I2C_STAT_BUSY          BIT(1)
+#define I2C_STAT_MST_SLV       BIT(0)
+
+#define I2C_REG_MYADDR         0x02
+#define I2C_REG_ADDR           0x03
+#define I2C_REG_DATA           0x04
+#define I2C_REG_PRESCALE1      0x05
+#define I2C_REG_PRESCALE2      0x06
+
+#define I2C_REG_ECTRL          0x07
+#define I2C_ECTRL_RST          BIT(7)
+
+#define I2C_REG_SEM            0x08
+#define I2C_SEM_INUSE          BIT(1)
+
+#define SMB_REG_HC2            0x0C
+
+#define SMB_REG_HS             0x00
+#define SMB_HS_BUSY            BIT(0)
+#define SMB_HS_FINISH          BIT(1)
+#define SMB_HS_ARL_ERR         BIT(3)
+#define SMB_HS_FAILED          BIT(4)
+#define SMB_HS_RX_READY                BIT(5)
+#define SMB_HS_INUSE           BIT(6)
+#define SMB_HS_TX_DONE         BIT(7)
+
+#define SMB_REG_HS2            0x01
+#define SMB_HS2_HNOTIFY                BIT(0)
+#define SMB_HS2_PEC_ERR                BIT(1)
+#define SMB_HS2_NACK_ERR       BIT(2)
+#define SMB_HS2_ALERT_STS      BIT(3)
+#define SMB_HS2_TO_ERR         BIT(4)
+#define SMB_HS2_SSTOP_STS      BIT(5)
+#define SMB_HS2_STX_REQ                BIT(6)
+#define SMB_HS2_SMODE          BIT(7)
+
+#define SMB_REG_HC             0x02
+#define SMB_HC_I2C_NACKEN      BIT(0)
+#define SMB_HC_KILL            BIT(1)
+#define SMB_HC_CMD_SHIFT       2
+#define SMB_HC_LAST_BYTE       BIT(5)
+#define SMB_HC_START           BIT(6)
+#define SMB_HC_PEC_EN          BIT(7)
+
+#define SMB_REG_HCMD           0x03
+#define SMB_REG_HADDR          0x04
+#define SMB_REG_HD0            0x05
+#define SMB_REG_HD1            0x06
+#define SMB_REG_HBLOCK         0x07
+#define SMB_REG_HPEC           0x08
+#define SMB_REG_SADDR          0x09
+#define SMB_REG_SD0            0x0A
+#define SMB_REG_SD1            0x0B
+
+#define SMB_REG_HC2            0x0C
+#define SMB_HC2_HNOTIFY_DIS    BIT(0)
+#define SMB_HC2_I2C_EN         BIT(1)
+#define SMB_HC2_AAPEC          BIT(2)
+#define SMB_HC2_E32B           BIT(3)
+#define SMB_HC2_SRESET         BIT(7)
+
+#define SMB_REG_HPIN           0x0D
+#define SMB_REG_HC3            0x0E
+#define SMB_REG_HC4            0x0F
+#define SMB_REG_NOTIFY_D0      0x11
+#define SMB_REG_NOTIFY_D1      0x12
+#define SMB_REG_HPRESCALE1     0x13
+#define SMB_REG_HPRESCALE2     0x14
+#define SMB_REG_HEXTRA         0x15
+
+#define I2C_TIMEOUT            (10 * USEC_PER_MSEC)
+#define USE_DEFAULT            -1
+
+#define CHIP_CLK               50000
+#define I2C_SCLH_HIGH          2500
+#define I2C_SCLH_LOW           1000
+#define I2C_SCL_FAST_MODE      0x80
+#define I2C_THRESHOLD_SPEED    100
+#define I2C_THRESHOLD_SCLH     30
+#define I2C_FREQ_MAX           400
+#define I2C_FREQ_MIN           8
+
+enum eio_chan_id {
+       EIO_I2C0 = 0,
+       EIO_I2C1,
+       EIO_SMB0,
+       EIO_SMB1,
+};
+
+struct eio_i2c_dev {
+       struct device *dev;
+       struct device *mfd;
+       struct regmap *regmap;
+       struct mutex pnp_mutex; /* Mutex for PNP acces */
+       struct eio_i2c_chan *chan[MAX_I2C_SMB];
+};
+
+struct eio_i2c_chan {
+       u16 base;
+       enum eio_chan_id id;
+       struct eio_i2c_dev *parent;
+       struct i2c_adapter adap;
+       struct mutex lock; /* Mutex for regmap writes */
+       int freq_override;                  /* kHz or USE_DEFAULT */
+};
+
+static int timeout = I2C_TIMEOUT;
+module_param(timeout, int, 0444);
+MODULE_PARM_DESC(timeout, "Set IO timeout value.\n");
+
+static int i2c0_freq = USE_DEFAULT;
+module_param(i2c0_freq, int, 0444);
+MODULE_PARM_DESC(i2c0_freq, "Set EIO's I2C0 freq.\n");
+
+static int i2c1_freq = USE_DEFAULT;
+module_param(i2c1_freq, int, 0444);
+MODULE_PARM_DESC(i2c1_freq, "Set EIO's I2C1 freq.\n");
+
+static int smb0_freq = USE_DEFAULT;
+module_param(smb0_freq, int, 0444);
+MODULE_PARM_DESC(smb0_freq, "Set EIO's SMB0 freq.\n");
+
+static int smb1_freq = USE_DEFAULT;
+module_param(smb1_freq, int, 0444);
+MODULE_PARM_DESC(smb1_freq, "Set EIO's SMB1 freq.\n");
+
+static inline u16 eio_enc_7bit_addr(u16 x)
+{
+       return ((x & 0x07F) << 1);
+}
+
+static inline u16 eio_enc_10bit_addr(u16 x)
+{
+       return ((x & 0xFF) | ((x & 0x0300) << 1) | 0xF000);
+}
+
+static inline bool is_i2c(const struct eio_i2c_chan *i2c_chan)
+{
+       return i2c_chan->id == EIO_I2C0 || i2c_chan->id == EIO_I2C1;
+}
+
+static inline struct device *eio_dev(const struct eio_i2c_chan *i2c_chan)
+{
+       return i2c_chan->parent->dev;
+}
+
+static inline struct regmap *eio_map(const struct eio_i2c_chan *i2c_chan)
+{
+       return i2c_chan->parent->regmap;
+}
+
+static inline int eio_reg_write(struct eio_i2c_chan *i2c_chan,
+                               unsigned int reg_off, unsigned int val)
+{
+       return regmap_write(eio_map(i2c_chan), i2c_chan->base + reg_off, val);
+}
+
+static inline int eio_reg_read(const struct eio_i2c_chan *chan,
+                              unsigned int reg, unsigned int *val)
+{
+       int ret;
+
+       ret = regmap_read(chan->parent->regmap, chan->base + reg, val);
+       return ret;
+}
+
+static inline int eio_reg_set_bits(const struct eio_i2c_chan *chan,
+                                  unsigned int reg, unsigned int mask)
+{
+       return regmap_update_bits(chan->parent->regmap, reg, mask, mask);
+}
+
+static inline int eio_reg_clear_bits(const struct eio_i2c_chan *chan,
+                                    unsigned int reg, unsigned int mask)
+{
+       return regmap_update_bits(chan->parent->regmap, reg, mask, 0);
+}
+
+static inline int eio_reg_or(struct eio_i2c_chan *chan,
+                            unsigned int reg, unsigned int mask)
+{
+       return eio_reg_set_bits(chan, reg, mask);
+}
+
+static inline int eio_reg_and(struct eio_i2c_chan *chan,
+                             unsigned int reg, unsigned int mask)
+{
+       return eio_reg_clear_bits(chan, reg, ~mask);
+}
+
+static inline unsigned int eio_chan_reg(const struct eio_i2c_chan *i2c_chan,
+                                       unsigned int i2c_reg,
+                                       unsigned int smb_reg)
+{
+       return is_i2c(i2c_chan) ? i2c_reg : smb_reg;
+}
+
+static inline int eio_trigger_read(struct eio_i2c_chan *i2c_chan, u32 *data)
+{
+       unsigned int reg = eio_chan_reg(i2c_chan, I2C_REG_DATA, SMB_REG_HD0);
+
+       return eio_reg_read(i2c_chan, reg, data);
+}
+
+static int wait_busy(struct eio_i2c_chan *i2c_chan)
+{
+       ktime_t time_end = ktime_add_us(ktime_get(), timeout);
+       unsigned int reg = eio_chan_reg(i2c_chan, I2C_REG_STAT, SMB_REG_HS);
+       unsigned int target = eio_chan_reg(i2c_chan, I2C_STAT_BUSY, 
SMB_HS_BUSY);
+       unsigned int val;
+       int cnt = 0;
+
+       do {
+               fsleep(cnt++);
+
+               if (ktime_after(ktime_get(), time_end)) {
+                       dev_err(eio_dev(i2c_chan), "Wait I2C bus busy 
timeout\n");
+                       return -ETIME;
+               }
+
+               if (eio_reg_read(i2c_chan, reg, &val))
+                       return -EIO;
+
+       } while (val & target);
+
+       return 0;
+}
+
+static void reset_bus(struct eio_i2c_chan *i2c_chan)
+{
+       ktime_t time_end = ktime_add_us(ktime_get(), timeout);
+       unsigned int reg = eio_chan_reg(i2c_chan, I2C_REG_ECTRL, SMB_REG_HC2);
+       unsigned int target = eio_chan_reg(i2c_chan, I2C_ECTRL_RST, 
SMB_HC2_SRESET);
+       unsigned int val = 0;
+       unsigned int cnt = 0;
+
+       dev_dbg(eio_dev(i2c_chan), "i2c[%d] bus reset\n", i2c_chan->id);
+
+       if (is_i2c(i2c_chan))
+               eio_reg_write(i2c_chan, I2C_REG_ECTRL, I2C_ECTRL_RST);
+       else
+               eio_reg_or(i2c_chan, SMB_REG_HC2, SMB_HC2_SRESET);
+
+       do {
+               fsleep(cnt++);
+
+               if (ktime_after(ktime_get(), time_end)) {
+                       dev_err(eio_dev(i2c_chan), "bus reset timeout\n");
+                       return;
+               }
+
+               if (eio_reg_read(i2c_chan, reg, &val))
+                       return;
+
+       } while (val & target);
+
+       wait_busy(i2c_chan);
+}
+
+static int wait_bus_free(struct eio_i2c_chan *i2c_chan)
+{
+       ktime_t time_end = ktime_add_us(ktime_get(), timeout);
+       unsigned int val;
+       int cnt = 1;
+
+       /* Wait if channel is resetting */
+       do {
+               fsleep(cnt);
+
+               if (ktime_after(ktime_get(), time_end)) {
+                       dev_err(eio_dev(i2c_chan), "Wait bus reset timeout\n");
+                       return -ETIME;
+               }
+
+               if (eio_reg_read(i2c_chan,
+                                eio_chan_reg(i2c_chan, I2C_REG_ECTRL, 
SMB_REG_HC2),
+                                &val))
+                       return -EIO;
+
+       } while (val & eio_chan_reg(i2c_chan, I2C_ECTRL_RST, SMB_HC2_SRESET));
+
+       /* Wait INUSE */
+       time_end = ktime_add_us(ktime_get(), timeout);
+
+       do {
+               fsleep(cnt);
+
+               if (ktime_after(ktime_get(), time_end)) {
+                       dev_err(eio_dev(i2c_chan), "Timeout: I2C bus in use\n");
+                       return -ETIME;
+               }
+
+               if (eio_reg_read(i2c_chan,
+                                eio_chan_reg(i2c_chan, I2C_REG_SEM, 
SMB_REG_HS),
+                                &val))
+                       return -EIO;
+
+       } while (val & eio_chan_reg(i2c_chan, I2C_SEM_INUSE, SMB_HS_INUSE));
+
+       return 0;
+}
+
+static int let_stop(struct eio_i2c_chan *i2c_chan)
+{
+       unsigned int reg = eio_chan_reg(i2c_chan, I2C_REG_CTRL, SMB_REG_HC);
+       unsigned int target = eio_chan_reg(i2c_chan, I2C_CTRL_STOP, 
SMB_HC_LAST_BYTE);
+
+       return eio_reg_or(i2c_chan, reg, target);
+}
+
+static int clr_inuse(struct eio_i2c_chan *i2c_chan)
+{
+       if (is_i2c(i2c_chan))
+               return eio_reg_write(i2c_chan, I2C_REG_SEM, I2C_SEM_INUSE);
+
+       return eio_reg_or(i2c_chan, SMB_REG_HS, SMB_HS_INUSE);
+}
+
+static int bus_stop(struct eio_i2c_chan *i2c_chan)
+{
+       ktime_t time_end = ktime_add_us(ktime_get(), timeout);
+       unsigned int reg = eio_chan_reg(i2c_chan, I2C_REG_CTRL, SMB_REG_HC);
+       unsigned int target = eio_chan_reg(i2c_chan, I2C_CTRL_STOP, 
SMB_HC_LAST_BYTE);
+       unsigned int val = 0;
+       int cnt = 0;
+
+       /* Set STOP bit */
+       eio_reg_or(i2c_chan, reg, target);
+
+       /* Wait until STOP bit clears */
+       do {
+               fsleep(cnt++);
+
+               if (ktime_after(ktime_get(), time_end))
+                       return -ETIME;
+
+               if (eio_reg_read(i2c_chan, reg, &val))
+                       return -EIO;
+
+       } while (val & target);
+
+       return 0;
+}
+
+static void switch_i2c_mode(struct eio_i2c_chan *i2c_chan, bool on)
+{
+       u32 tmp;
+
+       if (is_i2c(i2c_chan))
+               return;
+
+       if (eio_reg_read(i2c_chan, SMB_REG_HC2, &tmp))
+               return;
+
+       eio_reg_write(i2c_chan, SMB_REG_HC2,
+                     on ? (tmp | SMB_HC2_I2C_EN | SMB_HC2_SRESET)
+                     : (tmp & ~SMB_HC2_I2C_EN));
+}
+
+static void i2c_clear(struct eio_i2c_chan *i2c_chan)
+{
+       if (is_i2c(i2c_chan)) {
+               eio_reg_write(i2c_chan, I2C_REG_STAT, 0xFF);
+       } else {
+               eio_reg_or(i2c_chan, SMB_REG_HS,  0xA9);
+               eio_reg_or(i2c_chan, SMB_REG_HS2, 0x4C);
+       }
+}
+
+static int wait_write_done(struct eio_i2c_chan *i2c_chan, bool no_ack)
+{
+       ktime_t time_end = ktime_add_us(ktime_get(), timeout);
+       unsigned int val = 0;
+       int cnt = 0;
+       unsigned int reg = eio_chan_reg(i2c_chan, I2C_REG_STAT, SMB_REG_HS);
+       unsigned int target = eio_chan_reg(i2c_chan, I2C_STAT_TXDONE, 
SMB_HS_TX_DONE);
+
+       do {
+               fsleep(cnt++);
+               if (ktime_after(ktime_get(), time_end)) {
+                       if (is_i2c(i2c_chan)) {
+                               eio_reg_or(i2c_chan, I2C_REG_STAT, 0);
+                       } else {
+                               eio_reg_or(i2c_chan, SMB_REG_HS,  0);
+                               eio_reg_or(i2c_chan, SMB_REG_HS2, 0);
+                       }
+                       dev_err(eio_dev(i2c_chan), "wait write complete timeout 
%X %X\n",
+                               val, target);
+                       return -ETIME;
+               }
+               if (eio_reg_read(i2c_chan, reg, &val))
+                       return -EIO;
+
+       } while ((val & target) == 0);
+
+       if (no_ack)
+               return 0;
+
+       if (is_i2c(i2c_chan)) {
+               eio_reg_or(i2c_chan, I2C_REG_STAT, 0);
+               return (val & I2C_STAT_NAK_ERR) ? -EIO : 0;
+       }
+
+       eio_reg_or(i2c_chan, SMB_REG_HS, 0);
+       if (eio_reg_read(i2c_chan, SMB_REG_HS2, &val))
+               return -EIO;
+       eio_reg_write(i2c_chan, SMB_REG_HS2, val);
+
+       return (val & SMB_HS2_NACK_ERR) ? -EIO : 0;
+}
+
+static int wait_ready(struct eio_i2c_chan *i2c_chan)
+{
+       int ret;
+
+       ret = wait_bus_free(i2c_chan);
+       if (ret)
+               return ret;
+
+       if (wait_busy(i2c_chan) == 0)
+               return 0;
+
+       reset_bus(i2c_chan);
+
+       return wait_busy(i2c_chan);
+}
+
+static int write_addr(struct eio_i2c_chan *i2c_chan, int addr, bool no_ack)
+{
+       eio_reg_write(i2c_chan, eio_chan_reg(i2c_chan, I2C_REG_ADDR, 
SMB_REG_HADDR),
+                     addr);
+
+       return wait_write_done(i2c_chan, no_ack);
+}
+
+static int write_data(struct eio_i2c_chan *i2c_chan, int data, bool no_ack)
+{
+       eio_reg_write(i2c_chan, eio_chan_reg(i2c_chan, I2C_REG_DATA, 
SMB_REG_HD0),
+                     data);
+
+       return wait_write_done(i2c_chan, no_ack);
+}
+
+static int read_data(struct eio_i2c_chan *i2c_chan, u8 *data)
+{
+       unsigned int val = 0, tmp;
+       int cnt = 0;
+       ktime_t time_end = ktime_add_us(ktime_get(), timeout);
+       unsigned int stat = eio_chan_reg(i2c_chan, I2C_REG_STAT, SMB_REG_HS);
+       unsigned int target = eio_chan_reg(i2c_chan, I2C_STAT_RXREADY, 
SMB_HS_RX_READY);
+       unsigned int reg = eio_chan_reg(i2c_chan, I2C_REG_DATA, SMB_REG_HD0);
+
+       do {
+               fsleep(cnt++);
+
+               if (ktime_after(ktime_get(), time_end)) {
+                       eio_reg_or(i2c_chan, stat, 0);
+                       dev_err(eio_dev(i2c_chan), "read data timeout\n");
+                       return -ETIME;
+               }
+
+               if (eio_reg_read(i2c_chan, stat, &val))
+                       return -EIO;
+
+       } while ((val & target) != target);
+
+       /* clear status */
+       eio_reg_write(i2c_chan, stat, val);
+
+       /* Must read data after clearing status */
+       if (eio_reg_read(i2c_chan, reg, &tmp))
+               return -EIO;
+       *data = (u8)tmp;
+
+       return 0;
+}
+
+static int set_freq(struct eio_i2c_chan *i2c_chan, int freq)
+{
+       u8 pre1, pre2;
+       u16 speed;
+       unsigned int reg1 = eio_chan_reg(i2c_chan, I2C_REG_PRESCALE1, 
SMB_REG_HPRESCALE1);
+       unsigned int reg2 = eio_chan_reg(i2c_chan, I2C_REG_PRESCALE2, 
SMB_REG_HPRESCALE2);
+
+       dev_dbg(eio_dev(i2c_chan), "set freq: %dkHz\n", freq);
+       if (freq > I2C_FREQ_MAX || freq < I2C_FREQ_MIN) {
+               dev_err(eio_dev(i2c_chan), "Invalid i2c freq: %d\n", freq);
+               return -EINVAL;
+       }
+
+       speed = (freq < I2C_THRESHOLD_SCLH) ? I2C_SCLH_LOW : I2C_SCLH_HIGH;
+
+       pre1 = (u8)(CHIP_CLK / speed);
+       pre2 = (u8)((speed / freq) - 1);
+
+       if (freq > I2C_THRESHOLD_SCLH)
+               pre2 |= I2C_SCL_FAST_MODE;
+
+       eio_reg_write(i2c_chan, reg1, pre1);
+       eio_reg_write(i2c_chan, reg2, pre2);
+
+       return 0;
+}
+
+static int get_freq(struct eio_i2c_chan *i2c_chan, int *freq)
+{
+       int clk;
+       unsigned int pre1 = 0, pre2 = 0;
+       unsigned int reg1 = eio_chan_reg(i2c_chan, I2C_REG_PRESCALE1, 
SMB_REG_HPRESCALE1);
+       unsigned int reg2 = eio_chan_reg(i2c_chan, I2C_REG_PRESCALE2, 
SMB_REG_HPRESCALE2);
+
+       if (eio_reg_read(i2c_chan, reg1, &pre1))
+               return -EIO;
+       if (eio_reg_read(i2c_chan, reg2, &pre2))
+               return -EIO;
+
+       clk = (pre2 & I2C_SCL_FAST_MODE) ? I2C_SCLH_HIGH : I2C_SCLH_LOW;
+       pre2 &= ~I2C_SCL_FAST_MODE;
+
+       *freq = clk / ((int)pre2 + 1);
+
+       return 0;
+}
+
+static int smb_access(struct eio_i2c_chan *i2c_chan, u8 addr, bool is_read, u8 
cmd,
+                     int size, union i2c_smbus_data *data)
+{
+       int i, tmp, ret = 0;
+       unsigned int st1, st2;
+       int len = 0;
+
+       mutex_lock(&i2c_chan->lock);
+
+       ret = wait_ready(i2c_chan);
+       if (ret)
+               goto exit;
+
+       /* Force SMBus mode */
+       switch_i2c_mode(i2c_chan, false);
+
+       addr = eio_enc_7bit_addr(addr) | (is_read ? 1 : 0);
+       eio_reg_write(i2c_chan, SMB_REG_HADDR, addr);
+       eio_reg_write(i2c_chan, SMB_REG_HCMD,  cmd);
+
+       dev_dbg(eio_dev(i2c_chan), "SMB[%d], addr:0x%02X, cmd:0x%02X size=%d\n",
+               i2c_chan->id, addr, cmd, size);
+
+       switch (size) {
+       case I2C_SMBUS_QUICK:
+               dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_QUICK\n");
+               break;
+
+       case I2C_SMBUS_BYTE:
+               if (!is_read) {
+                       dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_BYTE\n");
+                       eio_reg_write(i2c_chan, SMB_REG_HCMD, cmd);
+               }
+               break;
+
+       case I2C_SMBUS_BYTE_DATA:
+               dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_BYTE_DATA\n");
+               if (!is_read) {
+                       eio_reg_write(i2c_chan, SMB_REG_HD0, data->byte);
+                       dev_dbg(eio_dev(i2c_chan), "write %X\n", data->byte);
+               }
+               break;
+
+       case I2C_SMBUS_WORD_DATA:
+               dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_WORD_DATA\n");
+               if (!is_read) {
+                       eio_reg_write(i2c_chan, SMB_REG_HD0, data->block[0]);
+                       eio_reg_write(i2c_chan, SMB_REG_HD1, data->block[1]);
+               }
+               break;
+
+       case I2C_SMBUS_PROC_CALL:
+               dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_PROC_CALL\n");
+               eio_reg_write(i2c_chan, SMB_REG_HD0, data->block[0]);
+               eio_reg_write(i2c_chan, SMB_REG_HD1, data->block[1]);
+               break;
+
+       case I2C_SMBUS_BLOCK_DATA:
+               dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_BLOCK_DATA\n");
+               if (is_read)
+                       break;
+
+               /* Program command type */
+               eio_reg_read(i2c_chan, SMB_REG_HC, (unsigned int *)&tmp);
+               tmp &= ~(0x07 << SMB_HC_CMD_SHIFT);
+               tmp |=  (size << SMB_HC_CMD_SHIFT);
+               eio_reg_write(i2c_chan, SMB_REG_HC, tmp);
+
+               /* Force write for payload stage */
+               eio_reg_write(i2c_chan, SMB_REG_HADDR, addr & ~0x01);
+
+               /* Reset internal buffer index pointer */
+               eio_reg_and(i2c_chan, SMB_REG_HC2, (int)~SMB_HC2_E32B);
+               eio_reg_or(i2c_chan, SMB_REG_HC2, SMB_HC2_E32B);
+
+               /* Write length + data */
+               eio_reg_write(i2c_chan, SMB_REG_HD0, data->block[0]);
+               for (i = 1; i <= data->block[0]; i++)
+                       eio_reg_write(i2c_chan, SMB_REG_HBLOCK, data->block[i]);
+               break;
+
+       case I2C_SMBUS_BLOCK_PROC_CALL:
+               /* Set command type field */
+               eio_reg_and(i2c_chan, SMB_REG_HC, (0x07 << SMB_HC_CMD_SHIFT));
+               eio_reg_write(i2c_chan, SMB_REG_HD0, data->block[0]);
+
+               /* Reset buffer index */
+               eio_reg_and(i2c_chan, SMB_REG_HC2, (int)~SMB_HC2_E32B);
+               eio_reg_or(i2c_chan, SMB_REG_HC2, SMB_HC2_E32B);
+
+               for (i = 1; i <= data->block[0]; i++)
+                       eio_reg_write(i2c_chan, SMB_REG_HBLOCK, data->block[i]);
+               break;
+
+       default:
+               ret = -EINVAL;
+               goto exit;
+       }
+
+       /* Launch transaction */
+       eio_reg_read(i2c_chan, SMB_REG_HC, (unsigned int *)&tmp);
+       tmp &= ~(0x07 << SMB_HC_CMD_SHIFT);
+       tmp |=  (size << SMB_HC_CMD_SHIFT) | SMB_HC_START;
+       tmp &= ~(SMB_HC_I2C_NACKEN | SMB_HC_KILL | SMB_HC_PEC_EN);
+       eio_reg_write(i2c_chan, SMB_REG_HC, tmp);
+
+       ret = wait_busy(i2c_chan);
+       if (ret)
+               goto exit;
+
+       eio_reg_read(i2c_chan, SMB_REG_HS,  &st1);
+       eio_reg_read(i2c_chan, SMB_REG_HS2, &st2);
+
+       if (st1 & SMB_HS_FAILED) {
+               dev_err(eio_dev(i2c_chan), "HS FAILED\n");
+               ret = -EIO;
+       } else if (st1 & SMB_HS_ARL_ERR) {
+               dev_err(eio_dev(i2c_chan), "ARL FAILED\n");
+               ret = -EIO;
+       } else if (st2 & SMB_HS2_TO_ERR) {
+               dev_err(eio_dev(i2c_chan), "timeout\n");
+               ret = -ETIME;
+       } else if (st2 & SMB_HS2_NACK_ERR) {
+               dev_err(eio_dev(i2c_chan), "NACK err\n");
+               ret = -EIO;
+       } else if (st2 & SMB_HS2_PEC_ERR) {
+               dev_err(eio_dev(i2c_chan), "PEC err\n");
+               ret = -EIO;
+       }
+       if (ret)
+               goto exit;
+
+       switch (size) {
+       case I2C_SMBUS_QUICK:
+               dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_QUICK\n");
+               break;
+
+       case I2C_SMBUS_BYTE:
+       case I2C_SMBUS_BYTE_DATA:
+               if (is_read) {
+                       unsigned int v;
+
+                       dev_dbg(eio_dev(i2c_chan), 
"I2C_SMBUS_BYTE/I2C_SMBUS_BYTE_DATA\n");
+                       eio_reg_read(i2c_chan, SMB_REG_HD0, &v);
+                       data->block[0] = (u8)v;
+                       dev_dbg(eio_dev(i2c_chan), "read %X\n", data->block[0]);
+               }
+               break;
+
+       case I2C_SMBUS_WORD_DATA: {
+               unsigned int v0, v1;
+
+               if (is_read) {
+                       dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_WORD_DATA\n");
+                       eio_reg_read(i2c_chan, SMB_REG_HD0, &v0);
+                       eio_reg_read(i2c_chan, SMB_REG_HD1, &v1);
+                       data->block[0] = (u8)v0;
+                       data->block[1] = (u8)v1;
+               }
+               break;
+       }
+
+       case I2C_SMBUS_PROC_CALL: {
+               unsigned int v0, v1;
+
+               dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_PROC_CALL\n");
+               eio_reg_read(i2c_chan, SMB_REG_HD0, &v0);
+               eio_reg_read(i2c_chan, SMB_REG_HD1, &v1);
+               data->block[0] = (u8)v0;
+               data->block[1] = (u8)v1;
+               break;
+       }
+
+       case I2C_SMBUS_BLOCK_DATA:
+               if (!is_read)
+                       break;
+
+               dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_BLOCK_DATA\n");
+               eio_reg_read(i2c_chan, SMB_REG_HD0, (unsigned int *)&len);
+               len = min(len, I2C_SMBUS_BLOCK_MAX);
+               data->block[0] = len;
+
+               for (i = 1; i <= len; i++)
+                       eio_reg_read(i2c_chan, SMB_REG_HBLOCK,
+                                    (unsigned int *)&data->block[i]);
+               break;
+
+       default:
+               ret = -EINVAL;
+               goto exit;
+       }
+
+exit:
+       /* Clear latched status */
+       eio_reg_write(i2c_chan, SMB_REG_HS,  0xFF);
+       eio_reg_write(i2c_chan, SMB_REG_HS2, 0xFF);
+
+       mutex_unlock(&i2c_chan->lock);
+       return ret;
+}
+
+static int i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int nmsgs)
+{
+       int msg, data;
+       int addr = 0;
+       int dummy;
+       int ret = 0;
+       struct eio_i2c_chan *i2c_chan = i2c_get_adapdata(adap);
+
+       mutex_lock(&i2c_chan->lock);
+
+       ret = wait_ready(i2c_chan);
+       if (ret)
+               goto exit;
+
+       switch_i2c_mode(i2c_chan, true);
+
+       dev_dbg(eio_dev(i2c_chan), "Transmit %d I2C messages\n", nmsgs);
+       for (msg = 0; msg < nmsgs; msg++) {
+               int is_read = msgs[msg].flags & I2C_M_RD;
+               bool no_ack = msgs[msg].flags & I2C_M_IGNORE_NAK;
+
+               dev_dbg(eio_dev(i2c_chan), "message %d len=%d\n", msg, 
msgs[msg].len);
+
+               if (!msgs[msg].len)
+                       let_stop(i2c_chan);
+
+               if (msgs[msg].flags & I2C_M_TEN) {
+                       addr = eio_enc_10bit_addr(msgs[msg].addr);
+                       addr |= is_read;
+                       dev_dbg(eio_dev(i2c_chan), "10-bit addr: %X\n", addr);
+
+                       ret = write_addr(i2c_chan, addr >> 8, no_ack);
+                       if (!ret)
+                               ret = write_data(i2c_chan, addr & 0x7F, no_ack);
+               } else {
+                       addr = eio_enc_7bit_addr(msgs[msg].addr);
+                       addr |= is_read;
+                       dev_dbg(eio_dev(i2c_chan), "7-bit addr: %X\n", addr);
+
+                       ret = write_addr(i2c_chan, addr, no_ack);
+               }
+
+               if (ret)
+                       goto exit;
+
+               if (!msgs[msg].len)
+                       goto exit;
+
+               if (is_read)
+                       ret = eio_trigger_read(i2c_chan, (u32 *)&dummy);
+
+               /* Transmit all messages */
+               for (data = 0; data < msgs[msg].len; data++) {
+                       if (msgs[msg].flags & I2C_M_RD) {
+                               bool last = (msgs[msg].len == data + 1);
+
+                               if (last)
+                                       let_stop(i2c_chan);
+
+                               ret = read_data(i2c_chan, &msgs[msg].buf[data]);
+                               dev_dbg(eio_dev(i2c_chan), "I2C read[%d] = 
%x\n",
+                                       data, msgs[msg].buf[data]);
+
+                               /* Don't stop twice */
+                               if (last && ret == 0)
+                                       goto exit;
+                       } else {
+                               ret = write_data(i2c_chan, msgs[msg].buf[data], 
no_ack);
+                               dev_dbg(eio_dev(i2c_chan), "I2C write[%d] = 
%x\n",
+                                       data, msgs[msg].buf[data]);
+                       }
+                       if (ret)
+                               goto exit;
+               }
+       }
+
+       if (!ret)
+               ret = bus_stop(i2c_chan);
+
+       if (!ret)
+               goto exit;
+
+exit:
+       if (ret)
+               reset_bus(i2c_chan);
+
+       i2c_clear(i2c_chan);
+       clr_inuse(i2c_chan);
+
+       mutex_unlock(&i2c_chan->lock);
+       return ret ? ret : nmsgs;
+}
+
+static int smbus_xfer(struct i2c_adapter *adap, u16 addr,
+                     u16 flags, char is_read, u8 cmd,
+                     int size, union i2c_smbus_data *data)
+{
+       int ret;
+       struct eio_i2c_chan *i2c_chan = i2c_get_adapdata(adap);
+       int nmsgs = is_read ? 2 : 1;
+       u8 buf[I2C_SMBUS_BLOCK_MAX + sizeof(u32)] = { cmd, };
+       struct i2c_msg msgs[2] = {
+               { .addr = addr, .flags = flags & ~I2C_M_RD, .buf = buf + 0 },
+               { .addr = addr, .flags = flags |  I2C_M_RD, .buf = buf + 1 },
+       };
+
+       /* Non-I2C channels use the SMB engine, except I2C block variants we 
emulate */
+       if (!is_i2c(i2c_chan) && size != I2C_SMBUS_I2C_BLOCK_DATA)
+               return smb_access(i2c_chan, addr, is_read, cmd, size, data);
+
+       if (data) {
+               buf[0] = cmd;
+               /* FIX: preserve other flags; only toggle I2C_M_RD */
+               msgs[0].flags = is_read ? (flags | I2C_M_RD) : (flags & 
~I2C_M_RD);
+               msgs[1].buf   = data->block;
+       }
+
+       switch (size) {
+       case I2C_SMBUS_QUICK:
+               dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_QUICK on I2C\n");
+               nmsgs = 1;
+               break;
+
+       case I2C_SMBUS_BYTE:
+               dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_BYTE on I2C\n");
+               nmsgs = 1;
+               msgs[0].len   = 1;
+               msgs[0].buf   = is_read ? data->block : buf;
+               msgs[0].flags = is_read ? (flags | I2C_M_RD) : (flags & 
~I2C_M_RD);
+               break;
+
+       case I2C_SMBUS_BYTE_DATA:
+               dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_BYTE_DATA on I2C\n");
+               if (!data)
+                       return -EINVAL;
+               msgs[0].len = is_read ? 1 : 2;
+               buf[1]      = data->block[0];
+               msgs[1].len = 1;
+               break;
+
+       case I2C_SMBUS_WORD_DATA:
+               dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_WORD_DATA on I2C\n");
+               if (!data)
+                       return -EINVAL;
+               msgs[0].len = is_read ? 1 : 3;
+               msgs[1].len = 2;
+               buf[1]      = data->block[0];
+               buf[2]      = data->block[1];
+               msgs[1].buf = data->block;
+               break;
+
+       case I2C_SMBUS_I2C_BLOCK_DATA:
+       case I2C_SMBUS_I2C_BLOCK_BROKEN:
+               dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_I2C_BLOCK_(DATA/BROKEN) 
on I2C len=%d\n",
+                       data->block[0]);
+               if (!data)
+                       return -EINVAL;
+               msgs[0].len = is_read ? 1 : data->block[0] + 1;
+               msgs[1].len = data->block[0];
+               msgs[1].buf = data->block + 1;
+               if (msgs[0].len >= I2C_SMBUS_BLOCK_MAX ||
+                   msgs[1].len >= I2C_SMBUS_BLOCK_MAX)
+                       return -EINVAL;
+               if (!is_read)
+                       memcpy(buf + 1, data->block + 1, msgs[0].len);
+               break;
+
+       case I2C_SMBUS_PROC_CALL:
+               dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_PROC_CALL on I2C\n");
+               if (!data)
+                       return -EINVAL;
+               nmsgs = 2;
+               msgs[0].flags = flags & ~I2C_M_RD;
+               msgs[0].len   = 3;
+               buf[1]        = data->block[0];
+               buf[2]        = data->block[1];
+               msgs[1].len   = 2;
+               break;
+
+       case I2C_SMBUS_BLOCK_DATA:
+               dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_BLOCK_DATA on I2C not 
supported\n");
+               return -EINVAL;
+
+       case I2C_SMBUS_BLOCK_PROC_CALL:
+               dev_dbg(eio_dev(i2c_chan), "I2C_SMBUS_BLOCK_PROC_CALL on I2C 
not supported\n");
+               return -EINVAL;
+
+       default:
+               return -EINVAL;
+       }
+
+       ret = i2c_xfer(adap, msgs, nmsgs);
+       return ret < 0 ? ret : 0;
+}
+
+static int load_i2c(struct device *dev, enum eio_chan_id id,
+                   struct eio_i2c_chan *i2c_chan)
+{
+       u32 base_lo, base_hi, base;
+       int ldn = LDN_I2C0 + id;
+       struct eio_i2c_dev *eio_i2c = i2c_chan->parent;
+       struct regmap *map;
+
+       if (!eio_i2c || !eio_i2c->regmap)
+               return dev_err_probe(dev, -ENODEV, "missing parent/regmap\n");
+
+       map = eio_i2c->regmap;
+
+       /* Read channel I/O base via shared PNP window */
+       mutex_lock(&eio_i2c->pnp_mutex);
+       if (regmap_write(map, REG_PNP_INDEX, REG_EXT_MODE_ENTER) ||
+           regmap_write(map, REG_PNP_INDEX, REG_EXT_MODE_ENTER) ||
+           regmap_write(map, REG_PNP_INDEX, REG_LDN) ||
+           regmap_write(map, REG_PNP_DATA,  ldn) ||
+           regmap_write(map, REG_PNP_INDEX, REG_BASE_HI) ||
+           regmap_read(map,  REG_PNP_DATA,  &base_hi) ||
+           regmap_write(map, REG_PNP_INDEX, REG_BASE_LO) ||
+           regmap_read(map,  REG_PNP_DATA,  &base_lo) ||
+           regmap_write(map, REG_PNP_INDEX, REG_EXT_MODE_EXIT)) {
+               mutex_unlock(&eio_i2c->pnp_mutex);
+               dev_err(dev, "error read/write I2C[%d] IO port\n", id);
+               return -EIO;
+       }
+       mutex_unlock(&eio_i2c->pnp_mutex);
+
+       base = (base_hi << 8) | base_lo;
+       if (base == 0xFFFF || base == 0) {
+               dev_dbg(dev, "i2c[%d] base addr=%#x (not in-use)\n", id, base);
+               return -ENODEV;
+       }
+
+       dev_dbg(dev, "i2c[%d] base addr=%#x\n", id, base);
+
+       /* Bind channel (no per-chan dev) */
+       i2c_chan->base = (u16)base;
+       i2c_chan->id   = id;
+
+       /* Per-channel frequency policy */
+       if (i2c_chan->freq_override != USE_DEFAULT)
+               set_freq(i2c_chan, i2c_chan->freq_override);
+
+       get_freq(i2c_chan, &i2c_chan->freq_override);
+
+       return 0;
+}
+
+static u32 functionality(struct i2c_adapter *adap)
+{
+       struct eio_i2c_chan *i2c_chan = i2c_get_adapdata(adap);
+
+       return is_i2c(i2c_chan) ? SUPPORTED_I2C : SUPPORTED_SMB;
+}
+
+static const struct i2c_algorithm algo = {
+       .smbus_xfer     = smbus_xfer,
+       .master_xfer    = i2c_xfer,
+       .functionality  = functionality,
+};
+
+static int eio_i2c_probe(struct platform_device *pdev)
+{
+       static const char * const names[] = { "i2c0", "i2c1", "smb0", "smb1" };
+       struct device *dev = &pdev->dev;
+       struct eio_i2c_dev *eio_i2c;
+       struct eio_dev *eio_dev = dev_get_drvdata(dev->parent);
+       int ret = 0;
+       enum eio_chan_id ch;
+
+       if (!eio_dev) {
+               dev_err(dev, "Error contact eio_core\n");
+               return -ENODEV;
+       }
+
+       timeout = clamp_t(int, timeout, I2C_TIMEOUT / 100, I2C_TIMEOUT * 100);
+       dev_info(dev, "Timeout value %d\n", timeout);
+
+       eio_i2c = devm_kzalloc(dev, sizeof(*eio_i2c), GFP_KERNEL);
+       if (!eio_i2c)
+               return -ENOMEM;
+
+       eio_i2c->dev = dev;
+       eio_i2c->mfd = dev->parent;
+       eio_i2c->regmap = dev_get_regmap(dev->parent, NULL);
+       if (!eio_i2c->regmap)
+               return dev_err_probe(dev, -ENODEV, "parent regmap not found\n");
+
+       mutex_init(&eio_i2c->pnp_mutex);
+       platform_set_drvdata(pdev, eio_i2c);
+
+       for (ch = EIO_I2C0; ch < MAX_I2C_SMB; ch++) {
+               struct eio_i2c_chan *i2c_chan;
+
+               i2c_chan = devm_kzalloc(dev, sizeof(*i2c_chan), GFP_KERNEL);
+               if (!i2c_chan) {
+                       ret = -ENOMEM;
+                       break;
+               }
+
+               i2c_chan->parent = eio_i2c;
+               i2c_chan->freq_override = USE_DEFAULT;
+               mutex_init(&i2c_chan->lock);
+
+               if (load_i2c(dev, ch, i2c_chan)) {
+                       dev_info(dev, "No %s%d!\n", (ch < 2) ? "I2C" : "SMBus", 
ch & 1);
+                       continue;
+               }
+
+               i2c_chan->adap.owner      = THIS_MODULE;
+               i2c_chan->adap.class      = I2C_CLASS_HWMON;
+               i2c_chan->adap.algo       = &algo;
+               i2c_chan->adap.dev.parent = dev;
+               snprintf(i2c_chan->adap.name, sizeof(i2c_chan->adap.name), 
"eio-%s",
+                        names[ch]);
+
+               i2c_set_adapdata(&i2c_chan->adap, i2c_chan);
+
+               ret = i2c_add_adapter(&i2c_chan->adap);
+               dev_info(dev, "Add %s%d %s. %d\n", (ch < 2) ? "I2C" : "SMBus",
+                        ch, ret ? "Error" : "Success", ret);
+               if (ret)
+                       break;
+
+               eio_i2c->chan[ch] = i2c_chan;
+       }
+
+       if (ret) {
+               for (ch = EIO_I2C0; ch < MAX_I2C_SMB; ch++) {
+                       if (eio_i2c->chan[ch]) {
+                               i2c_del_adapter(&eio_i2c->chan[ch]->adap);
+                               eio_i2c->chan[ch] = NULL;
+                       }
+               }
+       }
+
+       return ret;
+}
+
+static void eio_i2c_remove(struct platform_device *pdev)
+{
+       struct eio_i2c_dev *eio_i2c = platform_get_drvdata(pdev);
+       enum eio_chan_id ch;
+
+       for (ch = EIO_I2C0; ch < MAX_I2C_SMB; ch++) {
+               if (eio_i2c->chan[ch]) {
+                       i2c_del_adapter(&eio_i2c->chan[ch]->adap);
+                       eio_i2c->chan[ch] = NULL;
+               }
+       }
+}
+
+static struct platform_driver eio_i2c_driver = {
+       .probe  = eio_i2c_probe,
+       .remove = eio_i2c_remove,
+       .driver = {
+               .name = "i2c_eio",
+       },
+};
+
+module_platform_driver(eio_i2c_driver);
+
+MODULE_AUTHOR("Wenkai Chung <[email protected]>");
+MODULE_AUTHOR("Ramiro Oliveira <[email protected]>");
+MODULE_DESCRIPTION("I2C driver for Advantech EIO embedded controller");
+MODULE_LICENSE("GPL");
+MODULE_SOFTDEP("pre: eio_core");

-- 
2.43.0


Reply via email to