summaryrefslogtreecommitdiffstats
path: root/kernel/drivers/iio/proximity
diff options
context:
space:
mode:
authorJosé Pekkarinen <jose.pekkarinen@nokia.com>2016-04-11 10:41:07 +0300
committerJosé Pekkarinen <jose.pekkarinen@nokia.com>2016-04-13 08:17:18 +0300
commite09b41010ba33a20a87472ee821fa407a5b8da36 (patch)
treed10dc367189862e7ca5c592f033dc3726e1df4e3 /kernel/drivers/iio/proximity
parentf93b97fd65072de626c074dbe099a1fff05ce060 (diff)
These changes are the raw update to linux-4.4.6-rt14. Kernel sources
are taken from kernel.org, and rt patch from the rt wiki download page. During the rebasing, the following patch collided: Force tick interrupt and get rid of softirq magic(I70131fb85). Collisions have been removed because its logic was found on the source already. Change-Id: I7f57a4081d9deaa0d9ccfc41a6c8daccdee3b769 Signed-off-by: José Pekkarinen <jose.pekkarinen@nokia.com>
Diffstat (limited to 'kernel/drivers/iio/proximity')
-rw-r--r--kernel/drivers/iio/proximity/Kconfig12
-rw-r--r--kernel/drivers/iio/proximity/Makefile1
-rw-r--r--kernel/drivers/iio/proximity/as3935.c8
-rw-r--r--kernel/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c289
-rw-r--r--kernel/drivers/iio/proximity/sx9500.c454
5 files changed, 686 insertions, 78 deletions
diff --git a/kernel/drivers/iio/proximity/Kconfig b/kernel/drivers/iio/proximity/Kconfig
index 41a8d8ffa..ef4c73db5 100644
--- a/kernel/drivers/iio/proximity/Kconfig
+++ b/kernel/drivers/iio/proximity/Kconfig
@@ -20,6 +20,18 @@ endmenu
menu "Proximity sensors"
+config LIDAR_LITE_V2
+ tristate "PulsedLight LIDAR sensor"
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ depends on I2C
+ help
+ Say Y to build a driver for PulsedLight LIDAR range finding
+ sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pulsedlight-lite-v2
+
config SX9500
tristate "SX9500 Semtech proximity sensor"
select IIO_BUFFER
diff --git a/kernel/drivers/iio/proximity/Makefile b/kernel/drivers/iio/proximity/Makefile
index 9818dc562..9aadd9a8e 100644
--- a/kernel/drivers/iio/proximity/Makefile
+++ b/kernel/drivers/iio/proximity/Makefile
@@ -4,4 +4,5 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_AS3935) += as3935.o
+obj-$(CONFIG_LIDAR_LITE_V2) += pulsedlight-lidar-lite-v2.o
obj-$(CONFIG_SX9500) += sx9500.o
diff --git a/kernel/drivers/iio/proximity/as3935.c b/kernel/drivers/iio/proximity/as3935.c
index bc0d68efd..f4d29d5db 100644
--- a/kernel/drivers/iio/proximity/as3935.c
+++ b/kernel/drivers/iio/proximity/as3935.c
@@ -434,6 +434,12 @@ static int as3935_remove(struct spi_device *spi)
return 0;
}
+static const struct of_device_id as3935_of_match[] = {
+ { .compatible = "ams,as3935", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, as3935_of_match);
+
static const struct spi_device_id as3935_id[] = {
{"as3935", 0},
{},
@@ -443,7 +449,7 @@ MODULE_DEVICE_TABLE(spi, as3935_id);
static struct spi_driver as3935_driver = {
.driver = {
.name = "as3935",
- .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(as3935_of_match),
.pm = AS3935_PM_OPS,
},
.probe = as3935_probe,
diff --git a/kernel/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c b/kernel/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c
new file mode 100644
index 000000000..e544fcfd5
--- /dev/null
+++ b/kernel/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c
@@ -0,0 +1,289 @@
+/*
+ * pulsedlight-lidar-lite-v2.c - Support for PulsedLight LIDAR sensor
+ *
+ * Copyright (C) 2015 Matt Ranostay <mranostay@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * TODO: runtime pm, interrupt mode, and signal strength reporting
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+
+#define LIDAR_REG_CONTROL 0x00
+#define LIDAR_REG_CONTROL_ACQUIRE BIT(2)
+
+#define LIDAR_REG_STATUS 0x01
+#define LIDAR_REG_STATUS_INVALID BIT(3)
+#define LIDAR_REG_STATUS_READY BIT(0)
+
+#define LIDAR_REG_DATA_HBYTE 0x0f
+#define LIDAR_REG_DATA_LBYTE 0x10
+
+#define LIDAR_DRV_NAME "lidar"
+
+struct lidar_data {
+ struct iio_dev *indio_dev;
+ struct i2c_client *client;
+
+ u16 buffer[8]; /* 2 byte distance + 8 byte timestamp */
+};
+
+static const struct iio_chan_spec lidar_channels[] = {
+ {
+ .type = IIO_DISTANCE,
+ .info_mask_separate =
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static int lidar_read_byte(struct lidar_data *data, int reg)
+{
+ struct i2c_client *client = data->client;
+ int ret;
+
+ /*
+ * Device needs a STOP condition between address write, and data read
+ * so in turn i2c_smbus_read_byte_data cannot be used
+ */
+
+ ret = i2c_smbus_write_byte(client, reg);
+ if (ret < 0) {
+ dev_err(&client->dev, "cannot write addr value");
+ return ret;
+ }
+
+ ret = i2c_smbus_read_byte(client);
+ if (ret < 0)
+ dev_err(&client->dev, "cannot read data value");
+
+ return ret;
+}
+
+static inline int lidar_write_control(struct lidar_data *data, int val)
+{
+ return i2c_smbus_write_byte_data(data->client, LIDAR_REG_CONTROL, val);
+}
+
+static int lidar_read_measurement(struct lidar_data *data, u16 *reg)
+{
+ int ret;
+ int val;
+
+ ret = lidar_read_byte(data, LIDAR_REG_DATA_HBYTE);
+ if (ret < 0)
+ return ret;
+ val = ret << 8;
+
+ ret = lidar_read_byte(data, LIDAR_REG_DATA_LBYTE);
+ if (ret < 0)
+ return ret;
+
+ val |= ret;
+ *reg = val;
+
+ return 0;
+}
+
+static int lidar_get_measurement(struct lidar_data *data, u16 *reg)
+{
+ struct i2c_client *client = data->client;
+ int tries = 10;
+ int ret;
+
+ /* start sample */
+ ret = lidar_write_control(data, LIDAR_REG_CONTROL_ACQUIRE);
+ if (ret < 0) {
+ dev_err(&client->dev, "cannot send start measurement command");
+ return ret;
+ }
+
+ while (tries--) {
+ usleep_range(1000, 2000);
+
+ ret = lidar_read_byte(data, LIDAR_REG_STATUS);
+ if (ret < 0)
+ break;
+
+ /* return -EINVAL since laser is likely pointed out of range */
+ if (ret & LIDAR_REG_STATUS_INVALID) {
+ *reg = 0;
+ ret = -EINVAL;
+ break;
+ }
+
+ /* sample ready to read */
+ if (!(ret & LIDAR_REG_STATUS_READY)) {
+ ret = lidar_read_measurement(data, reg);
+ break;
+ }
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+static int lidar_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct lidar_data *data = iio_priv(indio_dev);
+ int ret = -EINVAL;
+
+ mutex_lock(&indio_dev->mlock);
+
+ if (iio_buffer_enabled(indio_dev) && mask == IIO_CHAN_INFO_RAW) {
+ ret = -EBUSY;
+ goto error_busy;
+ }
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW: {
+ u16 reg;
+
+ ret = lidar_get_measurement(data, &reg);
+ if (!ret) {
+ *val = reg;
+ ret = IIO_VAL_INT;
+ }
+ break;
+ }
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ *val2 = 10000;
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ }
+
+error_busy:
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static irqreturn_t lidar_trigger_handler(int irq, void *private)
+{
+ struct iio_poll_func *pf = private;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct lidar_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = lidar_get_measurement(data, data->buffer);
+ if (!ret) {
+ iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
+ iio_get_time_ns());
+ } else if (ret != -EINVAL) {
+ dev_err(&data->client->dev, "cannot read LIDAR measurement");
+ }
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_info lidar_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = lidar_read_raw,
+};
+
+static int lidar_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct lidar_data *data;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ indio_dev->info = &lidar_info;
+ indio_dev->name = LIDAR_DRV_NAME;
+ indio_dev->channels = lidar_channels;
+ indio_dev->num_channels = ARRAY_SIZE(lidar_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+
+ data->client = client;
+ data->indio_dev = indio_dev;
+
+ ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ lidar_trigger_handler, NULL);
+ if (ret)
+ return ret;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_unreg_buffer;
+
+ return 0;
+
+error_unreg_buffer:
+ iio_triggered_buffer_cleanup(indio_dev);
+
+ return ret;
+}
+
+static int lidar_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+ iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id lidar_id[] = {
+ {"lidar-lite-v2", 0},
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, lidar_id);
+
+static const struct of_device_id lidar_dt_ids[] = {
+ { .compatible = "pulsedlight,lidar-lite-v2" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, lidar_dt_ids);
+
+static struct i2c_driver lidar_driver = {
+ .driver = {
+ .name = LIDAR_DRV_NAME,
+ .of_match_table = of_match_ptr(lidar_dt_ids),
+ },
+ .probe = lidar_probe,
+ .remove = lidar_remove,
+ .id_table = lidar_id,
+};
+module_i2c_driver(lidar_driver);
+
+MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
+MODULE_DESCRIPTION("PulsedLight LIDAR sensor");
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/iio/proximity/sx9500.c b/kernel/drivers/iio/proximity/sx9500.c
index bd26a484a..66cd09a18 100644
--- a/kernel/drivers/iio/proximity/sx9500.c
+++ b/kernel/drivers/iio/proximity/sx9500.c
@@ -18,6 +18,8 @@
#include <linux/acpi.h>
#include <linux/gpio/consumer.h>
#include <linux/regmap.h>
+#include <linux/pm.h>
+#include <linux/delay.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
@@ -29,7 +31,9 @@
#define SX9500_DRIVER_NAME "sx9500"
#define SX9500_IRQ_NAME "sx9500_event"
-#define SX9500_GPIO_NAME "sx9500_gpio"
+
+#define SX9500_GPIO_INT "interrupt"
+#define SX9500_GPIO_RESET "reset"
/* Register definitions. */
#define SX9500_REG_IRQ_SRC 0x00
@@ -73,14 +77,17 @@
#define SX9500_CONVDONE_IRQ BIT(3)
#define SX9500_PROXSTAT_SHIFT 4
+#define SX9500_COMPSTAT_MASK GENMASK(3, 0)
#define SX9500_NUM_CHANNELS 4
+#define SX9500_CHAN_MASK GENMASK(SX9500_NUM_CHANNELS - 1, 0)
struct sx9500_data {
struct mutex mutex;
struct i2c_client *client;
struct iio_trigger *trig;
struct regmap *regmap;
+ struct gpio_desc *gpiod_rst;
/*
* Last reading of the proximity status for each channel. We
* only send an event to user space when this changes.
@@ -89,6 +96,11 @@ struct sx9500_data {
bool event_enabled[SX9500_NUM_CHANNELS];
bool trigger_enabled;
u16 *buffer;
+ /* Remember enabled channels and sample rate during suspend. */
+ unsigned int suspend_ctrl0;
+ struct completion completion;
+ int data_rdy_users, close_far_users;
+ int channel_users[SX9500_NUM_CHANNELS];
};
static const struct iio_event_spec sx9500_events[] = {
@@ -139,6 +151,10 @@ static const struct {
{2, 500000},
};
+static const unsigned int sx9500_scan_period_table[] = {
+ 30, 60, 90, 120, 150, 200, 300, 400,
+};
+
static const struct regmap_range sx9500_writable_reg_ranges[] = {
regmap_reg_range(SX9500_REG_IRQ_MSK, SX9500_REG_IRQ_MSK),
regmap_reg_range(SX9500_REG_PROX_CTRL0, SX9500_REG_PROX_CTRL8),
@@ -191,7 +207,67 @@ static const struct regmap_config sx9500_regmap_config = {
.volatile_table = &sx9500_volatile_regs,
};
-static int sx9500_read_proximity(struct sx9500_data *data,
+static int sx9500_inc_users(struct sx9500_data *data, int *counter,
+ unsigned int reg, unsigned int bitmask)
+{
+ (*counter)++;
+ if (*counter != 1)
+ /* Bit is already active, nothing to do. */
+ return 0;
+
+ return regmap_update_bits(data->regmap, reg, bitmask, bitmask);
+}
+
+static int sx9500_dec_users(struct sx9500_data *data, int *counter,
+ unsigned int reg, unsigned int bitmask)
+{
+ (*counter)--;
+ if (*counter != 0)
+ /* There are more users, do not deactivate. */
+ return 0;
+
+ return regmap_update_bits(data->regmap, reg, bitmask, 0);
+}
+
+static int sx9500_inc_chan_users(struct sx9500_data *data, int chan)
+{
+ return sx9500_inc_users(data, &data->channel_users[chan],
+ SX9500_REG_PROX_CTRL0, BIT(chan));
+}
+
+static int sx9500_dec_chan_users(struct sx9500_data *data, int chan)
+{
+ return sx9500_dec_users(data, &data->channel_users[chan],
+ SX9500_REG_PROX_CTRL0, BIT(chan));
+}
+
+static int sx9500_inc_data_rdy_users(struct sx9500_data *data)
+{
+ return sx9500_inc_users(data, &data->data_rdy_users,
+ SX9500_REG_IRQ_MSK, SX9500_CONVDONE_IRQ);
+}
+
+static int sx9500_dec_data_rdy_users(struct sx9500_data *data)
+{
+ return sx9500_dec_users(data, &data->data_rdy_users,
+ SX9500_REG_IRQ_MSK, SX9500_CONVDONE_IRQ);
+}
+
+static int sx9500_inc_close_far_users(struct sx9500_data *data)
+{
+ return sx9500_inc_users(data, &data->close_far_users,
+ SX9500_REG_IRQ_MSK,
+ SX9500_CLOSE_IRQ | SX9500_FAR_IRQ);
+}
+
+static int sx9500_dec_close_far_users(struct sx9500_data *data)
+{
+ return sx9500_dec_users(data, &data->close_far_users,
+ SX9500_REG_IRQ_MSK,
+ SX9500_CLOSE_IRQ | SX9500_FAR_IRQ);
+}
+
+static int sx9500_read_prox_data(struct sx9500_data *data,
const struct iio_chan_spec *chan,
int *val)
{
@@ -211,6 +287,81 @@ static int sx9500_read_proximity(struct sx9500_data *data,
return IIO_VAL_INT;
}
+/*
+ * If we have no interrupt support, we have to wait for a scan period
+ * after enabling a channel to get a result.
+ */
+static int sx9500_wait_for_sample(struct sx9500_data *data)
+{
+ int ret;
+ unsigned int val;
+
+ ret = regmap_read(data->regmap, SX9500_REG_PROX_CTRL0, &val);
+ if (ret < 0)
+ return ret;
+
+ val = (val & SX9500_SCAN_PERIOD_MASK) >> SX9500_SCAN_PERIOD_SHIFT;
+
+ msleep(sx9500_scan_period_table[val]);
+
+ return 0;
+}
+
+static int sx9500_read_proximity(struct sx9500_data *data,
+ const struct iio_chan_spec *chan,
+ int *val)
+{
+ int ret;
+
+ mutex_lock(&data->mutex);
+
+ ret = sx9500_inc_chan_users(data, chan->channel);
+ if (ret < 0)
+ goto out;
+
+ ret = sx9500_inc_data_rdy_users(data);
+ if (ret < 0)
+ goto out_dec_chan;
+
+ mutex_unlock(&data->mutex);
+
+ if (data->client->irq > 0)
+ ret = wait_for_completion_interruptible(&data->completion);
+ else
+ ret = sx9500_wait_for_sample(data);
+
+ mutex_lock(&data->mutex);
+
+ if (ret < 0)
+ goto out_dec_data_rdy;
+
+ ret = sx9500_read_prox_data(data, chan, val);
+ if (ret < 0)
+ goto out_dec_data_rdy;
+
+ ret = sx9500_dec_data_rdy_users(data);
+ if (ret < 0)
+ goto out_dec_chan;
+
+ ret = sx9500_dec_chan_users(data, chan->channel);
+ if (ret < 0)
+ goto out;
+
+ ret = IIO_VAL_INT;
+
+ goto out;
+
+out_dec_data_rdy:
+ sx9500_dec_data_rdy_users(data);
+out_dec_chan:
+ sx9500_dec_chan_users(data, chan->channel);
+out:
+ mutex_unlock(&data->mutex);
+ reinit_completion(&data->completion);
+
+ return ret;
+}
+
static int sx9500_read_samp_freq(struct sx9500_data *data,
int *val, int *val2)
{
@@ -236,7 +387,6 @@ static int sx9500_read_raw(struct iio_dev *indio_dev,
int *val, int *val2, long mask)
{
struct sx9500_data *data = iio_priv(indio_dev);
- int ret;
switch (chan->type) {
case IIO_PROXIMITY:
@@ -244,10 +394,7 @@ static int sx9500_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_RAW:
if (iio_buffer_enabled(indio_dev))
return -EBUSY;
- mutex_lock(&data->mutex);
- ret = sx9500_read_proximity(data, chan, val);
- mutex_unlock(&data->mutex);
- return ret;
+ return sx9500_read_proximity(data, chan, val);
case IIO_CHAN_INFO_SAMP_FREQ:
return sx9500_read_samp_freq(data, val, val2);
default:
@@ -318,28 +465,16 @@ static irqreturn_t sx9500_irq_handler(int irq, void *private)
return IRQ_WAKE_THREAD;
}
-static irqreturn_t sx9500_irq_thread_handler(int irq, void *private)
+static void sx9500_push_events(struct iio_dev *indio_dev)
{
- struct iio_dev *indio_dev = private;
- struct sx9500_data *data = iio_priv(indio_dev);
int ret;
unsigned int val, chan;
-
- mutex_lock(&data->mutex);
-
- ret = regmap_read(data->regmap, SX9500_REG_IRQ_SRC, &val);
- if (ret < 0) {
- dev_err(&data->client->dev, "i2c transfer error in irq\n");
- goto out;
- }
-
- if (!(val & (SX9500_CLOSE_IRQ | SX9500_FAR_IRQ)))
- goto out;
+ struct sx9500_data *data = iio_priv(indio_dev);
ret = regmap_read(data->regmap, SX9500_REG_STAT, &val);
if (ret < 0) {
dev_err(&data->client->dev, "i2c transfer error in irq\n");
- goto out;
+ return;
}
val >>= SX9500_PROXSTAT_SHIFT;
@@ -354,15 +489,34 @@ static irqreturn_t sx9500_irq_thread_handler(int irq, void *private)
/* No change on this channel. */
continue;
- dir = new_prox ? IIO_EV_DIR_FALLING :
- IIO_EV_DIR_RISING;
- ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY,
- chan,
- IIO_EV_TYPE_THRESH,
- dir);
+ dir = new_prox ? IIO_EV_DIR_FALLING : IIO_EV_DIR_RISING;
+ ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, chan,
+ IIO_EV_TYPE_THRESH, dir);
iio_push_event(indio_dev, ev, iio_get_time_ns());
data->prox_stat[chan] = new_prox;
}
+}
+
+static irqreturn_t sx9500_irq_thread_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct sx9500_data *data = iio_priv(indio_dev);
+ int ret;
+ unsigned int val;
+
+ mutex_lock(&data->mutex);
+
+ ret = regmap_read(data->regmap, SX9500_REG_IRQ_SRC, &val);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "i2c transfer error in irq\n");
+ goto out;
+ }
+
+ if (val & (SX9500_CLOSE_IRQ | SX9500_FAR_IRQ))
+ sx9500_push_events(indio_dev);
+
+ if (val & SX9500_CONVDONE_IRQ)
+ complete_all(&data->completion);
out:
mutex_unlock(&data->mutex);
@@ -391,9 +545,7 @@ static int sx9500_write_event_config(struct iio_dev *indio_dev,
int state)
{
struct sx9500_data *data = iio_priv(indio_dev);
- int ret, i;
- bool any_active = false;
- unsigned int irqmask;
+ int ret;
if (chan->type != IIO_PROXIMITY || type != IIO_EV_TYPE_THRESH ||
dir != IIO_EV_DIR_EITHER)
@@ -401,24 +553,32 @@ static int sx9500_write_event_config(struct iio_dev *indio_dev,
mutex_lock(&data->mutex);
- data->event_enabled[chan->channel] = state;
+ if (state == 1) {
+ ret = sx9500_inc_chan_users(data, chan->channel);
+ if (ret < 0)
+ goto out_unlock;
+ ret = sx9500_inc_close_far_users(data);
+ if (ret < 0)
+ goto out_undo_chan;
+ } else {
+ ret = sx9500_dec_chan_users(data, chan->channel);
+ if (ret < 0)
+ goto out_unlock;
+ ret = sx9500_dec_close_far_users(data);
+ if (ret < 0)
+ goto out_undo_chan;
+ }
- for (i = 0; i < SX9500_NUM_CHANNELS; i++)
- if (data->event_enabled[i]) {
- any_active = true;
- break;
- }
+ data->event_enabled[chan->channel] = state;
+ goto out_unlock;
- irqmask = SX9500_CLOSE_IRQ | SX9500_FAR_IRQ;
- if (any_active)
- ret = regmap_update_bits(data->regmap, SX9500_REG_IRQ_MSK,
- irqmask, irqmask);
+out_undo_chan:
+ if (state == 1)
+ sx9500_dec_chan_users(data, chan->channel);
else
- ret = regmap_update_bits(data->regmap, SX9500_REG_IRQ_MSK,
- irqmask, 0);
-
+ sx9500_inc_chan_users(data, chan->channel);
+out_unlock:
mutex_unlock(&data->mutex);
-
return ret;
}
@@ -469,12 +629,16 @@ static int sx9500_set_trigger_state(struct iio_trigger *trig,
mutex_lock(&data->mutex);
- ret = regmap_update_bits(data->regmap, SX9500_REG_IRQ_MSK,
- SX9500_CONVDONE_IRQ,
- state ? SX9500_CONVDONE_IRQ : 0);
- if (ret == 0)
- data->trigger_enabled = state;
+ if (state)
+ ret = sx9500_inc_data_rdy_users(data);
+ else
+ ret = sx9500_dec_data_rdy_users(data);
+ if (ret < 0)
+ goto out;
+
+ data->trigger_enabled = state;
+out:
mutex_unlock(&data->mutex);
return ret;
@@ -496,7 +660,7 @@ static irqreturn_t sx9500_trigger_handler(int irq, void *private)
for_each_set_bit(bit, indio_dev->active_scan_mask,
indio_dev->masklength) {
- ret = sx9500_read_proximity(data, &indio_dev->channels[bit],
+ ret = sx9500_read_prox_data(data, &indio_dev->channels[bit],
&val);
if (ret < 0)
goto out;
@@ -515,6 +679,62 @@ out:
return IRQ_HANDLED;
}
+static int sx9500_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct sx9500_data *data = iio_priv(indio_dev);
+ int ret = 0, i;
+
+ mutex_lock(&data->mutex);
+
+ for (i = 0; i < SX9500_NUM_CHANNELS; i++)
+ if (test_bit(i, indio_dev->active_scan_mask)) {
+ ret = sx9500_inc_chan_users(data, i);
+ if (ret)
+ break;
+ }
+
+ if (ret)
+ for (i = i - 1; i >= 0; i--)
+ if (test_bit(i, indio_dev->active_scan_mask))
+ sx9500_dec_chan_users(data, i);
+
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
+static int sx9500_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct sx9500_data *data = iio_priv(indio_dev);
+ int ret = 0, i;
+
+ iio_triggered_buffer_predisable(indio_dev);
+
+ mutex_lock(&data->mutex);
+
+ for (i = 0; i < SX9500_NUM_CHANNELS; i++)
+ if (test_bit(i, indio_dev->active_scan_mask)) {
+ ret = sx9500_dec_chan_users(data, i);
+ if (ret)
+ break;
+ }
+
+ if (ret)
+ for (i = i - 1; i >= 0; i--)
+ if (test_bit(i, indio_dev->active_scan_mask))
+ sx9500_inc_chan_users(data, i);
+
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
+static const struct iio_buffer_setup_ops sx9500_buffer_setup_ops = {
+ .preenable = sx9500_buffer_preenable,
+ .postenable = iio_triggered_buffer_postenable,
+ .predisable = sx9500_buffer_predisable,
+};
+
struct sx9500_reg_default {
u8 reg;
u8 def;
@@ -570,17 +790,56 @@ static const struct sx9500_reg_default sx9500_default_regs[] = {
},
{
.reg = SX9500_REG_PROX_CTRL0,
- /* Scan period: 30ms, all sensors enabled. */
- .def = 0x0f,
+ /* Scan period: 30ms, all sensors disabled. */
+ .def = 0x00,
},
};
+/* Activate all channels and perform an initial compensation. */
+static int sx9500_init_compensation(struct iio_dev *indio_dev)
+{
+ struct sx9500_data *data = iio_priv(indio_dev);
+ int i, ret;
+ unsigned int val;
+
+ ret = regmap_update_bits(data->regmap, SX9500_REG_PROX_CTRL0,
+ SX9500_CHAN_MASK, SX9500_CHAN_MASK);
+ if (ret < 0)
+ return ret;
+
+ for (i = 10; i >= 0; i--) {
+ usleep_range(10000, 20000);
+ ret = regmap_read(data->regmap, SX9500_REG_STAT, &val);
+ if (ret < 0)
+ goto out;
+ if (!(val & SX9500_COMPSTAT_MASK))
+ break;
+ }
+
+ if (i < 0) {
+ dev_err(&data->client->dev, "initial compensation timed out");
+ ret = -ETIMEDOUT;
+ }
+
+out:
+ regmap_update_bits(data->regmap, SX9500_REG_PROX_CTRL0,
+ SX9500_CHAN_MASK, 0);
+ return ret;
+}
+
static int sx9500_init_device(struct iio_dev *indio_dev)
{
struct sx9500_data *data = iio_priv(indio_dev);
int ret, i;
unsigned int val;
+ if (data->gpiod_rst) {
+ gpiod_set_value_cansleep(data->gpiod_rst, 0);
+ usleep_range(1000, 2000);
+ gpiod_set_value_cansleep(data->gpiod_rst, 1);
+ usleep_range(1000, 2000);
+ }
+
ret = regmap_write(data->regmap, SX9500_REG_IRQ_MSK, 0);
if (ret < 0)
return ret;
@@ -602,33 +861,25 @@ static int sx9500_init_device(struct iio_dev *indio_dev)
return ret;
}
- return 0;
+ return sx9500_init_compensation(indio_dev);
}
-static int sx9500_gpio_probe(struct i2c_client *client,
- struct sx9500_data *data)
+static void sx9500_gpio_probe(struct i2c_client *client,
+ struct sx9500_data *data)
{
struct device *dev;
- struct gpio_desc *gpio;
- int ret;
if (!client)
- return -EINVAL;
+ return;
dev = &client->dev;
- /* data ready gpio interrupt pin */
- gpio = devm_gpiod_get_index(dev, SX9500_GPIO_NAME, 0, GPIOD_IN);
- if (IS_ERR(gpio)) {
- dev_err(dev, "acpi gpio get index failed\n");
- return PTR_ERR(gpio);
+ data->gpiod_rst = devm_gpiod_get_index(dev, SX9500_GPIO_RESET,
+ 0, GPIOD_OUT_HIGH);
+ if (IS_ERR(data->gpiod_rst)) {
+ dev_warn(dev, "gpio get reset pin failed\n");
+ data->gpiod_rst = NULL;
}
-
- ret = gpiod_to_irq(gpio);
-
- dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
-
- return ret;
}
static int sx9500_probe(struct i2c_client *client,
@@ -645,14 +896,13 @@ static int sx9500_probe(struct i2c_client *client,
data = iio_priv(indio_dev);
data->client = client;
mutex_init(&data->mutex);
+ init_completion(&data->completion);
data->trigger_enabled = false;
data->regmap = devm_regmap_init_i2c(client, &sx9500_regmap_config);
if (IS_ERR(data->regmap))
return PTR_ERR(data->regmap);
- sx9500_init_device(indio_dev);
-
indio_dev->dev.parent = &client->dev;
indio_dev->name = SX9500_DRIVER_NAME;
indio_dev->channels = sx9500_channels;
@@ -661,10 +911,15 @@ static int sx9500_probe(struct i2c_client *client,
indio_dev->modes = INDIO_DIRECT_MODE;
i2c_set_clientdata(client, indio_dev);
- if (client->irq <= 0)
- client->irq = sx9500_gpio_probe(client, data);
+ sx9500_gpio_probe(client, data);
- if (client->irq > 0) {
+ ret = sx9500_init_device(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ if (client->irq <= 0)
+ dev_warn(&client->dev, "no valid irq found\n");
+ else {
ret = devm_request_threaded_irq(&client->dev, client->irq,
sx9500_irq_handler, sx9500_irq_thread_handler,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
@@ -687,7 +942,8 @@ static int sx9500_probe(struct i2c_client *client,
}
ret = iio_triggered_buffer_setup(indio_dev, NULL,
- sx9500_trigger_handler, NULL);
+ sx9500_trigger_handler,
+ &sx9500_buffer_setup_ops);
if (ret < 0)
goto out_trigger_unregister;
@@ -720,6 +976,49 @@ static int sx9500_remove(struct i2c_client *client)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int sx9500_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct sx9500_data *data = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&data->mutex);
+ ret = regmap_read(data->regmap, SX9500_REG_PROX_CTRL0,
+ &data->suspend_ctrl0);
+ if (ret < 0)
+ goto out;
+
+ /*
+ * Scan period doesn't matter because when all the sensors are
+ * deactivated the device is in sleep mode.
+ */
+ ret = regmap_write(data->regmap, SX9500_REG_PROX_CTRL0, 0);
+
+out:
+ mutex_unlock(&data->mutex);
+ return ret;
+}
+
+static int sx9500_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct sx9500_data *data = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&data->mutex);
+ ret = regmap_write(data->regmap, SX9500_REG_PROX_CTRL0,
+ data->suspend_ctrl0);
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops sx9500_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(sx9500_suspend, sx9500_resume)
+};
+
static const struct acpi_device_id sx9500_acpi_match[] = {
{"SSX9500", 0},
{ },
@@ -728,7 +1027,7 @@ MODULE_DEVICE_TABLE(acpi, sx9500_acpi_match);
static const struct i2c_device_id sx9500_id[] = {
{"sx9500", 0},
- {}
+ { },
};
MODULE_DEVICE_TABLE(i2c, sx9500_id);
@@ -736,6 +1035,7 @@ static struct i2c_driver sx9500_driver = {
.driver = {
.name = SX9500_DRIVER_NAME,
.acpi_match_table = ACPI_PTR(sx9500_acpi_match),
+ .pm = &sx9500_pm_ops,
},
.probe = sx9500_probe,
.remove = sx9500_remove,