summaryrefslogtreecommitdiff
path: root/drivers/iio
diff options
context:
space:
mode:
authorAndré Fabian Silva Delgado <emulatorman@parabola.nu>2015-09-08 01:01:14 -0300
committerAndré Fabian Silva Delgado <emulatorman@parabola.nu>2015-09-08 01:01:14 -0300
commite5fd91f1ef340da553f7a79da9540c3db711c937 (patch)
treeb11842027dc6641da63f4bcc524f8678263304a3 /drivers/iio
parent2a9b0348e685a63d97486f6749622b61e9e3292f (diff)
Linux-libre 4.2-gnu
Diffstat (limited to 'drivers/iio')
-rw-r--r--drivers/iio/accel/Kconfig21
-rw-r--r--drivers/iio/accel/Makefile3
-rw-r--r--drivers/iio/accel/bmc150-accel.c55
-rw-r--r--drivers/iio/accel/hid-sensor-accel-3d.c15
-rw-r--r--drivers/iio/accel/kxcjk-1013.c41
-rw-r--r--drivers/iio/accel/mma8452.c555
-rw-r--r--drivers/iio/accel/mma9551_core.c8
-rw-r--r--drivers/iio/accel/mma9551_core.h2
-rw-r--r--drivers/iio/accel/mma9553.c148
-rw-r--r--drivers/iio/accel/st_accel.h1
-rw-r--r--drivers/iio/accel/st_accel_core.c86
-rw-r--r--drivers/iio/accel/st_accel_i2c.c4
-rw-r--r--drivers/iio/accel/stk8312.c390
-rw-r--r--drivers/iio/accel/stk8ba50.c302
-rw-r--r--drivers/iio/adc/Kconfig11
-rw-r--r--drivers/iio/adc/Makefile1
-rw-r--r--drivers/iio/adc/axp288_adc.c2
-rw-r--r--drivers/iio/adc/berlin2-adc.c378
-rw-r--r--drivers/iio/adc/mcp320x.c2
-rw-r--r--drivers/iio/adc/ti-adc128s052.c30
-rw-r--r--drivers/iio/adc/ti_am335x_adc.c83
-rw-r--r--drivers/iio/adc/twl4030-madc.c8
-rw-r--r--drivers/iio/adc/vf610_adc.c148
-rw-r--r--drivers/iio/buffer_cb.c2
-rw-r--r--drivers/iio/common/st_sensors/st_sensors_core.c27
-rw-r--r--drivers/iio/common/st_sensors/st_sensors_trigger.c4
-rw-r--r--drivers/iio/dac/Kconfig10
-rw-r--r--drivers/iio/dac/Makefile1
-rw-r--r--drivers/iio/dac/m62332.c269
-rw-r--r--drivers/iio/gyro/bmg160.c67
-rw-r--r--drivers/iio/gyro/hid-sensor-gyro-3d.c15
-rw-r--r--drivers/iio/humidity/Kconfig2
-rw-r--r--drivers/iio/industrialio-buffer.c378
-rw-r--r--drivers/iio/industrialio-core.c6
-rw-r--r--drivers/iio/industrialio-event.c2
-rw-r--r--drivers/iio/kfifo_buf.c2
-rw-r--r--drivers/iio/light/Kconfig39
-rw-r--r--drivers/iio/light/Makefile3
-rw-r--r--drivers/iio/light/acpi-als.c231
-rw-r--r--drivers/iio/light/bh1750.c334
-rw-r--r--drivers/iio/light/hid-sensor-als.c14
-rw-r--r--drivers/iio/light/hid-sensor-prox.c2
-rw-r--r--drivers/iio/light/ltr501.c1284
-rw-r--r--drivers/iio/light/stk3310.c703
-rw-r--r--drivers/iio/light/tsl2563.c36
-rw-r--r--drivers/iio/light/tsl4531.c10
-rw-r--r--drivers/iio/magnetometer/Kconfig30
-rw-r--r--drivers/iio/magnetometer/Makefile3
-rw-r--r--drivers/iio/magnetometer/bmc150_magn.c1109
-rw-r--r--drivers/iio/magnetometer/hid-sensor-magn-3d.c2
-rw-r--r--drivers/iio/magnetometer/mmc35240.c588
-rw-r--r--drivers/iio/magnetometer/st_magn.h1
-rw-r--r--drivers/iio/magnetometer/st_magn_core.c116
-rw-r--r--drivers/iio/magnetometer/st_magn_i2c.c5
-rw-r--r--drivers/iio/orientation/hid-sensor-incl-3d.c16
-rw-r--r--drivers/iio/orientation/hid-sensor-rotation.c17
-rw-r--r--drivers/iio/pressure/hid-sensor-press.c14
-rw-r--r--drivers/iio/proximity/sx9500.c459
-rw-r--r--drivers/iio/temperature/mlx90614.c355
-rw-r--r--drivers/iio/temperature/tmp006.c6
60 files changed, 7831 insertions, 625 deletions
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index 7c9a9a94a..00e7bcbdb 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -136,4 +136,25 @@ config MMA9553
To compile this driver as a module, choose M here: the module
will be called mma9553.
+
+config STK8312
+ tristate "Sensortek STK8312 3-Axis Accelerometer Driver"
+ depends on I2C
+ help
+ Say yes here to get support for the Sensortek STK8312 3-axis
+ accelerometer.
+
+ Choosing M will build the driver as a module. If so, the module
+ will be called stk8312.
+
+config STK8BA50
+ tristate "Sensortek STK8BA50 3-Axis Accelerometer Driver"
+ depends on I2C
+ help
+ Say yes here to get support for the Sensortek STK8BA50 3-axis
+ accelerometer.
+
+ Choosing M will build the driver as a module. If so, the module
+ will be called stk8ba50.
+
endmenu
diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
index 99d89e46c..ebd2675b2 100644
--- a/drivers/iio/accel/Makefile
+++ b/drivers/iio/accel/Makefile
@@ -14,6 +14,9 @@ obj-$(CONFIG_MMA9551_CORE) += mma9551_core.o
obj-$(CONFIG_MMA9551) += mma9551.o
obj-$(CONFIG_MMA9553) += mma9553.o
+obj-$(CONFIG_STK8312) += stk8312.o
+obj-$(CONFIG_STK8BA50) += stk8ba50.o
+
obj-$(CONFIG_IIO_SSP_SENSORS_COMMONS) += ssp_accel_sensor.o
obj-$(CONFIG_IIO_ST_ACCEL_3AXIS) += st_accel.o
diff --git a/drivers/iio/accel/bmc150-accel.c b/drivers/iio/accel/bmc150-accel.c
index bf827d012..cc5a35750 100644
--- a/drivers/iio/accel/bmc150-accel.c
+++ b/drivers/iio/accel/bmc150-accel.c
@@ -196,7 +196,7 @@ struct bmc150_accel_data {
u32 slope_thres;
u32 range;
int ev_enable_state;
- int64_t timestamp, old_timestamp;
+ int64_t timestamp, old_timestamp; /* Only used in hw fifo mode. */
const struct bmc150_accel_chip_info *chip_info;
};
@@ -1183,7 +1183,6 @@ static const struct iio_info bmc150_accel_info = {
.write_event_value = bmc150_accel_write_event,
.write_event_config = bmc150_accel_write_event_config,
.read_event_config = bmc150_accel_read_event_config,
- .validate_trigger = bmc150_accel_validate_trigger,
.driver_module = THIS_MODULE,
};
@@ -1222,7 +1221,7 @@ static irqreturn_t bmc150_accel_trigger_handler(int irq, void *p)
mutex_unlock(&data->mutex);
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
- data->timestamp);
+ pf->timestamp);
err_read:
iio_trigger_notify_done(indio_dev->trig);
@@ -1535,6 +1534,13 @@ static int bmc150_accel_fifo_set_mode(struct bmc150_accel_data *data)
return ret;
}
+static int bmc150_accel_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct bmc150_accel_data *data = iio_priv(indio_dev);
+
+ return bmc150_accel_set_power_state(data, true);
+}
+
static int bmc150_accel_buffer_postenable(struct iio_dev *indio_dev)
{
struct bmc150_accel_data *data = iio_priv(indio_dev);
@@ -1591,9 +1597,18 @@ out:
return 0;
}
+static int bmc150_accel_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct bmc150_accel_data *data = iio_priv(indio_dev);
+
+ return bmc150_accel_set_power_state(data, false);
+}
+
static const struct iio_buffer_setup_ops bmc150_accel_buffer_ops = {
+ .preenable = bmc150_accel_buffer_preenable,
.postenable = bmc150_accel_buffer_postenable,
.predisable = bmc150_accel_buffer_predisable,
+ .postdisable = bmc150_accel_buffer_postdisable,
};
static int bmc150_accel_probe(struct i2c_client *client,
@@ -1636,6 +1651,15 @@ static int bmc150_accel_probe(struct i2c_client *client,
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &bmc150_accel_info;
+ ret = iio_triggered_buffer_setup(indio_dev,
+ &iio_pollfunc_store_time,
+ bmc150_accel_trigger_handler,
+ &bmc150_accel_buffer_ops);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed: iio triggered buffer setup\n");
+ return ret;
+ }
+
if (client->irq < 0)
client->irq = bmc150_accel_gpio_probe(client, data);
@@ -1648,7 +1672,7 @@ static int bmc150_accel_probe(struct i2c_client *client,
BMC150_ACCEL_IRQ_NAME,
indio_dev);
if (ret)
- return ret;
+ goto err_buffer_cleanup;
/*
* Set latched mode interrupt. While certain interrupts are
@@ -1661,24 +1685,14 @@ static int bmc150_accel_probe(struct i2c_client *client,
BMC150_ACCEL_INT_MODE_LATCH_RESET);
if (ret < 0) {
dev_err(&data->client->dev, "Error writing reg_int_rst_latch\n");
- return ret;
+ goto err_buffer_cleanup;
}
bmc150_accel_interrupts_setup(indio_dev, data);
ret = bmc150_accel_triggers_setup(indio_dev, data);
if (ret)
- return ret;
-
- ret = iio_triggered_buffer_setup(indio_dev,
- &iio_pollfunc_store_time,
- bmc150_accel_trigger_handler,
- &bmc150_accel_buffer_ops);
- if (ret < 0) {
- dev_err(&client->dev,
- "Failed: iio triggered buffer setup\n");
- goto err_trigger_unregister;
- }
+ goto err_buffer_cleanup;
if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) ||
i2c_check_functionality(client->adapter,
@@ -1692,7 +1706,7 @@ static int bmc150_accel_probe(struct i2c_client *client,
ret = iio_device_register(indio_dev);
if (ret < 0) {
dev_err(&client->dev, "Unable to register iio device\n");
- goto err_buffer_cleanup;
+ goto err_trigger_unregister;
}
ret = pm_runtime_set_active(&client->dev);
@@ -1708,11 +1722,10 @@ static int bmc150_accel_probe(struct i2c_client *client,
err_iio_unregister:
iio_device_unregister(indio_dev);
-err_buffer_cleanup:
- if (indio_dev->pollfunc)
- iio_triggered_buffer_cleanup(indio_dev);
err_trigger_unregister:
bmc150_accel_unregister_triggers(data, BMC150_ACCEL_TRIGGERS - 1);
+err_buffer_cleanup:
+ iio_triggered_buffer_cleanup(indio_dev);
return ret;
}
@@ -1730,6 +1743,8 @@ static int bmc150_accel_remove(struct i2c_client *client)
bmc150_accel_unregister_triggers(data, BMC150_ACCEL_TRIGGERS - 1);
+ iio_triggered_buffer_cleanup(indio_dev);
+
mutex_lock(&data->mutex);
bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_DEEP_SUSPEND, 0);
mutex_unlock(&data->mutex);
diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c
index 2b4fad699..ab1e238d5 100644
--- a/drivers/iio/accel/hid-sensor-accel-3d.c
+++ b/drivers/iio/accel/hid-sensor-accel-3d.c
@@ -299,7 +299,6 @@ static int hid_accel_3d_probe(struct platform_device *pdev)
struct iio_dev *indio_dev;
struct accel_3d_state *accel_state;
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
- struct iio_chan_spec *channels;
indio_dev = devm_iio_device_alloc(&pdev->dev,
sizeof(struct accel_3d_state));
@@ -320,21 +319,21 @@ static int hid_accel_3d_probe(struct platform_device *pdev)
return ret;
}
- channels = kmemdup(accel_3d_channels, sizeof(accel_3d_channels),
- GFP_KERNEL);
- if (!channels) {
+ indio_dev->channels = kmemdup(accel_3d_channels,
+ sizeof(accel_3d_channels), GFP_KERNEL);
+ if (!indio_dev->channels) {
dev_err(&pdev->dev, "failed to duplicate channels\n");
return -ENOMEM;
}
- ret = accel_3d_parse_report(pdev, hsdev, channels,
- HID_USAGE_SENSOR_ACCEL_3D, accel_state);
+ ret = accel_3d_parse_report(pdev, hsdev,
+ (struct iio_chan_spec *)indio_dev->channels,
+ HID_USAGE_SENSOR_ACCEL_3D, accel_state);
if (ret) {
dev_err(&pdev->dev, "failed to setup attributes\n");
goto error_free_dev_mem;
}
- indio_dev->channels = channels;
indio_dev->num_channels = ARRAY_SIZE(accel_3d_channels);
indio_dev->dev.parent = &pdev->dev;
indio_dev->info = &accel_3d_info;
@@ -400,7 +399,7 @@ static int hid_accel_3d_remove(struct platform_device *pdev)
return 0;
}
-static struct platform_device_id hid_accel_3d_ids[] = {
+static const struct platform_device_id hid_accel_3d_ids[] = {
{
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
.name = "HID-SENSOR-200073",
diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c
index 5b7a860df..0d9bd35ff 100644
--- a/drivers/iio/accel/kxcjk-1013.c
+++ b/drivers/iio/accel/kxcjk-1013.c
@@ -875,15 +875,18 @@ static int kxcjk1013_write_event_config(struct iio_dev *indio_dev,
return 0;
}
-static int kxcjk1013_validate_trigger(struct iio_dev *indio_dev,
- struct iio_trigger *trig)
+static int kxcjk1013_buffer_preenable(struct iio_dev *indio_dev)
{
struct kxcjk1013_data *data = iio_priv(indio_dev);
- if (data->dready_trig != trig && data->motion_trig != trig)
- return -EINVAL;
+ return kxcjk1013_set_power_state(data, true);
+}
- return 0;
+static int kxcjk1013_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct kxcjk1013_data *data = iio_priv(indio_dev);
+
+ return kxcjk1013_set_power_state(data, false);
}
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
@@ -935,6 +938,13 @@ static const struct iio_chan_spec kxcjk1013_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(3),
};
+static const struct iio_buffer_setup_ops kxcjk1013_buffer_setup_ops = {
+ .preenable = kxcjk1013_buffer_preenable,
+ .postenable = iio_triggered_buffer_postenable,
+ .postdisable = kxcjk1013_buffer_postdisable,
+ .predisable = iio_triggered_buffer_predisable,
+};
+
static const struct iio_info kxcjk1013_info = {
.attrs = &kxcjk1013_attrs_group,
.read_raw = kxcjk1013_read_raw,
@@ -943,7 +953,6 @@ static const struct iio_info kxcjk1013_info = {
.write_event_value = kxcjk1013_write_event,
.write_event_config = kxcjk1013_write_event_config,
.read_event_config = kxcjk1013_read_event_config,
- .validate_trigger = kxcjk1013_validate_trigger,
.driver_module = THIS_MODULE,
};
@@ -1147,8 +1156,10 @@ static const char *kxcjk1013_match_acpi_device(struct device *dev,
id = acpi_match_device(dev->driver->acpi_match_table, dev);
if (!id)
return NULL;
+
if (strcmp(id->id, "SMO8500") == 0)
*is_smo8500_device = true;
+
*chipset = (enum kx_chipset)id->driver_data;
return dev_name(dev);
@@ -1163,6 +1174,7 @@ static int kxcjk1013_gpio_probe(struct i2c_client *client,
if (!client)
return -EINVAL;
+
if (data->is_smo8500_device)
return -ENOTSUPP;
@@ -1276,16 +1288,15 @@ static int kxcjk1013_probe(struct i2c_client *client,
data->motion_trig = NULL;
goto err_trigger_unregister;
}
+ }
- ret = iio_triggered_buffer_setup(indio_dev,
- &iio_pollfunc_store_time,
- kxcjk1013_trigger_handler,
- NULL);
- if (ret < 0) {
- dev_err(&client->dev,
- "iio triggered buffer setup failed\n");
- goto err_trigger_unregister;
- }
+ ret = iio_triggered_buffer_setup(indio_dev,
+ &iio_pollfunc_store_time,
+ kxcjk1013_trigger_handler,
+ &kxcjk1013_buffer_setup_ops);
+ if (ret < 0) {
+ dev_err(&client->dev, "iio triggered buffer setup failed\n");
+ goto err_trigger_unregister;
}
ret = iio_device_register(indio_dev);
diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c
index 5b8065788..13ea1ea23 100644
--- a/drivers/iio/accel/mma8452.c
+++ b/drivers/iio/accel/mma8452.c
@@ -9,7 +9,7 @@
*
* 7-bit I2C slave address 0x1c/0x1d (pin selectable)
*
- * TODO: interrupt, thresholding, orientation / freefall events, autosleep
+ * TODO: orientation / freefall events, autosleep
*/
#include <linux/module.h>
@@ -18,20 +18,42 @@
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/events.h>
#include <linux/delay.h>
#define MMA8452_STATUS 0x00
#define MMA8452_OUT_X 0x01 /* MSB first, 12-bit */
#define MMA8452_OUT_Y 0x03
#define MMA8452_OUT_Z 0x05
+#define MMA8452_INT_SRC 0x0c
#define MMA8452_WHO_AM_I 0x0d
#define MMA8452_DATA_CFG 0x0e
+#define MMA8452_HP_FILTER_CUTOFF 0x0f
+#define MMA8452_HP_FILTER_CUTOFF_SEL_MASK (BIT(0) | BIT(1))
+#define MMA8452_TRANSIENT_CFG 0x1d
+#define MMA8452_TRANSIENT_CFG_ELE BIT(4)
+#define MMA8452_TRANSIENT_CFG_CHAN(chan) BIT(chan + 1)
+#define MMA8452_TRANSIENT_CFG_HPF_BYP BIT(0)
+#define MMA8452_TRANSIENT_SRC 0x1e
+#define MMA8452_TRANSIENT_SRC_XTRANSE BIT(1)
+#define MMA8452_TRANSIENT_SRC_YTRANSE BIT(3)
+#define MMA8452_TRANSIENT_SRC_ZTRANSE BIT(5)
+#define MMA8452_TRANSIENT_THS 0x1f
+#define MMA8452_TRANSIENT_THS_MASK 0x7f
+#define MMA8452_TRANSIENT_COUNT 0x20
#define MMA8452_OFF_X 0x2f
#define MMA8452_OFF_Y 0x30
#define MMA8452_OFF_Z 0x31
#define MMA8452_CTRL_REG1 0x2a
#define MMA8452_CTRL_REG2 0x2b
+#define MMA8452_CTRL_REG2_RST BIT(6)
+#define MMA8452_CTRL_REG4 0x2d
+#define MMA8452_CTRL_REG5 0x2e
+
+#define MMA8452_MAX_REG 0x31
#define MMA8452_STATUS_DRDY (BIT(2) | BIT(1) | BIT(0))
@@ -44,6 +66,10 @@
#define MMA8452_DATA_CFG_FS_2G 0
#define MMA8452_DATA_CFG_FS_4G 1
#define MMA8452_DATA_CFG_FS_8G 2
+#define MMA8452_DATA_CFG_HPF_MASK BIT(4)
+
+#define MMA8452_INT_DRDY BIT(0)
+#define MMA8452_INT_TRANS BIT(5)
#define MMA8452_DEVICE_ID 0x2a
@@ -106,6 +132,12 @@ static int mma8452_get_int_plus_micros_index(const int (*vals)[2], int n,
return -EINVAL;
}
+static int mma8452_get_odr_index(struct mma8452_data *data)
+{
+ return (data->ctrl_reg1 & MMA8452_CTRL_DR_MASK) >>
+ MMA8452_CTRL_DR_SHIFT;
+}
+
static const int mma8452_samp_freq[8][2] = {
{800, 0}, {400, 0}, {200, 0}, {100, 0}, {50, 0}, {12, 500000},
{6, 250000}, {1, 560000}
@@ -121,6 +153,30 @@ static const int mma8452_scales[3][2] = {
{0, 9577}, {0, 19154}, {0, 38307}
};
+/* Datasheet table 35 (step time vs sample frequency) */
+static const int mma8452_transient_time_step_us[8] = {
+ 1250,
+ 2500,
+ 5000,
+ 10000,
+ 20000,
+ 20000,
+ 20000,
+ 20000
+};
+
+/* Datasheet table 18 (normal mode) */
+static const int mma8452_hp_filter_cutoff[8][4][2] = {
+ { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, /* 800 Hz sample */
+ { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, /* 400 Hz sample */
+ { {8, 0}, {4, 0}, {2, 0}, {1, 0} }, /* 200 Hz sample */
+ { {4, 0}, {2, 0}, {1, 0}, {0, 500000} }, /* 100 Hz sample */
+ { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, /* 50 Hz sample */
+ { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, /* 12.5 Hz sample */
+ { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, /* 6.25 Hz sample */
+ { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} } /* 1.56 Hz sample */
+};
+
static ssize_t mma8452_show_samp_freq_avail(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -135,9 +191,23 @@ static ssize_t mma8452_show_scale_avail(struct device *dev,
ARRAY_SIZE(mma8452_scales));
}
+static ssize_t mma8452_show_hp_cutoff_avail(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct mma8452_data *data = iio_priv(indio_dev);
+ int i = mma8452_get_odr_index(data);
+
+ return mma8452_show_int_plus_micros(buf, mma8452_hp_filter_cutoff[i],
+ ARRAY_SIZE(mma8452_hp_filter_cutoff[0]));
+}
+
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(mma8452_show_samp_freq_avail);
static IIO_DEVICE_ATTR(in_accel_scale_available, S_IRUGO,
mma8452_show_scale_avail, NULL, 0);
+static IIO_DEVICE_ATTR(in_accel_filter_high_pass_3db_frequency_available,
+ S_IRUGO, mma8452_show_hp_cutoff_avail, NULL, 0);
static int mma8452_get_samp_freq_index(struct mma8452_data *data,
int val, int val2)
@@ -153,6 +223,31 @@ static int mma8452_get_scale_index(struct mma8452_data *data,
ARRAY_SIZE(mma8452_scales), val, val2);
}
+static int mma8452_get_hp_filter_index(struct mma8452_data *data,
+ int val, int val2)
+{
+ int i = mma8452_get_odr_index(data);
+
+ return mma8452_get_int_plus_micros_index(mma8452_hp_filter_cutoff[i],
+ ARRAY_SIZE(mma8452_scales[0]), val, val2);
+}
+
+static int mma8452_read_hp_filter(struct mma8452_data *data, int *hz, int *uHz)
+{
+ int i, ret;
+
+ ret = i2c_smbus_read_byte_data(data->client, MMA8452_HP_FILTER_CUTOFF);
+ if (ret < 0)
+ return ret;
+
+ i = mma8452_get_odr_index(data);
+ ret &= MMA8452_HP_FILTER_CUTOFF_SEL_MASK;
+ *hz = mma8452_hp_filter_cutoff[i][ret][0];
+ *uHz = mma8452_hp_filter_cutoff[i][ret][1];
+
+ return 0;
+}
+
static int mma8452_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
@@ -180,8 +275,7 @@ static int mma8452_read_raw(struct iio_dev *indio_dev,
*val2 = mma8452_scales[i][1];
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_SAMP_FREQ:
- i = (data->ctrl_reg1 & MMA8452_CTRL_DR_MASK) >>
- MMA8452_CTRL_DR_SHIFT;
+ i = mma8452_get_odr_index(data);
*val = mma8452_samp_freq[i][0];
*val2 = mma8452_samp_freq[i][1];
return IIO_VAL_INT_PLUS_MICRO;
@@ -192,6 +286,16 @@ static int mma8452_read_raw(struct iio_dev *indio_dev,
return ret;
*val = sign_extend32(ret, 7);
return IIO_VAL_INT;
+ case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
+ if (data->data_cfg & MMA8452_DATA_CFG_HPF_MASK) {
+ ret = mma8452_read_hp_filter(data, val, val2);
+ if (ret < 0)
+ return ret;
+ } else {
+ *val = 0;
+ *val2 = 0;
+ }
+ return IIO_VAL_INT_PLUS_MICRO;
}
return -EINVAL;
}
@@ -233,12 +337,31 @@ fail:
return ret;
}
+static int mma8452_set_hp_filter_frequency(struct mma8452_data *data,
+ int val, int val2)
+{
+ int i, reg;
+
+ i = mma8452_get_hp_filter_index(data, val, val2);
+ if (i < 0)
+ return -EINVAL;
+
+ reg = i2c_smbus_read_byte_data(data->client,
+ MMA8452_HP_FILTER_CUTOFF);
+ if (reg < 0)
+ return reg;
+ reg &= ~MMA8452_HP_FILTER_CUTOFF_SEL_MASK;
+ reg |= i;
+
+ return mma8452_change_config(data, MMA8452_HP_FILTER_CUTOFF, reg);
+}
+
static int mma8452_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct mma8452_data *data = iio_priv(indio_dev);
- int i;
+ int i, ret;
if (iio_buffer_enabled(indio_dev))
return -EBUSY;
@@ -266,11 +389,217 @@ static int mma8452_write_raw(struct iio_dev *indio_dev,
return -EINVAL;
return mma8452_change_config(data, MMA8452_OFF_X +
chan->scan_index, val);
+
+ case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
+ if (val == 0 && val2 == 0) {
+ data->data_cfg &= ~MMA8452_DATA_CFG_HPF_MASK;
+ } else {
+ data->data_cfg |= MMA8452_DATA_CFG_HPF_MASK;
+ ret = mma8452_set_hp_filter_frequency(data, val, val2);
+ if (ret < 0)
+ return ret;
+ }
+ return mma8452_change_config(data, MMA8452_DATA_CFG,
+ data->data_cfg);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mma8452_read_thresh(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 mma8452_data *data = iio_priv(indio_dev);
+ int ret, us;
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ ret = i2c_smbus_read_byte_data(data->client,
+ MMA8452_TRANSIENT_THS);
+ if (ret < 0)
+ return ret;
+
+ *val = ret & MMA8452_TRANSIENT_THS_MASK;
+ return IIO_VAL_INT;
+
+ case IIO_EV_INFO_PERIOD:
+ ret = i2c_smbus_read_byte_data(data->client,
+ MMA8452_TRANSIENT_COUNT);
+ if (ret < 0)
+ return ret;
+
+ us = ret * mma8452_transient_time_step_us[
+ mma8452_get_odr_index(data)];
+ *val = us / USEC_PER_SEC;
+ *val2 = us % USEC_PER_SEC;
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ case IIO_EV_INFO_HIGH_PASS_FILTER_3DB:
+ ret = i2c_smbus_read_byte_data(data->client,
+ MMA8452_TRANSIENT_CFG);
+ if (ret < 0)
+ return ret;
+
+ if (ret & MMA8452_TRANSIENT_CFG_HPF_BYP) {
+ *val = 0;
+ *val2 = 0;
+ } else {
+ ret = mma8452_read_hp_filter(data, val, val2);
+ if (ret < 0)
+ return ret;
+ }
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mma8452_write_thresh(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 mma8452_data *data = iio_priv(indio_dev);
+ int ret, reg, steps;
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ return mma8452_change_config(data, MMA8452_TRANSIENT_THS,
+ val & MMA8452_TRANSIENT_THS_MASK);
+
+ case IIO_EV_INFO_PERIOD:
+ steps = (val * USEC_PER_SEC + val2) /
+ mma8452_transient_time_step_us[
+ mma8452_get_odr_index(data)];
+
+ if (steps > 0xff)
+ return -EINVAL;
+
+ return mma8452_change_config(data, MMA8452_TRANSIENT_COUNT,
+ steps);
+ case IIO_EV_INFO_HIGH_PASS_FILTER_3DB:
+ reg = i2c_smbus_read_byte_data(data->client,
+ MMA8452_TRANSIENT_CFG);
+ if (reg < 0)
+ return reg;
+
+ if (val == 0 && val2 == 0) {
+ reg |= MMA8452_TRANSIENT_CFG_HPF_BYP;
+ } else {
+ reg &= ~MMA8452_TRANSIENT_CFG_HPF_BYP;
+ ret = mma8452_set_hp_filter_frequency(data, val, val2);
+ if (ret < 0)
+ return ret;
+ }
+ return mma8452_change_config(data, MMA8452_TRANSIENT_CFG, reg);
+
default:
return -EINVAL;
}
}
+static int mma8452_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ struct mma8452_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_CFG);
+ if (ret < 0)
+ return ret;
+
+ return ret & MMA8452_TRANSIENT_CFG_CHAN(chan->scan_index) ? 1 : 0;
+}
+
+static int mma8452_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)
+{
+ struct mma8452_data *data = iio_priv(indio_dev);
+ int val;
+
+ val = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_CFG);
+ if (val < 0)
+ return val;
+
+ if (state)
+ val |= MMA8452_TRANSIENT_CFG_CHAN(chan->scan_index);
+ else
+ val &= ~MMA8452_TRANSIENT_CFG_CHAN(chan->scan_index);
+
+ val |= MMA8452_TRANSIENT_CFG_ELE;
+
+ return mma8452_change_config(data, MMA8452_TRANSIENT_CFG, val);
+}
+
+static void mma8452_transient_interrupt(struct iio_dev *indio_dev)
+{
+ struct mma8452_data *data = iio_priv(indio_dev);
+ s64 ts = iio_get_time_ns();
+ int src;
+
+ src = i2c_smbus_read_byte_data(data->client, MMA8452_TRANSIENT_SRC);
+ if (src < 0)
+ return;
+
+ if (src & MMA8452_TRANSIENT_SRC_XTRANSE)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X,
+ IIO_EV_TYPE_MAG,
+ IIO_EV_DIR_RISING),
+ ts);
+
+ if (src & MMA8452_TRANSIENT_SRC_YTRANSE)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_Y,
+ IIO_EV_TYPE_MAG,
+ IIO_EV_DIR_RISING),
+ ts);
+
+ if (src & MMA8452_TRANSIENT_SRC_ZTRANSE)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_Z,
+ IIO_EV_TYPE_MAG,
+ IIO_EV_DIR_RISING),
+ ts);
+}
+
+static irqreturn_t mma8452_interrupt(int irq, void *p)
+{
+ struct iio_dev *indio_dev = p;
+ struct mma8452_data *data = iio_priv(indio_dev);
+ int ret = IRQ_NONE;
+ int src;
+
+ src = i2c_smbus_read_byte_data(data->client, MMA8452_INT_SRC);
+ if (src < 0)
+ return IRQ_NONE;
+
+ if (src & MMA8452_INT_DRDY) {
+ iio_trigger_poll_chained(indio_dev->trig);
+ ret = IRQ_HANDLED;
+ }
+
+ if (src & MMA8452_INT_TRANS) {
+ mma8452_transient_interrupt(indio_dev);
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
static irqreturn_t mma8452_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
@@ -291,6 +620,55 @@ done:
return IRQ_HANDLED;
}
+static int mma8452_reg_access_dbg(struct iio_dev *indio_dev,
+ unsigned reg, unsigned writeval,
+ unsigned *readval)
+{
+ int ret;
+ struct mma8452_data *data = iio_priv(indio_dev);
+
+ if (reg > MMA8452_MAX_REG)
+ return -EINVAL;
+
+ if (!readval)
+ return mma8452_change_config(data, reg, writeval);
+
+ ret = i2c_smbus_read_byte_data(data->client, reg);
+ if (ret < 0)
+ return ret;
+
+ *readval = ret;
+
+ return 0;
+}
+
+static const struct iio_event_spec mma8452_transient_event[] = {
+ {
+ .type = IIO_EV_TYPE_MAG,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+ .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_PERIOD) |
+ BIT(IIO_EV_INFO_HIGH_PASS_FILTER_3DB)
+ },
+};
+
+/*
+ * Threshold is configured in fixed 8G/127 steps regardless of
+ * currently selected scale for measurement.
+ */
+static IIO_CONST_ATTR_NAMED(accel_transient_scale, in_accel_scale, "0.617742");
+
+static struct attribute *mma8452_event_attributes[] = {
+ &iio_const_attr_accel_transient_scale.dev_attr.attr,
+ NULL,
+};
+
+static struct attribute_group mma8452_event_attribute_group = {
+ .attrs = mma8452_event_attributes,
+ .name = "events",
+};
+
#define MMA8452_CHANNEL(axis, idx) { \
.type = IIO_ACCEL, \
.modified = 1, \
@@ -298,7 +676,8 @@ done:
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_CALIBBIAS), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
- BIT(IIO_CHAN_INFO_SCALE), \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \
.scan_index = idx, \
.scan_type = { \
.sign = 's', \
@@ -307,6 +686,8 @@ done:
.shift = 4, \
.endianness = IIO_BE, \
}, \
+ .event_spec = mma8452_transient_event, \
+ .num_event_specs = ARRAY_SIZE(mma8452_transient_event), \
}
static const struct iio_chan_spec mma8452_channels[] = {
@@ -319,6 +700,7 @@ static const struct iio_chan_spec mma8452_channels[] = {
static struct attribute *mma8452_attributes[] = {
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
&iio_dev_attr_in_accel_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_accel_filter_high_pass_3db_frequency_available.dev_attr.attr,
NULL
};
@@ -330,11 +712,107 @@ static const struct iio_info mma8452_info = {
.attrs = &mma8452_group,
.read_raw = &mma8452_read_raw,
.write_raw = &mma8452_write_raw,
+ .event_attrs = &mma8452_event_attribute_group,
+ .read_event_value = &mma8452_read_thresh,
+ .write_event_value = &mma8452_write_thresh,
+ .read_event_config = &mma8452_read_event_config,
+ .write_event_config = &mma8452_write_event_config,
+ .debugfs_reg_access = &mma8452_reg_access_dbg,
.driver_module = THIS_MODULE,
};
static const unsigned long mma8452_scan_masks[] = {0x7, 0};
+static int mma8452_data_rdy_trigger_set_state(struct iio_trigger *trig,
+ bool state)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct mma8452_data *data = iio_priv(indio_dev);
+ int reg;
+
+ reg = i2c_smbus_read_byte_data(data->client, MMA8452_CTRL_REG4);
+ if (reg < 0)
+ return reg;
+
+ if (state)
+ reg |= MMA8452_INT_DRDY;
+ else
+ reg &= ~MMA8452_INT_DRDY;
+
+ return mma8452_change_config(data, MMA8452_CTRL_REG4, reg);
+}
+
+static int mma8452_validate_device(struct iio_trigger *trig,
+ struct iio_dev *indio_dev)
+{
+ struct iio_dev *indio = iio_trigger_get_drvdata(trig);
+
+ if (indio != indio_dev)
+ return -EINVAL;
+
+ return 0;
+}
+
+static const struct iio_trigger_ops mma8452_trigger_ops = {
+ .set_trigger_state = mma8452_data_rdy_trigger_set_state,
+ .validate_device = mma8452_validate_device,
+ .owner = THIS_MODULE,
+};
+
+static int mma8452_trigger_setup(struct iio_dev *indio_dev)
+{
+ struct mma8452_data *data = iio_priv(indio_dev);
+ struct iio_trigger *trig;
+ int ret;
+
+ trig = devm_iio_trigger_alloc(&data->client->dev, "%s-dev%d",
+ indio_dev->name,
+ indio_dev->id);
+ if (!trig)
+ return -ENOMEM;
+
+ trig->dev.parent = &data->client->dev;
+ trig->ops = &mma8452_trigger_ops;
+ iio_trigger_set_drvdata(trig, indio_dev);
+
+ ret = iio_trigger_register(trig);
+ if (ret)
+ return ret;
+
+ indio_dev->trig = trig;
+ return 0;
+}
+
+static void mma8452_trigger_cleanup(struct iio_dev *indio_dev)
+{
+ if (indio_dev->trig)
+ iio_trigger_unregister(indio_dev->trig);
+}
+
+static int mma8452_reset(struct i2c_client *client)
+{
+ int i;
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG2,
+ MMA8452_CTRL_REG2_RST);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < 10; i++) {
+ usleep_range(100, 200);
+ ret = i2c_smbus_read_byte_data(client, MMA8452_CTRL_REG2);
+ if (ret == -EIO)
+ continue; /* I2C comm reset */
+ if (ret < 0)
+ return ret;
+ if (!(ret & MMA8452_CTRL_REG2_RST))
+ return 0;
+ }
+
+ return -ETIMEDOUT;
+}
+
static int mma8452_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -365,10 +843,7 @@ static int mma8452_probe(struct i2c_client *client,
indio_dev->num_channels = ARRAY_SIZE(mma8452_channels);
indio_dev->available_scan_masks = mma8452_scan_masks;
- data->ctrl_reg1 = MMA8452_CTRL_ACTIVE |
- (MMA8452_CTRL_DR_DEFAULT << MMA8452_CTRL_DR_SHIFT);
- ret = i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG1,
- data->ctrl_reg1);
+ ret = mma8452_reset(client);
if (ret < 0)
return ret;
@@ -378,18 +853,77 @@ static int mma8452_probe(struct i2c_client *client,
if (ret < 0)
return ret;
+ /*
+ * By default set transient threshold to max to avoid events if
+ * enabling without configuring threshold.
+ */
+ ret = i2c_smbus_write_byte_data(client, MMA8452_TRANSIENT_THS,
+ MMA8452_TRANSIENT_THS_MASK);
+ if (ret < 0)
+ return ret;
+
+ if (client->irq) {
+ /*
+ * Although we enable the transient interrupt source once and
+ * for all here the transient event detection itself is not
+ * enabled until userspace asks for it by
+ * mma8452_write_event_config()
+ */
+ int supported_interrupts = MMA8452_INT_DRDY | MMA8452_INT_TRANS;
+ int enabled_interrupts = MMA8452_INT_TRANS;
+
+ /* Assume wired to INT1 pin */
+ ret = i2c_smbus_write_byte_data(client,
+ MMA8452_CTRL_REG5,
+ supported_interrupts);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(client,
+ MMA8452_CTRL_REG4,
+ enabled_interrupts);
+ if (ret < 0)
+ return ret;
+
+ ret = mma8452_trigger_setup(indio_dev);
+ if (ret < 0)
+ return ret;
+ }
+
+ data->ctrl_reg1 = MMA8452_CTRL_ACTIVE |
+ (MMA8452_CTRL_DR_DEFAULT << MMA8452_CTRL_DR_SHIFT);
+ ret = i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG1,
+ data->ctrl_reg1);
+ if (ret < 0)
+ goto trigger_cleanup;
+
ret = iio_triggered_buffer_setup(indio_dev, NULL,
mma8452_trigger_handler, NULL);
if (ret < 0)
- return ret;
+ goto trigger_cleanup;
+
+ if (client->irq) {
+ ret = devm_request_threaded_irq(&client->dev,
+ client->irq,
+ NULL, mma8452_interrupt,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ client->name, indio_dev);
+ if (ret)
+ goto buffer_cleanup;
+ }
ret = iio_device_register(indio_dev);
if (ret < 0)
goto buffer_cleanup;
+
return 0;
buffer_cleanup:
iio_triggered_buffer_cleanup(indio_dev);
+
+trigger_cleanup:
+ mma8452_trigger_cleanup(indio_dev);
+
return ret;
}
@@ -399,6 +933,7 @@ static int mma8452_remove(struct i2c_client *client)
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
+ mma8452_trigger_cleanup(indio_dev);
mma8452_standby(iio_priv(indio_dev));
return 0;
diff --git a/drivers/iio/accel/mma9551_core.c b/drivers/iio/accel/mma9551_core.c
index c6d5a3a40..2fd2a9956 100644
--- a/drivers/iio/accel/mma9551_core.c
+++ b/drivers/iio/accel/mma9551_core.c
@@ -374,7 +374,7 @@ EXPORT_SYMBOL(mma9551_read_status_word);
* @app_id: Application ID
* @reg: Application register
* @len: Length of array to read in bytes
- * @val: Array of words to read
+ * @buf: Array of words to read
*
* Read multiple configuration registers (word-sized registers).
*
@@ -414,7 +414,7 @@ EXPORT_SYMBOL(mma9551_read_config_words);
* @app_id: Application ID
* @reg: Application register
* @len: Length of array to read in bytes
- * @val: Array of words to read
+ * @buf: Array of words to read
*
* Read multiple status registers (word-sized registers).
*
@@ -454,7 +454,7 @@ EXPORT_SYMBOL(mma9551_read_status_words);
* @app_id: Application ID
* @reg: Application register
* @len: Length of array to write in bytes
- * @val: Array of words to write
+ * @buf: Array of words to write
*
* Write multiple configuration registers (word-sized registers).
*
@@ -800,7 +800,7 @@ EXPORT_SYMBOL(mma9551_read_accel_scale);
*/
int mma9551_app_reset(struct i2c_client *client, u32 app_mask)
{
- return mma9551_write_config_byte(client, MMA9551_APPID_RCS,
+ return mma9551_write_config_byte(client, MMA9551_APPID_RSC,
MMA9551_RSC_RESET +
MMA9551_RSC_OFFSET(app_mask),
MMA9551_RSC_VAL(app_mask));
diff --git a/drivers/iio/accel/mma9551_core.h b/drivers/iio/accel/mma9551_core.h
index edaa56b10..79939e408 100644
--- a/drivers/iio/accel/mma9551_core.h
+++ b/drivers/iio/accel/mma9551_core.h
@@ -22,7 +22,7 @@
#define MMA9551_APPID_TILT 0x0B
#define MMA9551_APPID_SLEEP_WAKE 0x12
#define MMA9551_APPID_PEDOMETER 0x15
-#define MMA9551_APPID_RCS 0x17
+#define MMA9551_APPID_RSC 0x17
#define MMA9551_APPID_NONE 0xff
/* Reset/Suspend/Clear application app masks */
diff --git a/drivers/iio/accel/mma9553.c b/drivers/iio/accel/mma9553.c
index 365a109aa..8bfc61824 100644
--- a/drivers/iio/accel/mma9553.c
+++ b/drivers/iio/accel/mma9553.c
@@ -63,8 +63,8 @@
#define MMA9553_MASK_STATUS_STEPCHG BIT(13)
#define MMA9553_MASK_STATUS_ACTCHG BIT(12)
#define MMA9553_MASK_STATUS_SUSP BIT(11)
-#define MMA9553_MASK_STATUS_ACTIVITY (BIT(10) | BIT(9) | BIT(8))
-#define MMA9553_MASK_STATUS_VERSION 0x00FF
+#define MMA9553_MASK_STATUS_ACTIVITY GENMASK(10, 8)
+#define MMA9553_MASK_STATUS_VERSION GENMASK(7, 0)
#define MMA9553_REG_STEPCNT 0x02
#define MMA9553_REG_DISTANCE 0x04
@@ -76,14 +76,15 @@
#define MMA9553_DEFAULT_GPIO_PIN mma9551_gpio6
#define MMA9553_DEFAULT_GPIO_POLARITY 0
-/* Bitnum used for gpio configuration = bit number in high status byte */
-#define STATUS_TO_BITNUM(bit) (ffs(bit) - 9)
+/* Bitnum used for GPIO configuration = bit number in high status byte */
+#define MMA9553_STATUS_TO_BITNUM(bit) (ffs(bit) - 9)
+#define MMA9553_MAX_BITNUM MMA9553_STATUS_TO_BITNUM(BIT(16))
#define MMA9553_DEFAULT_SAMPLE_RATE 30 /* Hz */
/*
* The internal activity level must be stable for ACTTHD samples before
- * ACTIVITY is updated.The ACTIVITY variable contains the current activity
+ * ACTIVITY is updated. The ACTIVITY variable contains the current activity
* level and is updated every time a step is detected or once a second
* if there are no steps.
*/
@@ -351,11 +352,11 @@ static int mma9553_conf_gpio(struct mma9553_data *data)
* This bit is the logical OR of the SUSPCHG, STEPCHG, and ACTCHG flags.
*/
if (activity_enabled && ev_step_detect->enabled)
- bitnum = STATUS_TO_BITNUM(MMA9553_MASK_STATUS_MRGFL);
+ bitnum = MMA9553_STATUS_TO_BITNUM(MMA9553_MASK_STATUS_MRGFL);
else if (ev_step_detect->enabled)
- bitnum = STATUS_TO_BITNUM(MMA9553_MASK_STATUS_STEPCHG);
+ bitnum = MMA9553_STATUS_TO_BITNUM(MMA9553_MASK_STATUS_STEPCHG);
else if (activity_enabled)
- bitnum = STATUS_TO_BITNUM(MMA9553_MASK_STATUS_ACTCHG);
+ bitnum = MMA9553_STATUS_TO_BITNUM(MMA9553_MASK_STATUS_ACTCHG);
else /* Reset */
appid = MMA9551_APPID_NONE;
@@ -363,9 +364,12 @@ static int mma9553_conf_gpio(struct mma9553_data *data)
return 0;
/* Save initial values for activity and stepcnt */
- if (activity_enabled || ev_step_detect->enabled)
- mma9553_read_activity_stepcnt(data, &data->activity,
- &data->stepcnt);
+ if (activity_enabled || ev_step_detect->enabled) {
+ ret = mma9553_read_activity_stepcnt(data, &data->activity,
+ &data->stepcnt);
+ if (ret < 0)
+ return ret;
+ }
ret = mma9551_gpio_config(data->client,
MMA9553_DEFAULT_GPIO_PIN,
@@ -396,13 +400,13 @@ static int mma9553_init(struct mma9553_data *data)
sizeof(data->conf), (u16 *) &data->conf);
if (ret < 0) {
dev_err(&data->client->dev,
- "device is not MMA9553L: failed to read cfg regs\n");
+ "failed to read configuration registers\n");
return ret;
}
- /* Reset gpio */
- data->gpio_bitnum = -1;
+ /* Reset GPIO */
+ data->gpio_bitnum = MMA9553_MAX_BITNUM;
ret = mma9553_conf_gpio(data);
if (ret < 0)
return ret;
@@ -436,6 +440,32 @@ static int mma9553_init(struct mma9553_data *data)
return mma9551_set_device_state(data->client, true);
}
+static int mma9553_read_status_word(struct mma9553_data *data, u16 reg,
+ u16 *tmp)
+{
+ bool powered_on;
+ int ret;
+
+ /*
+ * The HW only counts steps and other dependent
+ * parameters (speed, distance, calories, activity)
+ * if power is on (from enabling an event or the
+ * step counter).
+ */
+ powered_on = mma9553_is_any_event_enabled(data, false, 0) ||
+ data->stepcnt_enabled;
+ if (!powered_on) {
+ dev_err(&data->client->dev, "No channels enabled\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&data->mutex);
+ ret = mma9551_read_status_word(data->client, MMA9551_APPID_PEDOMETER,
+ reg, tmp);
+ mutex_unlock(&data->mutex);
+ return ret;
+}
+
static int mma9553_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
@@ -444,69 +474,30 @@ static int mma9553_read_raw(struct iio_dev *indio_dev,
int ret;
u16 tmp;
u8 activity;
- bool powered_on;
switch (mask) {
case IIO_CHAN_INFO_PROCESSED:
switch (chan->type) {
case IIO_STEPS:
- /*
- * The HW only counts steps and other dependent
- * parameters (speed, distance, calories, activity)
- * if power is on (from enabling an event or the
- * step counter */
- powered_on =
- mma9553_is_any_event_enabled(data, false, 0) ||
- data->stepcnt_enabled;
- if (!powered_on) {
- dev_err(&data->client->dev,
- "No channels enabled\n");
- return -EINVAL;
- }
- mutex_lock(&data->mutex);
- ret = mma9551_read_status_word(data->client,
- MMA9551_APPID_PEDOMETER,
+ ret = mma9553_read_status_word(data,
MMA9553_REG_STEPCNT,
&tmp);
- mutex_unlock(&data->mutex);
if (ret < 0)
return ret;
*val = tmp;
return IIO_VAL_INT;
case IIO_DISTANCE:
- powered_on =
- mma9553_is_any_event_enabled(data, false, 0) ||
- data->stepcnt_enabled;
- if (!powered_on) {
- dev_err(&data->client->dev,
- "No channels enabled\n");
- return -EINVAL;
- }
- mutex_lock(&data->mutex);
- ret = mma9551_read_status_word(data->client,
- MMA9551_APPID_PEDOMETER,
+ ret = mma9553_read_status_word(data,
MMA9553_REG_DISTANCE,
&tmp);
- mutex_unlock(&data->mutex);
if (ret < 0)
return ret;
*val = tmp;
return IIO_VAL_INT;
case IIO_ACTIVITY:
- powered_on =
- mma9553_is_any_event_enabled(data, false, 0) ||
- data->stepcnt_enabled;
- if (!powered_on) {
- dev_err(&data->client->dev,
- "No channels enabled\n");
- return -EINVAL;
- }
- mutex_lock(&data->mutex);
- ret = mma9551_read_status_word(data->client,
- MMA9551_APPID_PEDOMETER,
+ ret = mma9553_read_status_word(data,
MMA9553_REG_STATUS,
&tmp);
- mutex_unlock(&data->mutex);
if (ret < 0)
return ret;
@@ -531,38 +522,17 @@ static int mma9553_read_raw(struct iio_dev *indio_dev,
case IIO_VELOCITY: /* m/h */
if (chan->channel2 != IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
return -EINVAL;
- powered_on =
- mma9553_is_any_event_enabled(data, false, 0) ||
- data->stepcnt_enabled;
- if (!powered_on) {
- dev_err(&data->client->dev,
- "No channels enabled\n");
- return -EINVAL;
- }
- mutex_lock(&data->mutex);
- ret = mma9551_read_status_word(data->client,
- MMA9551_APPID_PEDOMETER,
- MMA9553_REG_SPEED, &tmp);
- mutex_unlock(&data->mutex);
+ ret = mma9553_read_status_word(data,
+ MMA9553_REG_SPEED,
+ &tmp);
if (ret < 0)
return ret;
*val = tmp;
return IIO_VAL_INT;
case IIO_ENERGY: /* Cal or kcal */
- powered_on =
- mma9553_is_any_event_enabled(data, false, 0) ||
- data->stepcnt_enabled;
- if (!powered_on) {
- dev_err(&data->client->dev,
- "No channels enabled\n");
- return -EINVAL;
- }
- mutex_lock(&data->mutex);
- ret = mma9551_read_status_word(data->client,
- MMA9551_APPID_PEDOMETER,
+ ret = mma9553_read_status_word(data,
MMA9553_REG_CALORIES,
&tmp);
- mutex_unlock(&data->mutex);
if (ret < 0)
return ret;
*val = tmp;
@@ -789,7 +759,7 @@ static int mma9553_write_event_config(struct iio_dev *indio_dev,
mutex_unlock(&data->mutex);
- return ret;
+ return 0;
err_conf_gpio:
if (state) {
@@ -897,7 +867,7 @@ static int mma9553_get_calibgender_mode(struct iio_dev *indio_dev,
gender = mma9553_get_bits(data->conf.filter, MMA9553_MASK_CONF_MALE);
/*
* HW expects 0 for female and 1 for male,
- * while iio index is 0 for male and 1 for female
+ * while iio index is 0 for male and 1 for female.
*/
return !gender;
}
@@ -944,11 +914,11 @@ static const struct iio_event_spec mma9553_activity_events[] = {
},
};
-static const char * const calibgender_modes[] = { "male", "female" };
+static const char * const mma9553_calibgender_modes[] = { "male", "female" };
static const struct iio_enum mma9553_calibgender_enum = {
- .items = calibgender_modes,
- .num_items = ARRAY_SIZE(calibgender_modes),
+ .items = mma9553_calibgender_modes,
+ .num_items = ARRAY_SIZE(mma9553_calibgender_modes),
.get = mma9553_get_calibgender_mode,
.set = mma9553_set_calibgender_mode,
};
@@ -1110,16 +1080,16 @@ static int mma9553_gpio_probe(struct i2c_client *client)
dev = &client->dev;
- /* data ready gpio interrupt pin */
+ /* data ready GPIO interrupt pin */
gpio = devm_gpiod_get_index(dev, MMA9553_GPIO_NAME, 0, GPIOD_IN);
if (IS_ERR(gpio)) {
- dev_err(dev, "acpi gpio get index failed\n");
+ dev_err(dev, "ACPI GPIO get index failed\n");
return PTR_ERR(gpio);
}
ret = gpiod_to_irq(gpio);
- dev_dbg(dev, "gpio resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
+ dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
return ret;
}
diff --git a/drivers/iio/accel/st_accel.h b/drivers/iio/accel/st_accel.h
index 7ee9724b1..aa1001931 100644
--- a/drivers/iio/accel/st_accel.h
+++ b/drivers/iio/accel/st_accel.h
@@ -20,6 +20,7 @@
#define LSM330D_ACCEL_DEV_NAME "lsm330d_accel"
#define LSM330DL_ACCEL_DEV_NAME "lsm330dl_accel"
#define LSM330DLC_ACCEL_DEV_NAME "lsm330dlc_accel"
+#define LIS331DL_ACCEL_DEV_NAME "lis331dl_accel"
#define LIS331DLH_ACCEL_DEV_NAME "lis331dlh"
#define LSM303DL_ACCEL_DEV_NAME "lsm303dl_accel"
#define LSM303DLH_ACCEL_DEV_NAME "lsm303dlh_accel"
diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c
index 211b13271..4002e6410 100644
--- a/drivers/iio/accel/st_accel_core.c
+++ b/drivers/iio/accel/st_accel_core.c
@@ -153,6 +153,44 @@
#define ST_ACCEL_4_IG1_EN_MASK 0x08
#define ST_ACCEL_4_MULTIREAD_BIT true
+/* CUSTOM VALUES FOR SENSOR 5 */
+#define ST_ACCEL_5_WAI_EXP 0x3b
+#define ST_ACCEL_5_ODR_ADDR 0x20
+#define ST_ACCEL_5_ODR_MASK 0x80
+#define ST_ACCEL_5_ODR_AVL_100HZ_VAL 0x00
+#define ST_ACCEL_5_ODR_AVL_400HZ_VAL 0x01
+#define ST_ACCEL_5_PW_ADDR 0x20
+#define ST_ACCEL_5_PW_MASK 0x40
+#define ST_ACCEL_5_FS_ADDR 0x20
+#define ST_ACCEL_5_FS_MASK 0x20
+#define ST_ACCEL_5_FS_AVL_2_VAL 0X00
+#define ST_ACCEL_5_FS_AVL_8_VAL 0X01
+/* TODO: check these resulting gain settings, these are not in the datsheet */
+#define ST_ACCEL_5_FS_AVL_2_GAIN IIO_G_TO_M_S_2(18000)
+#define ST_ACCEL_5_FS_AVL_8_GAIN IIO_G_TO_M_S_2(72000)
+#define ST_ACCEL_5_DRDY_IRQ_ADDR 0x22
+#define ST_ACCEL_5_DRDY_IRQ_INT1_MASK 0x04
+#define ST_ACCEL_5_DRDY_IRQ_INT2_MASK 0x20
+#define ST_ACCEL_5_IG1_EN_ADDR 0x21
+#define ST_ACCEL_5_IG1_EN_MASK 0x08
+#define ST_ACCEL_5_MULTIREAD_BIT false
+
+static const struct iio_chan_spec st_accel_8bit_channels[] = {
+ ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 8, 8,
+ ST_ACCEL_DEFAULT_OUT_X_L_ADDR+1),
+ ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 8, 8,
+ ST_ACCEL_DEFAULT_OUT_Y_L_ADDR+1),
+ ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 8, 8,
+ ST_ACCEL_DEFAULT_OUT_Z_L_ADDR+1),
+ IIO_CHAN_SOFT_TIMESTAMP(3)
+};
+
static const struct iio_chan_spec st_accel_12bit_channels[] = {
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
@@ -454,6 +492,54 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
.multi_read_bit = ST_ACCEL_4_MULTIREAD_BIT,
.bootime = 2, /* guess */
},
+ {
+ .wai = ST_ACCEL_5_WAI_EXP,
+ .sensors_supported = {
+ [0] = LIS331DL_ACCEL_DEV_NAME,
+ },
+ .ch = (struct iio_chan_spec *)st_accel_8bit_channels,
+ .odr = {
+ .addr = ST_ACCEL_5_ODR_ADDR,
+ .mask = ST_ACCEL_5_ODR_MASK,
+ .odr_avl = {
+ { 100, ST_ACCEL_5_ODR_AVL_100HZ_VAL },
+ { 400, ST_ACCEL_5_ODR_AVL_400HZ_VAL, },
+ },
+ },
+ .pw = {
+ .addr = ST_ACCEL_5_PW_ADDR,
+ .mask = ST_ACCEL_5_PW_MASK,
+ .value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
+ .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
+ },
+ .enable_axis = {
+ .addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
+ .mask = ST_SENSORS_DEFAULT_AXIS_MASK,
+ },
+ .fs = {
+ .addr = ST_ACCEL_5_FS_ADDR,
+ .mask = ST_ACCEL_5_FS_MASK,
+ .fs_avl = {
+ [0] = {
+ .num = ST_ACCEL_FS_AVL_2G,
+ .value = ST_ACCEL_5_FS_AVL_2_VAL,
+ .gain = ST_ACCEL_5_FS_AVL_2_GAIN,
+ },
+ [1] = {
+ .num = ST_ACCEL_FS_AVL_8G,
+ .value = ST_ACCEL_5_FS_AVL_8_VAL,
+ .gain = ST_ACCEL_5_FS_AVL_8_GAIN,
+ },
+ },
+ },
+ .drdy_irq = {
+ .addr = ST_ACCEL_5_DRDY_IRQ_ADDR,
+ .mask_int1 = ST_ACCEL_5_DRDY_IRQ_INT1_MASK,
+ .mask_int2 = ST_ACCEL_5_DRDY_IRQ_INT2_MASK,
+ },
+ .multi_read_bit = ST_ACCEL_5_MULTIREAD_BIT,
+ .bootime = 2, /* guess */
+ },
};
static int st_accel_read_raw(struct iio_dev *indio_dev,
diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c
index 6b720c190..d4ad72ca4 100644
--- a/drivers/iio/accel/st_accel_i2c.c
+++ b/drivers/iio/accel/st_accel_i2c.c
@@ -49,6 +49,10 @@ static const struct of_device_id st_accel_of_match[] = {
.data = LSM330DLC_ACCEL_DEV_NAME,
},
{
+ .compatible = "st,lis331dl-accel",
+ .data = LIS331DL_ACCEL_DEV_NAME,
+ },
+ {
.compatible = "st,lis331dlh-accel",
.data = LIS331DLH_ACCEL_DEV_NAME,
},
diff --git a/drivers/iio/accel/stk8312.c b/drivers/iio/accel/stk8312.c
new file mode 100644
index 000000000..d211d9f39
--- /dev/null
+++ b/drivers/iio/accel/stk8312.c
@@ -0,0 +1,390 @@
+/**
+ * Sensortek STK8312 3-Axis Accelerometer
+ *
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This file is subject to the terms and conditions of version 2 of
+ * the GNU General Public License. See the file COPYING in the main
+ * directory of this archive for more details.
+ *
+ * IIO driver for STK8312; 7-bit I2C address: 0x3D.
+ */
+
+#include <linux/acpi.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define STK8312_REG_XOUT 0x00
+#define STK8312_REG_YOUT 0x01
+#define STK8312_REG_ZOUT 0x02
+#define STK8312_REG_MODE 0x07
+#define STK8312_REG_STH 0x13
+#define STK8312_REG_RESET 0x20
+#define STK8312_REG_AFECTRL 0x24
+#define STK8312_REG_OTPADDR 0x3D
+#define STK8312_REG_OTPDATA 0x3E
+#define STK8312_REG_OTPCTRL 0x3F
+
+#define STK8312_MODE_ACTIVE 1
+#define STK8312_MODE_STANDBY 0
+#define STK8312_MODE_MASK 0x01
+#define STK8312_RNG_MASK 0xC0
+#define STK8312_RNG_SHIFT 6
+#define STK8312_READ_RETRIES 16
+
+#define STK8312_DRIVER_NAME "stk8312"
+
+/*
+ * The accelerometer has two measurement ranges:
+ *
+ * -6g - +6g (8-bit, signed)
+ * -16g - +16g (8-bit, signed)
+ *
+ * scale1 = (6 + 6) * 9.81 / (2^8 - 1) = 0.4616
+ * scale2 = (16 + 16) * 9.81 / (2^8 - 1) = 1.2311
+ */
+#define STK8312_SCALE_AVAIL "0.4616 1.2311"
+
+static const int stk8312_scale_table[][2] = {
+ {0, 461600}, {1, 231100}
+};
+
+#define STK8312_ACCEL_CHANNEL(reg, axis) { \
+ .type = IIO_ACCEL, \
+ .address = reg, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_##axis, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+}
+
+static const struct iio_chan_spec stk8312_channels[] = {
+ STK8312_ACCEL_CHANNEL(STK8312_REG_XOUT, X),
+ STK8312_ACCEL_CHANNEL(STK8312_REG_YOUT, Y),
+ STK8312_ACCEL_CHANNEL(STK8312_REG_ZOUT, Z),
+};
+
+struct stk8312_data {
+ struct i2c_client *client;
+ struct mutex lock;
+ int range;
+ u8 mode;
+};
+
+static IIO_CONST_ATTR(in_accel_scale_available, STK8312_SCALE_AVAIL);
+
+static struct attribute *stk8312_attributes[] = {
+ &iio_const_attr_in_accel_scale_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group stk8312_attribute_group = {
+ .attrs = stk8312_attributes
+};
+
+static int stk8312_otp_init(struct stk8312_data *data)
+{
+ int ret;
+ int count = 10;
+ struct i2c_client *client = data->client;
+
+ ret = i2c_smbus_write_byte_data(client, STK8312_REG_OTPADDR, 0x70);
+ if (ret < 0)
+ goto exit_err;
+ ret = i2c_smbus_write_byte_data(client, STK8312_REG_OTPCTRL, 0x02);
+ if (ret < 0)
+ goto exit_err;
+
+ do {
+ usleep_range(1000, 5000);
+ ret = i2c_smbus_read_byte_data(client, STK8312_REG_OTPCTRL);
+ if (ret < 0)
+ goto exit_err;
+ count--;
+ } while (!(ret & 0x80) && count > 0);
+
+ if (count == 0)
+ goto exit_err;
+
+ ret = i2c_smbus_read_byte_data(client, STK8312_REG_OTPDATA);
+ if (ret < 0)
+ goto exit_err;
+
+ ret = i2c_smbus_write_byte_data(data->client,
+ STK8312_REG_AFECTRL, ret);
+ if (ret < 0)
+ goto exit_err;
+ msleep(150);
+
+ return ret;
+
+exit_err:
+ dev_err(&client->dev, "failed to initialize sensor\n");
+ return ret;
+}
+
+static int stk8312_set_mode(struct stk8312_data *data, u8 mode)
+{
+ int ret;
+ u8 masked_reg;
+ struct i2c_client *client = data->client;
+
+ if (mode > 1)
+ return -EINVAL;
+ else if (mode == data->mode)
+ return 0;
+
+ ret = i2c_smbus_read_byte_data(client, STK8312_REG_MODE);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to change sensor mode\n");
+ return ret;
+ }
+ masked_reg = ret & (~STK8312_MODE_MASK);
+ masked_reg |= mode;
+
+ ret = i2c_smbus_write_byte_data(client,
+ STK8312_REG_MODE, masked_reg);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to change sensor mode\n");
+ return ret;
+ }
+
+ data->mode = mode;
+ if (mode == STK8312_MODE_ACTIVE) {
+ /* Need to run OTP sequence before entering active mode */
+ usleep_range(1000, 5000);
+ ret = stk8312_otp_init(data);
+ }
+
+ return ret;
+}
+
+static int stk8312_set_range(struct stk8312_data *data, u8 range)
+{
+ int ret;
+ u8 masked_reg;
+ u8 mode;
+ struct i2c_client *client = data->client;
+
+ if (range != 1 && range != 2)
+ return -EINVAL;
+ else if (range == data->range)
+ return 0;
+
+ mode = data->mode;
+ /* We need to go in standby mode to modify registers */
+ ret = stk8312_set_mode(data, STK8312_MODE_STANDBY);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_byte_data(client, STK8312_REG_STH);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to change sensor range\n");
+ return ret;
+ }
+
+ masked_reg = ret & (~STK8312_RNG_MASK);
+ masked_reg |= range << STK8312_RNG_SHIFT;
+
+ ret = i2c_smbus_write_byte_data(client, STK8312_REG_STH, masked_reg);
+ if (ret < 0)
+ dev_err(&client->dev, "failed to change sensor range\n");
+ else
+ data->range = range;
+
+ return stk8312_set_mode(data, mode);
+}
+
+static int stk8312_read_accel(struct stk8312_data *data, u8 address)
+{
+ int ret;
+ struct i2c_client *client = data->client;
+
+ if (address > 2)
+ return -EINVAL;
+
+ ret = i2c_smbus_read_byte_data(client, address);
+ if (ret < 0) {
+ dev_err(&client->dev, "register read failed\n");
+ return ret;
+ }
+
+ return sign_extend32(ret, 7);
+}
+
+static int stk8312_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct stk8312_data *data = iio_priv(indio_dev);
+
+ if (chan->type != IIO_ACCEL)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&data->lock);
+ *val = stk8312_read_accel(data, chan->address);
+ mutex_unlock(&data->lock);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = stk8312_scale_table[data->range - 1][0];
+ *val2 = stk8312_scale_table[data->range - 1][1];
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+
+ return -EINVAL;
+}
+
+static int stk8312_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ int i;
+ int index = -1;
+ int ret;
+ struct stk8312_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ for (i = 0; i < ARRAY_SIZE(stk8312_scale_table); i++)
+ if (val == stk8312_scale_table[i][0] &&
+ val2 == stk8312_scale_table[i][1]) {
+ index = i + 1;
+ break;
+ }
+ if (index < 0)
+ return -EINVAL;
+
+ mutex_lock(&data->lock);
+ ret = stk8312_set_range(data, index);
+ mutex_unlock(&data->lock);
+
+ return ret;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info stk8312_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = stk8312_read_raw,
+ .write_raw = stk8312_write_raw,
+ .attrs = &stk8312_attribute_group,
+};
+
+static int stk8312_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ struct iio_dev *indio_dev;
+ struct stk8312_data *data;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev) {
+ dev_err(&client->dev, "iio allocation failed!\n");
+ return -ENOMEM;
+ }
+
+ data = iio_priv(indio_dev);
+ data->client = client;
+ i2c_set_clientdata(client, indio_dev);
+ mutex_init(&data->lock);
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->info = &stk8312_info;
+ indio_dev->name = STK8312_DRIVER_NAME;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = stk8312_channels;
+ indio_dev->num_channels = ARRAY_SIZE(stk8312_channels);
+
+ /* A software reset is recommended at power-on */
+ ret = i2c_smbus_write_byte_data(data->client, STK8312_REG_RESET, 0x00);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to reset sensor\n");
+ return ret;
+ }
+ ret = stk8312_set_range(data, 1);
+ if (ret < 0)
+ return ret;
+
+ ret = stk8312_set_mode(data, STK8312_MODE_ACTIVE);
+ if (ret < 0)
+ return ret;
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(&client->dev, "device_register failed\n");
+ stk8312_set_mode(data, STK8312_MODE_STANDBY);
+ }
+
+ return ret;
+}
+
+static int stk8312_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+ iio_device_unregister(indio_dev);
+
+ return stk8312_set_mode(iio_priv(indio_dev), STK8312_MODE_STANDBY);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int stk8312_suspend(struct device *dev)
+{
+ struct stk8312_data *data;
+
+ data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+
+ return stk8312_set_mode(data, STK8312_MODE_STANDBY);
+}
+
+static int stk8312_resume(struct device *dev)
+{
+ struct stk8312_data *data;
+
+ data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+
+ return stk8312_set_mode(data, STK8312_MODE_ACTIVE);
+}
+
+static SIMPLE_DEV_PM_OPS(stk8312_pm_ops, stk8312_suspend, stk8312_resume);
+
+#define STK8312_PM_OPS (&stk8312_pm_ops)
+#else
+#define STK8312_PM_OPS NULL
+#endif
+
+static const struct i2c_device_id stk8312_i2c_id[] = {
+ {"STK8312", 0},
+ {}
+};
+
+static const struct acpi_device_id stk8312_acpi_id[] = {
+ {"STK8312", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(acpi, stk8312_acpi_id);
+
+static struct i2c_driver stk8312_driver = {
+ .driver = {
+ .name = "stk8312",
+ .pm = STK8312_PM_OPS,
+ .acpi_match_table = ACPI_PTR(stk8312_acpi_id),
+ },
+ .probe = stk8312_probe,
+ .remove = stk8312_remove,
+ .id_table = stk8312_i2c_id,
+};
+
+module_i2c_driver(stk8312_driver);
+
+MODULE_AUTHOR("Tiberiu Breana <tiberiu.a.breana@intel.com>");
+MODULE_DESCRIPTION("STK8312 3-Axis Accelerometer driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/stk8ba50.c b/drivers/iio/accel/stk8ba50.c
new file mode 100644
index 000000000..30950c6b3
--- /dev/null
+++ b/drivers/iio/accel/stk8ba50.c
@@ -0,0 +1,302 @@
+/**
+ * Sensortek STK8BA50 3-Axis Accelerometer
+ *
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This file is subject to the terms and conditions of version 2 of
+ * the GNU General Public License. See the file COPYING in the main
+ * directory of this archive for more details.
+ *
+ * STK8BA50 7-bit I2C address: 0x18.
+ */
+
+#include <linux/acpi.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define STK8BA50_REG_XOUT 0x02
+#define STK8BA50_REG_YOUT 0x04
+#define STK8BA50_REG_ZOUT 0x06
+#define STK8BA50_REG_RANGE 0x0F
+#define STK8BA50_REG_POWMODE 0x11
+#define STK8BA50_REG_SWRST 0x14
+
+#define STK8BA50_MODE_NORMAL 0
+#define STK8BA50_MODE_SUSPEND 1
+#define STK8BA50_MODE_POWERBIT BIT(7)
+#define STK8BA50_DATA_SHIFT 6
+#define STK8BA50_RESET_CMD 0xB6
+
+#define STK8BA50_DRIVER_NAME "stk8ba50"
+
+#define STK8BA50_SCALE_AVAIL "0.0384 0.0767 0.1534 0.3069"
+
+/*
+ * The accelerometer has four measurement ranges:
+ * +/-2g; +/-4g; +/-8g; +/-16g
+ *
+ * Acceleration values are 10-bit, 2's complement.
+ * Scales are calculated as following:
+ *
+ * scale1 = (2 + 2) * 9.81 / (2^10 - 1) = 0.0384
+ * scale2 = (4 + 4) * 9.81 / (2^10 - 1) = 0.0767
+ * etc.
+ *
+ * Scales are stored in this format:
+ * { <register value>, <scale value> }
+ *
+ * Locally, the range is stored as a table index.
+ */
+static const int stk8ba50_scale_table[][2] = {
+ {3, 38400}, {5, 76700}, {8, 153400}, {12, 306900}
+};
+
+struct stk8ba50_data {
+ struct i2c_client *client;
+ struct mutex lock;
+ int range;
+};
+
+#define STK8BA50_ACCEL_CHANNEL(reg, axis) { \
+ .type = IIO_ACCEL, \
+ .address = reg, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_##axis, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+}
+
+static const struct iio_chan_spec stk8ba50_channels[] = {
+ STK8BA50_ACCEL_CHANNEL(STK8BA50_REG_XOUT, X),
+ STK8BA50_ACCEL_CHANNEL(STK8BA50_REG_YOUT, Y),
+ STK8BA50_ACCEL_CHANNEL(STK8BA50_REG_ZOUT, Z),
+};
+
+static IIO_CONST_ATTR(in_accel_scale_available, STK8BA50_SCALE_AVAIL);
+
+static struct attribute *stk8ba50_attributes[] = {
+ &iio_const_attr_in_accel_scale_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group stk8ba50_attribute_group = {
+ .attrs = stk8ba50_attributes
+};
+
+static int stk8ba50_read_accel(struct stk8ba50_data *data, u8 reg)
+{
+ int ret;
+ struct i2c_client *client = data->client;
+
+ ret = i2c_smbus_read_word_data(client, reg);
+ if (ret < 0) {
+ dev_err(&client->dev, "register read failed\n");
+ return ret;
+ }
+
+ return sign_extend32(ret >> STK8BA50_DATA_SHIFT, 9);
+}
+
+static int stk8ba50_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct stk8ba50_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&data->lock);
+ *val = stk8ba50_read_accel(data, chan->address);
+ mutex_unlock(&data->lock);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ *val2 = stk8ba50_scale_table[data->range][1];
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+
+ return -EINVAL;
+}
+
+static int stk8ba50_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ int ret;
+ int i;
+ int index = -1;
+ struct stk8ba50_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ if (val != 0)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(stk8ba50_scale_table); i++)
+ if (val2 == stk8ba50_scale_table[i][1]) {
+ index = i;
+ break;
+ }
+ if (index < 0)
+ return -EINVAL;
+
+ ret = i2c_smbus_write_byte_data(data->client,
+ STK8BA50_REG_RANGE,
+ stk8ba50_scale_table[index][0]);
+ if (ret < 0)
+ dev_err(&data->client->dev,
+ "failed to set measurement range\n");
+ else
+ data->range = index;
+
+ return ret;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info stk8ba50_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = stk8ba50_read_raw,
+ .write_raw = stk8ba50_write_raw,
+ .attrs = &stk8ba50_attribute_group,
+};
+
+static int stk8ba50_set_power(struct stk8ba50_data *data, bool mode)
+{
+ int ret;
+ u8 masked_reg;
+ struct i2c_client *client = data->client;
+
+ ret = i2c_smbus_read_byte_data(client, STK8BA50_REG_POWMODE);
+ if (ret < 0)
+ goto exit_err;
+
+ if (mode)
+ masked_reg = ret | STK8BA50_MODE_POWERBIT;
+ else
+ masked_reg = ret & (~STK8BA50_MODE_POWERBIT);
+
+ ret = i2c_smbus_write_byte_data(client, STK8BA50_REG_POWMODE,
+ masked_reg);
+ if (ret < 0)
+ goto exit_err;
+
+ return ret;
+
+exit_err:
+ dev_err(&client->dev, "failed to change sensor mode\n");
+ return ret;
+}
+
+static int stk8ba50_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ struct iio_dev *indio_dev;
+ struct stk8ba50_data *data;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev) {
+ dev_err(&client->dev, "iio allocation failed!\n");
+ return -ENOMEM;
+ }
+
+ data = iio_priv(indio_dev);
+ data->client = client;
+ i2c_set_clientdata(client, indio_dev);
+ mutex_init(&data->lock);
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->info = &stk8ba50_info;
+ indio_dev->name = STK8BA50_DRIVER_NAME;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = stk8ba50_channels;
+ indio_dev->num_channels = ARRAY_SIZE(stk8ba50_channels);
+
+ /* Reset all registers on startup */
+ ret = i2c_smbus_write_byte_data(client,
+ STK8BA50_REG_SWRST, STK8BA50_RESET_CMD);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to reset sensor\n");
+ return ret;
+ }
+
+ /* The default range is +/-2g */
+ data->range = 0;
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(&client->dev, "device_register failed\n");
+ stk8ba50_set_power(data, STK8BA50_MODE_SUSPEND);
+ }
+
+ return ret;
+}
+
+static int stk8ba50_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+ iio_device_unregister(indio_dev);
+
+ return stk8ba50_set_power(iio_priv(indio_dev), STK8BA50_MODE_SUSPEND);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int stk8ba50_suspend(struct device *dev)
+{
+ struct stk8ba50_data *data;
+
+ data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+
+ return stk8ba50_set_power(data, STK8BA50_MODE_SUSPEND);
+}
+
+static int stk8ba50_resume(struct device *dev)
+{
+ struct stk8ba50_data *data;
+
+ data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+
+ return stk8ba50_set_power(data, STK8BA50_MODE_NORMAL);
+}
+
+static SIMPLE_DEV_PM_OPS(stk8ba50_pm_ops, stk8ba50_suspend, stk8ba50_resume);
+
+#define STK8BA50_PM_OPS (&stk8ba50_pm_ops)
+#else
+#define STK8BA50_PM_OPS NULL
+#endif
+
+static const struct i2c_device_id stk8ba50_i2c_id[] = {
+ {"stk8ba50", 0},
+ {}
+};
+
+static const struct acpi_device_id stk8ba50_acpi_id[] = {
+ {"STK8BA50", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(acpi, stk8ba50_acpi_id);
+
+static struct i2c_driver stk8ba50_driver = {
+ .driver = {
+ .name = "stk8ba50",
+ .pm = STK8BA50_PM_OPS,
+ .acpi_match_table = ACPI_PTR(stk8ba50_acpi_id),
+ },
+ .probe = stk8ba50_probe,
+ .remove = stk8ba50_remove,
+ .id_table = stk8ba50_i2c_id,
+};
+
+module_i2c_driver(stk8ba50_driver);
+
+MODULE_AUTHOR("Tiberiu Breana <tiberiu.a.breana@intel.com>");
+MODULE_DESCRIPTION("STK8BA50 3-Axis Accelerometer driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 1bcb65b8d..eb0cd8977 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -135,6 +135,13 @@ config AXP288_ADC
device. Depending on platform configuration, this general purpose ADC can
be used for sampling sensors such as thermal resistors.
+config BERLIN2_ADC
+ tristate "Marvell Berlin2 ADC driver"
+ depends on ARCH_BERLIN
+ help
+ Marvell Berlin2 ADC driver. This ADC has 8 channels, with one used for
+ temperature measurement.
+
config DA9150_GPADC
tristate "Dialog DA9150 GPADC driver support"
depends on MFD_DA9150
@@ -284,11 +291,11 @@ config TI_ADC081C
called ti-adc081c.
config TI_ADC128S052
- tristate "Texas Instruments ADC128S052"
+ tristate "Texas Instruments ADC128S052/ADC122S021"
depends on SPI
help
If you say yes here you get support for Texas Instruments ADC128S052
- chip.
+ and ADC122S021 chips.
This driver can also be built as a module. If so, the module will be
called ti-adc128s052.
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 3930e63e8..a0962103e 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_AD7887) += ad7887.o
obj-$(CONFIG_AD799X) += ad799x.o
obj-$(CONFIG_AT91_ADC) += at91_adc.o
obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
+obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
diff --git a/drivers/iio/adc/axp288_adc.c b/drivers/iio/adc/axp288_adc.c
index 56008a86b..0c904edd6 100644
--- a/drivers/iio/adc/axp288_adc.c
+++ b/drivers/iio/adc/axp288_adc.c
@@ -238,7 +238,7 @@ static int axp288_adc_remove(struct platform_device *pdev)
return 0;
}
-static struct platform_device_id axp288_adc_id_table[] = {
+static const struct platform_device_id axp288_adc_id_table[] = {
{ .name = "axp288_adc" },
{},
};
diff --git a/drivers/iio/adc/berlin2-adc.c b/drivers/iio/adc/berlin2-adc.c
new file mode 100644
index 000000000..aecc9ad99
--- /dev/null
+++ b/drivers/iio/adc/berlin2-adc.c
@@ -0,0 +1,378 @@
+/*
+ * Marvell Berlin2 ADC driver
+ *
+ * Copyright (C) 2015 Marvell Technology Group Ltd.
+ *
+ * Antoine Tenart <antoine.tenart@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/iio/iio.h>
+#include <linux/iio/driver.h>
+#include <linux/iio/machine.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+
+#define BERLIN2_SM_CTRL 0x14
+#define BERLIN2_SM_CTRL_SM_SOC_INT BIT(1)
+#define BERLIN2_SM_CTRL_SOC_SM_INT BIT(2)
+#define BERLIN2_SM_CTRL_ADC_SEL(x) (BIT(x) << 5) /* 0-15 */
+#define BERLIN2_SM_CTRL_ADC_SEL_MASK (0xf << 5)
+#define BERLIN2_SM_CTRL_ADC_POWER BIT(9)
+#define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV2 (0x0 << 10)
+#define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV3 (0x1 << 10)
+#define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV4 (0x2 << 10)
+#define BERLIN2_SM_CTRL_ADC_CLKSEL_DIV8 (0x3 << 10)
+#define BERLIN2_SM_CTRL_ADC_CLKSEL_MASK (0x3 << 10)
+#define BERLIN2_SM_CTRL_ADC_START BIT(12)
+#define BERLIN2_SM_CTRL_ADC_RESET BIT(13)
+#define BERLIN2_SM_CTRL_ADC_BANDGAP_RDY BIT(14)
+#define BERLIN2_SM_CTRL_ADC_CONT_SINGLE (0x0 << 15)
+#define BERLIN2_SM_CTRL_ADC_CONT_CONTINUOUS (0x1 << 15)
+#define BERLIN2_SM_CTRL_ADC_BUFFER_EN BIT(16)
+#define BERLIN2_SM_CTRL_ADC_VREF_EXT (0x0 << 17)
+#define BERLIN2_SM_CTRL_ADC_VREF_INT (0x1 << 17)
+#define BERLIN2_SM_CTRL_ADC_ROTATE BIT(19)
+#define BERLIN2_SM_CTRL_TSEN_EN BIT(20)
+#define BERLIN2_SM_CTRL_TSEN_CLK_SEL_125 (0x0 << 21) /* 1.25 MHz */
+#define BERLIN2_SM_CTRL_TSEN_CLK_SEL_250 (0x1 << 21) /* 2.5 MHz */
+#define BERLIN2_SM_CTRL_TSEN_MODE_0_125 (0x0 << 22) /* 0-125 C */
+#define BERLIN2_SM_CTRL_TSEN_MODE_10_50 (0x1 << 22) /* 10-50 C */
+#define BERLIN2_SM_CTRL_TSEN_RESET BIT(29)
+#define BERLIN2_SM_ADC_DATA 0x20
+#define BERLIN2_SM_ADC_MASK 0x3ff
+#define BERLIN2_SM_ADC_STATUS 0x1c
+#define BERLIN2_SM_ADC_STATUS_DATA_RDY(x) BIT(x) /* 0-15 */
+#define BERLIN2_SM_ADC_STATUS_DATA_RDY_MASK 0xf
+#define BERLIN2_SM_ADC_STATUS_INT_EN(x) (BIT(x) << 16) /* 0-15 */
+#define BERLIN2_SM_ADC_STATUS_INT_EN_MASK (0xf << 16)
+#define BERLIN2_SM_TSEN_STATUS 0x24
+#define BERLIN2_SM_TSEN_STATUS_DATA_RDY BIT(0)
+#define BERLIN2_SM_TSEN_STATUS_INT_EN BIT(1)
+#define BERLIN2_SM_TSEN_DATA 0x28
+#define BERLIN2_SM_TSEN_MASK 0xfff
+#define BERLIN2_SM_TSEN_CTRL 0x74
+#define BERLIN2_SM_TSEN_CTRL_START BIT(8)
+#define BERLIN2_SM_TSEN_CTRL_SETTLING_4 (0x0 << 21) /* 4 us */
+#define BERLIN2_SM_TSEN_CTRL_SETTLING_12 (0x1 << 21) /* 12 us */
+#define BERLIN2_SM_TSEN_CTRL_SETTLING_MASK (0x1 << 21)
+#define BERLIN2_SM_TSEN_CTRL_TRIM(x) ((x) << 22)
+#define BERLIN2_SM_TSEN_CTRL_TRIM_MASK (0xf << 22)
+
+struct berlin2_adc_priv {
+ struct regmap *regmap;
+ struct mutex lock;
+ wait_queue_head_t wq;
+ bool data_available;
+ int data;
+};
+
+#define BERLIN2_ADC_CHANNEL(n, t) \
+ { \
+ .channel = n, \
+ .datasheet_name = "channel"#n, \
+ .type = t, \
+ .indexed = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ }
+
+static struct iio_chan_spec berlin2_adc_channels[] = {
+ BERLIN2_ADC_CHANNEL(0, IIO_VOLTAGE), /* external input */
+ BERLIN2_ADC_CHANNEL(1, IIO_VOLTAGE), /* external input */
+ BERLIN2_ADC_CHANNEL(2, IIO_VOLTAGE), /* external input */
+ BERLIN2_ADC_CHANNEL(3, IIO_VOLTAGE), /* external input */
+ BERLIN2_ADC_CHANNEL(4, IIO_VOLTAGE), /* reserved */
+ BERLIN2_ADC_CHANNEL(5, IIO_VOLTAGE), /* reserved */
+ { /* temperature sensor */
+ .channel = 6,
+ .datasheet_name = "channel6",
+ .type = IIO_TEMP,
+ .indexed = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ },
+ BERLIN2_ADC_CHANNEL(7, IIO_VOLTAGE), /* reserved */
+ IIO_CHAN_SOFT_TIMESTAMP(8), /* timestamp */
+};
+#define BERLIN2_N_CHANNELS ARRAY_SIZE(berlin2_adc_channels)
+
+static int berlin2_adc_read(struct iio_dev *indio_dev, int channel)
+{
+ struct berlin2_adc_priv *priv = iio_priv(indio_dev);
+ int data, ret;
+
+ mutex_lock(&priv->lock);
+
+ /* Configure the ADC */
+ regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
+ BERLIN2_SM_CTRL_ADC_RESET | BERLIN2_SM_CTRL_ADC_SEL_MASK
+ | BERLIN2_SM_CTRL_ADC_START,
+ BERLIN2_SM_CTRL_ADC_SEL(channel) | BERLIN2_SM_CTRL_ADC_START);
+
+ ret = wait_event_interruptible_timeout(priv->wq, priv->data_available,
+ msecs_to_jiffies(1000));
+
+ /* Disable the interrupts */
+ regmap_update_bits(priv->regmap, BERLIN2_SM_ADC_STATUS,
+ BERLIN2_SM_ADC_STATUS_INT_EN(channel), 0);
+
+ if (ret == 0)
+ ret = -ETIMEDOUT;
+ if (ret < 0) {
+ mutex_unlock(&priv->lock);
+ return ret;
+ }
+
+ regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
+ BERLIN2_SM_CTRL_ADC_START, 0);
+
+ data = priv->data;
+ priv->data_available = false;
+
+ mutex_unlock(&priv->lock);
+
+ return data;
+}
+
+static int berlin2_adc_tsen_read(struct iio_dev *indio_dev)
+{
+ struct berlin2_adc_priv *priv = iio_priv(indio_dev);
+ int data, ret;
+
+ mutex_lock(&priv->lock);
+
+ /* Configure the ADC */
+ regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
+ BERLIN2_SM_CTRL_TSEN_RESET | BERLIN2_SM_CTRL_ADC_ROTATE,
+ BERLIN2_SM_CTRL_ADC_ROTATE);
+
+ /* Configure the temperature sensor */
+ regmap_update_bits(priv->regmap, BERLIN2_SM_TSEN_CTRL,
+ BERLIN2_SM_TSEN_CTRL_TRIM_MASK | BERLIN2_SM_TSEN_CTRL_SETTLING_MASK
+ | BERLIN2_SM_TSEN_CTRL_START,
+ BERLIN2_SM_TSEN_CTRL_TRIM(3) | BERLIN2_SM_TSEN_CTRL_SETTLING_12
+ | BERLIN2_SM_TSEN_CTRL_START);
+
+ ret = wait_event_interruptible_timeout(priv->wq, priv->data_available,
+ msecs_to_jiffies(1000));
+
+ /* Disable interrupts */
+ regmap_update_bits(priv->regmap, BERLIN2_SM_TSEN_STATUS,
+ BERLIN2_SM_TSEN_STATUS_INT_EN, 0);
+
+ if (ret == 0)
+ ret = -ETIMEDOUT;
+ if (ret < 0) {
+ mutex_unlock(&priv->lock);
+ return ret;
+ }
+
+ regmap_update_bits(priv->regmap, BERLIN2_SM_TSEN_CTRL,
+ BERLIN2_SM_TSEN_CTRL_START, 0);
+
+ data = priv->data;
+ priv->data_available = false;
+
+ mutex_unlock(&priv->lock);
+
+ return data;
+}
+
+static int berlin2_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2,
+ long mask)
+{
+ struct berlin2_adc_priv *priv = iio_priv(indio_dev);
+ int temp;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (chan->type != IIO_VOLTAGE)
+ return -EINVAL;
+
+ /* Enable the interrupts */
+ regmap_write(priv->regmap, BERLIN2_SM_ADC_STATUS,
+ BERLIN2_SM_ADC_STATUS_INT_EN(chan->channel));
+
+ *val = berlin2_adc_read(indio_dev, chan->channel);
+ if (*val < 0)
+ return *val;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_PROCESSED:
+ if (chan->type != IIO_TEMP)
+ return -EINVAL;
+
+ /* Enable interrupts */
+ regmap_write(priv->regmap, BERLIN2_SM_TSEN_STATUS,
+ BERLIN2_SM_TSEN_STATUS_INT_EN);
+
+ temp = berlin2_adc_tsen_read(indio_dev);
+ if (temp < 0)
+ return temp;
+
+ if (temp > 2047)
+ temp = -(4096 - temp);
+
+ /* Convert to milli Celsius */
+ *val = ((temp * 100000) / 264 - 270000);
+ return IIO_VAL_INT;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static irqreturn_t berlin2_adc_irq(int irq, void *private)
+{
+ struct berlin2_adc_priv *priv = iio_priv(private);
+ unsigned val;
+
+ regmap_read(priv->regmap, BERLIN2_SM_ADC_STATUS, &val);
+ if (val & BERLIN2_SM_ADC_STATUS_DATA_RDY_MASK) {
+ regmap_read(priv->regmap, BERLIN2_SM_ADC_DATA, &priv->data);
+ priv->data &= BERLIN2_SM_ADC_MASK;
+
+ val &= ~BERLIN2_SM_ADC_STATUS_DATA_RDY_MASK;
+ regmap_write(priv->regmap, BERLIN2_SM_ADC_STATUS, val);
+
+ priv->data_available = true;
+ wake_up_interruptible(&priv->wq);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t berlin2_adc_tsen_irq(int irq, void *private)
+{
+ struct berlin2_adc_priv *priv = iio_priv(private);
+ unsigned val;
+
+ regmap_read(priv->regmap, BERLIN2_SM_TSEN_STATUS, &val);
+ if (val & BERLIN2_SM_TSEN_STATUS_DATA_RDY) {
+ regmap_read(priv->regmap, BERLIN2_SM_TSEN_DATA, &priv->data);
+ priv->data &= BERLIN2_SM_TSEN_MASK;
+
+ val &= ~BERLIN2_SM_TSEN_STATUS_DATA_RDY;
+ regmap_write(priv->regmap, BERLIN2_SM_TSEN_STATUS, val);
+
+ priv->data_available = true;
+ wake_up_interruptible(&priv->wq);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_info berlin2_adc_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = berlin2_adc_read_raw,
+};
+
+static int berlin2_adc_probe(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev;
+ struct berlin2_adc_priv *priv;
+ struct device_node *parent_np = of_get_parent(pdev->dev.of_node);
+ int irq, tsen_irq;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev,
+ sizeof(struct berlin2_adc_priv));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ priv = iio_priv(indio_dev);
+ platform_set_drvdata(pdev, indio_dev);
+
+ priv->regmap = syscon_node_to_regmap(parent_np);
+ of_node_put(parent_np);
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
+ irq = platform_get_irq_byname(pdev, "adc");
+ if (irq < 0)
+ return -ENODEV;
+
+ tsen_irq = platform_get_irq_byname(pdev, "tsen");
+ if (tsen_irq < 0)
+ return -ENODEV;
+
+ ret = devm_request_irq(&pdev->dev, irq, berlin2_adc_irq, 0,
+ pdev->dev.driver->name, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = devm_request_irq(&pdev->dev, tsen_irq, berlin2_adc_tsen_irq,
+ 0, pdev->dev.driver->name, indio_dev);
+ if (ret)
+ return ret;
+
+ init_waitqueue_head(&priv->wq);
+ mutex_init(&priv->lock);
+
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &berlin2_adc_info;
+
+ indio_dev->num_channels = BERLIN2_N_CHANNELS;
+ indio_dev->channels = berlin2_adc_channels;
+
+ /* Power up the ADC */
+ regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
+ BERLIN2_SM_CTRL_ADC_POWER, BERLIN2_SM_CTRL_ADC_POWER);
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ /* Power down the ADC */
+ regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
+ BERLIN2_SM_CTRL_ADC_POWER, 0);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int berlin2_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct berlin2_adc_priv *priv = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ /* Power down the ADC */
+ regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL,
+ BERLIN2_SM_CTRL_ADC_POWER, 0);
+
+ return 0;
+}
+
+static const struct of_device_id berlin2_adc_match[] = {
+ { .compatible = "marvell,berlin2-adc", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, berlin2_adc_match);
+
+static struct platform_driver berlin2_adc_driver = {
+ .driver = {
+ .name = "berlin2-adc",
+ .of_match_table = berlin2_adc_match,
+ },
+ .probe = berlin2_adc_probe,
+ .remove = berlin2_adc_remove,
+};
+module_platform_driver(berlin2_adc_driver);
+
+MODULE_AUTHOR("Antoine Tenart <antoine.tenart@free-electrons.com>");
+MODULE_DESCRIPTION("Marvell Berlin2 ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/mcp320x.c b/drivers/iio/adc/mcp320x.c
index 8d9c9b921..d819823f7 100644
--- a/drivers/iio/adc/mcp320x.c
+++ b/drivers/iio/adc/mcp320x.c
@@ -299,6 +299,8 @@ static int mcp320x_probe(struct spi_device *spi)
indio_dev->channels = chip_info->channels;
indio_dev->num_channels = chip_info->num_channels;
+ adc->chip_info = chip_info;
+
adc->transfer[0].tx_buf = &adc->tx_buf;
adc->transfer[0].len = sizeof(adc->tx_buf);
adc->transfer[1].rx_buf = adc->rx_buf;
diff --git a/drivers/iio/adc/ti-adc128s052.c b/drivers/iio/adc/ti-adc128s052.c
index 655cb564e..915be6b60 100644
--- a/drivers/iio/adc/ti-adc128s052.c
+++ b/drivers/iio/adc/ti-adc128s052.c
@@ -1,9 +1,10 @@
/*
* Copyright (C) 2014 Angelo Compagnucci <angelo.compagnucci@gmail.com>
*
- * Driver for Texas Instruments' ADC128S052 ADC chip.
- * Datasheet can be found here:
+ * Driver for Texas Instruments' ADC128S052 and ADC122S021 ADC chip.
+ * Datasheets can be found here:
* http://www.ti.com/lit/ds/symlink/adc128s052.pdf
+ * http://www.ti.com/lit/ds/symlink/adc122s021.pdf
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -16,6 +17,11 @@
#include <linux/iio/iio.h>
#include <linux/regulator/consumer.h>
+struct adc128_configuration {
+ const struct iio_chan_spec *channels;
+ u8 num_channels;
+};
+
struct adc128 {
struct spi_device *spi;
@@ -92,7 +98,7 @@ static int adc128_read_raw(struct iio_dev *indio_dev,
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
}
-static const struct iio_chan_spec adc128_channels[] = {
+static const struct iio_chan_spec adc128s052_channels[] = {
ADC128_VOLTAGE_CHANNEL(0),
ADC128_VOLTAGE_CHANNEL(1),
ADC128_VOLTAGE_CHANNEL(2),
@@ -103,6 +109,16 @@ static const struct iio_chan_spec adc128_channels[] = {
ADC128_VOLTAGE_CHANNEL(7),
};
+static const struct iio_chan_spec adc122s021_channels[] = {
+ ADC128_VOLTAGE_CHANNEL(0),
+ ADC128_VOLTAGE_CHANNEL(1),
+};
+
+static const struct adc128_configuration adc128_config[] = {
+ { adc128s052_channels, ARRAY_SIZE(adc128s052_channels) },
+ { adc122s021_channels, ARRAY_SIZE(adc122s021_channels) },
+};
+
static const struct iio_info adc128_info = {
.read_raw = adc128_read_raw,
.driver_module = THIS_MODULE,
@@ -112,6 +128,7 @@ static int adc128_probe(struct spi_device *spi)
{
struct iio_dev *indio_dev;
struct adc128 *adc;
+ int config = spi_get_device_id(spi)->driver_data;
int ret;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
@@ -128,8 +145,8 @@ static int adc128_probe(struct spi_device *spi)
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &adc128_info;
- indio_dev->channels = adc128_channels;
- indio_dev->num_channels = ARRAY_SIZE(adc128_channels);
+ indio_dev->channels = adc128_config[config].channels;
+ indio_dev->num_channels = adc128_config[config].num_channels;
adc->reg = devm_regulator_get(&spi->dev, "vref");
if (IS_ERR(adc->reg))
@@ -158,7 +175,8 @@ static int adc128_remove(struct spi_device *spi)
}
static const struct spi_device_id adc128_id[] = {
- { "adc128s052", 0},
+ { "adc128s052", 0}, /* index into adc128_config */
+ { "adc122s021", 1},
{ }
};
MODULE_DEVICE_TABLE(spi, adc128_id);
diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
index a0e7161f0..942320e32 100644
--- a/drivers/iio/adc/ti_am335x_adc.c
+++ b/drivers/iio/adc/ti_am335x_adc.c
@@ -37,6 +37,7 @@ struct tiadc_device {
u8 channel_step[8];
int buffer_en_ch_steps;
u16 data[8];
+ u32 open_delay[8], sample_delay[8], step_avg[8];
};
static unsigned int tiadc_readl(struct tiadc_device *adc, unsigned int reg)
@@ -85,6 +86,7 @@ static u32 get_adc_step_bit(struct tiadc_device *adc_dev, int chan)
static void tiadc_step_config(struct iio_dev *indio_dev)
{
struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ struct device *dev = adc_dev->mfd_tscadc->dev;
unsigned int stepconfig;
int i, steps = 0;
@@ -98,20 +100,47 @@ static void tiadc_step_config(struct iio_dev *indio_dev)
* needs to be given to ADC to digitalize data.
*/
- if (iio_buffer_enabled(indio_dev))
- stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1
- | STEPCONFIG_MODE_SWCNT;
- else
- stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1;
for (i = 0; i < adc_dev->channels; i++) {
int chan;
chan = adc_dev->channel_line[i];
+
+ if (adc_dev->step_avg[i] > STEPCONFIG_AVG_16) {
+ dev_warn(dev, "chan %d step_avg truncating to %d\n",
+ chan, STEPCONFIG_AVG_16);
+ adc_dev->step_avg[i] = STEPCONFIG_AVG_16;
+ }
+
+ if (adc_dev->step_avg[i])
+ stepconfig =
+ STEPCONFIG_AVG(ffs(adc_dev->step_avg[i]) - 1) |
+ STEPCONFIG_FIFO1;
+ else
+ stepconfig = STEPCONFIG_FIFO1;
+
+ if (iio_buffer_enabled(indio_dev))
+ stepconfig |= STEPCONFIG_MODE_SWCNT;
+
tiadc_writel(adc_dev, REG_STEPCONFIG(steps),
stepconfig | STEPCONFIG_INP(chan));
+
+ if (adc_dev->open_delay[i] > STEPDELAY_OPEN_MASK) {
+ dev_warn(dev, "chan %d open delay truncating to 0x3FFFF\n",
+ chan);
+ adc_dev->open_delay[i] = STEPDELAY_OPEN_MASK;
+ }
+
+ if (adc_dev->sample_delay[i] > 0xFF) {
+ dev_warn(dev, "chan %d sample delay truncating to 0xFF\n",
+ chan);
+ adc_dev->sample_delay[i] = 0xFF;
+ }
+
tiadc_writel(adc_dev, REG_STEPDELAY(steps),
- STEPCONFIG_OPENDLY);
+ STEPDELAY_OPEN(adc_dev->open_delay[i]) |
+ STEPDELAY_SAMPLE(adc_dev->sample_delay[i]));
+
adc_dev->channel_step[i] = steps;
steps++;
}
@@ -395,16 +424,43 @@ static const struct iio_info tiadc_info = {
.driver_module = THIS_MODULE,
};
+static int tiadc_parse_dt(struct platform_device *pdev,
+ struct tiadc_device *adc_dev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct property *prop;
+ const __be32 *cur;
+ int channels = 0;
+ u32 val;
+
+ of_property_for_each_u32(node, "ti,adc-channels", prop, cur, val) {
+ adc_dev->channel_line[channels] = val;
+
+ /* Set Default values for optional DT parameters */
+ adc_dev->open_delay[channels] = STEPCONFIG_OPENDLY;
+ adc_dev->sample_delay[channels] = STEPCONFIG_SAMPLEDLY;
+ adc_dev->step_avg[channels] = 16;
+
+ channels++;
+ }
+
+ of_property_read_u32_array(node, "ti,chan-step-avg",
+ adc_dev->step_avg, channels);
+ of_property_read_u32_array(node, "ti,chan-step-opendelay",
+ adc_dev->open_delay, channels);
+ of_property_read_u32_array(node, "ti,chan-step-sampledelay",
+ adc_dev->sample_delay, channels);
+
+ adc_dev->channels = channels;
+ return 0;
+}
+
static int tiadc_probe(struct platform_device *pdev)
{
struct iio_dev *indio_dev;
struct tiadc_device *adc_dev;
struct device_node *node = pdev->dev.of_node;
- struct property *prop;
- const __be32 *cur;
int err;
- u32 val;
- int channels = 0;
if (!node) {
dev_err(&pdev->dev, "Could not find valid DT data.\n");
@@ -420,12 +476,7 @@ static int tiadc_probe(struct platform_device *pdev)
adc_dev = iio_priv(indio_dev);
adc_dev->mfd_tscadc = ti_tscadc_dev_get(pdev);
-
- of_property_for_each_u32(node, "ti,adc-channels", prop, cur, val) {
- adc_dev->channel_line[channels] = val;
- channels++;
- }
- adc_dev->channels = channels;
+ tiadc_parse_dt(pdev, adc_dev);
indio_dev->dev.parent = &pdev->dev;
indio_dev->name = dev_name(&pdev->dev);
diff --git a/drivers/iio/adc/twl4030-madc.c b/drivers/iio/adc/twl4030-madc.c
index 4caecbea4..ebe415f10 100644
--- a/drivers/iio/adc/twl4030-madc.c
+++ b/drivers/iio/adc/twl4030-madc.c
@@ -235,7 +235,7 @@ static int twl4030battery_temperature(int raw_volt)
if (ret < 0)
return ret;
- curr = ((val & TWL4030_BCI_ITHEN) + 1) * 10;
+ curr = ((val & TWL4030_BCI_ITHSENS) + 1) * 10;
/* Getting and calculating the thermistor resistance in ohms */
res = volt * 1000 / curr;
/* calculating temperature */
@@ -662,10 +662,8 @@ EXPORT_SYMBOL_GPL(twl4030_get_madc_conversion);
*
* @madc: pointer to twl4030_madc_data struct
* @chan: can be one of the two values:
- * TWL4030_BCI_ITHEN
- * Enables bias current for main battery type reading
- * TWL4030_BCI_TYPEN
- * Enables bias current for main battery temperature sensing
+ * 0 - Enables bias current for main battery type reading
+ * 1 - Enables bias current for main battery temperature sensing
* @on: enable or disable chan.
*
* Function to enable or disable bias current for
diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c
index 56292ae45..819632bf1 100644
--- a/drivers/iio/adc/vf610_adc.c
+++ b/drivers/iio/adc/vf610_adc.c
@@ -118,15 +118,21 @@ enum average_sel {
VF610_ADC_SAMPLE_32,
};
+enum conversion_mode_sel {
+ VF610_ADC_CONV_NORMAL,
+ VF610_ADC_CONV_HIGH_SPEED,
+ VF610_ADC_CONV_LOW_POWER,
+};
+
struct vf610_adc_feature {
enum clk_sel clk_sel;
enum vol_ref vol_ref;
+ enum conversion_mode_sel conv_mode;
int clk_div;
int sample_rate;
int res_mode;
- bool lpm;
bool calibration;
bool ovwren;
};
@@ -139,6 +145,8 @@ struct vf610_adc {
u32 vref_uv;
u32 value;
struct regulator *vref;
+
+ u32 max_adck_rate[3];
struct vf610_adc_feature adc_feature;
u32 sample_freq_avail[5];
@@ -148,46 +156,22 @@ struct vf610_adc {
static const u32 vf610_hw_avgs[] = { 1, 4, 8, 16, 32 };
-#define VF610_ADC_CHAN(_idx, _chan_type) { \
- .type = (_chan_type), \
- .indexed = 1, \
- .channel = (_idx), \
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
- BIT(IIO_CHAN_INFO_SAMP_FREQ), \
-}
-
-#define VF610_ADC_TEMPERATURE_CHAN(_idx, _chan_type) { \
- .type = (_chan_type), \
- .channel = (_idx), \
- .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
-}
-
-static const struct iio_chan_spec vf610_adc_iio_channels[] = {
- VF610_ADC_CHAN(0, IIO_VOLTAGE),
- VF610_ADC_CHAN(1, IIO_VOLTAGE),
- VF610_ADC_CHAN(2, IIO_VOLTAGE),
- VF610_ADC_CHAN(3, IIO_VOLTAGE),
- VF610_ADC_CHAN(4, IIO_VOLTAGE),
- VF610_ADC_CHAN(5, IIO_VOLTAGE),
- VF610_ADC_CHAN(6, IIO_VOLTAGE),
- VF610_ADC_CHAN(7, IIO_VOLTAGE),
- VF610_ADC_CHAN(8, IIO_VOLTAGE),
- VF610_ADC_CHAN(9, IIO_VOLTAGE),
- VF610_ADC_CHAN(10, IIO_VOLTAGE),
- VF610_ADC_CHAN(11, IIO_VOLTAGE),
- VF610_ADC_CHAN(12, IIO_VOLTAGE),
- VF610_ADC_CHAN(13, IIO_VOLTAGE),
- VF610_ADC_CHAN(14, IIO_VOLTAGE),
- VF610_ADC_CHAN(15, IIO_VOLTAGE),
- VF610_ADC_TEMPERATURE_CHAN(26, IIO_TEMP),
- /* sentinel */
-};
-
static inline void vf610_adc_calculate_rates(struct vf610_adc *info)
{
+ struct vf610_adc_feature *adc_feature = &info->adc_feature;
unsigned long adck_rate, ipg_rate = clk_get_rate(info->clk);
- int i;
+ int divisor, i;
+
+ adck_rate = info->max_adck_rate[adc_feature->conv_mode];
+
+ if (adck_rate) {
+ /* calculate clk divider which is within specification */
+ divisor = ipg_rate / adck_rate;
+ adc_feature->clk_div = 1 << fls(divisor + 1);
+ } else {
+ /* fall-back value using a safe divisor */
+ adc_feature->clk_div = 8;
+ }
/*
* Calculate ADC sample frequencies
@@ -219,10 +203,8 @@ static inline void vf610_adc_cfg_init(struct vf610_adc *info)
adc_feature->res_mode = 12;
adc_feature->sample_rate = 1;
- adc_feature->lpm = true;
- /* Use a save ADCK which is below 20MHz on all devices */
- adc_feature->clk_div = 8;
+ adc_feature->conv_mode = VF610_ADC_CONV_LOW_POWER;
vf610_adc_calculate_rates(info);
}
@@ -304,10 +286,12 @@ static void vf610_adc_cfg_set(struct vf610_adc *info)
cfg_data = readl(info->regs + VF610_REG_ADC_CFG);
cfg_data &= ~VF610_ADC_ADLPC_EN;
- if (adc_feature->lpm)
+ if (adc_feature->conv_mode == VF610_ADC_CONV_LOW_POWER)
cfg_data |= VF610_ADC_ADLPC_EN;
cfg_data &= ~VF610_ADC_ADHSC_EN;
+ if (adc_feature->conv_mode == VF610_ADC_CONV_HIGH_SPEED)
+ cfg_data |= VF610_ADC_ADHSC_EN;
writel(cfg_data, info->regs + VF610_REG_ADC_CFG);
}
@@ -409,6 +393,81 @@ static void vf610_adc_hw_init(struct vf610_adc *info)
vf610_adc_cfg_set(info);
}
+static int vf610_set_conversion_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ unsigned int mode)
+{
+ struct vf610_adc *info = iio_priv(indio_dev);
+
+ mutex_lock(&indio_dev->mlock);
+ info->adc_feature.conv_mode = mode;
+ vf610_adc_calculate_rates(info);
+ vf610_adc_hw_init(info);
+ mutex_unlock(&indio_dev->mlock);
+
+ return 0;
+}
+
+static int vf610_get_conversion_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct vf610_adc *info = iio_priv(indio_dev);
+
+ return info->adc_feature.conv_mode;
+}
+
+static const char * const vf610_conv_modes[] = { "normal", "high-speed",
+ "low-power" };
+
+static const struct iio_enum vf610_conversion_mode = {
+ .items = vf610_conv_modes,
+ .num_items = ARRAY_SIZE(vf610_conv_modes),
+ .get = vf610_get_conversion_mode,
+ .set = vf610_set_conversion_mode,
+};
+
+static const struct iio_chan_spec_ext_info vf610_ext_info[] = {
+ IIO_ENUM("conversion_mode", IIO_SHARED_BY_DIR, &vf610_conversion_mode),
+ {},
+};
+
+#define VF610_ADC_CHAN(_idx, _chan_type) { \
+ .type = (_chan_type), \
+ .indexed = 1, \
+ .channel = (_idx), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .ext_info = vf610_ext_info, \
+}
+
+#define VF610_ADC_TEMPERATURE_CHAN(_idx, _chan_type) { \
+ .type = (_chan_type), \
+ .channel = (_idx), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
+}
+
+static const struct iio_chan_spec vf610_adc_iio_channels[] = {
+ VF610_ADC_CHAN(0, IIO_VOLTAGE),
+ VF610_ADC_CHAN(1, IIO_VOLTAGE),
+ VF610_ADC_CHAN(2, IIO_VOLTAGE),
+ VF610_ADC_CHAN(3, IIO_VOLTAGE),
+ VF610_ADC_CHAN(4, IIO_VOLTAGE),
+ VF610_ADC_CHAN(5, IIO_VOLTAGE),
+ VF610_ADC_CHAN(6, IIO_VOLTAGE),
+ VF610_ADC_CHAN(7, IIO_VOLTAGE),
+ VF610_ADC_CHAN(8, IIO_VOLTAGE),
+ VF610_ADC_CHAN(9, IIO_VOLTAGE),
+ VF610_ADC_CHAN(10, IIO_VOLTAGE),
+ VF610_ADC_CHAN(11, IIO_VOLTAGE),
+ VF610_ADC_CHAN(12, IIO_VOLTAGE),
+ VF610_ADC_CHAN(13, IIO_VOLTAGE),
+ VF610_ADC_CHAN(14, IIO_VOLTAGE),
+ VF610_ADC_CHAN(15, IIO_VOLTAGE),
+ VF610_ADC_TEMPERATURE_CHAN(26, IIO_TEMP),
+ /* sentinel */
+};
+
static int vf610_adc_read_data(struct vf610_adc *info)
{
int result;
@@ -576,7 +635,7 @@ static int vf610_adc_reg_access(struct iio_dev *indio_dev,
struct vf610_adc *info = iio_priv(indio_dev);
if ((readval == NULL) ||
- (!(reg % 4) || (reg > VF610_REG_ADC_PCTL)))
+ ((reg % 4) || (reg > VF610_REG_ADC_PCTL)))
return -EINVAL;
*readval = readl(info->regs + reg);
@@ -651,6 +710,9 @@ static int vf610_adc_probe(struct platform_device *pdev)
info->vref_uv = regulator_get_voltage(info->vref);
+ of_property_read_u32_array(pdev->dev.of_node, "fsl,adck-max-frequency",
+ info->max_adck_rate, 3);
+
platform_set_drvdata(pdev, indio_dev);
init_completion(&info->completion);
diff --git a/drivers/iio/buffer_cb.c b/drivers/iio/buffer_cb.c
index eb46e728a..1648e6e5a 100644
--- a/drivers/iio/buffer_cb.c
+++ b/drivers/iio/buffer_cb.c
@@ -33,6 +33,8 @@ static void iio_buffer_cb_release(struct iio_buffer *buffer)
static const struct iio_buffer_access_funcs iio_cb_access = {
.store_to = &iio_buffer_cb_store_to,
.release = &iio_buffer_cb_release,
+
+ .modes = INDIO_BUFFER_SOFTWARE | INDIO_BUFFER_TRIGGERED,
};
struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c
index 8dd0477e2..8086cbcff 100644
--- a/drivers/iio/common/st_sensors/st_sensors_core.c
+++ b/drivers/iio/common/st_sensors/st_sensors_core.c
@@ -245,6 +245,16 @@ static int st_sensors_set_drdy_int_pin(struct iio_dev *indio_dev,
{
struct st_sensor_data *sdata = iio_priv(indio_dev);
+ /* Sensor does not support interrupts */
+ if (sdata->sensor_settings->drdy_irq.addr == 0) {
+ if (pdata->drdy_int_pin)
+ dev_info(&indio_dev->dev,
+ "DRDY on pin INT%d specified, but sensor "
+ "does not support interrupts\n",
+ pdata->drdy_int_pin);
+ return 0;
+ }
+
switch (pdata->drdy_int_pin) {
case 1:
if (sdata->sensor_settings->drdy_irq.mask_int1 == 0) {
@@ -285,7 +295,7 @@ static struct st_sensors_platform_data *st_sensors_of_probe(struct device *dev,
if (!of_property_read_u32(np, "st,drdy-int-pin", &val) && (val <= 2))
pdata->drdy_int_pin = (u8) val;
else
- pdata->drdy_int_pin = defdata ? defdata->drdy_int_pin : 1;
+ pdata->drdy_int_pin = defdata ? defdata->drdy_int_pin : 0;
return pdata;
}
@@ -332,11 +342,13 @@ int st_sensors_init_sensor(struct iio_dev *indio_dev,
return err;
/* set BDU */
- err = st_sensors_write_data_with_mask(indio_dev,
+ if (sdata->sensor_settings->bdu.addr) {
+ err = st_sensors_write_data_with_mask(indio_dev,
sdata->sensor_settings->bdu.addr,
sdata->sensor_settings->bdu.mask, true);
- if (err < 0)
- return err;
+ if (err < 0)
+ return err;
+ }
err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
@@ -419,7 +431,9 @@ static int st_sensors_read_axis_data(struct iio_dev *indio_dev,
if (err < 0)
goto st_sensors_free_memory;
- if (byte_for_channel == 2)
+ if (byte_for_channel == 1)
+ *data = (s8)*outdata;
+ else if (byte_for_channel == 2)
*data = (s16)get_unaligned_le16(outdata);
else if (byte_for_channel == 3)
*data = (s32)st_sensors_get_unaligned_le24(outdata);
@@ -489,7 +503,8 @@ int st_sensors_check_device_support(struct iio_dev *indio_dev,
break;
}
if (n == ARRAY_SIZE(sensor_settings[i].sensors_supported)) {
- dev_err(&indio_dev->dev, "device name and WhoAmI mismatch.\n");
+ dev_err(&indio_dev->dev, "device name \"%s\" and WhoAmI (0x%02x) mismatch",
+ indio_dev->name, wai);
goto sensor_name_mismatch;
}
diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c
index 8d8ca6f1e..3e907040c 100644
--- a/drivers/iio/common/st_sensors/st_sensors_trigger.c
+++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c
@@ -37,8 +37,10 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
IRQF_TRIGGER_RISING,
sdata->trig->name,
sdata->trig);
- if (err)
+ if (err) {
+ dev_err(&indio_dev->dev, "failed to request trigger IRQ.\n");
goto request_irq_error;
+ }
iio_trigger_set_drvdata(sdata->trig, indio_dev);
sdata->trig->ops = trigger_ops;
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
index 13471a76e..e701e28fb 100644
--- a/drivers/iio/dac/Kconfig
+++ b/drivers/iio/dac/Kconfig
@@ -142,6 +142,16 @@ config AD7303
To compile this driver as module choose M here: the module will be called
ad7303.
+config M62332
+ tristate "Mitsubishi M62332 DAC driver"
+ depends on I2C
+ help
+ If you say yes here you get support for the Mitsubishi M62332
+ (I2C 8-Bit DACs with rail-to-rail outputs).
+
+ This driver can also be built as a module. If so, the module
+ will be called m62332.
+
config MAX517
tristate "Maxim MAX517/518/519/520/521 DAC driver"
depends on I2C
diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
index 52be7e1ac..63ae05633 100644
--- a/drivers/iio/dac/Makefile
+++ b/drivers/iio/dac/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_AD5764) += ad5764.o
obj-$(CONFIG_AD5791) += ad5791.o
obj-$(CONFIG_AD5686) += ad5686.o
obj-$(CONFIG_AD7303) += ad7303.o
+obj-$(CONFIG_M62332) += m62332.o
obj-$(CONFIG_MAX517) += max517.o
obj-$(CONFIG_MAX5821) += max5821.o
obj-$(CONFIG_MCP4725) += mcp4725.o
diff --git a/drivers/iio/dac/m62332.c b/drivers/iio/dac/m62332.c
new file mode 100644
index 000000000..c23d7fa88
--- /dev/null
+++ b/drivers/iio/dac/m62332.c
@@ -0,0 +1,269 @@
+/*
+ * m62332.c - Support for Mitsubishi m62332 DAC
+ *
+ * Copyright (c) 2014 Dmitry Eremin-Solenikov
+ *
+ * Based on max517 driver:
+ * Copyright (C) 2010, 2011 Roland Stigge <stigge@antcom.de>
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/driver.h>
+
+#include <linux/regulator/consumer.h>
+
+#define M62332_CHANNELS 2
+
+struct m62332_data {
+ struct i2c_client *client;
+ u16 vref_mv;
+ struct regulator *vcc;
+ struct mutex mutex;
+ u8 raw[M62332_CHANNELS];
+#ifdef CONFIG_PM_SLEEP
+ u8 save[M62332_CHANNELS];
+#endif
+};
+
+static int m62332_set_value(struct iio_dev *indio_dev,
+ u8 val, int channel)
+{
+ struct m62332_data *data = iio_priv(indio_dev);
+ struct i2c_client *client = data->client;
+ u8 outbuf[2];
+ int res;
+
+ if (val == data->raw[channel])
+ return 0;
+
+ outbuf[0] = channel;
+ outbuf[1] = val;
+
+ mutex_lock(&data->mutex);
+
+ if (val) {
+ res = regulator_enable(data->vcc);
+ if (res)
+ goto out;
+ }
+
+ res = i2c_master_send(client, outbuf, 2);
+ if (res >= 0 && res != 2)
+ res = -EIO;
+ if (res < 0)
+ goto out;
+
+ data->raw[channel] = val;
+
+ if (!val)
+ regulator_disable(data->vcc);
+
+ mutex_unlock(&data->mutex);
+
+ return 0;
+
+out:
+ mutex_unlock(&data->mutex);
+
+ return res;
+}
+
+static int m62332_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct m62332_data *data = iio_priv(indio_dev);
+
+ switch (m) {
+ case IIO_CHAN_INFO_SCALE:
+ /* Corresponds to Vref / 2^(bits) */
+ *val = data->vref_mv;
+ *val2 = 8;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_RAW:
+ *val = data->raw[chan->channel];
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = 1;
+ return IIO_VAL_INT;
+ default:
+ break;
+ }
+ return -EINVAL;
+}
+
+static int m62332_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2, long mask)
+{
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (val < 0 || val > 255)
+ return -EINVAL;
+
+ ret = m62332_set_value(indio_dev, val, chan->channel);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int m62332_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct m62332_data *data = iio_priv(indio_dev);
+ int ret;
+
+ data->save[0] = data->raw[0];
+ data->save[1] = data->raw[1];
+
+ ret = m62332_set_value(indio_dev, 0, 0);
+ if (ret < 0)
+ return ret;
+
+ return m62332_set_value(indio_dev, 0, 1);
+}
+
+static int m62332_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct m62332_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = m62332_set_value(indio_dev, data->save[0], 0);
+ if (ret < 0)
+ return ret;
+
+ return m62332_set_value(indio_dev, data->save[1], 1);
+}
+
+static SIMPLE_DEV_PM_OPS(m62332_pm_ops, m62332_suspend, m62332_resume);
+#define M62332_PM_OPS (&m62332_pm_ops)
+#else
+#define M62332_PM_OPS NULL
+#endif
+
+static const struct iio_info m62332_info = {
+ .read_raw = m62332_read_raw,
+ .write_raw = m62332_write_raw,
+ .driver_module = THIS_MODULE,
+};
+
+#define M62332_CHANNEL(chan) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = (chan), \
+ .datasheet_name = "CH" #chan, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OFFSET), \
+}
+
+static const struct iio_chan_spec m62332_channels[M62332_CHANNELS] = {
+ M62332_CHANNEL(0),
+ M62332_CHANNEL(1)
+};
+
+static int m62332_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct m62332_data *data;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ data->client = client;
+
+ mutex_init(&data->mutex);
+
+ data->vcc = devm_regulator_get(&client->dev, "VCC");
+ if (IS_ERR(data->vcc))
+ return PTR_ERR(data->vcc);
+
+ /* establish that the iio_dev is a child of the i2c device */
+ indio_dev->dev.parent = &client->dev;
+
+ indio_dev->num_channels = M62332_CHANNELS;
+ indio_dev->channels = m62332_channels;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &m62332_info;
+
+ ret = regulator_get_voltage(data->vcc);
+ if (ret < 0)
+ return ret;
+ data->vref_mv = ret / 1000; /* mV */
+
+ ret = iio_map_array_register(indio_dev, client->dev.platform_data);
+ if (ret < 0)
+ return ret;
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ goto err;
+
+ return 0;
+
+err:
+ iio_map_array_unregister(indio_dev);
+ return ret;
+}
+
+static int m62332_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+ iio_device_unregister(indio_dev);
+ iio_map_array_unregister(indio_dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id m62332_id[] = {
+ { "m62332", },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, m62332_id);
+
+static struct i2c_driver m62332_driver = {
+ .driver = {
+ .name = "m62332",
+ .pm = M62332_PM_OPS,
+ },
+ .probe = m62332_probe,
+ .remove = m62332_remove,
+ .id_table = m62332_id,
+};
+module_i2c_driver(m62332_driver);
+
+MODULE_AUTHOR("Dmitry Eremin-Solenikov");
+MODULE_DESCRIPTION("M62332 8-bit DAC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/gyro/bmg160.c b/drivers/iio/gyro/bmg160.c
index 4415f55d2..460bf715d 100644
--- a/drivers/iio/gyro/bmg160.c
+++ b/drivers/iio/gyro/bmg160.c
@@ -108,7 +108,6 @@ struct bmg160_data {
int slope_thres;
bool dready_trigger_on;
bool motion_trigger_on;
- int64_t timestamp;
};
enum bmg160_axis {
@@ -738,17 +737,6 @@ static int bmg160_write_event_config(struct iio_dev *indio_dev,
return 0;
}
-static int bmg160_validate_trigger(struct iio_dev *indio_dev,
- struct iio_trigger *trig)
-{
- struct bmg160_data *data = iio_priv(indio_dev);
-
- if (data->dready_trig != trig && data->motion_trig != trig)
- return -EINVAL;
-
- return 0;
-}
-
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("100 200 400 1000 2000");
static IIO_CONST_ATTR(in_anglvel_scale_available,
@@ -810,7 +798,6 @@ static const struct iio_info bmg160_info = {
.write_event_value = bmg160_write_event,
.write_event_config = bmg160_write_event_config,
.read_event_config = bmg160_read_event_config,
- .validate_trigger = bmg160_validate_trigger,
.driver_module = THIS_MODULE,
};
@@ -835,7 +822,7 @@ static irqreturn_t bmg160_trigger_handler(int irq, void *p)
mutex_unlock(&data->mutex);
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
- data->timestamp);
+ pf->timestamp);
err:
iio_trigger_notify_done(indio_dev->trig);
@@ -938,21 +925,21 @@ static irqreturn_t bmg160_event_handler(int irq, void *private)
IIO_MOD_X,
IIO_EV_TYPE_ROC,
dir),
- data->timestamp);
+ iio_get_time_ns());
if (ret & BMG160_ANY_MOTION_BIT_Y)
iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL,
0,
IIO_MOD_Y,
IIO_EV_TYPE_ROC,
dir),
- data->timestamp);
+ iio_get_time_ns());
if (ret & BMG160_ANY_MOTION_BIT_Z)
iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL,
0,
IIO_MOD_Z,
IIO_EV_TYPE_ROC,
dir),
- data->timestamp);
+ iio_get_time_ns());
ack_intr_status:
if (!data->dready_trigger_on) {
@@ -973,8 +960,6 @@ static irqreturn_t bmg160_data_rdy_trig_poll(int irq, void *private)
struct iio_dev *indio_dev = private;
struct bmg160_data *data = iio_priv(indio_dev);
- data->timestamp = iio_get_time_ns();
-
if (data->dready_trigger_on)
iio_trigger_poll(data->dready_trig);
else if (data->motion_trigger_on)
@@ -987,6 +972,27 @@ static irqreturn_t bmg160_data_rdy_trig_poll(int irq, void *private)
}
+static int bmg160_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct bmg160_data *data = iio_priv(indio_dev);
+
+ return bmg160_set_power_state(data, true);
+}
+
+static int bmg160_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct bmg160_data *data = iio_priv(indio_dev);
+
+ return bmg160_set_power_state(data, false);
+}
+
+static const struct iio_buffer_setup_ops bmg160_buffer_setup_ops = {
+ .preenable = bmg160_buffer_preenable,
+ .postenable = iio_triggered_buffer_postenable,
+ .predisable = iio_triggered_buffer_predisable,
+ .postdisable = bmg160_buffer_postdisable,
+};
+
static int bmg160_gpio_probe(struct i2c_client *client,
struct bmg160_data *data)
@@ -1103,16 +1109,16 @@ static int bmg160_probe(struct i2c_client *client,
data->motion_trig = NULL;
goto err_trigger_unregister;
}
+ }
- ret = iio_triggered_buffer_setup(indio_dev,
- NULL,
- bmg160_trigger_handler,
- NULL);
- if (ret < 0) {
- dev_err(&client->dev,
- "iio triggered buffer setup failed\n");
- goto err_trigger_unregister;
- }
+ ret = iio_triggered_buffer_setup(indio_dev,
+ iio_pollfunc_store_time,
+ bmg160_trigger_handler,
+ &bmg160_buffer_setup_ops);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "iio triggered buffer setup failed\n");
+ goto err_trigger_unregister;
}
ret = iio_device_register(indio_dev);
@@ -1135,8 +1141,7 @@ static int bmg160_probe(struct i2c_client *client,
err_iio_unregister:
iio_device_unregister(indio_dev);
err_buffer_cleanup:
- if (data->dready_trig)
- iio_triggered_buffer_cleanup(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
err_trigger_unregister:
if (data->dready_trig)
iio_trigger_unregister(data->dready_trig);
@@ -1156,9 +1161,9 @@ static int bmg160_remove(struct i2c_client *client)
pm_runtime_put_noidle(&client->dev);
iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
if (data->dready_trig) {
- iio_triggered_buffer_cleanup(indio_dev);
iio_trigger_unregister(data->dready_trig);
iio_trigger_unregister(data->motion_trig);
}
diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c
index b5883b6f4..c67ce2ac4 100644
--- a/drivers/iio/gyro/hid-sensor-gyro-3d.c
+++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c
@@ -298,7 +298,6 @@ static int hid_gyro_3d_probe(struct platform_device *pdev)
struct iio_dev *indio_dev;
struct gyro_3d_state *gyro_state;
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
- struct iio_chan_spec *channels;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*gyro_state));
if (!indio_dev)
@@ -317,21 +316,21 @@ static int hid_gyro_3d_probe(struct platform_device *pdev)
return ret;
}
- channels = kmemdup(gyro_3d_channels, sizeof(gyro_3d_channels),
- GFP_KERNEL);
- if (!channels) {
+ indio_dev->channels = kmemdup(gyro_3d_channels,
+ sizeof(gyro_3d_channels), GFP_KERNEL);
+ if (!indio_dev->channels) {
dev_err(&pdev->dev, "failed to duplicate channels\n");
return -ENOMEM;
}
- ret = gyro_3d_parse_report(pdev, hsdev, channels,
- HID_USAGE_SENSOR_GYRO_3D, gyro_state);
+ ret = gyro_3d_parse_report(pdev, hsdev,
+ (struct iio_chan_spec *)indio_dev->channels,
+ HID_USAGE_SENSOR_GYRO_3D, gyro_state);
if (ret) {
dev_err(&pdev->dev, "failed to setup attributes\n");
goto error_free_dev_mem;
}
- indio_dev->channels = channels;
indio_dev->num_channels = ARRAY_SIZE(gyro_3d_channels);
indio_dev->dev.parent = &pdev->dev;
indio_dev->info = &gyro_3d_info;
@@ -397,7 +396,7 @@ static int hid_gyro_3d_remove(struct platform_device *pdev)
return 0;
}
-static struct platform_device_id hid_gyro_3d_ids[] = {
+static const struct platform_device_id hid_gyro_3d_ids[] = {
{
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
.name = "HID-SENSOR-200076",
diff --git a/drivers/iio/humidity/Kconfig b/drivers/iio/humidity/Kconfig
index 4813b793b..688c0d1cb 100644
--- a/drivers/iio/humidity/Kconfig
+++ b/drivers/iio/humidity/Kconfig
@@ -5,7 +5,7 @@ menu "Humidity sensors"
config DHT11
tristate "DHT11 (and compatible sensors) driver"
- depends on GPIOLIB
+ depends on GPIOLIB || COMPILE_TEST
help
This driver supports reading data via a single interrupt
generating GPIO line. Currently tested are DHT11 and DHT22.
diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index df919f44d..6eee1b044 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -239,13 +239,19 @@ static ssize_t iio_scan_el_show(struct device *dev,
/* Note NULL used as error indicator as it doesn't make sense. */
static const unsigned long *iio_scan_mask_match(const unsigned long *av_masks,
unsigned int masklength,
- const unsigned long *mask)
+ const unsigned long *mask,
+ bool strict)
{
if (bitmap_empty(mask, masklength))
return NULL;
while (*av_masks) {
- if (bitmap_subset(mask, av_masks, masklength))
- return av_masks;
+ if (strict) {
+ if (bitmap_equal(mask, av_masks, masklength))
+ return av_masks;
+ } else {
+ if (bitmap_subset(mask, av_masks, masklength))
+ return av_masks;
+ }
av_masks += BITS_TO_LONGS(masklength);
}
return NULL;
@@ -295,7 +301,7 @@ static int iio_scan_mask_set(struct iio_dev *indio_dev,
if (indio_dev->available_scan_masks) {
mask = iio_scan_mask_match(indio_dev->available_scan_masks,
indio_dev->masklength,
- trialmask);
+ trialmask, false);
if (!mask)
goto err_invalid_mask;
}
@@ -539,26 +545,13 @@ static void iio_buffer_deactivate(struct iio_buffer *buffer)
iio_buffer_put(buffer);
}
-void iio_disable_all_buffers(struct iio_dev *indio_dev)
+static void iio_buffer_deactivate_all(struct iio_dev *indio_dev)
{
struct iio_buffer *buffer, *_buffer;
- if (list_empty(&indio_dev->buffer_list))
- return;
-
- if (indio_dev->setup_ops->predisable)
- indio_dev->setup_ops->predisable(indio_dev);
-
list_for_each_entry_safe(buffer, _buffer,
&indio_dev->buffer_list, buffer_list)
iio_buffer_deactivate(buffer);
-
- indio_dev->currentmode = INDIO_DIRECT_MODE;
- if (indio_dev->setup_ops->postdisable)
- indio_dev->setup_ops->postdisable(indio_dev);
-
- if (indio_dev->available_scan_masks == NULL)
- kfree(indio_dev->active_scan_mask);
}
static void iio_buffer_update_bytes_per_datum(struct iio_dev *indio_dev,
@@ -575,167 +568,277 @@ static void iio_buffer_update_bytes_per_datum(struct iio_dev *indio_dev,
buffer->access->set_bytes_per_datum(buffer, bytes);
}
-static int __iio_update_buffers(struct iio_dev *indio_dev,
- struct iio_buffer *insert_buffer,
- struct iio_buffer *remove_buffer)
+static int iio_buffer_request_update(struct iio_dev *indio_dev,
+ struct iio_buffer *buffer)
{
int ret;
- int success = 0;
- struct iio_buffer *buffer;
- unsigned long *compound_mask;
- const unsigned long *old_mask;
- /* Wind down existing buffers - iff there are any */
- if (!list_empty(&indio_dev->buffer_list)) {
- if (indio_dev->setup_ops->predisable) {
- ret = indio_dev->setup_ops->predisable(indio_dev);
- if (ret)
- return ret;
- }
- indio_dev->currentmode = INDIO_DIRECT_MODE;
- if (indio_dev->setup_ops->postdisable) {
- ret = indio_dev->setup_ops->postdisable(indio_dev);
- if (ret)
- return ret;
+ iio_buffer_update_bytes_per_datum(indio_dev, buffer);
+ if (buffer->access->request_update) {
+ ret = buffer->access->request_update(buffer);
+ if (ret) {
+ dev_dbg(&indio_dev->dev,
+ "Buffer not started: buffer parameter update failed (%d)\n",
+ ret);
+ return ret;
}
}
- /* Keep a copy of current setup to allow roll back */
- old_mask = indio_dev->active_scan_mask;
+
+ return 0;
+}
+
+static void iio_free_scan_mask(struct iio_dev *indio_dev,
+ const unsigned long *mask)
+{
+ /* If the mask is dynamically allocated free it, otherwise do nothing */
if (!indio_dev->available_scan_masks)
- indio_dev->active_scan_mask = NULL;
+ kfree(mask);
+}
+
+struct iio_device_config {
+ unsigned int mode;
+ const unsigned long *scan_mask;
+ unsigned int scan_bytes;
+ bool scan_timestamp;
+};
+
+static int iio_verify_update(struct iio_dev *indio_dev,
+ struct iio_buffer *insert_buffer, struct iio_buffer *remove_buffer,
+ struct iio_device_config *config)
+{
+ unsigned long *compound_mask;
+ const unsigned long *scan_mask;
+ bool strict_scanmask = false;
+ struct iio_buffer *buffer;
+ bool scan_timestamp;
+ unsigned int modes;
+
+ memset(config, 0, sizeof(*config));
+
+ /*
+ * If there is just one buffer and we are removing it there is nothing
+ * to verify.
+ */
+ if (remove_buffer && !insert_buffer &&
+ list_is_singular(&indio_dev->buffer_list))
+ return 0;
+
+ modes = indio_dev->modes;
+
+ list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) {
+ if (buffer == remove_buffer)
+ continue;
+ modes &= buffer->access->modes;
+ }
- if (remove_buffer)
- iio_buffer_deactivate(remove_buffer);
if (insert_buffer)
- iio_buffer_activate(indio_dev, insert_buffer);
+ modes &= insert_buffer->access->modes;
- /* If no buffers in list, we are done */
- if (list_empty(&indio_dev->buffer_list)) {
- indio_dev->currentmode = INDIO_DIRECT_MODE;
- if (indio_dev->available_scan_masks == NULL)
- kfree(old_mask);
- return 0;
+ /* Definitely possible for devices to support both of these. */
+ if ((modes & INDIO_BUFFER_TRIGGERED) && indio_dev->trig) {
+ config->mode = INDIO_BUFFER_TRIGGERED;
+ } else if (modes & INDIO_BUFFER_HARDWARE) {
+ /*
+ * Keep things simple for now and only allow a single buffer to
+ * be connected in hardware mode.
+ */
+ if (insert_buffer && !list_empty(&indio_dev->buffer_list))
+ return -EINVAL;
+ config->mode = INDIO_BUFFER_HARDWARE;
+ strict_scanmask = true;
+ } else if (modes & INDIO_BUFFER_SOFTWARE) {
+ config->mode = INDIO_BUFFER_SOFTWARE;
+ } else {
+ /* Can only occur on first buffer */
+ if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
+ dev_dbg(&indio_dev->dev, "Buffer not started: no trigger\n");
+ return -EINVAL;
}
/* What scan mask do we actually have? */
compound_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength),
sizeof(long), GFP_KERNEL);
- if (compound_mask == NULL) {
- if (indio_dev->available_scan_masks == NULL)
- kfree(old_mask);
+ if (compound_mask == NULL)
return -ENOMEM;
- }
- indio_dev->scan_timestamp = 0;
+
+ scan_timestamp = false;
list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) {
+ if (buffer == remove_buffer)
+ continue;
bitmap_or(compound_mask, compound_mask, buffer->scan_mask,
indio_dev->masklength);
- indio_dev->scan_timestamp |= buffer->scan_timestamp;
+ scan_timestamp |= buffer->scan_timestamp;
+ }
+
+ if (insert_buffer) {
+ bitmap_or(compound_mask, compound_mask,
+ insert_buffer->scan_mask, indio_dev->masklength);
+ scan_timestamp |= insert_buffer->scan_timestamp;
}
+
if (indio_dev->available_scan_masks) {
- indio_dev->active_scan_mask =
- iio_scan_mask_match(indio_dev->available_scan_masks,
- indio_dev->masklength,
- compound_mask);
- if (indio_dev->active_scan_mask == NULL) {
- /*
- * Roll back.
- * Note can only occur when adding a buffer.
- */
- iio_buffer_deactivate(insert_buffer);
- if (old_mask) {
- indio_dev->active_scan_mask = old_mask;
- success = -EINVAL;
- }
- else {
- kfree(compound_mask);
- ret = -EINVAL;
- return ret;
- }
- }
+ scan_mask = iio_scan_mask_match(indio_dev->available_scan_masks,
+ indio_dev->masklength,
+ compound_mask,
+ strict_scanmask);
+ kfree(compound_mask);
+ if (scan_mask == NULL)
+ return -EINVAL;
} else {
- indio_dev->active_scan_mask = compound_mask;
+ scan_mask = compound_mask;
}
+ config->scan_bytes = iio_compute_scan_bytes(indio_dev,
+ scan_mask, scan_timestamp);
+ config->scan_mask = scan_mask;
+ config->scan_timestamp = scan_timestamp;
+
+ return 0;
+}
+
+static int iio_enable_buffers(struct iio_dev *indio_dev,
+ struct iio_device_config *config)
+{
+ int ret;
+
+ indio_dev->active_scan_mask = config->scan_mask;
+ indio_dev->scan_timestamp = config->scan_timestamp;
+ indio_dev->scan_bytes = config->scan_bytes;
+
iio_update_demux(indio_dev);
/* Wind up again */
if (indio_dev->setup_ops->preenable) {
ret = indio_dev->setup_ops->preenable(indio_dev);
if (ret) {
- printk(KERN_ERR
+ dev_dbg(&indio_dev->dev,
"Buffer not started: buffer preenable failed (%d)\n", ret);
- goto error_remove_inserted;
- }
- }
- indio_dev->scan_bytes =
- iio_compute_scan_bytes(indio_dev,
- indio_dev->active_scan_mask,
- indio_dev->scan_timestamp);
- list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) {
- iio_buffer_update_bytes_per_datum(indio_dev, buffer);
- if (buffer->access->request_update) {
- ret = buffer->access->request_update(buffer);
- if (ret) {
- printk(KERN_INFO
- "Buffer not started: buffer parameter update failed (%d)\n", ret);
- goto error_run_postdisable;
- }
+ goto err_undo_config;
}
}
+
if (indio_dev->info->update_scan_mode) {
ret = indio_dev->info
->update_scan_mode(indio_dev,
indio_dev->active_scan_mask);
if (ret < 0) {
- printk(KERN_INFO "Buffer not started: update scan mode failed (%d)\n", ret);
- goto error_run_postdisable;
+ dev_dbg(&indio_dev->dev,
+ "Buffer not started: update scan mode failed (%d)\n",
+ ret);
+ goto err_run_postdisable;
}
}
- /* Definitely possible for devices to support both of these. */
- if ((indio_dev->modes & INDIO_BUFFER_TRIGGERED) && indio_dev->trig) {
- indio_dev->currentmode = INDIO_BUFFER_TRIGGERED;
- } else if (indio_dev->modes & INDIO_BUFFER_HARDWARE) {
- indio_dev->currentmode = INDIO_BUFFER_HARDWARE;
- } else if (indio_dev->modes & INDIO_BUFFER_SOFTWARE) {
- indio_dev->currentmode = INDIO_BUFFER_SOFTWARE;
- } else { /* Should never be reached */
- /* Can only occur on first buffer */
- if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
- pr_info("Buffer not started: no trigger\n");
- ret = -EINVAL;
- goto error_run_postdisable;
- }
+
+ indio_dev->currentmode = config->mode;
if (indio_dev->setup_ops->postenable) {
ret = indio_dev->setup_ops->postenable(indio_dev);
if (ret) {
- printk(KERN_INFO
+ dev_dbg(&indio_dev->dev,
"Buffer not started: postenable failed (%d)\n", ret);
- indio_dev->currentmode = INDIO_DIRECT_MODE;
- if (indio_dev->setup_ops->postdisable)
- indio_dev->setup_ops->postdisable(indio_dev);
- goto error_disable_all_buffers;
+ goto err_run_postdisable;
}
}
- if (indio_dev->available_scan_masks)
- kfree(compound_mask);
- else
- kfree(old_mask);
-
- return success;
+ return 0;
-error_disable_all_buffers:
+err_run_postdisable:
indio_dev->currentmode = INDIO_DIRECT_MODE;
-error_run_postdisable:
if (indio_dev->setup_ops->postdisable)
indio_dev->setup_ops->postdisable(indio_dev);
-error_remove_inserted:
+err_undo_config:
+ indio_dev->active_scan_mask = NULL;
+
+ return ret;
+}
+
+static int iio_disable_buffers(struct iio_dev *indio_dev)
+{
+ int ret = 0;
+ int ret2;
+
+ /* Wind down existing buffers - iff there are any */
+ if (list_empty(&indio_dev->buffer_list))
+ return 0;
+
+ /*
+ * If things go wrong at some step in disable we still need to continue
+ * to perform the other steps, otherwise we leave the device in a
+ * inconsistent state. We return the error code for the first error we
+ * encountered.
+ */
+
+ if (indio_dev->setup_ops->predisable) {
+ ret2 = indio_dev->setup_ops->predisable(indio_dev);
+ if (ret2 && !ret)
+ ret = ret2;
+ }
+
+ indio_dev->currentmode = INDIO_DIRECT_MODE;
+
+ if (indio_dev->setup_ops->postdisable) {
+ ret2 = indio_dev->setup_ops->postdisable(indio_dev);
+ if (ret2 && !ret)
+ ret = ret2;
+ }
+
+ iio_free_scan_mask(indio_dev, indio_dev->active_scan_mask);
+ indio_dev->active_scan_mask = NULL;
+
+ return ret;
+}
+
+static int __iio_update_buffers(struct iio_dev *indio_dev,
+ struct iio_buffer *insert_buffer,
+ struct iio_buffer *remove_buffer)
+{
+ struct iio_device_config new_config;
+ int ret;
+
+ ret = iio_verify_update(indio_dev, insert_buffer, remove_buffer,
+ &new_config);
+ if (ret)
+ return ret;
+
+ if (insert_buffer) {
+ ret = iio_buffer_request_update(indio_dev, insert_buffer);
+ if (ret)
+ goto err_free_config;
+ }
+
+ ret = iio_disable_buffers(indio_dev);
+ if (ret)
+ goto err_deactivate_all;
+
+ if (remove_buffer)
+ iio_buffer_deactivate(remove_buffer);
if (insert_buffer)
- iio_buffer_deactivate(insert_buffer);
- indio_dev->active_scan_mask = old_mask;
- kfree(compound_mask);
+ iio_buffer_activate(indio_dev, insert_buffer);
+
+ /* If no buffers in list, we are done */
+ if (list_empty(&indio_dev->buffer_list))
+ return 0;
+
+ ret = iio_enable_buffers(indio_dev, &new_config);
+ if (ret)
+ goto err_deactivate_all;
+
+ return 0;
+
+err_deactivate_all:
+ /*
+ * We've already verified that the config is valid earlier. If things go
+ * wrong in either enable or disable the most likely reason is an IO
+ * error from the device. In this case there is no good recovery
+ * strategy. Just make sure to disable everything and leave the device
+ * in a sane state. With a bit of luck the device might come back to
+ * life again later and userspace can try again.
+ */
+ iio_buffer_deactivate_all(indio_dev);
+
+err_free_config:
+ iio_free_scan_mask(indio_dev, new_config.scan_mask);
return ret;
}
@@ -777,6 +880,12 @@ out_unlock:
}
EXPORT_SYMBOL_GPL(iio_update_buffers);
+void iio_disable_all_buffers(struct iio_dev *indio_dev)
+{
+ iio_disable_buffers(indio_dev);
+ iio_buffer_deactivate_all(indio_dev);
+}
+
static ssize_t iio_buffer_store_enable(struct device *dev,
struct device_attribute *attr,
const char *buf,
@@ -806,8 +915,6 @@ static ssize_t iio_buffer_store_enable(struct device *dev,
ret = __iio_update_buffers(indio_dev,
NULL, indio_dev->buffer);
- if (ret < 0)
- goto done;
done:
mutex_unlock(&indio_dev->mlock);
return (ret < 0) ? ret : len;
@@ -886,6 +993,15 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
int ret, i, attrn, attrcount, attrcount_orig = 0;
const struct iio_chan_spec *channels;
+ channels = indio_dev->channels;
+ if (channels) {
+ int ml = indio_dev->masklength;
+
+ for (i = 0; i < indio_dev->num_channels; i++)
+ ml = max(ml, channels[i].scan_index + 1);
+ indio_dev->masklength = ml;
+ }
+
if (!buffer)
return 0;
@@ -929,12 +1045,6 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
if (channels[i].scan_index < 0)
continue;
- /* Establish necessary mask length */
- if (channels[i].scan_index >
- (int)indio_dev->masklength - 1)
- indio_dev->masklength
- = channels[i].scan_index + 1;
-
ret = iio_buffer_add_channel_sysfs(indio_dev,
&channels[i]);
if (ret < 0)
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 4df97f650..3524b0de8 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -101,6 +101,8 @@ static const char * const iio_modifier_names[] = {
[IIO_MOD_WALKING] = "walking",
[IIO_MOD_STILL] = "still",
[IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z] = "sqrt(x^2+y^2+z^2)",
+ [IIO_MOD_I] = "i",
+ [IIO_MOD_Q] = "q",
};
/* relies on pairs of these shared then separate */
@@ -117,6 +119,8 @@ static const char * const iio_chan_info_postfix[] = {
[IIO_CHAN_INFO_AVERAGE_RAW] = "mean_raw",
[IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY]
= "filter_low_pass_3db_frequency",
+ [IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY]
+ = "filter_high_pass_3db_frequency",
[IIO_CHAN_INFO_SAMP_FREQ] = "sampling_frequency",
[IIO_CHAN_INFO_FREQUENCY] = "frequency",
[IIO_CHAN_INFO_PHASE] = "phase",
@@ -128,6 +132,8 @@ static const char * const iio_chan_info_postfix[] = {
[IIO_CHAN_INFO_CALIBWEIGHT] = "calibweight",
[IIO_CHAN_INFO_DEBOUNCE_COUNT] = "debounce_count",
[IIO_CHAN_INFO_DEBOUNCE_TIME] = "debounce_time",
+ [IIO_CHAN_INFO_CALIBEMISSIVITY] = "calibemissivity",
+ [IIO_CHAN_INFO_OVERSAMPLING_RATIO] = "oversampling_ratio",
};
/**
diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c
index a99692ba9..894d8137c 100644
--- a/drivers/iio/industrialio-event.c
+++ b/drivers/iio/industrialio-event.c
@@ -211,6 +211,8 @@ static const char * const iio_ev_info_text[] = {
[IIO_EV_INFO_VALUE] = "value",
[IIO_EV_INFO_HYSTERESIS] = "hysteresis",
[IIO_EV_INFO_PERIOD] = "period",
+ [IIO_EV_INFO_HIGH_PASS_FILTER_3DB] = "high_pass_filter_3db",
+ [IIO_EV_INFO_LOW_PASS_FILTER_3DB] = "low_pass_filter_3db",
};
static enum iio_event_direction iio_ev_attr_dir(struct iio_dev_attr *attr)
diff --git a/drivers/iio/kfifo_buf.c b/drivers/iio/kfifo_buf.c
index 55c267bbf..c5b999f0c 100644
--- a/drivers/iio/kfifo_buf.c
+++ b/drivers/iio/kfifo_buf.c
@@ -136,6 +136,8 @@ static const struct iio_buffer_access_funcs kfifo_access_funcs = {
.set_bytes_per_datum = &iio_set_bytes_per_datum_kfifo,
.set_length = &iio_set_length_kfifo,
.release = &iio_kfifo_buffer_release,
+
+ .modes = INDIO_BUFFER_SOFTWARE | INDIO_BUFFER_TRIGGERED,
};
struct iio_buffer *iio_kfifo_allocate(void)
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
index 01a1a16ab..a5c59251e 100644
--- a/drivers/iio/light/Kconfig
+++ b/drivers/iio/light/Kconfig
@@ -5,6 +5,19 @@
menu "Light sensors"
+config ACPI_ALS
+ tristate "ACPI Ambient Light Sensor"
+ depends on ACPI
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ select IIO_KFIFO_BUF
+ help
+ Say Y here if you want to build a driver for the ACPI0008
+ Ambient Light Sensor.
+
+ To compile this driver as a module, choose M here: the module will
+ be called acpi-als.
+
config ADJD_S311
tristate "ADJD-S311-CR999 digital color sensor"
select IIO_BUFFER
@@ -37,6 +50,16 @@ config APDS9300
To compile this driver as a module, choose M here: the
module will be called apds9300.
+config BH1750
+ tristate "ROHM BH1750 ambient light sensor"
+ depends on I2C
+ help
+ Say Y here to build support for the ROHM BH1710, BH1715, BH1721,
+ BH1750, BH1751 ambient light sensors.
+
+ To compile this driver as a module, choose M here: the module will
+ be called bh1750.
+
config CM32181
depends on I2C
tristate "CM32181 driver"
@@ -165,15 +188,29 @@ config SENSORS_LM3533
config LTR501
tristate "LTR-501ALS-01 light sensor"
depends on I2C
+ select REGMAP_I2C
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
If you say yes here you get support for the Lite-On LTR-501ALS-01
- ambient light and proximity sensor.
+ ambient light and proximity sensor. This driver also supports LTR-559
+ ALS/PS or LTR-301 ALS sensors.
This driver can also be built as a module. If so, the module
will be called ltr501.
+config STK3310
+ tristate "STK3310 ALS and proximity sensor"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Say yes here to get support for the Sensortek STK3310 ambient light
+ and proximity sensor. The STK3311 model is also supported by this
+ driver.
+
+ Choosing M will build the driver as a module. If so, the module
+ will be called stk3310.
+
config TCS3414
tristate "TAOS TCS3414 digital color sensor"
depends on I2C
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
index ad7c30fe4..e2d50fd59 100644
--- a/drivers/iio/light/Makefile
+++ b/drivers/iio/light/Makefile
@@ -3,9 +3,11 @@
#
# When adding new entries keep the list in alphabetical order
+obj-$(CONFIG_ACPI_ALS) += acpi-als.o
obj-$(CONFIG_ADJD_S311) += adjd_s311.o
obj-$(CONFIG_AL3320A) += al3320a.o
obj-$(CONFIG_APDS9300) += apds9300.o
+obj-$(CONFIG_BH1750) += bh1750.o
obj-$(CONFIG_CM32181) += cm32181.o
obj-$(CONFIG_CM3232) += cm3232.o
obj-$(CONFIG_CM3323) += cm3323.o
@@ -18,6 +20,7 @@ obj-$(CONFIG_JSA1212) += jsa1212.o
obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o
obj-$(CONFIG_LTR501) += ltr501.o
obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o
+obj-$(CONFIG_STK3310) += stk3310.o
obj-$(CONFIG_TCS3414) += tcs3414.o
obj-$(CONFIG_TCS3472) += tcs3472.o
obj-$(CONFIG_TSL4531) += tsl4531.o
diff --git a/drivers/iio/light/acpi-als.c b/drivers/iio/light/acpi-als.c
new file mode 100644
index 000000000..1dafa0756
--- /dev/null
+++ b/drivers/iio/light/acpi-als.c
@@ -0,0 +1,231 @@
+/*
+ * ACPI Ambient Light Sensor Driver
+ *
+ * Based on ALS driver:
+ * Copyright (C) 2009 Zhang Rui <rui.zhang@intel.com>
+ *
+ * Rework for IIO subsystem:
+ * Copyright (C) 2012-2013 Martin Liska <marxin.liska@gmail.com>
+ *
+ * Final cleanup and debugging:
+ * Copyright (C) 2013-2014 Marek Vasut <marex@denx.de>
+ * Copyright (C) 2015 Gabriele Mazzotta <gabriele.mzt@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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/kfifo_buf.h>
+
+#define ACPI_ALS_CLASS "als"
+#define ACPI_ALS_DEVICE_NAME "acpi-als"
+#define ACPI_ALS_NOTIFY_ILLUMINANCE 0x80
+
+ACPI_MODULE_NAME("acpi-als");
+
+/*
+ * So far, there's only one channel in here, but the specification for
+ * ACPI0008 says there can be more to what the block can report. Like
+ * chromaticity and such. We are ready for incoming additions!
+ */
+static const struct iio_chan_spec acpi_als_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 32,
+ .storagebits = 32,
+ },
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ },
+};
+
+/*
+ * The event buffer contains timestamp and all the data from
+ * the ACPI0008 block. There are multiple, but so far we only
+ * support _ALI (illuminance). Once someone adds new channels
+ * to acpi_als_channels[], the evt_buffer below will grow
+ * automatically.
+ */
+#define EVT_NR_SOURCES ARRAY_SIZE(acpi_als_channels)
+#define EVT_BUFFER_SIZE \
+ (sizeof(s64) + (EVT_NR_SOURCES * sizeof(s32)))
+
+struct acpi_als {
+ struct acpi_device *device;
+ struct mutex lock;
+
+ s32 evt_buffer[EVT_BUFFER_SIZE];
+};
+
+/*
+ * All types of properties the ACPI0008 block can report. The ALI, ALC, ALT
+ * and ALP can all be handled by als_read_value() below, while the ALR is
+ * special.
+ *
+ * The _ALR property returns tables that can be used to fine-tune the values
+ * reported by the other props based on the particular hardware type and it's
+ * location (it contains tables for "rainy", "bright inhouse lighting" etc.).
+ *
+ * So far, we support only ALI (illuminance).
+ */
+#define ACPI_ALS_ILLUMINANCE "_ALI"
+#define ACPI_ALS_CHROMATICITY "_ALC"
+#define ACPI_ALS_COLOR_TEMP "_ALT"
+#define ACPI_ALS_POLLING "_ALP"
+#define ACPI_ALS_TABLES "_ALR"
+
+static int als_read_value(struct acpi_als *als, char *prop, s32 *val)
+{
+ unsigned long long temp_val;
+ acpi_status status;
+
+ status = acpi_evaluate_integer(als->device->handle, prop, NULL,
+ &temp_val);
+
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status, "Error reading ALS %s", prop));
+ return -EIO;
+ }
+
+ *val = temp_val;
+
+ return 0;
+}
+
+static void acpi_als_notify(struct acpi_device *device, u32 event)
+{
+ struct iio_dev *indio_dev = acpi_driver_data(device);
+ struct acpi_als *als = iio_priv(indio_dev);
+ s32 *buffer = als->evt_buffer;
+ s64 time_ns = iio_get_time_ns();
+ s32 val;
+ int ret;
+
+ mutex_lock(&als->lock);
+
+ memset(buffer, 0, EVT_BUFFER_SIZE);
+
+ switch (event) {
+ case ACPI_ALS_NOTIFY_ILLUMINANCE:
+ ret = als_read_value(als, ACPI_ALS_ILLUMINANCE, &val);
+ if (ret < 0)
+ goto out;
+ *buffer++ = val;
+ break;
+ default:
+ /* Unhandled event */
+ dev_dbg(&device->dev, "Unhandled ACPI ALS event (%08x)!\n",
+ event);
+ goto out;
+ }
+
+ iio_push_to_buffers_with_timestamp(indio_dev, als->evt_buffer, time_ns);
+
+out:
+ mutex_unlock(&als->lock);
+}
+
+static int acpi_als_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct acpi_als *als = iio_priv(indio_dev);
+ s32 temp_val;
+ int ret;
+
+ if (mask != IIO_CHAN_INFO_RAW)
+ return -EINVAL;
+
+ /* we support only illumination (_ALI) so far. */
+ if (chan->type != IIO_LIGHT)
+ return -EINVAL;
+
+ ret = als_read_value(als, ACPI_ALS_ILLUMINANCE, &temp_val);
+ if (ret < 0)
+ return ret;
+
+ *val = temp_val;
+
+ return IIO_VAL_INT;
+}
+
+static const struct iio_info acpi_als_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = acpi_als_read_raw,
+};
+
+static int acpi_als_add(struct acpi_device *device)
+{
+ struct acpi_als *als;
+ struct iio_dev *indio_dev;
+ struct iio_buffer *buffer;
+
+ indio_dev = devm_iio_device_alloc(&device->dev, sizeof(*als));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ als = iio_priv(indio_dev);
+
+ device->driver_data = indio_dev;
+ als->device = device;
+ mutex_init(&als->lock);
+
+ indio_dev->name = ACPI_ALS_DEVICE_NAME;
+ indio_dev->dev.parent = &device->dev;
+ indio_dev->info = &acpi_als_info;
+ indio_dev->modes = INDIO_BUFFER_SOFTWARE;
+ indio_dev->channels = acpi_als_channels;
+ indio_dev->num_channels = ARRAY_SIZE(acpi_als_channels);
+
+ buffer = devm_iio_kfifo_allocate(&device->dev);
+ if (!buffer)
+ return -ENOMEM;
+
+ iio_device_attach_buffer(indio_dev, buffer);
+
+ return devm_iio_device_register(&device->dev, indio_dev);
+}
+
+static const struct acpi_device_id acpi_als_device_ids[] = {
+ {"ACPI0008", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(acpi, acpi_als_device_ids);
+
+static struct acpi_driver acpi_als_driver = {
+ .name = "acpi_als",
+ .class = ACPI_ALS_CLASS,
+ .ids = acpi_als_device_ids,
+ .ops = {
+ .add = acpi_als_add,
+ .notify = acpi_als_notify,
+ },
+};
+
+module_acpi_driver(acpi_als_driver);
+
+MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
+MODULE_AUTHOR("Martin Liska <marxin.liska@gmail.com>");
+MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
+MODULE_DESCRIPTION("ACPI Ambient Light Sensor Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/light/bh1750.c b/drivers/iio/light/bh1750.c
new file mode 100644
index 000000000..564c2b3c1
--- /dev/null
+++ b/drivers/iio/light/bh1750.c
@@ -0,0 +1,334 @@
+/*
+ * ROHM BH1710/BH1715/BH1721/BH1750/BH1751 ambient light sensor driver
+ *
+ * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Data sheets:
+ * http://rohmfs.rohm.com/en/products/databook/datasheet/ic/sensor/light/bh1710fvc-e.pdf
+ * http://rohmfs.rohm.com/en/products/databook/datasheet/ic/sensor/light/bh1715fvc-e.pdf
+ * http://rohmfs.rohm.com/en/products/databook/datasheet/ic/sensor/light/bh1721fvc-e.pdf
+ * http://rohmfs.rohm.com/en/products/databook/datasheet/ic/sensor/light/bh1750fvi-e.pdf
+ * http://rohmfs.rohm.com/en/products/databook/datasheet/ic/sensor/light/bh1751fvi-e.pdf
+ *
+ * 7-bit I2C slave addresses:
+ * 0x23 (ADDR pin low)
+ * 0x5C (ADDR pin high)
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/module.h>
+
+#define BH1750_POWER_DOWN 0x00
+#define BH1750_ONE_TIME_H_RES_MODE 0x20 /* auto-mode for BH1721 */
+#define BH1750_CHANGE_INT_TIME_H_BIT 0x40
+#define BH1750_CHANGE_INT_TIME_L_BIT 0x60
+
+enum {
+ BH1710,
+ BH1721,
+ BH1750,
+};
+
+struct bh1750_chip_info;
+struct bh1750_data {
+ struct i2c_client *client;
+ struct mutex lock;
+ const struct bh1750_chip_info *chip_info;
+ u16 mtreg;
+};
+
+struct bh1750_chip_info {
+ u16 mtreg_min;
+ u16 mtreg_max;
+ u16 mtreg_default;
+ int mtreg_to_usec;
+ int mtreg_to_scale;
+
+ /*
+ * For BH1710/BH1721 all possible integration time values won't fit
+ * into one page so displaying is limited to every second one.
+ * Note, that user can still write proper values which were not
+ * listed.
+ */
+ int inc;
+
+ u16 int_time_low_mask;
+ u16 int_time_high_mask;
+}
+
+static const bh1750_chip_info_tbl[] = {
+ [BH1710] = { 140, 1022, 300, 400, 250000000, 2, 0x001F, 0x03E0 },
+ [BH1721] = { 140, 1020, 300, 400, 250000000, 2, 0x0010, 0x03E0 },
+ [BH1750] = { 31, 254, 69, 1740, 57500000, 1, 0x001F, 0x00E0 },
+};
+
+static int bh1750_change_int_time(struct bh1750_data *data, int usec)
+{
+ int ret;
+ u16 val;
+ u8 regval;
+ const struct bh1750_chip_info *chip_info = data->chip_info;
+
+ if ((usec % chip_info->mtreg_to_usec) != 0)
+ return -EINVAL;
+
+ val = usec / chip_info->mtreg_to_usec;
+ if (val < chip_info->mtreg_min || val > chip_info->mtreg_max)
+ return -EINVAL;
+
+ ret = i2c_smbus_write_byte(data->client, BH1750_POWER_DOWN);
+ if (ret < 0)
+ return ret;
+
+ regval = (val & chip_info->int_time_high_mask) >> 5;
+ ret = i2c_smbus_write_byte(data->client,
+ BH1750_CHANGE_INT_TIME_H_BIT | regval);
+ if (ret < 0)
+ return ret;
+
+ regval = val & chip_info->int_time_low_mask;
+ ret = i2c_smbus_write_byte(data->client,
+ BH1750_CHANGE_INT_TIME_L_BIT | regval);
+ if (ret < 0)
+ return ret;
+
+ data->mtreg = val;
+
+ return 0;
+}
+
+static int bh1750_read(struct bh1750_data *data, int *val)
+{
+ int ret;
+ __be16 result;
+ const struct bh1750_chip_info *chip_info = data->chip_info;
+ unsigned long delay = chip_info->mtreg_to_usec * data->mtreg;
+
+ /*
+ * BH1721 will enter continuous mode on receiving this command.
+ * Note, that this eliminates need for bh1750_resume().
+ */
+ ret = i2c_smbus_write_byte(data->client, BH1750_ONE_TIME_H_RES_MODE);
+ if (ret < 0)
+ return ret;
+
+ usleep_range(delay + 15000, delay + 40000);
+
+ ret = i2c_master_recv(data->client, (char *)&result, 2);
+ if (ret < 0)
+ return ret;
+
+ *val = be16_to_cpu(result);
+
+ return 0;
+}
+
+static int bh1750_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ int ret, tmp;
+ struct bh1750_data *data = iio_priv(indio_dev);
+ const struct bh1750_chip_info *chip_info = data->chip_info;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ switch (chan->type) {
+ case IIO_LIGHT:
+ mutex_lock(&data->lock);
+ ret = bh1750_read(data, val);
+ mutex_unlock(&data->lock);
+ if (ret < 0)
+ return ret;
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SCALE:
+ tmp = chip_info->mtreg_to_scale / data->mtreg;
+ *val = tmp / 1000000;
+ *val2 = tmp % 1000000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_INT_TIME:
+ *val = 0;
+ *val2 = chip_info->mtreg_to_usec * data->mtreg;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int bh1750_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ int ret;
+ struct bh1750_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_INT_TIME:
+ if (val != 0)
+ return -EINVAL;
+
+ mutex_lock(&data->lock);
+ ret = bh1750_change_int_time(data, val2);
+ mutex_unlock(&data->lock);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+static ssize_t bh1750_show_int_time_available(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int i;
+ size_t len = 0;
+ struct bh1750_data *data = iio_priv(dev_to_iio_dev(dev));
+ const struct bh1750_chip_info *chip_info = data->chip_info;
+
+ for (i = chip_info->mtreg_min; i <= chip_info->mtreg_max; i += chip_info->inc)
+ len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06d ",
+ chip_info->mtreg_to_usec * i);
+
+ buf[len - 1] = '\n';
+
+ return len;
+}
+
+static IIO_DEV_ATTR_INT_TIME_AVAIL(bh1750_show_int_time_available);
+
+static struct attribute *bh1750_attributes[] = {
+ &iio_dev_attr_integration_time_available.dev_attr.attr,
+ NULL,
+};
+
+static struct attribute_group bh1750_attribute_group = {
+ .attrs = bh1750_attributes,
+};
+
+static const struct iio_info bh1750_info = {
+ .driver_module = THIS_MODULE,
+ .attrs = &bh1750_attribute_group,
+ .read_raw = bh1750_read_raw,
+ .write_raw = bh1750_write_raw,
+};
+
+static const struct iio_chan_spec bh1750_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_INT_TIME)
+ }
+};
+
+static int bh1750_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret, usec;
+ struct bh1750_data *data;
+ struct iio_dev *indio_dev;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
+ I2C_FUNC_SMBUS_WRITE_BYTE))
+ return -ENODEV;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ data->client = client;
+ data->chip_info = &bh1750_chip_info_tbl[id->driver_data];
+
+ usec = data->chip_info->mtreg_to_usec * data->chip_info->mtreg_default;
+ ret = bh1750_change_int_time(data, usec);
+ if (ret < 0)
+ return ret;
+
+ mutex_init(&data->lock);
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->info = &bh1750_info;
+ indio_dev->name = id->name;
+ indio_dev->channels = bh1750_channels;
+ indio_dev->num_channels = ARRAY_SIZE(bh1750_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ return iio_device_register(indio_dev);
+}
+
+static int bh1750_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct bh1750_data *data = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ mutex_lock(&data->lock);
+ i2c_smbus_write_byte(client, BH1750_POWER_DOWN);
+ mutex_unlock(&data->lock);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int bh1750_suspend(struct device *dev)
+{
+ int ret;
+ struct bh1750_data *data =
+ iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+
+ /*
+ * This is mainly for BH1721 which doesn't enter power down
+ * mode automatically.
+ */
+ mutex_lock(&data->lock);
+ ret = i2c_smbus_write_byte(data->client, BH1750_POWER_DOWN);
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(bh1750_pm_ops, bh1750_suspend, NULL);
+#define BH1750_PM_OPS (&bh1750_pm_ops)
+#else
+#define BH1750_PM_OPS NULL
+#endif
+
+static const struct i2c_device_id bh1750_id[] = {
+ { "bh1710", BH1710 },
+ { "bh1715", BH1750 },
+ { "bh1721", BH1721 },
+ { "bh1750", BH1750 },
+ { "bh1751", BH1750 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, bh1750_id);
+
+static struct i2c_driver bh1750_driver = {
+ .driver = {
+ .name = "bh1750",
+ .owner = THIS_MODULE,
+ .pm = BH1750_PM_OPS,
+ },
+ .probe = bh1750_probe,
+ .remove = bh1750_remove,
+ .id_table = bh1750_id,
+
+};
+module_i2c_driver(bh1750_driver);
+
+MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
+MODULE_DESCRIPTION("ROHM BH1710/BH1715/BH1721/BH1750/BH1751 als driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c
index 1609ecdd0..8bb1f90ec 100644
--- a/drivers/iio/light/hid-sensor-als.c
+++ b/drivers/iio/light/hid-sensor-als.c
@@ -263,7 +263,6 @@ static int hid_als_probe(struct platform_device *pdev)
struct iio_dev *indio_dev;
struct als_state *als_state;
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
- struct iio_chan_spec *channels;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct als_state));
if (!indio_dev)
@@ -281,20 +280,21 @@ static int hid_als_probe(struct platform_device *pdev)
return ret;
}
- channels = kmemdup(als_channels, sizeof(als_channels), GFP_KERNEL);
- if (!channels) {
+ indio_dev->channels = kmemdup(als_channels,
+ sizeof(als_channels), GFP_KERNEL);
+ if (!indio_dev->channels) {
dev_err(&pdev->dev, "failed to duplicate channels\n");
return -ENOMEM;
}
- ret = als_parse_report(pdev, hsdev, channels,
- HID_USAGE_SENSOR_ALS, als_state);
+ ret = als_parse_report(pdev, hsdev,
+ (struct iio_chan_spec *)indio_dev->channels,
+ HID_USAGE_SENSOR_ALS, als_state);
if (ret) {
dev_err(&pdev->dev, "failed to setup attributes\n");
goto error_free_dev_mem;
}
- indio_dev->channels = channels;
indio_dev->num_channels =
ARRAY_SIZE(als_channels);
indio_dev->dev.parent = &pdev->dev;
@@ -361,7 +361,7 @@ static int hid_als_remove(struct platform_device *pdev)
return 0;
}
-static struct platform_device_id hid_als_ids[] = {
+static const struct platform_device_id hid_als_ids[] = {
{
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
.name = "HID-SENSOR-200041",
diff --git a/drivers/iio/light/hid-sensor-prox.c b/drivers/iio/light/hid-sensor-prox.c
index ef60bae73..0d248476f 100644
--- a/drivers/iio/light/hid-sensor-prox.c
+++ b/drivers/iio/light/hid-sensor-prox.c
@@ -350,7 +350,7 @@ static int hid_prox_remove(struct platform_device *pdev)
return 0;
}
-static struct platform_device_id hid_prox_ids[] = {
+static const struct platform_device_id hid_prox_ids[] = {
{
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
.name = "HID-SENSOR-200011",
diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c
index 78b87839c..b5a0e66b5 100644
--- a/drivers/iio/light/ltr501.c
+++ b/drivers/iio/light/ltr501.c
@@ -9,15 +9,18 @@
*
* 7-bit I2C slave address 0x23
*
- * TODO: interrupt, threshold, measurement rate, IR LED characteristics
+ * TODO: IR LED characteristics
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/delay.h>
+#include <linux/regmap.h>
+#include <linux/acpi.h>
#include <linux/iio/iio.h>
+#include <linux/iio/events.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/buffer.h>
@@ -27,12 +30,21 @@
#define LTR501_ALS_CONTR 0x80 /* ALS operation mode, SW reset */
#define LTR501_PS_CONTR 0x81 /* PS operation mode */
+#define LTR501_PS_MEAS_RATE 0x84 /* measurement rate*/
+#define LTR501_ALS_MEAS_RATE 0x85 /* ALS integ time, measurement rate*/
#define LTR501_PART_ID 0x86
#define LTR501_MANUFAC_ID 0x87
#define LTR501_ALS_DATA1 0x88 /* 16-bit, little endian */
#define LTR501_ALS_DATA0 0x8a /* 16-bit, little endian */
#define LTR501_ALS_PS_STATUS 0x8c
#define LTR501_PS_DATA 0x8d /* 16-bit, little endian */
+#define LTR501_INTR 0x8f /* output mode, polarity, mode */
+#define LTR501_PS_THRESH_UP 0x90 /* 11 bit, ps upper threshold */
+#define LTR501_PS_THRESH_LOW 0x92 /* 11 bit, ps lower threshold */
+#define LTR501_ALS_THRESH_UP 0x97 /* 16 bit, ALS upper threshold */
+#define LTR501_ALS_THRESH_LOW 0x99 /* 16 bit, ALS lower threshold */
+#define LTR501_INTR_PRST 0x9e /* ps thresh, als thresh */
+#define LTR501_MAX_REG 0x9f
#define LTR501_ALS_CONTR_SW_RESET BIT(2)
#define LTR501_CONTR_PS_GAIN_MASK (BIT(3) | BIT(2))
@@ -40,28 +52,288 @@
#define LTR501_CONTR_ALS_GAIN_MASK BIT(3)
#define LTR501_CONTR_ACTIVE BIT(1)
+#define LTR501_STATUS_ALS_INTR BIT(3)
#define LTR501_STATUS_ALS_RDY BIT(2)
+#define LTR501_STATUS_PS_INTR BIT(1)
#define LTR501_STATUS_PS_RDY BIT(0)
#define LTR501_PS_DATA_MASK 0x7ff
+#define LTR501_PS_THRESH_MASK 0x7ff
+#define LTR501_ALS_THRESH_MASK 0xffff
+
+#define LTR501_ALS_DEF_PERIOD 500000
+#define LTR501_PS_DEF_PERIOD 100000
+
+#define LTR501_REGMAP_NAME "ltr501_regmap"
+
+#define LTR501_LUX_CONV(vis_coeff, vis_data, ir_coeff, ir_data) \
+ ((vis_coeff * vis_data) - (ir_coeff * ir_data))
+
+static const int int_time_mapping[] = {100000, 50000, 200000, 400000};
+
+static const struct reg_field reg_field_it =
+ REG_FIELD(LTR501_ALS_MEAS_RATE, 3, 4);
+static const struct reg_field reg_field_als_intr =
+ REG_FIELD(LTR501_INTR, 0, 0);
+static const struct reg_field reg_field_ps_intr =
+ REG_FIELD(LTR501_INTR, 1, 1);
+static const struct reg_field reg_field_als_rate =
+ REG_FIELD(LTR501_ALS_MEAS_RATE, 0, 2);
+static const struct reg_field reg_field_ps_rate =
+ REG_FIELD(LTR501_PS_MEAS_RATE, 0, 3);
+static const struct reg_field reg_field_als_prst =
+ REG_FIELD(LTR501_INTR_PRST, 0, 3);
+static const struct reg_field reg_field_ps_prst =
+ REG_FIELD(LTR501_INTR_PRST, 4, 7);
+
+struct ltr501_samp_table {
+ int freq_val; /* repetition frequency in micro HZ*/
+ int time_val; /* repetition rate in micro seconds */
+};
+
+#define LTR501_RESERVED_GAIN -1
+
+enum {
+ ltr501 = 0,
+ ltr559,
+ ltr301,
+};
+
+struct ltr501_gain {
+ int scale;
+ int uscale;
+};
+
+static struct ltr501_gain ltr501_als_gain_tbl[] = {
+ {1, 0},
+ {0, 5000},
+};
+
+static struct ltr501_gain ltr559_als_gain_tbl[] = {
+ {1, 0},
+ {0, 500000},
+ {0, 250000},
+ {0, 125000},
+ {LTR501_RESERVED_GAIN, LTR501_RESERVED_GAIN},
+ {LTR501_RESERVED_GAIN, LTR501_RESERVED_GAIN},
+ {0, 20000},
+ {0, 10000},
+};
+
+static struct ltr501_gain ltr501_ps_gain_tbl[] = {
+ {1, 0},
+ {0, 250000},
+ {0, 125000},
+ {0, 62500},
+};
+
+static struct ltr501_gain ltr559_ps_gain_tbl[] = {
+ {0, 62500}, /* x16 gain */
+ {0, 31250}, /* x32 gain */
+ {0, 15625}, /* bits X1 are for x64 gain */
+ {0, 15624},
+};
+
+struct ltr501_chip_info {
+ u8 partid;
+ struct ltr501_gain *als_gain;
+ int als_gain_tbl_size;
+ struct ltr501_gain *ps_gain;
+ int ps_gain_tbl_size;
+ u8 als_mode_active;
+ u8 als_gain_mask;
+ u8 als_gain_shift;
+ struct iio_chan_spec const *channels;
+ const int no_channels;
+ const struct iio_info *info;
+ const struct iio_info *info_no_irq;
+};
struct ltr501_data {
struct i2c_client *client;
struct mutex lock_als, lock_ps;
+ struct ltr501_chip_info *chip_info;
u8 als_contr, ps_contr;
+ int als_period, ps_period; /* period in micro seconds */
+ struct regmap *regmap;
+ struct regmap_field *reg_it;
+ struct regmap_field *reg_als_intr;
+ struct regmap_field *reg_ps_intr;
+ struct regmap_field *reg_als_rate;
+ struct regmap_field *reg_ps_rate;
+ struct regmap_field *reg_als_prst;
+ struct regmap_field *reg_ps_prst;
+};
+
+static const struct ltr501_samp_table ltr501_als_samp_table[] = {
+ {20000000, 50000}, {10000000, 100000},
+ {5000000, 200000}, {2000000, 500000},
+ {1000000, 1000000}, {500000, 2000000},
+ {500000, 2000000}, {500000, 2000000}
};
+static const struct ltr501_samp_table ltr501_ps_samp_table[] = {
+ {20000000, 50000}, {14285714, 70000},
+ {10000000, 100000}, {5000000, 200000},
+ {2000000, 500000}, {1000000, 1000000},
+ {500000, 2000000}, {500000, 2000000},
+ {500000, 2000000}
+};
+
+static unsigned int ltr501_match_samp_freq(const struct ltr501_samp_table *tab,
+ int len, int val, int val2)
+{
+ int i, freq;
+
+ freq = val * 1000000 + val2;
+
+ for (i = 0; i < len; i++) {
+ if (tab[i].freq_val == freq)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int ltr501_als_read_samp_freq(struct ltr501_data *data,
+ int *val, int *val2)
+{
+ int ret, i;
+
+ ret = regmap_field_read(data->reg_als_rate, &i);
+ if (ret < 0)
+ return ret;
+
+ if (i < 0 || i >= ARRAY_SIZE(ltr501_als_samp_table))
+ return -EINVAL;
+
+ *val = ltr501_als_samp_table[i].freq_val / 1000000;
+ *val2 = ltr501_als_samp_table[i].freq_val % 1000000;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int ltr501_ps_read_samp_freq(struct ltr501_data *data,
+ int *val, int *val2)
+{
+ int ret, i;
+
+ ret = regmap_field_read(data->reg_ps_rate, &i);
+ if (ret < 0)
+ return ret;
+
+ if (i < 0 || i >= ARRAY_SIZE(ltr501_ps_samp_table))
+ return -EINVAL;
+
+ *val = ltr501_ps_samp_table[i].freq_val / 1000000;
+ *val2 = ltr501_ps_samp_table[i].freq_val % 1000000;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int ltr501_als_write_samp_freq(struct ltr501_data *data,
+ int val, int val2)
+{
+ int i, ret;
+
+ i = ltr501_match_samp_freq(ltr501_als_samp_table,
+ ARRAY_SIZE(ltr501_als_samp_table),
+ val, val2);
+
+ if (i < 0)
+ return i;
+
+ mutex_lock(&data->lock_als);
+ ret = regmap_field_write(data->reg_als_rate, i);
+ mutex_unlock(&data->lock_als);
+
+ return ret;
+}
+
+static int ltr501_ps_write_samp_freq(struct ltr501_data *data,
+ int val, int val2)
+{
+ int i, ret;
+
+ i = ltr501_match_samp_freq(ltr501_ps_samp_table,
+ ARRAY_SIZE(ltr501_ps_samp_table),
+ val, val2);
+
+ if (i < 0)
+ return i;
+
+ mutex_lock(&data->lock_ps);
+ ret = regmap_field_write(data->reg_ps_rate, i);
+ mutex_unlock(&data->lock_ps);
+
+ return ret;
+}
+
+static int ltr501_als_read_samp_period(struct ltr501_data *data, int *val)
+{
+ int ret, i;
+
+ ret = regmap_field_read(data->reg_als_rate, &i);
+ if (ret < 0)
+ return ret;
+
+ if (i < 0 || i >= ARRAY_SIZE(ltr501_als_samp_table))
+ return -EINVAL;
+
+ *val = ltr501_als_samp_table[i].time_val;
+
+ return IIO_VAL_INT;
+}
+
+static int ltr501_ps_read_samp_period(struct ltr501_data *data, int *val)
+{
+ int ret, i;
+
+ ret = regmap_field_read(data->reg_ps_rate, &i);
+ if (ret < 0)
+ return ret;
+
+ if (i < 0 || i >= ARRAY_SIZE(ltr501_ps_samp_table))
+ return -EINVAL;
+
+ *val = ltr501_ps_samp_table[i].time_val;
+
+ return IIO_VAL_INT;
+}
+
+/* IR and visible spectrum coeff's are given in data sheet */
+static unsigned long ltr501_calculate_lux(u16 vis_data, u16 ir_data)
+{
+ unsigned long ratio, lux;
+
+ if (vis_data == 0)
+ return 0;
+
+ /* multiply numerator by 100 to avoid handling ratio < 1 */
+ ratio = DIV_ROUND_UP(ir_data * 100, ir_data + vis_data);
+
+ if (ratio < 45)
+ lux = LTR501_LUX_CONV(1774, vis_data, -1105, ir_data);
+ else if (ratio >= 45 && ratio < 64)
+ lux = LTR501_LUX_CONV(3772, vis_data, 1336, ir_data);
+ else if (ratio >= 64 && ratio < 85)
+ lux = LTR501_LUX_CONV(1690, vis_data, 169, ir_data);
+ else
+ lux = 0;
+
+ return lux / 1000;
+}
+
static int ltr501_drdy(struct ltr501_data *data, u8 drdy_mask)
{
int tries = 100;
- int ret;
+ int ret, status;
while (tries--) {
- ret = i2c_smbus_read_byte_data(data->client,
- LTR501_ALS_PS_STATUS);
+ ret = regmap_read(data->regmap, LTR501_ALS_PS_STATUS, &status);
if (ret < 0)
return ret;
- if ((ret & drdy_mask) == drdy_mask)
+ if ((status & drdy_mask) == drdy_mask)
return 0;
msleep(25);
}
@@ -70,25 +342,221 @@ static int ltr501_drdy(struct ltr501_data *data, u8 drdy_mask)
return -EIO;
}
+static int ltr501_set_it_time(struct ltr501_data *data, int it)
+{
+ int ret, i, index = -1, status;
+
+ for (i = 0; i < ARRAY_SIZE(int_time_mapping); i++) {
+ if (int_time_mapping[i] == it) {
+ index = i;
+ break;
+ }
+ }
+ /* Make sure integ time index is valid */
+ if (index < 0)
+ return -EINVAL;
+
+ ret = regmap_read(data->regmap, LTR501_ALS_CONTR, &status);
+ if (ret < 0)
+ return ret;
+
+ if (status & LTR501_CONTR_ALS_GAIN_MASK) {
+ /*
+ * 200 ms and 400 ms integ time can only be
+ * used in dynamic range 1
+ */
+ if (index > 1)
+ return -EINVAL;
+ } else
+ /* 50 ms integ time can only be used in dynamic range 2 */
+ if (index == 1)
+ return -EINVAL;
+
+ return regmap_field_write(data->reg_it, index);
+}
+
+/* read int time in micro seconds */
+static int ltr501_read_it_time(struct ltr501_data *data, int *val, int *val2)
+{
+ int ret, index;
+
+ ret = regmap_field_read(data->reg_it, &index);
+ if (ret < 0)
+ return ret;
+
+ /* Make sure integ time index is valid */
+ if (index < 0 || index >= ARRAY_SIZE(int_time_mapping))
+ return -EINVAL;
+
+ *val2 = int_time_mapping[index];
+ *val = 0;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
static int ltr501_read_als(struct ltr501_data *data, __le16 buf[2])
{
- int ret = ltr501_drdy(data, LTR501_STATUS_ALS_RDY);
+ int ret;
+
+ ret = ltr501_drdy(data, LTR501_STATUS_ALS_RDY);
if (ret < 0)
return ret;
/* always read both ALS channels in given order */
- return i2c_smbus_read_i2c_block_data(data->client,
- LTR501_ALS_DATA1, 2 * sizeof(__le16), (u8 *) buf);
+ return regmap_bulk_read(data->regmap, LTR501_ALS_DATA1,
+ buf, 2 * sizeof(__le16));
}
static int ltr501_read_ps(struct ltr501_data *data)
{
- int ret = ltr501_drdy(data, LTR501_STATUS_PS_RDY);
+ int ret, status;
+
+ ret = ltr501_drdy(data, LTR501_STATUS_PS_RDY);
if (ret < 0)
return ret;
- return i2c_smbus_read_word_data(data->client, LTR501_PS_DATA);
+
+ ret = regmap_bulk_read(data->regmap, LTR501_PS_DATA,
+ &status, 2);
+ if (ret < 0)
+ return ret;
+
+ return status;
+}
+
+static int ltr501_read_intr_prst(struct ltr501_data *data,
+ enum iio_chan_type type,
+ int *val2)
+{
+ int ret, samp_period, prst;
+
+ switch (type) {
+ case IIO_INTENSITY:
+ ret = regmap_field_read(data->reg_als_prst, &prst);
+ if (ret < 0)
+ return ret;
+
+ ret = ltr501_als_read_samp_period(data, &samp_period);
+
+ if (ret < 0)
+ return ret;
+ *val2 = samp_period * prst;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_PROXIMITY:
+ ret = regmap_field_read(data->reg_ps_prst, &prst);
+ if (ret < 0)
+ return ret;
+
+ ret = ltr501_ps_read_samp_period(data, &samp_period);
+
+ if (ret < 0)
+ return ret;
+
+ *val2 = samp_period * prst;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+
+ return -EINVAL;
}
-#define LTR501_INTENSITY_CHANNEL(_idx, _addr, _mod, _shared) { \
+static int ltr501_write_intr_prst(struct ltr501_data *data,
+ enum iio_chan_type type,
+ int val, int val2)
+{
+ int ret, samp_period, new_val;
+ unsigned long period;
+
+ if (val < 0 || val2 < 0)
+ return -EINVAL;
+
+ /* period in microseconds */
+ period = ((val * 1000000) + val2);
+
+ switch (type) {
+ case IIO_INTENSITY:
+ ret = ltr501_als_read_samp_period(data, &samp_period);
+ if (ret < 0)
+ return ret;
+
+ /* period should be atleast equal to sampling period */
+ if (period < samp_period)
+ return -EINVAL;
+
+ new_val = DIV_ROUND_UP(period, samp_period);
+ if (new_val < 0 || new_val > 0x0f)
+ return -EINVAL;
+
+ mutex_lock(&data->lock_als);
+ ret = regmap_field_write(data->reg_als_prst, new_val);
+ mutex_unlock(&data->lock_als);
+ if (ret >= 0)
+ data->als_period = period;
+
+ return ret;
+ case IIO_PROXIMITY:
+ ret = ltr501_ps_read_samp_period(data, &samp_period);
+ if (ret < 0)
+ return ret;
+
+ /* period should be atleast equal to rate */
+ if (period < samp_period)
+ return -EINVAL;
+
+ new_val = DIV_ROUND_UP(period, samp_period);
+ if (new_val < 0 || new_val > 0x0f)
+ return -EINVAL;
+
+ mutex_lock(&data->lock_ps);
+ ret = regmap_field_write(data->reg_ps_prst, new_val);
+ mutex_unlock(&data->lock_ps);
+ if (ret >= 0)
+ data->ps_period = period;
+
+ return ret;
+ default:
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_event_spec ltr501_als_event_spec[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE),
+ }, {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE),
+ }, {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE) |
+ BIT(IIO_EV_INFO_PERIOD),
+ },
+
+};
+
+static const struct iio_event_spec ltr501_pxs_event_spec[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE),
+ }, {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE),
+ }, {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE) |
+ BIT(IIO_EV_INFO_PERIOD),
+ },
+};
+
+#define LTR501_INTENSITY_CHANNEL(_idx, _addr, _mod, _shared, \
+ _evspec, _evsize) { \
.type = IIO_INTENSITY, \
.modified = 1, \
.address = (_addr), \
@@ -101,13 +569,27 @@ static int ltr501_read_ps(struct ltr501_data *data)
.realbits = 16, \
.storagebits = 16, \
.endianness = IIO_CPU, \
- } \
+ }, \
+ .event_spec = _evspec,\
+ .num_event_specs = _evsize,\
+}
+
+#define LTR501_LIGHT_CHANNEL() { \
+ .type = IIO_LIGHT, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
+ .scan_index = -1, \
}
static const struct iio_chan_spec ltr501_channels[] = {
- LTR501_INTENSITY_CHANNEL(0, LTR501_ALS_DATA0, IIO_MOD_LIGHT_BOTH, 0),
+ LTR501_LIGHT_CHANNEL(),
+ LTR501_INTENSITY_CHANNEL(0, LTR501_ALS_DATA0, IIO_MOD_LIGHT_BOTH, 0,
+ ltr501_als_event_spec,
+ ARRAY_SIZE(ltr501_als_event_spec)),
LTR501_INTENSITY_CHANNEL(1, LTR501_ALS_DATA1, IIO_MOD_LIGHT_IR,
- BIT(IIO_CHAN_INFO_SCALE)),
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ NULL, 0),
{
.type = IIO_PROXIMITY,
.address = LTR501_PS_DATA,
@@ -120,23 +602,51 @@ static const struct iio_chan_spec ltr501_channels[] = {
.storagebits = 16,
.endianness = IIO_CPU,
},
+ .event_spec = ltr501_pxs_event_spec,
+ .num_event_specs = ARRAY_SIZE(ltr501_pxs_event_spec),
},
IIO_CHAN_SOFT_TIMESTAMP(3),
};
-static const int ltr501_ps_gain[4][2] = {
- {1, 0}, {0, 250000}, {0, 125000}, {0, 62500}
+static const struct iio_chan_spec ltr301_channels[] = {
+ LTR501_LIGHT_CHANNEL(),
+ LTR501_INTENSITY_CHANNEL(0, LTR501_ALS_DATA0, IIO_MOD_LIGHT_BOTH, 0,
+ ltr501_als_event_spec,
+ ARRAY_SIZE(ltr501_als_event_spec)),
+ LTR501_INTENSITY_CHANNEL(1, LTR501_ALS_DATA1, IIO_MOD_LIGHT_IR,
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ NULL, 0),
+ IIO_CHAN_SOFT_TIMESTAMP(2),
};
static int ltr501_read_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int *val, int *val2, long mask)
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
{
struct ltr501_data *data = iio_priv(indio_dev);
__le16 buf[2];
int ret, i;
switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ if (iio_buffer_enabled(indio_dev))
+ return -EBUSY;
+
+ switch (chan->type) {
+ case IIO_LIGHT:
+ mutex_lock(&data->lock_als);
+ ret = ltr501_read_als(data, buf);
+ mutex_unlock(&data->lock_als);
+ if (ret < 0)
+ return ret;
+ *val = ltr501_calculate_lux(le16_to_cpu(buf[1]),
+ le16_to_cpu(buf[0]));
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
case IIO_CHAN_INFO_RAW:
if (iio_buffer_enabled(indio_dev))
return -EBUSY;
@@ -149,7 +659,7 @@ static int ltr501_read_raw(struct iio_dev *indio_dev,
if (ret < 0)
return ret;
*val = le16_to_cpu(chan->address == LTR501_ALS_DATA1 ?
- buf[0] : buf[1]);
+ buf[0] : buf[1]);
return IIO_VAL_INT;
case IIO_PROXIMITY:
mutex_lock(&data->lock_ps);
@@ -165,45 +675,59 @@ static int ltr501_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_INTENSITY:
- if (data->als_contr & LTR501_CONTR_ALS_GAIN_MASK) {
- *val = 0;
- *val2 = 5000;
- return IIO_VAL_INT_PLUS_MICRO;
- } else {
- *val = 1;
- *val2 = 0;
- return IIO_VAL_INT;
- }
+ i = (data->als_contr & data->chip_info->als_gain_mask)
+ >> data->chip_info->als_gain_shift;
+ *val = data->chip_info->als_gain[i].scale;
+ *val2 = data->chip_info->als_gain[i].uscale;
+ return IIO_VAL_INT_PLUS_MICRO;
case IIO_PROXIMITY:
i = (data->ps_contr & LTR501_CONTR_PS_GAIN_MASK) >>
LTR501_CONTR_PS_GAIN_SHIFT;
- *val = ltr501_ps_gain[i][0];
- *val2 = ltr501_ps_gain[i][1];
+ *val = data->chip_info->ps_gain[i].scale;
+ *val2 = data->chip_info->ps_gain[i].uscale;
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
+ case IIO_CHAN_INFO_INT_TIME:
+ switch (chan->type) {
+ case IIO_INTENSITY:
+ return ltr501_read_it_time(data, val, val2);
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ switch (chan->type) {
+ case IIO_INTENSITY:
+ return ltr501_als_read_samp_freq(data, val, val2);
+ case IIO_PROXIMITY:
+ return ltr501_ps_read_samp_freq(data, val, val2);
+ default:
+ return -EINVAL;
+ }
}
return -EINVAL;
}
-static int ltr501_get_ps_gain_index(int val, int val2)
+static int ltr501_get_gain_index(struct ltr501_gain *gain, int size,
+ int val, int val2)
{
int i;
- for (i = 0; i < ARRAY_SIZE(ltr501_ps_gain); i++)
- if (val == ltr501_ps_gain[i][0] && val2 == ltr501_ps_gain[i][1])
+ for (i = 0; i < size; i++)
+ if (val == gain[i].scale && val2 == gain[i].uscale)
return i;
return -1;
}
static int ltr501_write_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int val, int val2, long mask)
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
{
struct ltr501_data *data = iio_priv(indio_dev);
- int i;
+ int i, ret, freq_val, freq_val2;
+ struct ltr501_chip_info *info = data->chip_info;
if (iio_buffer_enabled(indio_dev))
return -EBUSY;
@@ -212,35 +736,382 @@ static int ltr501_write_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_INTENSITY:
- if (val == 0 && val2 == 5000)
- data->als_contr |= LTR501_CONTR_ALS_GAIN_MASK;
- else if (val == 1 && val2 == 0)
- data->als_contr &= ~LTR501_CONTR_ALS_GAIN_MASK;
- else
+ i = ltr501_get_gain_index(info->als_gain,
+ info->als_gain_tbl_size,
+ val, val2);
+ if (i < 0)
return -EINVAL;
- return i2c_smbus_write_byte_data(data->client,
- LTR501_ALS_CONTR, data->als_contr);
+
+ data->als_contr &= ~info->als_gain_mask;
+ data->als_contr |= i << info->als_gain_shift;
+
+ return regmap_write(data->regmap, LTR501_ALS_CONTR,
+ data->als_contr);
case IIO_PROXIMITY:
- i = ltr501_get_ps_gain_index(val, val2);
+ i = ltr501_get_gain_index(info->ps_gain,
+ info->ps_gain_tbl_size,
+ val, val2);
if (i < 0)
return -EINVAL;
data->ps_contr &= ~LTR501_CONTR_PS_GAIN_MASK;
data->ps_contr |= i << LTR501_CONTR_PS_GAIN_SHIFT;
- return i2c_smbus_write_byte_data(data->client,
- LTR501_PS_CONTR, data->ps_contr);
+
+ return regmap_write(data->regmap, LTR501_PS_CONTR,
+ data->ps_contr);
default:
return -EINVAL;
}
+ case IIO_CHAN_INFO_INT_TIME:
+ switch (chan->type) {
+ case IIO_INTENSITY:
+ if (val != 0)
+ return -EINVAL;
+ mutex_lock(&data->lock_als);
+ i = ltr501_set_it_time(data, val2);
+ mutex_unlock(&data->lock_als);
+ return i;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ switch (chan->type) {
+ case IIO_INTENSITY:
+ ret = ltr501_als_read_samp_freq(data, &freq_val,
+ &freq_val2);
+ if (ret < 0)
+ return ret;
+
+ ret = ltr501_als_write_samp_freq(data, val, val2);
+ if (ret < 0)
+ return ret;
+
+ /* update persistence count when changing frequency */
+ ret = ltr501_write_intr_prst(data, chan->type,
+ 0, data->als_period);
+
+ if (ret < 0)
+ return ltr501_als_write_samp_freq(data,
+ freq_val,
+ freq_val2);
+ return ret;
+ case IIO_PROXIMITY:
+ ret = ltr501_ps_read_samp_freq(data, &freq_val,
+ &freq_val2);
+ if (ret < 0)
+ return ret;
+
+ ret = ltr501_ps_write_samp_freq(data, val, val2);
+ if (ret < 0)
+ return ret;
+
+ /* update persistence count when changing frequency */
+ ret = ltr501_write_intr_prst(data, chan->type,
+ 0, data->ps_period);
+
+ if (ret < 0)
+ return ltr501_ps_write_samp_freq(data,
+ freq_val,
+ freq_val2);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+ }
+ return -EINVAL;
+}
+
+static int ltr501_read_thresh(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 ltr501_data *data = iio_priv(indio_dev);
+ int ret, thresh_data;
+
+ switch (chan->type) {
+ case IIO_INTENSITY:
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ ret = regmap_bulk_read(data->regmap,
+ LTR501_ALS_THRESH_UP,
+ &thresh_data, 2);
+ if (ret < 0)
+ return ret;
+ *val = thresh_data & LTR501_ALS_THRESH_MASK;
+ return IIO_VAL_INT;
+ case IIO_EV_DIR_FALLING:
+ ret = regmap_bulk_read(data->regmap,
+ LTR501_ALS_THRESH_LOW,
+ &thresh_data, 2);
+ if (ret < 0)
+ return ret;
+ *val = thresh_data & LTR501_ALS_THRESH_MASK;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ case IIO_PROXIMITY:
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ ret = regmap_bulk_read(data->regmap,
+ LTR501_PS_THRESH_UP,
+ &thresh_data, 2);
+ if (ret < 0)
+ return ret;
+ *val = thresh_data & LTR501_PS_THRESH_MASK;
+ return IIO_VAL_INT;
+ case IIO_EV_DIR_FALLING:
+ ret = regmap_bulk_read(data->regmap,
+ LTR501_PS_THRESH_LOW,
+ &thresh_data, 2);
+ if (ret < 0)
+ return ret;
+ *val = thresh_data & LTR501_PS_THRESH_MASK;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
}
+
return -EINVAL;
}
-static IIO_CONST_ATTR(in_proximity_scale_available, "1 0.25 0.125 0.0625");
-static IIO_CONST_ATTR(in_intensity_scale_available, "1 0.005");
+static int ltr501_write_thresh(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 ltr501_data *data = iio_priv(indio_dev);
+ int ret;
+
+ if (val < 0)
+ return -EINVAL;
+
+ switch (chan->type) {
+ case IIO_INTENSITY:
+ if (val > LTR501_ALS_THRESH_MASK)
+ return -EINVAL;
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ mutex_lock(&data->lock_als);
+ ret = regmap_bulk_write(data->regmap,
+ LTR501_ALS_THRESH_UP,
+ &val, 2);
+ mutex_unlock(&data->lock_als);
+ return ret;
+ case IIO_EV_DIR_FALLING:
+ mutex_lock(&data->lock_als);
+ ret = regmap_bulk_write(data->regmap,
+ LTR501_ALS_THRESH_LOW,
+ &val, 2);
+ mutex_unlock(&data->lock_als);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+ case IIO_PROXIMITY:
+ if (val > LTR501_PS_THRESH_MASK)
+ return -EINVAL;
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ mutex_lock(&data->lock_ps);
+ ret = regmap_bulk_write(data->regmap,
+ LTR501_PS_THRESH_UP,
+ &val, 2);
+ mutex_unlock(&data->lock_ps);
+ return ret;
+ case IIO_EV_DIR_FALLING:
+ mutex_lock(&data->lock_ps);
+ ret = regmap_bulk_write(data->regmap,
+ LTR501_PS_THRESH_LOW,
+ &val, 2);
+ mutex_unlock(&data->lock_ps);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+}
+
+static int ltr501_read_event(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;
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ return ltr501_read_thresh(indio_dev, chan, type, dir,
+ info, val, val2);
+ case IIO_EV_INFO_PERIOD:
+ ret = ltr501_read_intr_prst(iio_priv(indio_dev),
+ chan->type, val2);
+ *val = *val2 / 1000000;
+ *val2 = *val2 % 1000000;
+ return ret;
+ default:
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+}
+
+static int ltr501_write_event(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)
+{
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ if (val2 != 0)
+ return -EINVAL;
+ return ltr501_write_thresh(indio_dev, chan, type, dir,
+ info, val, val2);
+ case IIO_EV_INFO_PERIOD:
+ return ltr501_write_intr_prst(iio_priv(indio_dev), chan->type,
+ val, val2);
+ default:
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+}
+
+static int ltr501_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ struct ltr501_data *data = iio_priv(indio_dev);
+ int ret, status;
+
+ switch (chan->type) {
+ case IIO_INTENSITY:
+ ret = regmap_field_read(data->reg_als_intr, &status);
+ if (ret < 0)
+ return ret;
+ return status;
+ case IIO_PROXIMITY:
+ ret = regmap_field_read(data->reg_ps_intr, &status);
+ if (ret < 0)
+ return ret;
+ return status;
+ default:
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+}
+
+static int ltr501_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)
+{
+ struct ltr501_data *data = iio_priv(indio_dev);
+ int ret;
+
+ /* only 1 and 0 are valid inputs */
+ if (state != 1 && state != 0)
+ return -EINVAL;
+
+ switch (chan->type) {
+ case IIO_INTENSITY:
+ mutex_lock(&data->lock_als);
+ ret = regmap_field_write(data->reg_als_intr, state);
+ mutex_unlock(&data->lock_als);
+ return ret;
+ case IIO_PROXIMITY:
+ mutex_lock(&data->lock_ps);
+ ret = regmap_field_write(data->reg_ps_intr, state);
+ mutex_unlock(&data->lock_ps);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+}
+
+static ssize_t ltr501_show_proximity_scale_avail(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ltr501_data *data = iio_priv(dev_to_iio_dev(dev));
+ struct ltr501_chip_info *info = data->chip_info;
+ ssize_t len = 0;
+ int i;
+
+ for (i = 0; i < info->ps_gain_tbl_size; i++) {
+ if (info->ps_gain[i].scale == LTR501_RESERVED_GAIN)
+ continue;
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06d ",
+ info->ps_gain[i].scale,
+ info->ps_gain[i].uscale);
+ }
+
+ buf[len - 1] = '\n';
+
+ return len;
+}
+
+static ssize_t ltr501_show_intensity_scale_avail(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ltr501_data *data = iio_priv(dev_to_iio_dev(dev));
+ struct ltr501_chip_info *info = data->chip_info;
+ ssize_t len = 0;
+ int i;
+
+ for (i = 0; i < info->als_gain_tbl_size; i++) {
+ if (info->als_gain[i].scale == LTR501_RESERVED_GAIN)
+ continue;
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06d ",
+ info->als_gain[i].scale,
+ info->als_gain[i].uscale);
+ }
+
+ buf[len - 1] = '\n';
+
+ return len;
+}
+
+static IIO_CONST_ATTR_INT_TIME_AVAIL("0.05 0.1 0.2 0.4");
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("20 10 5 2 1 0.5");
+
+static IIO_DEVICE_ATTR(in_proximity_scale_available, S_IRUGO,
+ ltr501_show_proximity_scale_avail, NULL, 0);
+static IIO_DEVICE_ATTR(in_intensity_scale_available, S_IRUGO,
+ ltr501_show_intensity_scale_avail, NULL, 0);
static struct attribute *ltr501_attributes[] = {
- &iio_const_attr_in_proximity_scale_available.dev_attr.attr,
- &iio_const_attr_in_intensity_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_proximity_scale_available.dev_attr.attr,
+ &iio_dev_attr_in_intensity_scale_available.dev_attr.attr,
+ &iio_const_attr_integration_time_available.dev_attr.attr,
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ NULL
+};
+
+static struct attribute *ltr301_attributes[] = {
+ &iio_dev_attr_in_intensity_scale_available.dev_attr.attr,
+ &iio_const_attr_integration_time_available.dev_attr.attr,
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
NULL
};
@@ -248,20 +1119,98 @@ static const struct attribute_group ltr501_attribute_group = {
.attrs = ltr501_attributes,
};
+static const struct attribute_group ltr301_attribute_group = {
+ .attrs = ltr301_attributes,
+};
+
+static const struct iio_info ltr501_info_no_irq = {
+ .read_raw = ltr501_read_raw,
+ .write_raw = ltr501_write_raw,
+ .attrs = &ltr501_attribute_group,
+ .driver_module = THIS_MODULE,
+};
+
static const struct iio_info ltr501_info = {
.read_raw = ltr501_read_raw,
.write_raw = ltr501_write_raw,
.attrs = &ltr501_attribute_group,
+ .read_event_value = &ltr501_read_event,
+ .write_event_value = &ltr501_write_event,
+ .read_event_config = &ltr501_read_event_config,
+ .write_event_config = &ltr501_write_event_config,
+ .driver_module = THIS_MODULE,
+};
+
+static const struct iio_info ltr301_info_no_irq = {
+ .read_raw = ltr501_read_raw,
+ .write_raw = ltr501_write_raw,
+ .attrs = &ltr301_attribute_group,
.driver_module = THIS_MODULE,
};
-static int ltr501_write_contr(struct i2c_client *client, u8 als_val, u8 ps_val)
+static const struct iio_info ltr301_info = {
+ .read_raw = ltr501_read_raw,
+ .write_raw = ltr501_write_raw,
+ .attrs = &ltr301_attribute_group,
+ .read_event_value = &ltr501_read_event,
+ .write_event_value = &ltr501_write_event,
+ .read_event_config = &ltr501_read_event_config,
+ .write_event_config = &ltr501_write_event_config,
+ .driver_module = THIS_MODULE,
+};
+
+static struct ltr501_chip_info ltr501_chip_info_tbl[] = {
+ [ltr501] = {
+ .partid = 0x08,
+ .als_gain = ltr501_als_gain_tbl,
+ .als_gain_tbl_size = ARRAY_SIZE(ltr501_als_gain_tbl),
+ .ps_gain = ltr501_ps_gain_tbl,
+ .ps_gain_tbl_size = ARRAY_SIZE(ltr501_ps_gain_tbl),
+ .als_mode_active = BIT(0) | BIT(1),
+ .als_gain_mask = BIT(3),
+ .als_gain_shift = 3,
+ .info = &ltr501_info,
+ .info_no_irq = &ltr501_info_no_irq,
+ .channels = ltr501_channels,
+ .no_channels = ARRAY_SIZE(ltr501_channels),
+ },
+ [ltr559] = {
+ .partid = 0x09,
+ .als_gain = ltr559_als_gain_tbl,
+ .als_gain_tbl_size = ARRAY_SIZE(ltr559_als_gain_tbl),
+ .ps_gain = ltr559_ps_gain_tbl,
+ .ps_gain_tbl_size = ARRAY_SIZE(ltr559_ps_gain_tbl),
+ .als_mode_active = BIT(1),
+ .als_gain_mask = BIT(2) | BIT(3) | BIT(4),
+ .als_gain_shift = 2,
+ .info = &ltr501_info,
+ .info_no_irq = &ltr501_info_no_irq,
+ .channels = ltr501_channels,
+ .no_channels = ARRAY_SIZE(ltr501_channels),
+ },
+ [ltr301] = {
+ .partid = 0x08,
+ .als_gain = ltr501_als_gain_tbl,
+ .als_gain_tbl_size = ARRAY_SIZE(ltr501_als_gain_tbl),
+ .als_mode_active = BIT(0) | BIT(1),
+ .als_gain_mask = BIT(3),
+ .als_gain_shift = 3,
+ .info = &ltr301_info,
+ .info_no_irq = &ltr301_info_no_irq,
+ .channels = ltr301_channels,
+ .no_channels = ARRAY_SIZE(ltr301_channels),
+ },
+};
+
+static int ltr501_write_contr(struct ltr501_data *data, u8 als_val, u8 ps_val)
{
- int ret = i2c_smbus_write_byte_data(client, LTR501_ALS_CONTR, als_val);
+ int ret;
+
+ ret = regmap_write(data->regmap, LTR501_ALS_CONTR, als_val);
if (ret < 0)
return ret;
- return i2c_smbus_write_byte_data(client, LTR501_PS_CONTR, ps_val);
+ return regmap_write(data->regmap, LTR501_PS_CONTR, ps_val);
}
static irqreturn_t ltr501_trigger_handler(int irq, void *p)
@@ -273,13 +1222,13 @@ static irqreturn_t ltr501_trigger_handler(int irq, void *p)
__le16 als_buf[2];
u8 mask = 0;
int j = 0;
- int ret;
+ int ret, psdata;
memset(buf, 0, sizeof(buf));
/* figure out which data needs to be ready */
if (test_bit(0, indio_dev->active_scan_mask) ||
- test_bit(1, indio_dev->active_scan_mask))
+ test_bit(1, indio_dev->active_scan_mask))
mask |= LTR501_STATUS_ALS_RDY;
if (test_bit(2, indio_dev->active_scan_mask))
mask |= LTR501_STATUS_PS_RDY;
@@ -289,8 +1238,8 @@ static irqreturn_t ltr501_trigger_handler(int irq, void *p)
goto done;
if (mask & LTR501_STATUS_ALS_RDY) {
- ret = i2c_smbus_read_i2c_block_data(data->client,
- LTR501_ALS_DATA1, sizeof(als_buf), (u8 *) als_buf);
+ ret = regmap_bulk_read(data->regmap, LTR501_ALS_DATA1,
+ (u8 *)als_buf, sizeof(als_buf));
if (ret < 0)
return ret;
if (test_bit(0, indio_dev->active_scan_mask))
@@ -300,14 +1249,14 @@ static irqreturn_t ltr501_trigger_handler(int irq, void *p)
}
if (mask & LTR501_STATUS_PS_RDY) {
- ret = i2c_smbus_read_word_data(data->client, LTR501_PS_DATA);
+ ret = regmap_bulk_read(data->regmap, LTR501_PS_DATA,
+ &psdata, 2);
if (ret < 0)
goto done;
- buf[j++] = ret & LTR501_PS_DATA_MASK;
+ buf[j++] = psdata & LTR501_PS_DATA_MASK;
}
- iio_push_to_buffers_with_timestamp(indio_dev, buf,
- iio_get_time_ns());
+ iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns());
done:
iio_trigger_notify_done(indio_dev->trig);
@@ -315,67 +1264,225 @@ done:
return IRQ_HANDLED;
}
+static irqreturn_t ltr501_interrupt_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct ltr501_data *data = iio_priv(indio_dev);
+ int ret, status;
+
+ ret = regmap_read(data->regmap, LTR501_ALS_PS_STATUS, &status);
+ if (ret < 0) {
+ dev_err(&data->client->dev,
+ "irq read int reg failed\n");
+ return IRQ_HANDLED;
+ }
+
+ if (status & LTR501_STATUS_ALS_INTR)
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER),
+ iio_get_time_ns());
+
+ if (status & LTR501_STATUS_PS_INTR)
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER),
+ iio_get_time_ns());
+
+ return IRQ_HANDLED;
+}
+
static int ltr501_init(struct ltr501_data *data)
{
- int ret;
+ int ret, status;
- ret = i2c_smbus_read_byte_data(data->client, LTR501_ALS_CONTR);
+ ret = regmap_read(data->regmap, LTR501_ALS_CONTR, &status);
if (ret < 0)
return ret;
- data->als_contr = ret | LTR501_CONTR_ACTIVE;
- ret = i2c_smbus_read_byte_data(data->client, LTR501_PS_CONTR);
+ data->als_contr = status | data->chip_info->als_mode_active;
+
+ ret = regmap_read(data->regmap, LTR501_PS_CONTR, &status);
if (ret < 0)
return ret;
- data->ps_contr = ret | LTR501_CONTR_ACTIVE;
- return ltr501_write_contr(data->client, data->als_contr,
- data->ps_contr);
+ data->ps_contr = status | LTR501_CONTR_ACTIVE;
+
+ ret = ltr501_read_intr_prst(data, IIO_INTENSITY, &data->als_period);
+ if (ret < 0)
+ return ret;
+
+ ret = ltr501_read_intr_prst(data, IIO_PROXIMITY, &data->ps_period);
+ if (ret < 0)
+ return ret;
+
+ return ltr501_write_contr(data, data->als_contr, data->ps_contr);
+}
+
+static bool ltr501_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case LTR501_ALS_DATA1:
+ case LTR501_ALS_DATA0:
+ case LTR501_ALS_PS_STATUS:
+ case LTR501_PS_DATA:
+ return true;
+ default:
+ return false;
+ }
}
+static struct regmap_config ltr501_regmap_config = {
+ .name = LTR501_REGMAP_NAME,
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = LTR501_MAX_REG,
+ .cache_type = REGCACHE_RBTREE,
+ .volatile_reg = ltr501_is_volatile_reg,
+};
+
static int ltr501_powerdown(struct ltr501_data *data)
{
- return ltr501_write_contr(data->client,
- data->als_contr & ~LTR501_CONTR_ACTIVE,
+ return ltr501_write_contr(data, data->als_contr &
+ ~data->chip_info->als_mode_active,
data->ps_contr & ~LTR501_CONTR_ACTIVE);
}
+static const char *ltr501_match_acpi_device(struct device *dev, int *chip_idx)
+{
+ const struct acpi_device_id *id;
+
+ id = acpi_match_device(dev->driver->acpi_match_table, dev);
+ if (!id)
+ return NULL;
+ *chip_idx = id->driver_data;
+ return dev_name(dev);
+}
+
static int ltr501_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+ const struct i2c_device_id *id)
{
struct ltr501_data *data;
struct iio_dev *indio_dev;
- int ret;
+ struct regmap *regmap;
+ int ret, partid, chip_idx = 0;
+ const char *name = NULL;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
+ regmap = devm_regmap_init_i2c(client, &ltr501_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(&client->dev, "Regmap initialization failed.\n");
+ return PTR_ERR(regmap);
+ }
+
data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
data->client = client;
+ data->regmap = regmap;
mutex_init(&data->lock_als);
mutex_init(&data->lock_ps);
- ret = i2c_smbus_read_byte_data(data->client, LTR501_PART_ID);
+ data->reg_it = devm_regmap_field_alloc(&client->dev, regmap,
+ reg_field_it);
+ if (IS_ERR(data->reg_it)) {
+ dev_err(&client->dev, "Integ time reg field init failed.\n");
+ return PTR_ERR(data->reg_it);
+ }
+
+ data->reg_als_intr = devm_regmap_field_alloc(&client->dev, regmap,
+ reg_field_als_intr);
+ if (IS_ERR(data->reg_als_intr)) {
+ dev_err(&client->dev, "ALS intr mode reg field init failed\n");
+ return PTR_ERR(data->reg_als_intr);
+ }
+
+ data->reg_ps_intr = devm_regmap_field_alloc(&client->dev, regmap,
+ reg_field_ps_intr);
+ if (IS_ERR(data->reg_ps_intr)) {
+ dev_err(&client->dev, "PS intr mode reg field init failed.\n");
+ return PTR_ERR(data->reg_ps_intr);
+ }
+
+ data->reg_als_rate = devm_regmap_field_alloc(&client->dev, regmap,
+ reg_field_als_rate);
+ if (IS_ERR(data->reg_als_rate)) {
+ dev_err(&client->dev, "ALS samp rate field init failed.\n");
+ return PTR_ERR(data->reg_als_rate);
+ }
+
+ data->reg_ps_rate = devm_regmap_field_alloc(&client->dev, regmap,
+ reg_field_ps_rate);
+ if (IS_ERR(data->reg_ps_rate)) {
+ dev_err(&client->dev, "PS samp rate field init failed.\n");
+ return PTR_ERR(data->reg_ps_rate);
+ }
+
+ data->reg_als_prst = devm_regmap_field_alloc(&client->dev, regmap,
+ reg_field_als_prst);
+ if (IS_ERR(data->reg_als_prst)) {
+ dev_err(&client->dev, "ALS prst reg field init failed\n");
+ return PTR_ERR(data->reg_als_prst);
+ }
+
+ data->reg_ps_prst = devm_regmap_field_alloc(&client->dev, regmap,
+ reg_field_ps_prst);
+ if (IS_ERR(data->reg_ps_prst)) {
+ dev_err(&client->dev, "PS prst reg field init failed.\n");
+ return PTR_ERR(data->reg_ps_prst);
+ }
+
+ ret = regmap_read(data->regmap, LTR501_PART_ID, &partid);
if (ret < 0)
return ret;
- if ((ret >> 4) != 0x8)
+
+ if (id) {
+ name = id->name;
+ chip_idx = id->driver_data;
+ } else if (ACPI_HANDLE(&client->dev)) {
+ name = ltr501_match_acpi_device(&client->dev, &chip_idx);
+ } else {
+ return -ENODEV;
+ }
+
+ data->chip_info = &ltr501_chip_info_tbl[chip_idx];
+
+ if ((partid >> 4) != data->chip_info->partid)
return -ENODEV;
indio_dev->dev.parent = &client->dev;
- indio_dev->info = &ltr501_info;
- indio_dev->channels = ltr501_channels;
- indio_dev->num_channels = ARRAY_SIZE(ltr501_channels);
- indio_dev->name = LTR501_DRV_NAME;
+ indio_dev->info = data->chip_info->info;
+ indio_dev->channels = data->chip_info->channels;
+ indio_dev->num_channels = data->chip_info->no_channels;
+ indio_dev->name = name;
indio_dev->modes = INDIO_DIRECT_MODE;
ret = ltr501_init(data);
if (ret < 0)
return ret;
+ if (client->irq > 0) {
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, ltr501_interrupt_handler,
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ "ltr501_thresh_event",
+ indio_dev);
+ if (ret) {
+ dev_err(&client->dev, "request irq (%d) failed\n",
+ client->irq);
+ return ret;
+ }
+ } else {
+ indio_dev->info = data->chip_info->info_no_irq;
+ }
+
ret = iio_triggered_buffer_setup(indio_dev, NULL,
- ltr501_trigger_handler, NULL);
+ ltr501_trigger_handler, NULL);
if (ret)
goto powerdown_on_error;
@@ -407,24 +1514,34 @@ static int ltr501_remove(struct i2c_client *client)
static int ltr501_suspend(struct device *dev)
{
struct ltr501_data *data = iio_priv(i2c_get_clientdata(
- to_i2c_client(dev)));
+ to_i2c_client(dev)));
return ltr501_powerdown(data);
}
static int ltr501_resume(struct device *dev)
{
struct ltr501_data *data = iio_priv(i2c_get_clientdata(
- to_i2c_client(dev)));
+ to_i2c_client(dev)));
- return ltr501_write_contr(data->client, data->als_contr,
+ return ltr501_write_contr(data, data->als_contr,
data->ps_contr);
}
#endif
static SIMPLE_DEV_PM_OPS(ltr501_pm_ops, ltr501_suspend, ltr501_resume);
+static const struct acpi_device_id ltr_acpi_match[] = {
+ {"LTER0501", ltr501},
+ {"LTER0559", ltr559},
+ {"LTER0301", ltr301},
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, ltr_acpi_match);
+
static const struct i2c_device_id ltr501_id[] = {
- { "ltr501", 0 },
+ { "ltr501", ltr501},
+ { "ltr559", ltr559},
+ { "ltr301", ltr301},
{ }
};
MODULE_DEVICE_TABLE(i2c, ltr501_id);
@@ -433,6 +1550,7 @@ static struct i2c_driver ltr501_driver = {
.driver = {
.name = LTR501_DRV_NAME,
.pm = &ltr501_pm_ops,
+ .acpi_match_table = ACPI_PTR(ltr_acpi_match),
.owner = THIS_MODULE,
},
.probe = ltr501_probe,
diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c
new file mode 100644
index 000000000..11a027adc
--- /dev/null
+++ b/drivers/iio/light/stk3310.c
@@ -0,0 +1,703 @@
+/**
+ * Sensortek STK3310/STK3311 Ambient Light and Proximity Sensor
+ *
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This file is subject to the terms and conditions of version 2 of
+ * the GNU General Public License. See the file COPYING in the main
+ * directory of this archive for more details.
+ *
+ * IIO driver for STK3310/STK3311. 7-bit I2C address: 0x48.
+ */
+
+#include <linux/acpi.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iio/events.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define STK3310_REG_STATE 0x00
+#define STK3310_REG_PSCTRL 0x01
+#define STK3310_REG_ALSCTRL 0x02
+#define STK3310_REG_INT 0x04
+#define STK3310_REG_THDH_PS 0x06
+#define STK3310_REG_THDL_PS 0x08
+#define STK3310_REG_FLAG 0x10
+#define STK3310_REG_PS_DATA_MSB 0x11
+#define STK3310_REG_PS_DATA_LSB 0x12
+#define STK3310_REG_ALS_DATA_MSB 0x13
+#define STK3310_REG_ALS_DATA_LSB 0x14
+#define STK3310_REG_ID 0x3E
+#define STK3310_MAX_REG 0x80
+
+#define STK3310_STATE_EN_PS 0x01
+#define STK3310_STATE_EN_ALS 0x02
+#define STK3310_STATE_STANDBY 0x00
+
+#define STK3310_CHIP_ID_VAL 0x13
+#define STK3311_CHIP_ID_VAL 0x1D
+#define STK3310_PSINT_EN 0x01
+#define STK3310_PS_MAX_VAL 0xFFFF
+
+#define STK3310_DRIVER_NAME "stk3310"
+#define STK3310_REGMAP_NAME "stk3310_regmap"
+#define STK3310_EVENT "stk3310_event"
+#define STK3310_GPIO "stk3310_gpio"
+
+#define STK3310_SCALE_AVAILABLE "6.4 1.6 0.4 0.1"
+
+#define STK3310_IT_AVAILABLE \
+ "0.000185 0.000370 0.000741 0.001480 0.002960 0.005920 0.011840 " \
+ "0.023680 0.047360 0.094720 0.189440 0.378880 0.757760 1.515520 " \
+ "3.031040 6.062080"
+
+#define STK3310_REGFIELD(name) \
+ do { \
+ data->reg_##name = \
+ devm_regmap_field_alloc(&client->dev, regmap, \
+ stk3310_reg_field_##name); \
+ if (IS_ERR(data->reg_##name)) { \
+ dev_err(&client->dev, "reg field alloc failed.\n"); \
+ return PTR_ERR(data->reg_##name); \
+ } \
+ } while (0)
+
+static const struct reg_field stk3310_reg_field_state =
+ REG_FIELD(STK3310_REG_STATE, 0, 2);
+static const struct reg_field stk3310_reg_field_als_gain =
+ REG_FIELD(STK3310_REG_ALSCTRL, 4, 5);
+static const struct reg_field stk3310_reg_field_ps_gain =
+ REG_FIELD(STK3310_REG_PSCTRL, 4, 5);
+static const struct reg_field stk3310_reg_field_als_it =
+ REG_FIELD(STK3310_REG_ALSCTRL, 0, 3);
+static const struct reg_field stk3310_reg_field_ps_it =
+ REG_FIELD(STK3310_REG_PSCTRL, 0, 3);
+static const struct reg_field stk3310_reg_field_int_ps =
+ REG_FIELD(STK3310_REG_INT, 0, 2);
+static const struct reg_field stk3310_reg_field_flag_psint =
+ REG_FIELD(STK3310_REG_FLAG, 4, 4);
+static const struct reg_field stk3310_reg_field_flag_nf =
+ REG_FIELD(STK3310_REG_FLAG, 0, 0);
+
+/* Estimate maximum proximity values with regard to measurement scale. */
+static const int stk3310_ps_max[4] = {
+ STK3310_PS_MAX_VAL / 640,
+ STK3310_PS_MAX_VAL / 160,
+ STK3310_PS_MAX_VAL / 40,
+ STK3310_PS_MAX_VAL / 10
+};
+
+static const int stk3310_scale_table[][2] = {
+ {6, 400000}, {1, 600000}, {0, 400000}, {0, 100000}
+};
+
+/* Integration time in seconds, microseconds */
+static const int stk3310_it_table[][2] = {
+ {0, 185}, {0, 370}, {0, 741}, {0, 1480},
+ {0, 2960}, {0, 5920}, {0, 11840}, {0, 23680},
+ {0, 47360}, {0, 94720}, {0, 189440}, {0, 378880},
+ {0, 757760}, {1, 515520}, {3, 31040}, {6, 62080},
+};
+
+struct stk3310_data {
+ struct i2c_client *client;
+ struct mutex lock;
+ bool als_enabled;
+ bool ps_enabled;
+ u64 timestamp;
+ struct regmap *regmap;
+ struct regmap_field *reg_state;
+ struct regmap_field *reg_als_gain;
+ struct regmap_field *reg_ps_gain;
+ struct regmap_field *reg_als_it;
+ struct regmap_field *reg_ps_it;
+ struct regmap_field *reg_int_ps;
+ struct regmap_field *reg_flag_psint;
+ struct regmap_field *reg_flag_nf;
+};
+
+static const struct iio_event_spec stk3310_events[] = {
+ /* Proximity event */
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+ /* Out-of-proximity event */
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
+static const struct iio_chan_spec stk3310_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .info_mask_separate =
+ BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_INT_TIME),
+ },
+ {
+ .type = IIO_PROXIMITY,
+ .info_mask_separate =
+ BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_INT_TIME),
+ .event_spec = stk3310_events,
+ .num_event_specs = ARRAY_SIZE(stk3310_events),
+ }
+};
+
+static IIO_CONST_ATTR(in_illuminance_scale_available, STK3310_SCALE_AVAILABLE);
+
+static IIO_CONST_ATTR(in_proximity_scale_available, STK3310_SCALE_AVAILABLE);
+
+static IIO_CONST_ATTR(in_illuminance_integration_time_available,
+ STK3310_IT_AVAILABLE);
+
+static IIO_CONST_ATTR(in_proximity_integration_time_available,
+ STK3310_IT_AVAILABLE);
+
+static struct attribute *stk3310_attributes[] = {
+ &iio_const_attr_in_illuminance_scale_available.dev_attr.attr,
+ &iio_const_attr_in_proximity_scale_available.dev_attr.attr,
+ &iio_const_attr_in_illuminance_integration_time_available.dev_attr.attr,
+ &iio_const_attr_in_proximity_integration_time_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group stk3310_attribute_group = {
+ .attrs = stk3310_attributes
+};
+
+static int stk3310_get_index(const int table[][2], int table_size,
+ int val, int val2)
+{
+ int i;
+
+ for (i = 0; i < table_size; i++) {
+ if (val == table[i][0] && val2 == table[i][1])
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int stk3310_read_event(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)
+{
+ u8 reg;
+ __be16 buf;
+ int ret;
+ struct stk3310_data *data = iio_priv(indio_dev);
+
+ if (info != IIO_EV_INFO_VALUE)
+ return -EINVAL;
+
+ /* Only proximity interrupts are implemented at the moment. */
+ if (dir == IIO_EV_DIR_RISING)
+ reg = STK3310_REG_THDH_PS;
+ else if (dir == IIO_EV_DIR_FALLING)
+ reg = STK3310_REG_THDL_PS;
+ else
+ return -EINVAL;
+
+ mutex_lock(&data->lock);
+ ret = regmap_bulk_read(data->regmap, reg, &buf, 2);
+ mutex_unlock(&data->lock);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "register read failed\n");
+ return ret;
+ }
+ *val = be16_to_cpu(buf);
+
+ return IIO_VAL_INT;
+}
+
+static int stk3310_write_event(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)
+{
+ u8 reg;
+ __be16 buf;
+ int ret;
+ unsigned int index;
+ struct stk3310_data *data = iio_priv(indio_dev);
+ struct i2c_client *client = data->client;
+
+ regmap_field_read(data->reg_ps_gain, &index);
+ if (val > stk3310_ps_max[index])
+ return -EINVAL;
+
+ if (dir == IIO_EV_DIR_RISING)
+ reg = STK3310_REG_THDH_PS;
+ else if (dir == IIO_EV_DIR_FALLING)
+ reg = STK3310_REG_THDL_PS;
+ else
+ return -EINVAL;
+
+ buf = cpu_to_be16(val);
+ ret = regmap_bulk_write(data->regmap, reg, &buf, 2);
+ if (ret < 0)
+ dev_err(&client->dev, "failed to set PS threshold!\n");
+
+ return ret;
+}
+
+static int stk3310_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ unsigned int event_val;
+ struct stk3310_data *data = iio_priv(indio_dev);
+
+ regmap_field_read(data->reg_int_ps, &event_val);
+
+ return event_val;
+}
+
+static int stk3310_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)
+{
+ int ret;
+ struct stk3310_data *data = iio_priv(indio_dev);
+ struct i2c_client *client = data->client;
+
+ if (state < 0 || state > 7)
+ return -EINVAL;
+
+ /* Set INT_PS value */
+ mutex_lock(&data->lock);
+ ret = regmap_field_write(data->reg_int_ps, state);
+ if (ret < 0)
+ dev_err(&client->dev, "failed to set interrupt mode\n");
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static int stk3310_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ u8 reg;
+ __be16 buf;
+ int ret;
+ unsigned int index;
+ struct stk3310_data *data = iio_priv(indio_dev);
+ struct i2c_client *client = data->client;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (chan->type == IIO_LIGHT)
+ reg = STK3310_REG_ALS_DATA_MSB;
+ else if (chan->type == IIO_PROXIMITY)
+ reg = STK3310_REG_PS_DATA_MSB;
+ else
+ return -EINVAL;
+ mutex_lock(&data->lock);
+ ret = regmap_bulk_read(data->regmap, reg, &buf, 2);
+ if (ret < 0) {
+ dev_err(&client->dev, "register read failed\n");
+ mutex_unlock(&data->lock);
+ return ret;
+ }
+ *val = be16_to_cpu(buf);
+ mutex_unlock(&data->lock);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_INT_TIME:
+ if (chan->type == IIO_LIGHT)
+ regmap_field_read(data->reg_als_it, &index);
+ else
+ regmap_field_read(data->reg_ps_it, &index);
+ *val = stk3310_it_table[index][0];
+ *val2 = stk3310_it_table[index][1];
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type == IIO_LIGHT)
+ regmap_field_read(data->reg_als_gain, &index);
+ else
+ regmap_field_read(data->reg_ps_gain, &index);
+ *val = stk3310_scale_table[index][0];
+ *val2 = stk3310_scale_table[index][1];
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+
+ return -EINVAL;
+}
+
+static int stk3310_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ int ret;
+ int index;
+ struct stk3310_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_INT_TIME:
+ index = stk3310_get_index(stk3310_it_table,
+ ARRAY_SIZE(stk3310_it_table),
+ val, val2);
+ if (index < 0)
+ return -EINVAL;
+ mutex_lock(&data->lock);
+ if (chan->type == IIO_LIGHT)
+ ret = regmap_field_write(data->reg_als_it, index);
+ else
+ ret = regmap_field_write(data->reg_ps_it, index);
+ if (ret < 0)
+ dev_err(&data->client->dev,
+ "sensor configuration failed\n");
+ mutex_unlock(&data->lock);
+ return ret;
+
+ case IIO_CHAN_INFO_SCALE:
+ index = stk3310_get_index(stk3310_scale_table,
+ ARRAY_SIZE(stk3310_scale_table),
+ val, val2);
+ if (index < 0)
+ return -EINVAL;
+ mutex_lock(&data->lock);
+ if (chan->type == IIO_LIGHT)
+ ret = regmap_field_write(data->reg_als_gain, index);
+ else
+ ret = regmap_field_write(data->reg_ps_gain, index);
+ if (ret < 0)
+ dev_err(&data->client->dev,
+ "sensor configuration failed\n");
+ mutex_unlock(&data->lock);
+ return ret;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info stk3310_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = stk3310_read_raw,
+ .write_raw = stk3310_write_raw,
+ .attrs = &stk3310_attribute_group,
+ .read_event_value = stk3310_read_event,
+ .write_event_value = stk3310_write_event,
+ .read_event_config = stk3310_read_event_config,
+ .write_event_config = stk3310_write_event_config,
+};
+
+static int stk3310_set_state(struct stk3310_data *data, u8 state)
+{
+ int ret;
+ struct i2c_client *client = data->client;
+
+ /* 3-bit state; 0b100 is not supported. */
+ if (state > 7 || state == 4)
+ return -EINVAL;
+
+ mutex_lock(&data->lock);
+ ret = regmap_field_write(data->reg_state, state);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to change sensor state\n");
+ } else if (state != STK3310_STATE_STANDBY) {
+ /* Don't reset the 'enabled' flags if we're going in standby */
+ data->ps_enabled = !!(state & 0x01);
+ data->als_enabled = !!(state & 0x02);
+ }
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static int stk3310_init(struct iio_dev *indio_dev)
+{
+ int ret;
+ int chipid;
+ u8 state;
+ struct stk3310_data *data = iio_priv(indio_dev);
+ struct i2c_client *client = data->client;
+
+ regmap_read(data->regmap, STK3310_REG_ID, &chipid);
+ if (chipid != STK3310_CHIP_ID_VAL &&
+ chipid != STK3311_CHIP_ID_VAL) {
+ dev_err(&client->dev, "invalid chip id: 0x%x\n", chipid);
+ return -ENODEV;
+ }
+
+ state = STK3310_STATE_EN_ALS | STK3310_STATE_EN_PS;
+ ret = stk3310_set_state(data, state);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to enable sensor");
+ return ret;
+ }
+
+ /* Enable PS interrupts */
+ ret = regmap_field_write(data->reg_int_ps, STK3310_PSINT_EN);
+ if (ret < 0)
+ dev_err(&client->dev, "failed to enable interrupts!\n");
+
+ return ret;
+}
+
+static int stk3310_gpio_probe(struct i2c_client *client)
+{
+ struct device *dev;
+ struct gpio_desc *gpio;
+ int ret;
+
+ if (!client)
+ return -EINVAL;
+
+ dev = &client->dev;
+
+ /* gpio interrupt pin */
+ gpio = devm_gpiod_get_index(dev, STK3310_GPIO, 0);
+ if (IS_ERR(gpio)) {
+ dev_err(dev, "acpi gpio get index failed\n");
+ return PTR_ERR(gpio);
+ }
+
+ ret = gpiod_direction_input(gpio);
+ if (ret)
+ return ret;
+
+ ret = gpiod_to_irq(gpio);
+ dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
+
+ return ret;
+}
+
+static bool stk3310_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case STK3310_REG_ALS_DATA_MSB:
+ case STK3310_REG_ALS_DATA_LSB:
+ case STK3310_REG_PS_DATA_LSB:
+ case STK3310_REG_PS_DATA_MSB:
+ case STK3310_REG_FLAG:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static struct regmap_config stk3310_regmap_config = {
+ .name = STK3310_REGMAP_NAME,
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = STK3310_MAX_REG,
+ .cache_type = REGCACHE_RBTREE,
+ .volatile_reg = stk3310_is_volatile_reg,
+};
+
+static int stk3310_regmap_init(struct stk3310_data *data)
+{
+ struct regmap *regmap;
+ struct i2c_client *client;
+
+ client = data->client;
+ regmap = devm_regmap_init_i2c(client, &stk3310_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(&client->dev, "regmap initialization failed.\n");
+ return PTR_ERR(regmap);
+ }
+ data->regmap = regmap;
+
+ STK3310_REGFIELD(state);
+ STK3310_REGFIELD(als_gain);
+ STK3310_REGFIELD(ps_gain);
+ STK3310_REGFIELD(als_it);
+ STK3310_REGFIELD(ps_it);
+ STK3310_REGFIELD(int_ps);
+ STK3310_REGFIELD(flag_psint);
+ STK3310_REGFIELD(flag_nf);
+
+ return 0;
+}
+
+static irqreturn_t stk3310_irq_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct stk3310_data *data = iio_priv(indio_dev);
+
+ data->timestamp = iio_get_time_ns();
+
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t stk3310_irq_event_handler(int irq, void *private)
+{
+ int ret;
+ unsigned int dir;
+ u64 event;
+
+ struct iio_dev *indio_dev = private;
+ struct stk3310_data *data = iio_priv(indio_dev);
+
+ /* Read FLAG_NF to figure out what threshold has been met. */
+ mutex_lock(&data->lock);
+ ret = regmap_field_read(data->reg_flag_nf, &dir);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "register read failed\n");
+ mutex_unlock(&data->lock);
+ return ret;
+ }
+ event = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 1,
+ IIO_EV_TYPE_THRESH,
+ (dir ? IIO_EV_DIR_FALLING :
+ IIO_EV_DIR_RISING));
+ iio_push_event(indio_dev, event, data->timestamp);
+
+ /* Reset the interrupt flag */
+ ret = regmap_field_write(data->reg_flag_psint, 0);
+ if (ret < 0)
+ dev_err(&data->client->dev, "failed to reset interrupts\n");
+ mutex_unlock(&data->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int stk3310_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ struct iio_dev *indio_dev;
+ struct stk3310_data *data;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev) {
+ dev_err(&client->dev, "iio allocation failed!\n");
+ return -ENOMEM;
+ }
+
+ data = iio_priv(indio_dev);
+ data->client = client;
+ i2c_set_clientdata(client, indio_dev);
+ mutex_init(&data->lock);
+
+ ret = stk3310_regmap_init(data);
+ if (ret < 0)
+ return ret;
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->info = &stk3310_info;
+ indio_dev->name = STK3310_DRIVER_NAME;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = stk3310_channels;
+ indio_dev->num_channels = ARRAY_SIZE(stk3310_channels);
+
+ ret = stk3310_init(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ if (client->irq < 0)
+ client->irq = stk3310_gpio_probe(client);
+
+ if (client->irq >= 0) {
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ stk3310_irq_handler,
+ stk3310_irq_event_handler,
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ STK3310_EVENT, indio_dev);
+ if (ret < 0)
+ dev_err(&client->dev, "request irq %d failed\n",
+ client->irq);
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(&client->dev, "device_register failed\n");
+ stk3310_set_state(data, STK3310_STATE_STANDBY);
+ }
+
+ return ret;
+}
+
+static int stk3310_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+ iio_device_unregister(indio_dev);
+ return stk3310_set_state(iio_priv(indio_dev), STK3310_STATE_STANDBY);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int stk3310_suspend(struct device *dev)
+{
+ struct stk3310_data *data;
+
+ data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+
+ return stk3310_set_state(data, STK3310_STATE_STANDBY);
+}
+
+static int stk3310_resume(struct device *dev)
+{
+ int state = 0;
+ struct stk3310_data *data;
+
+ data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+ if (data->ps_enabled)
+ state |= STK3310_STATE_EN_PS;
+ if (data->als_enabled)
+ state |= STK3310_STATE_EN_ALS;
+
+ return stk3310_set_state(data, state);
+}
+
+static SIMPLE_DEV_PM_OPS(stk3310_pm_ops, stk3310_suspend, stk3310_resume);
+
+#define STK3310_PM_OPS (&stk3310_pm_ops)
+#else
+#define STK3310_PM_OPS NULL
+#endif
+
+static const struct i2c_device_id stk3310_i2c_id[] = {
+ {"STK3310", 0},
+ {"STK3311", 0},
+ {}
+};
+
+static const struct acpi_device_id stk3310_acpi_id[] = {
+ {"STK3310", 0},
+ {"STK3311", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(acpi, stk3310_acpi_id);
+
+static struct i2c_driver stk3310_driver = {
+ .driver = {
+ .name = "stk3310",
+ .pm = STK3310_PM_OPS,
+ .acpi_match_table = ACPI_PTR(stk3310_acpi_id),
+ },
+ .probe = stk3310_probe,
+ .remove = stk3310_remove,
+ .id_table = stk3310_i2c_id,
+};
+
+module_i2c_driver(stk3310_driver);
+
+MODULE_AUTHOR("Tiberiu Breana <tiberiu.a.breana@intel.com>");
+MODULE_DESCRIPTION("STK3310 Ambient Light and Proximity Sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/light/tsl2563.c b/drivers/iio/light/tsl2563.c
index 94daa9fc1..12731d6b8 100644
--- a/drivers/iio/light/tsl2563.c
+++ b/drivers/iio/light/tsl2563.c
@@ -240,7 +240,7 @@ static int tsl2563_read_id(struct tsl2563_chip *chip, u8 *id)
* convert between normalized values and HW values obtained using given
* timing and gain settings.
*/
-static int adc_shiftbits(u8 timing)
+static int tsl2563_adc_shiftbits(u8 timing)
{
int shift = 0;
@@ -263,9 +263,9 @@ static int adc_shiftbits(u8 timing)
}
/* Convert a HW ADC value to normalized scale. */
-static u32 normalize_adc(u16 adc, u8 timing)
+static u32 tsl2563_normalize_adc(u16 adc, u8 timing)
{
- return adc << adc_shiftbits(timing);
+ return adc << tsl2563_adc_shiftbits(timing);
}
static void tsl2563_wait_adc(struct tsl2563_chip *chip)
@@ -350,8 +350,8 @@ static int tsl2563_get_adc(struct tsl2563_chip *chip)
retry = tsl2563_adjust_gainlevel(chip, adc0);
}
- chip->data0 = normalize_adc(adc0, chip->gainlevel->gaintime);
- chip->data1 = normalize_adc(adc1, chip->gainlevel->gaintime);
+ chip->data0 = tsl2563_normalize_adc(adc0, chip->gainlevel->gaintime);
+ chip->data1 = tsl2563_normalize_adc(adc1, chip->gainlevel->gaintime);
if (!chip->int_enabled)
schedule_delayed_work(&chip->poweroff_work, 5 * HZ);
@@ -361,13 +361,13 @@ out:
return ret;
}
-static inline int calib_to_sysfs(u32 calib)
+static inline int tsl2563_calib_to_sysfs(u32 calib)
{
return (int) (((calib * CALIB_BASE_SYSFS) +
CALIB_FRAC_HALF) >> CALIB_FRAC_BITS);
}
-static inline u32 calib_from_sysfs(int value)
+static inline u32 tsl2563_calib_from_sysfs(int value)
{
return (((u32) value) << CALIB_FRAC_BITS) / CALIB_BASE_SYSFS;
}
@@ -426,7 +426,7 @@ static const struct tsl2563_lux_coeff lux_table[] = {
};
/* Convert normalized, scaled ADC values to lux. */
-static unsigned int adc_to_lux(u32 adc0, u32 adc1)
+static unsigned int tsl2563_adc_to_lux(u32 adc0, u32 adc1)
{
const struct tsl2563_lux_coeff *lp = lux_table;
unsigned long ratio, lux, ch0 = adc0, ch1 = adc1;
@@ -442,7 +442,7 @@ static unsigned int adc_to_lux(u32 adc0, u32 adc1)
}
/* Apply calibration coefficient to ADC count. */
-static u32 calib_adc(u32 adc, u32 calib)
+static u32 tsl2563_calib_adc(u32 adc, u32 calib)
{
unsigned long scaled = adc;
@@ -463,9 +463,9 @@ static int tsl2563_write_raw(struct iio_dev *indio_dev,
if (mask != IIO_CHAN_INFO_CALIBSCALE)
return -EINVAL;
if (chan->channel2 == IIO_MOD_LIGHT_BOTH)
- chip->calib0 = calib_from_sysfs(val);
+ chip->calib0 = tsl2563_calib_from_sysfs(val);
else if (chan->channel2 == IIO_MOD_LIGHT_IR)
- chip->calib1 = calib_from_sysfs(val);
+ chip->calib1 = tsl2563_calib_from_sysfs(val);
else
return -EINVAL;
@@ -491,11 +491,11 @@ static int tsl2563_read_raw(struct iio_dev *indio_dev,
ret = tsl2563_get_adc(chip);
if (ret)
goto error_ret;
- calib0 = calib_adc(chip->data0, chip->calib0) *
+ calib0 = tsl2563_calib_adc(chip->data0, chip->calib0) *
chip->cover_comp_gain;
- calib1 = calib_adc(chip->data1, chip->calib1) *
+ calib1 = tsl2563_calib_adc(chip->data1, chip->calib1) *
chip->cover_comp_gain;
- *val = adc_to_lux(calib0, calib1);
+ *val = tsl2563_adc_to_lux(calib0, calib1);
ret = IIO_VAL_INT;
break;
case IIO_INTENSITY:
@@ -515,9 +515,9 @@ static int tsl2563_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_CALIBSCALE:
if (chan->channel2 == IIO_MOD_LIGHT_BOTH)
- *val = calib_to_sysfs(chip->calib0);
+ *val = tsl2563_calib_to_sysfs(chip->calib0);
else
- *val = calib_to_sysfs(chip->calib1);
+ *val = tsl2563_calib_to_sysfs(chip->calib1);
ret = IIO_VAL_INT;
break;
default:
@@ -750,8 +750,8 @@ static int tsl2563_probe(struct i2c_client *client,
chip->high_thres = 0xffff;
chip->gainlevel = tsl2563_gainlevel_table;
chip->intr = TSL2563_INT_PERSIST(4);
- chip->calib0 = calib_from_sysfs(CALIB_BASE_SYSFS);
- chip->calib1 = calib_from_sysfs(CALIB_BASE_SYSFS);
+ chip->calib0 = tsl2563_calib_from_sysfs(CALIB_BASE_SYSFS);
+ chip->calib1 = tsl2563_calib_from_sysfs(CALIB_BASE_SYSFS);
if (pdata)
chip->cover_comp_gain = pdata->cover_comp_gain;
diff --git a/drivers/iio/light/tsl4531.c b/drivers/iio/light/tsl4531.c
index 0763b8632..63c26e2d5 100644
--- a/drivers/iio/light/tsl4531.c
+++ b/drivers/iio/light/tsl4531.c
@@ -24,12 +24,12 @@
#define TSL4531_DRV_NAME "tsl4531"
-#define TCS3472_COMMAND BIT(7)
+#define TSL4531_COMMAND BIT(7)
-#define TSL4531_CONTROL (TCS3472_COMMAND | 0x00)
-#define TSL4531_CONFIG (TCS3472_COMMAND | 0x01)
-#define TSL4531_DATA (TCS3472_COMMAND | 0x04)
-#define TSL4531_ID (TCS3472_COMMAND | 0x0a)
+#define TSL4531_CONTROL (TSL4531_COMMAND | 0x00)
+#define TSL4531_CONFIG (TSL4531_COMMAND | 0x01)
+#define TSL4531_DATA (TSL4531_COMMAND | 0x04)
+#define TSL4531_ID (TSL4531_COMMAND | 0x0a)
/* operating modes in control register */
#define TSL4531_MODE_POWERDOWN 0x00
diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
index a5d6de72c..efb9350b0 100644
--- a/drivers/iio/magnetometer/Kconfig
+++ b/drivers/iio/magnetometer/Kconfig
@@ -8,7 +8,7 @@ menu "Magnetometer sensors"
config AK8975
tristate "Asahi Kasei AK 3-Axis Magnetometer"
depends on I2C
- depends on GPIOLIB
+ depends on GPIOLIB || COMPILE_TEST
help
Say yes here to build support for Asahi Kasei AK8975, AK8963,
AK09911 or AK09912 3-Axis Magnetometer.
@@ -19,7 +19,7 @@ config AK8975
config AK09911
tristate "Asahi Kasei AK09911 3-axis Compass"
depends on I2C
- depends on GPIOLIB
+ depends on GPIOLIB || COMPILE_TEST
select AK8975
help
Deprecated: AK09911 is now supported by AK8975 driver.
@@ -47,6 +47,17 @@ config HID_SENSOR_MAGNETOMETER_3D
Say yes here to build support for the HID SENSOR
Magnetometer 3D.
+config MMC35240
+ tristate "MEMSIC MMC35240 3-axis magnetic sensor"
+ select REGMAP_I2C
+ depends on I2C
+ help
+ Say yes here to build support for the MEMSIC MMC35240 3-axis
+ magnetic sensor.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mmc35240.
+
config IIO_ST_MAGN_3AXIS
tristate "STMicroelectronics magnetometers 3-Axis Driver"
depends on (I2C || SPI_MASTER) && SYSFS
@@ -76,4 +87,19 @@ config IIO_ST_MAGN_SPI_3AXIS
depends on IIO_ST_MAGN_3AXIS
depends on IIO_ST_SENSORS_SPI
+config BMC150_MAGN
+ tristate "Bosch BMC150 Magnetometer Driver"
+ depends on I2C
+ select REGMAP_I2C
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for the BMC150 magnetometer.
+
+ Currently this only supports the device via an i2c interface.
+
+ This is a combo module with both accelerometer and magnetometer.
+ This driver is only implementing magnetometer part, which has
+ its own address and register map.
+
endmenu
diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile
index 0f5d3c985..33b1d4d54 100644
--- a/drivers/iio/magnetometer/Makefile
+++ b/drivers/iio/magnetometer/Makefile
@@ -6,6 +6,7 @@
obj-$(CONFIG_AK8975) += ak8975.o
obj-$(CONFIG_MAG3110) += mag3110.o
obj-$(CONFIG_HID_SENSOR_MAGNETOMETER_3D) += hid-sensor-magn-3d.o
+obj-$(CONFIG_MMC35240) += mmc35240.o
obj-$(CONFIG_IIO_ST_MAGN_3AXIS) += st_magn.o
st_magn-y := st_magn_core.o
@@ -13,3 +14,5 @@ st_magn-$(CONFIG_IIO_BUFFER) += st_magn_buffer.o
obj-$(CONFIG_IIO_ST_MAGN_I2C_3AXIS) += st_magn_i2c.o
obj-$(CONFIG_IIO_ST_MAGN_SPI_3AXIS) += st_magn_spi.o
+
+obj-$(CONFIG_BMC150_MAGN) += bmc150_magn.o
diff --git a/drivers/iio/magnetometer/bmc150_magn.c b/drivers/iio/magnetometer/bmc150_magn.c
new file mode 100644
index 000000000..1347a1f2e
--- /dev/null
+++ b/drivers/iio/magnetometer/bmc150_magn.c
@@ -0,0 +1,1109 @@
+/*
+ * Bosch BMC150 three-axis magnetic field sensor driver
+ *
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This code is based on bmm050_api.c authored by contact@bosch.sensortec.com:
+ *
+ * (C) Copyright 2011~2014 Bosch Sensortec GmbH All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/events.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/regmap.h>
+
+#define BMC150_MAGN_DRV_NAME "bmc150_magn"
+#define BMC150_MAGN_IRQ_NAME "bmc150_magn_event"
+#define BMC150_MAGN_GPIO_INT "interrupt"
+
+#define BMC150_MAGN_REG_CHIP_ID 0x40
+#define BMC150_MAGN_CHIP_ID_VAL 0x32
+
+#define BMC150_MAGN_REG_X_L 0x42
+#define BMC150_MAGN_REG_X_M 0x43
+#define BMC150_MAGN_REG_Y_L 0x44
+#define BMC150_MAGN_REG_Y_M 0x45
+#define BMC150_MAGN_SHIFT_XY_L 3
+#define BMC150_MAGN_REG_Z_L 0x46
+#define BMC150_MAGN_REG_Z_M 0x47
+#define BMC150_MAGN_SHIFT_Z_L 1
+#define BMC150_MAGN_REG_RHALL_L 0x48
+#define BMC150_MAGN_REG_RHALL_M 0x49
+#define BMC150_MAGN_SHIFT_RHALL_L 2
+
+#define BMC150_MAGN_REG_INT_STATUS 0x4A
+
+#define BMC150_MAGN_REG_POWER 0x4B
+#define BMC150_MAGN_MASK_POWER_CTL BIT(0)
+
+#define BMC150_MAGN_REG_OPMODE_ODR 0x4C
+#define BMC150_MAGN_MASK_OPMODE GENMASK(2, 1)
+#define BMC150_MAGN_SHIFT_OPMODE 1
+#define BMC150_MAGN_MODE_NORMAL 0x00
+#define BMC150_MAGN_MODE_FORCED 0x01
+#define BMC150_MAGN_MODE_SLEEP 0x03
+#define BMC150_MAGN_MASK_ODR GENMASK(5, 3)
+#define BMC150_MAGN_SHIFT_ODR 3
+
+#define BMC150_MAGN_REG_INT 0x4D
+
+#define BMC150_MAGN_REG_INT_DRDY 0x4E
+#define BMC150_MAGN_MASK_DRDY_EN BIT(7)
+#define BMC150_MAGN_SHIFT_DRDY_EN 7
+#define BMC150_MAGN_MASK_DRDY_INT3 BIT(6)
+#define BMC150_MAGN_MASK_DRDY_Z_EN BIT(5)
+#define BMC150_MAGN_MASK_DRDY_Y_EN BIT(4)
+#define BMC150_MAGN_MASK_DRDY_X_EN BIT(3)
+#define BMC150_MAGN_MASK_DRDY_DR_POLARITY BIT(2)
+#define BMC150_MAGN_MASK_DRDY_LATCHING BIT(1)
+#define BMC150_MAGN_MASK_DRDY_INT3_POLARITY BIT(0)
+
+#define BMC150_MAGN_REG_LOW_THRESH 0x4F
+#define BMC150_MAGN_REG_HIGH_THRESH 0x50
+#define BMC150_MAGN_REG_REP_XY 0x51
+#define BMC150_MAGN_REG_REP_Z 0x52
+
+#define BMC150_MAGN_REG_TRIM_START 0x5D
+#define BMC150_MAGN_REG_TRIM_END 0x71
+
+#define BMC150_MAGN_XY_OVERFLOW_VAL -4096
+#define BMC150_MAGN_Z_OVERFLOW_VAL -16384
+
+/* Time from SUSPEND to SLEEP */
+#define BMC150_MAGN_START_UP_TIME_MS 3
+
+#define BMC150_MAGN_AUTO_SUSPEND_DELAY_MS 2000
+
+#define BMC150_MAGN_REGVAL_TO_REPXY(regval) (((regval) * 2) + 1)
+#define BMC150_MAGN_REGVAL_TO_REPZ(regval) ((regval) + 1)
+#define BMC150_MAGN_REPXY_TO_REGVAL(rep) (((rep) - 1) / 2)
+#define BMC150_MAGN_REPZ_TO_REGVAL(rep) ((rep) - 1)
+
+enum bmc150_magn_axis {
+ AXIS_X,
+ AXIS_Y,
+ AXIS_Z,
+ RHALL,
+ AXIS_XYZ_MAX = RHALL,
+ AXIS_XYZR_MAX,
+};
+
+enum bmc150_magn_power_modes {
+ BMC150_MAGN_POWER_MODE_SUSPEND,
+ BMC150_MAGN_POWER_MODE_SLEEP,
+ BMC150_MAGN_POWER_MODE_NORMAL,
+};
+
+struct bmc150_magn_trim_regs {
+ s8 x1;
+ s8 y1;
+ __le16 reserved1;
+ u8 reserved2;
+ __le16 z4;
+ s8 x2;
+ s8 y2;
+ __le16 reserved3;
+ __le16 z2;
+ __le16 z1;
+ __le16 xyz1;
+ __le16 z3;
+ s8 xy2;
+ u8 xy1;
+} __packed;
+
+struct bmc150_magn_data {
+ struct i2c_client *client;
+ /*
+ * 1. Protect this structure.
+ * 2. Serialize sequences that power on/off the device and access HW.
+ */
+ struct mutex mutex;
+ struct regmap *regmap;
+ /* 4 x 32 bits for x, y z, 4 bytes align, 64 bits timestamp */
+ s32 buffer[6];
+ struct iio_trigger *dready_trig;
+ bool dready_trigger_on;
+ int max_odr;
+};
+
+static const struct {
+ int freq;
+ u8 reg_val;
+} bmc150_magn_samp_freq_table[] = { {2, 0x01},
+ {6, 0x02},
+ {8, 0x03},
+ {10, 0x00},
+ {15, 0x04},
+ {20, 0x05},
+ {25, 0x06},
+ {30, 0x07} };
+
+enum bmc150_magn_presets {
+ LOW_POWER_PRESET,
+ REGULAR_PRESET,
+ ENHANCED_REGULAR_PRESET,
+ HIGH_ACCURACY_PRESET
+};
+
+static const struct bmc150_magn_preset {
+ u8 rep_xy;
+ u8 rep_z;
+ u8 odr;
+} bmc150_magn_presets_table[] = {
+ [LOW_POWER_PRESET] = {3, 3, 10},
+ [REGULAR_PRESET] = {9, 15, 10},
+ [ENHANCED_REGULAR_PRESET] = {15, 27, 10},
+ [HIGH_ACCURACY_PRESET] = {47, 83, 20},
+};
+
+#define BMC150_MAGN_DEFAULT_PRESET REGULAR_PRESET
+
+static bool bmc150_magn_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case BMC150_MAGN_REG_POWER:
+ case BMC150_MAGN_REG_OPMODE_ODR:
+ case BMC150_MAGN_REG_INT:
+ case BMC150_MAGN_REG_INT_DRDY:
+ case BMC150_MAGN_REG_LOW_THRESH:
+ case BMC150_MAGN_REG_HIGH_THRESH:
+ case BMC150_MAGN_REG_REP_XY:
+ case BMC150_MAGN_REG_REP_Z:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static bool bmc150_magn_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case BMC150_MAGN_REG_X_L:
+ case BMC150_MAGN_REG_X_M:
+ case BMC150_MAGN_REG_Y_L:
+ case BMC150_MAGN_REG_Y_M:
+ case BMC150_MAGN_REG_Z_L:
+ case BMC150_MAGN_REG_Z_M:
+ case BMC150_MAGN_REG_RHALL_L:
+ case BMC150_MAGN_REG_RHALL_M:
+ case BMC150_MAGN_REG_INT_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config bmc150_magn_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = BMC150_MAGN_REG_TRIM_END,
+ .cache_type = REGCACHE_RBTREE,
+
+ .writeable_reg = bmc150_magn_is_writeable_reg,
+ .volatile_reg = bmc150_magn_is_volatile_reg,
+};
+
+static int bmc150_magn_set_power_mode(struct bmc150_magn_data *data,
+ enum bmc150_magn_power_modes mode,
+ bool state)
+{
+ int ret;
+
+ switch (mode) {
+ case BMC150_MAGN_POWER_MODE_SUSPEND:
+ ret = regmap_update_bits(data->regmap, BMC150_MAGN_REG_POWER,
+ BMC150_MAGN_MASK_POWER_CTL, !state);
+ if (ret < 0)
+ return ret;
+ usleep_range(BMC150_MAGN_START_UP_TIME_MS * 1000, 20000);
+ return 0;
+ case BMC150_MAGN_POWER_MODE_SLEEP:
+ return regmap_update_bits(data->regmap,
+ BMC150_MAGN_REG_OPMODE_ODR,
+ BMC150_MAGN_MASK_OPMODE,
+ BMC150_MAGN_MODE_SLEEP <<
+ BMC150_MAGN_SHIFT_OPMODE);
+ case BMC150_MAGN_POWER_MODE_NORMAL:
+ return regmap_update_bits(data->regmap,
+ BMC150_MAGN_REG_OPMODE_ODR,
+ BMC150_MAGN_MASK_OPMODE,
+ BMC150_MAGN_MODE_NORMAL <<
+ BMC150_MAGN_SHIFT_OPMODE);
+ }
+
+ return -EINVAL;
+}
+
+static int bmc150_magn_set_power_state(struct bmc150_magn_data *data, bool on)
+{
+#ifdef CONFIG_PM
+ int ret;
+
+ if (on) {
+ ret = pm_runtime_get_sync(&data->client->dev);
+ } else {
+ pm_runtime_mark_last_busy(&data->client->dev);
+ ret = pm_runtime_put_autosuspend(&data->client->dev);
+ }
+
+ if (ret < 0) {
+ dev_err(&data->client->dev,
+ "failed to change power state to %d\n", on);
+ if (on)
+ pm_runtime_put_noidle(&data->client->dev);
+
+ return ret;
+ }
+#endif
+
+ return 0;
+}
+
+static int bmc150_magn_get_odr(struct bmc150_magn_data *data, int *val)
+{
+ int ret, reg_val;
+ u8 i, odr_val;
+
+ ret = regmap_read(data->regmap, BMC150_MAGN_REG_OPMODE_ODR, &reg_val);
+ if (ret < 0)
+ return ret;
+ odr_val = (reg_val & BMC150_MAGN_MASK_ODR) >> BMC150_MAGN_SHIFT_ODR;
+
+ for (i = 0; i < ARRAY_SIZE(bmc150_magn_samp_freq_table); i++)
+ if (bmc150_magn_samp_freq_table[i].reg_val == odr_val) {
+ *val = bmc150_magn_samp_freq_table[i].freq;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int bmc150_magn_set_odr(struct bmc150_magn_data *data, int val)
+{
+ int ret;
+ u8 i;
+
+ for (i = 0; i < ARRAY_SIZE(bmc150_magn_samp_freq_table); i++) {
+ if (bmc150_magn_samp_freq_table[i].freq == val) {
+ ret = regmap_update_bits(data->regmap,
+ BMC150_MAGN_REG_OPMODE_ODR,
+ BMC150_MAGN_MASK_ODR,
+ bmc150_magn_samp_freq_table[i].
+ reg_val <<
+ BMC150_MAGN_SHIFT_ODR);
+ if (ret < 0)
+ return ret;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int bmc150_magn_set_max_odr(struct bmc150_magn_data *data, int rep_xy,
+ int rep_z, int odr)
+{
+ int ret, reg_val, max_odr;
+
+ if (rep_xy <= 0) {
+ ret = regmap_read(data->regmap, BMC150_MAGN_REG_REP_XY,
+ &reg_val);
+ if (ret < 0)
+ return ret;
+ rep_xy = BMC150_MAGN_REGVAL_TO_REPXY(reg_val);
+ }
+ if (rep_z <= 0) {
+ ret = regmap_read(data->regmap, BMC150_MAGN_REG_REP_Z,
+ &reg_val);
+ if (ret < 0)
+ return ret;
+ rep_z = BMC150_MAGN_REGVAL_TO_REPZ(reg_val);
+ }
+ if (odr <= 0) {
+ ret = bmc150_magn_get_odr(data, &odr);
+ if (ret < 0)
+ return ret;
+ }
+ /* the maximum selectable read-out frequency from datasheet */
+ max_odr = 1000000 / (145 * rep_xy + 500 * rep_z + 980);
+ if (odr > max_odr) {
+ dev_err(&data->client->dev,
+ "Can't set oversampling with sampling freq %d\n",
+ odr);
+ return -EINVAL;
+ }
+ data->max_odr = max_odr;
+
+ return 0;
+}
+
+static s32 bmc150_magn_compensate_x(struct bmc150_magn_trim_regs *tregs, s16 x,
+ u16 rhall)
+{
+ s16 val;
+ u16 xyz1 = le16_to_cpu(tregs->xyz1);
+
+ if (x == BMC150_MAGN_XY_OVERFLOW_VAL)
+ return S32_MIN;
+
+ if (!rhall)
+ rhall = xyz1;
+
+ val = ((s16)(((u16)((((s32)xyz1) << 14) / rhall)) - ((u16)0x4000)));
+ val = ((s16)((((s32)x) * ((((((((s32)tregs->xy2) * ((((s32)val) *
+ ((s32)val)) >> 7)) + (((s32)val) *
+ ((s32)(((s16)tregs->xy1) << 7)))) >> 9) + ((s32)0x100000)) *
+ ((s32)(((s16)tregs->x2) + ((s16)0xA0)))) >> 12)) >> 13)) +
+ (((s16)tregs->x1) << 3);
+
+ return (s32)val;
+}
+
+static s32 bmc150_magn_compensate_y(struct bmc150_magn_trim_regs *tregs, s16 y,
+ u16 rhall)
+{
+ s16 val;
+ u16 xyz1 = le16_to_cpu(tregs->xyz1);
+
+ if (y == BMC150_MAGN_XY_OVERFLOW_VAL)
+ return S32_MIN;
+
+ if (!rhall)
+ rhall = xyz1;
+
+ val = ((s16)(((u16)((((s32)xyz1) << 14) / rhall)) - ((u16)0x4000)));
+ val = ((s16)((((s32)y) * ((((((((s32)tregs->xy2) * ((((s32)val) *
+ ((s32)val)) >> 7)) + (((s32)val) *
+ ((s32)(((s16)tregs->xy1) << 7)))) >> 9) + ((s32)0x100000)) *
+ ((s32)(((s16)tregs->y2) + ((s16)0xA0)))) >> 12)) >> 13)) +
+ (((s16)tregs->y1) << 3);
+
+ return (s32)val;
+}
+
+static s32 bmc150_magn_compensate_z(struct bmc150_magn_trim_regs *tregs, s16 z,
+ u16 rhall)
+{
+ s32 val;
+ u16 xyz1 = le16_to_cpu(tregs->xyz1);
+ u16 z1 = le16_to_cpu(tregs->z1);
+ s16 z2 = le16_to_cpu(tregs->z2);
+ s16 z3 = le16_to_cpu(tregs->z3);
+ s16 z4 = le16_to_cpu(tregs->z4);
+
+ if (z == BMC150_MAGN_Z_OVERFLOW_VAL)
+ return S32_MIN;
+
+ val = (((((s32)(z - z4)) << 15) - ((((s32)z3) * ((s32)(((s16)rhall) -
+ ((s16)xyz1)))) >> 2)) / (z2 + ((s16)(((((s32)z1) *
+ ((((s16)rhall) << 1))) + (1 << 15)) >> 16))));
+
+ return val;
+}
+
+static int bmc150_magn_read_xyz(struct bmc150_magn_data *data, s32 *buffer)
+{
+ int ret;
+ __le16 values[AXIS_XYZR_MAX];
+ s16 raw_x, raw_y, raw_z;
+ u16 rhall;
+ struct bmc150_magn_trim_regs tregs;
+
+ ret = regmap_bulk_read(data->regmap, BMC150_MAGN_REG_X_L,
+ values, sizeof(values));
+ if (ret < 0)
+ return ret;
+
+ raw_x = (s16)le16_to_cpu(values[AXIS_X]) >> BMC150_MAGN_SHIFT_XY_L;
+ raw_y = (s16)le16_to_cpu(values[AXIS_Y]) >> BMC150_MAGN_SHIFT_XY_L;
+ raw_z = (s16)le16_to_cpu(values[AXIS_Z]) >> BMC150_MAGN_SHIFT_Z_L;
+ rhall = le16_to_cpu(values[RHALL]) >> BMC150_MAGN_SHIFT_RHALL_L;
+
+ ret = regmap_bulk_read(data->regmap, BMC150_MAGN_REG_TRIM_START,
+ &tregs, sizeof(tregs));
+ if (ret < 0)
+ return ret;
+
+ buffer[AXIS_X] = bmc150_magn_compensate_x(&tregs, raw_x, rhall);
+ buffer[AXIS_Y] = bmc150_magn_compensate_y(&tregs, raw_y, rhall);
+ buffer[AXIS_Z] = bmc150_magn_compensate_z(&tregs, raw_z, rhall);
+
+ return 0;
+}
+
+static int bmc150_magn_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct bmc150_magn_data *data = iio_priv(indio_dev);
+ int ret, tmp;
+ s32 values[AXIS_XYZ_MAX];
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (iio_buffer_enabled(indio_dev))
+ return -EBUSY;
+ mutex_lock(&data->mutex);
+
+ ret = bmc150_magn_set_power_state(data, true);
+ if (ret < 0) {
+ mutex_unlock(&data->mutex);
+ return ret;
+ }
+
+ ret = bmc150_magn_read_xyz(data, values);
+ if (ret < 0) {
+ bmc150_magn_set_power_state(data, false);
+ mutex_unlock(&data->mutex);
+ return ret;
+ }
+ *val = values[chan->scan_index];
+
+ ret = bmc150_magn_set_power_state(data, false);
+ if (ret < 0) {
+ mutex_unlock(&data->mutex);
+ return ret;
+ }
+
+ mutex_unlock(&data->mutex);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ /*
+ * The API/driver performs an off-chip temperature
+ * compensation and outputs x/y/z magnetic field data in
+ * 16 LSB/uT to the upper application layer.
+ */
+ *val = 0;
+ *val2 = 625;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = bmc150_magn_get_odr(data, val);
+ if (ret < 0)
+ return ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ switch (chan->channel2) {
+ case IIO_MOD_X:
+ case IIO_MOD_Y:
+ ret = regmap_read(data->regmap, BMC150_MAGN_REG_REP_XY,
+ &tmp);
+ if (ret < 0)
+ return ret;
+ *val = BMC150_MAGN_REGVAL_TO_REPXY(tmp);
+ return IIO_VAL_INT;
+ case IIO_MOD_Z:
+ ret = regmap_read(data->regmap, BMC150_MAGN_REG_REP_Z,
+ &tmp);
+ if (ret < 0)
+ return ret;
+ *val = BMC150_MAGN_REGVAL_TO_REPZ(tmp);
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int bmc150_magn_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct bmc150_magn_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (val > data->max_odr)
+ return -EINVAL;
+ mutex_lock(&data->mutex);
+ ret = bmc150_magn_set_odr(data, val);
+ mutex_unlock(&data->mutex);
+ return ret;
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ switch (chan->channel2) {
+ case IIO_MOD_X:
+ case IIO_MOD_Y:
+ if (val < 1 || val > 511)
+ return -EINVAL;
+ mutex_lock(&data->mutex);
+ ret = bmc150_magn_set_max_odr(data, val, 0, 0);
+ if (ret < 0) {
+ mutex_unlock(&data->mutex);
+ return ret;
+ }
+ ret = regmap_update_bits(data->regmap,
+ BMC150_MAGN_REG_REP_XY,
+ 0xFF,
+ BMC150_MAGN_REPXY_TO_REGVAL
+ (val));
+ mutex_unlock(&data->mutex);
+ return ret;
+ case IIO_MOD_Z:
+ if (val < 1 || val > 256)
+ return -EINVAL;
+ mutex_lock(&data->mutex);
+ ret = bmc150_magn_set_max_odr(data, 0, val, 0);
+ if (ret < 0) {
+ mutex_unlock(&data->mutex);
+ return ret;
+ }
+ ret = regmap_update_bits(data->regmap,
+ BMC150_MAGN_REG_REP_Z,
+ 0xFF,
+ BMC150_MAGN_REPZ_TO_REGVAL
+ (val));
+ mutex_unlock(&data->mutex);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int bmc150_magn_validate_trigger(struct iio_dev *indio_dev,
+ struct iio_trigger *trig)
+{
+ struct bmc150_magn_data *data = iio_priv(indio_dev);
+
+ if (data->dready_trig != trig)
+ return -EINVAL;
+
+ return 0;
+}
+
+static ssize_t bmc150_magn_show_samp_freq_avail(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct bmc150_magn_data *data = iio_priv(indio_dev);
+ size_t len = 0;
+ u8 i;
+
+ for (i = 0; i < ARRAY_SIZE(bmc150_magn_samp_freq_table); i++) {
+ if (bmc150_magn_samp_freq_table[i].freq > data->max_odr)
+ break;
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
+ bmc150_magn_samp_freq_table[i].freq);
+ }
+ /* replace last space with a newline */
+ buf[len - 1] = '\n';
+
+ return len;
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(bmc150_magn_show_samp_freq_avail);
+
+static struct attribute *bmc150_magn_attributes[] = {
+ &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group bmc150_magn_attrs_group = {
+ .attrs = bmc150_magn_attributes,
+};
+
+#define BMC150_MAGN_CHANNEL(_axis) { \
+ .type = IIO_MAGN, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_##_axis, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_index = AXIS_##_axis, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 32, \
+ .storagebits = 32, \
+ .endianness = IIO_LE \
+ }, \
+}
+
+static const struct iio_chan_spec bmc150_magn_channels[] = {
+ BMC150_MAGN_CHANNEL(X),
+ BMC150_MAGN_CHANNEL(Y),
+ BMC150_MAGN_CHANNEL(Z),
+ IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static const struct iio_info bmc150_magn_info = {
+ .attrs = &bmc150_magn_attrs_group,
+ .read_raw = bmc150_magn_read_raw,
+ .write_raw = bmc150_magn_write_raw,
+ .validate_trigger = bmc150_magn_validate_trigger,
+ .driver_module = THIS_MODULE,
+};
+
+static const unsigned long bmc150_magn_scan_masks[] = {0x07, 0};
+
+static irqreturn_t bmc150_magn_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct bmc150_magn_data *data = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&data->mutex);
+ ret = bmc150_magn_read_xyz(data, data->buffer);
+ mutex_unlock(&data->mutex);
+ if (ret < 0)
+ goto err;
+
+ iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
+ pf->timestamp);
+
+err:
+ iio_trigger_notify_done(data->dready_trig);
+
+ return IRQ_HANDLED;
+}
+
+static int bmc150_magn_init(struct bmc150_magn_data *data)
+{
+ int ret, chip_id;
+ struct bmc150_magn_preset preset;
+
+ ret = bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND,
+ false);
+ if (ret < 0) {
+ dev_err(&data->client->dev,
+ "Failed to bring up device from suspend mode\n");
+ return ret;
+ }
+
+ ret = regmap_read(data->regmap, BMC150_MAGN_REG_CHIP_ID, &chip_id);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Failed reading chip id\n");
+ goto err_poweroff;
+ }
+ if (chip_id != BMC150_MAGN_CHIP_ID_VAL) {
+ dev_err(&data->client->dev, "Invalid chip id 0x%x\n", chip_id);
+ ret = -ENODEV;
+ goto err_poweroff;
+ }
+ dev_dbg(&data->client->dev, "Chip id %x\n", chip_id);
+
+ preset = bmc150_magn_presets_table[BMC150_MAGN_DEFAULT_PRESET];
+ ret = bmc150_magn_set_odr(data, preset.odr);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Failed to set ODR to %d\n",
+ preset.odr);
+ goto err_poweroff;
+ }
+
+ ret = regmap_write(data->regmap, BMC150_MAGN_REG_REP_XY,
+ BMC150_MAGN_REPXY_TO_REGVAL(preset.rep_xy));
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Failed to set REP XY to %d\n",
+ preset.rep_xy);
+ goto err_poweroff;
+ }
+
+ ret = regmap_write(data->regmap, BMC150_MAGN_REG_REP_Z,
+ BMC150_MAGN_REPZ_TO_REGVAL(preset.rep_z));
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Failed to set REP Z to %d\n",
+ preset.rep_z);
+ goto err_poweroff;
+ }
+
+ ret = bmc150_magn_set_max_odr(data, preset.rep_xy, preset.rep_z,
+ preset.odr);
+ if (ret < 0)
+ goto err_poweroff;
+
+ ret = bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_NORMAL,
+ true);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Failed to power on device\n");
+ goto err_poweroff;
+ }
+
+ return 0;
+
+err_poweroff:
+ bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND, true);
+ return ret;
+}
+
+static int bmc150_magn_reset_intr(struct bmc150_magn_data *data)
+{
+ int tmp;
+
+ /*
+ * Data Ready (DRDY) is always cleared after
+ * readout of data registers ends.
+ */
+ return regmap_read(data->regmap, BMC150_MAGN_REG_X_L, &tmp);
+}
+
+static int bmc150_magn_trig_try_reen(struct iio_trigger *trig)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct bmc150_magn_data *data = iio_priv(indio_dev);
+ int ret;
+
+ if (!data->dready_trigger_on)
+ return 0;
+
+ mutex_lock(&data->mutex);
+ ret = bmc150_magn_reset_intr(data);
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
+static int bmc150_magn_data_rdy_trigger_set_state(struct iio_trigger *trig,
+ bool state)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct bmc150_magn_data *data = iio_priv(indio_dev);
+ int ret = 0;
+
+ mutex_lock(&data->mutex);
+ if (state == data->dready_trigger_on)
+ goto err_unlock;
+
+ ret = bmc150_magn_set_power_state(data, state);
+ if (ret < 0)
+ goto err_unlock;
+
+ ret = regmap_update_bits(data->regmap, BMC150_MAGN_REG_INT_DRDY,
+ BMC150_MAGN_MASK_DRDY_EN,
+ state << BMC150_MAGN_SHIFT_DRDY_EN);
+ if (ret < 0)
+ goto err_poweroff;
+
+ data->dready_trigger_on = state;
+
+ if (state) {
+ ret = bmc150_magn_reset_intr(data);
+ if (ret < 0)
+ goto err_poweroff;
+ }
+ mutex_unlock(&data->mutex);
+
+ return 0;
+
+err_poweroff:
+ bmc150_magn_set_power_state(data, false);
+err_unlock:
+ mutex_unlock(&data->mutex);
+ return ret;
+}
+
+static const struct iio_trigger_ops bmc150_magn_trigger_ops = {
+ .set_trigger_state = bmc150_magn_data_rdy_trigger_set_state,
+ .try_reenable = bmc150_magn_trig_try_reen,
+ .owner = THIS_MODULE,
+};
+
+static int bmc150_magn_gpio_probe(struct i2c_client *client)
+{
+ struct device *dev;
+ struct gpio_desc *gpio;
+ int ret;
+
+ if (!client)
+ return -EINVAL;
+
+ dev = &client->dev;
+
+ /* data ready GPIO interrupt pin */
+ gpio = devm_gpiod_get_index(dev, BMC150_MAGN_GPIO_INT, 0);
+ if (IS_ERR(gpio)) {
+ dev_err(dev, "ACPI GPIO get index failed\n");
+ return PTR_ERR(gpio);
+ }
+
+ ret = gpiod_direction_input(gpio);
+ if (ret)
+ return ret;
+
+ ret = gpiod_to_irq(gpio);
+
+ dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
+
+ return ret;
+}
+
+static const char *bmc150_magn_match_acpi_device(struct device *dev)
+{
+ const struct acpi_device_id *id;
+
+ id = acpi_match_device(dev->driver->acpi_match_table, dev);
+ if (!id)
+ return NULL;
+
+ return dev_name(dev);
+}
+
+static int bmc150_magn_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct bmc150_magn_data *data;
+ struct iio_dev *indio_dev;
+ const char *name = NULL;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ data->client = client;
+
+ if (id)
+ name = id->name;
+ else if (ACPI_HANDLE(&client->dev))
+ name = bmc150_magn_match_acpi_device(&client->dev);
+ else
+ return -ENOSYS;
+
+ mutex_init(&data->mutex);
+ data->regmap = devm_regmap_init_i2c(client, &bmc150_magn_regmap_config);
+ if (IS_ERR(data->regmap)) {
+ dev_err(&client->dev, "Failed to allocate register map\n");
+ return PTR_ERR(data->regmap);
+ }
+
+ ret = bmc150_magn_init(data);
+ if (ret < 0)
+ return ret;
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->channels = bmc150_magn_channels;
+ indio_dev->num_channels = ARRAY_SIZE(bmc150_magn_channels);
+ indio_dev->available_scan_masks = bmc150_magn_scan_masks;
+ indio_dev->name = name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &bmc150_magn_info;
+
+ if (client->irq <= 0)
+ client->irq = bmc150_magn_gpio_probe(client);
+
+ if (client->irq > 0) {
+ data->dready_trig = devm_iio_trigger_alloc(&client->dev,
+ "%s-dev%d",
+ indio_dev->name,
+ indio_dev->id);
+ if (!data->dready_trig) {
+ ret = -ENOMEM;
+ dev_err(&client->dev, "iio trigger alloc failed\n");
+ goto err_poweroff;
+ }
+
+ data->dready_trig->dev.parent = &client->dev;
+ data->dready_trig->ops = &bmc150_magn_trigger_ops;
+ iio_trigger_set_drvdata(data->dready_trig, indio_dev);
+ ret = iio_trigger_register(data->dready_trig);
+ if (ret) {
+ dev_err(&client->dev, "iio trigger register failed\n");
+ goto err_poweroff;
+ }
+
+ ret = iio_triggered_buffer_setup(indio_dev,
+ &iio_pollfunc_store_time,
+ bmc150_magn_trigger_handler,
+ NULL);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "iio triggered buffer setup failed\n");
+ goto err_trigger_unregister;
+ }
+
+ ret = request_threaded_irq(client->irq,
+ iio_trigger_generic_data_rdy_poll,
+ NULL,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ BMC150_MAGN_IRQ_NAME,
+ data->dready_trig);
+ if (ret < 0) {
+ dev_err(&client->dev, "request irq %d failed\n",
+ client->irq);
+ goto err_buffer_cleanup;
+ }
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(&client->dev, "unable to register iio device\n");
+ goto err_free_irq;
+ }
+
+ ret = pm_runtime_set_active(&client->dev);
+ if (ret)
+ goto err_iio_unregister;
+
+ pm_runtime_enable(&client->dev);
+ pm_runtime_set_autosuspend_delay(&client->dev,
+ BMC150_MAGN_AUTO_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&client->dev);
+
+ dev_dbg(&indio_dev->dev, "Registered device %s\n", name);
+
+ return 0;
+
+err_iio_unregister:
+ iio_device_unregister(indio_dev);
+err_free_irq:
+ if (client->irq > 0)
+ free_irq(client->irq, data->dready_trig);
+err_buffer_cleanup:
+ if (data->dready_trig)
+ iio_triggered_buffer_cleanup(indio_dev);
+err_trigger_unregister:
+ if (data->dready_trig)
+ iio_trigger_unregister(data->dready_trig);
+err_poweroff:
+ bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND, true);
+ return ret;
+}
+
+static int bmc150_magn_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct bmc150_magn_data *data = iio_priv(indio_dev);
+
+ pm_runtime_disable(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+ pm_runtime_put_noidle(&client->dev);
+
+ iio_device_unregister(indio_dev);
+
+ if (client->irq > 0)
+ free_irq(data->client->irq, data->dready_trig);
+
+ if (data->dready_trig) {
+ iio_triggered_buffer_cleanup(indio_dev);
+ iio_trigger_unregister(data->dready_trig);
+ }
+
+ mutex_lock(&data->mutex);
+ bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SUSPEND, true);
+ mutex_unlock(&data->mutex);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int bmc150_magn_runtime_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct bmc150_magn_data *data = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&data->mutex);
+ ret = bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SLEEP,
+ true);
+ mutex_unlock(&data->mutex);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "powering off device failed\n");
+ return ret;
+ }
+ return 0;
+}
+
+static int bmc150_magn_runtime_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct bmc150_magn_data *data = iio_priv(indio_dev);
+
+ return bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_NORMAL,
+ true);
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int bmc150_magn_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct bmc150_magn_data *data = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&data->mutex);
+ ret = bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_SLEEP,
+ true);
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
+static int bmc150_magn_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct bmc150_magn_data *data = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&data->mutex);
+ ret = bmc150_magn_set_power_mode(data, BMC150_MAGN_POWER_MODE_NORMAL,
+ true);
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+#endif
+
+static const struct dev_pm_ops bmc150_magn_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(bmc150_magn_suspend, bmc150_magn_resume)
+ SET_RUNTIME_PM_OPS(bmc150_magn_runtime_suspend,
+ bmc150_magn_runtime_resume, NULL)
+};
+
+static const struct acpi_device_id bmc150_magn_acpi_match[] = {
+ {"BMC150B", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, bmc150_magn_acpi_match);
+
+static const struct i2c_device_id bmc150_magn_id[] = {
+ {"bmc150_magn", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, bmc150_magn_id);
+
+static struct i2c_driver bmc150_magn_driver = {
+ .driver = {
+ .name = BMC150_MAGN_DRV_NAME,
+ .acpi_match_table = ACPI_PTR(bmc150_magn_acpi_match),
+ .pm = &bmc150_magn_pm_ops,
+ },
+ .probe = bmc150_magn_probe,
+ .remove = bmc150_magn_remove,
+ .id_table = bmc150_magn_id,
+};
+module_i2c_driver(bmc150_magn_driver);
+
+MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("BMC150 magnetometer driver");
diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c
index 4f9c0be24..d8a0c8da8 100644
--- a/drivers/iio/magnetometer/hid-sensor-magn-3d.c
+++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c
@@ -510,7 +510,7 @@ static int hid_magn_3d_remove(struct platform_device *pdev)
return 0;
}
-static struct platform_device_id hid_magn_3d_ids[] = {
+static const struct platform_device_id hid_magn_3d_ids[] = {
{
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
.name = "HID-SENSOR-200083",
diff --git a/drivers/iio/magnetometer/mmc35240.c b/drivers/iio/magnetometer/mmc35240.c
new file mode 100644
index 000000000..706ebfd62
--- /dev/null
+++ b/drivers/iio/magnetometer/mmc35240.c
@@ -0,0 +1,588 @@
+/*
+ * MMC35240 - MEMSIC 3-axis Magnetic Sensor
+ *
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This file is subject to the terms and conditions of version 2 of
+ * the GNU General Public License. See the file COPYING in the main
+ * directory of this archive for more details.
+ *
+ * IIO driver for MMC35240 (7-bit I2C slave address 0x30).
+ *
+ * TODO: offset, ACPI, continuous measurement mode, PM
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/regmap.h>
+#include <linux/acpi.h>
+#include <linux/pm.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define MMC35240_DRV_NAME "mmc35240"
+#define MMC35240_REGMAP_NAME "mmc35240_regmap"
+
+#define MMC35240_REG_XOUT_L 0x00
+#define MMC35240_REG_XOUT_H 0x01
+#define MMC35240_REG_YOUT_L 0x02
+#define MMC35240_REG_YOUT_H 0x03
+#define MMC35240_REG_ZOUT_L 0x04
+#define MMC35240_REG_ZOUT_H 0x05
+
+#define MMC35240_REG_STATUS 0x06
+#define MMC35240_REG_CTRL0 0x07
+#define MMC35240_REG_CTRL1 0x08
+
+#define MMC35240_REG_ID 0x20
+
+#define MMC35240_STATUS_MEAS_DONE_BIT BIT(0)
+
+#define MMC35240_CTRL0_REFILL_BIT BIT(7)
+#define MMC35240_CTRL0_RESET_BIT BIT(6)
+#define MMC35240_CTRL0_SET_BIT BIT(5)
+#define MMC35240_CTRL0_CMM_BIT BIT(1)
+#define MMC35240_CTRL0_TM_BIT BIT(0)
+
+/* output resolution bits */
+#define MMC35240_CTRL1_BW0_BIT BIT(0)
+#define MMC35240_CTRL1_BW1_BIT BIT(1)
+
+#define MMC35240_CTRL1_BW_MASK (MMC35240_CTRL1_BW0_BIT | \
+ MMC35240_CTRL1_BW1_BIT)
+#define MMC35240_CTRL1_BW_SHIFT 0
+
+#define MMC35240_WAIT_CHARGE_PUMP 50000 /* us */
+#define MMC53240_WAIT_SET_RESET 1000 /* us */
+
+/*
+ * Memsic OTP process code piece is put here for reference:
+ *
+ * #define OTP_CONVERT(REG) ((float)((REG) >=32 ? (32 - (REG)) : (REG)) * 0.006
+ * 1) For X axis, the COEFFICIENT is always 1.
+ * 2) For Y axis, the COEFFICIENT is as below:
+ * f_OTP_matrix[4] = OTP_CONVERT(((reg_data[1] & 0x03) << 4) |
+ * (reg_data[2] >> 4)) + 1.0;
+ * 3) For Z axis, the COEFFICIENT is as below:
+ * f_OTP_matrix[8] = (OTP_CONVERT(reg_data[3] & 0x3f) + 1) * 1.35;
+ * We implemented the OTP logic into driver.
+ */
+
+/* scale = 1000 here for Y otp */
+#define MMC35240_OTP_CONVERT_Y(REG) (((REG) >= 32 ? (32 - (REG)) : (REG)) * 6)
+
+/* 0.6 * 1.35 = 0.81, scale 10000 for Z otp */
+#define MMC35240_OTP_CONVERT_Z(REG) (((REG) >= 32 ? (32 - (REG)) : (REG)) * 81)
+
+#define MMC35240_X_COEFF(x) (x)
+#define MMC35240_Y_COEFF(y) (y + 1000)
+#define MMC35240_Z_COEFF(z) (z + 13500)
+
+#define MMC35240_OTP_START_ADDR 0x1B
+
+enum mmc35240_resolution {
+ MMC35240_16_BITS_SLOW = 0, /* 7.92 ms */
+ MMC35240_16_BITS_FAST, /* 4.08 ms */
+ MMC35240_14_BITS, /* 2.16 ms */
+ MMC35240_12_BITS, /* 1.20 ms */
+};
+
+enum mmc35240_axis {
+ AXIS_X = 0,
+ AXIS_Y,
+ AXIS_Z,
+};
+
+static const struct {
+ int sens[3]; /* sensitivity per X, Y, Z axis */
+ int nfo; /* null field output */
+} mmc35240_props_table[] = {
+ /* 16 bits, 125Hz ODR */
+ {
+ {1024, 1024, 1024},
+ 32768,
+ },
+ /* 16 bits, 250Hz ODR */
+ {
+ {1024, 1024, 770},
+ 32768,
+ },
+ /* 14 bits, 450Hz ODR */
+ {
+ {256, 256, 193},
+ 8192,
+ },
+ /* 12 bits, 800Hz ODR */
+ {
+ {64, 64, 48},
+ 2048,
+ },
+};
+
+struct mmc35240_data {
+ struct i2c_client *client;
+ struct mutex mutex;
+ struct regmap *regmap;
+ enum mmc35240_resolution res;
+
+ /* OTP compensation */
+ int axis_coef[3];
+ int axis_scale[3];
+};
+
+static const struct {
+ int val;
+ int val2;
+} mmc35240_samp_freq[] = { {1, 500000},
+ {13, 0},
+ {25, 0},
+ {50, 0} };
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("1.5 13 25 50");
+
+#define MMC35240_CHANNEL(_axis) { \
+ .type = IIO_MAGN, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_ ## _axis, \
+ .address = AXIS_ ## _axis, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+}
+
+static const struct iio_chan_spec mmc35240_channels[] = {
+ MMC35240_CHANNEL(X),
+ MMC35240_CHANNEL(Y),
+ MMC35240_CHANNEL(Z),
+};
+
+static struct attribute *mmc35240_attributes[] = {
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group mmc35240_attribute_group = {
+ .attrs = mmc35240_attributes,
+};
+
+static int mmc35240_get_samp_freq_index(struct mmc35240_data *data,
+ int val, int val2)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mmc35240_samp_freq); i++)
+ if (mmc35240_samp_freq[i].val == val &&
+ mmc35240_samp_freq[i].val2 == val2)
+ return i;
+ return -EINVAL;
+}
+
+static int mmc35240_hw_set(struct mmc35240_data *data, bool set)
+{
+ int ret;
+ u8 coil_bit;
+
+ /*
+ * Recharge the capacitor at VCAP pin, requested to be issued
+ * before a SET/RESET command.
+ */
+ ret = regmap_update_bits(data->regmap, MMC35240_REG_CTRL0,
+ MMC35240_CTRL0_REFILL_BIT,
+ MMC35240_CTRL0_REFILL_BIT);
+ if (ret < 0)
+ return ret;
+ usleep_range(MMC35240_WAIT_CHARGE_PUMP, MMC35240_WAIT_CHARGE_PUMP + 1);
+
+ if (set)
+ coil_bit = MMC35240_CTRL0_SET_BIT;
+ else
+ coil_bit = MMC35240_CTRL0_RESET_BIT;
+
+ return regmap_update_bits(data->regmap, MMC35240_REG_CTRL0,
+ coil_bit, coil_bit);
+
+}
+
+static int mmc35240_init(struct mmc35240_data *data)
+{
+ int ret, y_convert, z_convert;
+ unsigned int reg_id;
+ u8 otp_data[6];
+
+ ret = regmap_read(data->regmap, MMC35240_REG_ID, &reg_id);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error reading product id\n");
+ return ret;
+ }
+
+ dev_dbg(&data->client->dev, "MMC35240 chip id %x\n", reg_id);
+
+ /*
+ * make sure we restore sensor characteristics, by doing
+ * a SET/RESET sequence, the axis polarity being naturally
+ * aligned after RESET
+ */
+ ret = mmc35240_hw_set(data, true);
+ if (ret < 0)
+ return ret;
+ usleep_range(MMC53240_WAIT_SET_RESET, MMC53240_WAIT_SET_RESET + 1);
+
+ ret = mmc35240_hw_set(data, false);
+ if (ret < 0)
+ return ret;
+
+ /* set default sampling frequency */
+ ret = regmap_update_bits(data->regmap, MMC35240_REG_CTRL1,
+ MMC35240_CTRL1_BW_MASK,
+ data->res << MMC35240_CTRL1_BW_SHIFT);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_bulk_read(data->regmap, MMC35240_OTP_START_ADDR,
+ (u8 *)otp_data, sizeof(otp_data));
+ if (ret < 0)
+ return ret;
+
+ y_convert = MMC35240_OTP_CONVERT_Y(((otp_data[1] & 0x03) << 4) |
+ (otp_data[2] >> 4));
+ z_convert = MMC35240_OTP_CONVERT_Z(otp_data[3] & 0x3f);
+
+ data->axis_coef[0] = MMC35240_X_COEFF(1);
+ data->axis_coef[1] = MMC35240_Y_COEFF(y_convert);
+ data->axis_coef[2] = MMC35240_Z_COEFF(z_convert);
+
+ data->axis_scale[0] = 1;
+ data->axis_scale[1] = 1000;
+ data->axis_scale[2] = 10000;
+
+ return 0;
+}
+
+static int mmc35240_take_measurement(struct mmc35240_data *data)
+{
+ int ret, tries = 100;
+ unsigned int reg_status;
+
+ ret = regmap_write(data->regmap, MMC35240_REG_CTRL0,
+ MMC35240_CTRL0_TM_BIT);
+ if (ret < 0)
+ return ret;
+
+ while (tries-- > 0) {
+ ret = regmap_read(data->regmap, MMC35240_REG_STATUS,
+ &reg_status);
+ if (ret < 0)
+ return ret;
+ if (reg_status & MMC35240_STATUS_MEAS_DONE_BIT)
+ break;
+ /* minimum wait time to complete measurement is 10 ms */
+ usleep_range(10000, 11000);
+ }
+
+ if (tries < 0) {
+ dev_err(&data->client->dev, "data not ready\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int mmc35240_read_measurement(struct mmc35240_data *data, __le16 buf[3])
+{
+ int ret;
+
+ ret = mmc35240_take_measurement(data);
+ if (ret < 0)
+ return ret;
+
+ return regmap_bulk_read(data->regmap, MMC35240_REG_XOUT_L, (u8 *)buf,
+ 3 * sizeof(__le16));
+}
+
+/**
+ * mmc35240_raw_to_mgauss - convert raw readings to milli gauss. Also apply
+ compensation for output value.
+ *
+ * @data: device private data
+ * @index: axis index for which we want the conversion
+ * @buf: raw data to be converted, 2 bytes in little endian format
+ * @val: compensated output reading (unit is milli gauss)
+ *
+ * Returns: 0 in case of success, -EINVAL when @index is not valid
+ */
+static int mmc35240_raw_to_mgauss(struct mmc35240_data *data, int index,
+ __le16 buf[], int *val)
+{
+ int raw_x, raw_y, raw_z;
+ int sens_x, sens_y, sens_z;
+ int nfo;
+
+ raw_x = le16_to_cpu(buf[AXIS_X]);
+ raw_y = le16_to_cpu(buf[AXIS_Y]);
+ raw_z = le16_to_cpu(buf[AXIS_Z]);
+
+ sens_x = mmc35240_props_table[data->res].sens[AXIS_X];
+ sens_y = mmc35240_props_table[data->res].sens[AXIS_Y];
+ sens_z = mmc35240_props_table[data->res].sens[AXIS_Z];
+
+ nfo = mmc35240_props_table[data->res].nfo;
+
+ switch (index) {
+ case AXIS_X:
+ *val = (raw_x - nfo) * 1000 / sens_x;
+ break;
+ case AXIS_Y:
+ *val = (raw_y - nfo) * 1000 / sens_y -
+ (raw_z - nfo) * 1000 / sens_z;
+ break;
+ case AXIS_Z:
+ *val = (raw_y - nfo) * 1000 / sens_y +
+ (raw_z - nfo) * 1000 / sens_z;
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* apply OTP compensation */
+ *val = (*val) * data->axis_coef[index] / data->axis_scale[index];
+
+ return 0;
+}
+
+static int mmc35240_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct mmc35240_data *data = iio_priv(indio_dev);
+ int ret, i;
+ unsigned int reg;
+ __le16 buf[3];
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&data->mutex);
+ ret = mmc35240_read_measurement(data, buf);
+ mutex_unlock(&data->mutex);
+ if (ret < 0)
+ return ret;
+ ret = mmc35240_raw_to_mgauss(data, chan->address, buf, val);
+ if (ret < 0)
+ return ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ *val2 = 1000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ mutex_lock(&data->mutex);
+ ret = regmap_read(data->regmap, MMC35240_REG_CTRL1, &reg);
+ mutex_unlock(&data->mutex);
+ if (ret < 0)
+ return ret;
+
+ i = (reg & MMC35240_CTRL1_BW_MASK) >> MMC35240_CTRL1_BW_SHIFT;
+ if (i < 0 || i >= ARRAY_SIZE(mmc35240_samp_freq))
+ return -EINVAL;
+
+ *val = mmc35240_samp_freq[i].val;
+ *val2 = mmc35240_samp_freq[i].val2;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mmc35240_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long mask)
+{
+ struct mmc35240_data *data = iio_priv(indio_dev);
+ int i, ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ i = mmc35240_get_samp_freq_index(data, val, val2);
+ if (i < 0)
+ return -EINVAL;
+ mutex_lock(&data->mutex);
+ ret = regmap_update_bits(data->regmap, MMC35240_REG_CTRL1,
+ MMC35240_CTRL1_BW_MASK,
+ i << MMC35240_CTRL1_BW_SHIFT);
+ mutex_unlock(&data->mutex);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info mmc35240_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = mmc35240_read_raw,
+ .write_raw = mmc35240_write_raw,
+ .attrs = &mmc35240_attribute_group,
+};
+
+static bool mmc35240_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MMC35240_REG_CTRL0:
+ case MMC35240_REG_CTRL1:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool mmc35240_is_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MMC35240_REG_XOUT_L:
+ case MMC35240_REG_XOUT_H:
+ case MMC35240_REG_YOUT_L:
+ case MMC35240_REG_YOUT_H:
+ case MMC35240_REG_ZOUT_L:
+ case MMC35240_REG_ZOUT_H:
+ case MMC35240_REG_STATUS:
+ case MMC35240_REG_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool mmc35240_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MMC35240_REG_CTRL0:
+ case MMC35240_REG_CTRL1:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static struct reg_default mmc35240_reg_defaults[] = {
+ { MMC35240_REG_CTRL0, 0x00 },
+ { MMC35240_REG_CTRL1, 0x00 },
+};
+
+static const struct regmap_config mmc35240_regmap_config = {
+ .name = MMC35240_REGMAP_NAME,
+
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = MMC35240_REG_ID,
+ .cache_type = REGCACHE_FLAT,
+
+ .writeable_reg = mmc35240_is_writeable_reg,
+ .readable_reg = mmc35240_is_readable_reg,
+ .volatile_reg = mmc35240_is_volatile_reg,
+
+ .reg_defaults = mmc35240_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(mmc35240_reg_defaults),
+};
+
+static int mmc35240_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct mmc35240_data *data;
+ struct iio_dev *indio_dev;
+ struct regmap *regmap;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ regmap = devm_regmap_init_i2c(client, &mmc35240_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(&client->dev, "regmap initialization failed\n");
+ return PTR_ERR(regmap);
+ }
+
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ data->client = client;
+ data->regmap = regmap;
+ data->res = MMC35240_16_BITS_SLOW;
+
+ mutex_init(&data->mutex);
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->info = &mmc35240_info;
+ indio_dev->name = MMC35240_DRV_NAME;
+ indio_dev->channels = mmc35240_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mmc35240_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = mmc35240_init(data);
+ if (ret < 0) {
+ dev_err(&client->dev, "mmc35240 chip init failed\n");
+ return ret;
+ }
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mmc35240_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct mmc35240_data *data = iio_priv(indio_dev);
+
+ regcache_cache_only(data->regmap, true);
+
+ return 0;
+}
+
+static int mmc35240_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct mmc35240_data *data = iio_priv(indio_dev);
+ int ret;
+
+ regcache_mark_dirty(data->regmap);
+ ret = regcache_sync_region(data->regmap, MMC35240_REG_CTRL0,
+ MMC35240_REG_CTRL1);
+ if (ret < 0)
+ dev_err(dev, "Failed to restore control registers\n");
+
+ regcache_cache_only(data->regmap, false);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops mmc35240_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(mmc35240_suspend, mmc35240_resume)
+};
+
+static const struct acpi_device_id mmc35240_acpi_match[] = {
+ {"MMC35240", 0},
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, mmc35240_acpi_match);
+
+static const struct i2c_device_id mmc35240_id[] = {
+ {"mmc35240", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, mmc35240_id);
+
+static struct i2c_driver mmc35240_driver = {
+ .driver = {
+ .name = MMC35240_DRV_NAME,
+ .pm = &mmc35240_pm_ops,
+ .acpi_match_table = ACPI_PTR(mmc35240_acpi_match),
+ },
+ .probe = mmc35240_probe,
+ .id_table = mmc35240_id,
+};
+
+module_i2c_driver(mmc35240_driver);
+
+MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
+MODULE_DESCRIPTION("MEMSIC MMC35240 magnetic sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/magnetometer/st_magn.h b/drivers/iio/magnetometer/st_magn.h
index 7e81d00ef..287691ca5 100644
--- a/drivers/iio/magnetometer/st_magn.h
+++ b/drivers/iio/magnetometer/st_magn.h
@@ -14,6 +14,7 @@
#include <linux/types.h>
#include <linux/iio/common/st_sensors.h>
+#define LSM303DLH_MAGN_DEV_NAME "lsm303dlh_magn"
#define LSM303DLHC_MAGN_DEV_NAME "lsm303dlhc_magn"
#define LSM303DLM_MAGN_DEV_NAME "lsm303dlm_magn"
#define LIS3MDL_MAGN_DEV_NAME "lis3mdl"
diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c
index 2e56f812a..b4bcfb790 100644
--- a/drivers/iio/magnetometer/st_magn_core.c
+++ b/drivers/iio/magnetometer/st_magn_core.c
@@ -45,6 +45,46 @@
#define ST_MAGN_FS_AVL_12000MG 12000
#define ST_MAGN_FS_AVL_16000MG 16000
+/* CUSTOM VALUES FOR SENSOR 0 */
+#define ST_MAGN_0_ODR_ADDR 0x00
+#define ST_MAGN_0_ODR_MASK 0x1c
+#define ST_MAGN_0_ODR_AVL_1HZ_VAL 0x00
+#define ST_MAGN_0_ODR_AVL_2HZ_VAL 0x01
+#define ST_MAGN_0_ODR_AVL_3HZ_VAL 0x02
+#define ST_MAGN_0_ODR_AVL_8HZ_VAL 0x03
+#define ST_MAGN_0_ODR_AVL_15HZ_VAL 0x04
+#define ST_MAGN_0_ODR_AVL_30HZ_VAL 0x05
+#define ST_MAGN_0_ODR_AVL_75HZ_VAL 0x06
+#define ST_MAGN_0_ODR_AVL_220HZ_VAL 0x07
+#define ST_MAGN_0_PW_ADDR 0x02
+#define ST_MAGN_0_PW_MASK 0x03
+#define ST_MAGN_0_PW_ON 0x00
+#define ST_MAGN_0_PW_OFF 0x03
+#define ST_MAGN_0_FS_ADDR 0x01
+#define ST_MAGN_0_FS_MASK 0xe0
+#define ST_MAGN_0_FS_AVL_1300_VAL 0x01
+#define ST_MAGN_0_FS_AVL_1900_VAL 0x02
+#define ST_MAGN_0_FS_AVL_2500_VAL 0x03
+#define ST_MAGN_0_FS_AVL_4000_VAL 0x04
+#define ST_MAGN_0_FS_AVL_4700_VAL 0x05
+#define ST_MAGN_0_FS_AVL_5600_VAL 0x06
+#define ST_MAGN_0_FS_AVL_8100_VAL 0x07
+#define ST_MAGN_0_FS_AVL_1300_GAIN_XY 1100
+#define ST_MAGN_0_FS_AVL_1900_GAIN_XY 855
+#define ST_MAGN_0_FS_AVL_2500_GAIN_XY 670
+#define ST_MAGN_0_FS_AVL_4000_GAIN_XY 450
+#define ST_MAGN_0_FS_AVL_4700_GAIN_XY 400
+#define ST_MAGN_0_FS_AVL_5600_GAIN_XY 330
+#define ST_MAGN_0_FS_AVL_8100_GAIN_XY 230
+#define ST_MAGN_0_FS_AVL_1300_GAIN_Z 980
+#define ST_MAGN_0_FS_AVL_1900_GAIN_Z 760
+#define ST_MAGN_0_FS_AVL_2500_GAIN_Z 600
+#define ST_MAGN_0_FS_AVL_4000_GAIN_Z 400
+#define ST_MAGN_0_FS_AVL_4700_GAIN_Z 355
+#define ST_MAGN_0_FS_AVL_5600_GAIN_Z 295
+#define ST_MAGN_0_FS_AVL_8100_GAIN_Z 205
+#define ST_MAGN_0_MULTIREAD_BIT false
+
/* CUSTOM VALUES FOR SENSOR 1 */
#define ST_MAGN_1_WAI_EXP 0x3c
#define ST_MAGN_1_ODR_ADDR 0x00
@@ -151,6 +191,82 @@ static const struct iio_chan_spec st_magn_2_16bit_channels[] = {
static const struct st_sensor_settings st_magn_sensors_settings[] = {
{
+ .wai = 0, /* This sensor has no valid WhoAmI report 0 */
+ .sensors_supported = {
+ [0] = LSM303DLH_MAGN_DEV_NAME,
+ },
+ .ch = (struct iio_chan_spec *)st_magn_16bit_channels,
+ .odr = {
+ .addr = ST_MAGN_0_ODR_ADDR,
+ .mask = ST_MAGN_0_ODR_MASK,
+ .odr_avl = {
+ { 1, ST_MAGN_0_ODR_AVL_1HZ_VAL, },
+ { 2, ST_MAGN_0_ODR_AVL_2HZ_VAL, },
+ { 3, ST_MAGN_0_ODR_AVL_3HZ_VAL, },
+ { 8, ST_MAGN_0_ODR_AVL_8HZ_VAL, },
+ { 15, ST_MAGN_0_ODR_AVL_15HZ_VAL, },
+ { 30, ST_MAGN_0_ODR_AVL_30HZ_VAL, },
+ { 75, ST_MAGN_0_ODR_AVL_75HZ_VAL, },
+ },
+ },
+ .pw = {
+ .addr = ST_MAGN_0_PW_ADDR,
+ .mask = ST_MAGN_0_PW_MASK,
+ .value_on = ST_MAGN_0_PW_ON,
+ .value_off = ST_MAGN_0_PW_OFF,
+ },
+ .fs = {
+ .addr = ST_MAGN_0_FS_ADDR,
+ .mask = ST_MAGN_0_FS_MASK,
+ .fs_avl = {
+ [0] = {
+ .num = ST_MAGN_FS_AVL_1300MG,
+ .value = ST_MAGN_0_FS_AVL_1300_VAL,
+ .gain = ST_MAGN_0_FS_AVL_1300_GAIN_XY,
+ .gain2 = ST_MAGN_0_FS_AVL_1300_GAIN_Z,
+ },
+ [1] = {
+ .num = ST_MAGN_FS_AVL_1900MG,
+ .value = ST_MAGN_0_FS_AVL_1900_VAL,
+ .gain = ST_MAGN_0_FS_AVL_1900_GAIN_XY,
+ .gain2 = ST_MAGN_0_FS_AVL_1900_GAIN_Z,
+ },
+ [2] = {
+ .num = ST_MAGN_FS_AVL_2500MG,
+ .value = ST_MAGN_0_FS_AVL_2500_VAL,
+ .gain = ST_MAGN_0_FS_AVL_2500_GAIN_XY,
+ .gain2 = ST_MAGN_0_FS_AVL_2500_GAIN_Z,
+ },
+ [3] = {
+ .num = ST_MAGN_FS_AVL_4000MG,
+ .value = ST_MAGN_0_FS_AVL_4000_VAL,
+ .gain = ST_MAGN_0_FS_AVL_4000_GAIN_XY,
+ .gain2 = ST_MAGN_0_FS_AVL_4000_GAIN_Z,
+ },
+ [4] = {
+ .num = ST_MAGN_FS_AVL_4700MG,
+ .value = ST_MAGN_0_FS_AVL_4700_VAL,
+ .gain = ST_MAGN_0_FS_AVL_4700_GAIN_XY,
+ .gain2 = ST_MAGN_0_FS_AVL_4700_GAIN_Z,
+ },
+ [5] = {
+ .num = ST_MAGN_FS_AVL_5600MG,
+ .value = ST_MAGN_0_FS_AVL_5600_VAL,
+ .gain = ST_MAGN_0_FS_AVL_5600_GAIN_XY,
+ .gain2 = ST_MAGN_0_FS_AVL_5600_GAIN_Z,
+ },
+ [6] = {
+ .num = ST_MAGN_FS_AVL_8100MG,
+ .value = ST_MAGN_0_FS_AVL_8100_VAL,
+ .gain = ST_MAGN_0_FS_AVL_8100_GAIN_XY,
+ .gain2 = ST_MAGN_0_FS_AVL_8100_GAIN_Z,
+ },
+ },
+ },
+ .multi_read_bit = ST_MAGN_0_MULTIREAD_BIT,
+ .bootime = 2,
+ },
+ {
.wai = ST_MAGN_1_WAI_EXP,
.sensors_supported = {
[0] = LSM303DLHC_MAGN_DEV_NAME,
diff --git a/drivers/iio/magnetometer/st_magn_i2c.c b/drivers/iio/magnetometer/st_magn_i2c.c
index 92e5c1545..5311d8aea 100644
--- a/drivers/iio/magnetometer/st_magn_i2c.c
+++ b/drivers/iio/magnetometer/st_magn_i2c.c
@@ -21,6 +21,10 @@
#ifdef CONFIG_OF
static const struct of_device_id st_magn_of_match[] = {
{
+ .compatible = "st,lsm303dlh-magn",
+ .data = LSM303DLH_MAGN_DEV_NAME,
+ },
+ {
.compatible = "st,lsm303dlhc-magn",
.data = LSM303DLHC_MAGN_DEV_NAME,
},
@@ -71,6 +75,7 @@ static int st_magn_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id st_magn_id_table[] = {
+ { LSM303DLH_MAGN_DEV_NAME },
{ LSM303DLHC_MAGN_DEV_NAME },
{ LSM303DLM_MAGN_DEV_NAME },
{ LIS3MDL_MAGN_DEV_NAME },
diff --git a/drivers/iio/orientation/hid-sensor-incl-3d.c b/drivers/iio/orientation/hid-sensor-incl-3d.c
index 5930fa32a..fd1b3696e 100644
--- a/drivers/iio/orientation/hid-sensor-incl-3d.c
+++ b/drivers/iio/orientation/hid-sensor-incl-3d.c
@@ -315,7 +315,6 @@ static int hid_incl_3d_probe(struct platform_device *pdev)
struct iio_dev *indio_dev;
struct incl_3d_state *incl_state;
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
- struct iio_chan_spec *channels;
indio_dev = devm_iio_device_alloc(&pdev->dev,
sizeof(struct incl_3d_state));
@@ -336,21 +335,22 @@ static int hid_incl_3d_probe(struct platform_device *pdev)
return ret;
}
- channels = kmemdup(incl_3d_channels, sizeof(incl_3d_channels),
- GFP_KERNEL);
- if (!channels) {
+ indio_dev->channels = kmemdup(incl_3d_channels,
+ sizeof(incl_3d_channels), GFP_KERNEL);
+ if (!indio_dev->channels) {
dev_err(&pdev->dev, "failed to duplicate channels\n");
return -ENOMEM;
}
- ret = incl_3d_parse_report(pdev, hsdev, channels,
- HID_USAGE_SENSOR_INCLINOMETER_3D, incl_state);
+ ret = incl_3d_parse_report(pdev, hsdev,
+ (struct iio_chan_spec *)indio_dev->channels,
+ HID_USAGE_SENSOR_INCLINOMETER_3D,
+ incl_state);
if (ret) {
dev_err(&pdev->dev, "failed to setup attributes\n");
goto error_free_dev_mem;
}
- indio_dev->channels = channels;
indio_dev->num_channels = ARRAY_SIZE(incl_3d_channels);
indio_dev->dev.parent = &pdev->dev;
indio_dev->info = &incl_3d_info;
@@ -417,7 +417,7 @@ static int hid_incl_3d_remove(struct platform_device *pdev)
return 0;
}
-static struct platform_device_id hid_incl_3d_ids[] = {
+static const struct platform_device_id hid_incl_3d_ids[] = {
{
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
.name = "HID-SENSOR-200086",
diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c
index 4afb6c79c..b98b9d94d 100644
--- a/drivers/iio/orientation/hid-sensor-rotation.c
+++ b/drivers/iio/orientation/hid-sensor-rotation.c
@@ -222,7 +222,6 @@ static int hid_dev_rot_probe(struct platform_device *pdev)
struct iio_dev *indio_dev;
struct dev_rot_state *rot_state;
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
- struct iio_chan_spec *channels;
indio_dev = devm_iio_device_alloc(&pdev->dev,
sizeof(struct dev_rot_state));
@@ -243,21 +242,23 @@ static int hid_dev_rot_probe(struct platform_device *pdev)
return ret;
}
- channels = devm_kmemdup(&pdev->dev, dev_rot_channels,
- sizeof(dev_rot_channels), GFP_KERNEL);
- if (!channels) {
+ indio_dev->channels = devm_kmemdup(&pdev->dev, dev_rot_channels,
+ sizeof(dev_rot_channels),
+ GFP_KERNEL);
+ if (!indio_dev->channels) {
dev_err(&pdev->dev, "failed to duplicate channels\n");
return -ENOMEM;
}
- ret = dev_rot_parse_report(pdev, hsdev, channels,
- HID_USAGE_SENSOR_DEVICE_ORIENTATION, rot_state);
+ ret = dev_rot_parse_report(pdev, hsdev,
+ (struct iio_chan_spec *)indio_dev->channels,
+ HID_USAGE_SENSOR_DEVICE_ORIENTATION,
+ rot_state);
if (ret) {
dev_err(&pdev->dev, "failed to setup attributes\n");
return ret;
}
- indio_dev->channels = channels;
indio_dev->num_channels = ARRAY_SIZE(dev_rot_channels);
indio_dev->dev.parent = &pdev->dev;
indio_dev->info = &dev_rot_info;
@@ -321,7 +322,7 @@ static int hid_dev_rot_remove(struct platform_device *pdev)
return 0;
}
-static struct platform_device_id hid_dev_rot_ids[] = {
+static const struct platform_device_id hid_dev_rot_ids[] = {
{
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
.name = "HID-SENSOR-20008a",
diff --git a/drivers/iio/pressure/hid-sensor-press.c b/drivers/iio/pressure/hid-sensor-press.c
index 3cf0bd67d..6848d8c80 100644
--- a/drivers/iio/pressure/hid-sensor-press.c
+++ b/drivers/iio/pressure/hid-sensor-press.c
@@ -260,7 +260,6 @@ static int hid_press_probe(struct platform_device *pdev)
struct iio_dev *indio_dev;
struct press_state *press_state;
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
- struct iio_chan_spec *channels;
indio_dev = devm_iio_device_alloc(&pdev->dev,
sizeof(struct press_state));
@@ -280,20 +279,21 @@ static int hid_press_probe(struct platform_device *pdev)
return ret;
}
- channels = kmemdup(press_channels, sizeof(press_channels), GFP_KERNEL);
- if (!channels) {
+ indio_dev->channels = kmemdup(press_channels, sizeof(press_channels),
+ GFP_KERNEL);
+ if (!indio_dev->channels) {
dev_err(&pdev->dev, "failed to duplicate channels\n");
return -ENOMEM;
}
- ret = press_parse_report(pdev, hsdev, channels,
- HID_USAGE_SENSOR_PRESSURE, press_state);
+ ret = press_parse_report(pdev, hsdev,
+ (struct iio_chan_spec *)indio_dev->channels,
+ HID_USAGE_SENSOR_PRESSURE, press_state);
if (ret) {
dev_err(&pdev->dev, "failed to setup attributes\n");
goto error_free_dev_mem;
}
- indio_dev->channels = channels;
indio_dev->num_channels =
ARRAY_SIZE(press_channels);
indio_dev->dev.parent = &pdev->dev;
@@ -360,7 +360,7 @@ static int hid_press_remove(struct platform_device *pdev)
return 0;
}
-static struct platform_device_id hid_press_ids[] = {
+static const struct platform_device_id hid_press_ids[] = {
{
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
.name = "HID-SENSOR-200031",
diff --git a/drivers/iio/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c
index bd26a484a..3d756bd8c 100644
--- a/drivers/iio/proximity/sx9500.c
+++ b/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,34 @@ 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);
+ if (client->irq <= 0) {
+ gpio = devm_gpiod_get_index(dev, SX9500_GPIO_INT, 0, GPIOD_IN);
+ if (IS_ERR(gpio))
+ dev_err(dev, "gpio get irq failed\n");
+ else
+ client->irq = gpiod_to_irq(gpio);
}
- ret = gpiod_to_irq(gpio);
-
- dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
-
- return ret;
+ 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;
+ }
}
static int sx9500_probe(struct i2c_client *client,
@@ -645,14 +905,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 +920,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 +951,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 +985,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 +1036,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 +1044,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,
diff --git a/drivers/iio/temperature/mlx90614.c b/drivers/iio/temperature/mlx90614.c
index a112fc9ab..7a2b639ea 100644
--- a/drivers/iio/temperature/mlx90614.c
+++ b/drivers/iio/temperature/mlx90614.c
@@ -12,12 +12,24 @@
*
* (7-bit I2C slave address 0x5a, 100KHz bus speed only!)
*
- * TODO: sleep mode, configuration EEPROM
+ * To wake up from sleep mode, the SDA line must be held low while SCL is high
+ * for at least 33ms. This is achieved with an extra GPIO that can be connected
+ * directly to the SDA line. In normal operation, the GPIO is set as input and
+ * will not interfere in I2C communication. While the GPIO is driven low, the
+ * i2c adapter is locked since it cannot be used by other clients. The SCL line
+ * always has a pull-up so we do not need an extra GPIO to drive it high. If
+ * the "wakeup" GPIO is not given, power management will be disabled.
+ *
+ * TODO: filter configuration
*/
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/jiffies.h>
+#include <linux/gpio/consumer.h>
+#include <linux/pm_runtime.h>
#include <linux/iio/iio.h>
@@ -51,10 +63,101 @@
#define MLX90614_TIMING_WAKEUP 34 /* time to hold SDA low for wake-up */
#define MLX90614_TIMING_STARTUP 250 /* time before first data after wake-up */
+#define MLX90614_AUTOSLEEP_DELAY 5000 /* default autosleep delay */
+
struct mlx90614_data {
struct i2c_client *client;
+ struct mutex lock; /* for EEPROM access only */
+ struct gpio_desc *wakeup_gpio; /* NULL to disable sleep/wake-up */
+ unsigned long ready_timestamp; /* in jiffies */
};
+/*
+ * Erase an address and write word.
+ * The mutex must be locked before calling.
+ */
+static s32 mlx90614_write_word(const struct i2c_client *client, u8 command,
+ u16 value)
+{
+ /*
+ * Note: The mlx90614 requires a PEC on writing but does not send us a
+ * valid PEC on reading. Hence, we cannot set I2C_CLIENT_PEC in
+ * i2c_client.flags. As a workaround, we use i2c_smbus_xfer here.
+ */
+ union i2c_smbus_data data;
+ s32 ret;
+
+ dev_dbg(&client->dev, "Writing 0x%x to address 0x%x", value, command);
+
+ data.word = 0x0000; /* erase command */
+ ret = i2c_smbus_xfer(client->adapter, client->addr,
+ client->flags | I2C_CLIENT_PEC,
+ I2C_SMBUS_WRITE, command,
+ I2C_SMBUS_WORD_DATA, &data);
+ if (ret < 0)
+ return ret;
+
+ msleep(MLX90614_TIMING_EEPROM);
+
+ data.word = value; /* actual write */
+ ret = i2c_smbus_xfer(client->adapter, client->addr,
+ client->flags | I2C_CLIENT_PEC,
+ I2C_SMBUS_WRITE, command,
+ I2C_SMBUS_WORD_DATA, &data);
+
+ msleep(MLX90614_TIMING_EEPROM);
+
+ return ret;
+}
+
+#ifdef CONFIG_PM
+/*
+ * If @startup is true, make sure MLX90614_TIMING_STARTUP ms have elapsed since
+ * the last wake-up. This is normally only needed to get a valid temperature
+ * reading. EEPROM access does not need such delay.
+ * Return 0 on success, <0 on error.
+ */
+static int mlx90614_power_get(struct mlx90614_data *data, bool startup)
+{
+ unsigned long now;
+
+ if (!data->wakeup_gpio)
+ return 0;
+
+ pm_runtime_get_sync(&data->client->dev);
+
+ if (startup) {
+ now = jiffies;
+ if (time_before(now, data->ready_timestamp) &&
+ msleep_interruptible(jiffies_to_msecs(
+ data->ready_timestamp - now)) != 0) {
+ pm_runtime_put_autosuspend(&data->client->dev);
+ return -EINTR;
+ }
+ }
+
+ return 0;
+}
+
+static void mlx90614_power_put(struct mlx90614_data *data)
+{
+ if (!data->wakeup_gpio)
+ return;
+
+ pm_runtime_mark_last_busy(&data->client->dev);
+ pm_runtime_put_autosuspend(&data->client->dev);
+}
+#else
+static inline int mlx90614_power_get(struct mlx90614_data *data, bool startup)
+{
+ return 0;
+}
+
+static inline void mlx90614_power_put(struct mlx90614_data *data)
+{
+}
+#endif
+
static int mlx90614_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *channel, int *val,
int *val2, long mask)
@@ -85,18 +188,85 @@ static int mlx90614_read_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
+ ret = mlx90614_power_get(data, true);
+ if (ret < 0)
+ return ret;
ret = i2c_smbus_read_word_data(data->client, cmd);
+ mlx90614_power_put(data);
+
if (ret < 0)
return ret;
+
+ /* MSB is an error flag */
+ if (ret & 0x8000)
+ return -EIO;
+
*val = ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_OFFSET:
- *val = 13657;
+ *val = -13657;
*val2 = 500000;
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_SCALE:
*val = 20;
return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBEMISSIVITY: /* 1/65535 / LSB */
+ mlx90614_power_get(data, false);
+ mutex_lock(&data->lock);
+ ret = i2c_smbus_read_word_data(data->client,
+ MLX90614_EMISSIVITY);
+ mutex_unlock(&data->lock);
+ mlx90614_power_put(data);
+
+ if (ret < 0)
+ return ret;
+
+ if (ret == 65535) {
+ *val = 1;
+ *val2 = 0;
+ } else {
+ *val = 0;
+ *val2 = ret * 15259; /* 1/65535 ~ 0.000015259 */
+ }
+ return IIO_VAL_INT_PLUS_NANO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mlx90614_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *channel, int val,
+ int val2, long mask)
+{
+ struct mlx90614_data *data = iio_priv(indio_dev);
+ s32 ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_CALIBEMISSIVITY: /* 1/65535 / LSB */
+ if (val < 0 || val2 < 0 || val > 1 || (val == 1 && val2 != 0))
+ return -EINVAL;
+ val = val * 65535 + val2 / 15259; /* 1/65535 ~ 0.000015259 */
+
+ mlx90614_power_get(data, false);
+ mutex_lock(&data->lock);
+ ret = mlx90614_write_word(data->client, MLX90614_EMISSIVITY,
+ val);
+ mutex_unlock(&data->lock);
+ mlx90614_power_put(data);
+
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mlx90614_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *channel,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_CALIBEMISSIVITY:
+ return IIO_VAL_INT_PLUS_NANO;
default:
return -EINVAL;
}
@@ -115,7 +285,8 @@ static const struct iio_chan_spec mlx90614_channels[] = {
.type = IIO_TEMP,
.modified = 1,
.channel2 = IIO_MOD_TEMP_OBJECT,
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_CALIBEMISSIVITY),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE),
},
@@ -125,7 +296,8 @@ static const struct iio_chan_spec mlx90614_channels[] = {
.modified = 1,
.channel = 1,
.channel2 = IIO_MOD_TEMP_OBJECT,
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_CALIBEMISSIVITY),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE),
},
@@ -133,9 +305,103 @@ static const struct iio_chan_spec mlx90614_channels[] = {
static const struct iio_info mlx90614_info = {
.read_raw = mlx90614_read_raw,
+ .write_raw = mlx90614_write_raw,
+ .write_raw_get_fmt = mlx90614_write_raw_get_fmt,
.driver_module = THIS_MODULE,
};
+#ifdef CONFIG_PM
+static int mlx90614_sleep(struct mlx90614_data *data)
+{
+ s32 ret;
+
+ if (!data->wakeup_gpio) {
+ dev_dbg(&data->client->dev, "Sleep disabled");
+ return -ENOSYS;
+ }
+
+ dev_dbg(&data->client->dev, "Requesting sleep");
+
+ mutex_lock(&data->lock);
+ ret = i2c_smbus_xfer(data->client->adapter, data->client->addr,
+ data->client->flags | I2C_CLIENT_PEC,
+ I2C_SMBUS_WRITE, MLX90614_OP_SLEEP,
+ I2C_SMBUS_BYTE, NULL);
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static int mlx90614_wakeup(struct mlx90614_data *data)
+{
+ if (!data->wakeup_gpio) {
+ dev_dbg(&data->client->dev, "Wake-up disabled");
+ return -ENOSYS;
+ }
+
+ dev_dbg(&data->client->dev, "Requesting wake-up");
+
+ i2c_lock_adapter(data->client->adapter);
+ gpiod_direction_output(data->wakeup_gpio, 0);
+ msleep(MLX90614_TIMING_WAKEUP);
+ gpiod_direction_input(data->wakeup_gpio);
+ i2c_unlock_adapter(data->client->adapter);
+
+ data->ready_timestamp = jiffies +
+ msecs_to_jiffies(MLX90614_TIMING_STARTUP);
+
+ /*
+ * Quirk: the i2c controller may get confused right after the
+ * wake-up signal has been sent. As a workaround, do a dummy read.
+ * If the read fails, the controller will probably be reset so that
+ * further reads will work.
+ */
+ i2c_smbus_read_word_data(data->client, MLX90614_CONFIG);
+
+ return 0;
+}
+
+/* Return wake-up GPIO or NULL if sleep functionality should be disabled. */
+static struct gpio_desc *mlx90614_probe_wakeup(struct i2c_client *client)
+{
+ struct gpio_desc *gpio;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_WRITE_BYTE)) {
+ dev_info(&client->dev,
+ "i2c adapter does not support SMBUS_WRITE_BYTE, sleep disabled");
+ return NULL;
+ }
+
+ gpio = devm_gpiod_get_optional(&client->dev, "wakeup", GPIOD_IN);
+
+ if (IS_ERR(gpio)) {
+ dev_warn(&client->dev,
+ "gpio acquisition failed with error %ld, sleep disabled",
+ PTR_ERR(gpio));
+ return NULL;
+ } else if (!gpio) {
+ dev_info(&client->dev,
+ "wakeup-gpio not found, sleep disabled");
+ }
+
+ return gpio;
+}
+#else
+static inline int mlx90614_sleep(struct mlx90614_data *data)
+{
+ return -ENOSYS;
+}
+static inline int mlx90614_wakeup(struct mlx90614_data *data)
+{
+ return -ENOSYS;
+}
+static inline struct gpio_desc *mlx90614_probe_wakeup(struct i2c_client *client)
+{
+ return NULL;
+}
+#endif
+
/* Return 0 for single sensor, 1 for dual sensor, <0 on error. */
static int mlx90614_probe_num_ir_sensors(struct i2c_client *client)
{
@@ -166,6 +432,10 @@ static int mlx90614_probe(struct i2c_client *client,
data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
data->client = client;
+ mutex_init(&data->lock);
+ data->wakeup_gpio = mlx90614_probe_wakeup(client);
+
+ mlx90614_wakeup(data);
indio_dev->dev.parent = &client->dev;
indio_dev->name = id->name;
@@ -188,12 +458,30 @@ static int mlx90614_probe(struct i2c_client *client,
return ret;
}
+ if (data->wakeup_gpio) {
+ pm_runtime_set_autosuspend_delay(&client->dev,
+ MLX90614_AUTOSLEEP_DELAY);
+ pm_runtime_use_autosuspend(&client->dev);
+ pm_runtime_set_active(&client->dev);
+ pm_runtime_enable(&client->dev);
+ }
+
return iio_device_register(indio_dev);
}
static int mlx90614_remove(struct i2c_client *client)
{
- iio_device_unregister(i2c_get_clientdata(client));
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct mlx90614_data *data = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ if (data->wakeup_gpio) {
+ pm_runtime_disable(&client->dev);
+ if (!pm_runtime_status_suspended(&client->dev))
+ mlx90614_sleep(data);
+ pm_runtime_set_suspended(&client->dev);
+ }
return 0;
}
@@ -204,10 +492,67 @@ static const struct i2c_device_id mlx90614_id[] = {
};
MODULE_DEVICE_TABLE(i2c, mlx90614_id);
+#ifdef CONFIG_PM_SLEEP
+static int mlx90614_pm_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct mlx90614_data *data = iio_priv(indio_dev);
+
+ if (data->wakeup_gpio && pm_runtime_active(dev))
+ return mlx90614_sleep(data);
+
+ return 0;
+}
+
+static int mlx90614_pm_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct mlx90614_data *data = iio_priv(indio_dev);
+ int err;
+
+ if (data->wakeup_gpio) {
+ err = mlx90614_wakeup(data);
+ if (err < 0)
+ return err;
+
+ pm_runtime_disable(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ }
+
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_PM
+static int mlx90614_pm_runtime_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct mlx90614_data *data = iio_priv(indio_dev);
+
+ return mlx90614_sleep(data);
+}
+
+static int mlx90614_pm_runtime_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct mlx90614_data *data = iio_priv(indio_dev);
+
+ return mlx90614_wakeup(data);
+}
+#endif
+
+static const struct dev_pm_ops mlx90614_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(mlx90614_pm_suspend, mlx90614_pm_resume)
+ SET_RUNTIME_PM_OPS(mlx90614_pm_runtime_suspend,
+ mlx90614_pm_runtime_resume, NULL)
+};
+
static struct i2c_driver mlx90614_driver = {
.driver = {
.name = "mlx90614",
.owner = THIS_MODULE,
+ .pm = &mlx90614_pm_ops,
},
.probe = mlx90614_probe,
.remove = mlx90614_remove,
diff --git a/drivers/iio/temperature/tmp006.c b/drivers/iio/temperature/tmp006.c
index 7a8050996..8f21f32f9 100644
--- a/drivers/iio/temperature/tmp006.c
+++ b/drivers/iio/temperature/tmp006.c
@@ -41,8 +41,8 @@
#define TMP006_CONFIG_CR_MASK 0x0e00
#define TMP006_CONFIG_CR_SHIFT 9
-#define MANUFACTURER_MAGIC 0x5449
-#define DEVICE_MAGIC 0x0067
+#define TMP006_MANUFACTURER_MAGIC 0x5449
+#define TMP006_DEVICE_MAGIC 0x0067
struct tmp006_data {
struct i2c_client *client;
@@ -194,7 +194,7 @@ static bool tmp006_check_identification(struct i2c_client *client)
if (did < 0)
return false;
- return mid == MANUFACTURER_MAGIC && did == DEVICE_MAGIC;
+ return mid == TMP006_MANUFACTURER_MAGIC && did == TMP006_DEVICE_MAGIC;
}
static int tmp006_probe(struct i2c_client *client,