// SPDX-License-Identifier: GPL-2.0+
|
/*
|
* AD7150 capacitive sensor driver supporting AD7150/1/6
|
*
|
* Copyright 2010-2011 Analog Devices Inc.
|
*/
|
|
#include <linux/bitfield.h>
|
#include <linux/interrupt.h>
|
#include <linux/device.h>
|
#include <linux/kernel.h>
|
#include <linux/slab.h>
|
#include <linux/i2c.h>
|
#include <linux/module.h>
|
|
#include <linux/iio/iio.h>
|
#include <linux/iio/sysfs.h>
|
#include <linux/iio/events.h>
|
/*
|
* AD7150 registers definition
|
*/
|
|
#define AD7150_STATUS 0
|
#define AD7150_STATUS_OUT1 BIT(3)
|
#define AD7150_STATUS_OUT2 BIT(5)
|
#define AD7150_CH1_DATA_HIGH 1
|
#define AD7150_CH2_DATA_HIGH 3
|
#define AD7150_CH1_AVG_HIGH 5
|
#define AD7150_CH2_AVG_HIGH 7
|
#define AD7150_CH1_SENSITIVITY 9
|
#define AD7150_CH1_THR_HOLD_H 9
|
#define AD7150_CH1_TIMEOUT 10
|
#define AD7150_CH1_SETUP 11
|
#define AD7150_CH2_SENSITIVITY 12
|
#define AD7150_CH2_THR_HOLD_H 12
|
#define AD7150_CH2_TIMEOUT 13
|
#define AD7150_CH2_SETUP 14
|
#define AD7150_CFG 15
|
#define AD7150_CFG_FIX BIT(7)
|
#define AD7150_PD_TIMER 16
|
#define AD7150_CH1_CAPDAC 17
|
#define AD7150_CH2_CAPDAC 18
|
#define AD7150_SN3 19
|
#define AD7150_SN2 20
|
#define AD7150_SN1 21
|
#define AD7150_SN0 22
|
#define AD7150_ID 23
|
|
/* AD7150 masks */
|
#define AD7150_THRESHTYPE_MSK GENMASK(6, 5)
|
|
/**
|
* struct ad7150_chip_info - instance specific chip data
|
* @client: i2c client for this device
|
* @current_event: device always has one type of event enabled.
|
* This element stores the event code of the current one.
|
* @threshold: thresholds for simple capacitance value events
|
* @thresh_sensitivity: threshold for simple capacitance offset
|
* from 'average' value.
|
* @mag_sensitity: threshold for magnitude of capacitance offset from
|
* from 'average' value.
|
* @thresh_timeout: a timeout, in samples from the moment an
|
* adaptive threshold event occurs to when the average
|
* value jumps to current value.
|
* @mag_timeout: a timeout, in sample from the moment an
|
* adaptive magnitude event occurs to when the average
|
* value jumps to the current value.
|
* @old_state: store state from previous event, allowing confirmation
|
* of new condition.
|
* @conversion_mode: the current conversion mode.
|
* @state_lock: ensure consistent state of this structure wrt the
|
* hardware.
|
*/
|
struct ad7150_chip_info {
|
struct i2c_client *client;
|
u64 current_event;
|
u16 threshold[2][2];
|
u8 thresh_sensitivity[2][2];
|
u8 mag_sensitivity[2][2];
|
u8 thresh_timeout[2][2];
|
u8 mag_timeout[2][2];
|
int old_state;
|
char *conversion_mode;
|
struct mutex state_lock;
|
};
|
|
/*
|
* sysfs nodes
|
*/
|
|
static const u8 ad7150_addresses[][6] = {
|
{ AD7150_CH1_DATA_HIGH, AD7150_CH1_AVG_HIGH,
|
AD7150_CH1_SETUP, AD7150_CH1_THR_HOLD_H,
|
AD7150_CH1_SENSITIVITY, AD7150_CH1_TIMEOUT },
|
{ AD7150_CH2_DATA_HIGH, AD7150_CH2_AVG_HIGH,
|
AD7150_CH2_SETUP, AD7150_CH2_THR_HOLD_H,
|
AD7150_CH2_SENSITIVITY, AD7150_CH2_TIMEOUT },
|
};
|
|
static int ad7150_read_raw(struct iio_dev *indio_dev,
|
struct iio_chan_spec const *chan,
|
int *val,
|
int *val2,
|
long mask)
|
{
|
int ret;
|
struct ad7150_chip_info *chip = iio_priv(indio_dev);
|
int channel = chan->channel;
|
|
switch (mask) {
|
case IIO_CHAN_INFO_RAW:
|
ret = i2c_smbus_read_word_data(chip->client,
|
ad7150_addresses[channel][0]);
|
if (ret < 0)
|
return ret;
|
*val = swab16(ret);
|
return IIO_VAL_INT;
|
case IIO_CHAN_INFO_AVERAGE_RAW:
|
ret = i2c_smbus_read_word_data(chip->client,
|
ad7150_addresses[channel][1]);
|
if (ret < 0)
|
return ret;
|
*val = swab16(ret);
|
return IIO_VAL_INT;
|
default:
|
return -EINVAL;
|
}
|
}
|
|
static int ad7150_read_event_config(struct iio_dev *indio_dev,
|
const struct iio_chan_spec *chan,
|
enum iio_event_type type,
|
enum iio_event_direction dir)
|
{
|
int ret;
|
u8 threshtype;
|
bool thrfixed;
|
struct ad7150_chip_info *chip = iio_priv(indio_dev);
|
|
ret = i2c_smbus_read_byte_data(chip->client, AD7150_CFG);
|
if (ret < 0)
|
return ret;
|
|
threshtype = FIELD_GET(AD7150_THRESHTYPE_MSK, ret);
|
|
/*check if threshold mode is fixed or adaptive*/
|
thrfixed = FIELD_GET(AD7150_CFG_FIX, ret);
|
|
switch (type) {
|
case IIO_EV_TYPE_MAG_ADAPTIVE:
|
if (dir == IIO_EV_DIR_RISING)
|
return !thrfixed && (threshtype == 0x1);
|
return !thrfixed && (threshtype == 0x0);
|
case IIO_EV_TYPE_THRESH_ADAPTIVE:
|
if (dir == IIO_EV_DIR_RISING)
|
return !thrfixed && (threshtype == 0x3);
|
return !thrfixed && (threshtype == 0x2);
|
case IIO_EV_TYPE_THRESH:
|
if (dir == IIO_EV_DIR_RISING)
|
return thrfixed && (threshtype == 0x1);
|
return thrfixed && (threshtype == 0x0);
|
default:
|
break;
|
}
|
return -EINVAL;
|
}
|
|
/* state_lock should be held to ensure consistent state*/
|
|
static int ad7150_write_event_params(struct iio_dev *indio_dev,
|
unsigned int chan,
|
enum iio_event_type type,
|
enum iio_event_direction dir)
|
{
|
int ret;
|
u16 value;
|
u8 sens, timeout;
|
struct ad7150_chip_info *chip = iio_priv(indio_dev);
|
int rising = (dir == IIO_EV_DIR_RISING);
|
u64 event_code;
|
|
event_code = IIO_UNMOD_EVENT_CODE(IIO_CAPACITANCE, chan, type, dir);
|
|
if (event_code != chip->current_event)
|
return 0;
|
|
switch (type) {
|
/* Note completely different from the adaptive versions */
|
case IIO_EV_TYPE_THRESH:
|
value = chip->threshold[rising][chan];
|
return i2c_smbus_write_word_data(chip->client,
|
ad7150_addresses[chan][3],
|
swab16(value));
|
case IIO_EV_TYPE_MAG_ADAPTIVE:
|
sens = chip->mag_sensitivity[rising][chan];
|
timeout = chip->mag_timeout[rising][chan];
|
break;
|
case IIO_EV_TYPE_THRESH_ADAPTIVE:
|
sens = chip->thresh_sensitivity[rising][chan];
|
timeout = chip->thresh_timeout[rising][chan];
|
break;
|
default:
|
return -EINVAL;
|
}
|
ret = i2c_smbus_write_byte_data(chip->client,
|
ad7150_addresses[chan][4],
|
sens);
|
if (ret)
|
return ret;
|
return i2c_smbus_write_byte_data(chip->client,
|
ad7150_addresses[chan][5],
|
timeout);
|
}
|
|
static int ad7150_write_event_config(struct iio_dev *indio_dev,
|
const struct iio_chan_spec *chan,
|
enum iio_event_type type,
|
enum iio_event_direction dir, int state)
|
{
|
u8 thresh_type, cfg, adaptive;
|
int ret;
|
struct ad7150_chip_info *chip = iio_priv(indio_dev);
|
int rising = (dir == IIO_EV_DIR_RISING);
|
u64 event_code;
|
|
/* Something must always be turned on */
|
if (!state)
|
return -EINVAL;
|
|
event_code = IIO_UNMOD_EVENT_CODE(chan->type, chan->channel, type, dir);
|
if (event_code == chip->current_event)
|
return 0;
|
mutex_lock(&chip->state_lock);
|
ret = i2c_smbus_read_byte_data(chip->client, AD7150_CFG);
|
if (ret < 0)
|
goto error_ret;
|
|
cfg = ret & ~((0x03 << 5) | BIT(7));
|
|
switch (type) {
|
case IIO_EV_TYPE_MAG_ADAPTIVE:
|
adaptive = 1;
|
if (rising)
|
thresh_type = 0x1;
|
else
|
thresh_type = 0x0;
|
break;
|
case IIO_EV_TYPE_THRESH_ADAPTIVE:
|
adaptive = 1;
|
if (rising)
|
thresh_type = 0x3;
|
else
|
thresh_type = 0x2;
|
break;
|
case IIO_EV_TYPE_THRESH:
|
adaptive = 0;
|
if (rising)
|
thresh_type = 0x1;
|
else
|
thresh_type = 0x0;
|
break;
|
default:
|
ret = -EINVAL;
|
goto error_ret;
|
}
|
|
cfg |= (!adaptive << 7) | (thresh_type << 5);
|
|
ret = i2c_smbus_write_byte_data(chip->client, AD7150_CFG, cfg);
|
if (ret < 0)
|
goto error_ret;
|
|
chip->current_event = event_code;
|
|
/* update control attributes */
|
ret = ad7150_write_event_params(indio_dev, chan->channel, type, dir);
|
error_ret:
|
mutex_unlock(&chip->state_lock);
|
|
return ret;
|
}
|
|
static int ad7150_read_event_value(struct iio_dev *indio_dev,
|
const struct iio_chan_spec *chan,
|
enum iio_event_type type,
|
enum iio_event_direction dir,
|
enum iio_event_info info,
|
int *val, int *val2)
|
{
|
struct ad7150_chip_info *chip = iio_priv(indio_dev);
|
int rising = (dir == IIO_EV_DIR_RISING);
|
|
/* Complex register sharing going on here */
|
switch (type) {
|
case IIO_EV_TYPE_MAG_ADAPTIVE:
|
*val = chip->mag_sensitivity[rising][chan->channel];
|
return IIO_VAL_INT;
|
case IIO_EV_TYPE_THRESH_ADAPTIVE:
|
*val = chip->thresh_sensitivity[rising][chan->channel];
|
return IIO_VAL_INT;
|
case IIO_EV_TYPE_THRESH:
|
*val = chip->threshold[rising][chan->channel];
|
return IIO_VAL_INT;
|
default:
|
return -EINVAL;
|
}
|
}
|
|
static int ad7150_write_event_value(struct iio_dev *indio_dev,
|
const struct iio_chan_spec *chan,
|
enum iio_event_type type,
|
enum iio_event_direction dir,
|
enum iio_event_info info,
|
int val, int val2)
|
{
|
int ret;
|
struct ad7150_chip_info *chip = iio_priv(indio_dev);
|
int rising = (dir == IIO_EV_DIR_RISING);
|
|
mutex_lock(&chip->state_lock);
|
switch (type) {
|
case IIO_EV_TYPE_MAG_ADAPTIVE:
|
chip->mag_sensitivity[rising][chan->channel] = val;
|
break;
|
case IIO_EV_TYPE_THRESH_ADAPTIVE:
|
chip->thresh_sensitivity[rising][chan->channel] = val;
|
break;
|
case IIO_EV_TYPE_THRESH:
|
chip->threshold[rising][chan->channel] = val;
|
break;
|
default:
|
ret = -EINVAL;
|
goto error_ret;
|
}
|
|
/* write back if active */
|
ret = ad7150_write_event_params(indio_dev, chan->channel, type, dir);
|
|
error_ret:
|
mutex_unlock(&chip->state_lock);
|
return ret;
|
}
|
|
static ssize_t ad7150_show_timeout(struct device *dev,
|
struct device_attribute *attr,
|
char *buf)
|
{
|
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
struct ad7150_chip_info *chip = iio_priv(indio_dev);
|
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
u8 value;
|
|
/* use the event code for consistency reasons */
|
int chan = IIO_EVENT_CODE_EXTRACT_CHAN(this_attr->address);
|
int rising = (IIO_EVENT_CODE_EXTRACT_DIR(this_attr->address)
|
== IIO_EV_DIR_RISING) ? 1 : 0;
|
|
switch (IIO_EVENT_CODE_EXTRACT_TYPE(this_attr->address)) {
|
case IIO_EV_TYPE_MAG_ADAPTIVE:
|
value = chip->mag_timeout[rising][chan];
|
break;
|
case IIO_EV_TYPE_THRESH_ADAPTIVE:
|
value = chip->thresh_timeout[rising][chan];
|
break;
|
default:
|
return -EINVAL;
|
}
|
|
return sprintf(buf, "%d\n", value);
|
}
|
|
static ssize_t ad7150_store_timeout(struct device *dev,
|
struct device_attribute *attr,
|
const char *buf,
|
size_t len)
|
{
|
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
struct ad7150_chip_info *chip = iio_priv(indio_dev);
|
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
int chan = IIO_EVENT_CODE_EXTRACT_CHAN(this_attr->address);
|
enum iio_event_direction dir;
|
enum iio_event_type type;
|
int rising;
|
u8 data;
|
int ret;
|
|
type = IIO_EVENT_CODE_EXTRACT_TYPE(this_attr->address);
|
dir = IIO_EVENT_CODE_EXTRACT_DIR(this_attr->address);
|
rising = (dir == IIO_EV_DIR_RISING);
|
|
ret = kstrtou8(buf, 10, &data);
|
if (ret < 0)
|
return ret;
|
|
mutex_lock(&chip->state_lock);
|
switch (type) {
|
case IIO_EV_TYPE_MAG_ADAPTIVE:
|
chip->mag_timeout[rising][chan] = data;
|
break;
|
case IIO_EV_TYPE_THRESH_ADAPTIVE:
|
chip->thresh_timeout[rising][chan] = data;
|
break;
|
default:
|
ret = -EINVAL;
|
goto error_ret;
|
}
|
|
ret = ad7150_write_event_params(indio_dev, chan, type, dir);
|
error_ret:
|
mutex_unlock(&chip->state_lock);
|
|
if (ret < 0)
|
return ret;
|
|
return len;
|
}
|
|
#define AD7150_TIMEOUT(chan, type, dir, ev_type, ev_dir) \
|
IIO_DEVICE_ATTR(in_capacitance##chan##_##type##_##dir##_timeout, \
|
0644, \
|
&ad7150_show_timeout, \
|
&ad7150_store_timeout, \
|
IIO_UNMOD_EVENT_CODE(IIO_CAPACITANCE, \
|
chan, \
|
IIO_EV_TYPE_##ev_type, \
|
IIO_EV_DIR_##ev_dir))
|
static AD7150_TIMEOUT(0, mag_adaptive, rising, MAG_ADAPTIVE, RISING);
|
static AD7150_TIMEOUT(0, mag_adaptive, falling, MAG_ADAPTIVE, FALLING);
|
static AD7150_TIMEOUT(1, mag_adaptive, rising, MAG_ADAPTIVE, RISING);
|
static AD7150_TIMEOUT(1, mag_adaptive, falling, MAG_ADAPTIVE, FALLING);
|
static AD7150_TIMEOUT(0, thresh_adaptive, rising, THRESH_ADAPTIVE, RISING);
|
static AD7150_TIMEOUT(0, thresh_adaptive, falling, THRESH_ADAPTIVE, FALLING);
|
static AD7150_TIMEOUT(1, thresh_adaptive, rising, THRESH_ADAPTIVE, RISING);
|
static AD7150_TIMEOUT(1, thresh_adaptive, falling, THRESH_ADAPTIVE, FALLING);
|
|
static const struct iio_event_spec ad7150_events[] = {
|
{
|
.type = IIO_EV_TYPE_THRESH,
|
.dir = IIO_EV_DIR_RISING,
|
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
BIT(IIO_EV_INFO_ENABLE),
|
}, {
|
.type = IIO_EV_TYPE_THRESH,
|
.dir = IIO_EV_DIR_FALLING,
|
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
BIT(IIO_EV_INFO_ENABLE),
|
}, {
|
.type = IIO_EV_TYPE_THRESH_ADAPTIVE,
|
.dir = IIO_EV_DIR_RISING,
|
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
BIT(IIO_EV_INFO_ENABLE),
|
}, {
|
.type = IIO_EV_TYPE_THRESH_ADAPTIVE,
|
.dir = IIO_EV_DIR_FALLING,
|
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
BIT(IIO_EV_INFO_ENABLE),
|
}, {
|
.type = IIO_EV_TYPE_MAG_ADAPTIVE,
|
.dir = IIO_EV_DIR_RISING,
|
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
BIT(IIO_EV_INFO_ENABLE),
|
}, {
|
.type = IIO_EV_TYPE_MAG_ADAPTIVE,
|
.dir = IIO_EV_DIR_FALLING,
|
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
BIT(IIO_EV_INFO_ENABLE),
|
},
|
};
|
|
#define AD7150_CAPACITANCE_CHAN(_chan) { \
|
.type = IIO_CAPACITANCE, \
|
.indexed = 1, \
|
.channel = _chan, \
|
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
BIT(IIO_CHAN_INFO_AVERAGE_RAW), \
|
.event_spec = ad7150_events, \
|
.num_event_specs = ARRAY_SIZE(ad7150_events), \
|
}
|
|
static const struct iio_chan_spec ad7150_channels[] = {
|
AD7150_CAPACITANCE_CHAN(0),
|
AD7150_CAPACITANCE_CHAN(1)
|
};
|
|
static irqreturn_t ad7150_event_handler(int irq, void *private)
|
{
|
struct iio_dev *indio_dev = private;
|
struct ad7150_chip_info *chip = iio_priv(indio_dev);
|
u8 int_status;
|
s64 timestamp = iio_get_time_ns(indio_dev);
|
int ret;
|
|
ret = i2c_smbus_read_byte_data(chip->client, AD7150_STATUS);
|
if (ret < 0)
|
return IRQ_HANDLED;
|
|
int_status = ret;
|
|
if ((int_status & AD7150_STATUS_OUT1) &&
|
!(chip->old_state & AD7150_STATUS_OUT1))
|
iio_push_event(indio_dev,
|
IIO_UNMOD_EVENT_CODE(IIO_CAPACITANCE,
|
0,
|
IIO_EV_TYPE_THRESH,
|
IIO_EV_DIR_RISING),
|
timestamp);
|
else if ((!(int_status & AD7150_STATUS_OUT1)) &&
|
(chip->old_state & AD7150_STATUS_OUT1))
|
iio_push_event(indio_dev,
|
IIO_UNMOD_EVENT_CODE(IIO_CAPACITANCE,
|
0,
|
IIO_EV_TYPE_THRESH,
|
IIO_EV_DIR_FALLING),
|
timestamp);
|
|
if ((int_status & AD7150_STATUS_OUT2) &&
|
!(chip->old_state & AD7150_STATUS_OUT2))
|
iio_push_event(indio_dev,
|
IIO_UNMOD_EVENT_CODE(IIO_CAPACITANCE,
|
1,
|
IIO_EV_TYPE_THRESH,
|
IIO_EV_DIR_RISING),
|
timestamp);
|
else if ((!(int_status & AD7150_STATUS_OUT2)) &&
|
(chip->old_state & AD7150_STATUS_OUT2))
|
iio_push_event(indio_dev,
|
IIO_UNMOD_EVENT_CODE(IIO_CAPACITANCE,
|
1,
|
IIO_EV_TYPE_THRESH,
|
IIO_EV_DIR_FALLING),
|
timestamp);
|
/* store the status to avoid repushing same events */
|
chip->old_state = int_status;
|
|
return IRQ_HANDLED;
|
}
|
|
/* Timeouts not currently handled by core */
|
static struct attribute *ad7150_event_attributes[] = {
|
&iio_dev_attr_in_capacitance0_mag_adaptive_rising_timeout
|
.dev_attr.attr,
|
&iio_dev_attr_in_capacitance0_mag_adaptive_falling_timeout
|
.dev_attr.attr,
|
&iio_dev_attr_in_capacitance1_mag_adaptive_rising_timeout
|
.dev_attr.attr,
|
&iio_dev_attr_in_capacitance1_mag_adaptive_falling_timeout
|
.dev_attr.attr,
|
&iio_dev_attr_in_capacitance0_thresh_adaptive_rising_timeout
|
.dev_attr.attr,
|
&iio_dev_attr_in_capacitance0_thresh_adaptive_falling_timeout
|
.dev_attr.attr,
|
&iio_dev_attr_in_capacitance1_thresh_adaptive_rising_timeout
|
.dev_attr.attr,
|
&iio_dev_attr_in_capacitance1_thresh_adaptive_falling_timeout
|
.dev_attr.attr,
|
NULL,
|
};
|
|
static const struct attribute_group ad7150_event_attribute_group = {
|
.attrs = ad7150_event_attributes,
|
.name = "events",
|
};
|
|
static const struct iio_info ad7150_info = {
|
.event_attrs = &ad7150_event_attribute_group,
|
.read_raw = &ad7150_read_raw,
|
.read_event_config = &ad7150_read_event_config,
|
.write_event_config = &ad7150_write_event_config,
|
.read_event_value = &ad7150_read_event_value,
|
.write_event_value = &ad7150_write_event_value,
|
};
|
|
static int ad7150_probe(struct i2c_client *client,
|
const struct i2c_device_id *id)
|
{
|
int ret;
|
struct ad7150_chip_info *chip;
|
struct iio_dev *indio_dev;
|
|
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
|
if (!indio_dev)
|
return -ENOMEM;
|
chip = iio_priv(indio_dev);
|
mutex_init(&chip->state_lock);
|
/* this is only used for device removal purposes */
|
i2c_set_clientdata(client, indio_dev);
|
|
chip->client = client;
|
|
indio_dev->name = id->name;
|
indio_dev->channels = ad7150_channels;
|
indio_dev->num_channels = ARRAY_SIZE(ad7150_channels);
|
|
indio_dev->info = &ad7150_info;
|
|
indio_dev->modes = INDIO_DIRECT_MODE;
|
|
if (client->irq) {
|
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
NULL,
|
&ad7150_event_handler,
|
IRQF_TRIGGER_RISING |
|
IRQF_TRIGGER_FALLING |
|
IRQF_ONESHOT,
|
"ad7150_irq1",
|
indio_dev);
|
if (ret)
|
return ret;
|
}
|
|
if (client->dev.platform_data) {
|
ret = devm_request_threaded_irq(&client->dev, *(unsigned int *)
|
client->dev.platform_data,
|
NULL,
|
&ad7150_event_handler,
|
IRQF_TRIGGER_RISING |
|
IRQF_TRIGGER_FALLING |
|
IRQF_ONESHOT,
|
"ad7150_irq2",
|
indio_dev);
|
if (ret)
|
return ret;
|
}
|
|
ret = devm_iio_device_register(indio_dev->dev.parent, indio_dev);
|
if (ret)
|
return ret;
|
|
dev_info(&client->dev, "%s capacitive sensor registered,irq: %d\n",
|
id->name, client->irq);
|
|
return 0;
|
}
|
|
static const struct i2c_device_id ad7150_id[] = {
|
{ "ad7150", 0 },
|
{ "ad7151", 0 },
|
{ "ad7156", 0 },
|
{}
|
};
|
|
MODULE_DEVICE_TABLE(i2c, ad7150_id);
|
|
static struct i2c_driver ad7150_driver = {
|
.driver = {
|
.name = "ad7150",
|
},
|
.probe = ad7150_probe,
|
.id_table = ad7150_id,
|
};
|
module_i2c_driver(ad7150_driver);
|
|
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
|
MODULE_DESCRIPTION("Analog Devices AD7150/1/6 capacitive sensor driver");
|
MODULE_LICENSE("GPL v2");
|