diff options
author | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2015-09-08 01:01:14 -0300 |
---|---|---|
committer | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2015-09-08 01:01:14 -0300 |
commit | e5fd91f1ef340da553f7a79da9540c3db711c937 (patch) | |
tree | b11842027dc6641da63f4bcc524f8678263304a3 /drivers/iio | |
parent | 2a9b0348e685a63d97486f6749622b61e9e3292f (diff) |
Linux-libre 4.2-gnu
Diffstat (limited to 'drivers/iio')
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 = <r501_attribute_group, + .driver_module = THIS_MODULE, +}; + static const struct iio_info ltr501_info = { .read_raw = ltr501_read_raw, .write_raw = ltr501_write_raw, .attrs = <r501_attribute_group, + .read_event_value = <r501_read_event, + .write_event_value = <r501_write_event, + .read_event_config = <r501_read_event_config, + .write_event_config = <r501_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 = <r301_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 = <r301_attribute_group, + .read_event_value = <r501_read_event, + .write_event_value = <r501_write_event, + .read_event_config = <r501_read_event_config, + .write_event_config = <r501_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 = <r501_info, + .info_no_irq = <r501_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 = <r501_info, + .info_no_irq = <r501_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 = <r301_info, + .info_no_irq = <r301_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, <r501_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 = <r501_chip_info_tbl[chip_idx]; + + if ((partid >> 4) != data->chip_info->partid) return -ENODEV; indio_dev->dev.parent = &client->dev; - indio_dev->info = <r501_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 = <r501_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, ®_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, + ®_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, + ®_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, ®_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, + ®_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, ®); + 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, |