From 2f7a23d91dbc2c1c0a29839d791b73eb516c0002 Mon Sep 17 00:00:00 2001
From: guowenxue <guowenxue@gmail.com>
Date: Wed, 17 Jul 2024 11:07:43 +0800
Subject: [PATCH]  Patch:IGKBoard-IMX8MP: Add MS1112 ADC driver

---
 kernel/patches/igkboard-imx8mp/linux-imx-lf-6.1.36-2.1.0.patch |  647 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 637 insertions(+), 10 deletions(-)

diff --git a/kernel/patches/igkboard-imx8mp/linux-imx-lf-6.1.36-2.1.0.patch b/kernel/patches/igkboard-imx8mp/linux-imx-lf-6.1.36-2.1.0.patch
index 2c92edf..9a930f9 100644
--- a/kernel/patches/igkboard-imx8mp/linux-imx-lf-6.1.36-2.1.0.patch
+++ b/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");

--
Gitblit v1.9.1