guowenxue
2024-07-17 2f7a23d91dbc2c1c0a29839d791b73eb516c0002
 Patch:IGKBoard-IMX8MP: Add MS1112 ADC driver

Signed-off-by: guowenxue <guowenxue@gmail.com>
1 files modified
647 ■■■■■ changed files
kernel/patches/igkboard-imx8mp/linux-imx-lf-6.1.36-2.1.0.patch 647 ●●●●● patch | view | raw | blame | history
kernel/patches/igkboard-imx8mp/linux-imx-lf-6.1.36-2.1.0.patch
@@ -10,10 +10,10 @@
+dtb-$(CONFIG_ARCH_MXC) += igkboard-imx8mp.dtb
diff --git a/arch/arm64/boot/dts/freescale/igkboard-imx8mp.dts b/arch/arm64/boot/dts/freescale/igkboard-imx8mp.dts
new file mode 100644
index 000000000..b0c36a3c0
index 000000000..8ce653b69
--- /dev/null
+++ b/arch/arm64/boot/dts/freescale/igkboard-imx8mp.dts
@@ -0,0 +1,690 @@
@@ -0,0 +1,712 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/* 
+ * Device Tree Source for LingYun IGKBoard(IoT Gateway Kits Board) - imx8mp
@@ -276,6 +276,28 @@
+        compatible = "isil,isl1208";
+        reg = <0x6f>;
+        status = "okay";
+    };
+
+    ms1112@4a {
+        compatible = "ms,ms1112";
+        reg = <0x4a>;
+        status = "okay";
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        channel@2 {
+                reg = <2>;
+                ti,gain = <0>;
+                ti,datarate = <3>;
+                ti,mode = <1>;
+        };
+
+        channel@3{
+                reg = <3>;
+                ti,gain = <0>;
+                ti,datarate = <3>;
+                ti,mode = <1>;
+        };
+    };
+};
+
@@ -706,10 +728,10 @@
+};
diff --git a/arch/arm64/configs/igkboard-imx8mp_defconfig b/arch/arm64/configs/igkboard-imx8mp_defconfig
new file mode 100644
index 000000000..d5bb6e7bf
index 000000000..b0f923742
--- /dev/null
+++ b/arch/arm64/configs/igkboard-imx8mp_defconfig
@@ -0,0 +1,1108 @@
@@ -0,0 +1,1103 @@
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_AUDIT=y
@@ -1630,11 +1652,7 @@
+CONFIG_IIO_ST_ACCEL_3AXIS=m
+CONFIG_IMX8QXP_ADC=y
+CONFIG_IMX93_ADC=y
+CONFIG_MAX9611=m
+CONFIG_QCOM_SPMI_VADC=m
+CONFIG_QCOM_SPMI_ADC5=m
+CONFIG_IIO_CROS_EC_SENSORS_CORE=m
+CONFIG_IIO_CROS_EC_SENSORS=m
+CONFIG_MS1112=y
+CONFIG_BMG160=m
+CONFIG_IIO_ST_GYRO_3AXIS=m
+CONFIG_MAX30100=m
@@ -1648,7 +1666,6 @@
+CONFIG_IIO_ST_LSM6DSX=y
+CONFIG_SENSORS_ISL29018=y
+CONFIG_IIO_ST_MAGN_3AXIS=m
+CONFIG_IIO_CROS_EC_BARO=m
+CONFIG_MPL3115=y
+CONFIG_MS5611=m
+CONFIG_MS5611_I2C=m
@@ -1818,3 +1835,613 @@
+CONFIG_CORESIGHT_CPU_DEBUG=m
+CONFIG_CORESIGHT_CTI=m
+CONFIG_MEMTEST=y
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 3946eb595..15278c13c 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -1164,6 +1164,18 @@ config TI_ADC161S626
       This driver can also be built as a module. If so, the module will be
       called ti-adc161s626.
+config MS1112
+    tristate "Ruimeng Technology MS1112 ADC"
+    depends on I2C
+    select IIO_BUFFER
+    select IIO_TRIGGERED_BUFFER
+    help
+      If you say yes here you get support for Ruimeng Technology ADS1015
+      ADC chip.
+
+      This driver can also be built as a module. If so, the module will be
+      called ms1112.
+
 config TI_ADS1015
     tristate "Texas Instruments ADS1015 ADC"
     depends on I2C
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 83233c38c..f403164cf 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -104,6 +104,7 @@ obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
 obj-$(CONFIG_TI_ADC108S102) += ti-adc108s102.o
 obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
 obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o
+obj-$(CONFIG_MS1112) += ms1112.o
 obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o
 obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o
 obj-$(CONFIG_TI_ADS8344) += ti-ads8344.o
diff --git a/drivers/iio/adc/ms1112.c b/drivers/iio/adc/ms1112.c
new file mode 100644
index 000000000..cf8ea5c66
--- /dev/null
+++ b/drivers/iio/adc/ms1112.c
@@ -0,0 +1,569 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MS1112 - Ruimeng Technology Analog-to-Digital Converter
+ *
+ * Copyright (c) 2024, LingYun IoT System Studio.
+ *
+ * IIO driver for MS1112 ADC 7-bit I2C slave address: 0x4A
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/of_gpio.h>
+#include <linux/semaphore.h>
+#include <linux/timer.h>
+#include <linux/i2c.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/driver.h>
+
+#define MS1112_DRV_NAME                "ms1112"
+
+#define MS1112_CONV_REG                0x00
+#define MS1112_CFG_REG                0x01
+#define MS1112_DEFAULT_CONFIG        0xFC
+
+#define MS1112_CHANNELS                4
+#define MS1112_CFG_DR_SHIFT            2
+#define MS1112_CFG_MOD_SHIFT        4
+#define MS1112_CFG_PGA_SHIFT        0
+#define MS1112_CFG_MUX_SHIFT        5
+
+#define MS1112_CFG_DR_MASK            GENMASK(3, 2)
+#define MS1112_CFG_MOD_MASK            BIT(4)
+#define MS1112_CFG_PGA_MASK            GENMASK(1, 0)
+#define MS1112_CFG_MUX_MASK            GENMASK(6, 5)
+
+#define MS1112_DEFAULT_PGA            0
+#define MS1112_DEFAULT_DATA_RATE    3
+#define MS1112_DEFAULT_CHAN            2
+#define MS1112_DEFAULT_MODE            1
+
+#define MS1112_CONTINUOUS            0
+#define MS1112_SINGLESHOT            1
+
+struct ms1112_chip_data {
+    struct iio_chan_spec const    *channels;
+    int                            num_channels;
+    const struct iio_info        *info;
+    const int                    *data_rate;
+    const int                    data_rate_len;
+    const int                    *scale;
+    const int                    scale_len;
+    bool                        has_comparator;
+};
+
+enum ms1112_channels {
+    MS1112_AIN0_AIN1 = 0,
+    MS1112_AIN2,
+    MS1112_AIN0,
+    MS1112_AIN1,
+    MS1112_TIMESTAMP,
+};
+
+static const int ms1112_data_rate[] = {
+    240,60,30,15
+};
+
+static const int ms1112_fullscale_range[] = {
+    2048
+};
+
+static const int ms1112_scale[] = { /* 12bit ADC */
+    2048,11,
+    2048,13,
+    2048,14,
+    2048,15
+};
+
+#define FIT_CHECK(_testbits, _fitbits)                \
+    (                                                \
+        (_fitbits) *                                \
+        !!sizeof(struct {                            \
+        static_assert((_testbits) <= (_fitbits));    \
+        int pad;                                    \
+        })                                            \
+    )
+
+#define MS1112_V_CHAN(_chan, _addr, _realbits, _shift, _event_spec, _num_event_specs) { \
+    .type = IIO_VOLTAGE,                \
+    .indexed = 1,                        \
+    .address = _addr,                    \
+    .channel = _chan,                    \
+    .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |    \
+    BIT(IIO_CHAN_INFO_SCALE) |                        \
+    BIT(IIO_CHAN_INFO_SAMP_FREQ),                    \
+    .info_mask_shared_by_all_available =            \
+    BIT(IIO_CHAN_INFO_SCALE) |                        \
+    BIT(IIO_CHAN_INFO_SAMP_FREQ),                    \
+    .scan_index = _addr,                            \
+    .scan_type = {                                    \
+        .sign = 's',                                \
+        .realbits = (_realbits),                    \
+        .storagebits = FIT_CHECK((_realbits) + (_shift), 16),    \
+        .shift = (_shift),                            \
+        .endianness = IIO_CPU,                        \
+    },                                                \
+    .event_spec = (_event_spec),                    \
+    .num_event_specs = (_num_event_specs),            \
+    .datasheet_name = "AIN"#_chan,                    \
+}
+
+#define MS1112_V_DIFF_CHAN(_chan, _chan2, _addr, _realbits, _shift, _event_spec, _num_event_specs) { \
+    .type = IIO_VOLTAGE,                            \
+    .differential = 1,                                \
+    .indexed = 1,                                    \
+    .address = _addr,                                \
+    .channel = _chan,                                \
+    .channel2 = _chan2,                                \
+    .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |    \
+    BIT(IIO_CHAN_INFO_SCALE) |                        \
+    BIT(IIO_CHAN_INFO_SAMP_FREQ),                    \
+    .info_mask_shared_by_all_available =            \
+    BIT(IIO_CHAN_INFO_SCALE) |                        \
+    BIT(IIO_CHAN_INFO_SAMP_FREQ),                    \
+    .scan_index = _addr,                            \
+    .scan_type = {                                    \
+        .sign = 's',                                \
+        .realbits = (_realbits),                    \
+        .storagebits = FIT_CHECK((_realbits) + (_shift), 16),    \
+        .shift = (_shift),                            \
+        .endianness = IIO_CPU,                        \
+    },                                                \
+    .event_spec = (_event_spec),                    \
+    .num_event_specs = (_num_event_specs),            \
+    .datasheet_name = "AIN"#_chan"-AIN"#_chan2,        \
+}
+
+struct ms1112_channel_data {
+    bool enabled;
+    unsigned int pga;
+    unsigned int data_rate;
+    unsigned int mode;
+};
+
+struct ms1112_thresh_data {
+    int high_thresh;
+    int low_thresh;
+};
+
+struct ms1112_data {
+    struct ms1112_channel_data channel_data[MS1112_CHANNELS];
+    struct ms1112_thresh_data thresh_data[MS1112_CHANNELS];
+    const struct ms1112_chip_data *chip;
+    struct mutex lock;
+    void *private_data;
+    struct i2c_client *client;
+};
+
+/* MS1112 don't use the register address */
+static int ms1112_read_regs(struct ms1112_data *dev, uint8_t reg, void *buf, uint8_t size)
+{
+    int                    ret = 0;
+    struct i2c_msg        msg[1];
+    struct i2c_client    *client = dev->client;
+
+    msg[0].addr  = client->addr;
+    msg[0].flags = I2C_M_RD;
+    msg[0].buf     = buf;
+    msg[0].len     = size;
+
+    ret = i2c_transfer(client->adapter, msg, 1);
+    if(ret != 1) {
+        dev_err(&dev->client->dev, "%s() i2c_transfer error, ret=%d\n", __func__, ret);
+        ret = -EREMOTEIO;
+    }
+
+    return ret;
+}
+
+/* MS1112 don't use the register address */
+static s32 ms1112_write_regs(struct ms1112_data *dev, uint8_t reg, uint8_t *data, uint8_t bytes)
+{
+    int                    ret = 0;
+    struct i2c_msg        msg;
+    struct i2c_client    *client = dev->client;
+
+    msg.addr  = client->addr;
+    msg.flags = 0;
+    msg.buf   = data;
+    msg.len   = bytes;
+
+    ret = i2c_transfer(client->adapter, &msg, 1);
+    if(ret != 1) {
+        dev_err(&dev->client->dev, "%s() i2c_transfer error, ret=%d\n", __func__, ret);
+        ret = -EREMOTEIO;
+    }
+
+    return ret;
+}
+
+static int ms1112_readdata(struct ms1112_data *dev,unsigned int *val)
+{
+    unsigned char    buf[3];
+    unsigned char    rx_data[3];
+    int                rv = 0;
+
+    rv = ms1112_read_regs(dev, MS1112_CONV_REG, rx_data, 3);
+    if(rv<0) {
+        return rv;
+    }
+
+    buf[0] = rx_data[0];
+    buf[1] = rx_data[1];
+    buf[2] = rx_data[2];
+
+    *val = (buf[0]<<8) | buf[1];
+    return rv;
+}
+
+static int ms1112_get_adc_result(struct ms1112_data *data, int chan, int *val)
+{
+    int            ret = 0;
+    int            pga, dr , mode;
+    uint8_t        mask, cfg;
+
+    if (chan < 0 || chan >= MS1112_CHANNELS)
+        return -EINVAL;
+
+    mode = data->channel_data[chan].mode;
+    pga = data->channel_data[chan].pga;
+    dr = data->channel_data[chan].data_rate;
+
+    mask = MS1112_CFG_MUX_MASK | MS1112_CFG_PGA_MASK |
+        MS1112_CFG_DR_MASK | MS1112_CFG_MOD_MASK | MS1112_SINGLESHOT << 7;
+
+    cfg = chan << MS1112_CFG_MUX_SHIFT | pga << MS1112_CFG_PGA_SHIFT |
+        dr << MS1112_CFG_DR_SHIFT | mode << MS1112_CFG_MOD_SHIFT | MS1112_SINGLESHOT << 7;
+
+    cfg = (cfg & mask);
+
+    ms1112_write_regs(data, MS1112_CFG_REG, &cfg, 1);
+
+    ret = ms1112_readdata(data,val);
+    return ret;
+}
+
+static int ms1112_set_scale(struct ms1112_data *data, struct iio_chan_spec const *chan,
+        int scale, int uscale)
+{
+    int i;
+    int fullscale = div_s64((scale * 1000000LL + uscale) <<
+            (chan->scan_type.realbits - 1), 1000000);
+
+    for (i = 0; i < ARRAY_SIZE(ms1112_fullscale_range); i++) {
+        if (ms1112_fullscale_range[i] == fullscale) {
+            data->channel_data[chan->address].pga = i;
+            return 0;
+        }
+    }
+
+    return -EINVAL;
+}
+
+static int ms1112_set_data_rate(struct ms1112_data *data, int chan, int rate)
+{
+    int i;
+
+    for (i = 0; i < data->chip->data_rate_len; i++) {
+        if (data->chip->data_rate[i] == rate) {
+            data->channel_data[chan].data_rate = i;
+            return 0;
+        }
+    }
+
+    return -EINVAL;
+}
+
+static int ms1112_read_avail(struct iio_dev *indio_dev,
+        struct iio_chan_spec const *chan,
+        const int **vals, int *type, int *length,
+        long mask)
+{
+    struct ms1112_data *data = iio_priv(indio_dev);
+
+    if (chan->type != IIO_VOLTAGE)
+        return -EINVAL;
+
+    switch (mask) {
+        case IIO_CHAN_INFO_SCALE:
+            *type = IIO_VAL_FRACTIONAL_LOG2;
+            *vals =  data->chip->scale;
+            *length = data->chip->scale_len;
+            return IIO_AVAIL_LIST;
+        case IIO_CHAN_INFO_SAMP_FREQ:
+            *type = IIO_VAL_INT;
+            *vals = data->chip->data_rate;
+            *length = data->chip->data_rate_len;
+            return IIO_AVAIL_LIST;
+        default:
+            return -EINVAL;
+    }
+}
+
+static int ms1112_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask)
+{
+    int ret, idx;
+    struct ms1112_data *data = iio_priv(indio_dev);
+
+    mutex_lock(&data->lock);
+    switch (mask) {
+        case IIO_CHAN_INFO_RAW:
+
+            ret = iio_device_claim_direct_mode(indio_dev);
+            if (ret)
+                break;
+
+            ret = ms1112_get_adc_result(data, chan->address, val);
+            if (ret < 0) {
+                goto release_direct;
+            }
+
+            *val = sign_extend32(*val >> chan->scan_type.shift,
+                    chan->scan_type.realbits - 1);
+
+            ret = IIO_VAL_INT;
+release_direct:
+            iio_device_release_direct_mode(indio_dev);
+            break;
+
+        case IIO_CHAN_INFO_SCALE:
+            idx = data->channel_data[chan->address].pga;
+            *val = ms1112_fullscale_range[idx];
+            *val2 = chan->scan_type.realbits - 1;
+            ret = IIO_VAL_FRACTIONAL_LOG2;
+            break;
+        case IIO_CHAN_INFO_SAMP_FREQ:
+            idx = data->channel_data[chan->address].data_rate;
+            *val = data->chip->data_rate[idx];
+            ret = IIO_VAL_INT;
+            break;
+        default:
+            ret = -EINVAL;
+            break;
+    }
+    mutex_unlock(&data->lock);
+
+    return ret;
+}
+
+static int ms1112_write_raw(struct iio_dev *indio_dev,
+        struct iio_chan_spec const *chan, int val,
+        int val2, long mask)
+{
+    struct ms1112_data *data = iio_priv(indio_dev);
+    int ret;
+
+    mutex_lock(&data->lock);
+    switch (mask) {
+        case IIO_CHAN_INFO_SCALE:
+            ret = ms1112_set_scale(data, chan, val, val2);
+            break;
+        case IIO_CHAN_INFO_SAMP_FREQ:
+            ret = ms1112_set_data_rate(data, chan->address, val);
+            break;
+        default:
+            ret = -EINVAL;
+            break;
+    }
+    mutex_unlock(&data->lock);
+
+    return ret;
+}
+
+static const struct iio_info ms1112_info = {
+    .read_raw = ms1112_read_raw,
+    .write_raw = ms1112_write_raw,
+    .read_avail = ms1112_read_avail,
+};
+
+
+static const struct iio_chan_spec ms1112_channels[] = {
+    MS1112_V_DIFF_CHAN(0, 1, MS1112_AIN0_AIN1, 16, 0, NULL, 0),
+    MS1112_V_CHAN(2, MS1112_AIN2, 16, 0, NULL, 0),
+    MS1112_V_CHAN(0, MS1112_AIN0, 16, 0, NULL, 0),
+    MS1112_V_CHAN(1, MS1112_AIN1, 16, 0, NULL, 0),
+    IIO_CHAN_SOFT_TIMESTAMP(MS1112_TIMESTAMP),
+};
+
+static int ms1112_client_get_channels_config(struct i2c_client *client)
+{
+    struct iio_dev *indio_dev = i2c_get_clientdata(client);
+    struct ms1112_data *data = iio_priv(indio_dev);
+    struct device *dev = &client->dev;
+    struct fwnode_handle *node;
+    int i = -1;
+
+    device_for_each_child_node(dev, node) {
+        u32 pval;
+        unsigned int channel;
+        unsigned int pga = MS1112_DEFAULT_PGA;
+        unsigned int data_rate = MS1112_DEFAULT_DATA_RATE;
+        unsigned int mode = MS1112_DEFAULT_MODE;
+
+        if (fwnode_property_read_u32(node, "reg", &pval)) {
+            dev_err(dev, "invalid reg on %pfw\n", node);
+            continue;
+        }
+
+        channel = pval;
+        if (channel >= MS1112_CHANNELS) {
+            dev_err(dev, "invalid channel index %d on %pfw\n",
+                    channel, node);
+            continue;
+        }
+
+        if (!fwnode_property_read_u32(node, "ti,gain", &pval)) {
+            pga = pval;
+            if (pga > 3 ) {
+                dev_err(dev, "invalid gain on %pfw\n", node);
+                fwnode_handle_put(node);
+                return -EINVAL;
+            }
+        }
+
+        if (!fwnode_property_read_u32(node, "ti,datarate", &pval)) {
+            data_rate = pval;
+            if (data_rate > 3) {
+                dev_err(dev, "invalid data_rate on %pfw\n", node);
+                fwnode_handle_put(node);
+                return -EINVAL;
+            }
+        }
+
+        if (!fwnode_property_read_u32(node, "ti,mode", &pval)) {
+            mode = pval;
+            if (mode > 1) {
+                dev_err(dev, "invalid mode on %pfw\n", node);
+                fwnode_handle_put(node);
+                return -EINVAL;
+            }
+        }
+
+
+        data->channel_data[channel].pga = pga;
+        data->channel_data[channel].data_rate = data_rate;
+        data->channel_data[channel].mode = mode;
+        i++;
+    }
+
+    return i < 0 ? -EINVAL : 0;
+}
+
+static void ms1112_get_channels_config(struct i2c_client *client)
+{
+    unsigned int k;
+
+    struct iio_dev *indio_dev = i2c_get_clientdata(client);
+    struct ms1112_data *data = iio_priv(indio_dev);
+
+    if (!ms1112_client_get_channels_config(client))
+        return;
+
+    /* fallback on default configuration */
+    for (k = 0; k < MS1112_CHANNELS; ++k) {
+        data->channel_data[k].pga = MS1112_DEFAULT_PGA;
+        data->channel_data[k].data_rate = MS1112_DEFAULT_DATA_RATE;
+        data->channel_data[k].mode = MS1112_DEFAULT_MODE;
+    }
+}
+
+static int ms1112_probe(struct i2c_client *client,const struct i2c_device_id *id)
+{
+    struct iio_dev *indio_dev;
+    const struct ms1112_chip_data *chip;
+    struct ms1112_data *data;
+    int ret;
+    int i;
+
+    chip = device_get_match_data(&client->dev);
+    if (!chip)
+        chip = (const struct ms1112_chip_data *)id->driver_data;
+    if (!chip)
+        return dev_err_probe(&client->dev, -EINVAL, "Unknown chip\n");
+
+    indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*indio_dev));
+    if (!indio_dev)
+        return -ENOMEM;
+
+    data = iio_priv(indio_dev);
+    i2c_set_clientdata(client, indio_dev);
+
+    mutex_init(&data->lock);
+
+    indio_dev->name = MS1112_DRV_NAME;
+    indio_dev->info = chip->info;
+    indio_dev->modes = INDIO_DIRECT_MODE;
+    indio_dev->channels = chip->channels;
+    indio_dev->num_channels = chip->num_channels;
+    data->chip = chip;
+    data->client = client;
+
+    for (i = 0; i < MS1112_CHANNELS; i++) {
+        int realbits = indio_dev->channels[i].scan_type.realbits;
+
+        data->thresh_data[i].low_thresh = -1 << (realbits - 1);
+        data->thresh_data[i].high_thresh = (1 << (realbits - 1)) - 1;
+    }
+
+    /* we need to keep this ABI the same as used by hwmon ADS1015 driver */
+    ms1112_get_channels_config(client);
+
+    ret = iio_device_register(indio_dev);
+    if (ret)
+        dev_err(&client->dev, "Failed to register IIO device\n");
+    return ret;
+}
+
+static void ms1112_remove(struct i2c_client *client)
+{
+    struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+    iio_device_unregister(indio_dev);
+
+}
+
+static const struct ms1112_chip_data ms1112_data = {
+    .channels    = ms1112_channels,
+    .num_channels    = ARRAY_SIZE(ms1112_channels),
+    .info        = &ms1112_info,
+    .data_rate    = ms1112_data_rate,
+    .data_rate_len    = ARRAY_SIZE(ms1112_data_rate),
+    .scale        = ms1112_scale,
+    .scale_len    = ARRAY_SIZE(ms1112_scale),
+    .has_comparator = false,
+};
+
+static const struct i2c_device_id ms1112_id[] = {
+    { "ms1112", (kernel_ulong_t)&ms1112_data },
+    {}
+};
+MODULE_DEVICE_TABLE(i2c, ms1112_id);
+
+static const struct of_device_id ms1112_of_match[] = {
+    { .compatible = "ms,ms1112" },
+    { },
+};
+MODULE_DEVICE_TABLE(of, ms1112_of_match);
+
+static struct i2c_driver ms1112_driver = {
+    .driver = {
+        .owner = THIS_MODULE,
+        .name = "ms1112",
+        .of_match_table = ms1112_of_match,
+    },
+    .probe = ms1112_probe,
+    .remove = ms1112_remove,
+    .id_table    = ms1112_id,
+};
+
+module_i2c_driver(ms1112_driver);
+
+MODULE_AUTHOR("Tang Junfeng");
+MODULE_DESCRIPTION("MS1112 IIO ADC Driver");
+MODULE_LICENSE("GPL");