From 866a1389174bbb71591bb0c927f1d63e7cc469c8 Mon Sep 17 00:00:00 2001 From: Gerald Loacker Date: Thu, 1 Dec 2022 08:22:20 +0100 Subject: iio: magnetometer: add ti tmag5273 driver Add support for TI TMAG5273 Low-Power Linear 3D Hall-Effect Sensor. Additionally to temperature and magnetic X, Y and Z-axes the angle and magnitude are reported. The sensor is operating in continuous measurement mode and changes to sleep mode if not used for 5 seconds. Datasheet: https://www.ti.com/lit/gpn/tmag5273 Signed-off-by: Gerald Loacker Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20221201072220.402585-4-gerald.loacker@wolfvision.net Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/Kconfig | 12 + drivers/iio/magnetometer/Makefile | 2 + drivers/iio/magnetometer/tmag5273.c | 743 ++++++++++++++++++++++++++++++++++++ 3 files changed, 757 insertions(+) create mode 100644 drivers/iio/magnetometer/tmag5273.c (limited to 'drivers/iio') diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig index b91fc5e6a26e..467819335588 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -208,6 +208,18 @@ config SENSORS_RM3100_SPI To compile this driver as a module, choose M here: the module will be called rm3100-spi. +config TI_TMAG5273 + tristate "TI TMAG5273 Low-Power Linear 3D Hall-Effect Sensor" + depends on I2C + select REGMAP_I2C + help + Say Y here to add support for the TI TMAG5273 Low-Power + Linear 3D Hall-Effect Sensor. + + This driver can also be compiled as a module. + To compile this driver as a module, choose M here: the module + will be called tmag5273. + config YAMAHA_YAS530 tristate "Yamaha YAS530 family of 3-Axis Magnetometers (I2C)" depends on I2C diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile index b9f45b7fafc3..b1c784ea71c8 100644 --- a/drivers/iio/magnetometer/Makefile +++ b/drivers/iio/magnetometer/Makefile @@ -29,4 +29,6 @@ obj-$(CONFIG_SENSORS_RM3100) += rm3100-core.o obj-$(CONFIG_SENSORS_RM3100_I2C) += rm3100-i2c.o obj-$(CONFIG_SENSORS_RM3100_SPI) += rm3100-spi.o +obj-$(CONFIG_TI_TMAG5273) += tmag5273.o + obj-$(CONFIG_YAMAHA_YAS530) += yamaha-yas530.o diff --git a/drivers/iio/magnetometer/tmag5273.c b/drivers/iio/magnetometer/tmag5273.c new file mode 100644 index 000000000000..28bb7efe8df8 --- /dev/null +++ b/drivers/iio/magnetometer/tmag5273.c @@ -0,0 +1,743 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for the TI TMAG5273 Low-Power Linear 3D Hall-Effect Sensor + * + * Copyright (C) 2022 WolfVision GmbH + * + * Author: Gerald Loacker + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define TMAG5273_DEVICE_CONFIG_1 0x00 +#define TMAG5273_DEVICE_CONFIG_2 0x01 +#define TMAG5273_SENSOR_CONFIG_1 0x02 +#define TMAG5273_SENSOR_CONFIG_2 0x03 +#define TMAG5273_X_THR_CONFIG 0x04 +#define TMAG5273_Y_THR_CONFIG 0x05 +#define TMAG5273_Z_THR_CONFIG 0x06 +#define TMAG5273_T_CONFIG 0x07 +#define TMAG5273_INT_CONFIG_1 0x08 +#define TMAG5273_MAG_GAIN_CONFIG 0x09 +#define TMAG5273_MAG_OFFSET_CONFIG_1 0x0A +#define TMAG5273_MAG_OFFSET_CONFIG_2 0x0B +#define TMAG5273_I2C_ADDRESS 0x0C +#define TMAG5273_DEVICE_ID 0x0D +#define TMAG5273_MANUFACTURER_ID_LSB 0x0E +#define TMAG5273_MANUFACTURER_ID_MSB 0x0F +#define TMAG5273_T_MSB_RESULT 0x10 +#define TMAG5273_T_LSB_RESULT 0x11 +#define TMAG5273_X_MSB_RESULT 0x12 +#define TMAG5273_X_LSB_RESULT 0x13 +#define TMAG5273_Y_MSB_RESULT 0x14 +#define TMAG5273_Y_LSB_RESULT 0x15 +#define TMAG5273_Z_MSB_RESULT 0x16 +#define TMAG5273_Z_LSB_RESULT 0x17 +#define TMAG5273_CONV_STATUS 0x18 +#define TMAG5273_ANGLE_RESULT_MSB 0x19 +#define TMAG5273_ANGLE_RESULT_LSB 0x1A +#define TMAG5273_MAGNITUDE_RESULT 0x1B +#define TMAG5273_DEVICE_STATUS 0x1C +#define TMAG5273_MAX_REG TMAG5273_DEVICE_STATUS + +#define TMAG5273_AUTOSLEEP_DELAY_MS 5000 +#define TMAG5273_MAX_AVERAGE 32 + +/* + * bits in the TMAG5273_MANUFACTURER_ID_LSB / MSB register + * 16-bit unique manufacturer ID 0x49 / 0x54 = "TI" + */ +#define TMAG5273_MANUFACTURER_ID 0x5449 + +/* bits in the TMAG5273_DEVICE_CONFIG_1 register */ +#define TMAG5273_AVG_MODE_MASK GENMASK(4, 2) +#define TMAG5273_AVG_1_MODE FIELD_PREP(TMAG5273_AVG_MODE_MASK, 0) +#define TMAG5273_AVG_2_MODE FIELD_PREP(TMAG5273_AVG_MODE_MASK, 1) +#define TMAG5273_AVG_4_MODE FIELD_PREP(TMAG5273_AVG_MODE_MASK, 2) +#define TMAG5273_AVG_8_MODE FIELD_PREP(TMAG5273_AVG_MODE_MASK, 3) +#define TMAG5273_AVG_16_MODE FIELD_PREP(TMAG5273_AVG_MODE_MASK, 4) +#define TMAG5273_AVG_32_MODE FIELD_PREP(TMAG5273_AVG_MODE_MASK, 5) + +/* bits in the TMAG5273_DEVICE_CONFIG_2 register */ +#define TMAG5273_OP_MODE_MASK GENMASK(1, 0) +#define TMAG5273_OP_MODE_STANDBY FIELD_PREP(TMAG5273_OP_MODE_MASK, 0) +#define TMAG5273_OP_MODE_SLEEP FIELD_PREP(TMAG5273_OP_MODE_MASK, 1) +#define TMAG5273_OP_MODE_CONT FIELD_PREP(TMAG5273_OP_MODE_MASK, 2) +#define TMAG5273_OP_MODE_WAKEUP FIELD_PREP(TMAG5273_OP_MODE_MASK, 3) + +/* bits in the TMAG5273_SENSOR_CONFIG_1 register */ +#define TMAG5273_MAG_CH_EN_MASK GENMASK(7, 4) +#define TMAG5273_MAG_CH_EN_X_Y_Z 7 + +/* bits in the TMAG5273_SENSOR_CONFIG_2 register */ +#define TMAG5273_Z_RANGE_MASK BIT(0) +#define TMAG5273_X_Y_RANGE_MASK BIT(1) +#define TMAG5273_ANGLE_EN_MASK GENMASK(3, 2) +#define TMAG5273_ANGLE_EN_OFF 0 +#define TMAG5273_ANGLE_EN_X_Y 1 +#define TMAG5273_ANGLE_EN_Y_Z 2 +#define TMAG5273_ANGLE_EN_X_Z 3 + +/* bits in the TMAG5273_T_CONFIG register */ +#define TMAG5273_T_CH_EN BIT(0) + +/* bits in the TMAG5273_DEVICE_ID register */ +#define TMAG5273_VERSION_MASK GENMASK(1, 0) + +/* bits in the TMAG5273_CONV_STATUS register */ +#define TMAG5273_CONV_STATUS_COMPLETE BIT(0) + +enum tmag5273_channels { + TEMPERATURE = 0, + AXIS_X, + AXIS_Y, + AXIS_Z, + ANGLE, + MAGNITUDE, +}; + +enum tmag5273_scale_index { + MAGN_RANGE_LOW = 0, + MAGN_RANGE_HIGH, + MAGN_RANGE_NUM +}; + +/* state container for the TMAG5273 driver */ +struct tmag5273_data { + struct device *dev; + unsigned int devid; + unsigned int version; + char name[16]; + unsigned int conv_avg; + unsigned int scale; + enum tmag5273_scale_index scale_index; + unsigned int angle_measurement; + struct regmap *map; + struct regulator *vcc; + + /* + * Locks the sensor for exclusive use during a measurement (which + * involves several register transactions so the regmap lock is not + * enough) so that measurements get serialized in a + * first-come-first-serve manner. + */ + struct mutex lock; +}; + +static const char *const tmag5273_angle_names[] = { "off", "x-y", "y-z", "x-z" }; + +/* + * Averaging enables additional sampling of the sensor data to reduce the noise + * effect, but also increases conversion time. + */ +static const unsigned int tmag5273_avg_table[] = { + 1, 2, 4, 8, 16, 32, +}; + +/* + * Magnetic resolution in Gauss for different TMAG5273 versions. + * Scale[Gauss] = Range[mT] * 1000 / 2^15 * 10, (1 mT = 10 Gauss) + * Only version 1 and 2 are valid, version 0 and 3 are reserved. + */ +static const struct iio_val_int_plus_micro tmag5273_scale[][MAGN_RANGE_NUM] = { + { { 0, 0 }, { 0, 0 } }, + { { 0, 12200 }, { 0, 24400 } }, + { { 0, 40600 }, { 0, 81200 } }, + { { 0, 0 }, { 0, 0 } }, +}; + +static int tmag5273_get_measure(struct tmag5273_data *data, s16 *t, s16 *x, + s16 *y, s16 *z, u16 *angle, u16 *magnitude) +{ + unsigned int status, val; + __be16 reg_data[4]; + int ret; + + mutex_lock(&data->lock); + + /* + * Max. conversion time is 2425 us in 32x averaging mode for all three + * channels. Since we are in continuous measurement mode, a measurement + * may already be there, so poll for completed measurement with + * timeout. + */ + ret = regmap_read_poll_timeout(data->map, TMAG5273_CONV_STATUS, status, + status & TMAG5273_CONV_STATUS_COMPLETE, + 100, 10000); + if (ret) { + dev_err(data->dev, "timeout waiting for measurement\n"); + goto out_unlock; + } + + ret = regmap_bulk_read(data->map, TMAG5273_T_MSB_RESULT, reg_data, + sizeof(reg_data)); + if (ret) + goto out_unlock; + *t = be16_to_cpu(reg_data[0]); + *x = be16_to_cpu(reg_data[1]); + *y = be16_to_cpu(reg_data[2]); + *z = be16_to_cpu(reg_data[3]); + + ret = regmap_bulk_read(data->map, TMAG5273_ANGLE_RESULT_MSB, + ®_data[0], sizeof(reg_data[0])); + if (ret) + goto out_unlock; + /* + * angle has 9 bits integer value and 4 bits fractional part + * 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + * 0 0 0 a a a a a a a a a f f f f + */ + *angle = be16_to_cpu(reg_data[0]); + + ret = regmap_read(data->map, TMAG5273_MAGNITUDE_RESULT, &val); + if (ret < 0) + goto out_unlock; + *magnitude = val; + +out_unlock: + mutex_unlock(&data->lock); + return ret; +} + +static int tmag5273_write_osr(struct tmag5273_data *data, int val) +{ + int i; + + if (val == data->conv_avg) + return 0; + + for (i = 0; i < ARRAY_SIZE(tmag5273_avg_table); i++) { + if (tmag5273_avg_table[i] == val) + break; + } + if (i == ARRAY_SIZE(tmag5273_avg_table)) + return -EINVAL; + data->conv_avg = val; + + return regmap_update_bits(data->map, TMAG5273_DEVICE_CONFIG_1, + TMAG5273_AVG_MODE_MASK, + FIELD_PREP(TMAG5273_AVG_MODE_MASK, i)); +} + +static int tmag5273_write_scale(struct tmag5273_data *data, int scale_micro) +{ + u32 value; + int i; + + for (i = 0; i < ARRAY_SIZE(tmag5273_scale[0]); i++) { + if (tmag5273_scale[data->version][i].micro == scale_micro) + break; + } + if (i == ARRAY_SIZE(tmag5273_scale[0])) + return -EINVAL; + data->scale_index = i; + + if (data->scale_index == MAGN_RANGE_LOW) + value = 0; + else + value = TMAG5273_Z_RANGE_MASK | TMAG5273_X_Y_RANGE_MASK; + + return regmap_update_bits(data->map, TMAG5273_SENSOR_CONFIG_2, + TMAG5273_Z_RANGE_MASK | TMAG5273_X_Y_RANGE_MASK, value); +} + +static int tmag5273_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct tmag5273_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals = tmag5273_avg_table; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(tmag5273_avg_table); + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_MAGN: + *type = IIO_VAL_INT_PLUS_MICRO; + *vals = (int *)tmag5273_scale[data->version]; + *length = ARRAY_SIZE(tmag5273_scale[data->version]) * + MAGN_RANGE_NUM; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int tmag5273_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *val, + int *val2, long mask) +{ + struct tmag5273_data *data = iio_priv(indio_dev); + s16 t, x, y, z; + u16 angle, magnitude; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + case IIO_CHAN_INFO_RAW: + ret = pm_runtime_resume_and_get(data->dev); + if (ret < 0) + return ret; + + ret = tmag5273_get_measure(data, &t, &x, &y, &z, &angle, &magnitude); + if (ret) + return ret; + + pm_runtime_mark_last_busy(data->dev); + pm_runtime_put_autosuspend(data->dev); + + switch (chan->address) { + case TEMPERATURE: + *val = t; + return IIO_VAL_INT; + case AXIS_X: + *val = x; + return IIO_VAL_INT; + case AXIS_Y: + *val = y; + return IIO_VAL_INT; + case AXIS_Z: + *val = z; + return IIO_VAL_INT; + case ANGLE: + *val = angle; + return IIO_VAL_INT; + case MAGNITUDE: + *val = magnitude; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_TEMP: + /* + * Convert device specific value to millicelsius. + * Resolution from the sensor is 60.1 LSB/celsius and + * the reference value at 25 celsius is 17508 LSBs. + */ + *val = 10000; + *val2 = 601; + return IIO_VAL_FRACTIONAL; + case IIO_MAGN: + /* Magnetic resolution in uT */ + *val = 0; + *val2 = tmag5273_scale[data->version] + [data->scale_index].micro; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_ANGL: + /* + * Angle is in degrees and has four fractional bits, + * therefore use 1/16 * pi/180 to convert to radians. + */ + *val = 1000; + *val2 = 916732; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + switch (chan->type) { + case IIO_TEMP: + *val = -266314; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *val = data->conv_avg; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int tmag5273_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct tmag5273_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + return tmag5273_write_osr(data, val); + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_MAGN: + if (val) + return -EINVAL; + return tmag5273_write_scale(data, val2); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +#define TMAG5273_AXIS_CHANNEL(axis, index) \ + { \ + .type = IIO_MAGN, \ + .modified = 1, \ + .channel2 = IIO_MOD_##axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .address = index, \ + .scan_index = index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + }, \ + } + +static const struct iio_chan_spec tmag5273_channels[] = { + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .address = TEMPERATURE, + .scan_index = TEMPERATURE, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, + }, + TMAG5273_AXIS_CHANNEL(X, AXIS_X), + TMAG5273_AXIS_CHANNEL(Y, AXIS_Y), + TMAG5273_AXIS_CHANNEL(Z, AXIS_Z), + { + .type = IIO_ANGL, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all_available = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .address = ANGLE, + .scan_index = ANGLE, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, + }, + { + .type = IIO_DISTANCE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_all = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all_available = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .address = MAGNITUDE, + .scan_index = MAGNITUDE, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(6), +}; + +static const struct iio_info tmag5273_info = { + .read_avail = tmag5273_read_avail, + .read_raw = tmag5273_read_raw, + .write_raw = tmag5273_write_raw, +}; + +static bool tmag5273_volatile_reg(struct device *dev, unsigned int reg) +{ + return reg >= TMAG5273_T_MSB_RESULT && reg <= TMAG5273_MAGNITUDE_RESULT; +} + +static const struct regmap_config tmag5273_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = TMAG5273_MAX_REG, + .volatile_reg = tmag5273_volatile_reg, +}; + +static int tmag5273_set_operating_mode(struct tmag5273_data *data, + unsigned int val) +{ + return regmap_write(data->map, TMAG5273_DEVICE_CONFIG_2, val); +} + +static void tmag5273_read_device_property(struct tmag5273_data *data) +{ + struct device *dev = data->dev; + const char *str; + int ret; + + data->angle_measurement = TMAG5273_ANGLE_EN_X_Y; + + ret = device_property_read_string(dev, "ti,angle-measurement", &str); + if (ret) + return; + + ret = match_string(tmag5273_angle_names, + ARRAY_SIZE(tmag5273_angle_names), str); + if (ret >= 0) + data->angle_measurement = ret; +} + +static void tmag5273_wake_up(struct tmag5273_data *data) +{ + int val; + + /* Wake up the chip by sending a dummy I2C command */ + regmap_read(data->map, TMAG5273_DEVICE_ID, &val); + /* + * Time to go to stand-by mode from sleep mode is 50us + * typically, during this time no I2C access is possible. + */ + usleep_range(80, 200); +} + +static int tmag5273_chip_init(struct tmag5273_data *data) +{ + int ret; + + ret = regmap_write(data->map, TMAG5273_DEVICE_CONFIG_1, + TMAG5273_AVG_32_MODE); + if (ret) + return ret; + data->conv_avg = 32; + + ret = regmap_write(data->map, TMAG5273_DEVICE_CONFIG_2, + TMAG5273_OP_MODE_CONT); + if (ret) + return ret; + + ret = regmap_write(data->map, TMAG5273_SENSOR_CONFIG_1, + FIELD_PREP(TMAG5273_MAG_CH_EN_MASK, + TMAG5273_MAG_CH_EN_X_Y_Z)); + if (ret) + return ret; + + ret = regmap_write(data->map, TMAG5273_SENSOR_CONFIG_2, + FIELD_PREP(TMAG5273_ANGLE_EN_MASK, + data->angle_measurement)); + if (ret) + return ret; + data->scale_index = MAGN_RANGE_LOW; + + return regmap_write(data->map, TMAG5273_T_CONFIG, TMAG5273_T_CH_EN); +} + +static int tmag5273_check_device_id(struct tmag5273_data *data) +{ + __le16 devid; + int val, ret; + + ret = regmap_read(data->map, TMAG5273_DEVICE_ID, &val); + if (ret) + return dev_err_probe(data->dev, ret, "failed to power on device\n"); + data->version = FIELD_PREP(TMAG5273_VERSION_MASK, val); + + ret = regmap_bulk_read(data->map, TMAG5273_MANUFACTURER_ID_LSB, &devid, + sizeof(devid)); + if (ret) + return dev_err_probe(data->dev, ret, "failed to read device ID\n"); + data->devid = le16_to_cpu(devid); + + switch (data->devid) { + case TMAG5273_MANUFACTURER_ID: + /* + * The device name matches the orderable part number. 'x' stands + * for A, B, C or D devices, which have different I2C addresses. + * Versions 1 or 2 (0 and 3 is reserved) stands for different + * magnetic strengths. + */ + snprintf(data->name, sizeof(data->name), "tmag5273x%1u", data->version); + if (data->version < 1 || data->version > 2) + dev_warn(data->dev, "Unsupported device %s\n", data->name); + return 0; + default: + /* + * Only print warning in case of unknown device ID to allow + * fallback compatible in device tree. + */ + dev_warn(data->dev, "Unknown device ID 0x%x\n", data->devid); + return 0; + } +} + +static void tmag5273_power_down(void *data) +{ + tmag5273_set_operating_mode(data, TMAG5273_OP_MODE_SLEEP); +} + +static int tmag5273_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct tmag5273_data *data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->dev = dev; + i2c_set_clientdata(i2c, indio_dev); + + data->map = devm_regmap_init_i2c(i2c, &tmag5273_regmap_config); + if (IS_ERR(data->map)) + return dev_err_probe(dev, PTR_ERR(data->map), + "failed to allocate register map\n"); + + mutex_init(&data->lock); + + ret = devm_regulator_get_enable(dev, "vcc"); + if (ret) + return dev_err_probe(dev, ret, "failed to enable regulator\n"); + + tmag5273_wake_up(data); + + ret = tmag5273_check_device_id(data); + if (ret) + return ret; + + ret = tmag5273_set_operating_mode(data, TMAG5273_OP_MODE_CONT); + if (ret) + return dev_err_probe(dev, ret, "failed to power on device\n"); + + /* + * Register powerdown deferred callback which suspends the chip + * after module unloaded. + * + * TMAG5273 should be in SUSPEND mode in the two cases: + * 1) When driver is loaded, but we do not have any data or + * configuration requests to it (we are solving it using + * autosuspend feature). + * 2) When driver is unloaded and device is not used (devm action is + * used in this case). + */ + ret = devm_add_action_or_reset(dev, tmag5273_power_down, data); + if (ret) + return dev_err_probe(dev, ret, "failed to add powerdown action\n"); + + ret = pm_runtime_set_active(dev); + if (ret < 0) + return ret; + + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + pm_runtime_get_noresume(dev); + pm_runtime_set_autosuspend_delay(dev, TMAG5273_AUTOSLEEP_DELAY_MS); + pm_runtime_use_autosuspend(dev); + + tmag5273_read_device_property(data); + + ret = tmag5273_chip_init(data); + if (ret) + return dev_err_probe(dev, ret, "failed to init device\n"); + + indio_dev->info = &tmag5273_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->name = data->name; + indio_dev->channels = tmag5273_channels; + indio_dev->num_channels = ARRAY_SIZE(tmag5273_channels); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return dev_err_probe(dev, ret, "device register failed\n"); + + return 0; +} + +static int tmag5273_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct tmag5273_data *data = iio_priv(indio_dev); + int ret; + + ret = tmag5273_set_operating_mode(data, TMAG5273_OP_MODE_SLEEP); + if (ret) + dev_err(dev, "failed to power off device (%pe)\n", ERR_PTR(ret)); + + return ret; +} + +static int tmag5273_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct tmag5273_data *data = iio_priv(indio_dev); + int ret; + + tmag5273_wake_up(data); + + ret = tmag5273_set_operating_mode(data, TMAG5273_OP_MODE_CONT); + if (ret) + dev_err(dev, "failed to power on device (%pe)\n", ERR_PTR(ret)); + + return ret; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(tmag5273_pm_ops, + tmag5273_runtime_suspend, tmag5273_runtime_resume, + NULL); + +static const struct i2c_device_id tmag5273_id[] = { + { "tmag5273" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, tmag5273_id); + +static const struct of_device_id tmag5273_of_match[] = { + { .compatible = "ti,tmag5273" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, tmag5273_of_match); + +static struct i2c_driver tmag5273_driver = { + .driver = { + .name = "tmag5273", + .of_match_table = tmag5273_of_match, + .pm = pm_ptr(&tmag5273_pm_ops), + }, + .probe_new = tmag5273_probe, + .id_table = tmag5273_id, +}; +module_i2c_driver(tmag5273_driver); + +MODULE_DESCRIPTION("TI TMAG5273 Low-Power Linear 3D Hall-Effect Sensor driver"); +MODULE_AUTHOR("Gerald Loacker "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 627198942641dae28024ad686066311f1aeedcf2 Mon Sep 17 00:00:00 2001 From: Leonard Göhrs Date: Mon, 28 Nov 2022 14:35:03 +0100 Subject: iio: adc: add ADC driver for the TI LMP92064 controller MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TI LMP92064 is a dual 12 Bit ADC connected via SPI. The two channels are intended for simultaneous measurements of the voltage across- and current through a load to allow accurate instantaneous power measurements. The driver does not yet take advantage of this feature, as buffering is not yet implemented. Signed-off-by: Leonard Göhrs Link: https://lore.kernel.org/r/20221128133503.1355898-2-l.goehrs@pengutronix.de Signed-off-by: Jonathan Cameron --- MAINTAINERS | 8 + drivers/iio/adc/Kconfig | 10 ++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ti-lmp92064.c | 332 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 351 insertions(+) create mode 100644 drivers/iio/adc/ti-lmp92064.c (limited to 'drivers/iio') diff --git a/MAINTAINERS b/MAINTAINERS index 86d8fac2495e..1b99152f6171 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20894,6 +20894,14 @@ S: Maintained F: sound/soc/codecs/isabelle* F: sound/soc/codecs/lm49453* +TI LMP92064 ADC DRIVER +M: Leonard Göhrs +R: kernel@pengutronix.de +L: linux-iio@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/iio/adc/ti,lmp92064.yaml +F: drivers/iio/adc/ti-lmp92064.c + TI PCM3060 ASoC CODEC DRIVER M: Kirill Marinushkin L: alsa-devel@alsa-project.org (moderated for non-subscribers) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 63f80d747cbd..46c4fc2fc534 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -1274,6 +1274,16 @@ config TI_AM335X_ADC To compile this driver as a module, choose M here: the module will be called ti_am335x_adc. +config TI_LMP92064 + tristate "Texas Instruments LMP92064 ADC driver" + depends on SPI + help + Say yes here to build support for the LMP92064 Precision Current and Voltage + sensor. + + This driver can also be built as a module. If so, the module will be called + ti-lmp92064. + config TI_TLC4541 tristate "Texas Instruments TLC4541 ADC driver" depends on SPI diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 4ef41a7dfac6..6e08415c3f3a 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -113,6 +113,7 @@ obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o obj-$(CONFIG_TI_ADS131E08) += ti-ads131e08.o obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o +obj-$(CONFIG_TI_LMP92064) += ti-lmp92064.o obj-$(CONFIG_TI_TLC4541) += ti-tlc4541.o obj-$(CONFIG_TI_TSC2046) += ti-tsc2046.o obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o diff --git a/drivers/iio/adc/ti-lmp92064.c b/drivers/iio/adc/ti-lmp92064.c new file mode 100644 index 000000000000..c30ed824924f --- /dev/null +++ b/drivers/iio/adc/ti-lmp92064.c @@ -0,0 +1,332 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Texas Instruments LMP92064 SPI ADC driver + * + * Copyright (c) 2022 Leonard Göhrs , Pengutronix + * + * Based on linux/drivers/iio/adc/ti-tsc2046.c + * Copyright (c) 2021 Oleksij Rempel , Pengutronix + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#define TI_LMP92064_REG_CONFIG_A 0x0000 +#define TI_LMP92064_REG_CONFIG_B 0x0001 +#define TI_LMP92064_REG_CHIP_REV 0x0006 + +#define TI_LMP92064_REG_MFR_ID1 0x000C +#define TI_LMP92064_REG_MFR_ID2 0x000D + +#define TI_LMP92064_REG_REG_UPDATE 0x000F +#define TI_LMP92064_REG_CONFIG_REG 0x0100 +#define TI_LMP92064_REG_STATUS 0x0103 + +#define TI_LMP92064_REG_DATA_VOUT_LSB 0x0200 +#define TI_LMP92064_REG_DATA_VOUT_MSB 0x0201 +#define TI_LMP92064_REG_DATA_COUT_LSB 0x0202 +#define TI_LMP92064_REG_DATA_COUT_MSB 0x0203 + +#define TI_LMP92064_VAL_CONFIG_A 0x99 +#define TI_LMP92064_VAL_CONFIG_B 0x00 +#define TI_LMP92064_VAL_STATUS_OK 0x01 + +/* + * Channel number definitions for the two channels of the device + * - IN Current (INC) + * - IN Voltage (INV) + */ +#define TI_LMP92064_CHAN_INC 0 +#define TI_LMP92064_CHAN_INV 1 + +static const struct regmap_range lmp92064_readable_reg_ranges[] = { + regmap_reg_range(TI_LMP92064_REG_CONFIG_A, TI_LMP92064_REG_CHIP_REV), + regmap_reg_range(TI_LMP92064_REG_MFR_ID1, TI_LMP92064_REG_MFR_ID2), + regmap_reg_range(TI_LMP92064_REG_REG_UPDATE, TI_LMP92064_REG_REG_UPDATE), + regmap_reg_range(TI_LMP92064_REG_CONFIG_REG, TI_LMP92064_REG_CONFIG_REG), + regmap_reg_range(TI_LMP92064_REG_STATUS, TI_LMP92064_REG_STATUS), + regmap_reg_range(TI_LMP92064_REG_DATA_VOUT_LSB, TI_LMP92064_REG_DATA_COUT_MSB), +}; + +static const struct regmap_access_table lmp92064_readable_regs = { + .yes_ranges = lmp92064_readable_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(lmp92064_readable_reg_ranges), +}; + +static const struct regmap_range lmp92064_writable_reg_ranges[] = { + regmap_reg_range(TI_LMP92064_REG_CONFIG_A, TI_LMP92064_REG_CONFIG_B), + regmap_reg_range(TI_LMP92064_REG_REG_UPDATE, TI_LMP92064_REG_REG_UPDATE), + regmap_reg_range(TI_LMP92064_REG_CONFIG_REG, TI_LMP92064_REG_CONFIG_REG), +}; + +static const struct regmap_access_table lmp92064_writable_regs = { + .yes_ranges = lmp92064_writable_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(lmp92064_writable_reg_ranges), +}; + +static const struct regmap_config lmp92064_spi_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = TI_LMP92064_REG_DATA_COUT_MSB, + .rd_table = &lmp92064_readable_regs, + .wr_table = &lmp92064_writable_regs, +}; + +struct lmp92064_adc_priv { + int shunt_resistor_uohm; + struct spi_device *spi; + struct regmap *regmap; +}; + +static const struct iio_chan_spec lmp92064_adc_channels[] = { + { + .type = IIO_CURRENT, + .address = TI_LMP92064_CHAN_INC, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .datasheet_name = "INC", + }, + { + .type = IIO_VOLTAGE, + .address = TI_LMP92064_CHAN_INV, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .datasheet_name = "INV", + }, +}; + +static int lmp92064_read_meas(struct lmp92064_adc_priv *priv, u16 *res) +{ + __be16 raw[2]; + int ret; + + /* + * The ADC only latches in new samples if all DATA registers are read + * in descending sequential order. + * The ADC auto-decrements the register index with each clocked byte. + * Read both channels in single SPI transfer by selecting the highest + * register using the command below and clocking out all four data + * bytes. + */ + + ret = regmap_bulk_read(priv->regmap, TI_LMP92064_REG_DATA_COUT_MSB, + &raw, sizeof(raw)); + + if (ret) { + dev_err(&priv->spi->dev, "regmap_bulk_read failed: %pe\n", + ERR_PTR(ret)); + return ret; + } + + res[0] = be16_to_cpu(raw[0]); + res[1] = be16_to_cpu(raw[1]); + + return 0; +} + +static int lmp92064_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct lmp92064_adc_priv *priv = iio_priv(indio_dev); + u16 raw[2]; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = lmp92064_read_meas(priv, raw); + if (ret < 0) + return ret; + + *val = (chan->address == TI_LMP92064_CHAN_INC) ? raw[0] : raw[1]; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + if (chan->address == TI_LMP92064_CHAN_INC) { + /* + * processed (mA) = raw * current_lsb (mA) + * current_lsb (mA) = shunt_voltage_lsb (nV) / shunt_resistor (uOhm) + * shunt_voltage_lsb (nV) = 81920000 / 4096 = 20000 + */ + *val = 20000; + *val2 = priv->shunt_resistor_uohm; + } else { + /* + * processed (mV) = raw * voltage_lsb (mV) + * voltage_lsb (mV) = 2048 / 4096 + */ + *val = 2048; + *val2 = 4096; + } + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } +} + +static int lmp92064_reset(struct lmp92064_adc_priv *priv, + struct gpio_desc *gpio_reset) +{ + unsigned int status; + int ret, i; + + if (gpio_reset) { + /* + * Perform a hard reset if gpio_reset is available. + * The datasheet specifies a very low 3.5ns reset pulse duration and does not + * specify how long to wait after a reset to access the device. + * Use more conservative pulse lengths to allow analog RC filtering of the + * reset line at the board level (as recommended in the datasheet). + */ + gpiod_set_value_cansleep(gpio_reset, 1); + usleep_range(1, 10); + gpiod_set_value_cansleep(gpio_reset, 0); + usleep_range(500, 750); + } else { + /* + * Perform a soft-reset if not. + * Also write default values to the config registers that are not + * affected by soft reset. + */ + ret = regmap_write(priv->regmap, TI_LMP92064_REG_CONFIG_A, + TI_LMP92064_VAL_CONFIG_A); + if (ret < 0) + return ret; + + ret = regmap_write(priv->regmap, TI_LMP92064_REG_CONFIG_B, + TI_LMP92064_VAL_CONFIG_B); + if (ret < 0) + return ret; + } + + /* + * Wait for the device to signal readiness to prevent reading bogus data + * and make sure device is actually connected. + * The datasheet does not specify how long this takes but usually it is + * not more than 3-4 iterations of this loop. + */ + for (i = 0; i < 10; i++) { + ret = regmap_read(priv->regmap, TI_LMP92064_REG_STATUS, &status); + if (ret < 0) + return ret; + + if (status == TI_LMP92064_VAL_STATUS_OK) + return 0; + + usleep_range(1000, 2000); + } + + /* + * No (correct) response received. + * Device is mostly likely not connected to the bus. + */ + return -ENXIO; +} + +static const struct iio_info lmp92064_adc_info = { + .read_raw = lmp92064_read_raw, +}; + +static int lmp92064_adc_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct lmp92064_adc_priv *priv; + struct gpio_desc *gpio_reset; + struct iio_dev *indio_dev; + u32 shunt_resistor_uohm; + struct regmap *regmap; + int ret; + + ret = spi_setup(spi); + if (ret < 0) + return dev_err_probe(dev, ret, "Error in SPI setup\n"); + + regmap = devm_regmap_init_spi(spi, &lmp92064_spi_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "Failed to set up SPI regmap\n"); + + indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); + if (!indio_dev) + return -ENOMEM; + + priv = iio_priv(indio_dev); + + priv->spi = spi; + priv->regmap = regmap; + + ret = device_property_read_u32(dev, "shunt-resistor-micro-ohms", + &shunt_resistor_uohm); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to get shunt-resistor value\n"); + + /* + * The shunt resistance is passed to userspace as the denominator of an iio + * fraction. Make sure it is in range for that. + */ + if (shunt_resistor_uohm == 0 || shunt_resistor_uohm > INT_MAX) { + dev_err(dev, "Shunt resistance is out of range\n"); + return -EINVAL; + } + + priv->shunt_resistor_uohm = shunt_resistor_uohm; + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return ret; + + ret = devm_regulator_get_enable(dev, "vdig"); + if (ret) + return ret; + + gpio_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(gpio_reset)) + return dev_err_probe(dev, PTR_ERR(gpio_reset), + "Failed to get GPIO reset pin\n"); + + ret = lmp92064_reset(priv, gpio_reset); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to reset device\n"); + + indio_dev->name = "lmp92064"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = lmp92064_adc_channels; + indio_dev->num_channels = ARRAY_SIZE(lmp92064_adc_channels); + indio_dev->info = &lmp92064_adc_info; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct spi_device_id lmp92064_id_table[] = { + { "lmp92064" }, + {} +}; +MODULE_DEVICE_TABLE(spi, lmp92064_id_table); + +static const struct of_device_id lmp92064_of_table[] = { + { .compatible = "ti,lmp92064" }, + {} +}; +MODULE_DEVICE_TABLE(of, lmp92064_of_table); + +static struct spi_driver lmp92064_adc_driver = { + .driver = { + .name = "lmp92064", + .of_match_table = lmp92064_of_table, + }, + .probe = lmp92064_adc_probe, + .id_table = lmp92064_id_table, +}; +module_spi_driver(lmp92064_adc_driver); + +MODULE_AUTHOR("Leonard Göhrs "); +MODULE_DESCRIPTION("TI LMP92064 ADC"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 58c2630020c5f820d63e2d03aa2341d19d4c0c5b Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 30 Nov 2022 15:26:32 -0300 Subject: iio: dac: ad5686: Add support for AD5337 AD5337 belongs to the same family as the AD5338. The difference is that the AD5337 has 8-bit precision instead of 10-bit. Add support for the AD5337 chip in the driver. Signed-off-by: Fabio Estevam Link: https://lore.kernel.org/r/20221130182632.3856675-2-festevam@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/dac/Kconfig | 8 ++++---- drivers/iio/dac/ad5686.c | 7 +++++++ drivers/iio/dac/ad5686.h | 1 + drivers/iio/dac/ad5696-i2c.c | 2 ++ 4 files changed, 14 insertions(+), 4 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 80521bd28d0f..e83eb75d87d1 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -162,10 +162,10 @@ config AD5696_I2C depends on I2C select AD5686 help - Say yes here to build support for Analog Devices AD5311R, AD5338R, - AD5671R, AD5673R, AD5675R, AD5677R, AD5691R, AD5692R, AD5693, AD5693R, - AD5694, AD5694R, AD5695R, AD5696, and AD5696R Digital to Analog - converters. + Say yes here to build support for Analog Devices AD5311R, AD5337, + AD5338R, AD5671R, AD5673R, AD5675R, AD5677R, AD5691R, AD5692R, AD5693, + AD5693R, AD5694, AD5694R, AD5695R, AD5696, and AD5696R Digital to + Analog converters. To compile this driver as a module, choose M here: the module will be called ad5696. diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 15361d8bbf94..57cc0f0eedc6 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -258,6 +258,7 @@ static const struct iio_chan_spec name[] = { \ DECLARE_AD5693_CHANNELS(ad5310r_channels, 10, 2); DECLARE_AD5693_CHANNELS(ad5311r_channels, 10, 6); +DECLARE_AD5338_CHANNELS(ad5337r_channels, 8, 8); DECLARE_AD5338_CHANNELS(ad5338r_channels, 10, 6); DECLARE_AD5676_CHANNELS(ad5672_channels, 12, 4); DECLARE_AD5679_CHANNELS(ad5674r_channels, 12, 4); @@ -283,6 +284,12 @@ static const struct ad5686_chip_info ad5686_chip_info_tbl[] = { .num_channels = 1, .regmap_type = AD5693_REGMAP, }, + [ID_AD5337R] = { + .channels = ad5337r_channels, + .int_vref_mv = 2500, + .num_channels = 2, + .regmap_type = AD5686_REGMAP, + }, [ID_AD5338R] = { .channels = ad5338r_channels, .int_vref_mv = 2500, diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h index b7ade3a6b9b6..760f852911df 100644 --- a/drivers/iio/dac/ad5686.h +++ b/drivers/iio/dac/ad5686.h @@ -54,6 +54,7 @@ enum ad5686_supported_device_ids { ID_AD5310R, ID_AD5311R, + ID_AD5337R, ID_AD5338R, ID_AD5671R, ID_AD5672R, diff --git a/drivers/iio/dac/ad5696-i2c.c b/drivers/iio/dac/ad5696-i2c.c index 160e80cf9135..8a95f0278018 100644 --- a/drivers/iio/dac/ad5696-i2c.c +++ b/drivers/iio/dac/ad5696-i2c.c @@ -72,6 +72,7 @@ static void ad5686_i2c_remove(struct i2c_client *i2c) static const struct i2c_device_id ad5686_i2c_id[] = { {"ad5311r", ID_AD5311R}, + {"ad5337r", ID_AD5337R}, {"ad5338r", ID_AD5338R}, {"ad5671r", ID_AD5671R}, {"ad5673r", ID_AD5673R}, @@ -92,6 +93,7 @@ MODULE_DEVICE_TABLE(i2c, ad5686_i2c_id); static const struct of_device_id ad5686_of_match[] = { { .compatible = "adi,ad5311r" }, + { .compatible = "adi,ad5337r" }, { .compatible = "adi,ad5338r" }, { .compatible = "adi,ad5671r" }, { .compatible = "adi,ad5675r" }, -- cgit v1.2.3 From 7ae267954af798f51985629dc795fba616af37cd Mon Sep 17 00:00:00 2001 From: ye xingchen Date: Thu, 1 Dec 2022 11:42:33 +0800 Subject: iio: adc: at91-sama5d2_adc: use sysfs_emit() to instead of scnprintf() Replace the open-code with sysfs_emit() to simplify the code. Signed-off-by: ye xingchen Link: https://lore.kernel.org/r/202212011142333790361@zte.com.cn Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index ed4f8501bda8..50d02e5fc6fc 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -2181,7 +2181,7 @@ static ssize_t at91_adc_get_fifo_state(struct device *dev, struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct at91_adc_state *st = iio_priv(indio_dev); - return scnprintf(buf, PAGE_SIZE, "%d\n", !!st->dma_st.dma_chan); + return sysfs_emit(buf, "%d\n", !!st->dma_st.dma_chan); } static ssize_t at91_adc_get_watermark(struct device *dev, @@ -2190,7 +2190,7 @@ static ssize_t at91_adc_get_watermark(struct device *dev, struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct at91_adc_state *st = iio_priv(indio_dev); - return scnprintf(buf, PAGE_SIZE, "%d\n", st->dma_st.watermark); + return sysfs_emit(buf, "%d\n", st->dma_st.watermark); } static IIO_DEVICE_ATTR(hwfifo_enabled, 0444, -- cgit v1.2.3 From f9e51aacc740935bd0201565e040fd7b36f645aa Mon Sep 17 00:00:00 2001 From: ye xingchen Date: Thu, 1 Dec 2022 11:56:31 +0800 Subject: iio: common: scmi_iio: use sysfs_emit() to instead of scnprintf() Replace the open-code with sysfs_emit() to simplify the code. Signed-off-by: ye xingchen Link: https://lore.kernel.org/r/202212011156314630626@zte.com.cn Signed-off-by: Jonathan Cameron --- drivers/iio/common/scmi_sensors/scmi_iio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/common/scmi_sensors/scmi_iio.c b/drivers/iio/common/scmi_sensors/scmi_iio.c index d92f7f651f7b..0c2caf3570db 100644 --- a/drivers/iio/common/scmi_sensors/scmi_iio.c +++ b/drivers/iio/common/scmi_sensors/scmi_iio.c @@ -400,12 +400,12 @@ static ssize_t scmi_iio_get_raw_available(struct iio_dev *iio_dev, rem = do_div(resolution, int_pow(10, abs(exponent)) ); - len = scnprintf(buf, PAGE_SIZE, + len = sysfs_emit(buf, "[%lld %llu.%llu %lld]\n", min_range, resolution, rem, max_range); } else { resolution = resolution * int_pow(10, exponent); - len = scnprintf(buf, PAGE_SIZE, "[%lld %llu %lld]\n", + len = sysfs_emit(buf, "[%lld %llu %lld]\n", min_range, resolution, max_range); } } -- cgit v1.2.3 From 4da9438d293d30def46b2801fa00e176c59883d2 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 16 Oct 2022 17:33:57 +0100 Subject: iio: accel: bma400: Use devm_regulator_bulk_get_enable() This driver only turns the power on at probe and off via a custom devm_add_action_or_reset() callback. The new devm_regulator_bulk_get_enable() replaces this boilerplate code. Signed-off-by: Jonathan Cameron Cc: Jagath Jog J Reviewed-by: Matti Vaittinen Link: https://lore.kernel.org/r/20221016163409.320197-3-jic23@kernel.org --- drivers/iio/accel/bma400.h | 4 ---- drivers/iio/accel/bma400_core.c | 29 ++++------------------------- 2 files changed, 4 insertions(+), 29 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/bma400.h b/drivers/iio/accel/bma400.h index 36edbaff4f7f..932358b45f17 100644 --- a/drivers/iio/accel/bma400.h +++ b/drivers/iio/accel/bma400.h @@ -141,10 +141,6 @@ #define BMA400_SCALE_MIN 9577 #define BMA400_SCALE_MAX 76617 -#define BMA400_NUM_REGULATORS 2 -#define BMA400_VDD_REGULATOR 0 -#define BMA400_VDDIO_REGULATOR 1 - extern const struct regmap_config bma400_regmap_config; int bma400_probe(struct device *dev, struct regmap *regmap, int irq, diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c index b612d0146d4d..623f37cbaf50 100644 --- a/drivers/iio/accel/bma400_core.c +++ b/drivers/iio/accel/bma400_core.c @@ -98,7 +98,6 @@ enum bma400_activity { struct bma400_data { struct device *dev; struct regmap *regmap; - struct regulator_bulk_data regulators[BMA400_NUM_REGULATORS]; struct mutex mutex; /* data register lock */ struct iio_mount_matrix orientation; enum bma400_power_mode power_mode; @@ -832,13 +831,6 @@ static void bma400_init_tables(void) } } -static void bma400_regulators_disable(void *data_ptr) -{ - struct bma400_data *data = data_ptr; - - regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators); -} - static void bma400_power_disable(void *data_ptr) { struct bma400_data *data = data_ptr; @@ -868,30 +860,17 @@ static enum iio_modifier bma400_act_to_mod(enum bma400_activity activity) static int bma400_init(struct bma400_data *data) { + static const char * const regulator_names[] = { "vdd", "vddio" }; unsigned int val; int ret; - data->regulators[BMA400_VDD_REGULATOR].supply = "vdd"; - data->regulators[BMA400_VDDIO_REGULATOR].supply = "vddio"; - ret = devm_regulator_bulk_get(data->dev, - ARRAY_SIZE(data->regulators), - data->regulators); + ret = devm_regulator_bulk_get_enable(data->dev, + ARRAY_SIZE(regulator_names), + regulator_names); if (ret) return dev_err_probe(data->dev, ret, "Failed to get regulators: %d\n", ret); - ret = regulator_bulk_enable(ARRAY_SIZE(data->regulators), - data->regulators); - if (ret) { - dev_err(data->dev, "Failed to enable regulators: %d\n", - ret); - return ret; - } - - ret = devm_add_action_or_reset(data->dev, bma400_regulators_disable, data); - if (ret) - return ret; - /* Try to read chip_id register. It must return 0x90. */ ret = regmap_read(data->regmap, BMA400_CHIP_ID_REG, &val); if (ret) { -- cgit v1.2.3 From 122ef59a2a16e4542705913b905d841704e239e9 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 16 Oct 2022 17:34:08 +0100 Subject: iio: pressure: ms5611: Use devm_regulator_get_enable() This driver only turns the power on at probe and off at remove. The new devm_regulator_get_enable() replaces this boilerplate code. Some additional refactoring to drop now unnecessary unwinding after this change. Signed-off-by: Jonathan Cameron Cc: Tomasz Duszynski Reviewed-by: Matti Vaittinen Link: https://lore.kernel.org/r/20221016163409.320197-14-jic23@kernel.org --- drivers/iio/pressure/ms5611.h | 3 --- drivers/iio/pressure/ms5611_core.c | 32 +++++--------------------------- 2 files changed, 5 insertions(+), 30 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/pressure/ms5611.h b/drivers/iio/pressure/ms5611.h index 550b75b7186f..6656ec9be8e6 100644 --- a/drivers/iio/pressure/ms5611.h +++ b/drivers/iio/pressure/ms5611.h @@ -13,8 +13,6 @@ #include #include -struct regulator; - #define MS5611_RESET 0x1e #define MS5611_READ_ADC 0x00 #define MS5611_READ_PROM_WORD 0xA0 @@ -52,7 +50,6 @@ struct ms5611_state { int (*compensate_temp_and_pressure)(struct ms5611_state *st, s32 *temp, s32 *pressure); - struct regulator *vdd; }; int ms5611_probe(struct iio_dev *indio_dev, struct device *dev, diff --git a/drivers/iio/pressure/ms5611_core.c b/drivers/iio/pressure/ms5611_core.c index c564a1d6cafe..a80f1d6120b7 100644 --- a/drivers/iio/pressure/ms5611_core.c +++ b/drivers/iio/pressure/ms5611_core.c @@ -380,40 +380,21 @@ static const struct iio_info ms5611_info = { static int ms5611_init(struct iio_dev *indio_dev) { int ret; - struct ms5611_state *st = iio_priv(indio_dev); /* Enable attached regulator if any. */ - st->vdd = devm_regulator_get(indio_dev->dev.parent, "vdd"); - if (IS_ERR(st->vdd)) - return PTR_ERR(st->vdd); - - ret = regulator_enable(st->vdd); - if (ret) { - dev_err(indio_dev->dev.parent, - "failed to enable Vdd supply: %d\n", ret); + ret = devm_regulator_get_enable(indio_dev->dev.parent, "vdd"); + if (ret) return ret; - } ret = ms5611_reset(indio_dev); if (ret < 0) - goto err_regulator_disable; + return ret; ret = ms5611_read_prom(indio_dev); if (ret < 0) - goto err_regulator_disable; + return ret; return 0; - -err_regulator_disable: - regulator_disable(st->vdd); - return ret; -} - -static void ms5611_fini(const struct iio_dev *indio_dev) -{ - const struct ms5611_state *st = iio_priv(indio_dev); - - regulator_disable(st->vdd); } int ms5611_probe(struct iio_dev *indio_dev, struct device *dev, @@ -457,7 +438,7 @@ int ms5611_probe(struct iio_dev *indio_dev, struct device *dev, ms5611_trigger_handler, NULL); if (ret < 0) { dev_err(dev, "iio triggered buffer setup failed\n"); - goto err_fini; + return ret; } ret = iio_device_register(indio_dev); @@ -470,8 +451,6 @@ int ms5611_probe(struct iio_dev *indio_dev, struct device *dev, err_buffer_cleanup: iio_triggered_buffer_cleanup(indio_dev); -err_fini: - ms5611_fini(indio_dev); return ret; } EXPORT_SYMBOL_NS(ms5611_probe, IIO_MS5611); @@ -480,7 +459,6 @@ void ms5611_remove(struct iio_dev *indio_dev) { iio_device_unregister(indio_dev); iio_triggered_buffer_cleanup(indio_dev); - ms5611_fini(indio_dev); } EXPORT_SYMBOL_NS(ms5611_remove, IIO_MS5611); -- cgit v1.2.3 From caa6693e408eb87169ac18db6073a326829ce7ad Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 16 Oct 2022 17:34:09 +0100 Subject: iio: pressure: ms5611: Switch to fully devm_ managed registration. All the remaining calls in probe() have devm_ equivalents so switching to those allows the remove() callbacks to be deleted. No functional change. Signed-off-by: Jonathan Cameron Cc: Tomasz Duszynski Reviewed-by: Matti Vaittinen Link: https://lore.kernel.org/r/20221016163409.320197-15-jic23@kernel.org Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/ms5611.h | 1 - drivers/iio/pressure/ms5611_core.c | 17 +++-------------- drivers/iio/pressure/ms5611_i2c.c | 6 ------ drivers/iio/pressure/ms5611_spi.c | 6 ------ 4 files changed, 3 insertions(+), 27 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/pressure/ms5611.h b/drivers/iio/pressure/ms5611.h index 6656ec9be8e6..a78acd3fb18f 100644 --- a/drivers/iio/pressure/ms5611.h +++ b/drivers/iio/pressure/ms5611.h @@ -54,6 +54,5 @@ struct ms5611_state { int ms5611_probe(struct iio_dev *indio_dev, struct device *dev, const char *name, int type); -void ms5611_remove(struct iio_dev *indio_dev); #endif /* _MS5611_H */ diff --git a/drivers/iio/pressure/ms5611_core.c b/drivers/iio/pressure/ms5611_core.c index a80f1d6120b7..627497e61a63 100644 --- a/drivers/iio/pressure/ms5611_core.c +++ b/drivers/iio/pressure/ms5611_core.c @@ -434,34 +434,23 @@ int ms5611_probe(struct iio_dev *indio_dev, struct device *dev, if (ret < 0) return ret; - ret = iio_triggered_buffer_setup(indio_dev, NULL, + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, ms5611_trigger_handler, NULL); if (ret < 0) { dev_err(dev, "iio triggered buffer setup failed\n"); return ret; } - ret = iio_device_register(indio_dev); + ret = devm_iio_device_register(dev, indio_dev); if (ret < 0) { dev_err(dev, "unable to register iio device\n"); - goto err_buffer_cleanup; + return ret; } return 0; - -err_buffer_cleanup: - iio_triggered_buffer_cleanup(indio_dev); - return ret; } EXPORT_SYMBOL_NS(ms5611_probe, IIO_MS5611); -void ms5611_remove(struct iio_dev *indio_dev) -{ - iio_device_unregister(indio_dev); - iio_triggered_buffer_cleanup(indio_dev); -} -EXPORT_SYMBOL_NS(ms5611_remove, IIO_MS5611); - MODULE_AUTHOR("Tomasz Duszynski "); MODULE_DESCRIPTION("MS5611 core driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/pressure/ms5611_i2c.c b/drivers/iio/pressure/ms5611_i2c.c index caf882497656..e3c68a3ed76a 100644 --- a/drivers/iio/pressure/ms5611_i2c.c +++ b/drivers/iio/pressure/ms5611_i2c.c @@ -105,11 +105,6 @@ static int ms5611_i2c_probe(struct i2c_client *client) return ms5611_probe(indio_dev, &client->dev, id->name, id->driver_data); } -static void ms5611_i2c_remove(struct i2c_client *client) -{ - ms5611_remove(i2c_get_clientdata(client)); -} - static const struct of_device_id ms5611_i2c_matches[] = { { .compatible = "meas,ms5611" }, { .compatible = "meas,ms5607" }, @@ -131,7 +126,6 @@ static struct i2c_driver ms5611_driver = { }, .id_table = ms5611_id, .probe_new = ms5611_i2c_probe, - .remove = ms5611_i2c_remove, }; module_i2c_driver(ms5611_driver); diff --git a/drivers/iio/pressure/ms5611_spi.c b/drivers/iio/pressure/ms5611_spi.c index a0a7205c9c3a..cc9d1f68c53c 100644 --- a/drivers/iio/pressure/ms5611_spi.c +++ b/drivers/iio/pressure/ms5611_spi.c @@ -107,11 +107,6 @@ static int ms5611_spi_probe(struct spi_device *spi) spi_get_device_id(spi)->driver_data); } -static void ms5611_spi_remove(struct spi_device *spi) -{ - ms5611_remove(spi_get_drvdata(spi)); -} - static const struct of_device_id ms5611_spi_matches[] = { { .compatible = "meas,ms5611" }, { .compatible = "meas,ms5607" }, @@ -133,7 +128,6 @@ static struct spi_driver ms5611_driver = { }, .id_table = ms5611_id, .probe = ms5611_spi_probe, - .remove = ms5611_spi_remove, }; module_spi_driver(ms5611_driver); -- cgit v1.2.3 From 22cd9320b11a2eb05e5d490863a4219ca32e8ec9 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 18 Nov 2022 23:37:46 +0100 Subject: iio: light: max44009: Convert to i2c's .probe_new() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The probe function doesn't make use of the i2c_device_id * parameter so it can be trivially converted. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20221118224540.619276-133-uwe@kleine-koenig.org Signed-off-by: Jonathan Cameron --- drivers/iio/light/max44009.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/light/max44009.c b/drivers/iio/light/max44009.c index 801e5a0ad496..3dadace09fe2 100644 --- a/drivers/iio/light/max44009.c +++ b/drivers/iio/light/max44009.c @@ -487,8 +487,7 @@ static irqreturn_t max44009_threaded_irq_handler(int irq, void *p) return IRQ_NONE; } -static int max44009_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int max44009_probe(struct i2c_client *client) { struct max44009_data *data; struct iio_dev *indio_dev; @@ -538,7 +537,7 @@ static struct i2c_driver max44009_driver = { .driver = { .name = MAX44009_DRV_NAME, }, - .probe = max44009_probe, + .probe_new = max44009_probe, .id_table = max44009_id, }; module_i2c_driver(max44009_driver); -- cgit v1.2.3 From 027641b52fe37b64af61025298ce160c8b9b7a73 Mon Sep 17 00:00:00 2001 From: Ferry Toth Date: Wed, 7 Dec 2022 21:03:38 +0200 Subject: iio: light: tsl2563: Do not hardcode interrupt trigger type Instead of hardcoding IRQ trigger type to IRQF_TRIGGER_RAISING, let's respect the settings specified in the firmware description. To be compatible with the older firmware descriptions, if trigger type is not set up there, we'll set it to default (raising edge). Fixes: 388be4883952 ("staging:iio: tsl2563 abi fixes and interrupt handling") Fixes: bdab1001738f ("staging:iio:light:tsl2563 remove old style event registration.") Signed-off-by: Ferry Toth Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20221207190348.9347-1-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/light/tsl2563.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/light/tsl2563.c b/drivers/iio/light/tsl2563.c index d0e42b73203a..71302ae864d9 100644 --- a/drivers/iio/light/tsl2563.c +++ b/drivers/iio/light/tsl2563.c @@ -704,6 +704,7 @@ static int tsl2563_probe(struct i2c_client *client) struct iio_dev *indio_dev; struct tsl2563_chip *chip; struct tsl2563_platform_data *pdata = client->dev.platform_data; + unsigned long irq_flags; int err = 0; u8 id = 0; @@ -759,10 +760,15 @@ static int tsl2563_probe(struct i2c_client *client) indio_dev->info = &tsl2563_info_no_irq; if (client->irq) { + irq_flags = irq_get_trigger_type(client->irq); + if (irq_flags == IRQF_TRIGGER_NONE) + irq_flags = IRQF_TRIGGER_RISING; + irq_flags |= IRQF_ONESHOT; + err = devm_request_threaded_irq(&client->dev, client->irq, NULL, &tsl2563_event_handler, - IRQF_TRIGGER_RISING | IRQF_ONESHOT, + irq_flags, "tsl2563_event", indio_dev); if (err) { -- cgit v1.2.3 From 3c183534f2784484182e51c8019c1e9f0638ef8a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 7 Dec 2022 21:03:39 +0200 Subject: iio: light: tsl2563: Use i2c_smbus_write_word_data() in tsl2563_configure() Driver already uses the word accessors when it makes sense, but in the tsl2563_configure(). Switch the latter to use word accessor. Signed-off-by: Andy Shevchenko Tested-by: Ferry Toth Link: https://lore.kernel.org/r/20221207190348.9347-2-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/light/tsl2563.c | 52 +++++++++++++++++---------------------------- 1 file changed, 19 insertions(+), 33 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/light/tsl2563.c b/drivers/iio/light/tsl2563.c index 71302ae864d9..d836c15ba777 100644 --- a/drivers/iio/light/tsl2563.c +++ b/drivers/iio/light/tsl2563.c @@ -49,16 +49,12 @@ #define TSL2563_REG_CTRL 0x00 #define TSL2563_REG_TIMING 0x01 -#define TSL2563_REG_LOWLOW 0x02 /* data0 low threshold, 2 bytes */ -#define TSL2563_REG_LOWHIGH 0x03 -#define TSL2563_REG_HIGHLOW 0x04 /* data0 high threshold, 2 bytes */ -#define TSL2563_REG_HIGHHIGH 0x05 +#define TSL2563_REG_LOW 0x02 /* data0 low threshold, 2 bytes */ +#define TSL2563_REG_HIGH 0x04 /* data0 high threshold, 2 bytes */ #define TSL2563_REG_INT 0x06 #define TSL2563_REG_ID 0x0a -#define TSL2563_REG_DATA0LOW 0x0c /* broadband sensor value, 2 bytes */ -#define TSL2563_REG_DATA0HIGH 0x0d -#define TSL2563_REG_DATA1LOW 0x0e /* infrared sensor value, 2 bytes */ -#define TSL2563_REG_DATA1HIGH 0x0f +#define TSL2563_REG_DATA0 0x0c /* broadband sensor value, 2 bytes */ +#define TSL2563_REG_DATA1 0x0e /* infrared sensor value, 2 bytes */ #define TSL2563_CMD_POWER_ON 0x03 #define TSL2563_CMD_POWER_OFF 0x00 @@ -161,24 +157,16 @@ static int tsl2563_configure(struct tsl2563_chip *chip) chip->gainlevel->gaintime); if (ret) goto error_ret; - ret = i2c_smbus_write_byte_data(chip->client, - TSL2563_CMD | TSL2563_REG_HIGHLOW, - chip->high_thres & 0xFF); - if (ret) - goto error_ret; - ret = i2c_smbus_write_byte_data(chip->client, - TSL2563_CMD | TSL2563_REG_HIGHHIGH, - (chip->high_thres >> 8) & 0xFF); + ret = i2c_smbus_write_word_data(chip->client, + TSL2563_CMD | TSL2563_REG_HIGH, + chip->high_thres); if (ret) goto error_ret; - ret = i2c_smbus_write_byte_data(chip->client, - TSL2563_CMD | TSL2563_REG_LOWLOW, - chip->low_thres & 0xFF); + ret = i2c_smbus_write_word_data(chip->client, + TSL2563_CMD | TSL2563_REG_LOW, + chip->low_thres); if (ret) goto error_ret; - ret = i2c_smbus_write_byte_data(chip->client, - TSL2563_CMD | TSL2563_REG_LOWHIGH, - (chip->low_thres >> 8) & 0xFF); /* * Interrupt register is automatically written anyway if it is relevant * so is not here. @@ -325,13 +313,13 @@ static int tsl2563_get_adc(struct tsl2563_chip *chip) while (retry) { ret = i2c_smbus_read_word_data(client, - TSL2563_CMD | TSL2563_REG_DATA0LOW); + TSL2563_CMD | TSL2563_REG_DATA0); if (ret < 0) goto out; adc0 = ret; ret = i2c_smbus_read_word_data(client, - TSL2563_CMD | TSL2563_REG_DATA1LOW); + TSL2563_CMD | TSL2563_REG_DATA1); if (ret < 0) goto out; adc1 = ret; @@ -584,20 +572,18 @@ static int tsl2563_write_thresh(struct iio_dev *indio_dev, { struct tsl2563_chip *chip = iio_priv(indio_dev); int ret; - u8 address; + + mutex_lock(&chip->lock); if (dir == IIO_EV_DIR_RISING) - address = TSL2563_REG_HIGHLOW; + ret = i2c_smbus_write_word_data(chip->client, + TSL2563_CMD | TSL2563_REG_HIGH, val); else - address = TSL2563_REG_LOWLOW; - mutex_lock(&chip->lock); - ret = i2c_smbus_write_byte_data(chip->client, TSL2563_CMD | address, - val & 0xFF); + ret = i2c_smbus_write_word_data(chip->client, + TSL2563_CMD | TSL2563_REG_LOW, val); if (ret) goto error_ret; - ret = i2c_smbus_write_byte_data(chip->client, - TSL2563_CMD | (address + 1), - (val >> 8) & 0xFF); + if (dir == IIO_EV_DIR_RISING) chip->high_thres = val; else -- cgit v1.2.3 From aca68c027322fd7c63c05b308569727a618c47a7 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 7 Dec 2022 21:03:40 +0200 Subject: iio: light: tsl2563: Configure INT in one place Introduce tsl2563_configure_irq() to configure INT in one place. While at it, make use of TSL2563_INT_LEVEL and newly introduced TSL2563_INT_MASK. Signed-off-by: Andy Shevchenko Tested-by: Ferry Toth Link: https://lore.kernel.org/r/20221207190348.9347-3-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/light/tsl2563.c | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/light/tsl2563.c b/drivers/iio/light/tsl2563.c index d836c15ba777..d071805239ef 100644 --- a/drivers/iio/light/tsl2563.c +++ b/drivers/iio/light/tsl2563.c @@ -69,6 +69,7 @@ #define TSL2563_INT_DISABLED 0x00 #define TSL2563_INT_LEVEL 0x10 +#define TSL2563_INT_MASK 0x30 #define TSL2563_INT_PERSIST(n) ((n) & 0x0F) struct tsl2563_gainlevel_coeff { @@ -211,6 +212,24 @@ static int tsl2563_read_id(struct tsl2563_chip *chip, u8 *id) return 0; } +static int tsl2563_configure_irq(struct tsl2563_chip *chip, bool enable) +{ + int ret; + + chip->intr &= ~TSL2563_INT_MASK; + if (enable) + chip->intr |= TSL2563_INT_LEVEL; + + ret = i2c_smbus_write_byte_data(chip->client, + TSL2563_CMD | TSL2563_REG_INT, + chip->intr); + if (ret < 0) + return ret; + + chip->int_enabled = enable; + return 0; +} + /* * "Normalized" ADC value is one obtained with 400ms of integration time and * 16x gain. This function returns the number of bits of shift needed to @@ -620,9 +639,7 @@ static int tsl2563_write_interrupt_config(struct iio_dev *indio_dev, int ret = 0; mutex_lock(&chip->lock); - if (state && !(chip->intr & 0x30)) { - chip->intr &= ~0x30; - chip->intr |= 0x10; + if (state && !(chip->intr & TSL2563_INT_MASK)) { /* ensure the chip is actually on */ cancel_delayed_work_sync(&chip->poweroff_work); if (!tsl2563_get_power(chip)) { @@ -633,18 +650,11 @@ static int tsl2563_write_interrupt_config(struct iio_dev *indio_dev, if (ret) goto out; } - ret = i2c_smbus_write_byte_data(chip->client, - TSL2563_CMD | TSL2563_REG_INT, - chip->intr); - chip->int_enabled = true; + ret = tsl2563_configure_irq(chip, true); } - if (!state && (chip->intr & 0x30)) { - chip->intr &= ~0x30; - ret = i2c_smbus_write_byte_data(chip->client, - TSL2563_CMD | TSL2563_REG_INT, - chip->intr); - chip->int_enabled = false; + if (!state && (chip->intr & TSL2563_INT_MASK)) { + ret = tsl2563_configure_irq(chip, false); /* now the interrupt is not enabled, we can go to sleep */ schedule_delayed_work(&chip->poweroff_work, 5 * HZ); } @@ -668,7 +678,7 @@ static int tsl2563_read_interrupt_config(struct iio_dev *indio_dev, if (ret < 0) return ret; - return !!(ret & 0x30); + return !!(ret & TSL2563_INT_MASK); } static const struct iio_info tsl2563_info_no_irq = { @@ -796,9 +806,7 @@ static void tsl2563_remove(struct i2c_client *client) if (!chip->int_enabled) cancel_delayed_work_sync(&chip->poweroff_work); /* Ensure that interrupts are disabled - then flush any bottom halves */ - chip->intr &= ~0x30; - i2c_smbus_write_byte_data(chip->client, TSL2563_CMD | TSL2563_REG_INT, - chip->intr); + tsl2563_configure_irq(chip, false); tsl2563_set_power(chip, 0); } -- cgit v1.2.3 From b90619c4efee915c6419373b1ec024878ee7e38c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 7 Dec 2022 21:03:41 +0200 Subject: iio: light: tsl2563: Make use of the macros from bits.h Make use of BIT() and GENMASK() where it makes sense. Signed-off-by: Andy Shevchenko Tested-by: Ferry Toth Link: https://lore.kernel.org/r/20221207190348.9347-4-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/light/tsl2563.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/light/tsl2563.c b/drivers/iio/light/tsl2563.c index d071805239ef..3b60d8000351 100644 --- a/drivers/iio/light/tsl2563.c +++ b/drivers/iio/light/tsl2563.c @@ -11,6 +11,7 @@ * Amit Kucheria */ +#include #include #include #include @@ -33,19 +34,19 @@ #define ADC_FRAC_BITS 14 /* Given number of 1/10000's in ADC_FRAC_BITS precision. */ -#define FRAC10K(f) (((f) * (1L << (ADC_FRAC_BITS))) / (10000)) +#define FRAC10K(f) (((f) * BIT(ADC_FRAC_BITS)) / (10000)) /* Bits used for fraction in calibration coefficients.*/ #define CALIB_FRAC_BITS 10 /* 0.5 in CALIB_FRAC_BITS precision */ -#define CALIB_FRAC_HALF (1 << (CALIB_FRAC_BITS - 1)) +#define CALIB_FRAC_HALF BIT(CALIB_FRAC_BITS - 1) /* Make a fraction from a number n that was multiplied with b. */ #define CALIB_FRAC(n, b) (((n) << CALIB_FRAC_BITS) / (b)) /* Decimal 10^(digits in sysfs presentation) */ #define CALIB_BASE_SYSFS 1000 -#define TSL2563_CMD 0x80 -#define TSL2563_CLEARINT 0x40 +#define TSL2563_CMD BIT(7) +#define TSL2563_CLEARINT BIT(6) #define TSL2563_REG_CTRL 0x00 #define TSL2563_REG_TIMING 0x01 @@ -58,19 +59,19 @@ #define TSL2563_CMD_POWER_ON 0x03 #define TSL2563_CMD_POWER_OFF 0x00 -#define TSL2563_CTRL_POWER_MASK 0x03 +#define TSL2563_CTRL_POWER_MASK GENMASK(1, 0) #define TSL2563_TIMING_13MS 0x00 #define TSL2563_TIMING_100MS 0x01 #define TSL2563_TIMING_400MS 0x02 -#define TSL2563_TIMING_MASK 0x03 +#define TSL2563_TIMING_MASK GENMASK(1, 0) #define TSL2563_TIMING_GAIN16 0x10 #define TSL2563_TIMING_GAIN1 0x00 #define TSL2563_INT_DISABLED 0x00 #define TSL2563_INT_LEVEL 0x10 -#define TSL2563_INT_MASK 0x30 -#define TSL2563_INT_PERSIST(n) ((n) & 0x0F) +#define TSL2563_INT_MASK GENMASK(5, 4) +#define TSL2563_INT_PERSIST(n) ((n) & GENMASK(3, 0)) struct tsl2563_gainlevel_coeff { u8 gaintime; -- cgit v1.2.3 From bbabf9199126d7eb950ccc369543c462cc58e1b7 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 7 Dec 2022 21:03:42 +0200 Subject: iio: light: tsl2563: Drop unused defintion(s) The CALIB_FRAC() is defined and might had been used in tsl2563_calib_from_sysfs(). But let's just move a comment from it to the latter function. CLAIB_FRAC_HALF is used in a single place, i.e. in tsl2563_calib_to_sysfs(). So, let's just inline it there. While at it, switch to use DIV_ROUND_CLOSEST(). Signed-off-by: Andy Shevchenko Tested-by: Ferry Toth Link: https://lore.kernel.org/r/20221207190348.9347-5-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/light/tsl2563.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/light/tsl2563.c b/drivers/iio/light/tsl2563.c index 3b60d8000351..bdd40a5df53d 100644 --- a/drivers/iio/light/tsl2563.c +++ b/drivers/iio/light/tsl2563.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -38,10 +39,6 @@ /* Bits used for fraction in calibration coefficients.*/ #define CALIB_FRAC_BITS 10 -/* 0.5 in CALIB_FRAC_BITS precision */ -#define CALIB_FRAC_HALF BIT(CALIB_FRAC_BITS - 1) -/* Make a fraction from a number n that was multiplied with b. */ -#define CALIB_FRAC(n, b) (((n) << CALIB_FRAC_BITS) / (b)) /* Decimal 10^(digits in sysfs presentation) */ #define CALIB_BASE_SYSFS 1000 @@ -360,12 +357,12 @@ out: static inline int tsl2563_calib_to_sysfs(u32 calib) { - return (int) (((calib * CALIB_BASE_SYSFS) + - CALIB_FRAC_HALF) >> CALIB_FRAC_BITS); + return (int)DIV_ROUND_CLOSEST(calib * CALIB_BASE_SYSFS, BIT(CALIB_FRAC_BITS)); } static inline u32 tsl2563_calib_from_sysfs(int value) { + /* Make a fraction from a number n that was multiplied with b. */ return (((u32) value) << CALIB_FRAC_BITS) / CALIB_BASE_SYSFS; } -- cgit v1.2.3 From 2080c8d34602a6ad86286a04e5b8b40f40505215 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 7 Dec 2022 21:03:43 +0200 Subject: iio: light: tsl2563: Simplify with dev_err_probe Code can be a bit simpler with dev_err_probe(). Signed-off-by: Andy Shevchenko Tested-by: Ferry Toth Link: https://lore.kernel.org/r/20221207190348.9347-6-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/light/tsl2563.c | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/light/tsl2563.c b/drivers/iio/light/tsl2563.c index bdd40a5df53d..cce044556293 100644 --- a/drivers/iio/light/tsl2563.c +++ b/drivers/iio/light/tsl2563.c @@ -695,12 +695,13 @@ static const struct iio_info tsl2563_info = { static int tsl2563_probe(struct i2c_client *client) { + struct device *dev = &client->dev; struct iio_dev *indio_dev; struct tsl2563_chip *chip; struct tsl2563_platform_data *pdata = client->dev.platform_data; unsigned long irq_flags; - int err = 0; u8 id = 0; + int err; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); if (!indio_dev) @@ -712,16 +713,12 @@ static int tsl2563_probe(struct i2c_client *client) chip->client = client; err = tsl2563_detect(chip); - if (err) { - dev_err(&client->dev, "detect error %d\n", -err); - return err; - } + if (err) + return dev_err_probe(dev, err, "detect error\n"); err = tsl2563_read_id(chip, &id); - if (err) { - dev_err(&client->dev, "read id error %d\n", -err); - return err; - } + if (err) + return dev_err_probe(dev, err, "read id error\n"); mutex_init(&chip->lock); @@ -765,17 +762,13 @@ static int tsl2563_probe(struct i2c_client *client) irq_flags, "tsl2563_event", indio_dev); - if (err) { - dev_err(&client->dev, "irq request error %d\n", -err); - return err; - } + if (err) + return dev_err_probe(dev, err, "irq request error\n"); } err = tsl2563_configure(chip); - if (err) { - dev_err(&client->dev, "configure error %d\n", -err); - return err; - } + if (err) + return dev_err_probe(dev, err, "configure error\n"); INIT_DELAYED_WORK(&chip->poweroff_work, tsl2563_poweroff_work); @@ -784,7 +777,7 @@ static int tsl2563_probe(struct i2c_client *client) err = iio_device_register(indio_dev); if (err) { - dev_err(&client->dev, "iio registration error %d\n", -err); + dev_err_probe(dev, err, "iio registration error\n"); goto fail; } -- cgit v1.2.3 From 1f5e408f6a000be980872b8065e547e2dbef6acc Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 7 Dec 2022 21:03:44 +0200 Subject: iio: light: tsl2563: Drop legacy platform data code There is no in-kernel user for legacy platform data. Otherwise, a new one can use software nodes instead. Hence, drop legacy platform data code. Signed-off-by: Andy Shevchenko Tested-by: Ferry Toth Link: https://lore.kernel.org/r/20221207190348.9347-7-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/light/tsl2563.c | 12 ++---------- include/linux/platform_data/tsl2563.h | 9 --------- 2 files changed, 2 insertions(+), 19 deletions(-) delete mode 100644 include/linux/platform_data/tsl2563.h (limited to 'drivers/iio') diff --git a/drivers/iio/light/tsl2563.c b/drivers/iio/light/tsl2563.c index cce044556293..ed193a3da91e 100644 --- a/drivers/iio/light/tsl2563.c +++ b/drivers/iio/light/tsl2563.c @@ -29,7 +29,6 @@ #include #include #include -#include /* Use this many bits for fraction part. */ #define ADC_FRAC_BITS 14 @@ -698,7 +697,6 @@ static int tsl2563_probe(struct i2c_client *client) struct device *dev = &client->dev; struct iio_dev *indio_dev; struct tsl2563_chip *chip; - struct tsl2563_platform_data *pdata = client->dev.platform_data; unsigned long irq_flags; u8 id = 0; int err; @@ -730,14 +728,8 @@ static int tsl2563_probe(struct i2c_client *client) 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; - } else { - err = device_property_read_u32(&client->dev, "amstaos,cover-comp-gain", - &chip->cover_comp_gain); - if (err) - chip->cover_comp_gain = 1; - } + chip->cover_comp_gain = 1; + device_property_read_u32(dev, "amstaos,cover-comp-gain", &chip->cover_comp_gain); dev_info(&client->dev, "model %d, rev. %d\n", id >> 4, id & 0x0f); indio_dev->name = client->name; diff --git a/include/linux/platform_data/tsl2563.h b/include/linux/platform_data/tsl2563.h deleted file mode 100644 index 9cf9309c3f24..000000000000 --- a/include/linux/platform_data/tsl2563.h +++ /dev/null @@ -1,9 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __LINUX_TSL2563_H -#define __LINUX_TSL2563_H - -struct tsl2563_platform_data { - int cover_comp_gain; -}; - -#endif /* __LINUX_TSL2563_H */ -- cgit v1.2.3 From 85a6b728eff68647746629b9332434e237a53100 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 7 Dec 2022 21:03:45 +0200 Subject: iio: light: tsl2563: Utilise temporary variable for struct device We have a temporary variable to keep pointer to struct device. Utilise it inside the ->probe() implementation. Signed-off-by: Andy Shevchenko Tested-by: Ferry Toth Link: https://lore.kernel.org/r/20221207190348.9347-8-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/light/tsl2563.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/light/tsl2563.c b/drivers/iio/light/tsl2563.c index ed193a3da91e..c5814545fd19 100644 --- a/drivers/iio/light/tsl2563.c +++ b/drivers/iio/light/tsl2563.c @@ -701,7 +701,7 @@ static int tsl2563_probe(struct i2c_client *client) u8 id = 0; int err; - indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*chip)); if (!indio_dev) return -ENOMEM; @@ -731,7 +731,7 @@ static int tsl2563_probe(struct i2c_client *client) chip->cover_comp_gain = 1; device_property_read_u32(dev, "amstaos,cover-comp-gain", &chip->cover_comp_gain); - dev_info(&client->dev, "model %d, rev. %d\n", id >> 4, id & 0x0f); + dev_info(dev, "model %d, rev. %d\n", id >> 4, id & 0x0f); indio_dev->name = client->name; indio_dev->channels = tsl2563_channels; indio_dev->num_channels = ARRAY_SIZE(tsl2563_channels); @@ -748,7 +748,7 @@ static int tsl2563_probe(struct i2c_client *client) irq_flags = IRQF_TRIGGER_RISING; irq_flags |= IRQF_ONESHOT; - err = devm_request_threaded_irq(&client->dev, client->irq, + err = devm_request_threaded_irq(dev, client->irq, NULL, &tsl2563_event_handler, irq_flags, -- cgit v1.2.3 From 571b97fd8777234fcd1f94ed36781bed39f1e20d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 7 Dec 2022 21:03:46 +0200 Subject: iio: light: tsl2563: Use dev_get_drvdata() directly in PM callbacks PM callbacks take struct device pointer as a parameter, use dev_get_drvdata() to retrieve it instead of unneeded double loop of referencing via i2c_get_clientdata(to_i2c_client(dev)). Signed-off-by: Andy Shevchenko Tested-by: Ferry Toth Link: https://lore.kernel.org/r/20221207190348.9347-9-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/light/tsl2563.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/light/tsl2563.c b/drivers/iio/light/tsl2563.c index c5814545fd19..f92190fadffd 100644 --- a/drivers/iio/light/tsl2563.c +++ b/drivers/iio/light/tsl2563.c @@ -795,7 +795,7 @@ static void tsl2563_remove(struct i2c_client *client) static int tsl2563_suspend(struct device *dev) { - struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct iio_dev *indio_dev = dev_get_drvdata(dev); struct tsl2563_chip *chip = iio_priv(indio_dev); int ret; @@ -814,7 +814,7 @@ out: static int tsl2563_resume(struct device *dev) { - struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct iio_dev *indio_dev = dev_get_drvdata(dev); struct tsl2563_chip *chip = iio_priv(indio_dev); int ret; -- cgit v1.2.3 From 3582a83b1f338d9be7b4461ff2ed8033d2ff9c6d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 7 Dec 2022 21:03:47 +0200 Subject: iio: light: tsl2563: sort header inclusion alphabetically Sort header inclusion alphabetically. Signed-off-by: Andy Shevchenko Tested-by: Ferry Toth Link: https://lore.kernel.org/r/20221207190348.9347-10-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/light/tsl2563.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/light/tsl2563.c b/drivers/iio/light/tsl2563.c index f92190fadffd..f2f55239a072 100644 --- a/drivers/iio/light/tsl2563.c +++ b/drivers/iio/light/tsl2563.c @@ -12,23 +12,23 @@ */ #include -#include -#include -#include +#include +#include #include #include #include -#include #include +#include +#include #include -#include #include -#include +#include +#include #include +#include #include #include -#include /* Use this many bits for fraction part. */ #define ADC_FRAC_BITS 14 -- cgit v1.2.3 From c12f0148f14bcac79c100e1eff9309744eed6684 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 7 Dec 2022 21:03:48 +0200 Subject: iio: light: tsl2563: Keep Makefile sorted by module name The Makefile is sorted by a module name, keep it that way. Signed-off-by: Andy Shevchenko Tested-by: Ferry Toth Link: https://lore.kernel.org/r/20221207190348.9347-11-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/light/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index 6f23817fae6f..d74d2b5ff14c 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -39,7 +39,6 @@ obj-$(CONFIG_NOA1305) += noa1305.o obj-$(CONFIG_OPT3001) += opt3001.o obj-$(CONFIG_PA12203001) += pa12203001.o obj-$(CONFIG_RPR0521) += rpr0521.o -obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o obj-$(CONFIG_SI1133) += si1133.o obj-$(CONFIG_SI1145) += si1145.o obj-$(CONFIG_STK3310) += stk3310.o @@ -48,6 +47,7 @@ obj-$(CONFIG_ST_UVIS25_I2C) += st_uvis25_i2c.o obj-$(CONFIG_ST_UVIS25_SPI) += st_uvis25_spi.o obj-$(CONFIG_TCS3414) += tcs3414.o obj-$(CONFIG_TCS3472) += tcs3472.o +obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o obj-$(CONFIG_TSL2583) += tsl2583.o obj-$(CONFIG_TSL2591) += tsl2591.o obj-$(CONFIG_TSL2772) += tsl2772.o -- cgit v1.2.3 From 5d1c74b155cb40606dce6f8db2f79537c11fcd71 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 9 Dec 2022 23:32:02 +0000 Subject: iio: imu: kmx61: Fix spelling mistake "Errow" -> "Error" There is a spelling mistake in a dev_err message. Fix it. Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20221209233202.222083-1-colin.i.king@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/imu/kmx61.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/imu/kmx61.c b/drivers/iio/imu/kmx61.c index e692dfeeda44..53ba020fa5d0 100644 --- a/drivers/iio/imu/kmx61.c +++ b/drivers/iio/imu/kmx61.c @@ -649,7 +649,7 @@ static int kmx61_chip_update_thresholds(struct kmx61_data *data) KMX61_REG_WUF_TIMER, data->wake_duration); if (ret < 0) { - dev_err(&data->client->dev, "Errow writing reg_wuf_timer\n"); + dev_err(&data->client->dev, "Error writing reg_wuf_timer\n"); return ret; } -- cgit v1.2.3 From 04104842736fb29d2e1b5c71dd93f87f428506f0 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Fri, 16 Dec 2022 20:09:45 +0100 Subject: iio: adc: qcom-spmi-adc5: Suppress probe-deferral error message Much like 807efb7102e8 ("thermal: qcom-spmi-adc-tm5: suppress probe-deferral error message") the ADC5 driver also spams a similar probe-deferral error on startup when a channel is not yet available: [ 0.343136] qcom-spmi-adc-tm5 1c40000.spmi:pmic@0:adc-tm@3500: get dt data failed: -517 Suppress it by using dev_err_probe instead, which also takes care of storing the message as reason for deferring. Signed-off-by: Marijn Suijten Reviewed-by: Martin Botka Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20221216190945.902754-1-marijn.suijten@somainline.org Signed-off-by: Jonathan Cameron --- drivers/iio/adc/qcom-spmi-adc5.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c index 821fee60a765..69cc36004b5a 100644 --- a/drivers/iio/adc/qcom-spmi-adc5.c +++ b/drivers/iio/adc/qcom-spmi-adc5.c @@ -894,10 +894,8 @@ static int adc5_probe(struct platform_device *pdev) mutex_init(&adc->lock); ret = adc5_get_fw_data(adc); - if (ret) { - dev_err(dev, "adc get dt data failed\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "adc get dt data failed\n"); irq_eoc = platform_get_irq(pdev, 0); if (irq_eoc < 0) { -- cgit v1.2.3 From 80cbddf59009933274989cb39d52eb8f0a43839f Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Fri, 23 Dec 2022 17:26:36 +0100 Subject: iio: adc: ep93xx: Add OF support Prepare for EP93xx conversion to DT. Signed-off-by: Alexander Sverdlin Link: https://lore.kernel.org/r/20221223162636.6488-2-alexander.sverdlin@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ep93xx_adc.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ep93xx_adc.c b/drivers/iio/adc/ep93xx_adc.c index fd5a9404c8dc..a35e6cead67d 100644 --- a/drivers/iio/adc/ep93xx_adc.c +++ b/drivers/iio/adc/ep93xx_adc.c @@ -21,6 +21,7 @@ #include #include #include +#include /* * This code could benefit from real HR Timers, but jiffy granularity would @@ -227,9 +228,16 @@ static int ep93xx_adc_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id ep93xx_adc_of_ids[] = { + { .compatible = "cirrus,ep9301-adc" }, + {} +}; +MODULE_DEVICE_TABLE(of, ep93xx_adc_of_ids); + static struct platform_driver ep93xx_adc_driver = { .driver = { .name = "ep93xx-adc", + .of_match_table = ep93xx_adc_of_ids, }, .probe = ep93xx_adc_probe, .remove = ep93xx_adc_remove, -- cgit v1.2.3 From b7b81d1c65bffb759c68d04b5e5450cf4e301f62 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sat, 31 Dec 2022 14:41:28 +0000 Subject: iio: adc: ep93xx: Enable wider build testing with COMPILE_TEST It is useful to be able to build this driver without needing to build support for ARCH_EP93XX. Also add an explicit dependency on HAS_IOMEM so we have devm_platform_ioremap_resource() etc available. Signed-off-by: Jonathan Cameron Cc: Alexander Sverdlin Cc: Hartley Sweeten Acked-by: Alexander Sverdlin Link: https://lore.kernel.org/r/20221231144128.489810-1-jic23@kernel.org --- drivers/iio/adc/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 46c4fc2fc534..7d6e74189a2d 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -441,7 +441,8 @@ config ENVELOPE_DETECTOR config EP93XX_ADC tristate "Cirrus Logic EP93XX ADC driver" - depends on ARCH_EP93XX + depends on ARCH_EP93XX || COMPILE_TEST + depends on HAS_IOMEM help Driver for the ADC module on the EP93XX series of SoC from Cirrus Logic. It's recommended to switch on CONFIG_HIGH_RES_TIMERS option, in this -- cgit v1.2.3 From d5f0da0c6972199a39d692309d9e0e09c57a3844 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 2 Jan 2023 19:34:48 +0200 Subject: iio: adc: ti-adc128s052: Switch to use spi_get_device_match_data() The spi_get_device_match_data() helps to get driver data from the firmware node or SPI ID table. Use it instead of open coding. While at it, switch ID tables to provide an acrual pointers to the configuration data. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230102173450.29882-2-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-adc128s052.c | 43 +++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 23 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ti-adc128s052.c b/drivers/iio/adc/ti-adc128s052.c index b3d5b9b7255b..4a15b6bea310 100644 --- a/drivers/iio/adc/ti-adc128s052.c +++ b/drivers/iio/adc/ti-adc128s052.c @@ -139,16 +139,11 @@ static void adc128_disable_regulator(void *reg) static int adc128_probe(struct spi_device *spi) { + const struct adc128_configuration *config; struct iio_dev *indio_dev; - unsigned int config; struct adc128 *adc; int ret; - if (dev_fwnode(&spi->dev)) - config = (unsigned long) device_get_match_data(&spi->dev); - else - config = spi_get_device_id(spi)->driver_data; - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc)); if (!indio_dev) return -ENOMEM; @@ -160,8 +155,10 @@ static int adc128_probe(struct spi_device *spi) indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &adc128_info; - indio_dev->channels = adc128_config[config].channels; - indio_dev->num_channels = adc128_config[config].num_channels; + config = spi_get_device_match_data(spi); + + indio_dev->channels = config->channels; + indio_dev->num_channels = config->num_channels; adc->reg = devm_regulator_get(&spi->dev, "vref"); if (IS_ERR(adc->reg)) @@ -181,32 +178,32 @@ static int adc128_probe(struct spi_device *spi) } static const struct of_device_id adc128_of_match[] = { - { .compatible = "ti,adc128s052", .data = (void*)0L, }, - { .compatible = "ti,adc122s021", .data = (void*)1L, }, - { .compatible = "ti,adc122s051", .data = (void*)1L, }, - { .compatible = "ti,adc122s101", .data = (void*)1L, }, - { .compatible = "ti,adc124s021", .data = (void*)2L, }, - { .compatible = "ti,adc124s051", .data = (void*)2L, }, - { .compatible = "ti,adc124s101", .data = (void*)2L, }, + { .compatible = "ti,adc128s052", .data = &adc128_config[0] }, + { .compatible = "ti,adc122s021", .data = &adc128_config[1] }, + { .compatible = "ti,adc122s051", .data = &adc128_config[1] }, + { .compatible = "ti,adc122s101", .data = &adc128_config[1] }, + { .compatible = "ti,adc124s021", .data = &adc128_config[2] }, + { .compatible = "ti,adc124s051", .data = &adc128_config[2] }, + { .compatible = "ti,adc124s101", .data = &adc128_config[2] }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, adc128_of_match); static const struct spi_device_id adc128_id[] = { - { "adc128s052", 0 }, /* index into adc128_config */ - { "adc122s021", 1 }, - { "adc122s051", 1 }, - { "adc122s101", 1 }, - { "adc124s021", 2 }, - { "adc124s051", 2 }, - { "adc124s101", 2 }, + { "adc128s052", (kernel_ulong_t)&adc128_config[0] }, + { "adc122s021", (kernel_ulong_t)&adc128_config[1] }, + { "adc122s051", (kernel_ulong_t)&adc128_config[1] }, + { "adc122s101", (kernel_ulong_t)&adc128_config[1] }, + { "adc124s021", (kernel_ulong_t)&adc128_config[2] }, + { "adc124s051", (kernel_ulong_t)&adc128_config[2] }, + { "adc124s101", (kernel_ulong_t)&adc128_config[2] }, { } }; MODULE_DEVICE_TABLE(spi, adc128_id); #ifdef CONFIG_ACPI static const struct acpi_device_id adc128_acpi_match[] = { - { "AANT1280", 2 }, /* ADC124S021 compatible ACPI ID */ + { "AANT1280", (kernel_ulong_t)&adc128_config[2] }, { } }; MODULE_DEVICE_TABLE(acpi, adc128_acpi_match); -- cgit v1.2.3 From c87d5e94fca8e2fc681a808608bddfb01878f791 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 2 Jan 2023 19:34:49 +0200 Subject: iio: adc: ti-adc128s052: Drop anti-pattern of ACPI_PTR() use ACPI_PTR() is more harmful than helpful. For example, in this case if CONFIG_ACPI=n, the ID table left unused and code is obfuscated by ifdeffery. Drop anti-pattern of ACPI_PTR() use. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230102173450.29882-3-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-adc128s052.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ti-adc128s052.c b/drivers/iio/adc/ti-adc128s052.c index 4a15b6bea310..0f737e9df0fa 100644 --- a/drivers/iio/adc/ti-adc128s052.c +++ b/drivers/iio/adc/ti-adc128s052.c @@ -9,7 +9,6 @@ * https://www.ti.com/lit/ds/symlink/adc124s021.pdf */ -#include #include #include #include @@ -201,19 +200,17 @@ static const struct spi_device_id adc128_id[] = { }; MODULE_DEVICE_TABLE(spi, adc128_id); -#ifdef CONFIG_ACPI static const struct acpi_device_id adc128_acpi_match[] = { { "AANT1280", (kernel_ulong_t)&adc128_config[2] }, { } }; MODULE_DEVICE_TABLE(acpi, adc128_acpi_match); -#endif static struct spi_driver adc128_driver = { .driver = { .name = "adc128s052", .of_match_table = adc128_of_match, - .acpi_match_table = ACPI_PTR(adc128_acpi_match), + .acpi_match_table = adc128_acpi_match, }, .probe = adc128_probe, .id_table = adc128_id, -- cgit v1.2.3 From 4b570fe0d4c9f326fb87b1c58abebb8f0376eaa9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 2 Jan 2023 19:34:50 +0200 Subject: iio: adc: ti-adc128s052: Sort headers Sort the headers in alphabetic order in order to ease the maintenance for this part. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230102173450.29882-4-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-adc128s052.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ti-adc128s052.c b/drivers/iio/adc/ti-adc128s052.c index 0f737e9df0fa..a456ea78462f 100644 --- a/drivers/iio/adc/ti-adc128s052.c +++ b/drivers/iio/adc/ti-adc128s052.c @@ -10,12 +10,12 @@ */ #include -#include -#include -#include #include +#include +#include #include #include +#include struct adc128_configuration { const struct iio_chan_spec *channels; -- cgit v1.2.3 From 591a6bc7f40d3ea6492527f595f1588e127d0b54 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 3 Jan 2023 16:49:03 +0200 Subject: iio: adc: max11410: Use asm intead of asm-generic There is no point to specify asm-generic for the unaligned.h. Drop the 'generic' suffix and move the inclusion to be after the non-IIO linux/* ones. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230103144903.39905-1-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max11410.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/max11410.c b/drivers/iio/adc/max11410.c index fdc9f03135b5..b74b689ee7de 100644 --- a/drivers/iio/adc/max11410.c +++ b/drivers/iio/adc/max11410.c @@ -4,7 +4,6 @@ * * Copyright 2022 Analog Devices Inc. */ -#include #include #include #include @@ -16,6 +15,8 @@ #include #include +#include + #include #include #include -- cgit v1.2.3 From eae3e6f1c86f4499325cdaa3df4b90cd1f32464c Mon Sep 17 00:00:00 2001 From: Alexander Vorwerk Date: Thu, 5 Jan 2023 03:53:51 +0100 Subject: drivers: iio: cdc: Drop redundant if check Reported-by: kernel test robot Signed-off-by: Alexander Vorwerk Link: https://lore.kernel.org/r/20230105025351.12099-1-zabe@avorwerk.net Signed-off-by: Jonathan Cameron --- drivers/iio/cdc/ad7746.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/cdc/ad7746.c b/drivers/iio/cdc/ad7746.c index 6f68651ce1d5..a1db5469f2d1 100644 --- a/drivers/iio/cdc/ad7746.c +++ b/drivers/iio/cdc/ad7746.c @@ -285,8 +285,7 @@ static int ad7746_select_channel(struct iio_dev *indio_dev, if (ret < 0) return ret; - if (chip->capdac_set != chan->channel) - chip->capdac_set = chan->channel; + chip->capdac_set = chan->channel; break; case IIO_VOLTAGE: case IIO_TEMP: -- cgit v1.2.3 From 23fd6f0bd6cbf010216a078a99dcbaa30c8bb5ae Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Fri, 6 Jan 2023 11:22:38 +0100 Subject: iio: accel: st_accel: Add LSM303C The accelerometer part of ST LSM303C is similar (perhaps even identical) to the already supported standalone LIS2HH12 accelerometer, so just add the new st,lsm303c-accel compatible for the existing definitions. Signed-off-by: Stephan Gerhold Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20230106102239.9647-3-stephan@gerhold.net Signed-off-by: Jonathan Cameron --- drivers/iio/accel/Kconfig | 2 +- drivers/iio/accel/st_accel.h | 1 + drivers/iio/accel/st_accel_core.c | 1 + drivers/iio/accel/st_accel_i2c.c | 5 +++++ drivers/iio/accel/st_accel_spi.c | 5 +++++ 5 files changed, 13 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index 03ac410c162e..b6b45d359f28 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -380,7 +380,7 @@ config IIO_ST_ACCEL_3AXIS select IIO_TRIGGERED_BUFFER if (IIO_BUFFER) help Say yes here to build support for STMicroelectronics accelerometers: - LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, + LSM303C, LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LIS331DLH, LSM303DL, LSM303DLM, LSM330, LIS2DH12, H3LIS331DL, LNG2DM, LIS3DE, LIS2DE12, LIS2HH12 diff --git a/drivers/iio/accel/st_accel.h b/drivers/iio/accel/st_accel.h index 5b0f54e33d9e..56ed0c776d4a 100644 --- a/drivers/iio/accel/st_accel.h +++ b/drivers/iio/accel/st_accel.h @@ -37,6 +37,7 @@ #define LIS2DE12_ACCEL_DEV_NAME "lis2de12" #define LIS2HH12_ACCEL_DEV_NAME "lis2hh12" #define LIS302DL_ACCEL_DEV_NAME "lis302dl" +#define LSM303C_ACCEL_DEV_NAME "lsm303c_accel" #define SC7A20_ACCEL_DEV_NAME "sc7a20" diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index c8c8eb15c34e..6b8562f684d5 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -929,6 +929,7 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS, .sensors_supported = { [0] = LIS2HH12_ACCEL_DEV_NAME, + [1] = LSM303C_ACCEL_DEV_NAME, }, .ch = (struct iio_chan_spec *)st_accel_16bit_channels, .odr = { diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c index 45ee0ddc133c..3f02fd5d5946 100644 --- a/drivers/iio/accel/st_accel_i2c.c +++ b/drivers/iio/accel/st_accel_i2c.c @@ -111,6 +111,10 @@ static const struct of_device_id st_accel_of_match[] = { .compatible = "st,lis302dl", .data = LIS302DL_ACCEL_DEV_NAME, }, + { + .compatible = "st,lsm303c-accel", + .data = LSM303C_ACCEL_DEV_NAME, + }, { .compatible = "silan,sc7a20", .data = SC7A20_ACCEL_DEV_NAME, @@ -151,6 +155,7 @@ static const struct i2c_device_id st_accel_id_table[] = { { LIS2DE12_ACCEL_DEV_NAME }, { LIS2HH12_ACCEL_DEV_NAME }, { LIS302DL_ACCEL_DEV_NAME }, + { LSM303C_ACCEL_DEV_NAME }, { SC7A20_ACCEL_DEV_NAME }, {}, }; diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c index 6c0917750288..5740dc1820bd 100644 --- a/drivers/iio/accel/st_accel_spi.c +++ b/drivers/iio/accel/st_accel_spi.c @@ -96,6 +96,10 @@ static const struct of_device_id st_accel_of_match[] = { .compatible = "st,lis302dl", .data = LIS302DL_ACCEL_DEV_NAME, }, + { + .compatible = "st,lsm303c-accel", + .data = LSM303C_ACCEL_DEV_NAME, + }, {} }; MODULE_DEVICE_TABLE(of, st_accel_of_match); @@ -152,6 +156,7 @@ static const struct spi_device_id st_accel_id_table[] = { { LIS3DHH_ACCEL_DEV_NAME }, { LIS3DE_ACCEL_DEV_NAME }, { LIS302DL_ACCEL_DEV_NAME }, + { LSM303C_ACCEL_DEV_NAME }, {}, }; MODULE_DEVICE_TABLE(spi, st_accel_id_table); -- cgit v1.2.3 From 721a2a7edcdd10f55ef7d3cf74fc276b0d4177e8 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Fri, 6 Jan 2023 11:22:39 +0100 Subject: iio: magnetometer: st_magn: Add LSM303C The magnetometer part of ST LSM303C is similar (perhaps even identical) to the already supported standalone LIS3MDL magnetometer, so just add the new st,lsm303c-magn compatible for the existing definitions. Signed-off-by: Stephan Gerhold Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20230106102239.9647-4-stephan@gerhold.net Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/Kconfig | 2 +- drivers/iio/magnetometer/st_magn.h | 1 + drivers/iio/magnetometer/st_magn_core.c | 1 + drivers/iio/magnetometer/st_magn_i2c.c | 5 +++++ drivers/iio/magnetometer/st_magn_spi.c | 5 +++++ 5 files changed, 13 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig index 467819335588..38532d840f2a 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -119,7 +119,7 @@ config IIO_ST_MAGN_3AXIS select IIO_TRIGGERED_BUFFER if (IIO_BUFFER) help Say yes here to build support for STMicroelectronics magnetometers: - LSM303DLHC, LSM303DLM, LIS3MDL. + LSM303C, LSM303DLHC, LSM303DLM, LIS3MDL. Also need to enable at least one of I2C and SPI interface drivers below. diff --git a/drivers/iio/magnetometer/st_magn.h b/drivers/iio/magnetometer/st_magn.h index 785b7f7b8b06..89945984d966 100644 --- a/drivers/iio/magnetometer/st_magn.h +++ b/drivers/iio/magnetometer/st_magn.h @@ -22,6 +22,7 @@ #define LIS2MDL_MAGN_DEV_NAME "lis2mdl" #define LSM9DS1_MAGN_DEV_NAME "lsm9ds1_magn" #define IIS2MDC_MAGN_DEV_NAME "iis2mdc" +#define LSM303C_MAGN_DEV_NAME "lsm303c_magn" #ifdef CONFIG_IIO_BUFFER int st_magn_allocate_ring(struct iio_dev *indio_dev); diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c index e2fd233b3626..8faa7409d9e1 100644 --- a/drivers/iio/magnetometer/st_magn_core.c +++ b/drivers/iio/magnetometer/st_magn_core.c @@ -305,6 +305,7 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = { .sensors_supported = { [0] = LIS3MDL_MAGN_DEV_NAME, [1] = LSM9DS1_MAGN_DEV_NAME, + [2] = LSM303C_MAGN_DEV_NAME, }, .ch = (struct iio_chan_spec *)st_magn_2_16bit_channels, .odr = { diff --git a/drivers/iio/magnetometer/st_magn_i2c.c b/drivers/iio/magnetometer/st_magn_i2c.c index b4098d3b3813..cc0e0e94b129 100644 --- a/drivers/iio/magnetometer/st_magn_i2c.c +++ b/drivers/iio/magnetometer/st_magn_i2c.c @@ -50,6 +50,10 @@ static const struct of_device_id st_magn_of_match[] = { .compatible = "st,iis2mdc", .data = IIS2MDC_MAGN_DEV_NAME, }, + { + .compatible = "st,lsm303c-magn", + .data = LSM303C_MAGN_DEV_NAME, + }, {}, }; MODULE_DEVICE_TABLE(of, st_magn_of_match); @@ -97,6 +101,7 @@ static const struct i2c_device_id st_magn_id_table[] = { { LIS2MDL_MAGN_DEV_NAME }, { LSM9DS1_MAGN_DEV_NAME }, { IIS2MDC_MAGN_DEV_NAME }, + { LSM303C_MAGN_DEV_NAME }, {}, }; MODULE_DEVICE_TABLE(i2c, st_magn_id_table); diff --git a/drivers/iio/magnetometer/st_magn_spi.c b/drivers/iio/magnetometer/st_magn_spi.c index 6ddc4318564a..f203e1f87eec 100644 --- a/drivers/iio/magnetometer/st_magn_spi.c +++ b/drivers/iio/magnetometer/st_magn_spi.c @@ -45,6 +45,10 @@ static const struct of_device_id st_magn_of_match[] = { .compatible = "st,iis2mdc", .data = IIS2MDC_MAGN_DEV_NAME, }, + { + .compatible = "st,lsm303c-magn", + .data = LSM303C_MAGN_DEV_NAME, + }, {} }; MODULE_DEVICE_TABLE(of, st_magn_of_match); @@ -89,6 +93,7 @@ static const struct spi_device_id st_magn_id_table[] = { { LIS2MDL_MAGN_DEV_NAME }, { LSM9DS1_MAGN_DEV_NAME }, { IIS2MDC_MAGN_DEV_NAME }, + { LSM303C_MAGN_DEV_NAME }, {}, }; MODULE_DEVICE_TABLE(spi, st_magn_id_table); -- cgit v1.2.3 From 14e5b7ab196e6f18f76191cae4a4f6b41c0bfadd Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Fri, 6 Jan 2023 16:39:41 +0100 Subject: iio: adc: qcom-spmi-adc5: define ADC5_BAT_ID_100K_PU channel Define the ADC channel used for battery identification purposes so it can be used in drivers. Signed-off-by: Luca Weiss Link: https://lore.kernel.org/r/20230106-pm7250b-bat_id-v1-1-82ca8f2db741@fairphone.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/qcom-spmi-adc5.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c index 69cc36004b5a..e90c299c913a 100644 --- a/drivers/iio/adc/qcom-spmi-adc5.c +++ b/drivers/iio/adc/qcom-spmi-adc5.c @@ -543,6 +543,8 @@ static const struct adc5_channels adc5_chans_pmic[ADC5_MAX_CHANNEL] = { SCALE_HW_CALIB_DEFAULT) [ADC5_XO_THERM_100K_PU] = ADC5_CHAN_TEMP("xo_therm", 0, SCALE_HW_CALIB_XOTHERM) + [ADC5_BAT_ID_100K_PU] = ADC5_CHAN_TEMP("bat_id", 0, + SCALE_HW_CALIB_DEFAULT) [ADC5_AMUX_THM1_100K_PU] = ADC5_CHAN_TEMP("amux_thm1_100k_pu", 0, SCALE_HW_CALIB_THERM_100K_PULLUP) [ADC5_AMUX_THM2_100K_PU] = ADC5_CHAN_TEMP("amux_thm2_100k_pu", 0, -- cgit v1.2.3 From bfcae956d9b50ea0e221cefc171604c569017d7e Mon Sep 17 00:00:00 2001 From: Olivier Moysan Date: Thu, 5 Jan 2023 13:53:30 +0100 Subject: iio: adc: stm32-dfsdm: add id registers support Add support of identification registers to STM32 DFSDM to allow hardware capabilities discovery and configuration check. The number of filters and channels, are read from registers, when they are available. Signed-off-by: Olivier Moysan Link: https://lore.kernel.org/r/20230105125331.328275-1-olivier.moysan@foss.st.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-dfsdm-core.c | 99 ++++++++++++++++++++++++++++++++------ drivers/iio/adc/stm32-dfsdm.h | 60 +++++++++++++++-------- 2 files changed, 124 insertions(+), 35 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c index a3d4de6ba4c2..0362df285a57 100644 --- a/drivers/iio/adc/stm32-dfsdm-core.c +++ b/drivers/iio/adc/stm32-dfsdm-core.c @@ -6,6 +6,7 @@ * Author(s): Arnaud Pouliquen for STMicroelectronics. */ +#include #include #include #include @@ -19,7 +20,15 @@ #include "stm32-dfsdm.h" +/** + * struct stm32_dfsdm_dev_data - DFSDM compatible configuration data + * @ipid: DFSDM identification number. Used only if hardware provides identification registers + * @num_filters: DFSDM number of filters. Unused if identification registers are available + * @num_channels: DFSDM number of channels. Unused if identification registers are available + * @regmap_cfg: SAI register map configuration pointer + */ struct stm32_dfsdm_dev_data { + u32 ipid; unsigned int num_filters; unsigned int num_channels; const struct regmap_config *regmap_cfg; @@ -27,8 +36,6 @@ struct stm32_dfsdm_dev_data { #define STM32H7_DFSDM_NUM_FILTERS 4 #define STM32H7_DFSDM_NUM_CHANNELS 8 -#define STM32MP1_DFSDM_NUM_FILTERS 6 -#define STM32MP1_DFSDM_NUM_CHANNELS 8 static bool stm32_dfsdm_volatile_reg(struct device *dev, unsigned int reg) { @@ -75,8 +82,7 @@ static const struct regmap_config stm32mp1_dfsdm_regmap_cfg = { }; static const struct stm32_dfsdm_dev_data stm32mp1_dfsdm_data = { - .num_filters = STM32MP1_DFSDM_NUM_FILTERS, - .num_channels = STM32MP1_DFSDM_NUM_CHANNELS, + .ipid = STM32MP15_IPIDR_NUMBER, .regmap_cfg = &stm32mp1_dfsdm_regmap_cfg, }; @@ -295,6 +301,65 @@ static const struct of_device_id stm32_dfsdm_of_match[] = { }; MODULE_DEVICE_TABLE(of, stm32_dfsdm_of_match); +static int stm32_dfsdm_probe_identification(struct platform_device *pdev, + struct dfsdm_priv *priv, + const struct stm32_dfsdm_dev_data *dev_data) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *child; + struct stm32_dfsdm *dfsdm = &priv->dfsdm; + const char *compat; + int ret, count = 0; + u32 id, val; + + if (!dev_data->ipid) { + dfsdm->num_fls = dev_data->num_filters; + dfsdm->num_chs = dev_data->num_channels; + return 0; + } + + ret = regmap_read(dfsdm->regmap, DFSDM_IPIDR, &id); + if (ret) + return ret; + + if (id != dev_data->ipid) { + dev_err(&pdev->dev, "Unexpected IP version: 0x%x", id); + return -EINVAL; + } + + for_each_child_of_node(np, child) { + ret = of_property_read_string(child, "compatible", &compat); + if (ret) + continue; + /* Count only child nodes with dfsdm compatible */ + if (strstr(compat, "dfsdm")) + count++; + } + + ret = regmap_read(dfsdm->regmap, DFSDM_HWCFGR, &val); + if (ret) + return ret; + + dfsdm->num_fls = FIELD_GET(DFSDM_HWCFGR_NBF_MASK, val); + dfsdm->num_chs = FIELD_GET(DFSDM_HWCFGR_NBT_MASK, val); + + if (count > dfsdm->num_fls) { + dev_err(&pdev->dev, "Unexpected child number: %d", count); + return -EINVAL; + } + + ret = regmap_read(dfsdm->regmap, DFSDM_VERR, &val); + if (ret) + return ret; + + dev_dbg(&pdev->dev, "DFSDM version: %lu.%lu. %d channels/%d filters\n", + FIELD_GET(DFSDM_VERR_MAJREV_MASK, val), + FIELD_GET(DFSDM_VERR_MINREV_MASK, val), + dfsdm->num_chs, dfsdm->num_fls); + + return 0; +} + static int stm32_dfsdm_probe(struct platform_device *pdev) { struct dfsdm_priv *priv; @@ -311,18 +376,6 @@ static int stm32_dfsdm_probe(struct platform_device *pdev) dev_data = of_device_get_match_data(&pdev->dev); dfsdm = &priv->dfsdm; - dfsdm->fl_list = devm_kcalloc(&pdev->dev, dev_data->num_filters, - sizeof(*dfsdm->fl_list), GFP_KERNEL); - if (!dfsdm->fl_list) - return -ENOMEM; - - dfsdm->num_fls = dev_data->num_filters; - dfsdm->ch_list = devm_kcalloc(&pdev->dev, dev_data->num_channels, - sizeof(*dfsdm->ch_list), - GFP_KERNEL); - if (!dfsdm->ch_list) - return -ENOMEM; - dfsdm->num_chs = dev_data->num_channels; ret = stm32_dfsdm_parse_of(pdev, priv); if (ret < 0) @@ -338,6 +391,20 @@ static int stm32_dfsdm_probe(struct platform_device *pdev) return ret; } + ret = stm32_dfsdm_probe_identification(pdev, priv, dev_data); + if (ret < 0) + return ret; + + dfsdm->fl_list = devm_kcalloc(&pdev->dev, dfsdm->num_fls, + sizeof(*dfsdm->fl_list), GFP_KERNEL); + if (!dfsdm->fl_list) + return -ENOMEM; + + dfsdm->ch_list = devm_kcalloc(&pdev->dev, dfsdm->num_chs, + sizeof(*dfsdm->ch_list), GFP_KERNEL); + if (!dfsdm->ch_list) + return -ENOMEM; + platform_set_drvdata(pdev, dfsdm); ret = stm32_dfsdm_clk_prepare_enable(dfsdm); diff --git a/drivers/iio/adc/stm32-dfsdm.h b/drivers/iio/adc/stm32-dfsdm.h index 4afc1f528b78..570a1552aec4 100644 --- a/drivers/iio/adc/stm32-dfsdm.h +++ b/drivers/iio/adc/stm32-dfsdm.h @@ -13,25 +13,29 @@ /* * STM32 DFSDM - global register map - * ________________________________________________________ - * | Offset | Registers block | - * -------------------------------------------------------- - * | 0x000 | CHANNEL 0 + COMMON CHANNEL FIELDS | - * -------------------------------------------------------- - * | 0x020 | CHANNEL 1 | - * -------------------------------------------------------- - * | ... | ..... | - * -------------------------------------------------------- - * | 0x0E0 | CHANNEL 7 | - * -------------------------------------------------------- - * | 0x100 | FILTER 0 + COMMON FILTER FIELDs | - * -------------------------------------------------------- - * | 0x200 | FILTER 1 | - * -------------------------------------------------------- - * | 0x300 | FILTER 2 | - * -------------------------------------------------------- - * | 0x400 | FILTER 3 | - * -------------------------------------------------------- + * __________________________________________________________ + * | Offset | Registers block | + * ---------------------------------------------------------- + * | 0x000 | CHANNEL 0 + COMMON CHANNEL FIELDS | + * ---------------------------------------------------------- + * | 0x020 | CHANNEL 1 | + * ---------------------------------------------------------- + * | ... | ..... | + * ---------------------------------------------------------- + * | 0x20 x n | CHANNEL n | + * ---------------------------------------------------------- + * | 0x100 | FILTER 0 + COMMON FILTER FIELDs | + * ---------------------------------------------------------- + * | 0x200 | FILTER 1 | + * ---------------------------------------------------------- + * | | ..... | + * ---------------------------------------------------------- + * | 0x100 x m | FILTER m | + * ---------------------------------------------------------- + * | | ..... | + * ---------------------------------------------------------- + * | 0x7F0-7FC | Identification registers | + * ---------------------------------------------------------- */ /* @@ -231,6 +235,24 @@ #define DFSDM_AWCFR_AWHTF_MASK GENMASK(15, 8) #define DFSDM_AWCFR_AWHTF(v) FIELD_PREP(DFSDM_AWCFR_AWHTF_MASK, v) +/* + * Identification register definitions + */ +#define DFSDM_HWCFGR 0x7F0 +#define DFSDM_VERR 0x7F4 +#define DFSDM_IPIDR 0x7F8 +#define DFSDM_SIDR 0x7FC + +/* HWCFGR: Hardware configuration register */ +#define DFSDM_HWCFGR_NBT_MASK GENMASK(7, 0) +#define DFSDM_HWCFGR_NBF_MASK GENMASK(15, 8) + +/* VERR: Version register */ +#define DFSDM_VERR_MINREV_MASK GENMASK(3, 0) +#define DFSDM_VERR_MAJREV_MASK GENMASK(7, 4) + +#define STM32MP15_IPIDR_NUMBER 0x00110031 + /* DFSDM filter order */ enum stm32_dfsdm_sinc_order { DFSDM_FASTSINC_ORDER, /* FastSinc filter type */ -- cgit v1.2.3 From ebda75fbfa95ea825557325962f1ed6724d557d0 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 13 Jan 2023 16:11:17 +0200 Subject: iio: chemical: scd30_core: use sysfs_emit() to instead of scnprintf() Follow the advice of the Documentation/filesystems/sysfs.rst and show() should only use sysfs_emit() or sysfs_emit_at() when formatting the value to be returned to user space. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230113141117.23353-1-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/scd30_core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/chemical/scd30_core.c b/drivers/iio/chemical/scd30_core.c index 682fca39d14d..e0bb1dd5e790 100644 --- a/drivers/iio/chemical/scd30_core.c +++ b/drivers/iio/chemical/scd30_core.c @@ -354,7 +354,7 @@ static ssize_t sampling_frequency_available_show(struct device *dev, struct devi ssize_t len = 0; do { - len += scnprintf(buf + len, PAGE_SIZE - len, "0.%09u ", 1000000000 / i); + len += sysfs_emit_at(buf, len, "0.%09u ", 1000000000 / i); /* * Not all values fit PAGE_SIZE buffer hence print every 6th * (each frequency differs by 6s in time domain from the @@ -380,7 +380,7 @@ static ssize_t calibration_auto_enable_show(struct device *dev, struct device_at ret = scd30_command_read(state, CMD_ASC, &val); mutex_unlock(&state->lock); - return ret ?: sprintf(buf, "%d\n", val); + return ret ?: sysfs_emit(buf, "%d\n", val); } static ssize_t calibration_auto_enable_store(struct device *dev, struct device_attribute *attr, @@ -414,7 +414,7 @@ static ssize_t calibration_forced_value_show(struct device *dev, struct device_a ret = scd30_command_read(state, CMD_FRC, &val); mutex_unlock(&state->lock); - return ret ?: sprintf(buf, "%d\n", val); + return ret ?: sysfs_emit(buf, "%d\n", val); } static ssize_t calibration_forced_value_store(struct device *dev, struct device_attribute *attr, -- cgit v1.2.3 From c44e031bcff1fa3483374fba7b053825f57dac8e Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 13 Jan 2023 16:19:17 +0200 Subject: iio: chemical: scd30_core: Switch to use dev_err_probe() Switch to use dev_err_probe() to simplify the error paths and unify message template. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230113141917.23725-1-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/scd30_core.c | 40 ++++++++++++++------------------------- 1 file changed, 14 insertions(+), 26 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/chemical/scd30_core.c b/drivers/iio/chemical/scd30_core.c index e0bb1dd5e790..7be5a45cf71a 100644 --- a/drivers/iio/chemical/scd30_core.c +++ b/drivers/iio/chemical/scd30_core.c @@ -642,10 +642,8 @@ static int scd30_setup_trigger(struct iio_dev *indio_dev) trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, iio_device_id(indio_dev)); - if (!trig) { - dev_err(dev, "failed to allocate trigger\n"); - return -ENOMEM; - } + if (!trig) + return dev_err_probe(dev, -ENOMEM, "failed to allocate trigger\n"); trig->ops = &scd30_trigger_ops; iio_trigger_set_drvdata(trig, indio_dev); @@ -667,9 +665,9 @@ static int scd30_setup_trigger(struct iio_dev *indio_dev) IRQF_NO_AUTOEN, indio_dev->name, indio_dev); if (ret) - dev_err(dev, "failed to request irq\n"); + return dev_err_probe(dev, ret, "failed to request irq\n"); - return ret; + return 0; } int scd30_probe(struct device *dev, int irq, const char *name, void *priv, @@ -717,17 +715,13 @@ int scd30_probe(struct device *dev, int irq, const char *name, void *priv, return ret; ret = scd30_reset(state); - if (ret) { - dev_err(dev, "failed to reset device: %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to reset device\n"); if (state->irq > 0) { ret = scd30_setup_trigger(indio_dev); - if (ret) { - dev_err(dev, "failed to setup trigger: %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to setup trigger\n"); } ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, scd30_trigger_handler, NULL); @@ -735,23 +729,17 @@ int scd30_probe(struct device *dev, int irq, const char *name, void *priv, return ret; ret = scd30_command_read(state, CMD_FW_VERSION, &val); - if (ret) { - dev_err(dev, "failed to read firmware version: %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to read firmware version\n"); dev_info(dev, "firmware version: %d.%d\n", val >> 8, (char)val); ret = scd30_command_write(state, CMD_MEAS_INTERVAL, state->meas_interval); - if (ret) { - dev_err(dev, "failed to set measurement interval: %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to set measurement interval\n"); ret = scd30_command_write(state, CMD_START_MEAS, state->pressure_comp); - if (ret) { - dev_err(dev, "failed to start measurement: %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to start measurement\n"); ret = devm_add_action_or_reset(dev, scd30_stop_meas, state); if (ret) -- cgit v1.2.3 From cd19fbfedca524947d0df9cab820c21237005de2 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 19 Jan 2023 19:47:49 +0200 Subject: iio: adc: xilinx-ams: Convert to use fwnode_device_is_compatible() Replace open coded fwnode_device_is_compatible() in the driver. Signed-off-by: Andy Shevchenko Reviewed-by: Michal Simek Link: https://lore.kernel.org/r/20230119174749.76769-1-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/xilinx-ams.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/xilinx-ams.c b/drivers/iio/adc/xilinx-ams.c index 5b4bdf3a26bb..fde358d388ab 100644 --- a/drivers/iio/adc/xilinx-ams.c +++ b/drivers/iio/adc/xilinx-ams.c @@ -1220,8 +1220,7 @@ static int ams_init_module(struct iio_dev *indio_dev, int num_channels = 0; int ret; - if (fwnode_property_match_string(fwnode, "compatible", - "xlnx,zynqmp-ams-ps") == 0) { + if (fwnode_device_is_compatible(fwnode, "xlnx,zynqmp-ams-ps")) { ams->ps_base = fwnode_iomap(fwnode, 0); if (!ams->ps_base) return -ENXIO; @@ -1232,8 +1231,7 @@ static int ams_init_module(struct iio_dev *indio_dev, /* add PS channels to iio device channels */ memcpy(channels, ams_ps_channels, sizeof(ams_ps_channels)); num_channels = ARRAY_SIZE(ams_ps_channels); - } else if (fwnode_property_match_string(fwnode, "compatible", - "xlnx,zynqmp-ams-pl") == 0) { + } else if (fwnode_device_is_compatible(fwnode, "xlnx,zynqmp-ams-pl")) { ams->pl_base = fwnode_iomap(fwnode, 0); if (!ams->pl_base) return -ENXIO; @@ -1247,8 +1245,7 @@ static int ams_init_module(struct iio_dev *indio_dev, num_channels += AMS_PL_MAX_FIXED_CHANNEL; num_channels = ams_get_ext_chan(fwnode, channels, num_channels); - } else if (fwnode_property_match_string(fwnode, "compatible", - "xlnx,zynqmp-ams") == 0) { + } else if (fwnode_device_is_compatible(fwnode, "xlnx,zynqmp-ams")) { /* add AMS channels to iio device channels */ memcpy(channels, ams_ctrl_channels, sizeof(ams_ctrl_channels)); num_channels += ARRAY_SIZE(ams_ctrl_channels); -- cgit v1.2.3 From 122fb8736efce9a1bab0433ff330f3bf03d4a00e Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 18 Jan 2023 09:48:27 +0200 Subject: iio: core: Replace iio_sysfs_match_string_with_gaps() by __sysfs_match_string() None of the current users is using gaps in the list of the items. No need to have a specific function for that, just replace it by library available __sysfs_match_string(). Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230118074828.66155-1-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 32 +------------------------------- 1 file changed, 1 insertion(+), 31 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 52e690f031cb..26e357f14db8 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -205,36 +205,6 @@ bool iio_buffer_enabled(struct iio_dev *indio_dev) } EXPORT_SYMBOL_GPL(iio_buffer_enabled); -/** - * iio_sysfs_match_string_with_gaps - matches given string in an array with gaps - * @array: array of strings - * @n: number of strings in the array - * @str: string to match with - * - * Returns index of @str in the @array or -EINVAL, similar to match_string(). - * Uses sysfs_streq instead of strcmp for matching. - * - * This routine will look for a string in an array of strings. - * The search will continue until the element is found or the n-th element - * is reached, regardless of any NULL elements in the array. - */ -static int iio_sysfs_match_string_with_gaps(const char * const *array, size_t n, - const char *str) -{ - const char *item; - int index; - - for (index = 0; index < n; index++) { - item = array[index]; - if (!item) - continue; - if (sysfs_streq(item, str)) - return index; - } - - return -EINVAL; -} - #if defined(CONFIG_DEBUG_FS) /* * There's also a CONFIG_DEBUG_FS guard in include/linux/iio/iio.h for @@ -569,7 +539,7 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev, if (!e->set) return -EINVAL; - ret = iio_sysfs_match_string_with_gaps(e->items, e->num_items, buf); + ret = __sysfs_match_string(e->items, e->num_items, buf); if (ret < 0) return ret; -- cgit v1.2.3 From 05f59aba3906ff4c0a70e302711d81043f15114d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 18 Jan 2023 09:48:28 +0200 Subject: iio: core: Sort headers Sort the headers in alphabetic order in order to ease the maintenance for this part. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230118074828.66155-2-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 26e357f14db8..c117f50d0cf3 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -8,30 +8,32 @@ #define pr_fmt(fmt) "iio-core: " fmt -#include -#include -#include -#include -#include +#include +#include +#include #include +#include #include +#include +#include +#include +#include +#include #include #include #include -#include -#include #include -#include -#include -#include -#include +#include + +#include +#include +#include #include +#include +#include + #include "iio_core.h" #include "iio_core_trigger.h" -#include -#include -#include -#include /* IDA to assign each registered device a unique id */ static DEFINE_IDA(iio_ida); -- cgit v1.2.3 From ba56b46d1c6d412ae8c079e67c01a1f7bbd5c079 Mon Sep 17 00:00:00 2001 From: Angelo Dureghello Date: Sun, 6 Nov 2022 17:59:28 +0100 Subject: iio: dac: add support for max5522 Add initial support for DAC max5522. Tested writing DAC A and B with some values, from 0 to 1023, measured output voltages, driver works properly. Additions for max5523/24/25 will follow. Signed-off-by: Angelo Dureghello Link: https://lore.kernel.org/r/20221106165928.223318-1-angelo.dureghello@timesys.com Signed-off-by: Jonathan Cameron --- drivers/iio/dac/Kconfig | 13 +++ drivers/iio/dac/Makefile | 1 + drivers/iio/dac/max5522.c | 207 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 221 insertions(+) create mode 100644 drivers/iio/dac/max5522.c (limited to 'drivers/iio') diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index e83eb75d87d1..d3f90cf86143 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -357,6 +357,19 @@ config MAX517 This driver can also be built as a module. If so, the module will be called max517. +config MAX5522 + tristate "Maxim MAX5522 DAC driver" + depends on SPI_MASTER + select REGMAP_SPI + help + Say Y here if you want to build a driver for the Maxim MAX5522. + + MAX5522 is a dual, ultra-low-power, 10-Bit, voltage-output + digital to analog converter (DAC) offering rail-to-rail buffered + voltage outputs. + + If compiled as a module, it will be called max5522. + config MAX5821 tristate "Maxim MAX5821 DAC driver" depends on I2C diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index ec3e42713f00..6c74fea21736 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_LTC2632) += ltc2632.o obj-$(CONFIG_LTC2688) += ltc2688.o obj-$(CONFIG_M62332) += m62332.o obj-$(CONFIG_MAX517) += max517.o +obj-$(CONFIG_MAX5522) += max5522.o obj-$(CONFIG_MAX5821) += max5821.o obj-$(CONFIG_MCP4725) += mcp4725.o obj-$(CONFIG_MCP4922) += mcp4922.o diff --git a/drivers/iio/dac/max5522.c b/drivers/iio/dac/max5522.c new file mode 100644 index 000000000000..00ba4e98fb9c --- /dev/null +++ b/drivers/iio/dac/max5522.c @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Maxim MAX5522 + * Dual, Ultra-Low-Power 10-Bit, Voltage-Output DACs + * + * Copyright 2022 Timesys Corp. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MAX5522_MAX_ADDR 15 +#define MAX5522_CTRL_NONE 0 +#define MAX5522_CTRL_LOAD_IN_A 9 +#define MAX5522_CTRL_LOAD_IN_B 10 + +#define MAX5522_REG_DATA(x) ((x) + MAX5522_CTRL_LOAD_IN_A) + +struct max5522_chip_info { + const char *name; + const struct iio_chan_spec *channels; + unsigned int num_channels; +}; + +struct max5522_state { + struct regmap *regmap; + const struct max5522_chip_info *chip_info; + unsigned short dac_cache[2]; + struct regulator *vrefin_reg; +}; + +#define MAX5522_CHANNEL(chan) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .output = 1, \ + .channel = chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 10, \ + .storagebits = 16, \ + .shift = 2, \ + } \ +} + +const struct iio_chan_spec max5522_channels[] = { + MAX5522_CHANNEL(0), + MAX5522_CHANNEL(1), +}; + +enum max5522_type { + ID_MAX5522, +}; + +static const struct max5522_chip_info max5522_chip_info_tbl[] = { + [ID_MAX5522] = { + .name = "max5522", + .channels = max5522_channels, + .num_channels = 2, + }, +}; + +static inline int max5522_info_to_reg(struct iio_chan_spec const *chan) +{ + return MAX5522_REG_DATA(chan->channel); +} + +static int max5522_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct max5522_state *state = iio_priv(indio_dev); + int ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + *val = state->dac_cache[chan->channel]; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + ret = regulator_get_voltage(state->vrefin_reg); + if (ret < 0) + return -EINVAL; + *val = ret / 1000; + *val2 = 10; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } + + return -EINVAL; +} + +static int max5522_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct max5522_state *state = iio_priv(indio_dev); + int rval; + + if (val > 1023 || val < 0) + return -EINVAL; + + rval = regmap_write(state->regmap, max5522_info_to_reg(chan), + val << chan->scan_type.shift); + if (rval < 0) + return rval; + + state->dac_cache[chan->channel] = val; + + return 0; +} + +static const struct iio_info max5522_info = { + .read_raw = max5522_read_raw, + .write_raw = max5522_write_raw, +}; + +static const struct regmap_config max5522_regmap_config = { + .reg_bits = 4, + .val_bits = 12, + .max_register = MAX5522_MAX_ADDR, +}; + +static int max5522_spi_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct iio_dev *indio_dev; + struct max5522_state *state; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*state)); + if (indio_dev == NULL) { + dev_err(&spi->dev, "failed to allocate iio device\n"); + return -ENOMEM; + } + + state = iio_priv(indio_dev); + state->chip_info = device_get_match_data(&spi->dev); + if (!state->chip_info) { + state->chip_info = + (struct max5522_chip_info *)(id->driver_data); + if (!state->chip_info) + return -EINVAL; + } + + state->vrefin_reg = devm_regulator_get(&spi->dev, "vrefin"); + if (IS_ERR(state->vrefin_reg)) + return dev_err_probe(&spi->dev, PTR_ERR(state->vrefin_reg), + "Vrefin regulator not specified\n"); + + ret = regulator_enable(state->vrefin_reg); + if (ret) { + return dev_err_probe(&spi->dev, ret, + "Failed to enable vref regulators\n"); + } + + state->regmap = devm_regmap_init_spi(spi, &max5522_regmap_config); + + if (IS_ERR(state->regmap)) + return PTR_ERR(state->regmap); + + indio_dev->info = &max5522_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = max5522_channels; + indio_dev->num_channels = ARRAY_SIZE(max5522_channels); + indio_dev->name = max5522_chip_info_tbl[ID_MAX5522].name; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id max5522_ids[] = { + { "max5522", (kernel_ulong_t)&max5522_chip_info_tbl[ID_MAX5522] }, + {} +}; +MODULE_DEVICE_TABLE(spi, max5522_ids); + +static const struct of_device_id max5522_of_match[] = { + { + .compatible = "maxim,max5522", + .data = &max5522_chip_info_tbl[ID_MAX5522], + }, + {} +}; +MODULE_DEVICE_TABLE(of, max5522_of_match); + +static struct spi_driver max5522_spi_driver = { + .driver = { + .name = "max5522", + .of_match_table = max5522_of_match, + }, + .probe = max5522_spi_probe, + .id_table = max5522_ids, +}; +module_spi_driver(max5522_spi_driver); + +MODULE_AUTHOR("Angelo Dureghello Date: Tue, 17 Jan 2023 20:00:15 +0100 Subject: iio: light: vcnl4000: Prepare for more generic setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to allow the chip_spec array reference the function pointers for interrupts, the code for these functions need to be moved above the chip_spec array. This is a prestep to support a more generic setup of interrupts. Signed-off-by: Mårten Lindahl Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230117190017.3789181-2-marten.lindahl@axis.com Signed-off-by: Jonathan Cameron --- drivers/iio/light/vcnl4000.c | 256 +++++++++++++++++++++---------------------- 1 file changed, 128 insertions(+), 128 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index cc1a2062e76d..11b54b57e592 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -887,6 +887,134 @@ static ssize_t vcnl4000_read_near_level(struct iio_dev *indio_dev, return sprintf(buf, "%u\n", data->near_level); } +static irqreturn_t vcnl4010_irq_thread(int irq, void *p) +{ + struct iio_dev *indio_dev = p; + struct vcnl4000_data *data = iio_priv(indio_dev); + unsigned long isr; + int ret; + + ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR); + if (ret < 0) + goto end; + + isr = ret; + + if (isr & VCNL4010_INT_THR) { + if (test_bit(VCNL4010_INT_THR_LOW, &isr)) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE( + IIO_PROXIMITY, + 1, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + iio_get_time_ns(indio_dev)); + } + + if (test_bit(VCNL4010_INT_THR_HIGH, &isr)) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE( + IIO_PROXIMITY, + 1, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + iio_get_time_ns(indio_dev)); + } + + i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, + isr & VCNL4010_INT_THR); + } + + if (isr & VCNL4010_INT_DRDY && iio_buffer_enabled(indio_dev)) + iio_trigger_poll_chained(indio_dev->trig); + +end: + return IRQ_HANDLED; +} + +static irqreturn_t vcnl4010_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct vcnl4000_data *data = iio_priv(indio_dev); + const unsigned long *active_scan_mask = indio_dev->active_scan_mask; + u16 buffer[8] __aligned(8) = {0}; /* 1x16-bit + naturally aligned ts */ + bool data_read = false; + unsigned long isr; + int val = 0; + int ret; + + ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR); + if (ret < 0) + goto end; + + isr = ret; + + if (test_bit(0, active_scan_mask)) { + if (test_bit(VCNL4010_INT_PROXIMITY, &isr)) { + ret = vcnl4000_read_data(data, + VCNL4000_PS_RESULT_HI, + &val); + if (ret < 0) + goto end; + + buffer[0] = val; + data_read = true; + } + } + + ret = i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, + isr & VCNL4010_INT_DRDY); + if (ret < 0) + goto end; + + if (!data_read) + goto end; + + iio_push_to_buffers_with_timestamp(indio_dev, buffer, + iio_get_time_ns(indio_dev)); + +end: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +static int vcnl4010_buffer_postenable(struct iio_dev *indio_dev) +{ + struct vcnl4000_data *data = iio_priv(indio_dev); + int ret; + int cmd; + + /* Do not enable the buffer if we are already capturing events. */ + if (vcnl4010_is_in_periodic_mode(data)) + return -EBUSY; + + ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, + VCNL4010_INT_PROX_EN); + if (ret < 0) + return ret; + + cmd = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN; + return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, cmd); +} + +static int vcnl4010_buffer_predisable(struct iio_dev *indio_dev) +{ + struct vcnl4000_data *data = iio_priv(indio_dev); + int ret; + + ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 0); + if (ret < 0) + return ret; + + return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 0); +} + +static const struct iio_buffer_setup_ops vcnl4010_buffer_ops = { + .postenable = &vcnl4010_buffer_postenable, + .predisable = &vcnl4010_buffer_predisable, +}; + static const struct iio_chan_spec_ext_info vcnl4000_ext_info[] = { { .name = "nearlevel", @@ -1030,134 +1158,6 @@ static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { }, }; -static irqreturn_t vcnl4010_irq_thread(int irq, void *p) -{ - struct iio_dev *indio_dev = p; - struct vcnl4000_data *data = iio_priv(indio_dev); - unsigned long isr; - int ret; - - ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR); - if (ret < 0) - goto end; - - isr = ret; - - if (isr & VCNL4010_INT_THR) { - if (test_bit(VCNL4010_INT_THR_LOW, &isr)) { - iio_push_event(indio_dev, - IIO_UNMOD_EVENT_CODE( - IIO_PROXIMITY, - 1, - IIO_EV_TYPE_THRESH, - IIO_EV_DIR_FALLING), - iio_get_time_ns(indio_dev)); - } - - if (test_bit(VCNL4010_INT_THR_HIGH, &isr)) { - iio_push_event(indio_dev, - IIO_UNMOD_EVENT_CODE( - IIO_PROXIMITY, - 1, - IIO_EV_TYPE_THRESH, - IIO_EV_DIR_RISING), - iio_get_time_ns(indio_dev)); - } - - i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, - isr & VCNL4010_INT_THR); - } - - if (isr & VCNL4010_INT_DRDY && iio_buffer_enabled(indio_dev)) - iio_trigger_poll_chained(indio_dev->trig); - -end: - return IRQ_HANDLED; -} - -static irqreturn_t vcnl4010_trigger_handler(int irq, void *p) -{ - struct iio_poll_func *pf = p; - struct iio_dev *indio_dev = pf->indio_dev; - struct vcnl4000_data *data = iio_priv(indio_dev); - const unsigned long *active_scan_mask = indio_dev->active_scan_mask; - u16 buffer[8] __aligned(8) = {0}; /* 1x16-bit + naturally aligned ts */ - bool data_read = false; - unsigned long isr; - int val = 0; - int ret; - - ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR); - if (ret < 0) - goto end; - - isr = ret; - - if (test_bit(0, active_scan_mask)) { - if (test_bit(VCNL4010_INT_PROXIMITY, &isr)) { - ret = vcnl4000_read_data(data, - VCNL4000_PS_RESULT_HI, - &val); - if (ret < 0) - goto end; - - buffer[0] = val; - data_read = true; - } - } - - ret = i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, - isr & VCNL4010_INT_DRDY); - if (ret < 0) - goto end; - - if (!data_read) - goto end; - - iio_push_to_buffers_with_timestamp(indio_dev, buffer, - iio_get_time_ns(indio_dev)); - -end: - iio_trigger_notify_done(indio_dev->trig); - return IRQ_HANDLED; -} - -static int vcnl4010_buffer_postenable(struct iio_dev *indio_dev) -{ - struct vcnl4000_data *data = iio_priv(indio_dev); - int ret; - int cmd; - - /* Do not enable the buffer if we are already capturing events. */ - if (vcnl4010_is_in_periodic_mode(data)) - return -EBUSY; - - ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, - VCNL4010_INT_PROX_EN); - if (ret < 0) - return ret; - - cmd = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN; - return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, cmd); -} - -static int vcnl4010_buffer_predisable(struct iio_dev *indio_dev) -{ - struct vcnl4000_data *data = iio_priv(indio_dev); - int ret; - - ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 0); - if (ret < 0) - return ret; - - return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 0); -} - -static const struct iio_buffer_setup_ops vcnl4010_buffer_ops = { - .postenable = &vcnl4010_buffer_postenable, - .predisable = &vcnl4010_buffer_predisable, -}; - static const struct iio_trigger_ops vcnl4010_trigger_ops = { .validate_device = iio_trigger_validate_own_device, }; -- cgit v1.2.3 From bfb6cfeeb83f314dd4a09ad6c9a24678f66c3b3b Mon Sep 17 00:00:00 2001 From: Mårten Lindahl Date: Tue, 17 Jan 2023 20:00:16 +0100 Subject: iio: light: vcnl4000: Make irq handling more generic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This driver supports 4 chips, by which only one (vcnl4010) handles interrupts and has support for triggered buffer. The setup of these functions is hardcoded for vcnl4010 inside the generic vcnl4000_probe, and thus ignores the chip specific configuration structure where all other chip specific functions are specified. This complicates adding interrupt handler and triggered buffer support to chips which may have support for it. Add members for irq threads and iio_buffer_setup_ops to the generic vcnl4000_chip_spec struct, so that instead of checking a chip specific boolean irq support, we check for a chip specific triggered buffer handler, and/or a chip specific irq thread handler. Signed-off-by: Mårten Lindahl Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230117190017.3789181-3-marten.lindahl@axis.com Signed-off-by: Jonathan Cameron --- drivers/iio/light/vcnl4000.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index 11b54b57e592..b2f60f3f8d44 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -150,11 +150,13 @@ struct vcnl4000_chip_spec { struct iio_chan_spec const *channels; const int num_channels; const struct iio_info *info; - bool irq_support; + const struct iio_buffer_setup_ops *buffer_setup_ops; int (*init)(struct vcnl4000_data *data); int (*measure_light)(struct vcnl4000_data *data, int *val); int (*measure_proximity)(struct vcnl4000_data *data, int *val); int (*set_power_state)(struct vcnl4000_data *data, bool on); + irqreturn_t (*irq_thread)(int irq, void *priv); + irqreturn_t (*trig_buffer_func)(int irq, void *priv); }; static const struct i2c_device_id vcnl4000_id[] = { @@ -1121,7 +1123,6 @@ static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { .channels = vcnl4000_channels, .num_channels = ARRAY_SIZE(vcnl4000_channels), .info = &vcnl4000_info, - .irq_support = false, }, [VCNL4010] = { .prod = "VCNL4010/4020", @@ -1132,7 +1133,9 @@ static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { .channels = vcnl4010_channels, .num_channels = ARRAY_SIZE(vcnl4010_channels), .info = &vcnl4010_info, - .irq_support = true, + .irq_thread = vcnl4010_irq_thread, + .trig_buffer_func = vcnl4010_trigger_handler, + .buffer_setup_ops = &vcnl4010_buffer_ops, }, [VCNL4040] = { .prod = "VCNL4040", @@ -1143,7 +1146,6 @@ static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { .channels = vcnl4040_channels, .num_channels = ARRAY_SIZE(vcnl4040_channels), .info = &vcnl4040_info, - .irq_support = false, }, [VCNL4200] = { .prod = "VCNL4200", @@ -1154,7 +1156,6 @@ static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { .channels = vcnl4000_channels, .num_channels = ARRAY_SIZE(vcnl4000_channels), .info = &vcnl4000_info, - .irq_support = false, }, }; @@ -1214,22 +1215,25 @@ static int vcnl4000_probe(struct i2c_client *client) indio_dev->name = VCNL4000_DRV_NAME; indio_dev->modes = INDIO_DIRECT_MODE; - if (client->irq && data->chip_spec->irq_support) { + if (data->chip_spec->trig_buffer_func && + data->chip_spec->buffer_setup_ops) { ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, NULL, - vcnl4010_trigger_handler, - &vcnl4010_buffer_ops); + data->chip_spec->trig_buffer_func, + data->chip_spec->buffer_setup_ops); if (ret < 0) { dev_err(&client->dev, "unable to setup iio triggered buffer\n"); return ret; } + } + if (client->irq && data->chip_spec->irq_thread) { ret = devm_request_threaded_irq(&client->dev, client->irq, - NULL, vcnl4010_irq_thread, + NULL, data->chip_spec->irq_thread, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - "vcnl4010_irq", + "vcnl4000_irq", indio_dev); if (ret < 0) { dev_err(&client->dev, "irq request failed\n"); -- cgit v1.2.3 From 546676121cb9d1c77862042611533e721b86565e Mon Sep 17 00:00:00 2001 From: Mårten Lindahl Date: Tue, 17 Jan 2023 20:00:17 +0100 Subject: iio: light: vcnl4000: Add interrupt support for vcnl4040 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support to configure proximity sensor interrupts and threshold limits for vcnl4040. If an interrupt is detected an event will be pushed to the event interface. Signed-off-by: Mårten Lindahl Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230117190017.3789181-4-marten.lindahl@axis.com Signed-off-by: Jonathan Cameron --- drivers/iio/light/vcnl4000.c | 169 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) (limited to 'drivers/iio') diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index b2f60f3f8d44..6bdfce9747f9 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -60,8 +60,11 @@ #define VCNL4200_AL_CONF 0x00 /* Ambient light configuration */ #define VCNL4200_PS_CONF1 0x03 /* Proximity configuration */ +#define VCNL4040_PS_THDL_LM 0x06 /* Proximity threshold low */ +#define VCNL4040_PS_THDH_LM 0x07 /* Proximity threshold high */ #define VCNL4200_PS_DATA 0x08 /* Proximity data */ #define VCNL4200_AL_DATA 0x09 /* Ambient light data */ +#define VCNL4040_INT_FLAGS 0x0b /* Interrupt register */ #define VCNL4200_DEV_ID 0x0e /* Device ID, slave address and version */ #define VCNL4040_DEV_ID 0x0c /* Device ID and version */ @@ -78,6 +81,9 @@ #define VCNL4040_ALS_CONF_ALS_SHUTDOWN BIT(0) #define VCNL4040_PS_CONF1_PS_SHUTDOWN BIT(0) #define VCNL4040_PS_CONF2_PS_IT GENMASK(3, 1) /* Proximity integration time */ +#define VCNL4040_PS_CONF2_PS_INT GENMASK(9, 8) /* Proximity interrupt mode */ +#define VCNL4040_PS_IF_AWAY BIT(8) /* Proximity event cross low threshold */ +#define VCNL4040_PS_IF_CLOSE BIT(9) /* Proximity event cross high threshold */ /* Bit masks for interrupt registers. */ #define VCNL4010_INT_THR_SEL BIT(0) /* Select threshold interrupt source */ @@ -138,6 +144,7 @@ struct vcnl4000_data { enum vcnl4000_device_ids id; int rev; int al_scale; + u8 ps_int; /* proximity interrupt mode */ const struct vcnl4000_chip_spec *chip_spec; struct mutex vcnl4000_lock; struct vcnl4200_channel vcnl4200_al; @@ -256,6 +263,10 @@ static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on) { int ret; + /* Do not power down if interrupts are enabled */ + if (!on && data->ps_int) + return 0; + ret = vcnl4000_write_als_enable(data, on); if (ret < 0) return ret; @@ -297,6 +308,7 @@ static int vcnl4200_init(struct vcnl4000_data *data) dev_dbg(&data->client->dev, "device id 0x%x", id); data->rev = (ret >> 8) & 0xf; + data->ps_int = 0; data->vcnl4200_al.reg = VCNL4200_AL_DATA; data->vcnl4200_ps.reg = VCNL4200_PS_DATA; @@ -797,6 +809,64 @@ static int vcnl4010_write_event(struct iio_dev *indio_dev, } } +static int vcnl4040_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; + struct vcnl4000_data *data = iio_priv(indio_dev); + + switch (dir) { + case IIO_EV_DIR_RISING: + ret = i2c_smbus_read_word_data(data->client, + VCNL4040_PS_THDH_LM); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + case IIO_EV_DIR_FALLING: + ret = i2c_smbus_read_word_data(data->client, + VCNL4040_PS_THDL_LM); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int vcnl4040_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) +{ + int ret; + struct vcnl4000_data *data = iio_priv(indio_dev); + + switch (dir) { + case IIO_EV_DIR_RISING: + ret = i2c_smbus_write_word_data(data->client, + VCNL4040_PS_THDH_LM, val); + if (ret < 0) + return ret; + return IIO_VAL_INT; + case IIO_EV_DIR_FALLING: + ret = i2c_smbus_write_word_data(data->client, + VCNL4040_PS_THDL_LM, val); + if (ret < 0) + return ret; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + static bool vcnl4010_is_thr_enabled(struct vcnl4000_data *data) { int ret; @@ -879,6 +949,86 @@ static int vcnl4010_write_event_config(struct iio_dev *indio_dev, } } +static int vcnl4040_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + int ret; + struct vcnl4000_data *data = iio_priv(indio_dev); + + ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); + if (ret < 0) + return ret; + + data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, ret); + + return (dir == IIO_EV_DIR_RISING) ? + FIELD_GET(VCNL4040_PS_IF_AWAY, ret) : + FIELD_GET(VCNL4040_PS_IF_CLOSE, ret); +} + +static int vcnl4040_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; + u16 val, mask; + struct vcnl4000_data *data = iio_priv(indio_dev); + + mutex_lock(&data->vcnl4000_lock); + + ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); + if (ret < 0) + goto out; + + if (dir == IIO_EV_DIR_RISING) + mask = VCNL4040_PS_IF_AWAY; + else + mask = VCNL4040_PS_IF_CLOSE; + + val = state ? (ret | mask) : (ret & ~mask); + + data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, val); + ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, val); + +out: + mutex_unlock(&data->vcnl4000_lock); + data->chip_spec->set_power_state(data, data->ps_int != 0); + + return ret; +} + +static irqreturn_t vcnl4040_irq_thread(int irq, void *p) +{ + struct iio_dev *indio_dev = p; + struct vcnl4000_data *data = iio_priv(indio_dev); + int ret; + + ret = i2c_smbus_read_word_data(data->client, VCNL4040_INT_FLAGS); + if (ret < 0) + return IRQ_HANDLED; + + if (ret & VCNL4040_PS_IF_CLOSE) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + iio_get_time_ns(indio_dev)); + } + + if (ret & VCNL4040_PS_IF_AWAY) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + iio_get_time_ns(indio_dev)); + } + + return IRQ_HANDLED; +} + static ssize_t vcnl4000_read_near_level(struct iio_dev *indio_dev, uintptr_t priv, const struct iio_chan_spec *chan, @@ -1042,6 +1192,18 @@ static const struct iio_event_spec vcnl4000_event_spec[] = { } }; +static const struct iio_event_spec vcnl4040_event_spec[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), + }, +}; + static const struct iio_chan_spec vcnl4000_channels[] = { { .type = IIO_LIGHT, @@ -1090,6 +1252,8 @@ static const struct iio_chan_spec vcnl4040_channels[] = { BIT(IIO_CHAN_INFO_INT_TIME), .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME), .ext_info = vcnl4000_ext_info, + .event_spec = vcnl4040_event_spec, + .num_event_specs = ARRAY_SIZE(vcnl4040_event_spec), } }; @@ -1110,6 +1274,10 @@ static const struct iio_info vcnl4010_info = { static const struct iio_info vcnl4040_info = { .read_raw = vcnl4000_read_raw, .write_raw = vcnl4040_write_raw, + .read_event_value = vcnl4040_read_event, + .write_event_value = vcnl4040_write_event, + .read_event_config = vcnl4040_read_event_config, + .write_event_config = vcnl4040_write_event_config, .read_avail = vcnl4040_read_avail, }; @@ -1146,6 +1314,7 @@ static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { .channels = vcnl4040_channels, .num_channels = ARRAY_SIZE(vcnl4040_channels), .info = &vcnl4040_info, + .irq_thread = vcnl4040_irq_thread, }, [VCNL4200] = { .prod = "VCNL4200", -- cgit v1.2.3 From 677a33b51f7fbe39e9bff17d1e5718087dfa9ede Mon Sep 17 00:00:00 2001 From: Philippe De Muyter Date: Mon, 16 Jan 2023 14:07:13 +0100 Subject: iio: imu: st_lsm6dsx: fix naming of 'struct iio_info' in st_lsm6dsx_shub.c. We need the name 'st_lsm6dsx_ext_info' for the actual 'iio_chan_spec_ext_info'. Rename the 'st_lsm6dsx_ext_info' in st_lsm6dsx_shub.c to 'st_lsm6dsx_shub_info'. For consistency, replace also 'ext' by 'shub' in 'st_lsm6dsx_ext_attributes' and 'st_lsm6dsx_ext_attribute_group'. Signed-off-by: Philippe De Muyter Acked-by: Lorenzo Bianconi Link: https://lore.kernel.org/r/1673874434-30750-2-git-send-email-Philippe.DeMuyter@macq.eu Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c index f2b64b4956a3..c1b444520d2a 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c @@ -704,18 +704,18 @@ static ssize_t st_lsm6dsx_shub_scale_avail(struct device *dev, static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_lsm6dsx_shub_sampling_freq_avail); static IIO_DEVICE_ATTR(in_scale_available, 0444, st_lsm6dsx_shub_scale_avail, NULL, 0); -static struct attribute *st_lsm6dsx_ext_attributes[] = { +static struct attribute *st_lsm6dsx_shub_attributes[] = { &iio_dev_attr_sampling_frequency_available.dev_attr.attr, &iio_dev_attr_in_scale_available.dev_attr.attr, NULL, }; -static const struct attribute_group st_lsm6dsx_ext_attribute_group = { - .attrs = st_lsm6dsx_ext_attributes, +static const struct attribute_group st_lsm6dsx_shub_attribute_group = { + .attrs = st_lsm6dsx_shub_attributes, }; -static const struct iio_info st_lsm6dsx_ext_info = { - .attrs = &st_lsm6dsx_ext_attribute_group, +static const struct iio_info st_lsm6dsx_shub_info = { + .attrs = &st_lsm6dsx_shub_attribute_group, .read_raw = st_lsm6dsx_shub_read_raw, .write_raw = st_lsm6dsx_shub_write_raw, .hwfifo_set_watermark = st_lsm6dsx_set_watermark, @@ -737,7 +737,7 @@ st_lsm6dsx_shub_alloc_iiodev(struct st_lsm6dsx_hw *hw, return NULL; iio_dev->modes = INDIO_DIRECT_MODE; - iio_dev->info = &st_lsm6dsx_ext_info; + iio_dev->info = &st_lsm6dsx_shub_info; sensor = iio_priv(iio_dev); sensor->id = id; -- cgit v1.2.3 From d354a2ee3915ada7a10a2c6634c78e9aadfb3db9 Mon Sep 17 00:00:00 2001 From: Philippe De Muyter Date: Mon, 16 Jan 2023 14:07:14 +0100 Subject: iio: imu: st_lsm6dsx: add 'mount_matrix' sysfs entry to gyro channel. Signed-off-by: Philippe De Muyter Acked-by: Lorenzo Bianconi Link: https://lore.kernel.org/r/1673874434-30750-3-git-send-email-Philippe.DeMuyter@macq.eu Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h index 5b6f195748fc..499fcf8875b4 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h @@ -93,7 +93,7 @@ enum st_lsm6dsx_hw_id { .endianness = IIO_LE, \ }, \ .event_spec = &st_lsm6dsx_event, \ - .ext_info = st_lsm6dsx_accel_ext_info, \ + .ext_info = st_lsm6dsx_ext_info, \ .num_event_specs = 1, \ } @@ -113,6 +113,7 @@ enum st_lsm6dsx_hw_id { .storagebits = 16, \ .endianness = IIO_LE, \ }, \ + .ext_info = st_lsm6dsx_ext_info, \ } struct st_lsm6dsx_reg { @@ -528,7 +529,7 @@ st_lsm6dsx_device_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable) } static const -struct iio_chan_spec_ext_info __maybe_unused st_lsm6dsx_accel_ext_info[] = { +struct iio_chan_spec_ext_info __maybe_unused st_lsm6dsx_ext_info[] = { IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, st_lsm6dsx_get_mount_matrix), { } }; -- cgit v1.2.3 From 4d82b2f98a25882481a53faca0ca5a2169f9b563 Mon Sep 17 00:00:00 2001 From: Hugo Villeneuve Date: Sun, 15 Jan 2023 12:06:22 -0500 Subject: iio: adc: ti-ads7924: add Texas Instruments ADS7924 driver The Texas Instruments ADS7924 is a 4 channels, 12-bit analog to digital converter (ADC) with an I2C interface. Datasheet: https://www.ti.com/lit/gpn/ads7924 Signed-off-by: Hugo Villeneuve Link: https://lore.kernel.org/r/20230115170623.3680647-2-hugo@hugovil.com Signed-off-by: Jonathan Cameron --- MAINTAINERS | 7 + drivers/iio/adc/Kconfig | 11 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ti-ads7924.c | 474 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 493 insertions(+) create mode 100644 drivers/iio/adc/ti-ads7924.c (limited to 'drivers/iio') diff --git a/MAINTAINERS b/MAINTAINERS index 9ff472ca1244..94eb68076f0c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20776,6 +20776,13 @@ M: Robert Richter S: Odd Fixes F: drivers/gpio/gpio-thunderx.c +TI ADS7924 ADC DRIVER +M: Hugo Villeneuve +L: linux-iio@vger.kernel.org +S: Supported +F: Documentation/devicetree/bindings/iio/adc/ti,ads7924.yaml +F: drivers/iio/adc/ti-ads7924.c + TI AM437X VPFE DRIVER M: "Lad, Prabhakar" L: linux-media@vger.kernel.org diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 7d6e74189a2d..99468c64daf9 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -1208,6 +1208,17 @@ config TI_ADS1015 This driver can also be built as a module. If so, the module will be called ti-ads1015. +config TI_ADS7924 + tristate "Texas Instruments ADS7924 ADC" + depends on I2C + select REGMAP_I2C + help + If you say yes here you get support for Texas Instruments ADS7924 + 4 channels, 12-bit I2C ADC chip. + + This driver can also be built as a module. If so, the module will be + called ti-ads7924. + config TI_ADS7950 tristate "Texas Instruments ADS7950 ADC driver" depends on SPI && GPIOLIB diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 6e08415c3f3a..1e5bdf47a091 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -107,6 +107,7 @@ obj-$(CONFIG_TI_ADC108S102) += ti-adc108s102.o obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o +obj-$(CONFIG_TI_ADS7924) += ti-ads7924.o obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o obj-$(CONFIG_TI_ADS8344) += ti-ads8344.o obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o diff --git a/drivers/iio/adc/ti-ads7924.c b/drivers/iio/adc/ti-ads7924.c new file mode 100644 index 000000000000..b02abb026966 --- /dev/null +++ b/drivers/iio/adc/ti-ads7924.c @@ -0,0 +1,474 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IIO driver for Texas Instruments ADS7924 ADC, 12-bit, 4-Channels, I2C + * + * Author: Hugo Villeneuve + * Copyright 2022 DimOnOff + * + * based on iio/adc/ti-ads1015.c + * Copyright (c) 2016, Intel Corporation. + * + * Datasheet: https://www.ti.com/lit/gpn/ads7924 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define ADS7924_CHANNELS 4 +#define ADS7924_BITS 12 +#define ADS7924_DATA_SHIFT 4 + +/* Registers. */ +#define ADS7924_MODECNTRL_REG 0x00 +#define ADS7924_INTCNTRL_REG 0x01 +#define ADS7924_DATA0_U_REG 0x02 +#define ADS7924_DATA0_L_REG 0x03 +#define ADS7924_DATA1_U_REG 0x04 +#define ADS7924_DATA1_L_REG 0x05 +#define ADS7924_DATA2_U_REG 0x06 +#define ADS7924_DATA2_L_REG 0x07 +#define ADS7924_DATA3_U_REG 0x08 +#define ADS7924_DATA3_L_REG 0x09 +#define ADS7924_ULR0_REG 0x0A +#define ADS7924_LLR0_REG 0x0B +#define ADS7924_ULR1_REG 0x0C +#define ADS7924_LLR1_REG 0x0D +#define ADS7924_ULR2_REG 0x0E +#define ADS7924_LLR2_REG 0x0F +#define ADS7924_ULR3_REG 0x10 +#define ADS7924_LLR3_REG 0x11 +#define ADS7924_INTCONFIG_REG 0x12 +#define ADS7924_SLPCONFIG_REG 0x13 +#define ADS7924_ACQCONFIG_REG 0x14 +#define ADS7924_PWRCONFIG_REG 0x15 +#define ADS7924_RESET_REG 0x16 + +/* + * Register address INC bit: when set to '1', the register address is + * automatically incremented after every register read which allows convenient + * reading of multiple registers. Set INC to '0' when reading a single register. + */ +#define ADS7924_AUTO_INCREMENT_BIT BIT(7) + +#define ADS7924_MODECNTRL_MODE_MASK GENMASK(7, 2) + +#define ADS7924_MODECNTRL_SEL_MASK GENMASK(1, 0) + +#define ADS7924_CFG_INTPOL_BIT 1 +#define ADS7924_CFG_INTTRIG_BIT 0 + +#define ADS7924_CFG_INTPOL_MASK BIT(ADS7924_CFG_INTPOL_BIT) +#define ADS7924_CFG_INTTRIG_MASK BIT(ADS7924_CFG_INTTRIG_BIT) + +/* Interrupt pin polarity */ +#define ADS7924_CFG_INTPOL_LOW 0 +#define ADS7924_CFG_INTPOL_HIGH 1 + +/* Interrupt pin signaling */ +#define ADS7924_CFG_INTTRIG_LEVEL 0 +#define ADS7924_CFG_INTTRIG_EDGE 1 + +/* Mode control values */ +#define ADS7924_MODECNTRL_IDLE 0x00 +#define ADS7924_MODECNTRL_AWAKE 0x20 +#define ADS7924_MODECNTRL_MANUAL_SINGLE 0x30 +#define ADS7924_MODECNTRL_MANUAL_SCAN 0x32 +#define ADS7924_MODECNTRL_AUTO_SINGLE 0x31 +#define ADS7924_MODECNTRL_AUTO_SCAN 0x33 +#define ADS7924_MODECNTRL_AUTO_SINGLE_SLEEP 0x39 +#define ADS7924_MODECNTRL_AUTO_SCAN_SLEEP 0x3B +#define ADS7924_MODECNTRL_AUTO_BURST_SLEEP 0x3F + +#define ADS7924_ACQTIME_MASK GENMASK(4, 0) + +#define ADS7924_PWRUPTIME_MASK GENMASK(4, 0) + +/* + * The power-up time is allowed to elapse whenever the device has been shutdown + * in idle mode. Power-up time can allow external circuits, such as an + * operational amplifier, between the MUXOUT and ADCIN pins to turn on. + * The nominal time programmed by the PUTIME[4:0] register bits is given by: + * t PU = PWRUPTIME[4:0] × 2 μs + * If a power-up time is not required, set the bits to '0' to effectively bypass. + */ +#define ADS7924_PWRUPTIME_US 0 /* Bypass (0us). */ + +/* + * Acquisition Time according to ACQTIME[4:0] register bits. + * The Acquisition Time is given by: + * t ACQ = (ACQTIME[4:0] × 2 μs) + 6 μs + * Using default value of 0 for ACQTIME[4:0] results in a minimum acquisition + * time of 6us. + */ +#define ADS7924_ACQTIME_US 6 + +/* The conversion time is always 4μs and cannot be programmed by the user. */ +#define ADS7924_CONVTIME_US 4 + +#define ADS7924_TOTAL_CONVTIME_US (ADS7924_PWRUPTIME_US + ADS7924_ACQTIME_US + \ + ADS7924_CONVTIME_US) + +#define ADS7924_V_CHAN(_chan, _addr) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _chan, \ + .address = _addr, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = "AIN"#_chan, \ +} + +struct ads7924_data { + struct device *dev; + struct regmap *regmap; + struct regulator *vref_reg; + + /* GPIO descriptor for device hard-reset pin. */ + struct gpio_desc *reset_gpio; + + /* + * Protects ADC ops, e.g: concurrent sysfs/buffered + * data reads, configuration updates + */ + struct mutex lock; + + /* + * Set to true when the ADC is switched to the continuous-conversion + * mode and exits from a power-down state. This flag is used to avoid + * getting the stale result from the conversion register. + */ + bool conv_invalid; +}; + +static bool ads7924_is_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ADS7924_MODECNTRL_REG: + case ADS7924_INTCNTRL_REG: + case ADS7924_ULR0_REG: + case ADS7924_LLR0_REG: + case ADS7924_ULR1_REG: + case ADS7924_LLR1_REG: + case ADS7924_ULR2_REG: + case ADS7924_LLR2_REG: + case ADS7924_ULR3_REG: + case ADS7924_LLR3_REG: + case ADS7924_INTCONFIG_REG: + case ADS7924_SLPCONFIG_REG: + case ADS7924_ACQCONFIG_REG: + case ADS7924_PWRCONFIG_REG: + case ADS7924_RESET_REG: + return true; + default: + return false; + } +} + +static const struct regmap_config ads7924_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = ADS7924_RESET_REG, + .writeable_reg = ads7924_is_writeable_reg, +}; + +static const struct iio_chan_spec ads7924_channels[] = { + ADS7924_V_CHAN(0, ADS7924_DATA0_U_REG), + ADS7924_V_CHAN(1, ADS7924_DATA1_U_REG), + ADS7924_V_CHAN(2, ADS7924_DATA2_U_REG), + ADS7924_V_CHAN(3, ADS7924_DATA3_U_REG), +}; + +static int ads7924_get_adc_result(struct ads7924_data *data, + struct iio_chan_spec const *chan, int *val) +{ + int ret; + __be16 be_val; + + if (chan->channel < 0 || chan->channel >= ADS7924_CHANNELS) + return -EINVAL; + + if (data->conv_invalid) { + int conv_time; + + conv_time = ADS7924_TOTAL_CONVTIME_US; + /* Allow 10% for internal clock inaccuracy. */ + conv_time += conv_time / 10; + usleep_range(conv_time, conv_time + 1); + data->conv_invalid = false; + } + + ret = regmap_raw_read(data->regmap, ADS7924_AUTO_INCREMENT_BIT | + chan->address, &be_val, sizeof(be_val)); + if (ret) + return ret; + + *val = be16_to_cpu(be_val) >> ADS7924_DATA_SHIFT; + + return 0; +} + +static int ads7924_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + int ret, vref_uv; + struct ads7924_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&data->lock); + ret = ads7924_get_adc_result(data, chan, val); + mutex_unlock(&data->lock); + if (ret < 0) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + vref_uv = regulator_get_voltage(data->vref_reg); + if (vref_uv < 0) + return vref_uv; + + *val = vref_uv / 1000; /* Convert reg voltage to mV */ + *val2 = ADS7924_BITS; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static const struct iio_info ads7924_info = { + .read_raw = ads7924_read_raw, +}; + +static int ads7924_get_channels_config(struct i2c_client *client, + struct iio_dev *indio_dev) +{ + struct ads7924_data *priv = iio_priv(indio_dev); + struct device *dev = priv->dev; + struct fwnode_handle *node; + int num_channels = 0; + + device_for_each_child_node(dev, node) { + u32 pval; + unsigned int channel; + + if (fwnode_property_read_u32(node, "reg", &pval)) { + dev_err(dev, "invalid reg on %pfw\n", node); + continue; + } + + channel = pval; + if (channel >= ADS7924_CHANNELS) { + dev_err(dev, "invalid channel index %d on %pfw\n", + channel, node); + continue; + } + + num_channels++; + } + + if (!num_channels) + return -EINVAL; + + return 0; +} + +static int ads7924_set_conv_mode(struct ads7924_data *data, int mode) +{ + int ret; + unsigned int mode_field; + struct device *dev = data->dev; + + /* + * When switching between modes, be sure to first select the Awake mode + * and then switch to the desired mode. This procedure ensures the + * internal control logic is properly synchronized. + */ + if (mode != ADS7924_MODECNTRL_IDLE) { + mode_field = FIELD_PREP(ADS7924_MODECNTRL_MODE_MASK, + ADS7924_MODECNTRL_AWAKE); + + ret = regmap_update_bits(data->regmap, ADS7924_MODECNTRL_REG, + ADS7924_MODECNTRL_MODE_MASK, + mode_field); + if (ret) { + dev_err(dev, "failed to set awake mode (%pe)\n", + ERR_PTR(ret)); + return ret; + } + } + + mode_field = FIELD_PREP(ADS7924_MODECNTRL_MODE_MASK, mode); + + ret = regmap_update_bits(data->regmap, ADS7924_MODECNTRL_REG, + ADS7924_MODECNTRL_MODE_MASK, mode_field); + if (ret) + dev_err(dev, "failed to set mode %d (%pe)\n", mode, + ERR_PTR(ret)); + + return ret; +} + +static int ads7924_reset(struct iio_dev *indio_dev) +{ + struct ads7924_data *data = iio_priv(indio_dev); + + if (data->reset_gpio) { + gpiod_set_value(data->reset_gpio, 1); /* Assert. */ + /* Educated guess: assert time not specified in datasheet... */ + mdelay(100); + gpiod_set_value(data->reset_gpio, 0); /* Deassert. */ + return 0; + } + + /* + * A write of 10101010 to this register will generate a + * software reset of the ADS7924. + */ + return regmap_write(data->regmap, ADS7924_RESET_REG, 0b10101010); +}; + +static void ads7924_reg_disable(void *data) +{ + regulator_disable(data); +} + +static void ads7924_set_idle_mode(void *data) +{ + ads7924_set_conv_mode(data, ADS7924_MODECNTRL_IDLE); +} + +static int ads7924_probe(struct i2c_client *client) +{ + struct iio_dev *indio_dev; + struct ads7924_data *data; + struct device *dev = &client->dev; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return dev_err_probe(dev, -ENOMEM, + "failed to allocate iio device\n"); + + data = iio_priv(indio_dev); + + data->dev = dev; + + /* Initialize the reset GPIO as output with an initial value of 0. */ + data->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(data->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(data->reset_gpio), + "failed to get request reset GPIO\n"); + + mutex_init(&data->lock); + + indio_dev->name = "ads7924"; + indio_dev->modes = INDIO_DIRECT_MODE; + + indio_dev->channels = ads7924_channels; + indio_dev->num_channels = ARRAY_SIZE(ads7924_channels); + indio_dev->info = &ads7924_info; + + ret = ads7924_get_channels_config(client, indio_dev); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to get channels configuration\n"); + + data->regmap = devm_regmap_init_i2c(client, &ads7924_regmap_config); + if (IS_ERR(data->regmap)) + return dev_err_probe(dev, PTR_ERR(data->regmap), + "failed to init regmap\n"); + + data->vref_reg = devm_regulator_get(dev, "vref"); + if (IS_ERR(data->vref_reg)) + return dev_err_probe(dev, PTR_ERR(data->vref_reg), + "failed to get vref regulator\n"); + + ret = regulator_enable(data->vref_reg); + if (ret) + return dev_err_probe(dev, ret, + "failed to enable regulator\n"); + + ret = devm_add_action_or_reset(dev, ads7924_reg_disable, data->vref_reg); + if (ret) + return dev_err_probe(dev, ret, + "failed to add regulator disable action\n"); + + ret = ads7924_reset(indio_dev); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to reset device\n"); + + ret = ads7924_set_conv_mode(data, ADS7924_MODECNTRL_AUTO_SCAN); + if (ret) + return dev_err_probe(dev, ret, + "failed to set conversion mode\n"); + + ret = devm_add_action_or_reset(dev, ads7924_set_idle_mode, data); + if (ret) + return dev_err_probe(dev, ret, + "failed to add idle mode action\n"); + + /* Use minimum signal acquire time. */ + ret = regmap_update_bits(data->regmap, ADS7924_ACQCONFIG_REG, + ADS7924_ACQTIME_MASK, + FIELD_PREP(ADS7924_ACQTIME_MASK, 0)); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to configure signal acquire time\n"); + + /* Disable power-up time. */ + ret = regmap_update_bits(data->regmap, ADS7924_PWRCONFIG_REG, + ADS7924_PWRUPTIME_MASK, + FIELD_PREP(ADS7924_PWRUPTIME_MASK, 0)); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to configure power-up time\n"); + + data->conv_invalid = true; + + ret = devm_iio_device_register(dev, indio_dev); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to register IIO device\n"); + + return 0; +} + +static const struct i2c_device_id ads7924_id[] = { + { "ads7924", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, ads7924_id); + +static const struct of_device_id ads7924_of_match[] = { + { .compatible = "ti,ads7924", }, + {} +}; +MODULE_DEVICE_TABLE(of, ads7924_of_match); + +static struct i2c_driver ads7924_driver = { + .driver = { + .name = "ads7924", + .of_match_table = ads7924_of_match, + }, + .probe_new = ads7924_probe, + .id_table = ads7924_id, +}; + +module_i2c_driver(ads7924_driver); + +MODULE_AUTHOR("Hugo Villeneuve "); +MODULE_DESCRIPTION("Texas Instruments ADS7924 ADC I2C driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 7d02296ac8b835ff3ffa355eab3c73d3cba921f8 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Tue, 17 Jan 2023 21:51:35 +0800 Subject: iio: adc: add imx93 adc support The ADC in i.mx93 is a total new ADC IP, add a driver to support this ADC. Currently, only support one shot normal conversion triggered by software. For other mode, will add in future. Signed-off-by: Haibo Chen Link: https://lore.kernel.org/r/20230117135137.1735536-2-haibo.chen@nxp.com Signed-off-by: Jonathan Cameron --- MAINTAINERS | 4 +- drivers/iio/adc/Kconfig | 10 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/imx93_adc.c | 484 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 498 insertions(+), 1 deletion(-) create mode 100644 drivers/iio/adc/imx93_adc.c (limited to 'drivers/iio') diff --git a/MAINTAINERS b/MAINTAINERS index 94eb68076f0c..89a81cb45b42 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15020,14 +15020,16 @@ S: Maintained F: Documentation/devicetree/bindings/iio/adc/nxp,imx8qxp-adc.yaml F: drivers/iio/adc/imx8qxp-adc.c -NXP i.MX 7D/6SX/6UL AND VF610 ADC DRIVER +NXP i.MX 7D/6SX/6UL/93 AND VF610 ADC DRIVER M: Haibo Chen L: linux-iio@vger.kernel.org L: linux-imx@nxp.com S: Maintained F: Documentation/devicetree/bindings/iio/adc/fsl,imx7d-adc.yaml F: Documentation/devicetree/bindings/iio/adc/fsl,vf610-adc.yaml +F: Documentation/devicetree/bindings/iio/adc/nxp,imx93-adc.yaml F: drivers/iio/adc/imx7d_adc.c +F: drivers/iio/adc/imx93_adc.c F: drivers/iio/adc/vf610_adc.c NXP PF8100/PF8121A/PF8200 PMIC REGULATOR DEVICE DRIVER diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 99468c64daf9..4a95c843a91b 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -566,6 +566,16 @@ config IMX8QXP_ADC This driver can also be built as a module. If so, the module will be called imx8qxp-adc. +config IMX93_ADC + tristate "IMX93 ADC driver" + depends on ARCH_MXC || COMPILE_TEST + depends on HAS_IOMEM + help + Say yes here to build support for IMX93 ADC. + + This driver can also be built as a module. If so, the module will be + called imx93_adc. + config LP8788_ADC tristate "LP8788 ADC driver" depends on MFD_LP8788 diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 1e5bdf47a091..36c18177322a 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -49,6 +49,7 @@ obj-$(CONFIG_HI8435) += hi8435.o obj-$(CONFIG_HX711) += hx711.o obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o obj-$(CONFIG_IMX8QXP_ADC) += imx8qxp-adc.o +obj-$(CONFIG_IMX93_ADC) += imx93_adc.o obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o obj-$(CONFIG_INGENIC_ADC) += ingenic-adc.o obj-$(CONFIG_INTEL_MRFLD_ADC) += intel_mrfld_adc.o diff --git a/drivers/iio/adc/imx93_adc.c b/drivers/iio/adc/imx93_adc.c new file mode 100644 index 000000000000..d8de8284e13d --- /dev/null +++ b/drivers/iio/adc/imx93_adc.c @@ -0,0 +1,484 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * NXP i.MX93 ADC driver + * + * Copyright 2023 NXP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IMX93_ADC_DRIVER_NAME "imx93-adc" + +/* Register map definition */ +#define IMX93_ADC_MCR 0x00 +#define IMX93_ADC_MSR 0x04 +#define IMX93_ADC_ISR 0x10 +#define IMX93_ADC_IMR 0x20 +#define IMX93_ADC_CIMR0 0x24 +#define IMX93_ADC_CTR0 0x94 +#define IMX93_ADC_NCMR0 0xA4 +#define IMX93_ADC_PCDR0 0x100 +#define IMX93_ADC_PCDR1 0x104 +#define IMX93_ADC_PCDR2 0x108 +#define IMX93_ADC_PCDR3 0x10c +#define IMX93_ADC_PCDR4 0x110 +#define IMX93_ADC_PCDR5 0x114 +#define IMX93_ADC_PCDR6 0x118 +#define IMX93_ADC_PCDR7 0x11c +#define IMX93_ADC_CALSTAT 0x39C + +/* ADC bit shift */ +#define IMX93_ADC_MCR_MODE_MASK BIT(29) +#define IMX93_ADC_MCR_NSTART_MASK BIT(24) +#define IMX93_ADC_MCR_CALSTART_MASK BIT(14) +#define IMX93_ADC_MCR_ADCLKSE_MASK BIT(8) +#define IMX93_ADC_MCR_PWDN_MASK BIT(0) +#define IMX93_ADC_MSR_CALFAIL_MASK BIT(30) +#define IMX93_ADC_MSR_CALBUSY_MASK BIT(29) +#define IMX93_ADC_MSR_ADCSTATUS_MASK GENMASK(2, 0) +#define IMX93_ADC_ISR_ECH_MASK BIT(0) +#define IMX93_ADC_ISR_EOC_MASK BIT(1) +#define IMX93_ADC_ISR_EOC_ECH_MASK (IMX93_ADC_ISR_EOC_MASK | \ + IMX93_ADC_ISR_ECH_MASK) +#define IMX93_ADC_IMR_JEOC_MASK BIT(3) +#define IMX93_ADC_IMR_JECH_MASK BIT(2) +#define IMX93_ADC_IMR_EOC_MASK BIT(1) +#define IMX93_ADC_IMR_ECH_MASK BIT(0) +#define IMX93_ADC_PCDR_CDATA_MASK GENMASK(11, 0) + +/* ADC status */ +#define IMX93_ADC_MSR_ADCSTATUS_IDLE 0 +#define IMX93_ADC_MSR_ADCSTATUS_POWER_DOWN 1 +#define IMX93_ADC_MSR_ADCSTATUS_WAIT_STATE 2 +#define IMX93_ADC_MSR_ADCSTATUS_BUSY_IN_CALIBRATION 3 +#define IMX93_ADC_MSR_ADCSTATUS_SAMPLE 4 +#define IMX93_ADC_MSR_ADCSTATUS_CONVERSION 6 + +#define IMX93_ADC_TIMEOUT msecs_to_jiffies(100) + +struct imx93_adc { + struct device *dev; + void __iomem *regs; + struct clk *ipg_clk; + int irq; + struct regulator *vref; + /* lock to protect against multiple access to the device */ + struct mutex lock; + struct completion completion; +}; + +#define IMX93_ADC_CHAN(_idx) { \ + .type = IIO_VOLTAGE, \ + .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), \ +} + +static const struct iio_chan_spec imx93_adc_iio_channels[] = { + IMX93_ADC_CHAN(0), + IMX93_ADC_CHAN(1), + IMX93_ADC_CHAN(2), + IMX93_ADC_CHAN(3), +}; + +static void imx93_adc_power_down(struct imx93_adc *adc) +{ + u32 mcr, msr; + int ret; + + mcr = readl(adc->regs + IMX93_ADC_MCR); + mcr |= FIELD_PREP(IMX93_ADC_MCR_PWDN_MASK, 1); + writel(mcr, adc->regs + IMX93_ADC_MCR); + + ret = readl_poll_timeout(adc->regs + IMX93_ADC_MSR, msr, + ((msr & IMX93_ADC_MSR_ADCSTATUS_MASK) == + IMX93_ADC_MSR_ADCSTATUS_POWER_DOWN), + 1, 50); + if (ret == -ETIMEDOUT) + dev_warn(adc->dev, + "ADC do not in power down mode, current MSR is %x\n", + msr); +} + +static void imx93_adc_power_up(struct imx93_adc *adc) +{ + u32 mcr; + + /* bring ADC out of power down state, in idle state */ + mcr = readl(adc->regs + IMX93_ADC_MCR); + mcr &= ~FIELD_PREP(IMX93_ADC_MCR_PWDN_MASK, 1); + writel(mcr, adc->regs + IMX93_ADC_MCR); +} + +static void imx93_adc_config_ad_clk(struct imx93_adc *adc) +{ + u32 mcr; + + /* put adc in power down mode */ + imx93_adc_power_down(adc); + + /* config the AD_CLK equal to bus clock */ + mcr = readl(adc->regs + IMX93_ADC_MCR); + mcr |= FIELD_PREP(IMX93_ADC_MCR_ADCLKSE_MASK, 1); + writel(mcr, adc->regs + IMX93_ADC_MCR); + + imx93_adc_power_up(adc); +} + +static int imx93_adc_calibration(struct imx93_adc *adc) +{ + u32 mcr, msr; + int ret; + + /* make sure ADC in power down mode */ + imx93_adc_power_down(adc); + + /* config SAR controller operating clock */ + mcr = readl(adc->regs + IMX93_ADC_MCR); + mcr &= ~FIELD_PREP(IMX93_ADC_MCR_ADCLKSE_MASK, 1); + writel(mcr, adc->regs + IMX93_ADC_MCR); + + imx93_adc_power_up(adc); + + /* + * TODO: we use the default TSAMP/NRSMPL/AVGEN in MCR, + * can add the setting of these bit if need in future. + */ + + /* run calibration */ + mcr = readl(adc->regs + IMX93_ADC_MCR); + mcr |= FIELD_PREP(IMX93_ADC_MCR_CALSTART_MASK, 1); + writel(mcr, adc->regs + IMX93_ADC_MCR); + + /* wait calibration to be finished */ + ret = readl_poll_timeout(adc->regs + IMX93_ADC_MSR, msr, + !(msr & IMX93_ADC_MSR_CALBUSY_MASK), 1000, 2000000); + if (ret == -ETIMEDOUT) { + dev_warn(adc->dev, "ADC do not finish calibration in 2 min!\n"); + imx93_adc_power_down(adc); + return ret; + } + + /* check whether calbration is success or not */ + msr = readl(adc->regs + IMX93_ADC_MSR); + if (msr & IMX93_ADC_MSR_CALFAIL_MASK) { + dev_warn(adc->dev, "ADC calibration failed!\n"); + imx93_adc_power_down(adc); + return -EAGAIN; + } + + return 0; +} + +static int imx93_adc_read_channel_conversion(struct imx93_adc *adc, + int channel_number, + int *result) +{ + u32 channel; + u32 imr, mcr, pcda; + long ret; + + reinit_completion(&adc->completion); + + /* config channel mask register */ + channel = 1 << channel_number; + writel(channel, adc->regs + IMX93_ADC_NCMR0); + + /* TODO: can config desired sample time in CTRn if need */ + + /* config interrupt mask */ + imr = FIELD_PREP(IMX93_ADC_IMR_EOC_MASK, 1); + writel(imr, adc->regs + IMX93_ADC_IMR); + writel(channel, adc->regs + IMX93_ADC_CIMR0); + + /* config one-shot mode */ + mcr = readl(adc->regs + IMX93_ADC_MCR); + mcr &= ~FIELD_PREP(IMX93_ADC_MCR_MODE_MASK, 1); + writel(mcr, adc->regs + IMX93_ADC_MCR); + + /* start normal conversion */ + mcr = readl(adc->regs + IMX93_ADC_MCR); + mcr |= FIELD_PREP(IMX93_ADC_MCR_NSTART_MASK, 1); + writel(mcr, adc->regs + IMX93_ADC_MCR); + + ret = wait_for_completion_interruptible_timeout(&adc->completion, + IMX93_ADC_TIMEOUT); + if (ret == 0) + return -ETIMEDOUT; + + if (ret < 0) + return ret; + + pcda = readl(adc->regs + IMX93_ADC_PCDR0 + channel_number * 4); + + *result = FIELD_GET(IMX93_ADC_PCDR_CDATA_MASK, pcda); + + return ret; +} + +static int imx93_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct imx93_adc *adc = iio_priv(indio_dev); + struct device *dev = adc->dev; + long ret; + u32 vref_uv; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + pm_runtime_get_sync(dev); + mutex_lock(&adc->lock); + ret = imx93_adc_read_channel_conversion(adc, chan->channel, val); + mutex_unlock(&adc->lock); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_sync_autosuspend(dev); + if (ret < 0) + return ret; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + ret = vref_uv = regulator_get_voltage(adc->vref); + if (ret < 0) + return ret; + *val = vref_uv / 1000; + *val2 = 12; + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_SAMP_FREQ: + *val = clk_get_rate(adc->ipg_clk); + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static irqreturn_t imx93_adc_isr(int irq, void *dev_id) +{ + struct imx93_adc *adc = dev_id; + u32 isr, eoc, unexpected; + + isr = readl(adc->regs + IMX93_ADC_ISR); + + if (FIELD_GET(IMX93_ADC_ISR_EOC_ECH_MASK, isr)) { + eoc = isr & IMX93_ADC_ISR_EOC_ECH_MASK; + writel(eoc, adc->regs + IMX93_ADC_ISR); + complete(&adc->completion); + } + + unexpected = isr & ~IMX93_ADC_ISR_EOC_ECH_MASK; + if (unexpected) { + writel(unexpected, adc->regs + IMX93_ADC_ISR); + dev_err(adc->dev, "Unexpected interrupt 0x%08x.\n", unexpected); + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +static const struct iio_info imx93_adc_iio_info = { + .read_raw = &imx93_adc_read_raw, +}; + +static int imx93_adc_probe(struct platform_device *pdev) +{ + struct imx93_adc *adc; + struct iio_dev *indio_dev; + struct device *dev = &pdev->dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); + if (!indio_dev) + return dev_err_probe(dev, -ENOMEM, + "Failed allocating iio device\n"); + + adc = iio_priv(indio_dev); + adc->dev = dev; + + mutex_init(&adc->lock); + adc->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(adc->regs)) + return dev_err_probe(dev, PTR_ERR(adc->regs), + "Failed geting ioremap resource\n"); + + /* The third irq is for ADC conversion usage */ + adc->irq = platform_get_irq(pdev, 2); + if (adc->irq < 0) + return adc->irq; + + adc->ipg_clk = devm_clk_get(dev, "ipg"); + if (IS_ERR(adc->ipg_clk)) + return dev_err_probe(dev, PTR_ERR(adc->ipg_clk), + "Failed getting clock.\n"); + + adc->vref = devm_regulator_get(dev, "vref"); + if (IS_ERR(adc->vref)) + return dev_err_probe(dev, PTR_ERR(adc->vref), + "Failed getting reference voltage.\n"); + + ret = regulator_enable(adc->vref); + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable reference voltage.\n"); + + platform_set_drvdata(pdev, indio_dev); + + init_completion(&adc->completion); + + indio_dev->name = "imx93-adc"; + indio_dev->info = &imx93_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = imx93_adc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(imx93_adc_iio_channels); + + ret = clk_prepare_enable(adc->ipg_clk); + if (ret) { + dev_err_probe(dev, ret, + "Failed to enable ipg clock.\n"); + goto error_regulator_disable; + } + + ret = request_irq(adc->irq, imx93_adc_isr, 0, IMX93_ADC_DRIVER_NAME, adc); + if (ret < 0) { + dev_err_probe(dev, ret, + "Failed requesting irq, irq = %d\n", adc->irq); + goto error_ipg_clk_disable; + } + + ret = imx93_adc_calibration(adc); + if (ret < 0) + goto error_free_adc_irq; + + imx93_adc_config_ad_clk(adc); + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err_probe(dev, ret, + "Failed to register this iio device.\n"); + goto error_adc_power_down; + } + + pm_runtime_set_active(dev); + pm_runtime_set_autosuspend_delay(dev, 50); + pm_runtime_use_autosuspend(dev); + pm_runtime_enable(dev); + + return 0; + +error_adc_power_down: + imx93_adc_power_down(adc); +error_free_adc_irq: + free_irq(adc->irq, adc); +error_ipg_clk_disable: + clk_disable_unprepare(adc->ipg_clk); +error_regulator_disable: + regulator_disable(adc->vref); + + return ret; +} + +static int imx93_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct imx93_adc *adc = iio_priv(indio_dev); + struct device *dev = adc->dev; + + /* adc power down need clock on */ + pm_runtime_get_sync(dev); + + pm_runtime_disable(dev); + pm_runtime_dont_use_autosuspend(dev); + pm_runtime_put_noidle(dev); + + iio_device_unregister(indio_dev); + imx93_adc_power_down(adc); + free_irq(adc->irq, adc); + clk_disable_unprepare(adc->ipg_clk); + regulator_disable(adc->vref); + + return 0; +} + +static int imx93_adc_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct imx93_adc *adc = iio_priv(indio_dev); + + imx93_adc_power_down(adc); + clk_disable_unprepare(adc->ipg_clk); + regulator_disable(adc->vref); + + return 0; +} + +static int imx93_adc_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct imx93_adc *adc = iio_priv(indio_dev); + int ret; + + ret = regulator_enable(adc->vref); + if (ret) { + dev_err(dev, + "Can't enable adc reference top voltage, err = %d\n", + ret); + return ret; + } + + ret = clk_prepare_enable(adc->ipg_clk); + if (ret) { + dev_err(dev, "Could not prepare or enable clock.\n"); + goto err_disable_reg; + } + + imx93_adc_power_up(adc); + + return 0; + +err_disable_reg: + regulator_disable(adc->vref); + + return ret; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(imx93_adc_pm_ops, + imx93_adc_runtime_suspend, + imx93_adc_runtime_resume, NULL); + +static const struct of_device_id imx93_adc_match[] = { + { .compatible = "nxp,imx93-adc", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx93_adc_match); + +static struct platform_driver imx93_adc_driver = { + .probe = imx93_adc_probe, + .remove = imx93_adc_remove, + .driver = { + .name = IMX93_ADC_DRIVER_NAME, + .of_match_table = imx93_adc_match, + .pm = pm_ptr(&imx93_adc_pm_ops), + }, +}; + +module_platform_driver(imx93_adc_driver); + +MODULE_DESCRIPTION("NXP i.MX93 ADC driver"); +MODULE_AUTHOR("Haibo Chen "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 20d889a91ce94abfc9c240d19294ac1f2e716780 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 24 Jan 2023 10:04:13 +0000 Subject: iio: adc: imx93: Fix spelling mistake "geting" -> "getting" Thrre is a spelling mistake in a dev_err_probe message. Fix it. Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20230124100413.684416-1-colin.i.king@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/imx93_adc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/imx93_adc.c b/drivers/iio/adc/imx93_adc.c index d8de8284e13d..a775d2e40567 100644 --- a/drivers/iio/adc/imx93_adc.c +++ b/drivers/iio/adc/imx93_adc.c @@ -315,7 +315,7 @@ static int imx93_adc_probe(struct platform_device *pdev) adc->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(adc->regs)) return dev_err_probe(dev, PTR_ERR(adc->regs), - "Failed geting ioremap resource\n"); + "Failed getting ioremap resource\n"); /* The third irq is for ADC conversion usage */ adc->irq = platform_get_irq(pdev, 2); -- cgit v1.2.3 From c612bcb75fa17807f17ef2dbb1b56fde7234bd9f Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Tue, 24 Jan 2023 11:43:01 -0800 Subject: iio: imu: fix spdx format checkpatch reports WARNING: Missing or malformed SPDX-License-Identifier tag in line 1 FILE: drivers/iio/imu/bno055/bno055_ser_trace.c:1: +//SPDX-License-Identifier: GPL-2.0 Add a space Fixes: 2eef5a9cc643 ("iio: imu: add BNO055 serdev driver") Signed-off-by: Tom Rix Link: https://lore.kernel.org/r/20230124194301.656518-1-trix@redhat.com Signed-off-by: Jonathan Cameron --- drivers/iio/imu/bno055/bno055_ser_trace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/imu/bno055/bno055_ser_trace.c b/drivers/iio/imu/bno055/bno055_ser_trace.c index 48397b66daef..ab564186d19c 100644 --- a/drivers/iio/imu/bno055/bno055_ser_trace.c +++ b/drivers/iio/imu/bno055/bno055_ser_trace.c @@ -1,4 +1,4 @@ -//SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0 /* * bno055_ser Trace Support -- cgit v1.2.3 From e56d2c34ce9dc122b1a618172ec0e05e50adb9e9 Mon Sep 17 00:00:00 2001 From: Harshit Mogalapalli Date: Thu, 26 Jan 2023 07:21:46 -0800 Subject: iio: accel: mma9551_core: Prevent uninitialized variable in mma9551_read_status_word() Smatch Warns: drivers/iio/accel/mma9551_core.c:357 mma9551_read_status_word() error: uninitialized symbol 'v'. When (offset >= 1 << 12) is true mma9551_transfer() will return -EINVAL without 'v' being initialized, so check for the error and return. Note: Not a bug as such because the caller checks return value and doesn't not use this parameter in the problem case. Signed-off-by: Harshit Mogalapalli Link: https://lore.kernel.org/r/20230126152147.3585874-1-harshit.m.mogalapalli@oracle.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mma9551_core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/mma9551_core.c b/drivers/iio/accel/mma9551_core.c index 64ca7d7a9673..86437ddc5ca1 100644 --- a/drivers/iio/accel/mma9551_core.c +++ b/drivers/iio/accel/mma9551_core.c @@ -354,9 +354,12 @@ int mma9551_read_status_word(struct i2c_client *client, u8 app_id, ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS, reg, NULL, 0, (u8 *)&v, 2); + if (ret < 0) + return ret; + *val = be16_to_cpu(v); - return ret; + return 0; } EXPORT_SYMBOL_NS(mma9551_read_status_word, IIO_MMA9551); -- cgit v1.2.3 From 64a68158738ec8f520347144352f7a09bdb9e169 Mon Sep 17 00:00:00 2001 From: Harshit Mogalapalli Date: Thu, 26 Jan 2023 07:36:09 -0800 Subject: iio: accel: mma9551_core: Prevent uninitialized variable in mma9551_read_config_word() Smatch Warns: drivers/iio/accel/mma9551_core.c:299 mma9551_read_config_word() error: uninitialized symbol 'v'. When (offset >= 1 << 12) is true mma9551_transfer() will return -EINVAL without 'v' being initialized, so check for the error and return. Note: No actual bug as caller checks the return value and does not use the parameter in the problem case. Signed-off-by: Harshit Mogalapalli Link: https://lore.kernel.org/r/20230126153610.3586243-1-harshit.m.mogalapalli@oracle.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mma9551_core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/mma9551_core.c b/drivers/iio/accel/mma9551_core.c index 86437ddc5ca1..b898f865fb87 100644 --- a/drivers/iio/accel/mma9551_core.c +++ b/drivers/iio/accel/mma9551_core.c @@ -296,9 +296,12 @@ int mma9551_read_config_word(struct i2c_client *client, u8 app_id, ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG, reg, NULL, 0, (u8 *)&v, 2); + if (ret < 0) + return ret; + *val = be16_to_cpu(v); - return ret; + return 0; } EXPORT_SYMBOL_NS(mma9551_read_config_word, IIO_MMA9551); -- cgit v1.2.3 From 4ae612e4af95d72e36f478cad66d47c06b86de68 Mon Sep 17 00:00:00 2001 From: Anup Sharma Date: Sat, 28 Jan 2023 23:18:17 +0530 Subject: iio: adc: ad7291: Fix indentation error by adding extra spaces Added extra spaces before statements to fix following indentation warnings reported by checkpatch.pl. WARNING: Statements should start on a tabstop + return 0; Signed-off-by: Anup Sharma Link: https://lore.kernel.org/r/Y9Vf4Tp8JKvy+y0u@yoga Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7291.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ad7291.c b/drivers/iio/adc/ad7291.c index 3dd0105f63d7..f9ee189925de 100644 --- a/drivers/iio/adc/ad7291.c +++ b/drivers/iio/adc/ad7291.c @@ -179,7 +179,7 @@ static unsigned int ad7291_threshold_reg(const struct iio_chan_spec *chan, offset = AD7291_VOLTAGE_OFFSET; break; default: - return 0; + return 0; } switch (info) { -- cgit v1.2.3