From ba6ec48e76bcd405c47d90b4497ddbcd4c9a578f Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Mon, 20 May 2019 21:13:32 -0500 Subject: iio: Add driver for Infineon DPS310 The DPS310 is a temperature and pressure sensor. It can be accessed over i2c and SPI, but this driver only supports polling over i2c. Signed-off-by: Eddie James Signed-off-by: Joel Stanley Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/Kconfig | 11 + drivers/iio/pressure/Makefile | 1 + drivers/iio/pressure/dps310.c | 468 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 480 insertions(+) create mode 100644 drivers/iio/pressure/dps310.c (limited to 'drivers/iio') diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig index efeb89f3df71..97976de3f07e 100644 --- a/drivers/iio/pressure/Kconfig +++ b/drivers/iio/pressure/Kconfig @@ -52,6 +52,17 @@ config IIO_CROS_EC_BARO To compile this driver as a module, choose M here: the module will be called cros_ec_baro. +config DPS310 + tristate "Infineon DPS310 pressure and temperature sensor" + depends on I2C + select REGMAP_I2C + help + Support for the Infineon DPS310 digital barometric pressure sensor. + It can be accessed over I2C bus. + + This driver can also be built as a module. If so, the module will be + called dps310. + config HID_SENSOR_PRESS depends on HID_SENSOR_HUB select IIO_BUFFER diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile index c2058d7b2f93..d8f5ace1f25d 100644 --- a/drivers/iio/pressure/Makefile +++ b/drivers/iio/pressure/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_BMP280) += bmp280.o bmp280-objs := bmp280-core.o bmp280-regmap.o obj-$(CONFIG_BMP280_I2C) += bmp280-i2c.o obj-$(CONFIG_BMP280_SPI) += bmp280-spi.o +obj-$(CONFIG_DPS310) += dps310.o obj-$(CONFIG_IIO_CROS_EC_BARO) += cros_ec_baro.o obj-$(CONFIG_HID_SENSOR_PRESS) += hid-sensor-press.o obj-$(CONFIG_HP03) += hp03.o diff --git a/drivers/iio/pressure/dps310.c b/drivers/iio/pressure/dps310.c new file mode 100644 index 000000000000..9acfccba1d50 --- /dev/null +++ b/drivers/iio/pressure/dps310.c @@ -0,0 +1,468 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright IBM Corp 2019 +/* + * The DPS310 is a barometric pressure and temperature sensor. + * Currently only reading a single temperature is supported by + * this driver. + * + * https://www.infineon.com/dgdl/?fileId=5546d462576f34750157750826c42242 + * + * Temperature calculation: + * c0 * 0.5 + c1 * T_raw / kT °C + * + * TODO: + * - Pressure sensor readings + * - Optionally support the FIFO + */ + +#include +#include +#include + +#include +#include + +#define DPS310_DEV_NAME "dps310" + +#define DPS310_PRS_B0 0x00 +#define DPS310_PRS_B1 0x01 +#define DPS310_PRS_B2 0x02 +#define DPS310_TMP_B0 0x03 +#define DPS310_TMP_B1 0x04 +#define DPS310_TMP_B2 0x05 +#define DPS310_PRS_CFG 0x06 +#define DPS310_TMP_CFG 0x07 +#define DPS310_TMP_RATE_BITS GENMASK(6, 4) +#define DPS310_TMP_PRC_BITS GENMASK(3, 0) +#define DPS310_TMP_EXT BIT(7) +#define DPS310_MEAS_CFG 0x08 +#define DPS310_MEAS_CTRL_BITS GENMASK(2, 0) +#define DPS310_PRS_EN BIT(0) +#define DPS310_TEMP_EN BIT(1) +#define DPS310_BACKGROUND BIT(2) +#define DPS310_PRS_RDY BIT(4) +#define DPS310_TMP_RDY BIT(5) +#define DPS310_SENSOR_RDY BIT(6) +#define DPS310_COEF_RDY BIT(7) +#define DPS310_CFG_REG 0x09 +#define DPS310_INT_HL BIT(7) +#define DPS310_TMP_SHIFT_EN BIT(3) +#define DPS310_PRS_SHIFT_EN BIT(4) +#define DPS310_FIFO_EN BIT(5) +#define DPS310_SPI_EN BIT(6) +#define DPS310_RESET 0x0c +#define DPS310_RESET_MAGIC 0x09 +#define DPS310_COEF_BASE 0x10 +#define DPS310_COEF_SRC 0x28 + +/* Make sure sleep time is <= 20ms for usleep_range */ +#define DPS310_POLL_SLEEP_US(t) min(20000, (t) / 8) +/* Silently handle error in rate value here */ +#define DPS310_POLL_TIMEOUT_US(rc) ((rc) <= 0 ? 1000000 : 1000000 / (rc)) + +#define DPS310_PRS_BASE DPS310_PRS_B0 +#define DPS310_TMP_BASE DPS310_TMP_B0 + +/* + * These values (defined in the spec) indicate how to scale the raw register + * values for each level of precision available. + */ +static const int scale_factors[] = { + 524288, + 1572864, + 3670016, + 7864320, + 253952, + 516096, + 1040384, + 2088960, +}; + +struct dps310_data { + struct i2c_client *client; + struct regmap *regmap; + struct mutex lock; /* Lock for sequential HW access functions */ + + s32 c0, c1; + s32 temp_raw; +}; + +static const struct iio_chan_spec dps310_channels[] = { + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | + BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_PROCESSED), + }, +}; + +/* To be called after checking the COEF_RDY bit in MEAS_CFG */ +static int dps310_get_temp_coef(struct dps310_data *data) +{ + int rc; + u8 coef[3]; + u32 c0, c1; + + /* + * Read temperature calibration coefficients c0 and c1 from the + * COEF register. The numbers are 12-bit 2's compliment numbers + */ + rc = regmap_bulk_read(data->regmap, DPS310_COEF_BASE, coef, + sizeof(coef)); + if (rc < 0) + return rc; + + c0 = (coef[0] << 4) | (coef[1] >> 4); + data->c0 = sign_extend32(c0, 11); + + c1 = ((coef[1] & GENMASK(3, 0)) << 8) | coef[2]; + data->c1 = sign_extend32(c1, 11); + + return 0; +} + +static int dps310_get_temp_precision(struct dps310_data *data) +{ + int rc; + int val; + + rc = regmap_read(data->regmap, DPS310_TMP_CFG, &val); + if (rc < 0) + return rc; + + /* + * Scale factor is bottom 4 bits of the register, but 1111 is + * reserved so just grab bottom three + */ + return BIT(val & GENMASK(2, 0)); +} + +/* Called with lock held */ +static int dps310_set_temp_precision(struct dps310_data *data, int val) +{ + int rc; + u8 shift_en; + + if (val < 0 || val > 128) + return -EINVAL; + + shift_en = val >= 16 ? DPS310_TMP_SHIFT_EN : 0; + rc = regmap_write_bits(data->regmap, DPS310_CFG_REG, + DPS310_TMP_SHIFT_EN, shift_en); + if (rc) + return rc; + + return regmap_update_bits(data->regmap, DPS310_TMP_CFG, + DPS310_TMP_PRC_BITS, ilog2(val)); +} + +/* Called with lock held */ +static int dps310_set_temp_samp_freq(struct dps310_data *data, int freq) +{ + u8 val; + + if (freq < 0 || freq > 128) + return -EINVAL; + + val = ilog2(freq) << 4; + + return regmap_update_bits(data->regmap, DPS310_TMP_CFG, + DPS310_TMP_RATE_BITS, val); +} + +static int dps310_get_temp_samp_freq(struct dps310_data *data) +{ + int rc; + int val; + + rc = regmap_read(data->regmap, DPS310_TMP_CFG, &val); + if (rc < 0) + return rc; + + return BIT((val & DPS310_TMP_RATE_BITS) >> 4); +} + +static int dps310_get_temp_k(struct dps310_data *data) +{ + int rc = dps310_get_temp_precision(data); + + if (rc < 0) + return rc; + + return scale_factors[ilog2(rc)]; +} + +static int dps310_read_temp(struct dps310_data *data) +{ + int rc; + int rate; + int ready; + int timeout; + s32 raw; + u8 val[3]; + + if (mutex_lock_interruptible(&data->lock)) + return -EINTR; + + rate = dps310_get_temp_samp_freq(data); + timeout = DPS310_POLL_TIMEOUT_US(rate); + + /* Poll for sensor readiness; base the timeout upon the sample rate. */ + rc = regmap_read_poll_timeout(data->regmap, DPS310_MEAS_CFG, ready, + ready & DPS310_TMP_RDY, + DPS310_POLL_SLEEP_US(timeout), timeout); + if (rc < 0) + goto done; + + rc = regmap_bulk_read(data->regmap, DPS310_TMP_BASE, val, sizeof(val)); + if (rc < 0) + goto done; + + raw = (val[0] << 16) | (val[1] << 8) | val[2]; + data->temp_raw = sign_extend32(raw, 23); + +done: + mutex_unlock(&data->lock); + return rc; +} + +static bool dps310_is_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case DPS310_PRS_CFG: + case DPS310_TMP_CFG: + case DPS310_MEAS_CFG: + case DPS310_CFG_REG: + case DPS310_RESET: + return true; + default: + return false; + } +} + +static bool dps310_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case DPS310_PRS_B0: + case DPS310_PRS_B1: + case DPS310_PRS_B2: + case DPS310_TMP_B0: + case DPS310_TMP_B1: + case DPS310_TMP_B2: + case DPS310_MEAS_CFG: + return true; + default: + return false; + } +} + +static int dps310_write_raw(struct iio_dev *iio, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + int rc; + struct dps310_data *data = iio_priv(iio); + + if (chan->type != IIO_TEMP) + return -EINVAL; + + if (mutex_lock_interruptible(&data->lock)) + return -EINTR; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + rc = dps310_set_temp_samp_freq(data, val); + break; + + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + rc = dps310_set_temp_precision(data, val); + break; + + default: + rc = -EINVAL; + break; + } + + mutex_unlock(&data->lock); + return rc; +} + +static int dps310_calculate_temp(struct dps310_data *data) +{ + s64 c0; + s64 t; + int kt = dps310_get_temp_k(data); + + if (kt < 0) + return kt; + + /* Obtain inverse-scaled offset */ + c0 = div_s64((s64)kt * (s64)data->c0, 2); + + /* Add the offset to the unscaled temperature */ + t = c0 + ((s64)data->temp_raw * (s64)data->c1); + + /* Convert to milliCelsius and scale the temperature */ + return (int)div_s64(t * 1000LL, kt); +} + +static int dps310_read_raw(struct iio_dev *iio, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct dps310_data *data = iio_priv(iio); + int rc; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + rc = dps310_get_temp_samp_freq(data); + if (rc < 0) + return rc; + + *val = rc; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_PROCESSED: + rc = dps310_read_temp(data); + if (rc) + return rc; + + rc = dps310_calculate_temp(data); + if (rc < 0) + return rc; + + *val = rc; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + rc = dps310_get_temp_precision(data); + if (rc < 0) + return rc; + + *val = rc; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static void dps310_reset(void *action_data) +{ + struct dps310_data *data = action_data; + + regmap_write(data->regmap, DPS310_RESET, DPS310_RESET_MAGIC); +} + +static const struct regmap_config dps310_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = dps310_is_writeable_reg, + .volatile_reg = dps310_is_volatile_reg, + .cache_type = REGCACHE_RBTREE, + .max_register = DPS310_COEF_SRC, +}; + +static const struct iio_info dps310_info = { + .read_raw = dps310_read_raw, + .write_raw = dps310_write_raw, +}; + +static int dps310_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct dps310_data *data; + struct iio_dev *iio; + int rc, ready; + + iio = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!iio) + return -ENOMEM; + + data = iio_priv(iio); + data->client = client; + mutex_init(&data->lock); + + iio->dev.parent = &client->dev; + iio->name = id->name; + iio->channels = dps310_channels; + iio->num_channels = ARRAY_SIZE(dps310_channels); + iio->info = &dps310_info; + iio->modes = INDIO_DIRECT_MODE; + + data->regmap = devm_regmap_init_i2c(client, &dps310_regmap_config); + if (IS_ERR(data->regmap)) + return PTR_ERR(data->regmap); + + /* Register to run the device reset when the device is removed */ + rc = devm_add_action_or_reset(&client->dev, dps310_reset, data); + if (rc) + return rc; + + /* + * Set up external (MEMS) temperature sensor in single sample, one + * measurement per second mode + */ + rc = regmap_write(data->regmap, DPS310_TMP_CFG, DPS310_TMP_EXT); + if (rc < 0) + return rc; + + /* Temp shift is disabled when PRC <= 8 */ + rc = regmap_write_bits(data->regmap, DPS310_CFG_REG, + DPS310_TMP_SHIFT_EN, 0); + if (rc < 0) + return rc; + + /* MEAS_CFG doesn't update correctly unless first written with 0 */ + rc = regmap_write_bits(data->regmap, DPS310_MEAS_CFG, + DPS310_MEAS_CTRL_BITS, 0); + if (rc < 0) + return rc; + + /* Turn on temperature measurement in the background */ + rc = regmap_write_bits(data->regmap, DPS310_MEAS_CFG, + DPS310_MEAS_CTRL_BITS, + DPS310_TEMP_EN | DPS310_BACKGROUND); + if (rc < 0) + return rc; + + /* + * Calibration coefficients required for reporting temperature. + * They are available 40ms after the device has started + */ + rc = regmap_read_poll_timeout(data->regmap, DPS310_MEAS_CFG, ready, + ready & DPS310_COEF_RDY, 10000, 40000); + if (rc < 0) + return rc; + + rc = dps310_get_temp_coef(data); + if (rc < 0) + return rc; + + rc = devm_iio_device_register(&client->dev, iio); + if (rc) + return rc; + + i2c_set_clientdata(client, iio); + + return 0; +} + +static const struct i2c_device_id dps310_id[] = { + { DPS310_DEV_NAME, 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, dps310_id); + +static struct i2c_driver dps310_driver = { + .driver = { + .name = DPS310_DEV_NAME, + }, + .probe = dps310_probe, + .id_table = dps310_id, +}; +module_i2c_driver(dps310_driver); + +MODULE_AUTHOR("Joel Stanley "); +MODULE_DESCRIPTION("Infineon DPS310 pressure and temperature sensor"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-70-g09d2 From cc8baffe176197645ed674b38cef1d8548769a69 Mon Sep 17 00:00:00 2001 From: Christopher Bostic Date: Mon, 20 May 2019 21:13:33 -0500 Subject: iio: dps310: Temperature measurement errata Add a manufacturer's suggested workaround to deal with early revisions of chip that don't indicate correct temperature. Readings can be in the ~60C range when they should be in the ~20's. Signed-off-by: Christopher Bostic Signed-off-by: Joel Stanley Signed-off-by: Eddie James Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/dps310.c | 52 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/pressure/dps310.c b/drivers/iio/pressure/dps310.c index 9acfccba1d50..a093e3a15665 100644 --- a/drivers/iio/pressure/dps310.c +++ b/drivers/iio/pressure/dps310.c @@ -53,7 +53,6 @@ #define DPS310_RESET 0x0c #define DPS310_RESET_MAGIC 0x09 #define DPS310_COEF_BASE 0x10 -#define DPS310_COEF_SRC 0x28 /* Make sure sleep time is <= 20ms for usleep_range */ #define DPS310_POLL_SLEEP_US(t) min(20000, (t) / 8) @@ -234,6 +233,10 @@ static bool dps310_is_writeable_reg(struct device *dev, unsigned int reg) case DPS310_MEAS_CFG: case DPS310_CFG_REG: case DPS310_RESET: + /* No documentation available on the registers below */ + case 0x0e: + case 0x0f: + case 0x62: return true; default: return false; @@ -250,6 +253,7 @@ static bool dps310_is_volatile_reg(struct device *dev, unsigned int reg) case DPS310_TMP_B1: case DPS310_TMP_B2: case DPS310_MEAS_CFG: + case 0x32: /* No documentation available on this register */ return true; default: return false; @@ -360,7 +364,7 @@ static const struct regmap_config dps310_regmap_config = { .writeable_reg = dps310_is_writeable_reg, .volatile_reg = dps310_is_volatile_reg, .cache_type = REGCACHE_RBTREE, - .max_register = DPS310_COEF_SRC, + .max_register = 0x62, /* No documentation available on this register */ }; static const struct iio_info dps310_info = { @@ -368,6 +372,46 @@ static const struct iio_info dps310_info = { .write_raw = dps310_write_raw, }; +/* + * Some verions of chip will read temperatures in the ~60C range when + * its actually ~20C. This is the manufacturer recommended workaround + * to correct the issue. The registers used below are undocumented. + */ +static int dps310_temp_workaround(struct dps310_data *data) +{ + int rc; + int reg; + + rc = regmap_read(data->regmap, 0x32, ®); + if (rc < 0) + return rc; + + /* + * If bit 1 is set then the device is okay, and the workaround does not + * need to be applied + */ + if (reg & BIT(1)) + return 0; + + rc = regmap_write(data->regmap, 0x0e, 0xA5); + if (rc < 0) + return rc; + + rc = regmap_write(data->regmap, 0x0f, 0x96); + if (rc < 0) + return rc; + + rc = regmap_write(data->regmap, 0x62, 0x02); + if (rc < 0) + return rc; + + rc = regmap_write(data->regmap, 0x0e, 0x00); + if (rc < 0) + return rc; + + return regmap_write(data->regmap, 0x0f, 0x00); +} + static int dps310_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -439,6 +483,10 @@ static int dps310_probe(struct i2c_client *client, if (rc < 0) return rc; + rc = dps310_temp_workaround(data); + if (rc < 0) + return rc; + rc = devm_iio_device_register(&client->dev, iio); if (rc) return rc; -- cgit v1.2.3-70-g09d2 From d711a3c7dc829c91048276db09b2794ad84498af Mon Sep 17 00:00:00 2001 From: Eddie James Date: Mon, 20 May 2019 21:13:34 -0500 Subject: iio: dps310: Add pressure sensing capability The DPS310 supports measurement of pressure, so support that in the driver. Use background measurement like the temperature sensing and default to lowest precision and lowest measurement rate. Signed-off-by: Eddie James Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/dps310.c | 363 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 337 insertions(+), 26 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/pressure/dps310.c b/drivers/iio/pressure/dps310.c index a093e3a15665..2c1943bbc433 100644 --- a/drivers/iio/pressure/dps310.c +++ b/drivers/iio/pressure/dps310.c @@ -11,11 +11,12 @@ * c0 * 0.5 + c1 * T_raw / kT °C * * TODO: - * - Pressure sensor readings * - Optionally support the FIFO */ #include +#include +#include #include #include @@ -31,6 +32,8 @@ #define DPS310_TMP_B1 0x04 #define DPS310_TMP_B2 0x05 #define DPS310_PRS_CFG 0x06 +#define DPS310_PRS_RATE_BITS GENMASK(6, 4) +#define DPS310_PRS_PRC_BITS GENMASK(3, 0) #define DPS310_TMP_CFG 0x07 #define DPS310_TMP_RATE_BITS GENMASK(6, 4) #define DPS310_TMP_PRC_BITS GENMASK(3, 0) @@ -83,6 +86,8 @@ struct dps310_data { struct mutex lock; /* Lock for sequential HW access functions */ s32 c0, c1; + s32 c00, c10, c20, c30, c01, c11, c21; + s32 pressure_raw; s32 temp_raw; }; @@ -93,33 +98,79 @@ static const struct iio_chan_spec dps310_channels[] = { BIT(IIO_CHAN_INFO_SAMP_FREQ) | BIT(IIO_CHAN_INFO_PROCESSED), }, + { + .type = IIO_PRESSURE, + .info_mask_separate = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | + BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_PROCESSED), + }, }; /* To be called after checking the COEF_RDY bit in MEAS_CFG */ -static int dps310_get_temp_coef(struct dps310_data *data) +static int dps310_get_coefs(struct dps310_data *data) { int rc; - u8 coef[3]; + u8 coef[18]; u32 c0, c1; + u32 c00, c10, c20, c30, c01, c11, c21; - /* - * Read temperature calibration coefficients c0 and c1 from the - * COEF register. The numbers are 12-bit 2's compliment numbers - */ + /* Read all sensor calibration coefficients from the COEF registers. */ rc = regmap_bulk_read(data->regmap, DPS310_COEF_BASE, coef, sizeof(coef)); if (rc < 0) return rc; + /* + * Calculate temperature calibration coefficients c0 and c1. The + * numbers are 12-bit 2's complement numbers. + */ c0 = (coef[0] << 4) | (coef[1] >> 4); data->c0 = sign_extend32(c0, 11); c1 = ((coef[1] & GENMASK(3, 0)) << 8) | coef[2]; data->c1 = sign_extend32(c1, 11); + /* + * Calculate pressure calibration coefficients. c00 and c10 are 20 bit + * 2's complement numbers, while the rest are 16 bit 2's complement + * numbers. + */ + c00 = (coef[3] << 12) | (coef[4] << 4) | (coef[5] >> 4); + data->c00 = sign_extend32(c00, 19); + + c10 = ((coef[5] & GENMASK(3, 0)) << 16) | (coef[6] << 8) | coef[7]; + data->c10 = sign_extend32(c10, 19); + + c01 = (coef[8] << 8) | coef[9]; + data->c01 = sign_extend32(c01, 15); + + c11 = (coef[10] << 8) | coef[11]; + data->c11 = sign_extend32(c11, 15); + + c20 = (coef[12] << 8) | coef[13]; + data->c20 = sign_extend32(c20, 15); + + c21 = (coef[14] << 8) | coef[15]; + data->c21 = sign_extend32(c21, 15); + + c30 = (coef[16] << 8) | coef[17]; + data->c30 = sign_extend32(c30, 15); + return 0; } +static int dps310_get_pres_precision(struct dps310_data *data) +{ + int rc; + int val; + + rc = regmap_read(data->regmap, DPS310_PRS_CFG, &val); + if (rc < 0) + return rc; + + return BIT(val & GENMASK(2, 0)); +} + static int dps310_get_temp_precision(struct dps310_data *data) { int rc; @@ -136,6 +187,25 @@ static int dps310_get_temp_precision(struct dps310_data *data) return BIT(val & GENMASK(2, 0)); } +/* Called with lock held */ +static int dps310_set_pres_precision(struct dps310_data *data, int val) +{ + int rc; + u8 shift_en; + + if (val < 0 || val > 128) + return -EINVAL; + + shift_en = val >= 16 ? DPS310_PRS_SHIFT_EN : 0; + rc = regmap_write_bits(data->regmap, DPS310_CFG_REG, + DPS310_PRS_SHIFT_EN, shift_en); + if (rc) + return rc; + + return regmap_update_bits(data->regmap, DPS310_PRS_CFG, + DPS310_PRS_PRC_BITS, ilog2(val)); +} + /* Called with lock held */ static int dps310_set_temp_precision(struct dps310_data *data, int val) { @@ -155,6 +225,20 @@ static int dps310_set_temp_precision(struct dps310_data *data, int val) DPS310_TMP_PRC_BITS, ilog2(val)); } +/* Called with lock held */ +static int dps310_set_pres_samp_freq(struct dps310_data *data, int freq) +{ + u8 val; + + if (freq < 0 || freq > 128) + return -EINVAL; + + val = ilog2(freq) << 4; + + return regmap_update_bits(data->regmap, DPS310_PRS_CFG, + DPS310_PRS_RATE_BITS, val); +} + /* Called with lock held */ static int dps310_set_temp_samp_freq(struct dps310_data *data, int freq) { @@ -169,6 +253,18 @@ static int dps310_set_temp_samp_freq(struct dps310_data *data, int freq) DPS310_TMP_RATE_BITS, val); } +static int dps310_get_pres_samp_freq(struct dps310_data *data) +{ + int rc; + int val; + + rc = regmap_read(data->regmap, DPS310_PRS_CFG, &val); + if (rc < 0) + return rc; + + return BIT((val & DPS310_PRS_RATE_BITS) >> 4); +} + static int dps310_get_temp_samp_freq(struct dps310_data *data) { int rc; @@ -181,6 +277,16 @@ static int dps310_get_temp_samp_freq(struct dps310_data *data) return BIT((val & DPS310_TMP_RATE_BITS) >> 4); } +static int dps310_get_pres_k(struct dps310_data *data) +{ + int rc = dps310_get_pres_precision(data); + + if (rc < 0) + return rc; + + return scale_factors[ilog2(rc)]; +} + static int dps310_get_temp_k(struct dps310_data *data) { int rc = dps310_get_temp_precision(data); @@ -191,7 +297,7 @@ static int dps310_get_temp_k(struct dps310_data *data) return scale_factors[ilog2(rc)]; } -static int dps310_read_temp(struct dps310_data *data) +static int dps310_read_pres_raw(struct dps310_data *data) { int rc; int rate; @@ -203,23 +309,67 @@ static int dps310_read_temp(struct dps310_data *data) if (mutex_lock_interruptible(&data->lock)) return -EINTR; - rate = dps310_get_temp_samp_freq(data); + rate = dps310_get_pres_samp_freq(data); timeout = DPS310_POLL_TIMEOUT_US(rate); /* Poll for sensor readiness; base the timeout upon the sample rate. */ rc = regmap_read_poll_timeout(data->regmap, DPS310_MEAS_CFG, ready, - ready & DPS310_TMP_RDY, + ready & DPS310_PRS_RDY, DPS310_POLL_SLEEP_US(timeout), timeout); + if (rc) + goto done; + + rc = regmap_bulk_read(data->regmap, DPS310_PRS_BASE, val, sizeof(val)); if (rc < 0) goto done; + raw = (val[0] << 16) | (val[1] << 8) | val[2]; + data->pressure_raw = sign_extend32(raw, 23); + +done: + mutex_unlock(&data->lock); + return rc; +} + +/* Called with lock held */ +static int dps310_read_temp_ready(struct dps310_data *data) +{ + int rc; + u8 val[3]; + s32 raw; + rc = regmap_bulk_read(data->regmap, DPS310_TMP_BASE, val, sizeof(val)); if (rc < 0) - goto done; + return rc; raw = (val[0] << 16) | (val[1] << 8) | val[2]; data->temp_raw = sign_extend32(raw, 23); + return 0; +} + +static int dps310_read_temp_raw(struct dps310_data *data) +{ + int rc; + int rate; + int ready; + int timeout; + + if (mutex_lock_interruptible(&data->lock)) + return -EINTR; + + rate = dps310_get_temp_samp_freq(data); + timeout = DPS310_POLL_TIMEOUT_US(rate); + + /* Poll for sensor readiness; base the timeout upon the sample rate. */ + rc = regmap_read_poll_timeout(data->regmap, DPS310_MEAS_CFG, ready, + ready & DPS310_TMP_RDY, + DPS310_POLL_SLEEP_US(timeout), timeout); + if (rc < 0) + goto done; + + rc = dps310_read_temp_ready(data); + done: mutex_unlock(&data->lock); return rc; @@ -267,19 +417,40 @@ static int dps310_write_raw(struct iio_dev *iio, int rc; struct dps310_data *data = iio_priv(iio); - if (chan->type != IIO_TEMP) - return -EINVAL; - if (mutex_lock_interruptible(&data->lock)) return -EINTR; switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: - rc = dps310_set_temp_samp_freq(data, val); + switch (chan->type) { + case IIO_PRESSURE: + rc = dps310_set_pres_samp_freq(data, val); + break; + + case IIO_TEMP: + rc = dps310_set_temp_samp_freq(data, val); + break; + + default: + rc = -EINVAL; + break; + } break; case IIO_CHAN_INFO_OVERSAMPLING_RATIO: - rc = dps310_set_temp_precision(data, val); + switch (chan->type) { + case IIO_PRESSURE: + rc = dps310_set_pres_precision(data, val); + break; + + case IIO_TEMP: + rc = dps310_set_temp_precision(data, val); + break; + + default: + rc = -EINVAL; + break; + } break; default: @@ -291,6 +462,124 @@ static int dps310_write_raw(struct iio_dev *iio, return rc; } +static int dps310_calculate_pressure(struct dps310_data *data) +{ + int i; + int rc; + int t_ready; + int kpi = dps310_get_pres_k(data); + int kti = dps310_get_temp_k(data); + s64 rem = 0ULL; + s64 pressure = 0ULL; + s64 p; + s64 t; + s64 denoms[7]; + s64 nums[7]; + s64 rems[7]; + s64 kp; + s64 kt; + + if (kpi < 0) + return kpi; + + if (kti < 0) + return kti; + + kp = (s64)kpi; + kt = (s64)kti; + + /* Refresh temp if it's ready, otherwise just use the latest value */ + if (mutex_trylock(&data->lock)) { + rc = regmap_read(data->regmap, DPS310_MEAS_CFG, &t_ready); + if (rc >= 0 && t_ready & DPS310_TMP_RDY) + dps310_read_temp_ready(data); + + mutex_unlock(&data->lock); + } + + p = (s64)data->pressure_raw; + t = (s64)data->temp_raw; + + /* Section 4.9.1 of the DPS310 spec; algebra'd to avoid underflow */ + nums[0] = (s64)data->c00; + denoms[0] = 1LL; + nums[1] = p * (s64)data->c10; + denoms[1] = kp; + nums[2] = p * p * (s64)data->c20; + denoms[2] = kp * kp; + nums[3] = p * p * p * (s64)data->c30; + denoms[3] = kp * kp * kp; + nums[4] = t * (s64)data->c01; + denoms[4] = kt; + nums[5] = t * p * (s64)data->c11; + denoms[5] = kp * kt; + nums[6] = t * p * p * (s64)data->c21; + denoms[6] = kp * kp * kt; + + /* Kernel lacks a div64_s64_rem function; denoms are all positive */ + for (i = 0; i < 7; ++i) { + u64 irem; + + if (nums[i] < 0LL) { + pressure -= div64_u64_rem(-nums[i], denoms[i], &irem); + rems[i] = -irem; + } else { + pressure += div64_u64_rem(nums[i], denoms[i], &irem); + rems[i] = (s64)irem; + } + } + + /* Increase precision and calculate the remainder sum */ + for (i = 0; i < 7; ++i) + rem += div64_s64((s64)rems[i] * 1000000000LL, denoms[i]); + + pressure += div_s64(rem, 1000000000LL); + if (pressure < 0LL) + return -ERANGE; + + return (int)min_t(s64, pressure, INT_MAX); +} + +static int dps310_read_pressure(struct dps310_data *data, int *val, int *val2, + long mask) +{ + int rc; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + rc = dps310_get_pres_samp_freq(data); + if (rc < 0) + return rc; + + *val = rc; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_PROCESSED: + rc = dps310_read_pres_raw(data); + if (rc) + return rc; + + rc = dps310_calculate_pressure(data); + if (rc < 0) + return rc; + + *val = rc; + *val2 = 1000; /* Convert Pa to KPa per IIO ABI */ + return IIO_VAL_FRACTIONAL; + + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + rc = dps310_get_pres_precision(data); + if (rc < 0) + return rc; + + *val = rc; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + static int dps310_calculate_temp(struct dps310_data *data) { s64 c0; @@ -310,11 +599,9 @@ static int dps310_calculate_temp(struct dps310_data *data) return (int)div_s64(t * 1000LL, kt); } -static int dps310_read_raw(struct iio_dev *iio, - struct iio_chan_spec const *chan, - int *val, int *val2, long mask) +static int dps310_read_temp(struct dps310_data *data, int *val, int *val2, + long mask) { - struct dps310_data *data = iio_priv(iio); int rc; switch (mask) { @@ -327,7 +614,7 @@ static int dps310_read_raw(struct iio_dev *iio, return IIO_VAL_INT; case IIO_CHAN_INFO_PROCESSED: - rc = dps310_read_temp(data); + rc = dps310_read_temp_raw(data); if (rc) return rc; @@ -351,6 +638,24 @@ static int dps310_read_raw(struct iio_dev *iio, } } +static int dps310_read_raw(struct iio_dev *iio, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct dps310_data *data = iio_priv(iio); + + switch (chan->type) { + case IIO_PRESSURE: + return dps310_read_pressure(data, val, val2, mask); + + case IIO_TEMP: + return dps310_read_temp(data, val, val2, mask); + + default: + return -EINVAL; + } +} + static void dps310_reset(void *action_data) { struct dps310_data *data = action_data; @@ -443,6 +748,12 @@ static int dps310_probe(struct i2c_client *client, if (rc) return rc; + /* + * Set up pressure sensor in single sample, one measurement per second + * mode + */ + rc = regmap_write(data->regmap, DPS310_PRS_CFG, 0); + /* * Set up external (MEMS) temperature sensor in single sample, one * measurement per second mode @@ -451,9 +762,9 @@ static int dps310_probe(struct i2c_client *client, if (rc < 0) return rc; - /* Temp shift is disabled when PRC <= 8 */ + /* Temp and pressure shifts are disabled when PRC <= 8 */ rc = regmap_write_bits(data->regmap, DPS310_CFG_REG, - DPS310_TMP_SHIFT_EN, 0); + DPS310_PRS_SHIFT_EN | DPS310_TMP_SHIFT_EN, 0); if (rc < 0) return rc; @@ -463,9 +774,9 @@ static int dps310_probe(struct i2c_client *client, if (rc < 0) return rc; - /* Turn on temperature measurement in the background */ + /* Turn on temperature and pressure measurement in the background */ rc = regmap_write_bits(data->regmap, DPS310_MEAS_CFG, - DPS310_MEAS_CTRL_BITS, + DPS310_MEAS_CTRL_BITS, DPS310_PRS_EN | DPS310_TEMP_EN | DPS310_BACKGROUND); if (rc < 0) return rc; @@ -479,7 +790,7 @@ static int dps310_probe(struct i2c_client *client, if (rc < 0) return rc; - rc = dps310_get_temp_coef(data); + rc = dps310_get_coefs(data); if (rc < 0) return rc; -- cgit v1.2.3-70-g09d2 From 13c12f693001c301420c53ae94c252ea6bb08765 Mon Sep 17 00:00:00 2001 From: Luís Ferreira Date: Tue, 2 Apr 2019 17:04:30 +0100 Subject: iio: accel: add missing sensor for some 2-in-1 based ultrabooks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some ultrabooks, like Teclast F6 Pro, use KIOX010A sensor on display and KIOX020A sensor on keyboard base, to detect tablet mode or screen orientation. Signed-off-by: Luís Ferreira Signed-off-by: Jonathan Cameron --- drivers/iio/accel/kxcjk-1013.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index 2922a2e88a1b..bcd4d50b0330 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -1495,6 +1495,7 @@ static const struct acpi_device_id kx_acpi_match[] = { {"KIOX0009", KXTJ21009}, {"KIOX000A", KXCJ91008}, {"KIOX010A", KXCJ91008}, /* KXCJ91008 inside the display of a 2-in-1 */ + {"KIOX020A", KXCJ91008}, {"KXTJ1009", KXTJ21009}, {"KXJ2109", KXTJ21009}, {"SMO8500", KXCJ91008}, -- cgit v1.2.3-70-g09d2 From 47dd8378f953152944b651df92e5f2c7ebb6729c Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Mon, 27 May 2019 15:43:14 +0200 Subject: iio: adc: meson_saradc: update with SPDX Licence identifier Signed-off-by: Neil Armstrong Reviewed-by: Martin Blumenstingl Signed-off-by: Jonathan Cameron --- drivers/iio/adc/meson_saradc.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c index 510d8b7ef3a0..7b28d045d271 100644 --- a/drivers/iio/adc/meson_saradc.c +++ b/drivers/iio/adc/meson_saradc.c @@ -1,14 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Amlogic Meson Successive Approximation Register (SAR) A/D Converter * * Copyright (C) 2017 Martin Blumenstingl - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . */ #include -- cgit v1.2.3-70-g09d2 From 88dd03135063b0f60efda064f7ecccdef57b0634 Mon Sep 17 00:00:00 2001 From: Beniamin Bia Date: Mon, 27 May 2019 15:56:47 +0300 Subject: iio: adc: ad7606: Move oversampling and scale options to chip info The device dependent options which are going to be different for devices which will be supported in the future by this driver, were moved in chip info for a more generic driver. This patch allows supporting more devices by the driver. Also, it is an intermediate step of adding support for ad7616 in software mode. Signed-off-by: Beniamin Bia Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7606.c | 61 ++++++++++++++++++++++++++++++++++-------------- drivers/iio/adc/ad7606.h | 15 +++++++++++- 2 files changed, 58 insertions(+), 18 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c index 24c70c3cefb4..c66ff22f32d2 100644 --- a/drivers/iio/adc/ad7606.c +++ b/drivers/iio/adc/ad7606.c @@ -158,7 +158,7 @@ static int ad7606_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: *val = 0; - *val2 = st->scale_avail[st->range]; + *val2 = st->scale_avail[st->range[0]]; return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_OVERSAMPLING_RATIO: *val = st->oversampling; @@ -194,6 +194,32 @@ static ssize_t in_voltage_scale_available_show(struct device *dev, static IIO_DEVICE_ATTR_RO(in_voltage_scale_available, 0); +static int ad7606_write_scale_hw(struct iio_dev *indio_dev, int ch, int val) +{ + struct ad7606_state *st = iio_priv(indio_dev); + + gpiod_set_value(st->gpio_range, val); + + return 0; +} + +static int ad7606_write_os_hw(struct iio_dev *indio_dev, int val) +{ + struct ad7606_state *st = iio_priv(indio_dev); + DECLARE_BITMAP(values, 3); + + values[0] = val; + + gpiod_set_array_value(ARRAY_SIZE(values), st->gpio_os->desc, + st->gpio_os->info, values); + + /* AD7616 requires a reset to update value */ + if (st->chip_info->os_req_reset) + ad7606_reset(st); + + return 0; +} + static int ad7606_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, @@ -201,15 +227,18 @@ static int ad7606_write_raw(struct iio_dev *indio_dev, long mask) { struct ad7606_state *st = iio_priv(indio_dev); - DECLARE_BITMAP(values, 3); - int i; + int i, ret, ch = 0; switch (mask) { case IIO_CHAN_INFO_SCALE: mutex_lock(&st->lock); i = find_closest(val2, st->scale_avail, st->num_scales); - gpiod_set_value(st->gpio_range, i); - st->range = i; + ret = st->write_scale(indio_dev, chan->address, i); + if (ret < 0) { + mutex_unlock(&st->lock); + return ret; + } + st->range[ch] = i; mutex_unlock(&st->lock); return 0; @@ -218,17 +247,12 @@ static int ad7606_write_raw(struct iio_dev *indio_dev, return -EINVAL; i = find_closest(val, st->oversampling_avail, st->num_os_ratios); - - values[0] = i; - mutex_lock(&st->lock); - gpiod_set_array_value(ARRAY_SIZE(values), st->gpio_os->desc, - st->gpio_os->info, values); - - /* AD7616 requires a reset to update value */ - if (st->chip_info->os_req_reset) - ad7606_reset(st); - + ret = st->write_os(indio_dev, i); + if (ret < 0) { + mutex_unlock(&st->lock); + return ret; + } st->oversampling = st->oversampling_avail[i]; mutex_unlock(&st->lock); @@ -536,7 +560,7 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address, st->bops = bops; st->base_address = base_address; /* tied to logic low, analog input range is +/- 5V */ - st->range = 0; + st->range[0] = 0; st->oversampling = 1; st->scale_avail = ad7606_scale_avail; st->num_scales = ARRAY_SIZE(ad7606_scale_avail); @@ -589,6 +613,9 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address, if (ret) dev_warn(st->dev, "failed to RESET: no RESET GPIO specified\n"); + st->write_scale = ad7606_write_scale_hw; + st->write_os = ad7606_write_os_hw; + st->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, indio_dev->id); if (!st->trig) @@ -643,7 +670,7 @@ static int ad7606_resume(struct device *dev) struct ad7606_state *st = iio_priv(indio_dev); if (st->gpio_standby) { - gpiod_set_value(st->gpio_range, st->range); + gpiod_set_value(st->gpio_range, st->range[0]); gpiod_set_value(st->gpio_standby, 1); ad7606_reset(st); } diff --git a/drivers/iio/adc/ad7606.h b/drivers/iio/adc/ad7606.h index f9ef52131e74..143c30163df9 100644 --- a/drivers/iio/adc/ad7606.h +++ b/drivers/iio/adc/ad7606.h @@ -16,6 +16,12 @@ * oversampling ratios. * @oversampling_num number of elements stored in oversampling_avail array * @os_req_reset some devices require a reset to update oversampling + * @write_scale_sw pointer to the function which writes the scale via spi + in software mode + * @write_os_sw pointer to the function which writes the os via spi + in software mode + * @sw_mode_config: pointer to a function which configured the device + * for software mode */ struct ad7606_chip_info { const struct iio_chan_spec *channels; @@ -23,6 +29,9 @@ struct ad7606_chip_info { const unsigned int *oversampling_avail; unsigned int oversampling_num; bool os_req_reset; + int (*write_scale_sw)(struct iio_dev *indio_dev, int ch, int val); + int (*write_os_sw)(struct iio_dev *indio_dev, int val); + int (*sw_mode_config)(struct iio_dev *indio_dev); }; /** @@ -39,6 +48,8 @@ struct ad7606_chip_info { * @oversampling_avail pointer to the array which stores the available * oversampling ratios. * @num_os_ratios number of elements stored in oversampling_avail array + * @write_scale pointer to the function which writes the scale + * @write_os pointer to the function which writes the os * @lock protect sensor state from concurrent accesses to GPIOs * @gpio_convst GPIO descriptor for conversion start signal (CONVST) * @gpio_reset GPIO descriptor for device hard-reset @@ -57,13 +68,15 @@ struct ad7606_state { const struct ad7606_chip_info *chip_info; struct regulator *reg; const struct ad7606_bus_ops *bops; - unsigned int range; + unsigned int range[16]; unsigned int oversampling; void __iomem *base_address; const unsigned int *scale_avail; unsigned int num_scales; const unsigned int *oversampling_avail; unsigned int num_os_ratios; + int (*write_scale)(struct iio_dev *indio_dev, int ch, int val); + int (*write_os)(struct iio_dev *indio_dev, int val); struct mutex lock; /* protect sensor state */ struct gpio_desc *gpio_convst; -- cgit v1.2.3-70-g09d2 From 3c23e9e8085308d7d960b920af145938a103ddc5 Mon Sep 17 00:00:00 2001 From: Beniamin Bia Date: Mon, 27 May 2019 15:56:48 +0300 Subject: iio: adc: ad7606: Add software configuration Because this driver will support multiple configurations for software, the software configuration was made generic. Signed-off-by: Beniamin Bia Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7606.c | 40 +++++++++++++++++++++++++++++++++++++--- drivers/iio/adc/ad7606.h | 2 ++ 2 files changed, 39 insertions(+), 3 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c index c66ff22f32d2..aba0fd123a51 100644 --- a/drivers/iio/adc/ad7606.c +++ b/drivers/iio/adc/ad7606.c @@ -140,7 +140,7 @@ static int ad7606_read_raw(struct iio_dev *indio_dev, int *val2, long m) { - int ret; + int ret, ch = 0; struct ad7606_state *st = iio_priv(indio_dev); switch (m) { @@ -157,8 +157,10 @@ static int ad7606_read_raw(struct iio_dev *indio_dev, *val = (short)ret; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: + if (st->sw_mode_en) + ch = chan->address; *val = 0; - *val2 = st->scale_avail[st->range[0]]; + *val2 = st->scale_avail[st->range[ch]]; return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_OVERSAMPLING_RATIO: *val = st->oversampling; @@ -233,7 +235,9 @@ static int ad7606_write_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_SCALE: mutex_lock(&st->lock); i = find_closest(val2, st->scale_avail, st->num_scales); - ret = st->write_scale(indio_dev, chan->address, i); + if (st->sw_mode_en) + ch = chan->address; + ret = st->write_scale(indio_dev, ch, i); if (ret < 0) { mutex_unlock(&st->lock); return ret; @@ -616,6 +620,36 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address, st->write_scale = ad7606_write_scale_hw; st->write_os = ad7606_write_os_hw; + if (st->chip_info->sw_mode_config) + st->sw_mode_en = device_property_present(st->dev, + "adi,sw-mode"); + + if (st->sw_mode_en) { + /* After reset, in software mode, ±10 V is set by default */ + memset32(st->range, 2, ARRAY_SIZE(st->range)); + indio_dev->info = &ad7606_info_os_and_range; + + /* + * In software mode, the range gpio has no longer its function. + * Instead, the scale can be configured individually for each + * channel from the range registers. + */ + if (st->chip_info->write_scale_sw) + st->write_scale = st->chip_info->write_scale_sw; + + /* + * In software mode, the oversampling is no longer configured + * with GPIO pins. Instead, the oversampling can be configured + * in configuratiion register. + */ + if (st->chip_info->write_os_sw) + st->write_os = st->chip_info->write_os_sw; + + ret = st->chip_info->sw_mode_config(indio_dev); + if (ret < 0) + return ret; + } + st->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, indio_dev->id); if (!st->trig) diff --git a/drivers/iio/adc/ad7606.h b/drivers/iio/adc/ad7606.h index 143c30163df9..d8a509c2c428 100644 --- a/drivers/iio/adc/ad7606.h +++ b/drivers/iio/adc/ad7606.h @@ -43,6 +43,7 @@ struct ad7606_chip_info { * @range voltage range selection, selects which scale to apply * @oversampling oversampling selection * @base_address address from where to read data in parallel operation + * @sw_mode_en software mode enabled * @scale_avail pointer to the array which stores the available scales * @num_scales number of elements stored in the scale_avail array * @oversampling_avail pointer to the array which stores the available @@ -71,6 +72,7 @@ struct ad7606_state { unsigned int range[16]; unsigned int oversampling; void __iomem *base_address; + bool sw_mode_en; const unsigned int *scale_avail; unsigned int num_scales; const unsigned int *oversampling_avail; -- cgit v1.2.3-70-g09d2 From aa7093299253a8c4cb5bd0053ab9c8128beba5e4 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sat, 8 Jun 2019 12:55:42 +0200 Subject: iio: light: bh1780: simplify getting the adapter of a client We have a dedicated pointer for that, so use it. Much easier to read and less computation involved. Signed-off-by: Wolfram Sang Signed-off-by: Jonathan Cameron --- drivers/iio/light/bh1780.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/light/bh1780.c b/drivers/iio/light/bh1780.c index 036f3bbe323c..4f823e9fab8e 100644 --- a/drivers/iio/light/bh1780.c +++ b/drivers/iio/light/bh1780.c @@ -145,7 +145,7 @@ static int bh1780_probe(struct i2c_client *client, { int ret; struct bh1780_data *bh1780; - struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct i2c_adapter *adapter = client->adapter; struct iio_dev *indio_dev; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) -- cgit v1.2.3-70-g09d2 From 936d3e536dcf88ce80d27bdb637009b13dba6d8c Mon Sep 17 00:00:00 2001 From: Young Xiao <92siuyang@gmail.com> Date: Tue, 4 Jun 2019 20:40:00 +0800 Subject: iio:core: Fix bug in length of event info_mask and catch unhandled bits set in masks. The incorrect limit for the for_each_set_bit loop was noticed whilst fixing this other case. Note that as we only have 3 possible entries a the moment and the value was set to 4, the bug would not have any effect currently. It will bite fairly soon though, so best fix it now. See commit ef4b4856593f ("iio:core: Fix bug in length of event info_mask and catch unhandled bits set in masks.") for details. Signed-off-by: Young Xiao <92siuyang@gmail.com> Reviewed-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/iio') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index f5a4581302f4..dd8873a752dd 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -1107,6 +1107,8 @@ static int iio_device_add_info_mask_type_avail(struct iio_dev *indio_dev, char *avail_postfix; for_each_set_bit(i, infomask, sizeof(*infomask) * 8) { + if (i >= ARRAY_SIZE(iio_chan_info_postfix)) + return -EINVAL; avail_postfix = kasprintf(GFP_KERNEL, "%s_available", iio_chan_info_postfix[i]); -- cgit v1.2.3-70-g09d2 From 677f16813a9245d02247d1379c8c535e361b7d0b Mon Sep 17 00:00:00 2001 From: Martijn Braam Date: Mon, 3 Jun 2019 21:20:39 +0200 Subject: iio: light: stk3310: Add support for stk3335 The stk3335 light/proximity sensor is similar to the stk3310 and stk3311 sensors and works with the stk3310 driver. Signed-off-by: Martijn Braam Signed-off-by: Luca Weiss Signed-off-by: Jonathan Cameron --- drivers/iio/light/stk3310.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c index 6e2a169da950..a25cc35f334e 100644 --- a/drivers/iio/light/stk3310.c +++ b/drivers/iio/light/stk3310.c @@ -40,6 +40,7 @@ #define STK3310_CHIP_ID_VAL 0x13 #define STK3311_CHIP_ID_VAL 0x1D +#define STK3335_CHIP_ID_VAL 0x51 #define STK3310_PSINT_EN 0x01 #define STK3310_PS_MAX_VAL 0xFFFF @@ -454,7 +455,8 @@ static int stk3310_init(struct iio_dev *indio_dev) return ret; if (chipid != STK3310_CHIP_ID_VAL && - chipid != STK3311_CHIP_ID_VAL) { + chipid != STK3311_CHIP_ID_VAL && + chipid != STK3335_CHIP_ID_VAL) { dev_err(&client->dev, "invalid chip id: 0x%x\n", chipid); return -ENODEV; } @@ -666,6 +668,7 @@ static SIMPLE_DEV_PM_OPS(stk3310_pm_ops, stk3310_suspend, stk3310_resume); static const struct i2c_device_id stk3310_i2c_id[] = { {"STK3310", 0}, {"STK3311", 0}, + {"STK3335", 0}, {} }; MODULE_DEVICE_TABLE(i2c, stk3310_i2c_id); @@ -673,6 +676,7 @@ MODULE_DEVICE_TABLE(i2c, stk3310_i2c_id); static const struct acpi_device_id stk3310_acpi_id[] = { {"STK3310", 0}, {"STK3311", 0}, + {"STK3335", 0}, {} }; -- cgit v1.2.3-70-g09d2 From fe93116459717eb363713b2e272006a46e914733 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 3 Jun 2019 16:34:30 -0300 Subject: iio: imx7d_adc: Remove unneeded error message In case of ioremap failure, the core code will take care of printing the error message, so there is no need for having a local error message in the driver. Signed-off-by: Fabio Estevam Signed-off-by: Jonathan Cameron --- drivers/iio/adc/imx7d_adc.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/imx7d_adc.c b/drivers/iio/adc/imx7d_adc.c index 958a34dd88ac..3327994fce3a 100644 --- a/drivers/iio/adc/imx7d_adc.c +++ b/drivers/iio/adc/imx7d_adc.c @@ -497,11 +497,8 @@ static int imx7d_adc_probe(struct platform_device *pdev) info->dev = dev; info->regs = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(info->regs)) { - ret = PTR_ERR(info->regs); - dev_err(dev, "Failed to remap adc memory, err = %d\n", ret); - return ret; - } + if (IS_ERR(info->regs)) + return PTR_ERR(info->regs); irq = platform_get_irq(pdev, 0); if (irq < 0) { -- cgit v1.2.3-70-g09d2 From 9ce92da84ff2eec3792edabbc3d3a654f2e2842a Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 3 Jun 2019 16:34:31 -0300 Subject: iio: imx7d_adc: Introduce a definition for the input clock Since the input clock is always 24MHz, there is no need for storing this value into a variable. Use a definition instead, which is more appropriate in this case. Signed-off-by: Fabio Estevam Signed-off-by: Jonathan Cameron --- drivers/iio/adc/imx7d_adc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/imx7d_adc.c b/drivers/iio/adc/imx7d_adc.c index 3327994fce3a..d77840718bc6 100644 --- a/drivers/iio/adc/imx7d_adc.c +++ b/drivers/iio/adc/imx7d_adc.c @@ -82,6 +82,7 @@ #define IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT 0xf0000 #define IMX7D_ADC_TIMEOUT msecs_to_jiffies(100) +#define IMX7D_ADC_INPUT_CLK 24000000 enum imx7d_adc_clk_pre_div { IMX7D_ADC_ANALOG_CLK_PRE_DIV_4, @@ -276,13 +277,11 @@ static void imx7d_adc_channel_set(struct imx7d_adc *info) static u32 imx7d_adc_get_sample_rate(struct imx7d_adc *info) { - /* input clock is always 24MHz */ - u32 input_clk = 24000000; u32 analogue_core_clk; u32 core_time_unit = info->adc_feature.core_time_unit; u32 tmp; - analogue_core_clk = input_clk / info->pre_div_num; + analogue_core_clk = IMX7D_ADC_INPUT_CLK / info->pre_div_num; tmp = (core_time_unit + 1) * 6; return analogue_core_clk / tmp; -- cgit v1.2.3-70-g09d2 From f5d2f9c293c2b51abd33477a64ffcc5cb64273f5 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 3 Jun 2019 16:34:32 -0300 Subject: iio: imx7d_adc: Fit into a single line All the parameters of devm_request_irq() can fit into a single line, so place them all in a single line for better readability. Signed-off-by: Fabio Estevam Signed-off-by: Jonathan Cameron --- drivers/iio/adc/imx7d_adc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/imx7d_adc.c b/drivers/iio/adc/imx7d_adc.c index d77840718bc6..e31d1a51ee81 100644 --- a/drivers/iio/adc/imx7d_adc.c +++ b/drivers/iio/adc/imx7d_adc.c @@ -531,9 +531,7 @@ static int imx7d_adc_probe(struct platform_device *pdev) indio_dev->channels = imx7d_adc_iio_channels; indio_dev->num_channels = ARRAY_SIZE(imx7d_adc_iio_channels); - ret = devm_request_irq(dev, irq, - imx7d_adc_isr, 0, - dev_name(dev), info); + ret = devm_request_irq(dev, irq, imx7d_adc_isr, 0, dev_name(dev), info); if (ret < 0) { dev_err(dev, "Failed requesting irq, irq = %d\n", irq); return ret; -- cgit v1.2.3-70-g09d2 From 9f3bf94f65fc1e065953572495abfe27a044d45c Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 3 Jun 2019 16:34:33 -0300 Subject: iio: imx7d_adc: Remove unneeded 'average_en' member average_en is always true, so there is not really need for this structure member. Signed-off-by: Fabio Estevam Signed-off-by: Jonathan Cameron --- drivers/iio/adc/imx7d_adc.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/imx7d_adc.c b/drivers/iio/adc/imx7d_adc.c index e31d1a51ee81..85cf8b508385 100644 --- a/drivers/iio/adc/imx7d_adc.c +++ b/drivers/iio/adc/imx7d_adc.c @@ -105,8 +105,6 @@ struct imx7d_adc_feature { enum imx7d_adc_average_num avg_num; u32 core_time_unit; /* impact the sample rate */ - - bool average_en; }; struct imx7d_adc { @@ -184,7 +182,6 @@ static void imx7d_adc_feature_config(struct imx7d_adc *info) info->adc_feature.clk_pre_div = IMX7D_ADC_ANALOG_CLK_PRE_DIV_4; info->adc_feature.avg_num = IMX7D_ADC_AVERAGE_NUM_32; info->adc_feature.core_time_unit = 1; - info->adc_feature.average_en = true; } static void imx7d_adc_sample_rate_set(struct imx7d_adc *info) @@ -245,9 +242,8 @@ static void imx7d_adc_channel_set(struct imx7d_adc *info) /* the channel choose single conversion, and enable average mode */ cfg1 |= (IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN | - IMX7D_REG_ADC_CH_CFG1_CHANNEL_SINGLE); - if (info->adc_feature.average_en) - cfg1 |= IMX7D_REG_ADC_CH_CFG1_CHANNEL_AVG_EN; + IMX7D_REG_ADC_CH_CFG1_CHANNEL_SINGLE | + IMX7D_REG_ADC_CH_CFG1_CHANNEL_AVG_EN); /* * physical channel 0 chose logical channel A -- cgit v1.2.3-70-g09d2 From 81956a93b552205b35250f19120058fc3337e01e Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 8 Jun 2019 14:30:08 +0200 Subject: iio: imu: st_lsm6dsx: get device name from st_lsm6dsx_sensor_settings Introduce sensor name in st_lsm6dsx_sensor_settings table. This is a preliminary patch to add I3C support to st_lsm6dsx since i3c_device_id data structure does not contain a name field Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h | 9 +++-- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 53 +++++++++++++++++++++------- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c | 3 +- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c | 3 +- 4 files changed, 48 insertions(+), 20 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 004a8a1a0027..cd1642bb4ec0 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h @@ -198,7 +198,7 @@ struct st_lsm6dsx_ext_dev_settings { * struct st_lsm6dsx_settings - ST IMU sensor settings * @wai: Sensor WhoAmI default value. * @max_fifo_size: Sensor max fifo length in FIFO words. - * @id: List of hw id supported by the driver configuration. + * @id: List of hw id/device name supported by the driver configuration. * @decimator: List of decimator register info (addr + mask). * @batch: List of FIFO batching register info (addr + mask). * @fifo_ops: Sensor hw FIFO parameters. @@ -208,7 +208,10 @@ struct st_lsm6dsx_ext_dev_settings { struct st_lsm6dsx_settings { u8 wai; u16 max_fifo_size; - enum st_lsm6dsx_hw_id id[ST_LSM6DSX_MAX_ID]; + struct { + enum st_lsm6dsx_hw_id hw_id; + const char *name; + } id[ST_LSM6DSX_MAX_ID]; struct st_lsm6dsx_reg decimator[ST_LSM6DSX_MAX_ID]; struct st_lsm6dsx_reg batch[ST_LSM6DSX_MAX_ID]; struct st_lsm6dsx_fifo_ops fifo_ops; @@ -302,7 +305,7 @@ struct st_lsm6dsx_hw { static const unsigned long st_lsm6dsx_available_scan_masks[] = {0x7, 0x0}; extern const struct dev_pm_ops st_lsm6dsx_pm_ops; -int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name, +int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, struct regmap *regmap); int st_lsm6dsx_sensor_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable); diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index cf82c9049945..04233928d23e 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -125,7 +125,10 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .wai = 0x69, .max_fifo_size = 1365, .id = { - [0] = ST_LSM6DS3_ID, + { + .hw_id = ST_LSM6DS3_ID, + .name = ST_LSM6DS3_DEV_NAME, + }, }, .decimator = { [ST_LSM6DSX_ID_ACC] = { @@ -172,7 +175,10 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .wai = 0x69, .max_fifo_size = 682, .id = { - [0] = ST_LSM6DS3H_ID, + { + .hw_id = ST_LSM6DS3H_ID, + .name = ST_LSM6DS3H_DEV_NAME, + }, }, .decimator = { [ST_LSM6DSX_ID_ACC] = { @@ -219,9 +225,16 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .wai = 0x6a, .max_fifo_size = 682, .id = { - [0] = ST_LSM6DSL_ID, - [1] = ST_LSM6DSM_ID, - [2] = ST_ISM330DLC_ID, + { + .hw_id = ST_LSM6DSL_ID, + .name = ST_LSM6DSL_DEV_NAME, + }, { + .hw_id = ST_LSM6DSM_ID, + .name = ST_LSM6DSM_DEV_NAME, + }, { + .hw_id = ST_ISM330DLC_ID, + .name = ST_ISM330DLC_DEV_NAME, + }, }, .decimator = { [ST_LSM6DSX_ID_ACC] = { @@ -268,8 +281,13 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .wai = 0x6c, .max_fifo_size = 512, .id = { - [0] = ST_LSM6DSO_ID, - [1] = ST_LSM6DSOX_ID, + { + .hw_id = ST_LSM6DSO_ID, + .name = ST_LSM6DSO_DEV_NAME, + }, { + .hw_id = ST_LSM6DSOX_ID, + .name = ST_LSM6DSOX_DEV_NAME, + }, }, .batch = { [ST_LSM6DSX_ID_ACC] = { @@ -334,7 +352,10 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .wai = 0x6b, .max_fifo_size = 512, .id = { - [0] = ST_ASM330LHH_ID, + { + .hw_id = ST_ASM330LHH_ID, + .name = ST_ASM330LHH_DEV_NAME, + }, }, .batch = { [ST_LSM6DSX_ID_ACC] = { @@ -373,7 +394,10 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .wai = 0x6b, .max_fifo_size = 512, .id = { - [0] = ST_LSM6DSR_ID, + { + .hw_id = ST_LSM6DSR_ID, + .name = ST_LSM6DSR_DEV_NAME, + }, }, .batch = { [ST_LSM6DSX_ID_ACC] = { @@ -471,13 +495,14 @@ int st_lsm6dsx_set_page(struct st_lsm6dsx_hw *hw, bool enable) return err; } -static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id) +static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id, + const char **name) { int err, i, j, data; for (i = 0; i < ARRAY_SIZE(st_lsm6dsx_sensor_settings); i++) { for (j = 0; j < ST_LSM6DSX_MAX_ID; j++) { - if (id == st_lsm6dsx_sensor_settings[i].id[j]) + if (id == st_lsm6dsx_sensor_settings[i].id[j].hw_id) break; } if (j < ST_LSM6DSX_MAX_ID) @@ -500,6 +525,7 @@ static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id) return -ENODEV; } + *name = st_lsm6dsx_sensor_settings[i].id[j].name; hw->settings = &st_lsm6dsx_sensor_settings[i]; return 0; @@ -1041,11 +1067,12 @@ static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw, return iio_dev; } -int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name, +int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, struct regmap *regmap) { const struct st_lsm6dsx_shub_settings *hub_settings; struct st_lsm6dsx_hw *hw; + const char *name = NULL; int i, err; hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL); @@ -1066,7 +1093,7 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name, hw->irq = irq; hw->regmap = regmap; - err = st_lsm6dsx_check_whoami(hw, hw_id); + err = st_lsm6dsx_check_whoami(hw, hw_id, &name); if (err < 0) return err; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c index f54370196098..47581a4e456e 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c @@ -36,8 +36,7 @@ static int st_lsm6dsx_i2c_probe(struct i2c_client *client, return PTR_ERR(regmap); } - return st_lsm6dsx_probe(&client->dev, client->irq, - hw_id, id->name, regmap); + return st_lsm6dsx_probe(&client->dev, client->irq, hw_id, regmap); } static const struct of_device_id st_lsm6dsx_i2c_of_match[] = { diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c index 4a4abb2935da..facf66978a4b 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c @@ -36,8 +36,7 @@ static int st_lsm6dsx_spi_probe(struct spi_device *spi) return PTR_ERR(regmap); } - return st_lsm6dsx_probe(&spi->dev, spi->irq, - hw_id, id->name, regmap); + return st_lsm6dsx_probe(&spi->dev, spi->irq, hw_id, regmap); } static const struct of_device_id st_lsm6dsx_spi_of_match[] = { -- cgit v1.2.3-70-g09d2 From 7f699bd14913423ce971f7b8d725448093eaa51a Mon Sep 17 00:00:00 2001 From: Stefan Popa Date: Tue, 4 Jun 2019 17:58:02 +0300 Subject: iio: frequency: adf4371: Add support for ADF4371 PLL The ADF4371 is a frequency synthesizer with an integrated voltage controlled oscillator (VCO) for phase-locked loops (PLLs). The ADF4371 has an integrated VCO with a fundamental output frequency ranging from 4000 MHz to 8000 MHz. In addition, the VCO frequency is connected to divide by 1, 2, 4, 8, 16, 32, or 64 circuits that allows the user to generate radio frequency (RF) output frequencies as low as 62.5 MHz at RF8x. A frequency multiplier at RF16x generates from 8 GHz to 16 GHz. A frequency quadrupler generates frequencies from 16 GHz to 32 GHz at RF32x. RFAUX8x duplicates the frequency range of RF8x or permits direct access to the VCO output. The driver takes the reference input frequency from the device tree and uses it to calculate and maximize the PFD frequency (frequency of the phase frequency detector). The PFD frequency is further used to calculate the timeouts: synthesizer lock, VCO band selection, automatic level calibration (ALC) and PLL settling time. This initial driver exposes the attributes for setting the frequency and enabling/disabling the different adf4371 channels. Datasheet: Link: https://www.analog.com/media/en/technical-documentation/data-sheets/adf4371.pdf Signed-off-by: Stefan Popa Signed-off-by: Jonathan Cameron --- .../ABI/testing/sysfs-bus-iio-frequency-adf4371 | 44 ++ drivers/iio/frequency/Kconfig | 10 + drivers/iio/frequency/Makefile | 1 + drivers/iio/frequency/adf4371.c | 594 +++++++++++++++++++++ 4 files changed, 649 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4371 create mode 100644 drivers/iio/frequency/adf4371.c (limited to 'drivers/iio') diff --git a/Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4371 b/Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4371 new file mode 100644 index 000000000000..302de64cb424 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4371 @@ -0,0 +1,44 @@ +What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_frequency +KernelVersion: +Contact: linux-iio@vger.kernel.org +Description: + Stores the PLL frequency in Hz for channel Y. + Reading returns the actual frequency in Hz. + The ADF4371 has an integrated VCO with fundamendal output + frequency ranging from 4000000000 Hz 8000000000 Hz. + + out_altvoltage0_frequency: + A divide by 1, 2, 4, 8, 16, 32 or circuit generates + frequencies from 62500000 Hz to 8000000000 Hz. + out_altvoltage1_frequency: + This channel duplicates the channel 0 frequency + out_altvoltage2_frequency: + A frequency doubler generates frequencies from + 8000000000 Hz to 16000000000 Hz. + out_altvoltage3_frequency: + A frequency quadrupler generates frequencies from + 16000000000 Hz to 32000000000 Hz. + + Note: writes to one of the channels will affect the frequency of + all the other channels, since it involves changing the VCO + fundamental output frequency. + +What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_name +KernelVersion: +Contact: linux-iio@vger.kernel.org +Description: + Reading returns the datasheet name for channel Y: + + out_altvoltage0_name: RF8x + out_altvoltage1_name: RFAUX8x + out_altvoltage2_name: RF16x + out_altvoltage3_name: RF32x + +What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_powerdown +KernelVersion: +Contact: linux-iio@vger.kernel.org +Description: + This attribute allows the user to power down the PLL and it's + RFOut buffers. + Writing 1 causes the specified channel to power down. + Clearing returns to normal operation. diff --git a/drivers/iio/frequency/Kconfig b/drivers/iio/frequency/Kconfig index dc5e0b72882f..e4a921ff60ea 100644 --- a/drivers/iio/frequency/Kconfig +++ b/drivers/iio/frequency/Kconfig @@ -38,5 +38,15 @@ config ADF4350 To compile this driver as a module, choose M here: the module will be called adf4350. +config ADF4371 + tristate "Analog Devices ADF4371 Wideband Synthesizer" + depends on SPI + select REGMAP_SPI + help + Say yes here to build support for Analog Devices ADF4371 + Wideband Synthesizer. The driver provides direct access via sysfs. + + To compile this driver as a module, choose M here: the + module will be called adf4371. endmenu endmenu diff --git a/drivers/iio/frequency/Makefile b/drivers/iio/frequency/Makefile index 2bca03f3e2e3..2ddda77358a4 100644 --- a/drivers/iio/frequency/Makefile +++ b/drivers/iio/frequency/Makefile @@ -5,3 +5,4 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_AD9523) += ad9523.o obj-$(CONFIG_ADF4350) += adf4350.o +obj-$(CONFIG_ADF4371) += adf4371.o diff --git a/drivers/iio/frequency/adf4371.c b/drivers/iio/frequency/adf4371.c new file mode 100644 index 000000000000..d8c414b70e44 --- /dev/null +++ b/drivers/iio/frequency/adf4371.c @@ -0,0 +1,594 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Analog Devices ADF4371 SPI Wideband Synthesizer driver + * + * Copyright 2019 Analog Devices Inc. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Registers address macro */ +#define ADF4371_REG(x) (x) + +/* ADF4371_REG0 */ +#define ADF4371_ADDR_ASC_MSK BIT(2) +#define ADF4371_ADDR_ASC(x) FIELD_PREP(ADF4371_ADDR_ASC_MSK, x) +#define ADF4371_ADDR_ASC_R_MSK BIT(5) +#define ADF4371_ADDR_ASC_R(x) FIELD_PREP(ADF4371_ADDR_ASC_R_MSK, x) +#define ADF4371_RESET_CMD 0x81 + +/* ADF4371_REG17 */ +#define ADF4371_FRAC2WORD_L_MSK GENMASK(7, 1) +#define ADF4371_FRAC2WORD_L(x) FIELD_PREP(ADF4371_FRAC2WORD_L_MSK, x) +#define ADF4371_FRAC1WORD_MSK BIT(0) +#define ADF4371_FRAC1WORD(x) FIELD_PREP(ADF4371_FRAC1WORD_MSK, x) + +/* ADF4371_REG18 */ +#define ADF4371_FRAC2WORD_H_MSK GENMASK(6, 0) +#define ADF4371_FRAC2WORD_H(x) FIELD_PREP(ADF4371_FRAC2WORD_H_MSK, x) + +/* ADF4371_REG1A */ +#define ADF4371_MOD2WORD_MSK GENMASK(5, 0) +#define ADF4371_MOD2WORD(x) FIELD_PREP(ADF4371_MOD2WORD_MSK, x) + +/* ADF4371_REG24 */ +#define ADF4371_RF_DIV_SEL_MSK GENMASK(6, 4) +#define ADF4371_RF_DIV_SEL(x) FIELD_PREP(ADF4371_RF_DIV_SEL_MSK, x) + +/* ADF4371_REG32 */ +#define ADF4371_TIMEOUT_MSK GENMASK(1, 0) +#define ADF4371_TIMEOUT(x) FIELD_PREP(ADF4371_TIMEOUT_MSK, x) + +/* ADF4371_REG34 */ +#define ADF4371_VCO_ALC_TOUT_MSK GENMASK(4, 0) +#define ADF4371_VCO_ALC_TOUT(x) FIELD_PREP(ADF4371_VCO_ALC_TOUT_MSK, x) + +/* Specifications */ +#define ADF4371_MIN_VCO_FREQ 4000000000ULL /* 4000 MHz */ +#define ADF4371_MAX_VCO_FREQ 8000000000ULL /* 8000 MHz */ +#define ADF4371_MAX_OUT_RF8_FREQ ADF4371_MAX_VCO_FREQ /* Hz */ +#define ADF4371_MIN_OUT_RF8_FREQ (ADF4371_MIN_VCO_FREQ / 64) /* Hz */ +#define ADF4371_MAX_OUT_RF16_FREQ (ADF4371_MAX_VCO_FREQ * 2) /* Hz */ +#define ADF4371_MIN_OUT_RF16_FREQ (ADF4371_MIN_VCO_FREQ * 2) /* Hz */ +#define ADF4371_MAX_OUT_RF32_FREQ (ADF4371_MAX_VCO_FREQ * 4) /* Hz */ +#define ADF4371_MIN_OUT_RF32_FREQ (ADF4371_MIN_VCO_FREQ * 4) /* Hz */ + +#define ADF4371_MAX_FREQ_PFD 250000000UL /* Hz */ +#define ADF4371_MAX_FREQ_REFIN 600000000UL /* Hz */ + +/* MOD1 is a 24-bit primary modulus with fixed value of 2^25 */ +#define ADF4371_MODULUS1 33554432ULL +/* MOD2 is the programmable, 14-bit auxiliary fractional modulus */ +#define ADF4371_MAX_MODULUS2 BIT(14) + +#define ADF4371_CHECK_RANGE(freq, range) \ + ((freq > ADF4371_MAX_ ## range) || (freq < ADF4371_MIN_ ## range)) + +enum { + ADF4371_FREQ, + ADF4371_POWER_DOWN, + ADF4371_CHANNEL_NAME +}; + +enum { + ADF4371_CH_RF8, + ADF4371_CH_RFAUX8, + ADF4371_CH_RF16, + ADF4371_CH_RF32 +}; + +struct adf4371_pwrdown { + unsigned int reg; + unsigned int bit; +}; + +static const char * const adf4371_ch_names[] = { + "RF8x", "RFAUX8x", "RF16x", "RF32x" +}; + +static const struct adf4371_pwrdown adf4371_pwrdown_ch[4] = { + [ADF4371_CH_RF8] = { ADF4371_REG(0x25), 2 }, + [ADF4371_CH_RFAUX8] = { ADF4371_REG(0x72), 3 }, + [ADF4371_CH_RF16] = { ADF4371_REG(0x25), 3 }, + [ADF4371_CH_RF32] = { ADF4371_REG(0x25), 4 }, +}; + +static const struct reg_sequence adf4371_reg_defaults[] = { + { ADF4371_REG(0x0), 0x18 }, + { ADF4371_REG(0x12), 0x40 }, + { ADF4371_REG(0x1E), 0x48 }, + { ADF4371_REG(0x20), 0x14 }, + { ADF4371_REG(0x22), 0x00 }, + { ADF4371_REG(0x23), 0x00 }, + { ADF4371_REG(0x24), 0x80 }, + { ADF4371_REG(0x25), 0x07 }, + { ADF4371_REG(0x27), 0xC5 }, + { ADF4371_REG(0x28), 0x83 }, + { ADF4371_REG(0x2C), 0x44 }, + { ADF4371_REG(0x2D), 0x11 }, + { ADF4371_REG(0x2E), 0x12 }, + { ADF4371_REG(0x2F), 0x94 }, + { ADF4371_REG(0x32), 0x04 }, + { ADF4371_REG(0x35), 0xFA }, + { ADF4371_REG(0x36), 0x30 }, + { ADF4371_REG(0x39), 0x07 }, + { ADF4371_REG(0x3A), 0x55 }, + { ADF4371_REG(0x3E), 0x0C }, + { ADF4371_REG(0x3F), 0x80 }, + { ADF4371_REG(0x40), 0x50 }, + { ADF4371_REG(0x41), 0x28 }, + { ADF4371_REG(0x47), 0xC0 }, + { ADF4371_REG(0x52), 0xF4 }, + { ADF4371_REG(0x70), 0x03 }, + { ADF4371_REG(0x71), 0x60 }, + { ADF4371_REG(0x72), 0x32 }, +}; + +static const struct regmap_config adf4371_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .read_flag_mask = BIT(7), +}; + +struct adf4371_state { + struct spi_device *spi; + struct regmap *regmap; + struct clk *clkin; + /* + * Lock for accessing device registers. Some operations require + * multiple consecutive R/W operations, during which the device + * shouldn't be interrupted. The buffers are also shared across + * all operations so need to be protected on stand alone reads and + * writes. + */ + struct mutex lock; + unsigned long clkin_freq; + unsigned long fpfd; + unsigned int integer; + unsigned int fract1; + unsigned int fract2; + unsigned int mod2; + unsigned int rf_div_sel; + unsigned int ref_div_factor; + u8 buf[10] ____cacheline_aligned; +}; + +static unsigned long long adf4371_pll_fract_n_get_rate(struct adf4371_state *st, + u32 channel) +{ + unsigned long long val, tmp; + unsigned int ref_div_sel; + + val = (((u64)st->integer * ADF4371_MODULUS1) + st->fract1) * st->fpfd; + tmp = (u64)st->fract2 * st->fpfd; + do_div(tmp, st->mod2); + val += tmp + ADF4371_MODULUS1 / 2; + + if (channel == ADF4371_CH_RF8 || channel == ADF4371_CH_RFAUX8) + ref_div_sel = st->rf_div_sel; + else + ref_div_sel = 0; + + do_div(val, ADF4371_MODULUS1 * (1 << ref_div_sel)); + + if (channel == ADF4371_CH_RF16) + val <<= 1; + else if (channel == ADF4371_CH_RF32) + val <<= 2; + + return val; +} + +static void adf4371_pll_fract_n_compute(unsigned long long vco, + unsigned long long pfd, + unsigned int *integer, + unsigned int *fract1, + unsigned int *fract2, + unsigned int *mod2) +{ + unsigned long long tmp; + u32 gcd_div; + + tmp = do_div(vco, pfd); + tmp = tmp * ADF4371_MODULUS1; + *fract2 = do_div(tmp, pfd); + + *integer = vco; + *fract1 = tmp; + + *mod2 = pfd; + + while (*mod2 > ADF4371_MAX_MODULUS2) { + *mod2 >>= 1; + *fract2 >>= 1; + } + + gcd_div = gcd(*fract2, *mod2); + *mod2 /= gcd_div; + *fract2 /= gcd_div; +} + +static int adf4371_set_freq(struct adf4371_state *st, unsigned long long freq, + unsigned int channel) +{ + u32 cp_bleed; + u8 int_mode = 0; + int ret; + + switch (channel) { + case ADF4371_CH_RF8: + case ADF4371_CH_RFAUX8: + if (ADF4371_CHECK_RANGE(freq, OUT_RF8_FREQ)) + return -EINVAL; + + st->rf_div_sel = 0; + + while (freq < ADF4371_MIN_VCO_FREQ) { + freq <<= 1; + st->rf_div_sel++; + } + break; + case ADF4371_CH_RF16: + /* ADF4371 RF16 8000...16000 MHz */ + if (ADF4371_CHECK_RANGE(freq, OUT_RF16_FREQ)) + return -EINVAL; + + freq >>= 1; + break; + case ADF4371_CH_RF32: + /* ADF4371 RF32 16000...32000 MHz */ + if (ADF4371_CHECK_RANGE(freq, OUT_RF32_FREQ)) + return -EINVAL; + + freq >>= 2; + break; + default: + return -EINVAL; + } + + adf4371_pll_fract_n_compute(freq, st->fpfd, &st->integer, &st->fract1, + &st->fract2, &st->mod2); + st->buf[0] = st->integer >> 8; + st->buf[1] = 0x40; /* REG12 default */ + st->buf[2] = 0x00; + st->buf[3] = st->fract2 & 0xFF; + st->buf[4] = st->fract2 >> 7; + st->buf[5] = st->fract2 >> 15; + st->buf[6] = ADF4371_FRAC2WORD_L(st->fract2 & 0x7F) | + ADF4371_FRAC1WORD(st->fract1 >> 23); + st->buf[7] = ADF4371_FRAC2WORD_H(st->fract2 >> 7); + st->buf[8] = st->mod2 & 0xFF; + st->buf[9] = ADF4371_MOD2WORD(st->mod2 >> 8); + + ret = regmap_bulk_write(st->regmap, ADF4371_REG(0x11), st->buf, 10); + if (ret < 0) + return ret; + /* + * The R counter allows the input reference frequency to be + * divided down to produce the reference clock to the PFD + */ + ret = regmap_write(st->regmap, ADF4371_REG(0x1F), st->ref_div_factor); + if (ret < 0) + return ret; + + ret = regmap_update_bits(st->regmap, ADF4371_REG(0x24), + ADF4371_RF_DIV_SEL_MSK, + ADF4371_RF_DIV_SEL(st->rf_div_sel)); + if (ret < 0) + return ret; + + cp_bleed = DIV_ROUND_UP(400 * 1750, st->integer * 375); + cp_bleed = clamp(cp_bleed, 1U, 255U); + ret = regmap_write(st->regmap, ADF4371_REG(0x26), cp_bleed); + if (ret < 0) + return ret; + /* + * Set to 1 when in INT mode (when FRAC1 = FRAC2 = 0), + * and set to 0 when in FRAC mode. + */ + if (st->fract1 == 0 && st->fract2 == 0) + int_mode = 0x01; + + ret = regmap_write(st->regmap, ADF4371_REG(0x2B), int_mode); + if (ret < 0) + return ret; + + return regmap_write(st->regmap, ADF4371_REG(0x10), st->integer & 0xFF); +} + +static ssize_t adf4371_read(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct adf4371_state *st = iio_priv(indio_dev); + unsigned long long val = 0; + unsigned int readval, reg, bit; + int ret; + + switch ((u32)private) { + case ADF4371_FREQ: + val = adf4371_pll_fract_n_get_rate(st, chan->channel); + ret = regmap_read(st->regmap, ADF4371_REG(0x7C), &readval); + if (ret < 0) + break; + + if (readval == 0x00) { + dev_dbg(&st->spi->dev, "PLL un-locked\n"); + ret = -EBUSY; + } + break; + case ADF4371_POWER_DOWN: + reg = adf4371_pwrdown_ch[chan->channel].reg; + bit = adf4371_pwrdown_ch[chan->channel].bit; + + ret = regmap_read(st->regmap, reg, &readval); + if (ret < 0) + break; + + val = !(readval & BIT(bit)); + break; + case ADF4371_CHANNEL_NAME: + return sprintf(buf, "%s\n", adf4371_ch_names[chan->channel]); + default: + ret = -EINVAL; + val = 0; + break; + } + + return ret < 0 ? ret : sprintf(buf, "%llu\n", val); +} + +static ssize_t adf4371_write(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct adf4371_state *st = iio_priv(indio_dev); + unsigned long long freq; + bool power_down; + unsigned int bit, readval, reg; + int ret; + + mutex_lock(&st->lock); + switch ((u32)private) { + case ADF4371_FREQ: + ret = kstrtoull(buf, 10, &freq); + if (ret) + break; + + ret = adf4371_set_freq(st, freq, chan->channel); + break; + case ADF4371_POWER_DOWN: + ret = kstrtobool(buf, &power_down); + if (ret) + break; + + reg = adf4371_pwrdown_ch[chan->channel].reg; + bit = adf4371_pwrdown_ch[chan->channel].bit; + ret = regmap_read(st->regmap, reg, &readval); + if (ret < 0) + break; + + readval &= ~BIT(bit); + readval |= (!power_down << bit); + + ret = regmap_write(st->regmap, reg, readval); + break; + default: + ret = -EINVAL; + break; + } + mutex_unlock(&st->lock); + + return ret ? ret : len; +} + +#define _ADF4371_EXT_INFO(_name, _ident) { \ + .name = _name, \ + .read = adf4371_read, \ + .write = adf4371_write, \ + .private = _ident, \ + .shared = IIO_SEPARATE, \ +} + +static const struct iio_chan_spec_ext_info adf4371_ext_info[] = { + /* + * Ideally we use IIO_CHAN_INFO_FREQUENCY, but there are + * values > 2^32 in order to support the entire frequency range + * in Hz. Using scale is a bit ugly. + */ + _ADF4371_EXT_INFO("frequency", ADF4371_FREQ), + _ADF4371_EXT_INFO("powerdown", ADF4371_POWER_DOWN), + _ADF4371_EXT_INFO("name", ADF4371_CHANNEL_NAME), + { }, +}; + +#define ADF4371_CHANNEL(index) { \ + .type = IIO_ALTVOLTAGE, \ + .output = 1, \ + .channel = index, \ + .ext_info = adf4371_ext_info, \ + .indexed = 1, \ + } + +static const struct iio_chan_spec adf4371_chan[] = { + ADF4371_CHANNEL(ADF4371_CH_RF8), + ADF4371_CHANNEL(ADF4371_CH_RFAUX8), + ADF4371_CHANNEL(ADF4371_CH_RF16), + ADF4371_CHANNEL(ADF4371_CH_RF32), +}; + +static int adf4371_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int writeval, + unsigned int *readval) +{ + struct adf4371_state *st = iio_priv(indio_dev); + + if (readval) + return regmap_read(st->regmap, reg, readval); + else + return regmap_write(st->regmap, reg, writeval); +} + +static const struct iio_info adf4371_info = { + .debugfs_reg_access = &adf4371_reg_access, +}; + +static int adf4371_setup(struct adf4371_state *st) +{ + unsigned int synth_timeout = 2, timeout = 1, vco_alc_timeout = 1; + unsigned int vco_band_div, tmp; + int ret; + + /* Perform a software reset */ + ret = regmap_write(st->regmap, ADF4371_REG(0x0), ADF4371_RESET_CMD); + if (ret < 0) + return ret; + + ret = regmap_multi_reg_write(st->regmap, adf4371_reg_defaults, + ARRAY_SIZE(adf4371_reg_defaults)); + if (ret < 0) + return ret; + + /* Set address in ascending order, so the bulk_write() will work */ + ret = regmap_update_bits(st->regmap, ADF4371_REG(0x0), + ADF4371_ADDR_ASC_MSK | ADF4371_ADDR_ASC_R_MSK, + ADF4371_ADDR_ASC(1) | ADF4371_ADDR_ASC_R(1)); + if (ret < 0) + return ret; + /* + * Calculate and maximize PFD frequency + * fPFD = REFIN × ((1 + D)/(R × (1 + T))) + * Where D is the REFIN doubler bit, T is the reference divide by 2, + * R is the reference division factor + * TODO: it is assumed D and T equal 0. + */ + do { + st->ref_div_factor++; + st->fpfd = st->clkin_freq / st->ref_div_factor; + } while (st->fpfd > ADF4371_MAX_FREQ_PFD); + + /* Calculate Timeouts */ + vco_band_div = DIV_ROUND_UP(st->fpfd, 2400000U); + + tmp = DIV_ROUND_CLOSEST(st->fpfd, 1000000U); + do { + timeout++; + if (timeout > 1023) { + timeout = 2; + synth_timeout++; + } + } while (synth_timeout * 1024 + timeout <= 20 * tmp); + + do { + vco_alc_timeout++; + } while (vco_alc_timeout * 1024 - timeout <= 50 * tmp); + + st->buf[0] = vco_band_div; + st->buf[1] = timeout & 0xFF; + st->buf[2] = ADF4371_TIMEOUT(timeout >> 8) | 0x04; + st->buf[3] = synth_timeout; + st->buf[4] = ADF4371_VCO_ALC_TOUT(vco_alc_timeout); + + return regmap_bulk_write(st->regmap, ADF4371_REG(0x30), st->buf, 5); +} + +static void adf4371_clk_disable(void *data) +{ + struct adf4371_state *st = data; + + clk_disable_unprepare(st->clkin); +} + +static int adf4371_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct iio_dev *indio_dev; + struct adf4371_state *st; + struct regmap *regmap; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + regmap = devm_regmap_init_spi(spi, &adf4371_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&spi->dev, "Error initializing spi regmap: %ld\n", + PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + st = iio_priv(indio_dev); + spi_set_drvdata(spi, indio_dev); + st->spi = spi; + st->regmap = regmap; + mutex_init(&st->lock); + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = id->name; + indio_dev->info = &adf4371_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = adf4371_chan; + indio_dev->num_channels = ARRAY_SIZE(adf4371_chan); + + st->clkin = devm_clk_get(&spi->dev, "clkin"); + if (IS_ERR(st->clkin)) + return PTR_ERR(st->clkin); + + ret = clk_prepare_enable(st->clkin); + if (ret < 0) + return ret; + + ret = devm_add_action_or_reset(&spi->dev, adf4371_clk_disable, st); + if (ret) + return ret; + + st->clkin_freq = clk_get_rate(st->clkin); + + ret = adf4371_setup(st); + if (ret < 0) { + dev_err(&spi->dev, "ADF4371 setup failed\n"); + return ret; + } + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id adf4371_id_table[] = { + { "adf4371", 0 }, + {} +}; +MODULE_DEVICE_TABLE(spi, adf4371_id_table); + +static const struct of_device_id adf4371_of_match[] = { + { .compatible = "adi,adf4371" }, + { }, +}; +MODULE_DEVICE_TABLE(of, adf4371_of_match); + +static struct spi_driver adf4371_driver = { + .driver = { + .name = "adf4371", + .of_match_table = adf4371_of_match, + }, + .probe = adf4371_probe, + .id_table = adf4371_id_table, +}; +module_spi_driver(adf4371_driver); + +MODULE_AUTHOR("Stefan Popa "); +MODULE_DESCRIPTION("Analog Devices ADF4371 SPI PLL"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-70-g09d2 From 0e4f0b42f42d88507b48282c8915f502551534e4 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Wed, 29 May 2019 16:01:08 +0300 Subject: iio: adxl372: fix iio_triggered_buffer_{pre,post}enable positions The iio_triggered_buffer_{predisable,postenable} functions attach/detach the poll functions. For the predisable hook, the disable code should occur before detaching the poll func, and for the postenable hook, the poll func should be attached before the enable code. Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adxl372.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c index 3b84cb243a87..055227cb3d43 100644 --- a/drivers/iio/accel/adxl372.c +++ b/drivers/iio/accel/adxl372.c @@ -782,10 +782,14 @@ static int adxl372_buffer_postenable(struct iio_dev *indio_dev) unsigned int mask; int i, ret; - ret = adxl372_set_interrupts(st, ADXL372_INT1_MAP_FIFO_FULL_MSK, 0); + ret = iio_triggered_buffer_postenable(indio_dev); if (ret < 0) return ret; + ret = adxl372_set_interrupts(st, ADXL372_INT1_MAP_FIFO_FULL_MSK, 0); + if (ret < 0) + goto err; + mask = *indio_dev->active_scan_mask; for (i = 0; i < ARRAY_SIZE(adxl372_axis_lookup_table); i++) { @@ -793,8 +797,10 @@ static int adxl372_buffer_postenable(struct iio_dev *indio_dev) break; } - if (i == ARRAY_SIZE(adxl372_axis_lookup_table)) - return -EINVAL; + if (i == ARRAY_SIZE(adxl372_axis_lookup_table)) { + ret = -EINVAL; + goto err; + } st->fifo_format = adxl372_axis_lookup_table[i].fifo_format; st->fifo_set_size = bitmap_weight(indio_dev->active_scan_mask, @@ -814,26 +820,25 @@ static int adxl372_buffer_postenable(struct iio_dev *indio_dev) if (ret < 0) { st->fifo_mode = ADXL372_FIFO_BYPASSED; adxl372_set_interrupts(st, 0, 0); - return ret; + goto err; } - return iio_triggered_buffer_postenable(indio_dev); + return 0; + +err: + iio_triggered_buffer_predisable(indio_dev); + return ret; } static int adxl372_buffer_predisable(struct iio_dev *indio_dev) { struct adxl372_state *st = iio_priv(indio_dev); - int ret; - - ret = iio_triggered_buffer_predisable(indio_dev); - if (ret < 0) - return ret; adxl372_set_interrupts(st, 0, 0); st->fifo_mode = ADXL372_FIFO_BYPASSED; adxl372_configure_fifo(st); - return 0; + return iio_triggered_buffer_predisable(indio_dev); } static const struct iio_buffer_setup_ops adxl372_buffer_ops = { -- cgit v1.2.3-70-g09d2 From 38ffa3a34cb04c378c32c04337d0b98247a04012 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Thu, 30 May 2019 16:18:08 +0300 Subject: iio: amplifiers: update license information Use the new `SPDX-License-Identifier` tag to specify the license. For ad8366, also update copyright years in the header part. Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/amplifiers/Kconfig | 1 + drivers/iio/amplifiers/ad8366.c | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/amplifiers/Kconfig b/drivers/iio/amplifiers/Kconfig index e9c5f2cd9257..da5830da9bcf 100644 --- a/drivers/iio/amplifiers/Kconfig +++ b/drivers/iio/amplifiers/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # # Gain Amplifiers, etc. # diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c index 4b76b61ba4be..82ac15914ff3 100644 --- a/drivers/iio/amplifiers/ad8366.c +++ b/drivers/iio/amplifiers/ad8366.c @@ -1,9 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * AD8366 SPI Dual-Digital Variable Gain Amplifier (VGA) * - * Copyright 2012 Analog Devices Inc. - * - * Licensed under the GPL-2. + * Copyright 2012-2019 Analog Devices Inc. */ #include -- cgit v1.2.3-70-g09d2 From dbcf6b5d2625b904f13810b168773b05d891204b Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Thu, 30 May 2019 16:18:09 +0300 Subject: iio: amplifiers: ad8366: use own lock to guard state This driver is still using iio_dev's mlock to guard against inconsistent state. This has been discouraged for some time. This change switches to using it's own mutex, defined on the state struct. Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/amplifiers/ad8366.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c index 82ac15914ff3..24ff5475d9f2 100644 --- a/drivers/iio/amplifiers/ad8366.c +++ b/drivers/iio/amplifiers/ad8366.c @@ -21,6 +21,7 @@ struct ad8366_state { struct spi_device *spi; struct regulator *reg; + struct mutex lock; /* protect sensor state */ unsigned char ch[2]; /* * DMA (thus cache coherency maintenance) requires the @@ -58,7 +59,7 @@ static int ad8366_read_raw(struct iio_dev *indio_dev, int ret; unsigned code; - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); switch (m) { case IIO_CHAN_INFO_HARDWAREGAIN: code = st->ch[chan->channel]; @@ -73,7 +74,7 @@ static int ad8366_read_raw(struct iio_dev *indio_dev, default: ret = -EINVAL; } - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); return ret; }; @@ -99,7 +100,7 @@ static int ad8366_write_raw(struct iio_dev *indio_dev, code = (code - 4500) / 253; - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); switch (mask) { case IIO_CHAN_INFO_HARDWAREGAIN: st->ch[chan->channel] = code; @@ -108,7 +109,7 @@ static int ad8366_write_raw(struct iio_dev *indio_dev, default: ret = -EINVAL; } - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); return ret; } @@ -151,6 +152,7 @@ static int ad8366_probe(struct spi_device *spi) } spi_set_drvdata(spi, indio_dev); + mutex_init(&st->lock); st->spi = spi; indio_dev->dev.parent = &spi->dev; -- cgit v1.2.3-70-g09d2 From 11ab555a6d3b0ecbb51076af79e625303bfa61de Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Thu, 30 May 2019 16:18:10 +0300 Subject: iio: amplifiers: ad8366: rework driver to allow other chips The SPI gain amplifiers are simple devices, with 1 or 2 channels, to which are read-from/written-to. The gain computation in ad8366_write_raw() has been updated to compute gain in dB for negative values. This driver will be extended to support other chips as well. To do that, this rework handles the AD8366 device as a special-case (via switch-statements). This will make things easier when adding new chips. Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/amplifiers/ad8366.c | 81 +++++++++++++++++++++++++++++++---------- 1 file changed, 62 insertions(+), 19 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c index 24ff5475d9f2..1beda6409301 100644 --- a/drivers/iio/amplifiers/ad8366.c +++ b/drivers/iio/amplifiers/ad8366.c @@ -18,11 +18,22 @@ #include #include +enum ad8366_type { + ID_AD8366, +}; + +struct ad8366_info { + int gain_min; + int gain_max; +}; + struct ad8366_state { struct spi_device *spi; struct regulator *reg; struct mutex lock; /* protect sensor state */ unsigned char ch[2]; + enum ad8366_type type; + struct ad8366_info *info; /* * DMA (thus cache coherency maintenance) requires the * transfer buffers to live in their own cache lines. @@ -30,19 +41,30 @@ struct ad8366_state { unsigned char data[2] ____cacheline_aligned; }; +static struct ad8366_info ad8366_infos[] = { + [ID_AD8366] = { + .gain_min = 4500, + .gain_max = 20500, + }, +}; + static int ad8366_write(struct iio_dev *indio_dev, unsigned char ch_a, unsigned char ch_b) { struct ad8366_state *st = iio_priv(indio_dev); int ret; - ch_a = bitrev8(ch_a & 0x3F); - ch_b = bitrev8(ch_b & 0x3F); + switch (st->type) { + case ID_AD8366: + ch_a = bitrev8(ch_a & 0x3F); + ch_b = bitrev8(ch_b & 0x3F); - st->data[0] = ch_b >> 4; - st->data[1] = (ch_b << 4) | (ch_a >> 2); + st->data[0] = ch_b >> 4; + st->data[1] = (ch_b << 4) | (ch_a >> 2); + break; + } - ret = spi_write(st->spi, st->data, ARRAY_SIZE(st->data)); + ret = spi_write(st->spi, st->data, indio_dev->num_channels); if (ret < 0) dev_err(&indio_dev->dev, "write failed (%d)", ret); @@ -57,17 +79,22 @@ static int ad8366_read_raw(struct iio_dev *indio_dev, { struct ad8366_state *st = iio_priv(indio_dev); int ret; - unsigned code; + int code, gain = 0; mutex_lock(&st->lock); switch (m) { case IIO_CHAN_INFO_HARDWAREGAIN: code = st->ch[chan->channel]; + switch (st->type) { + case ID_AD8366: + gain = code * 253 + 4500; + break; + } + /* Values in dB */ - code = code * 253 + 4500; - *val = code / 1000; - *val2 = (code % 1000) * 1000; + *val = gain / 1000; + *val2 = (gain % 1000) * 1000; ret = IIO_VAL_INT_PLUS_MICRO_DB; break; @@ -86,19 +113,24 @@ static int ad8366_write_raw(struct iio_dev *indio_dev, long mask) { struct ad8366_state *st = iio_priv(indio_dev); - unsigned code; + struct ad8366_info *inf = st->info; + int code = 0, gain; int ret; - if (val < 0 || val2 < 0) - return -EINVAL; - /* Values in dB */ - code = (((u8)val * 1000) + ((u32)val2 / 1000)); + if (val < 0) + gain = (val * 1000) - (val2 / 1000); + else + gain = (val * 1000) + (val2 / 1000); - if (code > 20500 || code < 4500) + if (gain > inf->gain_max || gain < inf->gain_min) return -EINVAL; - code = (code - 4500) / 253; + switch (st->type) { + case ID_AD8366: + code = (gain - 4500) / 253; + break; + } mutex_lock(&st->lock); switch (mask) { @@ -154,13 +186,24 @@ static int ad8366_probe(struct spi_device *spi) spi_set_drvdata(spi, indio_dev); mutex_init(&st->lock); st->spi = spi; + st->type = spi_get_device_id(spi)->driver_data; + + switch (st->type) { + case ID_AD8366: + indio_dev->channels = ad8366_channels; + indio_dev->num_channels = ARRAY_SIZE(ad8366_channels); + break; + default: + dev_err(&spi->dev, "Invalid device ID\n"); + ret = -EINVAL; + goto error_disable_reg; + } + st->info = &ad8366_infos[st->type]; indio_dev->dev.parent = &spi->dev; indio_dev->name = spi_get_device_id(spi)->name; indio_dev->info = &ad8366_info; indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = ad8366_channels; - indio_dev->num_channels = ARRAY_SIZE(ad8366_channels); ret = ad8366_write(indio_dev, 0 , 0); if (ret < 0) @@ -194,7 +237,7 @@ static int ad8366_remove(struct spi_device *spi) } static const struct spi_device_id ad8366_id[] = { - {"ad8366", 0}, + {"ad8366", ID_AD8366}, {} }; MODULE_DEVICE_TABLE(spi, ad8366_id); -- cgit v1.2.3-70-g09d2 From cee211f4e5a0f70ee0c202e9a5fc8eac0b9b4945 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Thu, 30 May 2019 16:18:11 +0300 Subject: iio: amplifiers: ad8366: Add support for the ADA4961 DGA This change adds support for the ADA4961 BiCMOS RF Digital Gain Amplifier, (DGA), which is optimized for driving heavy loads out 2.0 GHz and beyond. The device typically achieves -90 dBc IMD3 performance at 500 MHz and -85 dBc at 1.5 GHz. Datasheet link: http://www.analog.com/media/en/technical-documentation/data-sheets/ADA4961.pdf This change re-uses the existing ad8366 driver, as most logic is similar. Also, this chip has a reset pin which is initialized during probe. Signed-off-by: Paul Cercueil Signed-off-by: Michael Hennerich Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/amplifiers/Kconfig | 10 +++++++--- drivers/iio/amplifiers/ad8366.c | 34 ++++++++++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 5 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/amplifiers/Kconfig b/drivers/iio/amplifiers/Kconfig index da5830da9bcf..f39c05677d52 100644 --- a/drivers/iio/amplifiers/Kconfig +++ b/drivers/iio/amplifiers/Kconfig @@ -7,12 +7,16 @@ menu "Amplifiers" config AD8366 - tristate "Analog Devices AD8366 VGA" + tristate "Analog Devices AD8366 and similar Gain Amplifiers" depends on SPI + depends on GPIOLIB select BITREVERSE help - Say yes here to build support for Analog Devices AD8366 - SPI Dual-Digital Variable Gain Amplifier (VGA). + Say yes here to build support for Analog Devices AD8366 and similar + gain amplifiers. This driver supports the following gain amplifiers + from Analog Devices: + AD8366 Dual-Digital Variable Gain Amplifier (VGA) + ADA4961 BiCMOS RF Digital Gain Amplifier (DGA) To compile this driver as a module, choose M here: the module will be called ad8366. diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c index 1beda6409301..0a9883e8eb2e 100644 --- a/drivers/iio/amplifiers/ad8366.c +++ b/drivers/iio/amplifiers/ad8366.c @@ -1,6 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 /* - * AD8366 SPI Dual-Digital Variable Gain Amplifier (VGA) + * AD8366 and similar Gain Amplifiers + * This driver supports the following gain amplifiers: + * AD8366 Dual-Digital Variable Gain Amplifier (VGA) + * ADA4961 BiCMOS RF Digital Gain Amplifier (DGA) * * Copyright 2012-2019 Analog Devices Inc. */ @@ -11,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -20,6 +24,7 @@ enum ad8366_type { ID_AD8366, + ID_ADA4961, }; struct ad8366_info { @@ -31,6 +36,7 @@ struct ad8366_state { struct spi_device *spi; struct regulator *reg; struct mutex lock; /* protect sensor state */ + struct gpio_desc *reset_gpio; unsigned char ch[2]; enum ad8366_type type; struct ad8366_info *info; @@ -46,6 +52,10 @@ static struct ad8366_info ad8366_infos[] = { .gain_min = 4500, .gain_max = 20500, }, + [ID_ADA4961] = { + .gain_min = -6000, + .gain_max = 15000, + }, }; static int ad8366_write(struct iio_dev *indio_dev, @@ -62,6 +72,9 @@ static int ad8366_write(struct iio_dev *indio_dev, st->data[0] = ch_b >> 4; st->data[1] = (ch_b << 4) | (ch_a >> 2); break; + case ID_ADA4961: + st->data[0] = ch_a & 0x1F; + break; } ret = spi_write(st->spi, st->data, indio_dev->num_channels); @@ -90,6 +103,9 @@ static int ad8366_read_raw(struct iio_dev *indio_dev, case ID_AD8366: gain = code * 253 + 4500; break; + case ID_ADA4961: + gain = 15000 - code * 1000; + break; } /* Values in dB */ @@ -130,6 +146,9 @@ static int ad8366_write_raw(struct iio_dev *indio_dev, case ID_AD8366: code = (gain - 4500) / 253; break; + case ID_ADA4961: + code = (15000 - gain) / 1000; + break; } mutex_lock(&st->lock); @@ -164,6 +183,10 @@ static const struct iio_chan_spec ad8366_channels[] = { AD8366_CHAN(1), }; +static const struct iio_chan_spec ada4961_channels[] = { + AD8366_CHAN(0), +}; + static int ad8366_probe(struct spi_device *spi) { struct iio_dev *indio_dev; @@ -193,6 +216,12 @@ static int ad8366_probe(struct spi_device *spi) indio_dev->channels = ad8366_channels; indio_dev->num_channels = ARRAY_SIZE(ad8366_channels); break; + case ID_ADA4961: + st->reset_gpio = devm_gpiod_get(&spi->dev, "reset", + GPIOD_OUT_HIGH); + indio_dev->channels = ada4961_channels; + indio_dev->num_channels = ARRAY_SIZE(ada4961_channels); + break; default: dev_err(&spi->dev, "Invalid device ID\n"); ret = -EINVAL; @@ -238,6 +267,7 @@ static int ad8366_remove(struct spi_device *spi) static const struct spi_device_id ad8366_id[] = { {"ad8366", ID_AD8366}, + {"ada4961", ID_ADA4961}, {} }; MODULE_DEVICE_TABLE(spi, ad8366_id); @@ -254,5 +284,5 @@ static struct spi_driver ad8366_driver = { module_spi_driver(ad8366_driver); MODULE_AUTHOR("Michael Hennerich "); -MODULE_DESCRIPTION("Analog Devices AD8366 VGA"); +MODULE_DESCRIPTION("Analog Devices AD8366 and similar Gain Amplifiers"); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-70-g09d2 From 075da9cd6aa503e05c86f2bbd29b539d1b145644 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Thu, 30 May 2019 16:18:12 +0300 Subject: iio: amplifiers: ad8366: Add support for ADL5240 VGA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ADL5240 is a high performance, digitally controlled variable gain amplifier (VGA) operating from 100 MHz to 4000 MHz. The VGA integrates a high performance, 20 dB gain, internally matched amplifier (AMP) with a 6-bit digital step attenuator (DSA) that has a gain control range of 31.5 dB in 0.5 dB steps with ±0.25 dB step accuracy. Datasheet link: https://www.analog.com/media/en/technical-documentation/data-sheets/adl5240.pdf Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/amplifiers/Kconfig | 1 + drivers/iio/amplifiers/ad8366.c | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) (limited to 'drivers/iio') diff --git a/drivers/iio/amplifiers/Kconfig b/drivers/iio/amplifiers/Kconfig index f39c05677d52..da7f126d197b 100644 --- a/drivers/iio/amplifiers/Kconfig +++ b/drivers/iio/amplifiers/Kconfig @@ -17,6 +17,7 @@ config AD8366 from Analog Devices: AD8366 Dual-Digital Variable Gain Amplifier (VGA) ADA4961 BiCMOS RF Digital Gain Amplifier (DGA) + ADL5240 Digitally controlled variable gain amplifier (VGA) To compile this driver as a module, choose M here: the module will be called ad8366. diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c index 0a9883e8eb2e..0176d3d8cc9c 100644 --- a/drivers/iio/amplifiers/ad8366.c +++ b/drivers/iio/amplifiers/ad8366.c @@ -4,6 +4,7 @@ * This driver supports the following gain amplifiers: * AD8366 Dual-Digital Variable Gain Amplifier (VGA) * ADA4961 BiCMOS RF Digital Gain Amplifier (DGA) + * ADL5240 Digitally controlled variable gain amplifier (VGA) * * Copyright 2012-2019 Analog Devices Inc. */ @@ -25,6 +26,7 @@ enum ad8366_type { ID_AD8366, ID_ADA4961, + ID_ADL5240, }; struct ad8366_info { @@ -56,6 +58,10 @@ static struct ad8366_info ad8366_infos[] = { .gain_min = -6000, .gain_max = 15000, }, + [ID_ADL5240] = { + .gain_min = -11500, + .gain_max = 20000, + }, }; static int ad8366_write(struct iio_dev *indio_dev, @@ -75,6 +81,9 @@ static int ad8366_write(struct iio_dev *indio_dev, case ID_ADA4961: st->data[0] = ch_a & 0x1F; break; + case ID_ADL5240: + st->data[0] = (ch_a & 0x3F); + break; } ret = spi_write(st->spi, st->data, indio_dev->num_channels); @@ -106,6 +115,9 @@ static int ad8366_read_raw(struct iio_dev *indio_dev, case ID_ADA4961: gain = 15000 - code * 1000; break; + case ID_ADL5240: + gain = 20000 - 31500 + code * 500; + break; } /* Values in dB */ @@ -149,6 +161,9 @@ static int ad8366_write_raw(struct iio_dev *indio_dev, case ID_ADA4961: code = (15000 - gain) / 1000; break; + case ID_ADL5240: + code = ((gain - 500 - 20000) / 500) & 0x3F; + break; } mutex_lock(&st->lock); @@ -217,6 +232,7 @@ static int ad8366_probe(struct spi_device *spi) indio_dev->num_channels = ARRAY_SIZE(ad8366_channels); break; case ID_ADA4961: + case ID_ADL5240: st->reset_gpio = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_HIGH); indio_dev->channels = ada4961_channels; @@ -268,6 +284,7 @@ static int ad8366_remove(struct spi_device *spi) static const struct spi_device_id ad8366_id[] = { {"ad8366", ID_AD8366}, {"ada4961", ID_ADA4961}, + {"adl5240", ID_ADL5240}, {} }; MODULE_DEVICE_TABLE(spi, ad8366_id); -- cgit v1.2.3-70-g09d2 From 387c1d770667fbe046ef241ae6995bb4294402c6 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Thu, 30 May 2019 10:25:41 +0300 Subject: iio: ad_sigma_delta: return directly in ad_sd_buffer_postenable() There is nothing being done after the `err_predisable` label, so just remove it. Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad_sigma_delta.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index a4310600a853..ec0e38566ece 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -358,7 +358,7 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) ret = ad_sigma_delta_set_channel(sigma_delta, indio_dev->channels[channel].address); if (ret) - goto err_predisable; + return ret; spi_bus_lock(sigma_delta->spi->master); sigma_delta->bus_locked = true; @@ -375,7 +375,6 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) err_unlock: spi_bus_unlock(sigma_delta->spi->master); -err_predisable: return ret; } -- cgit v1.2.3-70-g09d2 From 7e8b817e5978fdaabba37ab1d2ddb062a4c9abbb Mon Sep 17 00:00:00 2001 From: Shobhit Kukreti Date: Mon, 10 Jun 2019 20:55:57 -0700 Subject: iio: humidity: Replace older GPIO APIs with GPIO Consumer APIs for the dht11 sensor The dht11 driver uses a single gpio to make measurements. It was using the older global gpio numberspace. The patch replaces the old gpio api with the new gpio descriptor based api. Removed header files "linux/gpio.h" and "linux/of_gpio.h" Signed-off-by: Shobhit Kukreti Acked-by: Harald Geyer Signed-off-by: Jonathan Cameron --- drivers/iio/humidity/dht11.c | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/humidity/dht11.c b/drivers/iio/humidity/dht11.c index 1a9f8f4ffb88..08573df2a8c6 100644 --- a/drivers/iio/humidity/dht11.c +++ b/drivers/iio/humidity/dht11.c @@ -31,8 +31,7 @@ #include #include #include -#include -#include +#include #include #include @@ -81,7 +80,7 @@ struct dht11 { struct device *dev; - int gpio; + struct gpio_desc *gpiod; int irq; struct completion completion; @@ -188,7 +187,7 @@ static irqreturn_t dht11_handle_irq(int irq, void *data) if (dht11->num_edges < DHT11_EDGES_PER_READ && dht11->num_edges >= 0) { dht11->edges[dht11->num_edges].ts = ktime_get_boot_ns(); dht11->edges[dht11->num_edges++].value = - gpio_get_value(dht11->gpio); + gpiod_get_value(dht11->gpiod); if (dht11->num_edges >= DHT11_EDGES_PER_READ) complete(&dht11->completion); @@ -226,12 +225,12 @@ static int dht11_read_raw(struct iio_dev *iio_dev, reinit_completion(&dht11->completion); dht11->num_edges = 0; - ret = gpio_direction_output(dht11->gpio, 0); + ret = gpiod_direction_output(dht11->gpiod, 0); if (ret) goto err; usleep_range(DHT11_START_TRANSMISSION_MIN, DHT11_START_TRANSMISSION_MAX); - ret = gpio_direction_input(dht11->gpio); + ret = gpiod_direction_input(dht11->gpiod); if (ret) goto err; @@ -303,10 +302,8 @@ MODULE_DEVICE_TABLE(of, dht11_dt_ids); static int dht11_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct device_node *node = dev->of_node; struct dht11 *dht11; struct iio_dev *iio; - int ret; iio = devm_iio_device_alloc(dev, sizeof(*dht11)); if (!iio) { @@ -316,18 +313,13 @@ static int dht11_probe(struct platform_device *pdev) dht11 = iio_priv(iio); dht11->dev = dev; + dht11->gpiod = devm_gpiod_get(dev, NULL, GPIOD_IN); + if (IS_ERR(dht11->gpiod)) + return PTR_ERR(dht11->gpiod); - ret = of_get_gpio(node, 0); - if (ret < 0) - return ret; - dht11->gpio = ret; - ret = devm_gpio_request_one(dev, dht11->gpio, GPIOF_IN, pdev->name); - if (ret) - return ret; - - dht11->irq = gpio_to_irq(dht11->gpio); + dht11->irq = gpiod_to_irq(dht11->gpiod); if (dht11->irq < 0) { - dev_err(dev, "GPIO %d has no interrupt\n", dht11->gpio); + dev_err(dev, "GPIO %d has no interrupt\n", desc_to_gpio(dht11->gpiod)); return -EINVAL; } -- cgit v1.2.3-70-g09d2 From 8bb0d36cbaeec31787a3062a1c433905d53afec5 Mon Sep 17 00:00:00 2001 From: Robert Hancock Date: Mon, 10 Jun 2019 10:23:44 -0600 Subject: iio: adc: xilinx: support all platforms Since the XADC logic can be used with standalone Xilinx FPGAs, this driver can potentially be used with many different platforms, not just the Zynq and MicroBlaze platforms this driver was allowed to be built for. There should be no platform-specific code in this driver, so just delete the platform dependency. Signed-off-by: Robert Hancock Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 2036eca546fd..c242b8b12f26 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -1084,7 +1084,6 @@ config VIPERBOARD_ADC config XILINX_XADC tristate "Xilinx XADC driver" - depends on ARCH_ZYNQ || MICROBLAZE || COMPILE_TEST depends on HAS_IOMEM select IIO_BUFFER select IIO_TRIGGERED_BUFFER -- cgit v1.2.3-70-g09d2 From 05b8bcc96278c9ef927a6f25a98e233e55de42e1 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Fri, 14 Jun 2019 13:47:25 +0300 Subject: iio: st_accel: fix iio_triggered_buffer_{pre,post}enable positions The iio_triggered_buffer_{predisable,postenable} functions attach/detach the poll functions. For the predisable hook, the disable code should occur before detaching the poll func, and for the postenable hook, the poll func should be attached before the enable code. Signed-off-by: Alexandru Ardelean Acked-by: Denis Ciocca Signed-off-by: Jonathan Cameron --- drivers/iio/accel/st_accel_buffer.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/st_accel_buffer.c b/drivers/iio/accel/st_accel_buffer.c index 7fddc137e91e..802ab7d2d93f 100644 --- a/drivers/iio/accel/st_accel_buffer.c +++ b/drivers/iio/accel/st_accel_buffer.c @@ -46,17 +46,19 @@ static int st_accel_buffer_postenable(struct iio_dev *indio_dev) goto allocate_memory_error; } - err = st_sensors_set_axis_enable(indio_dev, - (u8)indio_dev->active_scan_mask[0]); + err = iio_triggered_buffer_postenable(indio_dev); if (err < 0) goto st_accel_buffer_postenable_error; - err = iio_triggered_buffer_postenable(indio_dev); + err = st_sensors_set_axis_enable(indio_dev, + (u8)indio_dev->active_scan_mask[0]); if (err < 0) - goto st_accel_buffer_postenable_error; + goto st_sensors_set_axis_enable_error; return err; +st_sensors_set_axis_enable_error: + iio_triggered_buffer_predisable(indio_dev); st_accel_buffer_postenable_error: kfree(adata->buffer_data); allocate_memory_error: @@ -65,20 +67,22 @@ allocate_memory_error: static int st_accel_buffer_predisable(struct iio_dev *indio_dev) { - int err; + int err, err2; struct st_sensor_data *adata = iio_priv(indio_dev); - err = iio_triggered_buffer_predisable(indio_dev); - if (err < 0) - goto st_accel_buffer_predisable_error; - err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS); if (err < 0) goto st_accel_buffer_predisable_error; err = st_sensors_set_enable(indio_dev, false); + if (err < 0) + goto st_accel_buffer_predisable_error; st_accel_buffer_predisable_error: + err2 = iio_triggered_buffer_predisable(indio_dev); + if (!err) + err = err2; + kfree(adata->buffer_data); return err; } -- cgit v1.2.3-70-g09d2 From aabcbfe8dbbfa87f353071182d9bbc68d10f728e Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 21 Jun 2019 12:18:28 +0300 Subject: iio: sca3000: Potential endian bug in sca3000_read_event_value() The problem is that "ret" is an int but we're casting it as "(unsigned long *)&ret" when we do the for_each_set_bit() loop. This will not work on big endian 64 bit systems. Signed-off-by: Dan Carpenter Signed-off-by: Jonathan Cameron --- drivers/iio/accel/sca3000.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c index 4964561595f5..9e9f4347a325 100644 --- a/drivers/iio/accel/sca3000.c +++ b/drivers/iio/accel/sca3000.c @@ -872,8 +872,9 @@ static int sca3000_read_event_value(struct iio_dev *indio_dev, enum iio_event_info info, int *val, int *val2) { - int ret, i; struct sca3000_state *st = iio_priv(indio_dev); + long ret; + int i; switch (info) { case IIO_EV_INFO_VALUE: @@ -885,11 +886,11 @@ static int sca3000_read_event_value(struct iio_dev *indio_dev, return ret; *val = 0; if (chan->channel2 == IIO_MOD_Y) - for_each_set_bit(i, (unsigned long *)&ret, + for_each_set_bit(i, &ret, ARRAY_SIZE(st->info->mot_det_mult_y)) *val += st->info->mot_det_mult_y[i]; else - for_each_set_bit(i, (unsigned long *)&ret, + for_each_set_bit(i, &ret, ARRAY_SIZE(st->info->mot_det_mult_xz)) *val += st->info->mot_det_mult_xz[i]; -- cgit v1.2.3-70-g09d2 From 12c8398d8012ead3d3d68422067ab2f9a66ae76a Mon Sep 17 00:00:00 2001 From: Olivier Moysan Date: Wed, 19 Jun 2019 15:03:47 +0200 Subject: iio: adc: stm32-dfsdm: fix output resolution In buffered mode, output samples are shifted left unconditionally. This works for filter order 3, but this shift is not adapted for other filter orders. Compute required shift, left or right, and shift output data accordingly. Add also saturation management to avoid wrap-around when maximum positive sample is reached. Signed-off-by: Olivier Moysan Fixes: eca949800d2d ("IIO: ADC: add stm32 DFSDM support for PDM microphone") Acked-by: Fabrice Gasnier Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-dfsdm-adc.c | 158 +++++++++++++++++++++++++++++--------- drivers/iio/adc/stm32-dfsdm.h | 24 ++++-- 2 files changed, 142 insertions(+), 40 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c index 588907cc3b6b..cb596f104919 100644 --- a/drivers/iio/adc/stm32-dfsdm-adc.c +++ b/drivers/iio/adc/stm32-dfsdm-adc.c @@ -39,9 +39,16 @@ #define DFSDM_MAX_INT_OVERSAMPLING 256 #define DFSDM_MAX_FL_OVERSAMPLING 1024 -/* Max sample resolutions */ -#define DFSDM_MAX_RES BIT(31) -#define DFSDM_DATA_RES BIT(23) +/* Limit filter output resolution to 31 bits. (i.e. sample range is +/-2^30) */ +#define DFSDM_DATA_MAX BIT(30) +/* + * Data are output as two's complement data in a 24 bit field. + * Data from filters are in the range +/-2^(n-1) + * 2^(n-1) maximum positive value cannot be coded in 2's complement n bits + * An extra bit is required to avoid wrap-around of the binary code for 2^(n-1) + * So, the resolution of samples from filter is actually limited to 23 bits + */ +#define DFSDM_DATA_RES 24 /* Filter configuration */ #define DFSDM_CR1_CFG_MASK (DFSDM_CR1_RCH_MASK | DFSDM_CR1_RCONT_MASK | \ @@ -181,14 +188,15 @@ static int stm32_dfsdm_get_jextsel(struct iio_dev *indio_dev, return -EINVAL; } -static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl, - unsigned int fast, unsigned int oversamp) +static int stm32_dfsdm_compute_osrs(struct stm32_dfsdm_filter *fl, + unsigned int fast, unsigned int oversamp) { unsigned int i, d, fosr, iosr; - u64 res; - s64 delta; + u64 res, max; + int bits, shift; unsigned int m = 1; /* multiplication factor */ unsigned int p = fl->ford; /* filter order (ford) */ + struct stm32_dfsdm_filter_osr *flo = &fl->flo; pr_debug("%s: Requested oversampling: %d\n", __func__, oversamp); /* @@ -207,11 +215,9 @@ static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl, /* * Look for filter and integrator oversampling ratios which allows - * to reach 24 bits data output resolution. - * Leave as soon as if exact resolution if reached. - * Otherwise the higher resolution below 32 bits is kept. + * to maximize data output resolution. */ - fl->res = 0; + flo->res = 0; for (fosr = 1; fosr <= DFSDM_MAX_FL_OVERSAMPLING; fosr++) { for (iosr = 1; iosr <= DFSDM_MAX_INT_OVERSAMPLING; iosr++) { if (fast) @@ -236,32 +242,68 @@ static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl, res = fosr; for (i = p - 1; i > 0; i--) { res = res * (u64)fosr; - if (res > DFSDM_MAX_RES) + if (res > DFSDM_DATA_MAX) break; } - if (res > DFSDM_MAX_RES) + if (res > DFSDM_DATA_MAX) continue; + res = res * (u64)m * (u64)iosr; - if (res > DFSDM_MAX_RES) + if (res > DFSDM_DATA_MAX) continue; - delta = res - DFSDM_DATA_RES; - - if (res >= fl->res) { - fl->res = res; - fl->fosr = fosr; - fl->iosr = iosr; - fl->fast = fast; - pr_debug("%s: fosr = %d, iosr = %d\n", - __func__, fl->fosr, fl->iosr); + if (res >= flo->res) { + flo->res = res; + flo->fosr = fosr; + flo->iosr = iosr; + + bits = fls(flo->res); + /* 8 LBSs in data register contain chan info */ + max = flo->res << 8; + + /* if resolution is not a power of two */ + if (flo->res > BIT(bits - 1)) + bits++; + else + max--; + + shift = DFSDM_DATA_RES - bits; + /* + * Compute right/left shift + * Right shift is performed by hardware + * when transferring samples to data register. + * Left shift is done by software on buffer + */ + if (shift > 0) { + /* Resolution is lower than 24 bits */ + flo->rshift = 0; + flo->lshift = shift; + } else { + /* + * If resolution is 24 bits or more, + * max positive value may be ambiguous + * (equal to max negative value as sign + * bit is dropped). + * Reduce resolution to 23 bits (rshift) + * to keep the sign on bit 23 and treat + * saturation before rescaling on 24 + * bits (lshift). + */ + flo->rshift = 1 - shift; + flo->lshift = 1; + max >>= flo->rshift; + } + flo->max = (s32)max; + + pr_debug("%s: fast %d, fosr %d, iosr %d, res 0x%llx/%d bits, rshift %d, lshift %d\n", + __func__, fast, flo->fosr, flo->iosr, + flo->res, bits, flo->rshift, + flo->lshift); } - - if (!delta) - return 0; } } - if (!fl->res) + if (!flo->res) return -EINVAL; return 0; @@ -384,6 +426,36 @@ static int stm32_dfsdm_filter_set_trig(struct stm32_dfsdm_adc *adc, return 0; } +static int stm32_dfsdm_channels_configure(struct stm32_dfsdm_adc *adc, + unsigned int fl_id, + struct iio_trigger *trig) +{ + struct iio_dev *indio_dev = iio_priv_to_dev(adc); + struct regmap *regmap = adc->dfsdm->regmap; + struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[fl_id]; + struct stm32_dfsdm_filter_osr *flo = &fl->flo; + const struct iio_chan_spec *chan; + unsigned int bit; + int ret; + + if (!flo->res) + return -EINVAL; + + for_each_set_bit(bit, &adc->smask, + sizeof(adc->smask) * BITS_PER_BYTE) { + chan = indio_dev->channels + bit; + + ret = regmap_update_bits(regmap, + DFSDM_CHCFGR2(chan->channel), + DFSDM_CHCFGR2_DTRBS_MASK, + DFSDM_CHCFGR2_DTRBS(flo->rshift)); + if (ret) + return ret; + } + + return 0; +} + static int stm32_dfsdm_filter_configure(struct stm32_dfsdm_adc *adc, unsigned int fl_id, struct iio_trigger *trig) @@ -391,6 +463,7 @@ static int stm32_dfsdm_filter_configure(struct stm32_dfsdm_adc *adc, struct iio_dev *indio_dev = iio_priv_to_dev(adc); struct regmap *regmap = adc->dfsdm->regmap; struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[fl_id]; + struct stm32_dfsdm_filter_osr *flo = &fl->flo; u32 cr1; const struct iio_chan_spec *chan; unsigned int bit, jchg = 0; @@ -398,13 +471,13 @@ static int stm32_dfsdm_filter_configure(struct stm32_dfsdm_adc *adc, /* Average integrator oversampling */ ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_IOSR_MASK, - DFSDM_FCR_IOSR(fl->iosr - 1)); + DFSDM_FCR_IOSR(flo->iosr - 1)); if (ret) return ret; /* Filter order and Oversampling */ ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FOSR_MASK, - DFSDM_FCR_FOSR(fl->fosr - 1)); + DFSDM_FCR_FOSR(flo->fosr - 1)); if (ret) return ret; @@ -573,7 +646,7 @@ static int dfsdm_adc_set_samp_freq(struct iio_dev *indio_dev, "Rate not accurate. requested (%u), actual (%u)\n", sample_freq, spi_freq / oversamp); - ret = stm32_dfsdm_set_osrs(fl, 0, oversamp); + ret = stm32_dfsdm_compute_osrs(fl, 0, oversamp); if (ret < 0) { dev_err(&indio_dev->dev, "No filter parameters that match!\n"); return ret; @@ -623,6 +696,10 @@ static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc, struct regmap *regmap = adc->dfsdm->regmap; int ret; + ret = stm32_dfsdm_channels_configure(adc, adc->fl_id, trig); + if (ret < 0) + return ret; + ret = stm32_dfsdm_start_channel(adc); if (ret < 0) return ret; @@ -729,6 +806,8 @@ static void stm32_dfsdm_dma_buffer_done(void *data) { struct iio_dev *indio_dev = data; struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id]; + struct stm32_dfsdm_filter_osr *flo = &fl->flo; int available = stm32_dfsdm_adc_dma_residue(adc); size_t old_pos; @@ -751,10 +830,19 @@ static void stm32_dfsdm_dma_buffer_done(void *data) old_pos = adc->bufi; while (available >= indio_dev->scan_bytes) { - u32 *buffer = (u32 *)&adc->rx_buf[adc->bufi]; + s32 *buffer = (s32 *)&adc->rx_buf[adc->bufi]; /* Mask 8 LSB that contains the channel ID */ - *buffer = (*buffer & 0xFFFFFF00) << 8; + *buffer &= 0xFFFFFF00; + /* Convert 2^(n-1) sample to 2^(n-1)-1 to avoid wrap-around */ + if (*buffer > flo->max) + *buffer -= 1; + /* + * Samples from filter are retrieved with 23 bits resolution + * or less. Shift left to align MSB on 24 bits. + */ + *buffer <<= flo->lshift; + available -= indio_dev->scan_bytes; adc->bufi += indio_dev->scan_bytes; if (adc->bufi >= adc->buf_sz) { @@ -1078,7 +1166,7 @@ static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev, ret = iio_device_claim_direct_mode(indio_dev); if (ret) return ret; - ret = stm32_dfsdm_set_osrs(fl, 0, val); + ret = stm32_dfsdm_compute_osrs(fl, 0, val); if (!ret) adc->oversamp = val; iio_device_release_direct_mode(indio_dev); @@ -1327,8 +1415,8 @@ static int stm32_dfsdm_adc_init(struct iio_dev *indio_dev) int ret, chan_idx; adc->oversamp = DFSDM_DEFAULT_OVERSAMPLING; - ret = stm32_dfsdm_set_osrs(&adc->dfsdm->fl_list[adc->fl_id], 0, - adc->oversamp); + ret = stm32_dfsdm_compute_osrs(&adc->dfsdm->fl_list[adc->fl_id], 0, + adc->oversamp); if (ret < 0) return ret; diff --git a/drivers/iio/adc/stm32-dfsdm.h b/drivers/iio/adc/stm32-dfsdm.h index 8708394b0725..18b06ee6ed7b 100644 --- a/drivers/iio/adc/stm32-dfsdm.h +++ b/drivers/iio/adc/stm32-dfsdm.h @@ -243,19 +243,33 @@ enum stm32_dfsdm_sinc_order { }; /** - * struct stm32_dfsdm_filter - structure relative to stm32 FDSDM filter + * struct stm32_dfsdm_filter_osr - DFSDM filter settings linked to oversampling * @iosr: integrator oversampling * @fosr: filter oversampling - * @ford: filter order + * @rshift: output sample right shift (hardware shift) + * @lshift: output sample left shift (software shift) * @res: output sample resolution + * @max: output sample maximum positive value + */ +struct stm32_dfsdm_filter_osr { + unsigned int iosr; + unsigned int fosr; + unsigned int rshift; + unsigned int lshift; + u64 res; + s32 max; +}; + +/** + * struct stm32_dfsdm_filter - structure relative to stm32 FDSDM filter + * @ford: filter order + * @flo: filter oversampling structure * @sync_mode: filter synchronized with filter 0 * @fast: filter fast mode */ struct stm32_dfsdm_filter { - unsigned int iosr; - unsigned int fosr; enum stm32_dfsdm_sinc_order ford; - u64 res; + struct stm32_dfsdm_filter_osr flo; unsigned int sync_mode; unsigned int fast; }; -- cgit v1.2.3-70-g09d2 From c6013bf50e2a2a94ab3d012e191096432aa50c6f Mon Sep 17 00:00:00 2001 From: Olivier Moysan Date: Wed, 19 Jun 2019 15:03:48 +0200 Subject: iio: adc: stm32-dfsdm: fix data type Fix the data type as DFSDM raw output is complements 2, 24bits left aligned in a 32-bit register. This change does not affect AUDIO path - Set data as signed for IIO (as for AUDIO) - Set 8 bit right shift for IIO. The 8 LSBs bits of data contains channel info and are masked. Signed-off-by: Olivier Moysan Fixes: e2e6771c6462 ("IIO: ADC: add STM32 DFSDM sigma delta ADC support") Acked-by: Fabrice Gasnier Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-dfsdm-adc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c index cb596f104919..6b90a40882f2 100644 --- a/drivers/iio/adc/stm32-dfsdm-adc.c +++ b/drivers/iio/adc/stm32-dfsdm-adc.c @@ -1365,11 +1365,11 @@ static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev, BIT(IIO_CHAN_INFO_SAMP_FREQ); if (adc->dev_data->type == DFSDM_AUDIO) { - ch->scan_type.sign = 's'; ch->ext_info = dfsdm_adc_audio_ext_info; } else { - ch->scan_type.sign = 'u'; + ch->scan_type.shift = 8; } + ch->scan_type.sign = 's'; ch->scan_type.realbits = 24; ch->scan_type.storagebits = 32; -- cgit v1.2.3-70-g09d2 From 102afde62937de32758a023655ed90545c292245 Mon Sep 17 00:00:00 2001 From: Olivier Moysan Date: Wed, 19 Jun 2019 15:03:49 +0200 Subject: iio: adc: stm32-dfsdm: manage data resolution in trigger mode Add output sample resolution management in scan mode. Add stm32_dfsdm_process_data() function to share sample processing between continuous and trigger modes. Signed-off-by: Olivier Moysan Acked-by: Fabrice Gasnier Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-dfsdm-adc.c | 41 ++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c index 6b90a40882f2..5b19a88412a6 100644 --- a/drivers/iio/adc/stm32-dfsdm-adc.c +++ b/drivers/iio/adc/stm32-dfsdm-adc.c @@ -779,6 +779,30 @@ static unsigned int stm32_dfsdm_adc_dma_residue(struct stm32_dfsdm_adc *adc) return 0; } +static inline void stm32_dfsdm_process_data(struct stm32_dfsdm_adc *adc, + s32 *buffer) +{ + struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id]; + struct stm32_dfsdm_filter_osr *flo = &fl->flo; + unsigned int i = adc->nconv; + s32 *ptr = buffer; + + while (i--) { + /* Mask 8 LSB that contains the channel ID */ + *ptr &= 0xFFFFFF00; + /* Convert 2^(n-1) sample to 2^(n-1)-1 to avoid wrap-around */ + if (*ptr > flo->max) + *ptr -= 1; + /* + * Samples from filter are retrieved with 23 bits resolution + * or less. Shift left to align MSB on 24 bits. + */ + *ptr <<= flo->lshift; + + ptr++; + } +} + static irqreturn_t stm32_dfsdm_adc_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; @@ -787,7 +811,9 @@ static irqreturn_t stm32_dfsdm_adc_trigger_handler(int irq, void *p) int available = stm32_dfsdm_adc_dma_residue(adc); while (available >= indio_dev->scan_bytes) { - u32 *buffer = (u32 *)&adc->rx_buf[adc->bufi]; + s32 *buffer = (s32 *)&adc->rx_buf[adc->bufi]; + + stm32_dfsdm_process_data(adc, buffer); iio_push_to_buffers_with_timestamp(indio_dev, buffer, pf->timestamp); @@ -806,8 +832,6 @@ static void stm32_dfsdm_dma_buffer_done(void *data) { struct iio_dev *indio_dev = data; struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); - struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id]; - struct stm32_dfsdm_filter_osr *flo = &fl->flo; int available = stm32_dfsdm_adc_dma_residue(adc); size_t old_pos; @@ -832,16 +856,7 @@ static void stm32_dfsdm_dma_buffer_done(void *data) while (available >= indio_dev->scan_bytes) { s32 *buffer = (s32 *)&adc->rx_buf[adc->bufi]; - /* Mask 8 LSB that contains the channel ID */ - *buffer &= 0xFFFFFF00; - /* Convert 2^(n-1) sample to 2^(n-1)-1 to avoid wrap-around */ - if (*buffer > flo->max) - *buffer -= 1; - /* - * Samples from filter are retrieved with 23 bits resolution - * or less. Shift left to align MSB on 24 bits. - */ - *buffer <<= flo->lshift; + stm32_dfsdm_process_data(adc, buffer); available -= indio_dev->scan_bytes; adc->bufi += indio_dev->scan_bytes; -- cgit v1.2.3-70-g09d2 From d716204fd5472631970aa927c773c3302ac70fbe Mon Sep 17 00:00:00 2001 From: Olivier Moysan Date: Wed, 19 Jun 2019 15:03:50 +0200 Subject: iio: adc: stm32-dfsdm: add fast mode support The use of fast mode allows to get a larger set of solution for filter parameters. This can be useful to reach a better output sample resolution, when fast mode can be used. Fast mode is selected at startup if it is relevant. The startup is performed in postenable callback context, where there are too tight time constraints for filter parameters computation. For this reason both fast and non fast filter parameters are pre-computed previously. Signed-off-by: Olivier Moysan Acked-by: Fabrice Gasnier Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-dfsdm-adc.c | 65 ++++++++++++++++++++++++++++++--------- drivers/iio/adc/stm32-dfsdm.h | 4 +-- 2 files changed, 53 insertions(+), 16 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c index 5b19a88412a6..d855a605eab6 100644 --- a/drivers/iio/adc/stm32-dfsdm-adc.c +++ b/drivers/iio/adc/stm32-dfsdm-adc.c @@ -196,7 +196,7 @@ static int stm32_dfsdm_compute_osrs(struct stm32_dfsdm_filter *fl, int bits, shift; unsigned int m = 1; /* multiplication factor */ unsigned int p = fl->ford; /* filter order (ford) */ - struct stm32_dfsdm_filter_osr *flo = &fl->flo; + struct stm32_dfsdm_filter_osr *flo = &fl->flo[fast]; pr_debug("%s: Requested oversampling: %d\n", __func__, oversamp); /* @@ -217,7 +217,6 @@ static int stm32_dfsdm_compute_osrs(struct stm32_dfsdm_filter *fl, * Look for filter and integrator oversampling ratios which allows * to maximize data output resolution. */ - flo->res = 0; for (fosr = 1; fosr <= DFSDM_MAX_FL_OVERSAMPLING; fosr++) { for (iosr = 1; iosr <= DFSDM_MAX_INT_OVERSAMPLING; iosr++) { if (fast) @@ -309,6 +308,28 @@ static int stm32_dfsdm_compute_osrs(struct stm32_dfsdm_filter *fl, return 0; } +static int stm32_dfsdm_compute_all_osrs(struct iio_dev *indio_dev, + unsigned int oversamp) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id]; + int ret0, ret1; + + memset(&fl->flo[0], 0, sizeof(fl->flo[0])); + memset(&fl->flo[1], 0, sizeof(fl->flo[1])); + + ret0 = stm32_dfsdm_compute_osrs(fl, 0, oversamp); + ret1 = stm32_dfsdm_compute_osrs(fl, 1, oversamp); + if (ret0 < 0 && ret1 < 0) { + dev_err(&indio_dev->dev, + "Filter parameters not found: errors %d/%d\n", + ret0, ret1); + return -EINVAL; + } + + return 0; +} + static int stm32_dfsdm_start_channel(struct stm32_dfsdm_adc *adc) { struct iio_dev *indio_dev = iio_priv_to_dev(adc); @@ -433,11 +454,25 @@ static int stm32_dfsdm_channels_configure(struct stm32_dfsdm_adc *adc, struct iio_dev *indio_dev = iio_priv_to_dev(adc); struct regmap *regmap = adc->dfsdm->regmap; struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[fl_id]; - struct stm32_dfsdm_filter_osr *flo = &fl->flo; + struct stm32_dfsdm_filter_osr *flo = &fl->flo[0]; const struct iio_chan_spec *chan; unsigned int bit; int ret; + fl->fast = 0; + + /* + * In continuous mode, use fast mode configuration, + * if it provides a better resolution. + */ + if (adc->nconv == 1 && !trig && + (indio_dev->currentmode & INDIO_BUFFER_SOFTWARE)) { + if (fl->flo[1].res >= fl->flo[0].res) { + fl->fast = 1; + flo = &fl->flo[1]; + } + } + if (!flo->res) return -EINVAL; @@ -463,7 +498,7 @@ static int stm32_dfsdm_filter_configure(struct stm32_dfsdm_adc *adc, struct iio_dev *indio_dev = iio_priv_to_dev(adc); struct regmap *regmap = adc->dfsdm->regmap; struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[fl_id]; - struct stm32_dfsdm_filter_osr *flo = &fl->flo; + struct stm32_dfsdm_filter_osr *flo = &fl->flo[fl->fast]; u32 cr1; const struct iio_chan_spec *chan; unsigned int bit, jchg = 0; @@ -490,6 +525,12 @@ static int stm32_dfsdm_filter_configure(struct stm32_dfsdm_adc *adc, if (ret) return ret; + ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id), + DFSDM_CR1_FAST_MASK, + DFSDM_CR1_FAST(fl->fast)); + if (ret) + return ret; + /* * DFSDM modes configuration W.R.T audio/iio type modes * ---------------------------------------------------------------- @@ -636,7 +677,6 @@ static int dfsdm_adc_set_samp_freq(struct iio_dev *indio_dev, unsigned int spi_freq) { struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); - struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id]; unsigned int oversamp; int ret; @@ -646,11 +686,10 @@ static int dfsdm_adc_set_samp_freq(struct iio_dev *indio_dev, "Rate not accurate. requested (%u), actual (%u)\n", sample_freq, spi_freq / oversamp); - ret = stm32_dfsdm_compute_osrs(fl, 0, oversamp); - if (ret < 0) { - dev_err(&indio_dev->dev, "No filter parameters that match!\n"); + ret = stm32_dfsdm_compute_all_osrs(indio_dev, oversamp); + if (ret < 0) return ret; - } + adc->sample_freq = spi_freq / oversamp; adc->oversamp = oversamp; @@ -783,7 +822,7 @@ static inline void stm32_dfsdm_process_data(struct stm32_dfsdm_adc *adc, s32 *buffer) { struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id]; - struct stm32_dfsdm_filter_osr *flo = &fl->flo; + struct stm32_dfsdm_filter_osr *flo = &fl->flo[fl->fast]; unsigned int i = adc->nconv; s32 *ptr = buffer; @@ -1171,7 +1210,6 @@ static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev, int val, int val2, long mask) { struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); - struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id]; struct stm32_dfsdm_channel *ch = &adc->dfsdm->ch_list[chan->channel]; unsigned int spi_freq; int ret = -EINVAL; @@ -1181,7 +1219,7 @@ static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev, ret = iio_device_claim_direct_mode(indio_dev); if (ret) return ret; - ret = stm32_dfsdm_compute_osrs(fl, 0, val); + ret = stm32_dfsdm_compute_all_osrs(indio_dev, val); if (!ret) adc->oversamp = val; iio_device_release_direct_mode(indio_dev); @@ -1430,8 +1468,7 @@ static int stm32_dfsdm_adc_init(struct iio_dev *indio_dev) int ret, chan_idx; adc->oversamp = DFSDM_DEFAULT_OVERSAMPLING; - ret = stm32_dfsdm_compute_osrs(&adc->dfsdm->fl_list[adc->fl_id], 0, - adc->oversamp); + ret = stm32_dfsdm_compute_all_osrs(indio_dev, adc->oversamp); if (ret < 0) return ret; diff --git a/drivers/iio/adc/stm32-dfsdm.h b/drivers/iio/adc/stm32-dfsdm.h index 18b06ee6ed7b..5dbdae4ed881 100644 --- a/drivers/iio/adc/stm32-dfsdm.h +++ b/drivers/iio/adc/stm32-dfsdm.h @@ -263,13 +263,13 @@ struct stm32_dfsdm_filter_osr { /** * struct stm32_dfsdm_filter - structure relative to stm32 FDSDM filter * @ford: filter order - * @flo: filter oversampling structure + * @flo: filter oversampling data table indexed by fast mode flag * @sync_mode: filter synchronized with filter 0 * @fast: filter fast mode */ struct stm32_dfsdm_filter { enum stm32_dfsdm_sinc_order ford; - struct stm32_dfsdm_filter_osr flo; + struct stm32_dfsdm_filter_osr flo[2]; unsigned int sync_mode; unsigned int fast; }; -- cgit v1.2.3-70-g09d2 From 18eaffab90eb3c2280986bb546e571adc85756d5 Mon Sep 17 00:00:00 2001 From: Olivier Moysan Date: Wed, 19 Jun 2019 15:03:51 +0200 Subject: iio: adc: stm32-dfsdm: add comment for 16 bits record Add a comment on DMA configuration for 16 bits record. Signed-off-by: Olivier Moysan Acked-by: Fabrice Gasnier Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-dfsdm-adc.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c index d855a605eab6..ee1e0569d0e1 100644 --- a/drivers/iio/adc/stm32-dfsdm-adc.c +++ b/drivers/iio/adc/stm32-dfsdm-adc.c @@ -918,6 +918,11 @@ static void stm32_dfsdm_dma_buffer_done(void *data) static int stm32_dfsdm_adc_dma_start(struct iio_dev *indio_dev) { struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + /* + * The DFSDM supports half-word transfers. However, for 16 bits record, + * 4 bytes buswidth is kept, to avoid losing samples LSBs when left + * shift is required. + */ struct dma_slave_config config = { .src_addr = (dma_addr_t)adc->dfsdm->phys_base, .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, -- cgit v1.2.3-70-g09d2 From 7685010fca2ba0284f31fd1380df3cffc96d847e Mon Sep 17 00:00:00 2001 From: Fabrice Gasnier Date: Wed, 19 Jun 2019 14:29:55 +0200 Subject: iio: adc: stm32-adc: add missing vdda-supply Add missing vdda-supply, analog power supply, to STM32 ADC. When vdda is an independent supply, it needs to be properly turned on or off to supply the ADC. Signed-off-by: Fabrice Gasnier Fixes: 1add69880240 ("iio: adc: Add support for STM32 ADC core"). Cc: Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-adc-core.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c index 2327ec18b40c..1f7ce5186dfc 100644 --- a/drivers/iio/adc/stm32-adc-core.c +++ b/drivers/iio/adc/stm32-adc-core.c @@ -87,6 +87,7 @@ struct stm32_adc_priv_cfg { * @domain: irq domain reference * @aclk: clock reference for the analog circuitry * @bclk: bus clock common for all ADCs, depends on part used + * @vdda: vdda analog supply reference * @vref: regulator reference * @cfg: compatible configuration data * @common: common data for all ADC instances @@ -97,6 +98,7 @@ struct stm32_adc_priv { struct irq_domain *domain; struct clk *aclk; struct clk *bclk; + struct regulator *vdda; struct regulator *vref; const struct stm32_adc_priv_cfg *cfg; struct stm32_adc_common common; @@ -394,10 +396,16 @@ static int stm32_adc_core_hw_start(struct device *dev) struct stm32_adc_priv *priv = to_stm32_adc_priv(common); int ret; + ret = regulator_enable(priv->vdda); + if (ret < 0) { + dev_err(dev, "vdda enable failed %d\n", ret); + return ret; + } + ret = regulator_enable(priv->vref); if (ret < 0) { dev_err(dev, "vref enable failed\n"); - return ret; + goto err_vdda_disable; } if (priv->bclk) { @@ -425,6 +433,8 @@ err_bclk_disable: clk_disable_unprepare(priv->bclk); err_regulator_disable: regulator_disable(priv->vref); +err_vdda_disable: + regulator_disable(priv->vdda); return ret; } @@ -441,6 +451,7 @@ static void stm32_adc_core_hw_stop(struct device *dev) if (priv->bclk) clk_disable_unprepare(priv->bclk); regulator_disable(priv->vref); + regulator_disable(priv->vdda); } static int stm32_adc_probe(struct platform_device *pdev) @@ -468,6 +479,14 @@ static int stm32_adc_probe(struct platform_device *pdev) return PTR_ERR(priv->common.base); priv->common.phys_base = res->start; + priv->vdda = devm_regulator_get(&pdev->dev, "vdda"); + if (IS_ERR(priv->vdda)) { + ret = PTR_ERR(priv->vdda); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, "vdda get failed, %d\n", ret); + return ret; + } + priv->vref = devm_regulator_get(&pdev->dev, "vref"); if (IS_ERR(priv->vref)) { ret = PTR_ERR(priv->vref); -- cgit v1.2.3-70-g09d2 From 1c349f4fd36e7f957217402f3a7f87031dc8a0b7 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 18 Jun 2019 17:53:40 -0300 Subject: docs: iio: convert to ReST Rename the iio documentation files to ReST, add an index for them and adjust in order to produce a nice html output via the Sphinx build system. At its new index.rst, let's add a :orphan: while this is not linked to the main index.rst file, in order to avoid build warnings. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Cameron --- Documentation/iio/ep93xx_adc.rst | 40 +++++++++++++++ Documentation/iio/ep93xx_adc.txt | 29 ----------- Documentation/iio/iio_configfs.rst | 101 +++++++++++++++++++++++++++++++++++++ Documentation/iio/iio_configfs.txt | 93 ---------------------------------- Documentation/iio/index.rst | 12 +++++ drivers/iio/Kconfig | 2 +- 6 files changed, 154 insertions(+), 123 deletions(-) create mode 100644 Documentation/iio/ep93xx_adc.rst delete mode 100644 Documentation/iio/ep93xx_adc.txt create mode 100644 Documentation/iio/iio_configfs.rst delete mode 100644 Documentation/iio/iio_configfs.txt create mode 100644 Documentation/iio/index.rst (limited to 'drivers/iio') diff --git a/Documentation/iio/ep93xx_adc.rst b/Documentation/iio/ep93xx_adc.rst new file mode 100644 index 000000000000..4fd8dea3f6b8 --- /dev/null +++ b/Documentation/iio/ep93xx_adc.rst @@ -0,0 +1,40 @@ +============================== +Cirrus Logic EP93xx ADC driver +============================== + +1. Overview +=========== + +The driver is intended to work on both low-end (EP9301, EP9302) devices with +5-channel ADC and high-end (EP9307, EP9312, EP9315) devices with 10-channel +touchscreen/ADC module. + +2. Channel numbering +==================== + +Numbering scheme for channels 0..4 is defined in EP9301 and EP9302 datasheets. +EP9307, EP9312 and EP9312 have 3 channels more (total 8), but the numbering is +not defined. So the last three are numbered randomly, let's say. + +Assuming ep93xx_adc is IIO device0, you'd find the following entries under +/sys/bus/iio/devices/iio:device0/: + + +-----------------+---------------+ + | sysfs entry | ball/pin name | + +=================+===============+ + | in_voltage0_raw | YM | + +-----------------+---------------+ + | in_voltage1_raw | SXP | + +-----------------+---------------+ + | in_voltage2_raw | SXM | + +-----------------+---------------+ + | in_voltage3_raw | SYP | + +-----------------+---------------+ + | in_voltage4_raw | SYM | + +-----------------+---------------+ + | in_voltage5_raw | XP | + +-----------------+---------------+ + | in_voltage6_raw | XM | + +-----------------+---------------+ + | in_voltage7_raw | YP | + +-----------------+---------------+ diff --git a/Documentation/iio/ep93xx_adc.txt b/Documentation/iio/ep93xx_adc.txt deleted file mode 100644 index 23053e7817bd..000000000000 --- a/Documentation/iio/ep93xx_adc.txt +++ /dev/null @@ -1,29 +0,0 @@ -Cirrus Logic EP93xx ADC driver. - -1. Overview - -The driver is intended to work on both low-end (EP9301, EP9302) devices with -5-channel ADC and high-end (EP9307, EP9312, EP9315) devices with 10-channel -touchscreen/ADC module. - -2. Channel numbering - -Numbering scheme for channels 0..4 is defined in EP9301 and EP9302 datasheets. -EP9307, EP9312 and EP9312 have 3 channels more (total 8), but the numbering is -not defined. So the last three are numbered randomly, let's say. - -Assuming ep93xx_adc is IIO device0, you'd find the following entries under -/sys/bus/iio/devices/iio:device0/: - - +-----------------+---------------+ - | sysfs entry | ball/pin name | - +-----------------+---------------+ - | in_voltage0_raw | YM | - | in_voltage1_raw | SXP | - | in_voltage2_raw | SXM | - | in_voltage3_raw | SYP | - | in_voltage4_raw | SYM | - | in_voltage5_raw | XP | - | in_voltage6_raw | XM | - | in_voltage7_raw | YP | - +-----------------+---------------+ diff --git a/Documentation/iio/iio_configfs.rst b/Documentation/iio/iio_configfs.rst new file mode 100644 index 000000000000..ecbfdb3afef7 --- /dev/null +++ b/Documentation/iio/iio_configfs.rst @@ -0,0 +1,101 @@ +=============================== +Industrial IIO configfs support +=============================== + +1. Overview +=========== + +Configfs is a filesystem-based manager of kernel objects. IIO uses some +objects that could be easily configured using configfs (e.g.: devices, +triggers). + +See Documentation/filesystems/configfs/configfs.txt for more information +about how configfs works. + +2. Usage +======== + +In order to use configfs support in IIO we need to select it at compile +time via CONFIG_IIO_CONFIGFS config option. + +Then, mount the configfs filesystem (usually under /config directory):: + + $ mkdir /config + $ mount -t configfs none /config + +At this point, all default IIO groups will be created and can be accessed +under /config/iio. Next chapters will describe available IIO configuration +objects. + +3. Software triggers +==================== + +One of the IIO default configfs groups is the "triggers" group. It is +automagically accessible when the configfs is mounted and can be found +under /config/iio/triggers. + +IIO software triggers implementation offers support for creating multiple +trigger types. A new trigger type is usually implemented as a separate +kernel module following the interface in include/linux/iio/sw_trigger.h:: + + /* + * drivers/iio/trigger/iio-trig-sample.c + * sample kernel module implementing a new trigger type + */ + #include + + + static struct iio_sw_trigger *iio_trig_sample_probe(const char *name) + { + /* + * This allocates and registers an IIO trigger plus other + * trigger type specific initialization. + */ + } + + static int iio_trig_hrtimer_remove(struct iio_sw_trigger *swt) + { + /* + * This undoes the actions in iio_trig_sample_probe + */ + } + + static const struct iio_sw_trigger_ops iio_trig_sample_ops = { + .probe = iio_trig_sample_probe, + .remove = iio_trig_sample_remove, + }; + + static struct iio_sw_trigger_type iio_trig_sample = { + .name = "trig-sample", + .owner = THIS_MODULE, + .ops = &iio_trig_sample_ops, + }; + +module_iio_sw_trigger_driver(iio_trig_sample); + +Each trigger type has its own directory under /config/iio/triggers. Loading +iio-trig-sample module will create 'trig-sample' trigger type directory +/config/iio/triggers/trig-sample. + +We support the following interrupt sources (trigger types): + + * hrtimer, uses high resolution timers as interrupt source + +3.1 Hrtimer triggers creation and destruction +--------------------------------------------- + +Loading iio-trig-hrtimer module will register hrtimer trigger types allowing +users to create hrtimer triggers under /config/iio/triggers/hrtimer. + +e.g:: + + $ mkdir /config/iio/triggers/hrtimer/instance1 + $ rmdir /config/iio/triggers/hrtimer/instance1 + +Each trigger can have one or more attributes specific to the trigger type. + +3.2 "hrtimer" trigger types attributes +-------------------------------------- + +"hrtimer" trigger type doesn't have any configurable attribute from /config dir. +It does introduce the sampling_frequency attribute to trigger directory. diff --git a/Documentation/iio/iio_configfs.txt b/Documentation/iio/iio_configfs.txt deleted file mode 100644 index 4e5f101837a8..000000000000 --- a/Documentation/iio/iio_configfs.txt +++ /dev/null @@ -1,93 +0,0 @@ -Industrial IIO configfs support - -1. Overview - -Configfs is a filesystem-based manager of kernel objects. IIO uses some -objects that could be easily configured using configfs (e.g.: devices, -triggers). - -See Documentation/filesystems/configfs/configfs.txt for more information -about how configfs works. - -2. Usage - -In order to use configfs support in IIO we need to select it at compile -time via CONFIG_IIO_CONFIGFS config option. - -Then, mount the configfs filesystem (usually under /config directory): - -$ mkdir /config -$ mount -t configfs none /config - -At this point, all default IIO groups will be created and can be accessed -under /config/iio. Next chapters will describe available IIO configuration -objects. - -3. Software triggers - -One of the IIO default configfs groups is the "triggers" group. It is -automagically accessible when the configfs is mounted and can be found -under /config/iio/triggers. - -IIO software triggers implementation offers support for creating multiple -trigger types. A new trigger type is usually implemented as a separate -kernel module following the interface in include/linux/iio/sw_trigger.h: - -/* - * drivers/iio/trigger/iio-trig-sample.c - * sample kernel module implementing a new trigger type - */ -#include - - -static struct iio_sw_trigger *iio_trig_sample_probe(const char *name) -{ - /* - * This allocates and registers an IIO trigger plus other - * trigger type specific initialization. - */ -} - -static int iio_trig_hrtimer_remove(struct iio_sw_trigger *swt) -{ - /* - * This undoes the actions in iio_trig_sample_probe - */ -} - -static const struct iio_sw_trigger_ops iio_trig_sample_ops = { - .probe = iio_trig_sample_probe, - .remove = iio_trig_sample_remove, -}; - -static struct iio_sw_trigger_type iio_trig_sample = { - .name = "trig-sample", - .owner = THIS_MODULE, - .ops = &iio_trig_sample_ops, -}; - -module_iio_sw_trigger_driver(iio_trig_sample); - -Each trigger type has its own directory under /config/iio/triggers. Loading -iio-trig-sample module will create 'trig-sample' trigger type directory -/config/iio/triggers/trig-sample. - -We support the following interrupt sources (trigger types): - * hrtimer, uses high resolution timers as interrupt source - -3.1 Hrtimer triggers creation and destruction - -Loading iio-trig-hrtimer module will register hrtimer trigger types allowing -users to create hrtimer triggers under /config/iio/triggers/hrtimer. - -e.g: - -$ mkdir /config/iio/triggers/hrtimer/instance1 -$ rmdir /config/iio/triggers/hrtimer/instance1 - -Each trigger can have one or more attributes specific to the trigger type. - -3.2 "hrtimer" trigger types attributes - -"hrtimer" trigger type doesn't have any configurable attribute from /config dir. -It does introduce the sampling_frequency attribute to trigger directory. diff --git a/Documentation/iio/index.rst b/Documentation/iio/index.rst new file mode 100644 index 000000000000..0593dca89a94 --- /dev/null +++ b/Documentation/iio/index.rst @@ -0,0 +1,12 @@ +:orphan: + +============== +Industrial I/O +============== + +.. toctree:: + :maxdepth: 1 + + iio_configfs + + ep93xx_adc diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index a22cbee593fe..e8559c263a27 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -27,7 +27,7 @@ config IIO_CONFIGFS help This allows configuring various IIO bits through configfs (e.g. software triggers). For more info see - Documentation/iio/iio_configfs.txt. + Documentation/iio/iio_configfs.rst. config IIO_TRIGGER bool "Enable triggered sampling support" -- cgit v1.2.3-70-g09d2 From 8915aacac4dd5326a07f6b9c01f266289c57c71e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 18 Jun 2019 17:54:40 +0200 Subject: iio: core: no need to check return value of debugfs_create functions When calling debugfs functions, there is no need to ever check the return value. The function can work or not, but the code logic should never do something different based on this. Cc: Jonathan Cameron Cc: Hartmut Knaack Cc: Lars-Peter Clausen Cc: Peter Meerwald-Stadler Cc: linux-iio@vger.kernel.org Signed-off-by: Greg Kroah-Hartman Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 35 ++++++++--------------------------- 1 file changed, 8 insertions(+), 27 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index dd8873a752dd..85a699b5ae96 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -369,39 +369,25 @@ static void iio_device_unregister_debugfs(struct iio_dev *indio_dev) debugfs_remove_recursive(indio_dev->debugfs_dentry); } -static int iio_device_register_debugfs(struct iio_dev *indio_dev) +static void iio_device_register_debugfs(struct iio_dev *indio_dev) { - struct dentry *d; - if (indio_dev->info->debugfs_reg_access == NULL) - return 0; + return; if (!iio_debugfs_dentry) - return 0; + return; indio_dev->debugfs_dentry = debugfs_create_dir(dev_name(&indio_dev->dev), iio_debugfs_dentry); - if (indio_dev->debugfs_dentry == NULL) { - dev_warn(indio_dev->dev.parent, - "Failed to create debugfs directory\n"); - return -EFAULT; - } - - d = debugfs_create_file("direct_reg_access", 0644, - indio_dev->debugfs_dentry, - indio_dev, &iio_debugfs_reg_fops); - if (!d) { - iio_device_unregister_debugfs(indio_dev); - return -ENOMEM; - } - return 0; + debugfs_create_file("direct_reg_access", 0644, + indio_dev->debugfs_dentry, indio_dev, + &iio_debugfs_reg_fops); } #else -static int iio_device_register_debugfs(struct iio_dev *indio_dev) +static void iio_device_register_debugfs(struct iio_dev *indio_dev) { - return 0; } static void iio_device_unregister_debugfs(struct iio_dev *indio_dev) @@ -1674,12 +1660,7 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod) /* configure elements for the chrdev */ indio_dev->dev.devt = MKDEV(MAJOR(iio_devt), indio_dev->id); - ret = iio_device_register_debugfs(indio_dev); - if (ret) { - dev_err(indio_dev->dev.parent, - "Failed to register debugfs interfaces\n"); - return ret; - } + iio_device_register_debugfs(indio_dev); ret = iio_buffer_alloc_sysfs_and_mask(indio_dev); if (ret) { -- cgit v1.2.3-70-g09d2 From 10dd571c66a5bc5d3d5274829e00dea65ddc86cc Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Tue, 25 Jun 2019 15:48:40 +0300 Subject: iio: adis162xx: fix low-power docs & reports All current ADIS162XX drivers have incorrect values defined via comments. Also, when an error is reported the printed value is incorrect. The functionality itself isn't affected, so it's not a critical issue. And since the change is trivial, it was included in a single patch that fixes these in one go. All values were correlated with the ones specified in the data-sheets. Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adis16201.c | 4 ++-- drivers/iio/accel/adis16209.c | 4 ++-- drivers/staging/iio/accel/adis16203.c | 4 ++-- drivers/staging/iio/accel/adis16240.c | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/adis16201.c b/drivers/iio/accel/adis16201.c index 4c1d482ea73a..3257d4d27015 100644 --- a/drivers/iio/accel/adis16201.c +++ b/drivers/iio/accel/adis16201.c @@ -71,7 +71,7 @@ #define ADIS16201_DIAG_STAT_FLASH_UPT_FAIL_BIT 2 /* Power supply above 3.625 V */ #define ADIS16201_DIAG_STAT_POWER_HIGH_BIT 1 -/* Power supply below 3.15 V */ +/* Power supply below 2.975 V */ #define ADIS16201_DIAG_STAT_POWER_LOW_BIT 0 /* System Command Register Definition */ @@ -231,7 +231,7 @@ static const char * const adis16201_status_error_msgs[] = { [ADIS16201_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure", [ADIS16201_DIAG_STAT_FLASH_UPT_FAIL_BIT] = "Flash update failed", [ADIS16201_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V", - [ADIS16201_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 3.15V", + [ADIS16201_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 2.975V", }; static const struct adis_data adis16201_data = { diff --git a/drivers/iio/accel/adis16209.c b/drivers/iio/accel/adis16209.c index f2dc3a5f0463..01dd02f7e1d6 100644 --- a/drivers/iio/accel/adis16209.c +++ b/drivers/iio/accel/adis16209.c @@ -73,7 +73,7 @@ #define ADIS16209_STAT_FLASH_UPT_FAIL_BIT 2 /* Power supply above 3.625 V */ #define ADIS16209_STAT_POWER_HIGH_BIT 1 -/* Power supply below 3.15 V */ +/* Power supply below 2.975 V */ #define ADIS16209_STAT_POWER_LOW_BIT 0 #define ADIS16209_CMD_REG 0x3E @@ -241,7 +241,7 @@ static const char * const adis16209_status_error_msgs[] = { [ADIS16209_STAT_SPI_FAIL_BIT] = "SPI failure", [ADIS16209_STAT_FLASH_UPT_FAIL_BIT] = "Flash update failed", [ADIS16209_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V", - [ADIS16209_STAT_POWER_LOW_BIT] = "Power supply below 3.15V", + [ADIS16209_STAT_POWER_LOW_BIT] = "Power supply below 2.975V", }; static const struct adis_data adis16209_data = { diff --git a/drivers/staging/iio/accel/adis16203.c b/drivers/staging/iio/accel/adis16203.c index a5d974ac2e3b..39687139a7d3 100644 --- a/drivers/staging/iio/accel/adis16203.c +++ b/drivers/staging/iio/accel/adis16203.c @@ -122,7 +122,7 @@ /* Power supply above 3.625 V */ #define ADIS16203_DIAG_STAT_POWER_HIGH_BIT 1 -/* Power supply below 3.15 V */ +/* Power supply below 2.975 V */ #define ADIS16203_DIAG_STAT_POWER_LOW_BIT 0 /* GLOB_CMD */ @@ -234,7 +234,7 @@ static const char * const adis16203_status_error_msgs[] = { [ADIS16203_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure", [ADIS16203_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed", [ADIS16203_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V", - [ADIS16203_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 3.15V", + [ADIS16203_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 2.975V", }; static const struct adis_data adis16203_data = { diff --git a/drivers/staging/iio/accel/adis16240.c b/drivers/staging/iio/accel/adis16240.c index b80c8529784b..62f4b3b1b457 100644 --- a/drivers/staging/iio/accel/adis16240.c +++ b/drivers/staging/iio/accel/adis16240.c @@ -175,7 +175,7 @@ /* Power supply above 3.625 V */ #define ADIS16240_DIAG_STAT_POWER_HIGH_BIT 1 - /* Power supply below 3.15 V */ + /* Power supply below 2.225 V */ #define ADIS16240_DIAG_STAT_POWER_LOW_BIT 0 /* GLOB_CMD */ -- cgit v1.2.3-70-g09d2 From f1794fd7bdf7981e21595d0162ab4f7305b3c7c1 Mon Sep 17 00:00:00 2001 From: Mircea Caprioru Date: Tue, 25 Jun 2019 11:11:24 +0300 Subject: iio: adc: ad7124: Remove input number limitation The driver limits the user to use only 4/8 differential inputs, but this device has the option to use pseudo-differential channels. This will increase the number of channels to be equal with the number of inputs so 8 channels for ad7124-4 and 16 for ad7124-8. This patch removes the check between channel nodes and num_inputs value. Signed-off-by: Mircea Caprioru Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7124.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c index 659ef37d5fe8..810234db9c0d 100644 --- a/drivers/iio/adc/ad7124.c +++ b/drivers/iio/adc/ad7124.c @@ -462,13 +462,6 @@ static int ad7124_of_parse_channel_config(struct iio_dev *indio_dev, if (ret) goto err; - if (ain[0] >= st->chip_info->num_inputs || - ain[1] >= st->chip_info->num_inputs) { - dev_err(indio_dev->dev.parent, - "Input pin number out of range.\n"); - ret = -EINVAL; - goto err; - } st->channel_config[channel].ain = AD7124_CHANNEL_AINP(ain[0]) | AD7124_CHANNEL_AINM(ain[1]); st->channel_config[channel].bipolar = -- cgit v1.2.3-70-g09d2 From 0eaecea6e4878abbf1d3e8de3e4eeabc856133d4 Mon Sep 17 00:00:00 2001 From: Mircea Caprioru Date: Tue, 25 Jun 2019 11:11:25 +0300 Subject: iio: adc: ad7124: Add buffered input support This patch adds the option to enable the buffered mode for positive and negative inputs. Each option can be enabled independently. In buffered mode, the input channel feeds into a high impedance input stage of the buffer amplifier. Therefore, the input can tolerate significant source impedances and is tailored for direct connection to external resistive type sensors such as strain gages or RTDs. Signed-off-by: Mircea Caprioru Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7124.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c index 810234db9c0d..ab52c5e9ecb1 100644 --- a/drivers/iio/adc/ad7124.c +++ b/drivers/iio/adc/ad7124.c @@ -61,6 +61,8 @@ #define AD7124_CONFIG_REF_SEL(x) FIELD_PREP(AD7124_CONFIG_REF_SEL_MSK, x) #define AD7124_CONFIG_PGA_MSK GENMASK(2, 0) #define AD7124_CONFIG_PGA(x) FIELD_PREP(AD7124_CONFIG_PGA_MSK, x) +#define AD7124_CONFIG_IN_BUFF_MSK GENMASK(7, 6) +#define AD7124_CONFIG_IN_BUFF(x) FIELD_PREP(AD7124_CONFIG_IN_BUFF_MSK, x) /* AD7124_FILTER_X */ #define AD7124_FILTER_FS_MSK GENMASK(10, 0) @@ -108,6 +110,8 @@ struct ad7124_chip_info { struct ad7124_channel_config { enum ad7124_ref_sel refsel; bool bipolar; + bool buf_positive; + bool buf_negative; unsigned int ain; unsigned int vref_mv; unsigned int pga_bits; @@ -473,6 +477,11 @@ static int ad7124_of_parse_channel_config(struct iio_dev *indio_dev, else st->channel_config[channel].refsel = tmp; + st->channel_config[channel].buf_positive = + of_property_read_bool(child, "adi,buffered-positive"); + st->channel_config[channel].buf_negative = + of_property_read_bool(child, "adi,buffered-negative"); + *chan = ad7124_channel_template; chan->address = channel; chan->scan_index = channel; @@ -492,7 +501,7 @@ err: static int ad7124_setup(struct ad7124_state *st) { unsigned int val, fclk, power_mode; - int i, ret; + int i, ret, tmp; fclk = clk_get_rate(st->mclk); if (!fclk) @@ -525,8 +534,12 @@ static int ad7124_setup(struct ad7124_state *st) if (ret < 0) return ret; + tmp = (st->channel_config[i].buf_positive << 1) + + st->channel_config[i].buf_negative; + val = AD7124_CONFIG_BIPOLAR(st->channel_config[i].bipolar) | - AD7124_CONFIG_REF_SEL(st->channel_config[i].refsel); + AD7124_CONFIG_REF_SEL(st->channel_config[i].refsel) | + AD7124_CONFIG_IN_BUFF(tmp); ret = ad_sd_write_reg(&st->sd, AD7124_CONFIG(i), 2, val); if (ret < 0) return ret; -- cgit v1.2.3-70-g09d2 From 1478a388f4baaa6da4767a0d7cf82a47826b9fc0 Mon Sep 17 00:00:00 2001 From: Mircea Caprioru Date: Tue, 25 Jun 2019 11:11:26 +0300 Subject: iio: adc: ad7124: Shift to dynamic allocation for channel configuration This patch changes the channel configuration member of the device structure from a fixed size array to a dynamic allocated one with a size equal to the number of channels specified in the device tree. This will ensure a more flexibility for compatible devices. Ex. ad7124-4 - can have 4 differential or 8 pseudo-differential channels ad7124-8 - can have 8 differential or 16 pseudo-differential channels Also the device can suspport any other combination of differential and pseudo-differential channels base on the physical number of inputs available. Signed-off-by: Mircea Caprioru Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7124.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c index ab52c5e9ecb1..edc6f1cc90b2 100644 --- a/drivers/iio/adc/ad7124.c +++ b/drivers/iio/adc/ad7124.c @@ -121,7 +121,7 @@ struct ad7124_channel_config { struct ad7124_state { const struct ad7124_chip_info *chip_info; struct ad_sigma_delta sd; - struct ad7124_channel_config channel_config[4]; + struct ad7124_channel_config *channel_config; struct regulator *vref[4]; struct clk *mclk; unsigned int adc_control; @@ -439,6 +439,7 @@ static int ad7124_of_parse_channel_config(struct iio_dev *indio_dev, struct ad7124_state *st = iio_priv(indio_dev); struct device_node *child; struct iio_chan_spec *chan; + struct ad7124_channel_config *chan_config; unsigned int ain[2], channel = 0, tmp; int ret; @@ -453,8 +454,14 @@ static int ad7124_of_parse_channel_config(struct iio_dev *indio_dev, if (!chan) return -ENOMEM; + chan_config = devm_kcalloc(indio_dev->dev.parent, st->num_channels, + sizeof(*chan_config), GFP_KERNEL); + if (!chan_config) + return -ENOMEM; + indio_dev->channels = chan; indio_dev->num_channels = st->num_channels; + st->channel_config = chan_config; for_each_available_child_of_node(np, child) { ret = of_property_read_u32(child, "reg", &channel); -- cgit v1.2.3-70-g09d2 From 13a0af411a65c7cd452f03d86c7fe61e87109fa3 Mon Sep 17 00:00:00 2001 From: Stefan Popa Date: Mon, 24 Jun 2019 18:12:12 +0300 Subject: iio: frequency: adf4371: Add support for ADF4372 PLL The ADF4372 is part of the same family with ADF4371, the main difference is that it has only 3 channels instead of 4, as the frequency quadrupler is missing. As a result, the ADF4372 allows frequencies from 62.5 MHz to 16 GHz to be generated. Datasheet: Link: https://www.analog.com/media/en/technical-documentation/data-sheets/adf4372.pdf Signed-off-by: Stefan Popa Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/Kconfig | 6 +++--- drivers/iio/frequency/adf4371.c | 31 ++++++++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 6 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/frequency/Kconfig b/drivers/iio/frequency/Kconfig index e4a921ff60ea..353914bd6c05 100644 --- a/drivers/iio/frequency/Kconfig +++ b/drivers/iio/frequency/Kconfig @@ -39,12 +39,12 @@ config ADF4350 module will be called adf4350. config ADF4371 - tristate "Analog Devices ADF4371 Wideband Synthesizer" + tristate "Analog Devices ADF4371/ADF4372 Wideband Synthesizers" depends on SPI select REGMAP_SPI help - Say yes here to build support for Analog Devices ADF4371 - Wideband Synthesizer. The driver provides direct access via sysfs. + Say yes here to build support for Analog Devices ADF4371 and ADF4372 + Wideband Synthesizers. The driver provides direct access via sysfs. To compile this driver as a module, choose M here: the module will be called adf4371. diff --git a/drivers/iio/frequency/adf4371.c b/drivers/iio/frequency/adf4371.c index d8c414b70e44..f874219fa8a2 100644 --- a/drivers/iio/frequency/adf4371.c +++ b/drivers/iio/frequency/adf4371.c @@ -87,6 +87,11 @@ enum { ADF4371_CH_RF32 }; +enum adf4371_variant { + ADF4371, + ADF4372 +}; + struct adf4371_pwrdown { unsigned int reg; unsigned int bit; @@ -140,6 +145,11 @@ static const struct regmap_config adf4371_regmap_config = { .read_flag_mask = BIT(7), }; +struct adf4371_chip_info { + unsigned int num_channels; + const struct iio_chan_spec *channels; +}; + struct adf4371_state { struct spi_device *spi; struct regmap *regmap; @@ -152,6 +162,7 @@ struct adf4371_state { * writes. */ struct mutex lock; + const struct adf4371_chip_info *chip_info; unsigned long clkin_freq; unsigned long fpfd; unsigned int integer; @@ -429,6 +440,17 @@ static const struct iio_chan_spec adf4371_chan[] = { ADF4371_CHANNEL(ADF4371_CH_RF32), }; +static const struct adf4371_chip_info adf4371_chip_info[] = { + [ADF4371] = { + .channels = adf4371_chan, + .num_channels = 4, + }, + [ADF4372] = { + .channels = adf4371_chan, + .num_channels = 3, + } +}; + static int adf4371_reg_access(struct iio_dev *indio_dev, unsigned int reg, unsigned int writeval, @@ -537,12 +559,13 @@ static int adf4371_probe(struct spi_device *spi) st->regmap = regmap; mutex_init(&st->lock); + st->chip_info = &adf4371_chip_info[id->driver_data]; indio_dev->dev.parent = &spi->dev; indio_dev->name = id->name; indio_dev->info = &adf4371_info; indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = adf4371_chan; - indio_dev->num_channels = ARRAY_SIZE(adf4371_chan); + indio_dev->channels = st->chip_info->channels; + indio_dev->num_channels = st->chip_info->num_channels; st->clkin = devm_clk_get(&spi->dev, "clkin"); if (IS_ERR(st->clkin)) @@ -568,13 +591,15 @@ static int adf4371_probe(struct spi_device *spi) } static const struct spi_device_id adf4371_id_table[] = { - { "adf4371", 0 }, + { "adf4371", ADF4371 }, + { "adf4372", ADF4372 }, {} }; MODULE_DEVICE_TABLE(spi, adf4371_id_table); static const struct of_device_id adf4371_of_match[] = { { .compatible = "adi,adf4371" }, + { .compatible = "adi,adf4372" }, { }, }; MODULE_DEVICE_TABLE(of, adf4371_of_match); -- cgit v1.2.3-70-g09d2 From def914a4c3899b6b3705c8ea67d29972f5652a14 Mon Sep 17 00:00:00 2001 From: Stefan Popa Date: Mon, 24 Jun 2019 18:13:56 +0300 Subject: iio: frequency: adf4371: Add support for output stage mute Another feature of the ADF4371/ADF4372 is that the supply current to the RF8P and RF8N output stage can shut down until the ADF4371 achieves lock as measured by the digital lock detect circuitry. The mute to lock detect bit (MUTE_LD) in REG25 enables this function. Signed-off-by: Stefan Popa Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/frequency/adf4371.yaml | 7 +++++++ drivers/iio/frequency/adf4371.c | 13 +++++++++++++ 2 files changed, 20 insertions(+) (limited to 'drivers/iio') diff --git a/Documentation/devicetree/bindings/iio/frequency/adf4371.yaml b/Documentation/devicetree/bindings/iio/frequency/adf4371.yaml index 060900156ae5..7ec3ec94356b 100644 --- a/Documentation/devicetree/bindings/iio/frequency/adf4371.yaml +++ b/Documentation/devicetree/bindings/iio/frequency/adf4371.yaml @@ -33,6 +33,13 @@ properties: Must be "clkin" maxItems: 1 + adi,mute-till-lock-en: + type: boolean + description: + If this property is present, then the supply current to RF8P and RF8N + output stage will shut down until the ADF4371/ADF4372 achieves lock as + measured by the digital lock detect circuitry. + required: - compatible - reg diff --git a/drivers/iio/frequency/adf4371.c b/drivers/iio/frequency/adf4371.c index f874219fa8a2..e48f15cc9ab5 100644 --- a/drivers/iio/frequency/adf4371.c +++ b/drivers/iio/frequency/adf4371.c @@ -45,6 +45,10 @@ #define ADF4371_RF_DIV_SEL_MSK GENMASK(6, 4) #define ADF4371_RF_DIV_SEL(x) FIELD_PREP(ADF4371_RF_DIV_SEL_MSK, x) +/* ADF4371_REG25 */ +#define ADF4371_MUTE_LD_MSK BIT(7) +#define ADF4371_MUTE_LD(x) FIELD_PREP(ADF4371_MUTE_LD_MSK, x) + /* ADF4371_REG32 */ #define ADF4371_TIMEOUT_MSK GENMASK(1, 0) #define ADF4371_TIMEOUT(x) FIELD_PREP(ADF4371_TIMEOUT_MSK, x) @@ -484,6 +488,15 @@ static int adf4371_setup(struct adf4371_state *st) if (ret < 0) return ret; + /* Mute to Lock Detect */ + if (device_property_read_bool(&st->spi->dev, "adi,mute-till-lock-en")) { + ret = regmap_update_bits(st->regmap, ADF4371_REG(0x25), + ADF4371_MUTE_LD_MSK, + ADF4371_MUTE_LD(1)); + if (ret < 0) + return ret; + } + /* Set address in ascending order, so the bulk_write() will work */ ret = regmap_update_bits(st->regmap, ADF4371_REG(0x0), ADF4371_ADDR_ASC_MSK | ADF4371_ADDR_ASC_R_MSK, -- cgit v1.2.3-70-g09d2