diff options
Diffstat (limited to 'drivers/iio')
47 files changed, 2608 insertions, 827 deletions
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index b3c8c6ef0dff..d69e85a8bdc3 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -70,6 +70,7 @@ config IIO_TRIGGERED_EVENT source "drivers/iio/accel/Kconfig" source "drivers/iio/adc/Kconfig" +source "drivers/iio/afe/Kconfig" source "drivers/iio/amplifiers/Kconfig" source "drivers/iio/chemical/Kconfig" source "drivers/iio/common/Kconfig" diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index b16b2e9ddc40..d8cba9c229c0 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_IIO_TRIGGERED_EVENT) += industrialio-triggered-event.o obj-y += accel/ obj-y += adc/ +obj-y += afe/ obj-y += amplifiers/ obj-y += buffer/ obj-y += chemical/ diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index c6d9517d7611..62ae7e5abcfa 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -5,6 +5,30 @@ menu "Accelerometers" +config ADIS16201 + tristate "Analog Devices ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer" + depends on SPI + select IIO_ADIS_LIB + select IIO_ADIS_LIB_BUFFER if IIO_BUFFER + help + Say Y here to build support for Analog Devices adis16201 dual-axis + digital inclinometer and accelerometer. + + To compile this driver as a module, say M here: the module will + be called adis16201. + +config ADIS16209 + tristate "Analog Devices ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer" + depends on SPI + select IIO_ADIS_LIB + select IIO_ADIS_LIB_BUFFER if IIO_BUFFER + help + Say Y here to build support for Analog Devices adis16209 dual-axis digital inclinometer + and accelerometer. + + To compile this driver as a module, say M here: the module will be + called adis16209. + config ADXL345 tristate diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile index 368aedb6377a..636d4d1b2990 100644 --- a/drivers/iio/accel/Makefile +++ b/drivers/iio/accel/Makefile @@ -4,6 +4,8 @@ # # When adding new entries keep the list in alphabetical order +obj-$(CONFIG_ADIS16201) += adis16201.o +obj-$(CONFIG_ADIS16209) += adis16209.o obj-$(CONFIG_ADXL345) += adxl345_core.o obj-$(CONFIG_ADXL345_I2C) += adxl345_i2c.o obj-$(CONFIG_ADXL345_SPI) += adxl345_spi.o diff --git a/drivers/iio/accel/adis16201.c b/drivers/iio/accel/adis16201.c new file mode 100644 index 000000000000..4c1d482ea73a --- /dev/null +++ b/drivers/iio/accel/adis16201.c @@ -0,0 +1,321 @@ +/* + * ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> +#include <linux/sysfs.h> + +#include <linux/iio/iio.h> +#include <linux/iio/imu/adis.h> + +#define ADIS16201_STARTUP_DELAY_MS 220 +#define ADIS16201_FLASH_CNT 0x00 + +/* Data Output Register Information */ +#define ADIS16201_SUPPLY_OUT_REG 0x02 +#define ADIS16201_XACCL_OUT_REG 0x04 +#define ADIS16201_YACCL_OUT_REG 0x06 +#define ADIS16201_AUX_ADC_REG 0x08 +#define ADIS16201_TEMP_OUT_REG 0x0A +#define ADIS16201_XINCL_OUT_REG 0x0C +#define ADIS16201_YINCL_OUT_REG 0x0E + +/* Calibration Register Definition */ +#define ADIS16201_XACCL_OFFS_REG 0x10 +#define ADIS16201_YACCL_OFFS_REG 0x12 +#define ADIS16201_XACCL_SCALE_REG 0x14 +#define ADIS16201_YACCL_SCALE_REG 0x16 +#define ADIS16201_XINCL_OFFS_REG 0x18 +#define ADIS16201_YINCL_OFFS_REG 0x1A +#define ADIS16201_XINCL_SCALE_REG 0x1C +#define ADIS16201_YINCL_SCALE_REG 0x1E + +/* Alarm Register Definition */ +#define ADIS16201_ALM_MAG1_REG 0x20 +#define ADIS16201_ALM_MAG2_REG 0x22 +#define ADIS16201_ALM_SMPL1_REG 0x24 +#define ADIS16201_ALM_SMPL2_REG 0x26 +#define ADIS16201_ALM_CTRL_REG 0x28 + +#define ADIS16201_AUX_DAC_REG 0x30 +#define ADIS16201_GPIO_CTRL_REG 0x32 +#define ADIS16201_SMPL_PRD_REG 0x36 +/* Operation, filter configuration */ +#define ADIS16201_AVG_CNT_REG 0x38 +#define ADIS16201_SLP_CNT_REG 0x3A + +/* Miscellaneous Control Register Definition */ +#define ADIS16201_MSC_CTRL_REG 0x34 +#define ADIS16201_MSC_CTRL_SELF_TEST_EN BIT(8) +/* Data-ready enable: 1 = enabled, 0 = disabled */ +#define ADIS16201_MSC_CTRL_DATA_RDY_EN BIT(2) +/* Data-ready polarity: 1 = active high, 0 = active low */ +#define ADIS16201_MSC_CTRL_ACTIVE_DATA_RDY_HIGH BIT(1) +/* Data-ready line selection: 1 = DIO1, 0 = DIO0 */ +#define ADIS16201_MSC_CTRL_DATA_RDY_DIO1 BIT(0) + +/* Diagnostics System Status Register Definition */ +#define ADIS16201_DIAG_STAT_REG 0x3C +#define ADIS16201_DIAG_STAT_ALARM2 BIT(9) +#define ADIS16201_DIAG_STAT_ALARM1 BIT(8) +#define ADIS16201_DIAG_STAT_SPI_FAIL_BIT 3 +#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 */ +#define ADIS16201_DIAG_STAT_POWER_LOW_BIT 0 + +/* System Command Register Definition */ +#define ADIS16201_GLOB_CMD_REG 0x3E +#define ADIS16201_GLOB_CMD_SW_RESET BIT(7) +#define ADIS16201_GLOB_CMD_FACTORY_RESET BIT(1) + +#define ADIS16201_ERROR_ACTIVE BIT(14) + +enum adis16201_scan { + ADIS16201_SCAN_ACC_X, + ADIS16201_SCAN_ACC_Y, + ADIS16201_SCAN_INCLI_X, + ADIS16201_SCAN_INCLI_Y, + ADIS16201_SCAN_SUPPLY, + ADIS16201_SCAN_AUX_ADC, + ADIS16201_SCAN_TEMP, +}; + +static const u8 adis16201_addresses[] = { + [ADIS16201_SCAN_ACC_X] = ADIS16201_XACCL_OFFS_REG, + [ADIS16201_SCAN_ACC_Y] = ADIS16201_YACCL_OFFS_REG, + [ADIS16201_SCAN_INCLI_X] = ADIS16201_XINCL_OFFS_REG, + [ADIS16201_SCAN_INCLI_Y] = ADIS16201_YINCL_OFFS_REG, +}; + +static int adis16201_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, + long mask) +{ + struct adis *st = iio_priv(indio_dev); + int ret; + int bits; + u8 addr; + s16 val16; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + return adis_single_conversion(indio_dev, chan, + ADIS16201_ERROR_ACTIVE, val); + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + if (chan->channel == 0) { + /* Voltage base units are mV hence 1.22 mV */ + *val = 1; + *val2 = 220000; + } else { + /* Voltage base units are mV hence 0.61 mV */ + *val = 0; + *val2 = 610000; + } + return IIO_VAL_INT_PLUS_MICRO; + case IIO_TEMP: + *val = -470; + *val2 = 0; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_ACCEL: + /* + * IIO base unit for sensitivity of accelerometer + * is milli g. + * 1 LSB represents 0.244 mg. + */ + *val = 0; + *val2 = IIO_G_TO_M_S_2(462400); + return IIO_VAL_INT_PLUS_NANO; + case IIO_INCLI: + *val = 0; + *val2 = 100000; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + break; + case IIO_CHAN_INFO_OFFSET: + /* + * The raw ADC value is 1278 when the temperature + * is 25 degrees and the scale factor per milli + * degree celcius is -470. + */ + *val = 25000 / -470 - 1278; + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBBIAS: + switch (chan->type) { + case IIO_ACCEL: + bits = 12; + break; + case IIO_INCLI: + bits = 9; + break; + default: + return -EINVAL; + } + addr = adis16201_addresses[chan->scan_index]; + ret = adis_read_reg_16(st, addr, &val16); + if (ret) + return ret; + + *val = sign_extend32(val16, bits - 1); + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static int adis16201_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct adis *st = iio_priv(indio_dev); + int m; + + if (mask != IIO_CHAN_INFO_CALIBBIAS) + return -EINVAL; + + switch (chan->type) { + case IIO_ACCEL: + m = GENMASK(11, 0); + break; + case IIO_INCLI: + m = GENMASK(8, 0); + break; + default: + return -EINVAL; + } + + return adis_write_reg_16(st, adis16201_addresses[chan->scan_index], + val & m); +} + +static const struct iio_chan_spec adis16201_channels[] = { + ADIS_SUPPLY_CHAN(ADIS16201_SUPPLY_OUT_REG, ADIS16201_SCAN_SUPPLY, 0, + 12), + ADIS_TEMP_CHAN(ADIS16201_TEMP_OUT_REG, ADIS16201_SCAN_TEMP, 0, 12), + ADIS_ACCEL_CHAN(X, ADIS16201_XACCL_OUT_REG, ADIS16201_SCAN_ACC_X, + BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), + ADIS_ACCEL_CHAN(Y, ADIS16201_YACCL_OUT_REG, ADIS16201_SCAN_ACC_Y, + BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), + ADIS_AUX_ADC_CHAN(ADIS16201_AUX_ADC_REG, ADIS16201_SCAN_AUX_ADC, 0, 12), + ADIS_INCLI_CHAN(X, ADIS16201_XINCL_OUT_REG, ADIS16201_SCAN_INCLI_X, + BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), + ADIS_INCLI_CHAN(X, ADIS16201_YINCL_OUT_REG, ADIS16201_SCAN_INCLI_Y, + BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), + IIO_CHAN_SOFT_TIMESTAMP(7) +}; + +static const struct iio_info adis16201_info = { + .read_raw = adis16201_read_raw, + .write_raw = adis16201_write_raw, + .update_scan_mode = adis_update_scan_mode, +}; + +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", +}; + +static const struct adis_data adis16201_data = { + .read_delay = 20, + .msc_ctrl_reg = ADIS16201_MSC_CTRL_REG, + .glob_cmd_reg = ADIS16201_GLOB_CMD_REG, + .diag_stat_reg = ADIS16201_DIAG_STAT_REG, + + .self_test_mask = ADIS16201_MSC_CTRL_SELF_TEST_EN, + .self_test_no_autoclear = true, + .startup_delay = ADIS16201_STARTUP_DELAY_MS, + + .status_error_msgs = adis16201_status_error_msgs, + .status_error_mask = BIT(ADIS16201_DIAG_STAT_SPI_FAIL_BIT) | + BIT(ADIS16201_DIAG_STAT_FLASH_UPT_FAIL_BIT) | + BIT(ADIS16201_DIAG_STAT_POWER_HIGH_BIT) | + BIT(ADIS16201_DIAG_STAT_POWER_LOW_BIT), +}; + +static int adis16201_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct adis *st; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + spi_set_drvdata(spi, indio_dev); + + indio_dev->name = spi->dev.driver->name; + indio_dev->dev.parent = &spi->dev; + indio_dev->info = &adis16201_info; + + indio_dev->channels = adis16201_channels; + indio_dev->num_channels = ARRAY_SIZE(adis16201_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = adis_init(st, indio_dev, spi, &adis16201_data); + if (ret) + return ret; + + ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL); + if (ret) + return ret; + + ret = adis_initial_startup(st); + if (ret) + goto error_cleanup_buffer_trigger; + + ret = iio_device_register(indio_dev); + if (ret < 0) + goto error_cleanup_buffer_trigger; + + return 0; + +error_cleanup_buffer_trigger: + adis_cleanup_buffer_and_trigger(st, indio_dev); + return ret; +} + +static int adis16201_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct adis *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + adis_cleanup_buffer_and_trigger(st, indio_dev); + + return 0; +} + +static struct spi_driver adis16201_driver = { + .driver = { + .name = "adis16201", + }, + .probe = adis16201_probe, + .remove = adis16201_remove, +}; +module_spi_driver(adis16201_driver); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:adis16201"); diff --git a/drivers/iio/accel/adis16209.c b/drivers/iio/accel/adis16209.c new file mode 100644 index 000000000000..f2dc3a5f0463 --- /dev/null +++ b/drivers/iio/accel/adis16209.c @@ -0,0 +1,330 @@ +/* + * ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer + * + * Copyright 2010 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/spi/spi.h> +#include <linux/slab.h> +#include <linux/sysfs.h> + +#include <linux/iio/iio.h> +#include <linux/iio/imu/adis.h> + +#define ADIS16209_STARTUP_DELAY_MS 220 +#define ADIS16209_FLASH_CNT_REG 0x00 + +/* Data Output Register Definitions */ +#define ADIS16209_SUPPLY_OUT_REG 0x02 +#define ADIS16209_XACCL_OUT_REG 0x04 +#define ADIS16209_YACCL_OUT_REG 0x06 +/* Output, auxiliary ADC input */ +#define ADIS16209_AUX_ADC_REG 0x08 +/* Output, temperature */ +#define ADIS16209_TEMP_OUT_REG 0x0A +/* Output, +/- 90 degrees X-axis inclination */ +#define ADIS16209_XINCL_OUT_REG 0x0C +#define ADIS16209_YINCL_OUT_REG 0x0E +/* Output, +/-180 vertical rotational position */ +#define ADIS16209_ROT_OUT_REG 0x10 + +/* + * Calibration Register Definitions. + * Acceleration, inclination or rotation offset null. + */ +#define ADIS16209_XACCL_NULL_REG 0x12 +#define ADIS16209_YACCL_NULL_REG 0x14 +#define ADIS16209_XINCL_NULL_REG 0x16 +#define ADIS16209_YINCL_NULL_REG 0x18 +#define ADIS16209_ROT_NULL_REG 0x1A + +/* Alarm Register Definitions */ +#define ADIS16209_ALM_MAG1_REG 0x20 +#define ADIS16209_ALM_MAG2_REG 0x22 +#define ADIS16209_ALM_SMPL1_REG 0x24 +#define ADIS16209_ALM_SMPL2_REG 0x26 +#define ADIS16209_ALM_CTRL_REG 0x28 + +#define ADIS16209_AUX_DAC_REG 0x30 +#define ADIS16209_GPIO_CTRL_REG 0x32 +#define ADIS16209_SMPL_PRD_REG 0x36 +#define ADIS16209_AVG_CNT_REG 0x38 +#define ADIS16209_SLP_CNT_REG 0x3A + +#define ADIS16209_MSC_CTRL_REG 0x34 +#define ADIS16209_MSC_CTRL_PWRUP_SELF_TEST BIT(10) +#define ADIS16209_MSC_CTRL_SELF_TEST_EN BIT(8) +#define ADIS16209_MSC_CTRL_DATA_RDY_EN BIT(2) +/* Data-ready polarity: 1 = active high, 0 = active low */ +#define ADIS16209_MSC_CTRL_ACTIVE_HIGH BIT(1) +#define ADIS16209_MSC_CTRL_DATA_RDY_DIO2 BIT(0) + +#define ADIS16209_STAT_REG 0x3C +#define ADIS16209_STAT_ALARM2 BIT(9) +#define ADIS16209_STAT_ALARM1 BIT(8) +#define ADIS16209_STAT_SELFTEST_FAIL_BIT 5 +#define ADIS16209_STAT_SPI_FAIL_BIT 3 +#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 */ +#define ADIS16209_STAT_POWER_LOW_BIT 0 + +#define ADIS16209_CMD_REG 0x3E +#define ADIS16209_CMD_SW_RESET BIT(7) +#define ADIS16209_CMD_CLEAR_STAT BIT(4) +#define ADIS16209_CMD_FACTORY_CAL BIT(1) + +#define ADIS16209_ERROR_ACTIVE BIT(14) + +enum adis16209_scan { + ADIS16209_SCAN_SUPPLY, + ADIS16209_SCAN_ACC_X, + ADIS16209_SCAN_ACC_Y, + ADIS16209_SCAN_AUX_ADC, + ADIS16209_SCAN_TEMP, + ADIS16209_SCAN_INCLI_X, + ADIS16209_SCAN_INCLI_Y, + ADIS16209_SCAN_ROT, +}; + +static const u8 adis16209_addresses[8][1] = { + [ADIS16209_SCAN_SUPPLY] = { }, + [ADIS16209_SCAN_AUX_ADC] = { }, + [ADIS16209_SCAN_ACC_X] = { ADIS16209_XACCL_NULL_REG }, + [ADIS16209_SCAN_ACC_Y] = { ADIS16209_YACCL_NULL_REG }, + [ADIS16209_SCAN_INCLI_X] = { ADIS16209_XINCL_NULL_REG }, + [ADIS16209_SCAN_INCLI_Y] = { ADIS16209_YINCL_NULL_REG }, + [ADIS16209_SCAN_ROT] = { }, + [ADIS16209_SCAN_TEMP] = { }, +}; + +static int adis16209_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct adis *st = iio_priv(indio_dev); + int m; + + if (mask != IIO_CHAN_INFO_CALIBBIAS) + return -EINVAL; + + switch (chan->type) { + case IIO_ACCEL: + case IIO_INCLI: + m = GENMASK(13, 0); + break; + default: + return -EINVAL; + } + + return adis_write_reg_16(st, adis16209_addresses[chan->scan_index][0], + val & m); +} + +static int adis16209_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, + long mask) +{ + struct adis *st = iio_priv(indio_dev); + int ret; + int bits; + u8 addr; + s16 val16; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + return adis_single_conversion(indio_dev, chan, + ADIS16209_ERROR_ACTIVE, val); + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + *val = 0; + switch (chan->channel) { + case 0: + *val2 = 305180; /* 0.30518 mV */ + break; + case 1: + *val2 = 610500; /* 0.6105 mV */ + break; + default: + return -EINVAL; + } + return IIO_VAL_INT_PLUS_MICRO; + case IIO_TEMP: + *val = -470; + *val2 = 0; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_ACCEL: + /* + * IIO base unit for sensitivity of accelerometer + * is milli g. + * 1 LSB represents 0.244 mg. + */ + *val = 0; + *val2 = IIO_G_TO_M_S_2(244140); + return IIO_VAL_INT_PLUS_NANO; + case IIO_INCLI: + case IIO_ROT: + /* + * IIO base units for rotation are degrees. + * 1 LSB represents 0.025 milli degrees. + */ + *val = 0; + *val2 = 25000; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + break; + case IIO_CHAN_INFO_OFFSET: + /* + * The raw ADC value is 0x4FE when the temperature + * is 45 degrees and the scale factor per milli + * degree celcius is -470. + */ + *val = 25000 / -470 - 0x4FE; + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBBIAS: + switch (chan->type) { + case IIO_ACCEL: + bits = 14; + break; + default: + return -EINVAL; + } + addr = adis16209_addresses[chan->scan_index][0]; + ret = adis_read_reg_16(st, addr, &val16); + if (ret) + return ret; + + *val = sign_extend32(val16, bits - 1); + return IIO_VAL_INT; + } + return -EINVAL; +} + +static const struct iio_chan_spec adis16209_channels[] = { + ADIS_SUPPLY_CHAN(ADIS16209_SUPPLY_OUT_REG, ADIS16209_SCAN_SUPPLY, + 0, 14), + ADIS_TEMP_CHAN(ADIS16209_TEMP_OUT_REG, ADIS16209_SCAN_TEMP, 0, 12), + ADIS_ACCEL_CHAN(X, ADIS16209_XACCL_OUT_REG, ADIS16209_SCAN_ACC_X, + BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), + ADIS_ACCEL_CHAN(Y, ADIS16209_YACCL_OUT_REG, ADIS16209_SCAN_ACC_Y, + BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), + ADIS_AUX_ADC_CHAN(ADIS16209_AUX_ADC_REG, ADIS16209_SCAN_AUX_ADC, 0, 12), + ADIS_INCLI_CHAN(X, ADIS16209_XINCL_OUT_REG, ADIS16209_SCAN_INCLI_X, + 0, 0, 14), + ADIS_INCLI_CHAN(Y, ADIS16209_YINCL_OUT_REG, ADIS16209_SCAN_INCLI_Y, + 0, 0, 14), + ADIS_ROT_CHAN(X, ADIS16209_ROT_OUT_REG, ADIS16209_SCAN_ROT, 0, 0, 14), + IIO_CHAN_SOFT_TIMESTAMP(8) +}; + +static const struct iio_info adis16209_info = { + .read_raw = adis16209_read_raw, + .write_raw = adis16209_write_raw, + .update_scan_mode = adis_update_scan_mode, +}; + +static const char * const adis16209_status_error_msgs[] = { + [ADIS16209_STAT_SELFTEST_FAIL_BIT] = "Self test failure", + [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", +}; + +static const struct adis_data adis16209_data = { + .read_delay = 30, + .msc_ctrl_reg = ADIS16209_MSC_CTRL_REG, + .glob_cmd_reg = ADIS16209_CMD_REG, + .diag_stat_reg = ADIS16209_STAT_REG, + + .self_test_mask = ADIS16209_MSC_CTRL_SELF_TEST_EN, + .self_test_no_autoclear = true, + .startup_delay = ADIS16209_STARTUP_DELAY_MS, + + .status_error_msgs = adis16209_status_error_msgs, + .status_error_mask = BIT(ADIS16209_STAT_SELFTEST_FAIL_BIT) | + BIT(ADIS16209_STAT_SPI_FAIL_BIT) | + BIT(ADIS16209_STAT_FLASH_UPT_FAIL_BIT) | + BIT(ADIS16209_STAT_POWER_HIGH_BIT) | + BIT(ADIS16209_STAT_POWER_LOW_BIT), +}; + +static int adis16209_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct adis *st; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + spi_set_drvdata(spi, indio_dev); + + indio_dev->name = spi->dev.driver->name; + indio_dev->dev.parent = &spi->dev; + indio_dev->info = &adis16209_info; + indio_dev->channels = adis16209_channels; + indio_dev->num_channels = ARRAY_SIZE(adis16209_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = adis_init(st, indio_dev, spi, &adis16209_data); + if (ret) + return ret; + + ret = adis_setup_buffer_and_trigger(st, indio_dev, NULL); + if (ret) + return ret; + + ret = adis_initial_startup(st); + if (ret) + goto error_cleanup_buffer_trigger; + ret = iio_device_register(indio_dev); + if (ret) + goto error_cleanup_buffer_trigger; + + return 0; + +error_cleanup_buffer_trigger: + adis_cleanup_buffer_and_trigger(st, indio_dev); + return ret; +} + +static int adis16209_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct adis *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + adis_cleanup_buffer_and_trigger(st, indio_dev); + + return 0; +} + +static struct spi_driver adis16209_driver = { + .driver = { + .name = "adis16209", + }, + .probe = adis16209_probe, + .remove = adis16209_remove, +}; +module_spi_driver(adis16209_driver); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:adis16209"); diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c index 208f2d9f0e8a..383c802eb5b8 100644 --- a/drivers/iio/accel/bmc150-accel-core.c +++ b/drivers/iio/accel/bmc150-accel-core.c @@ -837,29 +837,12 @@ static int bmc150_accel_fifo_transfer(struct bmc150_accel_data *data, int sample_length = 3 * 2; int ret; int total_length = samples * sample_length; - int i; - size_t step = regmap_get_raw_read_max(data->regmap); - - if (!step || step > total_length) - step = total_length; - else if (step < total_length) - step = sample_length; - - /* - * Seems we have a bus with size limitation so we have to execute - * multiple reads - */ - for (i = 0; i < total_length; i += step) { - ret = regmap_raw_read(data->regmap, BMC150_ACCEL_REG_FIFO_DATA, - &buffer[i], step); - if (ret) - break; - } + ret = regmap_raw_read(data->regmap, BMC150_ACCEL_REG_FIFO_DATA, + buffer, total_length); if (ret) dev_err(dev, - "Error transferring data from fifo in single steps of %zu\n", - step); + "Error transferring data from fifo: %d\n", ret); return ret; } diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c index f33dadf7b262..4dceb75e3586 100644 --- a/drivers/iio/accel/sca3000.c +++ b/drivers/iio/accel/sca3000.c @@ -1277,7 +1277,7 @@ static int sca3000_configure_ring(struct iio_dev *indio_dev) { struct iio_buffer *buffer; - buffer = iio_kfifo_allocate(); + buffer = devm_iio_kfifo_allocate(&indio_dev->dev); if (!buffer) return -ENOMEM; @@ -1287,11 +1287,6 @@ static int sca3000_configure_ring(struct iio_dev *indio_dev) return 0; } -static void sca3000_unconfigure_ring(struct iio_dev *indio_dev) -{ - iio_kfifo_free(indio_dev->buffer); -} - static inline int __sca3000_hw_ring_state_set(struct iio_dev *indio_dev, bool state) { @@ -1486,7 +1481,9 @@ static int sca3000_probe(struct spi_device *spi) } indio_dev->modes = INDIO_DIRECT_MODE; - sca3000_configure_ring(indio_dev); + ret = sca3000_configure_ring(indio_dev); + if (ret) + return ret; if (spi->irq) { ret = request_threaded_irq(spi->irq, @@ -1546,8 +1543,6 @@ static int sca3000_remove(struct spi_device *spi) if (spi->irq) free_irq(spi->irq, indio_dev); - sca3000_unconfigure_ring(indio_dev); - return 0; } diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c index 6bdec8c451e0..056dddb27236 100644 --- a/drivers/iio/accel/st_accel_i2c.c +++ b/drivers/iio/accel/st_accel_i2c.c @@ -107,6 +107,7 @@ MODULE_DEVICE_TABLE(of, st_accel_of_match); #ifdef CONFIG_ACPI static const struct acpi_device_id st_accel_acpi_match[] = { + {"SMO8840", LNG2DM}, {"SMO8A90", LNG2DM}, { }, }; diff --git a/drivers/iio/adc/ad7791.c b/drivers/iio/adc/ad7791.c index 70fbf92f9827..a9ff0695ddf7 100644 --- a/drivers/iio/adc/ad7791.c +++ b/drivers/iio/adc/ad7791.c @@ -153,6 +153,17 @@ struct ad7791_state { const struct ad7791_chip_info *info; }; +static const int ad7791_sample_freq_avail[8][2] = { + [AD7791_FILTER_RATE_120] = { 120, 0 }, + [AD7791_FILTER_RATE_100] = { 100, 0 }, + [AD7791_FILTER_RATE_33_3] = { 33, 300000 }, + [AD7791_FILTER_RATE_20] = { 20, 0 }, + [AD7791_FILTER_RATE_16_6] = { 16, 600000 }, + [AD7791_FILTER_RATE_16_7] = { 16, 700000 }, + [AD7791_FILTER_RATE_13_3] = { 13, 300000 }, + [AD7791_FILTER_RATE_9_5] = { 9, 500000 }, +}; + static struct ad7791_state *ad_sigma_delta_to_ad7791(struct ad_sigma_delta *sd) { return container_of(sd, struct ad7791_state, sd); @@ -202,6 +213,7 @@ static int ad7791_read_raw(struct iio_dev *indio_dev, { struct ad7791_state *st = iio_priv(indio_dev); bool unipolar = !!(st->mode & AD7791_MODE_UNIPOLAR); + unsigned int rate; switch (info) { case IIO_CHAN_INFO_RAW: @@ -239,63 +251,56 @@ static int ad7791_read_raw(struct iio_dev *indio_dev, *val2 = chan->scan_type.realbits - 1; return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_SAMP_FREQ: + rate = st->filter & AD7791_FILTER_RATE_MASK; + *val = ad7791_sample_freq_avail[rate][0]; + *val2 = ad7791_sample_freq_avail[rate][1]; + return IIO_VAL_INT_PLUS_MICRO; } return -EINVAL; } -static const char * const ad7791_sample_freq_avail[] = { - [AD7791_FILTER_RATE_120] = "120", - [AD7791_FILTER_RATE_100] = "100", - [AD7791_FILTER_RATE_33_3] = "33.3", - [AD7791_FILTER_RATE_20] = "20", - [AD7791_FILTER_RATE_16_6] = "16.6", - [AD7791_FILTER_RATE_16_7] = "16.7", - [AD7791_FILTER_RATE_13_3] = "13.3", - [AD7791_FILTER_RATE_9_5] = "9.5", -}; - -static ssize_t ad7791_read_frequency(struct device *dev, - struct device_attribute *attr, char *buf) +static int ad7791_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, long mask) { - struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct ad7791_state *st = iio_priv(indio_dev); - unsigned int rate = st->filter & AD7791_FILTER_RATE_MASK; - - return sprintf(buf, "%s\n", ad7791_sample_freq_avail[rate]); -} - -static ssize_t ad7791_write_frequency(struct device *dev, - struct device_attribute *attr, const char *buf, size_t len) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ad7791_state *st = iio_priv(indio_dev); - int i, ret; - - i = sysfs_match_string(ad7791_sample_freq_avail, buf); - if (i < 0) - return i; + int ret, i; ret = iio_device_claim_direct_mode(indio_dev); if (ret) return ret; - st->filter &= ~AD7791_FILTER_RATE_MASK; - st->filter |= i; - ad_sd_write_reg(&st->sd, AD7791_REG_FILTER, sizeof(st->filter), - st->filter); - iio_device_release_direct_mode(indio_dev); - return len; -} + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + for (i = 0; i < ARRAY_SIZE(ad7791_sample_freq_avail); i++) { + if (ad7791_sample_freq_avail[i][0] == val && + ad7791_sample_freq_avail[i][1] == val2) + break; + } + + if (i == ARRAY_SIZE(ad7791_sample_freq_avail)) { + ret = -EINVAL; + break; + } + + st->filter &= ~AD7791_FILTER_RATE_MASK; + st->filter |= i; + ad_sd_write_reg(&st->sd, AD7791_REG_FILTER, + sizeof(st->filter), + st->filter); + break; + default: + ret = -EINVAL; + } -static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, - ad7791_read_frequency, - ad7791_write_frequency); + iio_device_release_direct_mode(indio_dev); + return ret; +} static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("120 100 33.3 20 16.7 16.6 13.3 9.5"); static struct attribute *ad7791_attributes[] = { - &iio_dev_attr_sampling_frequency.dev_attr.attr, &iio_const_attr_sampling_frequency_available.dev_attr.attr, NULL }; @@ -306,12 +311,14 @@ static const struct attribute_group ad7791_attribute_group = { static const struct iio_info ad7791_info = { .read_raw = &ad7791_read_raw, + .write_raw = &ad7791_write_raw, .attrs = &ad7791_attribute_group, .validate_trigger = ad_sd_validate_trigger, }; static const struct iio_info ad7791_no_filter_info = { .read_raw = &ad7791_read_raw, + .write_raw = &ad7791_write_raw, .validate_trigger = ad_sd_validate_trigger, }; diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c index ede955d9b2a4..2948909f3ee3 100644 --- a/drivers/iio/adc/meson_saradc.c +++ b/drivers/iio/adc/meson_saradc.c @@ -219,15 +219,19 @@ enum meson_sar_adc_chan7_mux_sel { CHAN7_MUX_CH7_INPUT = 0x7, }; -struct meson_sar_adc_data { +struct meson_sar_adc_param { bool has_bl30_integration; unsigned long clock_rate; u32 bandgap_reg; unsigned int resolution; - const char *name; const struct regmap_config *regmap_config; }; +struct meson_sar_adc_data { + const struct meson_sar_adc_param *param; + const char *name; +}; + struct meson_sar_adc_priv { struct regmap *regmap; struct regulator *vref; @@ -276,7 +280,7 @@ static int meson_sar_adc_calib_val(struct iio_dev *indio_dev, int val) /* use val_calib = scale * val_raw + offset calibration function */ tmp = div_s64((s64)val * priv->calibscale, MILLION) + priv->calibbias; - return clamp(tmp, 0, (1 << priv->data->resolution) - 1); + return clamp(tmp, 0, (1 << priv->data->param->resolution) - 1); } static int meson_sar_adc_wait_busy_clear(struct iio_dev *indio_dev) @@ -328,7 +332,7 @@ static int meson_sar_adc_read_raw_sample(struct iio_dev *indio_dev, } fifo_val = FIELD_GET(MESON_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK, regval); - fifo_val &= GENMASK(priv->data->resolution - 1, 0); + fifo_val &= GENMASK(priv->data->param->resolution - 1, 0); *val = meson_sar_adc_calib_val(indio_dev, fifo_val); return 0; @@ -447,7 +451,7 @@ static int meson_sar_adc_lock(struct iio_dev *indio_dev) mutex_lock(&indio_dev->mlock); - if (priv->data->has_bl30_integration) { + if (priv->data->param->has_bl30_integration) { /* prevent BL30 from using the SAR ADC while we are using it */ regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY, MESON_SAR_ADC_DELAY_KERNEL_BUSY, @@ -475,7 +479,7 @@ static void meson_sar_adc_unlock(struct iio_dev *indio_dev) { struct meson_sar_adc_priv *priv = iio_priv(indio_dev); - if (priv->data->has_bl30_integration) + if (priv->data->param->has_bl30_integration) /* allow BL30 to use the SAR ADC again */ regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY, MESON_SAR_ADC_DELAY_KERNEL_BUSY, 0); @@ -559,7 +563,7 @@ static int meson_sar_adc_iio_info_read_raw(struct iio_dev *indio_dev, } *val = ret / 1000; - *val2 = priv->data->resolution; + *val2 = priv->data->param->resolution; return IIO_VAL_FRACTIONAL_LOG2; case IIO_CHAN_INFO_CALIBBIAS: @@ -632,7 +636,7 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev) */ meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_CH7_INPUT); - if (priv->data->has_bl30_integration) { + if (priv->data->param->has_bl30_integration) { /* * leave sampling delay and the input clocks as configured by * BL30 to make sure BL30 gets the values it expects when @@ -712,7 +716,7 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev) return ret; } - ret = clk_set_rate(priv->adc_clk, priv->data->clock_rate); + ret = clk_set_rate(priv->adc_clk, priv->data->param->clock_rate); if (ret) { dev_err(indio_dev->dev.parent, "failed to set adc clock rate\n"); @@ -725,14 +729,15 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev) static void meson_sar_adc_set_bandgap(struct iio_dev *indio_dev, bool on_off) { struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + const struct meson_sar_adc_param *param = priv->data->param; u32 enable_mask; - if (priv->data->bandgap_reg == MESON_SAR_ADC_REG11) + if (param->bandgap_reg == MESON_SAR_ADC_REG11) enable_mask = MESON_SAR_ADC_REG11_BANDGAP_EN; else enable_mask = MESON_SAR_ADC_DELTA_10_TS_VBG_EN; - regmap_update_bits(priv->regmap, priv->data->bandgap_reg, enable_mask, + regmap_update_bits(priv->regmap, param->bandgap_reg, enable_mask, on_off ? enable_mask : 0); } @@ -844,8 +849,8 @@ static int meson_sar_adc_calib(struct iio_dev *indio_dev) int ret, nominal0, nominal1, value0, value1; /* use points 25% and 75% for calibration */ - nominal0 = (1 << priv->data->resolution) / 4; - nominal1 = (1 << priv->data->resolution) * 3 / 4; + nominal0 = (1 << priv->data->param->resolution) / 4; + nominal1 = (1 << priv->data->param->resolution) * 3 / 4; meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_DIV4); usleep_range(10, 20); @@ -883,51 +888,60 @@ static const struct iio_info meson_sar_adc_iio_info = { .read_raw = meson_sar_adc_iio_info_read_raw, }; -static const struct meson_sar_adc_data meson_sar_adc_meson8_data = { - .has_bl30_integration = false, - .clock_rate = 1150000, - .bandgap_reg = MESON_SAR_ADC_DELTA_10, - .regmap_config = &meson_sar_adc_regmap_config_meson8, - .resolution = 10, - .name = "meson-meson8-saradc", -}; - -static const struct meson_sar_adc_data meson_sar_adc_meson8b_data = { +static const struct meson_sar_adc_param meson_sar_adc_meson8_param = { .has_bl30_integration = false, .clock_rate = 1150000, .bandgap_reg = MESON_SAR_ADC_DELTA_10, .regmap_config = &meson_sar_adc_regmap_config_meson8, .resolution = 10, - .name = "meson-meson8b-saradc", }; -static const struct meson_sar_adc_data meson_sar_adc_gxbb_data = { +static const struct meson_sar_adc_param meson_sar_adc_gxbb_param = { .has_bl30_integration = true, .clock_rate = 1200000, .bandgap_reg = MESON_SAR_ADC_REG11, .regmap_config = &meson_sar_adc_regmap_config_gxbb, .resolution = 10, - .name = "meson-gxbb-saradc", }; -static const struct meson_sar_adc_data meson_sar_adc_gxl_data = { +static const struct meson_sar_adc_param meson_sar_adc_gxl_param = { .has_bl30_integration = true, .clock_rate = 1200000, .bandgap_reg = MESON_SAR_ADC_REG11, .regmap_config = &meson_sar_adc_regmap_config_gxbb, .resolution = 12, +}; + +static const struct meson_sar_adc_data meson_sar_adc_meson8_data = { + .param = &meson_sar_adc_meson8_param, + .name = "meson-meson8-saradc", +}; + +static const struct meson_sar_adc_data meson_sar_adc_meson8b_data = { + .param = &meson_sar_adc_meson8_param, + .name = "meson-meson8b-saradc", +}; + +static const struct meson_sar_adc_data meson_sar_adc_gxbb_data = { + .param = &meson_sar_adc_gxbb_param, + .name = "meson-gxbb-saradc", +}; + +static const struct meson_sar_adc_data meson_sar_adc_gxl_data = { + .param = &meson_sar_adc_gxl_param, .name = "meson-gxl-saradc", }; static const struct meson_sar_adc_data meson_sar_adc_gxm_data = { - .has_bl30_integration = true, - .clock_rate = 1200000, - .bandgap_reg = MESON_SAR_ADC_REG11, - .regmap_config = &meson_sar_adc_regmap_config_gxbb, - .resolution = 12, + .param = &meson_sar_adc_gxl_param, .name = "meson-gxm-saradc", }; +static const struct meson_sar_adc_data meson_sar_adc_axg_data = { + .param = &meson_sar_adc_gxl_param, + .name = "meson-axg-saradc", +}; + static const struct of_device_id meson_sar_adc_of_match[] = { { .compatible = "amlogic,meson8-saradc", @@ -946,6 +960,9 @@ static const struct of_device_id meson_sar_adc_of_match[] = { }, { .compatible = "amlogic,meson-gxm-saradc", .data = &meson_sar_adc_gxm_data, + }, { + .compatible = "amlogic,meson-axg-saradc", + .data = &meson_sar_adc_axg_data, }, {}, }; @@ -1001,7 +1018,7 @@ static int meson_sar_adc_probe(struct platform_device *pdev) return ret; priv->regmap = devm_regmap_init_mmio(&pdev->dev, base, - priv->data->regmap_config); + priv->data->param->regmap_config); if (IS_ERR(priv->regmap)) return PTR_ERR(priv->regmap); diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c index 40be7d9fadbf..ca432e7b6ff1 100644 --- a/drivers/iio/adc/stm32-adc-core.c +++ b/drivers/iio/adc/stm32-adc-core.c @@ -34,9 +34,6 @@ #define STM32F4_ADC_ADCPRE_SHIFT 16 #define STM32F4_ADC_ADCPRE_MASK GENMASK(17, 16) -/* STM32 F4 maximum analog clock rate (from datasheet) */ -#define STM32F4_ADC_MAX_CLK_RATE 36000000 - /* STM32H7 - common registers for all ADC instances */ #define STM32H7_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00) #define STM32H7_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x08) @@ -51,9 +48,6 @@ #define STM32H7_CKMODE_SHIFT 16 #define STM32H7_CKMODE_MASK GENMASK(17, 16) -/* STM32 H7 maximum analog clock rate (from datasheet) */ -#define STM32H7_ADC_MAX_CLK_RATE 36000000 - /** * stm32_adc_common_regs - stm32 common registers, compatible dependent data * @csr: common status register offset @@ -74,15 +68,17 @@ struct stm32_adc_priv; * stm32_adc_priv_cfg - stm32 core compatible configuration data * @regs: common registers for all instances * @clk_sel: clock selection routine + * @max_clk_rate_hz: maximum analog clock rate (Hz, from datasheet) */ struct stm32_adc_priv_cfg { const struct stm32_adc_common_regs *regs; int (*clk_sel)(struct platform_device *, struct stm32_adc_priv *); + u32 max_clk_rate_hz; }; /** * struct stm32_adc_priv - stm32 ADC core private data - * @irq: irq for ADC block + * @irq: irq(s) for ADC block * @domain: irq domain reference * @aclk: clock reference for the analog circuitry * @bclk: bus clock common for all ADCs, depends on part used @@ -91,7 +87,7 @@ struct stm32_adc_priv_cfg { * @common: common data for all ADC instances */ struct stm32_adc_priv { - int irq; + int irq[STM32_ADC_MAX_ADCS]; struct irq_domain *domain; struct clk *aclk; struct clk *bclk; @@ -133,7 +129,7 @@ static int stm32f4_adc_clk_sel(struct platform_device *pdev, } for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) { - if ((rate / stm32f4_pclk_div[i]) <= STM32F4_ADC_MAX_CLK_RATE) + if ((rate / stm32f4_pclk_div[i]) <= priv->cfg->max_clk_rate_hz) break; } if (i >= ARRAY_SIZE(stm32f4_pclk_div)) { @@ -222,7 +218,7 @@ static int stm32h7_adc_clk_sel(struct platform_device *pdev, if (ckmode) continue; - if ((rate / div) <= STM32H7_ADC_MAX_CLK_RATE) + if ((rate / div) <= priv->cfg->max_clk_rate_hz) goto out; } } @@ -242,7 +238,7 @@ static int stm32h7_adc_clk_sel(struct platform_device *pdev, if (!ckmode) continue; - if ((rate / div) <= STM32H7_ADC_MAX_CLK_RATE) + if ((rate / div) <= priv->cfg->max_clk_rate_hz) goto out; } @@ -328,11 +324,24 @@ static int stm32_adc_irq_probe(struct platform_device *pdev, struct stm32_adc_priv *priv) { struct device_node *np = pdev->dev.of_node; + unsigned int i; + + for (i = 0; i < STM32_ADC_MAX_ADCS; i++) { + priv->irq[i] = platform_get_irq(pdev, i); + if (priv->irq[i] < 0) { + /* + * At least one interrupt must be provided, make others + * optional: + * - stm32f4/h7 shares a common interrupt. + * - stm32mp1, has one line per ADC (either for ADC1, + * ADC2 or both). + */ + if (i && priv->irq[i] == -ENXIO) + continue; + dev_err(&pdev->dev, "failed to get irq\n"); - priv->irq = platform_get_irq(pdev, 0); - if (priv->irq < 0) { - dev_err(&pdev->dev, "failed to get irq\n"); - return priv->irq; + return priv->irq[i]; + } } priv->domain = irq_domain_add_simple(np, STM32_ADC_MAX_ADCS, 0, @@ -343,8 +352,12 @@ static int stm32_adc_irq_probe(struct platform_device *pdev, return -ENOMEM; } - irq_set_chained_handler(priv->irq, stm32_adc_irq_handler); - irq_set_handler_data(priv->irq, priv); + for (i = 0; i < STM32_ADC_MAX_ADCS; i++) { + if (priv->irq[i] < 0) + continue; + irq_set_chained_handler(priv->irq[i], stm32_adc_irq_handler); + irq_set_handler_data(priv->irq[i], priv); + } return 0; } @@ -353,11 +366,17 @@ static void stm32_adc_irq_remove(struct platform_device *pdev, struct stm32_adc_priv *priv) { int hwirq; + unsigned int i; for (hwirq = 0; hwirq < STM32_ADC_MAX_ADCS; hwirq++) irq_dispose_mapping(irq_find_mapping(priv->domain, hwirq)); irq_domain_remove(priv->domain); - irq_set_chained_handler(priv->irq, NULL); + + for (i = 0; i < STM32_ADC_MAX_ADCS; i++) { + if (priv->irq[i] < 0) + continue; + irq_set_chained_handler(priv->irq[i], NULL); + } } static int stm32_adc_probe(struct platform_device *pdev) @@ -497,11 +516,19 @@ static int stm32_adc_remove(struct platform_device *pdev) static const struct stm32_adc_priv_cfg stm32f4_adc_priv_cfg = { .regs = &stm32f4_adc_common_regs, .clk_sel = stm32f4_adc_clk_sel, + .max_clk_rate_hz = 36000000, }; static const struct stm32_adc_priv_cfg stm32h7_adc_priv_cfg = { .regs = &stm32h7_adc_common_regs, .clk_sel = stm32h7_adc_clk_sel, + .max_clk_rate_hz = 36000000, +}; + +static const struct stm32_adc_priv_cfg stm32mp1_adc_priv_cfg = { + .regs = &stm32h7_adc_common_regs, + .clk_sel = stm32h7_adc_clk_sel, + .max_clk_rate_hz = 40000000, }; static const struct of_device_id stm32_adc_of_match[] = { @@ -512,6 +539,9 @@ static const struct of_device_id stm32_adc_of_match[] = { .compatible = "st,stm32h7-adc-core", .data = (void *)&stm32h7_adc_priv_cfg }, { + .compatible = "st,stm32mp1-adc-core", + .data = (void *)&stm32mp1_adc_priv_cfg + }, { }, }; MODULE_DEVICE_TABLE(of, stm32_adc_of_match); diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 9a2583caedaa..378411853d75 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -84,6 +84,7 @@ #define STM32H7_ADC_CALFACT2 0xC8 /* STM32H7_ADC_ISR - bit fields */ +#define STM32MP1_VREGREADY BIT(12) #define STM32H7_EOC BIT(2) #define STM32H7_ADRDY BIT(0) @@ -249,6 +250,7 @@ struct stm32_adc; * @adc_info: per instance input channels definitions * @trigs: external trigger sources * @clk_required: clock is required + * @has_vregready: vregready status flag presence * @selfcalib: optional routine for self-calibration * @prepare: optional prepare routine (power-up, enable) * @start_conv: routine to start conversions @@ -261,6 +263,7 @@ struct stm32_adc_cfg { const struct stm32_adc_info *adc_info; struct stm32_adc_trig_info *trigs; bool clk_required; + bool has_vregready; int (*selfcalib)(struct stm32_adc *); int (*prepare)(struct stm32_adc *); void (*start_conv)(struct stm32_adc *, bool dma); @@ -695,8 +698,12 @@ static void stm32h7_adc_stop_conv(struct stm32_adc *adc) stm32_adc_clr_bits(adc, STM32H7_ADC_CFGR, STM32H7_DMNGT_MASK); } -static void stm32h7_adc_exit_pwr_down(struct stm32_adc *adc) +static int stm32h7_adc_exit_pwr_down(struct stm32_adc *adc) { + struct iio_dev *indio_dev = iio_priv_to_dev(adc); + int ret; + u32 val; + /* Exit deep power down, then enable ADC voltage regulator */ stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_DEEPPWD); stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADVREGEN); @@ -705,7 +712,20 @@ static void stm32h7_adc_exit_pwr_down(struct stm32_adc *adc) stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_BOOST); /* Wait for startup time */ - usleep_range(10, 20); + if (!adc->cfg->has_vregready) { + usleep_range(10, 20); + return 0; + } + + ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_ISR, val, + val & STM32MP1_VREGREADY, 100, + STM32_ADC_TIMEOUT_US); + if (ret) { + stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_DEEPPWD); + dev_err(&indio_dev->dev, "Failed to exit power down\n"); + } + + return ret; } static void stm32h7_adc_enter_pwr_down(struct stm32_adc *adc) @@ -888,7 +908,9 @@ static int stm32h7_adc_selfcalib(struct stm32_adc *adc) int ret; u32 val; - stm32h7_adc_exit_pwr_down(adc); + ret = stm32h7_adc_exit_pwr_down(adc); + if (ret) + return ret; /* * Select calibration mode: @@ -952,7 +974,10 @@ static int stm32h7_adc_prepare(struct stm32_adc *adc) { int ret; - stm32h7_adc_exit_pwr_down(adc); + ret = stm32h7_adc_exit_pwr_down(adc); + if (ret) + return ret; + stm32_adc_writel(adc, STM32H7_ADC_DIFSEL, adc->difsel); ret = stm32h7_adc_enable(adc); @@ -1944,9 +1969,23 @@ static const struct stm32_adc_cfg stm32h7_adc_cfg = { .smp_cycles = stm32h7_adc_smp_cycles, }; +static const struct stm32_adc_cfg stm32mp1_adc_cfg = { + .regs = &stm32h7_adc_regspec, + .adc_info = &stm32h7_adc_info, + .trigs = stm32h7_adc_trigs, + .has_vregready = true, + .selfcalib = stm32h7_adc_selfcalib, + .start_conv = stm32h7_adc_start_conv, + .stop_conv = stm32h7_adc_stop_conv, + .prepare = stm32h7_adc_prepare, + .unprepare = stm32h7_adc_unprepare, + .smp_cycles = stm32h7_adc_smp_cycles, +}; + static const struct of_device_id stm32_adc_of_match[] = { { .compatible = "st,stm32f4-adc", .data = (void *)&stm32f4_adc_cfg }, { .compatible = "st,stm32h7-adc", .data = (void *)&stm32h7_adc_cfg }, + { .compatible = "st,stm32mp1-adc", .data = (void *)&stm32mp1_adc_cfg }, {}, }; MODULE_DEVICE_TABLE(of, stm32_adc_of_match); diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c index 01422d11753c..1b78becaba5d 100644 --- a/drivers/iio/adc/stm32-dfsdm-adc.c +++ b/drivers/iio/adc/stm32-dfsdm-adc.c @@ -253,7 +253,8 @@ static int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, DFSDM_CR1_RSWSTART(1)); } -static void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id) +static void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, + unsigned int fl_id) { /* Disable conversion */ regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id), @@ -337,7 +338,7 @@ static int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm, "st,adc-channel-types", chan_idx, &of_str); if (!ret) { - val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_type); + val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_type); if (val < 0) return val; } else { @@ -349,7 +350,7 @@ static int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm, "st,adc-channel-clk-src", chan_idx, &of_str); if (!ret) { - val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_src); + val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_src); if (val < 0) return val; } else { @@ -1093,7 +1094,6 @@ static int stm32_dfsdm_adc_probe(struct platform_device *pdev) char *name; int ret, irq, val; - dev_data = of_device_get_match_data(dev); iio = devm_iio_device_alloc(dev, sizeof(*adc)); if (!iio) { @@ -1111,8 +1111,8 @@ static int stm32_dfsdm_adc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, adc); ret = of_property_read_u32(dev->of_node, "reg", &adc->fl_id); - if (ret != 0) { - dev_err(dev, "Missing reg property\n"); + if (ret != 0 || adc->fl_id >= adc->dfsdm->num_fls) { + dev_err(dev, "Missing or bad reg property\n"); return -EINVAL; } @@ -1161,7 +1161,6 @@ static int stm32_dfsdm_adc_probe(struct platform_device *pdev) if (ret < 0) goto err_cleanup; - dev_err(dev, "of_platform_populate\n"); if (dev_data->type == DFSDM_AUDIO) { ret = of_platform_populate(np, NULL, NULL, dev); if (ret < 0) { diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c index e50efdcc41ff..1d0d8238d9b5 100644 --- a/drivers/iio/adc/stm32-dfsdm-core.c +++ b/drivers/iio/adc/stm32-dfsdm-core.c @@ -227,6 +227,11 @@ static int stm32_dfsdm_parse_of(struct platform_device *pdev, } priv->spi_clk_out_div = div_u64_rem(clk_freq, spi_freq, &rem) - 1; + if (!priv->spi_clk_out_div) { + /* spi_clk_out_div == 0 means ckout is OFF */ + dev_err(&pdev->dev, "spi-max-frequency not achievable\n"); + return -EINVAL; + } priv->dfsdm.spi_master_freq = spi_freq; if (rem) { diff --git a/drivers/iio/adc/stx104.c b/drivers/iio/adc/stx104.c index 17b021f33180..0662ca199eb0 100644 --- a/drivers/iio/adc/stx104.c +++ b/drivers/iio/adc/stx104.c @@ -233,6 +233,16 @@ static int stx104_gpio_get(struct gpio_chip *chip, unsigned int offset) return !!(inb(stx104gpio->base) & BIT(offset)); } +static int stx104_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask, + unsigned long *bits) +{ + struct stx104_gpio *const stx104gpio = gpiochip_get_data(chip); + + *bits = inb(stx104gpio->base); + + return 0; +} + static void stx104_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { @@ -342,6 +352,7 @@ static int stx104_probe(struct device *dev, unsigned int id) stx104gpio->chip.direction_input = stx104_gpio_direction_input; stx104gpio->chip.direction_output = stx104_gpio_direction_output; stx104gpio->chip.get = stx104_gpio_get; + stx104gpio->chip.get_multiple = stx104_gpio_get_multiple; stx104gpio->chip.set = stx104_gpio_set; stx104gpio->chip.set_multiple = stx104_gpio_set_multiple; stx104gpio->base = base[id] + 3; diff --git a/drivers/iio/afe/Kconfig b/drivers/iio/afe/Kconfig new file mode 100644 index 000000000000..c91eef04825a --- /dev/null +++ b/drivers/iio/afe/Kconfig @@ -0,0 +1,19 @@ +# +# Analog Front End drivers +# +# When adding new entries keep the list in alphabetical order + +menu "Analog Front Ends" + +config IIO_RESCALE + tristate "IIO rescale" + depends on OF || COMPILE_TEST + help + Say yes here to build support for the IIO rescaling + that handles voltage dividers, current sense shunts and + current sense amplifiers. + + To compile this driver as a module, choose M here: the + module will be called iio-rescale. + +endmenu diff --git a/drivers/iio/afe/Makefile b/drivers/iio/afe/Makefile new file mode 100644 index 000000000000..5fabb7bcac47 --- /dev/null +++ b/drivers/iio/afe/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for industrial I/O Analog Front Ends (AFE) +# + +# When adding new entries keep the list in alphabetical order +obj-$(CONFIG_IIO_RESCALE) += iio-rescale.o diff --git a/drivers/iio/afe/iio-rescale.c b/drivers/iio/afe/iio-rescale.c new file mode 100644 index 000000000000..e9ceee66d1e7 --- /dev/null +++ b/drivers/iio/afe/iio-rescale.c @@ -0,0 +1,359 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IIO rescale driver + * + * Copyright (C) 2018 Axentia Technologies AB + * + * Author: Peter Rosin <peda@axentia.se> + */ + +#include <linux/err.h> +#include <linux/gcd.h> +#include <linux/iio/consumer.h> +#include <linux/iio/iio.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/property.h> + +struct rescale; + +struct rescale_cfg { + enum iio_chan_type type; + int (*props)(struct device *dev, struct rescale *rescale); +}; + +struct rescale { + const struct rescale_cfg *cfg; + struct iio_channel *source; + struct iio_chan_spec chan; + struct iio_chan_spec_ext_info *ext_info; + s32 numerator; + s32 denominator; +}; + +static int rescale_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct rescale *rescale = iio_priv(indio_dev); + unsigned long long tmp; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + return iio_read_channel_raw(rescale->source, val); + + case IIO_CHAN_INFO_SCALE: + ret = iio_read_channel_scale(rescale->source, val, val2); + switch (ret) { + case IIO_VAL_FRACTIONAL: + *val *= rescale->numerator; + *val2 *= rescale->denominator; + return ret; + case IIO_VAL_INT: + *val *= rescale->numerator; + if (rescale->denominator == 1) + return ret; + *val2 = rescale->denominator; + return IIO_VAL_FRACTIONAL; + case IIO_VAL_FRACTIONAL_LOG2: + tmp = *val * 1000000000LL; + do_div(tmp, rescale->denominator); + tmp *= rescale->numerator; + do_div(tmp, 1000000000LL); + *val = tmp; + return ret; + default: + return -EOPNOTSUPP; + } + default: + return -EINVAL; + } +} + +static int rescale_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct rescale *rescale = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + *type = IIO_VAL_INT; + return iio_read_avail_channel_raw(rescale->source, + vals, length); + default: + return -EINVAL; + } +} + +static const struct iio_info rescale_info = { + .read_raw = rescale_read_raw, + .read_avail = rescale_read_avail, +}; + +static ssize_t rescale_read_ext_info(struct iio_dev *indio_dev, + uintptr_t private, + struct iio_chan_spec const *chan, + char *buf) +{ + struct rescale *rescale = iio_priv(indio_dev); + + return iio_read_channel_ext_info(rescale->source, + rescale->ext_info[private].name, + buf); +} + +static ssize_t rescale_write_ext_info(struct iio_dev *indio_dev, + uintptr_t private, + struct iio_chan_spec const *chan, + const char *buf, size_t len) +{ + struct rescale *rescale = iio_priv(indio_dev); + + return iio_write_channel_ext_info(rescale->source, + rescale->ext_info[private].name, + buf, len); +} + +static int rescale_configure_channel(struct device *dev, + struct rescale *rescale) +{ + struct iio_chan_spec *chan = &rescale->chan; + struct iio_chan_spec const *schan = rescale->source->channel; + + chan->indexed = 1; + chan->output = schan->output; + chan->ext_info = rescale->ext_info; + chan->type = rescale->cfg->type; + + if (!iio_channel_has_info(schan, IIO_CHAN_INFO_RAW) || + !iio_channel_has_info(schan, IIO_CHAN_INFO_SCALE)) { + dev_err(dev, "source channel does not support raw/scale\n"); + return -EINVAL; + } + + chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE); + + if (iio_channel_has_available(schan, IIO_CHAN_INFO_RAW)) + chan->info_mask_separate_available |= BIT(IIO_CHAN_INFO_RAW); + + return 0; +} + +static int rescale_current_sense_amplifier_props(struct device *dev, + struct rescale *rescale) +{ + u32 sense; + u32 gain_mult = 1; + u32 gain_div = 1; + u32 factor; + int ret; + + ret = device_property_read_u32(dev, "sense-resistor-micro-ohms", + &sense); + if (ret) { + dev_err(dev, "failed to read the sense resistance: %d\n", ret); + return ret; + } + + device_property_read_u32(dev, "sense-gain-mult", &gain_mult); + device_property_read_u32(dev, "sense-gain-div", &gain_div); + + /* + * Calculate the scaling factor, 1 / (gain * sense), or + * gain_div / (gain_mult * sense), while trying to keep the + * numerator/denominator from overflowing. + */ + factor = gcd(sense, 1000000); + rescale->numerator = 1000000 / factor; + rescale->denominator = sense / factor; + + factor = gcd(rescale->numerator, gain_mult); + rescale->numerator /= factor; + rescale->denominator *= gain_mult / factor; + + factor = gcd(rescale->denominator, gain_div); + rescale->numerator *= gain_div / factor; + rescale->denominator /= factor; + + return 0; +} + +static int rescale_current_sense_shunt_props(struct device *dev, + struct rescale *rescale) +{ + u32 shunt; + u32 factor; + int ret; + + ret = device_property_read_u32(dev, "shunt-resistor-micro-ohms", + &shunt); + if (ret) { + dev_err(dev, "failed to read the shunt resistance: %d\n", ret); + return ret; + } + + factor = gcd(shunt, 1000000); + rescale->numerator = 1000000 / factor; + rescale->denominator = shunt / factor; + + return 0; +} + +static int rescale_voltage_divider_props(struct device *dev, + struct rescale *rescale) +{ + int ret; + u32 factor; + + ret = device_property_read_u32(dev, "output-ohms", + &rescale->denominator); + if (ret) { + dev_err(dev, "failed to read output-ohms: %d\n", ret); + return ret; + } + + ret = device_property_read_u32(dev, "full-ohms", + &rescale->numerator); + if (ret) { + dev_err(dev, "failed to read full-ohms: %d\n", ret); + return ret; + } + + factor = gcd(rescale->numerator, rescale->denominator); + rescale->numerator /= factor; + rescale->denominator /= factor; + + return 0; +} + +enum rescale_variant { + CURRENT_SENSE_AMPLIFIER, + CURRENT_SENSE_SHUNT, + VOLTAGE_DIVIDER, +}; + +static const struct rescale_cfg rescale_cfg[] = { + [CURRENT_SENSE_AMPLIFIER] = { + .type = IIO_CURRENT, + .props = rescale_current_sense_amplifier_props, + }, + [CURRENT_SENSE_SHUNT] = { + .type = IIO_CURRENT, + .props = rescale_current_sense_shunt_props, + }, + [VOLTAGE_DIVIDER] = { + .type = IIO_VOLTAGE, + .props = rescale_voltage_divider_props, + }, +}; + +static const struct of_device_id rescale_match[] = { + { .compatible = "current-sense-amplifier", + .data = &rescale_cfg[CURRENT_SENSE_AMPLIFIER], }, + { .compatible = "current-sense-shunt", + .data = &rescale_cfg[CURRENT_SENSE_SHUNT], }, + { .compatible = "voltage-divider", + .data = &rescale_cfg[VOLTAGE_DIVIDER], }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rescale_match); + +static int rescale_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct iio_channel *source; + struct rescale *rescale; + int sizeof_ext_info; + int sizeof_priv; + int i; + int ret; + + source = devm_iio_channel_get(dev, NULL); + if (IS_ERR(source)) { + if (PTR_ERR(source) != -EPROBE_DEFER) + dev_err(dev, "failed to get source channel\n"); + return PTR_ERR(source); + } + + sizeof_ext_info = iio_get_channel_ext_info_count(source); + if (sizeof_ext_info) { + sizeof_ext_info += 1; /* one extra entry for the sentinel */ + sizeof_ext_info *= sizeof(*rescale->ext_info); + } + + sizeof_priv = sizeof(*rescale) + sizeof_ext_info; + + indio_dev = devm_iio_device_alloc(dev, sizeof_priv); + if (!indio_dev) + return -ENOMEM; + + rescale = iio_priv(indio_dev); + + rescale->cfg = of_device_get_match_data(dev); + rescale->numerator = 1; + rescale->denominator = 1; + + ret = rescale->cfg->props(dev, rescale); + if (ret) + return ret; + + if (!rescale->numerator || !rescale->denominator) { + dev_err(dev, "invalid scaling factor.\n"); + return -EINVAL; + } + + platform_set_drvdata(pdev, indio_dev); + + rescale->source = source; + + indio_dev->name = dev_name(dev); + indio_dev->dev.parent = dev; + indio_dev->info = &rescale_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = &rescale->chan; + indio_dev->num_channels = 1; + if (sizeof_ext_info) { + rescale->ext_info = devm_kmemdup(dev, + source->channel->ext_info, + sizeof_ext_info, GFP_KERNEL); + if (!rescale->ext_info) + return -ENOMEM; + + for (i = 0; rescale->ext_info[i].name; ++i) { + struct iio_chan_spec_ext_info *ext_info = + &rescale->ext_info[i]; + + if (source->channel->ext_info[i].read) + ext_info->read = rescale_read_ext_info; + if (source->channel->ext_info[i].write) + ext_info->write = rescale_write_ext_info; + ext_info->private = i; + } + } + + ret = rescale_configure_channel(dev, rescale); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static struct platform_driver rescale_driver = { + .probe = rescale_probe, + .driver = { + .name = "iio-rescale", + .of_match_table = rescale_match, + }, +}; +module_platform_driver(rescale_driver); + +MODULE_DESCRIPTION("IIO rescale driver"); +MODULE_AUTHOR("Peter Rosin <peda@axentia.se>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/chemical/atlas-ph-sensor.c b/drivers/iio/chemical/atlas-ph-sensor.c index abfc4bbc4cfc..a406ad31b096 100644 --- a/drivers/iio/chemical/atlas-ph-sensor.c +++ b/drivers/iio/chemical/atlas-ph-sensor.c @@ -61,9 +61,9 @@ #define ATLAS_REG_ORP_CALIB_STATUS 0x0d #define ATLAS_REG_ORP_DATA 0x0e -#define ATLAS_PH_INT_TIME_IN_US 450000 -#define ATLAS_EC_INT_TIME_IN_US 650000 -#define ATLAS_ORP_INT_TIME_IN_US 450000 +#define ATLAS_PH_INT_TIME_IN_MS 450 +#define ATLAS_EC_INT_TIME_IN_MS 650 +#define ATLAS_ORP_INT_TIME_IN_MS 450 enum { ATLAS_PH_SM, @@ -270,21 +270,21 @@ static struct atlas_device atlas_devices[] = { .num_channels = 3, .data_reg = ATLAS_REG_PH_DATA, .calibration = &atlas_check_ph_calibration, - .delay = ATLAS_PH_INT_TIME_IN_US, + .delay = ATLAS_PH_INT_TIME_IN_MS, }, [ATLAS_EC_SM] = { .channels = atlas_ec_channels, .num_channels = 5, .data_reg = ATLAS_REG_EC_DATA, .calibration = &atlas_check_ec_calibration, - .delay = ATLAS_EC_INT_TIME_IN_US, + .delay = ATLAS_EC_INT_TIME_IN_MS, }, [ATLAS_ORP_SM] = { .channels = atlas_orp_channels, .num_channels = 2, .data_reg = ATLAS_REG_ORP_DATA, .calibration = &atlas_check_orp_calibration, - .delay = ATLAS_ORP_INT_TIME_IN_US, + .delay = ATLAS_ORP_INT_TIME_IN_MS, }, }; @@ -393,7 +393,7 @@ static int atlas_read_measurement(struct atlas_data *data, int reg, __be32 *val) } if (suspended) - usleep_range(data->chip->delay, data->chip->delay + 100000); + msleep(data->chip->delay); ret = regmap_bulk_read(data->regmap, reg, (u8 *) val, sizeof(*val)); diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c index 705cb3e72663..89cb0066a6e0 100644 --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c @@ -19,6 +19,7 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/iio/buffer.h> +#include <linux/iio/common/cros_ec_sensors_core.h> #include <linux/iio/iio.h> #include <linux/iio/kfifo_buf.h> #include <linux/iio/trigger_consumer.h> @@ -31,8 +32,6 @@ #include <linux/slab.h> #include <linux/sysfs.h> -#include "cros_ec_sensors_core.h" - #define CROS_EC_SENSORS_MAX_CHANNELS 4 /* State data for ec_sensors iio driver. */ diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c index a620eb5ce202..414cc43c287e 100644 --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c @@ -16,6 +16,7 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/iio/buffer.h> +#include <linux/iio/common/cros_ec_sensors_core.h> #include <linux/iio/iio.h> #include <linux/iio/kfifo_buf.h> #include <linux/iio/trigger_consumer.h> @@ -27,8 +28,6 @@ #include <linux/sysfs.h> #include <linux/platform_device.h> -#include "cros_ec_sensors_core.h" - static char *cros_ec_loc[] = { [MOTIONSENSE_LOC_BASE] = "base", [MOTIONSENSE_LOC_LID] = "lid", @@ -448,8 +447,7 @@ EXPORT_SYMBOL_GPL(cros_ec_sensors_core_write); static int __maybe_unused cros_ec_sensors_prepare(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct iio_dev *indio_dev = dev_get_drvdata(dev); struct cros_ec_sensors_core_state *st = iio_priv(indio_dev); if (st->curr_sampl_freq == 0) @@ -471,8 +469,7 @@ static int __maybe_unused cros_ec_sensors_prepare(struct device *dev) static void __maybe_unused cros_ec_sensors_complete(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct iio_dev *indio_dev = dev_get_drvdata(dev); struct cros_ec_sensors_core_state *st = iio_priv(indio_dev); if (st->curr_sampl_freq == 0) diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h deleted file mode 100644 index 2edf68dc7336..000000000000 --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h +++ /dev/null @@ -1,177 +0,0 @@ -/* - * ChromeOS EC sensor hub - * - * Copyright (C) 2016 Google, Inc - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef __CROS_EC_SENSORS_CORE_H -#define __CROS_EC_SENSORS_CORE_H - -#include <linux/irqreturn.h> - -enum { - CROS_EC_SENSOR_X, - CROS_EC_SENSOR_Y, - CROS_EC_SENSOR_Z, - CROS_EC_SENSOR_MAX_AXIS, -}; - -/* EC returns sensor values using signed 16 bit registers */ -#define CROS_EC_SENSOR_BITS 16 - -/* - * 4 16 bit channels are allowed. - * Good enough for current sensors, they use up to 3 16 bit vectors. - */ -#define CROS_EC_SAMPLE_SIZE (sizeof(s64) * 2) - -/* Minimum sampling period to use when device is suspending */ -#define CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY 1000 /* 1 second */ - -/** - * struct cros_ec_sensors_core_state - state data for EC sensors IIO driver - * @ec: cros EC device structure - * @cmd_lock: lock used to prevent simultaneous access to the - * commands. - * @msg: cros EC command structure - * @param: motion sensor parameters structure - * @resp: motion sensor response structure - * @type: type of motion sensor - * @loc: location where the motion sensor is placed - * @calib: calibration parameters. Note that trigger - * captured data will always provide the calibrated - * data - * @samples: static array to hold data from a single capture. - * For each channel we need 2 bytes, except for - * the timestamp. The timestamp is always last and - * is always 8-byte aligned. - * @read_ec_sensors_data: function used for accessing sensors values - * @cuur_sampl_freq: current sampling period - */ -struct cros_ec_sensors_core_state { - struct cros_ec_device *ec; - struct mutex cmd_lock; - - struct cros_ec_command *msg; - struct ec_params_motion_sense param; - struct ec_response_motion_sense *resp; - - enum motionsensor_type type; - enum motionsensor_location loc; - - s16 calib[CROS_EC_SENSOR_MAX_AXIS]; - - u8 samples[CROS_EC_SAMPLE_SIZE]; - - int (*read_ec_sensors_data)(struct iio_dev *indio_dev, - unsigned long scan_mask, s16 *data); - - int curr_sampl_freq; -}; - -/** - * cros_ec_sensors_read_lpc() - retrieve data from EC shared memory - * @indio_dev: pointer to IIO device - * @scan_mask: bitmap of the sensor indices to scan - * @data: location to store data - * - * This is the safe function for reading the EC data. It guarantees that the - * data sampled was not modified by the EC while being read. - * - * Return: 0 on success, -errno on failure. - */ -int cros_ec_sensors_read_lpc(struct iio_dev *indio_dev, unsigned long scan_mask, - s16 *data); - -/** - * cros_ec_sensors_read_cmd() - retrieve data using the EC command protocol - * @indio_dev: pointer to IIO device - * @scan_mask: bitmap of the sensor indices to scan - * @data: location to store data - * - * Return: 0 on success, -errno on failure. - */ -int cros_ec_sensors_read_cmd(struct iio_dev *indio_dev, unsigned long scan_mask, - s16 *data); - -/** - * cros_ec_sensors_core_init() - basic initialization of the core structure - * @pdev: platform device created for the sensors - * @indio_dev: iio device structure of the device - * @physical_device: true if the device refers to a physical device - * - * Return: 0 on success, -errno on failure. - */ -int cros_ec_sensors_core_init(struct platform_device *pdev, - struct iio_dev *indio_dev, bool physical_device); - -/** - * cros_ec_sensors_capture() - the trigger handler function - * @irq: the interrupt number. - * @p: a pointer to the poll function. - * - * On a trigger event occurring, if the pollfunc is attached then this - * handler is called as a threaded interrupt (and hence may sleep). It - * is responsible for grabbing data from the device and pushing it into - * the associated buffer. - * - * Return: IRQ_HANDLED - */ -irqreturn_t cros_ec_sensors_capture(int irq, void *p); - -/** - * cros_ec_motion_send_host_cmd() - send motion sense host command - * @st: pointer to state information for device - * @opt_length: optional length to reduce the response size, useful on the data - * path. Otherwise, the maximal allowed response size is used - * - * When called, the sub-command is assumed to be set in param->cmd. - * - * Return: 0 on success, -errno on failure. - */ -int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *st, - u16 opt_length); - -/** - * cros_ec_sensors_core_read() - function to request a value from the sensor - * @st: pointer to state information for device - * @chan: channel specification structure table - * @val: will contain one element making up the returned value - * @val2: will contain another element making up the returned value - * @mask: specifies which values to be requested - * - * Return: the type of value returned by the device - */ -int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st, - struct iio_chan_spec const *chan, - int *val, int *val2, long mask); - -/** - * cros_ec_sensors_core_write() - function to write a value to the sensor - * @st: pointer to state information for device - * @chan: channel specification structure table - * @val: first part of value to write - * @val2: second part of value to write - * @mask: specifies which values to write - * - * Return: the type of value returned by the device - */ -int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st, - struct iio_chan_spec const *chan, - int val, int val2, long mask); - -extern const struct dev_pm_ops cros_ec_sensors_pm_ops; - -/* List of extended channel specification for all sensors */ -extern const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[]; - -#endif /* __CROS_EC_SENSORS_CORE_H */ diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c index cfb6588565ba..2ce0efd98cc0 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c @@ -304,8 +304,7 @@ EXPORT_SYMBOL(hid_sensor_setup_trigger); static int __maybe_unused hid_sensor_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct iio_dev *indio_dev = dev_get_drvdata(dev); struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev); return _hid_sensor_power_state(attrb, false); @@ -313,8 +312,7 @@ static int __maybe_unused hid_sensor_suspend(struct device *dev) static int __maybe_unused hid_sensor_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct iio_dev *indio_dev = dev_get_drvdata(dev); struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev); schedule_work(&attrb->work); return 0; @@ -322,8 +320,7 @@ static int __maybe_unused hid_sensor_resume(struct device *dev) static int __maybe_unused hid_sensor_runtime_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct iio_dev *indio_dev = dev_get_drvdata(dev); struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev); return _hid_sensor_power_state(attrb, true); } diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 76db0768e454..06e90debb9f5 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -131,16 +131,31 @@ config LTC2632 module will be called ltc2632. config AD5686 - tristate "Analog Devices AD5686R/AD5685R/AD5684R DAC SPI driver" + tristate + +config AD5686_SPI + tristate "Analog Devices AD5686 and similar multi-channel DACs (SPI)" depends on SPI + select AD5686 help - Say yes here to build support for Analog Devices AD5686R, AD5685R, - AD5684R, AD5791 Voltage Output Digital to - Analog Converter. + Say yes here to build support for Analog Devices AD5672R, AD5676, + AD5676R, AD5684, AD5684R, AD5684R, AD5685R, AD5686, AD5686R. + Voltage Output Digital to Analog Converter. To compile this driver as a module, choose M here: the module will be called ad5686. +config AD5696_I2C + tristate "Analog Devices AD5696 and similar multi-channel DACs (I2C)" + depends on I2C + select AD5686 + help + Say yes here to build support for Analog Devices AD5671R, AD5675R, + AD5694, AD5694R, AD5695R, AD5696, AD5696R Voltage Output Digital to + Analog Converter. + To compile this driver as a module, choose M here: the module will be + called ad5696. + config AD5755 tristate "Analog Devices AD5755/AD5755-1/AD5757/AD5735/AD5737 DAC driver" depends on SPI_MASTER @@ -321,6 +336,16 @@ config TI_DAC082S085 If compiled as a module, it will be called ti-dac082s085. +config TI_DAC5571 + tristate "Texas Instruments 8/10/12/16-bit 1/2/4-channel DAC driver" + depends on I2C + help + Driver for the Texas Instruments + DAC5571, DAC6571, DAC7571, DAC5574, DAC6574, DAC7574, DAC5573, + DAC6573, DAC7573, DAC8571, DAC8574. + + If compiled as a module, it will be called ti-dac5571. + config VF610_DAC tristate "Vybrid vf610 DAC driver" depends on OF diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 81e710ed7491..57aa230d34ab 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -20,6 +20,8 @@ obj-$(CONFIG_AD5761) += ad5761.o obj-$(CONFIG_AD5764) += ad5764.o obj-$(CONFIG_AD5791) += ad5791.o obj-$(CONFIG_AD5686) += ad5686.o +obj-$(CONFIG_AD5686_SPI) += ad5686-spi.o +obj-$(CONFIG_AD5696_I2C) += ad5696-i2c.o obj-$(CONFIG_AD7303) += ad7303.o obj-$(CONFIG_AD8801) += ad8801.o obj-$(CONFIG_CIO_DAC) += cio-dac.o @@ -35,4 +37,5 @@ obj-$(CONFIG_MCP4922) += mcp4922.o obj-$(CONFIG_STM32_DAC_CORE) += stm32-dac-core.o obj-$(CONFIG_STM32_DAC) += stm32-dac.o obj-$(CONFIG_TI_DAC082S085) += ti-dac082s085.o +obj-$(CONFIG_TI_DAC5571) += ti-dac5571.o obj-$(CONFIG_VF610_DAC) += vf610_dac.o diff --git a/drivers/iio/dac/ad5686-spi.c b/drivers/iio/dac/ad5686-spi.c new file mode 100644 index 000000000000..6bb09e9259e6 --- /dev/null +++ b/drivers/iio/dac/ad5686-spi.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * AD5672R, AD5676, AD5676R, AD5684, AD5684R, AD5684R, AD5685R, AD5686, AD5686R + * Digital to analog converters driver + * + * Copyright 2018 Analog Devices Inc. + */ + +#include "ad5686.h" + +#include <linux/module.h> +#include <linux/spi/spi.h> + +static int ad5686_spi_write(struct ad5686_state *st, + u8 cmd, u8 addr, u16 val) +{ + struct spi_device *spi = to_spi_device(st->dev); + + st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) | + AD5686_ADDR(addr) | + val); + + return spi_write(spi, &st->data[0].d8[1], 3); +} + +static int ad5686_spi_read(struct ad5686_state *st, u8 addr) +{ + struct spi_transfer t[] = { + { + .tx_buf = &st->data[0].d8[1], + .len = 3, + .cs_change = 1, + }, { + .tx_buf = &st->data[1].d8[1], + .rx_buf = &st->data[2].d8[1], + .len = 3, + }, + }; + struct spi_device *spi = to_spi_device(st->dev); + int ret; + + st->data[0].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_READBACK_ENABLE) | + AD5686_ADDR(addr)); + st->data[1].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_NOOP)); + + ret = spi_sync_transfer(spi, t, ARRAY_SIZE(t)); + if (ret < 0) + return ret; + + return be32_to_cpu(st->data[2].d32); +} + +static int ad5686_spi_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + + return ad5686_probe(&spi->dev, id->driver_data, id->name, + ad5686_spi_write, ad5686_spi_read); +} + +static int ad5686_spi_remove(struct spi_device *spi) +{ + return ad5686_remove(&spi->dev); +} + +static const struct spi_device_id ad5686_spi_id[] = { + {"ad5672r", ID_AD5672R}, + {"ad5676", ID_AD5676}, + {"ad5676r", ID_AD5676R}, + {"ad5684", ID_AD5684}, + {"ad5684r", ID_AD5684R}, + {"ad5685", ID_AD5685R}, /* Does not exist */ + {"ad5685r", ID_AD5685R}, + {"ad5686", ID_AD5686}, + {"ad5686r", ID_AD5686R}, + {} +}; +MODULE_DEVICE_TABLE(spi, ad5686_spi_id); + +static struct spi_driver ad5686_spi_driver = { + .driver = { + .name = "ad5686", + }, + .probe = ad5686_spi_probe, + .remove = ad5686_spi_remove, + .id_table = ad5686_spi_id, +}; + +module_spi_driver(ad5686_spi_driver); + +MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>"); +MODULE_DESCRIPTION("Analog Devices AD5686 and similar multi-channel DACs"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 20254df7f9c7..89c5f089ae7f 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -1,9 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * AD5686R, AD5685R, AD5684R Digital to analog converters driver * * Copyright 2011 Analog Devices Inc. - * - * Licensed under the GPL-2. */ #include <linux/interrupt.h> @@ -11,7 +10,6 @@ #include <linux/device.h> #include <linux/module.h> #include <linux/kernel.h> -#include <linux/spi/spi.h> #include <linux/slab.h> #include <linux/sysfs.h> #include <linux/regulator/consumer.h> @@ -19,116 +17,7 @@ #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> -#define AD5686_DAC_CHANNELS 4 - -#define AD5686_ADDR(x) ((x) << 16) -#define AD5686_CMD(x) ((x) << 20) - -#define AD5686_ADDR_DAC(chan) (0x1 << (chan)) -#define AD5686_ADDR_ALL_DAC 0xF - -#define AD5686_CMD_NOOP 0x0 -#define AD5686_CMD_WRITE_INPUT_N 0x1 -#define AD5686_CMD_UPDATE_DAC_N 0x2 -#define AD5686_CMD_WRITE_INPUT_N_UPDATE_N 0x3 -#define AD5686_CMD_POWERDOWN_DAC 0x4 -#define AD5686_CMD_LDAC_MASK 0x5 -#define AD5686_CMD_RESET 0x6 -#define AD5686_CMD_INTERNAL_REFER_SETUP 0x7 -#define AD5686_CMD_DAISY_CHAIN_ENABLE 0x8 -#define AD5686_CMD_READBACK_ENABLE 0x9 - -#define AD5686_LDAC_PWRDN_NONE 0x0 -#define AD5686_LDAC_PWRDN_1K 0x1 -#define AD5686_LDAC_PWRDN_100K 0x2 -#define AD5686_LDAC_PWRDN_3STATE 0x3 - -/** - * struct ad5686_chip_info - chip specific information - * @int_vref_mv: AD5620/40/60: the internal reference voltage - * @channel: channel specification -*/ - -struct ad5686_chip_info { - u16 int_vref_mv; - struct iio_chan_spec channel[AD5686_DAC_CHANNELS]; -}; - -/** - * struct ad5446_state - driver instance specific data - * @spi: spi_device - * @chip_info: chip model specific constants, available modes etc - * @reg: supply regulator - * @vref_mv: actual reference voltage used - * @pwr_down_mask: power down mask - * @pwr_down_mode: current power down mode - * @data: spi transfer buffers - */ - -struct ad5686_state { - struct spi_device *spi; - const struct ad5686_chip_info *chip_info; - struct regulator *reg; - unsigned short vref_mv; - unsigned pwr_down_mask; - unsigned pwr_down_mode; - /* - * DMA (thus cache coherency maintenance) requires the - * transfer buffers to live in their own cache lines. - */ - - union { - __be32 d32; - u8 d8[4]; - } data[3] ____cacheline_aligned; -}; - -/** - * ad5686_supported_device_ids: - */ - -enum ad5686_supported_device_ids { - ID_AD5684, - ID_AD5685, - ID_AD5686, -}; -static int ad5686_spi_write(struct ad5686_state *st, - u8 cmd, u8 addr, u16 val, u8 shift) -{ - val <<= shift; - - st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) | - AD5686_ADDR(addr) | - val); - - return spi_write(st->spi, &st->data[0].d8[1], 3); -} - -static int ad5686_spi_read(struct ad5686_state *st, u8 addr) -{ - struct spi_transfer t[] = { - { - .tx_buf = &st->data[0].d8[1], - .len = 3, - .cs_change = 1, - }, { - .tx_buf = &st->data[1].d8[1], - .rx_buf = &st->data[2].d8[1], - .len = 3, - }, - }; - int ret; - - st->data[0].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_READBACK_ENABLE) | - AD5686_ADDR(addr)); - st->data[1].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_NOOP)); - - ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t)); - if (ret < 0) - return ret; - - return be32_to_cpu(st->data[2].d32); -} +#include "ad5686.h" static const char * const ad5686_powerdown_modes[] = { "1kohm_to_gnd", @@ -137,7 +26,7 @@ static const char * const ad5686_powerdown_modes[] = { }; static int ad5686_get_powerdown_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan) + const struct iio_chan_spec *chan) { struct ad5686_state *st = iio_priv(indio_dev); @@ -145,7 +34,8 @@ static int ad5686_get_powerdown_mode(struct iio_dev *indio_dev, } static int ad5686_set_powerdown_mode(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, unsigned int mode) + const struct iio_chan_spec *chan, + unsigned int mode) { struct ad5686_state *st = iio_priv(indio_dev); @@ -163,17 +53,19 @@ static const struct iio_enum ad5686_powerdown_mode_enum = { }; static ssize_t ad5686_read_dac_powerdown(struct iio_dev *indio_dev, - uintptr_t private, const struct iio_chan_spec *chan, char *buf) + uintptr_t private, const struct iio_chan_spec *chan, char *buf) { struct ad5686_state *st = iio_priv(indio_dev); return sprintf(buf, "%d\n", !!(st->pwr_down_mask & - (0x3 << (chan->channel * 2)))); + (0x3 << (chan->channel * 2)))); } static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev, - uintptr_t private, const struct iio_chan_spec *chan, const char *buf, - size_t len) + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, + size_t len) { bool readin; int ret; @@ -188,8 +80,9 @@ static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev, else st->pwr_down_mask &= ~(0x3 << (chan->channel * 2)); - ret = ad5686_spi_write(st, AD5686_CMD_POWERDOWN_DAC, 0, - st->pwr_down_mask & st->pwr_down_mode, 0); + ret = st->write(st, AD5686_CMD_POWERDOWN_DAC, 0, + st->pwr_down_mask & st->pwr_down_mode); + return ret ? ret : len; } @@ -206,7 +99,7 @@ static int ad5686_read_raw(struct iio_dev *indio_dev, switch (m) { case IIO_CHAN_INFO_RAW: mutex_lock(&indio_dev->mlock); - ret = ad5686_spi_read(st, chan->address); + ret = st->read(st, chan->address); mutex_unlock(&indio_dev->mlock); if (ret < 0) return ret; @@ -221,10 +114,10 @@ static int ad5686_read_raw(struct iio_dev *indio_dev, } static int ad5686_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) { struct ad5686_state *st = iio_priv(indio_dev); int ret; @@ -235,11 +128,10 @@ static int ad5686_write_raw(struct iio_dev *indio_dev, return -EINVAL; mutex_lock(&indio_dev->mlock); - ret = ad5686_spi_write(st, - AD5686_CMD_WRITE_INPUT_N_UPDATE_N, - chan->address, - val, - chan->scan_type.shift); + ret = st->write(st, + AD5686_CMD_WRITE_INPUT_N_UPDATE_N, + chan->address, + val << chan->scan_type.shift); mutex_unlock(&indio_dev->mlock); break; default: @@ -266,14 +158,14 @@ static const struct iio_chan_spec_ext_info ad5686_ext_info[] = { { }, }; -#define AD5868_CHANNEL(chan, bits, _shift) { \ +#define AD5868_CHANNEL(chan, addr, bits, _shift) { \ .type = IIO_VOLTAGE, \ .indexed = 1, \ .output = 1, \ .channel = chan, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\ - .address = AD5686_ADDR_DAC(chan), \ + .address = addr, \ .scan_type = { \ .sign = 'u', \ .realbits = (bits), \ @@ -283,45 +175,121 @@ static const struct iio_chan_spec_ext_info ad5686_ext_info[] = { .ext_info = ad5686_ext_info, \ } +#define DECLARE_AD5686_CHANNELS(name, bits, _shift) \ +static struct iio_chan_spec name[] = { \ + AD5868_CHANNEL(0, 1, bits, _shift), \ + AD5868_CHANNEL(1, 2, bits, _shift), \ + AD5868_CHANNEL(2, 4, bits, _shift), \ + AD5868_CHANNEL(3, 8, bits, _shift), \ +} + +#define DECLARE_AD5676_CHANNELS(name, bits, _shift) \ +static struct iio_chan_spec name[] = { \ + AD5868_CHANNEL(0, 0, bits, _shift), \ + AD5868_CHANNEL(1, 1, bits, _shift), \ + AD5868_CHANNEL(2, 2, bits, _shift), \ + AD5868_CHANNEL(3, 3, bits, _shift), \ + AD5868_CHANNEL(4, 4, bits, _shift), \ + AD5868_CHANNEL(5, 5, bits, _shift), \ + AD5868_CHANNEL(6, 6, bits, _shift), \ + AD5868_CHANNEL(7, 7, bits, _shift), \ +} + +DECLARE_AD5676_CHANNELS(ad5672_channels, 12, 4); +DECLARE_AD5676_CHANNELS(ad5676_channels, 16, 0); +DECLARE_AD5686_CHANNELS(ad5684_channels, 12, 4); +DECLARE_AD5686_CHANNELS(ad5685r_channels, 14, 2); +DECLARE_AD5686_CHANNELS(ad5686_channels, 16, 0); + static const struct ad5686_chip_info ad5686_chip_info_tbl[] = { + [ID_AD5671R] = { + .channels = ad5672_channels, + .int_vref_mv = 2500, + .num_channels = 8, + }, + [ID_AD5672R] = { + .channels = ad5672_channels, + .int_vref_mv = 2500, + .num_channels = 8, + }, + [ID_AD5675R] = { + .channels = ad5676_channels, + .int_vref_mv = 2500, + .num_channels = 8, + }, + [ID_AD5676] = { + .channels = ad5676_channels, + .num_channels = 8, + }, + [ID_AD5676R] = { + .channels = ad5676_channels, + .int_vref_mv = 2500, + .num_channels = 8, + }, [ID_AD5684] = { - .channel[0] = AD5868_CHANNEL(0, 12, 4), - .channel[1] = AD5868_CHANNEL(1, 12, 4), - .channel[2] = AD5868_CHANNEL(2, 12, 4), - .channel[3] = AD5868_CHANNEL(3, 12, 4), + .channels = ad5684_channels, + .num_channels = 4, + }, + [ID_AD5684R] = { + .channels = ad5684_channels, .int_vref_mv = 2500, + .num_channels = 4, }, - [ID_AD5685] = { - .channel[0] = AD5868_CHANNEL(0, 14, 2), - .channel[1] = AD5868_CHANNEL(1, 14, 2), - .channel[2] = AD5868_CHANNEL(2, 14, 2), - .channel[3] = AD5868_CHANNEL(3, 14, 2), + [ID_AD5685R] = { + .channels = ad5685r_channels, .int_vref_mv = 2500, + .num_channels = 4, }, [ID_AD5686] = { - .channel[0] = AD5868_CHANNEL(0, 16, 0), - .channel[1] = AD5868_CHANNEL(1, 16, 0), - .channel[2] = AD5868_CHANNEL(2, 16, 0), - .channel[3] = AD5868_CHANNEL(3, 16, 0), + .channels = ad5686_channels, + .num_channels = 4, + }, + [ID_AD5686R] = { + .channels = ad5686_channels, .int_vref_mv = 2500, + .num_channels = 4, + }, + [ID_AD5694] = { + .channels = ad5684_channels, + .num_channels = 4, + }, + [ID_AD5694R] = { + .channels = ad5684_channels, + .int_vref_mv = 2500, + .num_channels = 4, + }, + [ID_AD5696] = { + .channels = ad5686_channels, + .num_channels = 4, + }, + [ID_AD5696R] = { + .channels = ad5686_channels, + .int_vref_mv = 2500, + .num_channels = 4, }, }; - -static int ad5686_probe(struct spi_device *spi) +int ad5686_probe(struct device *dev, + enum ad5686_supported_device_ids chip_type, + const char *name, ad5686_write_func write, + ad5686_read_func read) { struct ad5686_state *st; struct iio_dev *indio_dev; int ret, voltage_uv = 0; - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (indio_dev == NULL) return -ENOMEM; st = iio_priv(indio_dev); - spi_set_drvdata(spi, indio_dev); + dev_set_drvdata(dev, indio_dev); + + st->dev = dev; + st->write = write; + st->read = read; - st->reg = devm_regulator_get_optional(&spi->dev, "vcc"); + st->reg = devm_regulator_get_optional(dev, "vcc"); if (!IS_ERR(st->reg)) { ret = regulator_enable(st->reg); if (ret) @@ -334,28 +302,25 @@ static int ad5686_probe(struct spi_device *spi) voltage_uv = ret; } - st->chip_info = - &ad5686_chip_info_tbl[spi_get_device_id(spi)->driver_data]; + st->chip_info = &ad5686_chip_info_tbl[chip_type]; if (voltage_uv) st->vref_mv = voltage_uv / 1000; else st->vref_mv = st->chip_info->int_vref_mv; - st->spi = spi; - /* Set all the power down mode for all channels to 1K pulldown */ st->pwr_down_mode = 0x55; - indio_dev->dev.parent = &spi->dev; - indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->dev.parent = dev; + indio_dev->name = name; indio_dev->info = &ad5686_info; indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = st->chip_info->channel; - indio_dev->num_channels = AD5686_DAC_CHANNELS; + indio_dev->channels = st->chip_info->channels; + indio_dev->num_channels = st->chip_info->num_channels; - ret = ad5686_spi_write(st, AD5686_CMD_INTERNAL_REFER_SETUP, 0, - !!voltage_uv, 0); + ret = st->write(st, AD5686_CMD_INTERNAL_REFER_SETUP, + 0, !!voltage_uv); if (ret) goto error_disable_reg; @@ -370,10 +335,11 @@ error_disable_reg: regulator_disable(st->reg); return ret; } +EXPORT_SYMBOL_GPL(ad5686_probe); -static int ad5686_remove(struct spi_device *spi) +int ad5686_remove(struct device *dev) { - struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct iio_dev *indio_dev = dev_get_drvdata(dev); struct ad5686_state *st = iio_priv(indio_dev); iio_device_unregister(indio_dev); @@ -382,24 +348,7 @@ static int ad5686_remove(struct spi_device *spi) return 0; } - -static const struct spi_device_id ad5686_id[] = { - {"ad5684", ID_AD5684}, - {"ad5685", ID_AD5685}, - {"ad5686", ID_AD5686}, - {} -}; -MODULE_DEVICE_TABLE(spi, ad5686_id); - -static struct spi_driver ad5686_driver = { - .driver = { - .name = "ad5686", - }, - .probe = ad5686_probe, - .remove = ad5686_remove, - .id_table = ad5686_id, -}; -module_spi_driver(ad5686_driver); +EXPORT_SYMBOL_GPL(ad5686_remove); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); MODULE_DESCRIPTION("Analog Devices AD5686/85/84 DAC"); diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h new file mode 100644 index 000000000000..05f0ce9d2de1 --- /dev/null +++ b/drivers/iio/dac/ad5686.h @@ -0,0 +1,121 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * This file is part of AD5686 DAC driver + * + * Copyright 2018 Analog Devices Inc. + */ + +#ifndef __DRIVERS_IIO_DAC_AD5686_H__ +#define __DRIVERS_IIO_DAC_AD5686_H__ + +#include <linux/types.h> +#include <linux/cache.h> +#include <linux/mutex.h> +#include <linux/kernel.h> + +#define AD5686_ADDR(x) ((x) << 16) +#define AD5686_CMD(x) ((x) << 20) + +#define AD5686_ADDR_DAC(chan) (0x1 << (chan)) +#define AD5686_ADDR_ALL_DAC 0xF + +#define AD5686_CMD_NOOP 0x0 +#define AD5686_CMD_WRITE_INPUT_N 0x1 +#define AD5686_CMD_UPDATE_DAC_N 0x2 +#define AD5686_CMD_WRITE_INPUT_N_UPDATE_N 0x3 +#define AD5686_CMD_POWERDOWN_DAC 0x4 +#define AD5686_CMD_LDAC_MASK 0x5 +#define AD5686_CMD_RESET 0x6 +#define AD5686_CMD_INTERNAL_REFER_SETUP 0x7 +#define AD5686_CMD_DAISY_CHAIN_ENABLE 0x8 +#define AD5686_CMD_READBACK_ENABLE 0x9 + +#define AD5686_LDAC_PWRDN_NONE 0x0 +#define AD5686_LDAC_PWRDN_1K 0x1 +#define AD5686_LDAC_PWRDN_100K 0x2 +#define AD5686_LDAC_PWRDN_3STATE 0x3 + +/** + * ad5686_supported_device_ids: + */ +enum ad5686_supported_device_ids { + ID_AD5671R, + ID_AD5672R, + ID_AD5675R, + ID_AD5676, + ID_AD5676R, + ID_AD5684, + ID_AD5684R, + ID_AD5685R, + ID_AD5686, + ID_AD5686R, + ID_AD5694, + ID_AD5694R, + ID_AD5695R, + ID_AD5696, + ID_AD5696R, +}; + +struct ad5686_state; + +typedef int (*ad5686_write_func)(struct ad5686_state *st, + u8 cmd, u8 addr, u16 val); + +typedef int (*ad5686_read_func)(struct ad5686_state *st, u8 addr); + +/** + * struct ad5686_chip_info - chip specific information + * @int_vref_mv: AD5620/40/60: the internal reference voltage + * @num_channels: number of channels + * @channel: channel specification + */ + +struct ad5686_chip_info { + u16 int_vref_mv; + unsigned int num_channels; + struct iio_chan_spec *channels; +}; + +/** + * struct ad5446_state - driver instance specific data + * @spi: spi_device + * @chip_info: chip model specific constants, available modes etc + * @reg: supply regulator + * @vref_mv: actual reference voltage used + * @pwr_down_mask: power down mask + * @pwr_down_mode: current power down mode + * @data: spi transfer buffers + */ + +struct ad5686_state { + struct device *dev; + const struct ad5686_chip_info *chip_info; + struct regulator *reg; + unsigned short vref_mv; + unsigned int pwr_down_mask; + unsigned int pwr_down_mode; + ad5686_write_func write; + ad5686_read_func read; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + + union { + __be32 d32; + __be16 d16; + u8 d8[4]; + } data[3] ____cacheline_aligned; +}; + + +int ad5686_probe(struct device *dev, + enum ad5686_supported_device_ids chip_type, + const char *name, ad5686_write_func write, + ad5686_read_func read); + +int ad5686_remove(struct device *dev); + + +#endif /* __DRIVERS_IIO_DAC_AD5686_H__ */ diff --git a/drivers/iio/dac/ad5696-i2c.c b/drivers/iio/dac/ad5696-i2c.c new file mode 100644 index 000000000000..275e0321bcf8 --- /dev/null +++ b/drivers/iio/dac/ad5696-i2c.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * AD5671R, AD5675R, AD5694, AD5694R, AD5695R, AD5696, AD5696R + * Digital to analog converters driver + * + * Copyright 2018 Analog Devices Inc. + */ + +#include "ad5686.h" + +#include <linux/module.h> +#include <linux/i2c.h> + +static int ad5686_i2c_read(struct ad5686_state *st, u8 addr) +{ + struct i2c_client *i2c = to_i2c_client(st->dev); + struct i2c_msg msg[2] = { + { + .addr = i2c->addr, + .flags = i2c->flags, + .len = 3, + .buf = &st->data[0].d8[1], + }, + { + .addr = i2c->addr, + .flags = i2c->flags | I2C_M_RD, + .len = 2, + .buf = (char *)&st->data[0].d16, + }, + }; + int ret; + + st->data[0].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_NOOP) | + AD5686_ADDR(addr) | + 0x00); + + ret = i2c_transfer(i2c->adapter, msg, 2); + if (ret < 0) + return ret; + + return be16_to_cpu(st->data[0].d16); +} + +static int ad5686_i2c_write(struct ad5686_state *st, + u8 cmd, u8 addr, u16 val) +{ + struct i2c_client *i2c = to_i2c_client(st->dev); + int ret; + + st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) | AD5686_ADDR(addr) + | val); + + ret = i2c_master_send(i2c, &st->data[0].d8[1], 3); + if (ret < 0) + return ret; + + return (ret != 3) ? -EIO : 0; +} + +static int ad5686_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + return ad5686_probe(&i2c->dev, id->driver_data, id->name, + ad5686_i2c_write, ad5686_i2c_read); +} + +static int ad5686_i2c_remove(struct i2c_client *i2c) +{ + return ad5686_remove(&i2c->dev); +} + +static const struct i2c_device_id ad5686_i2c_id[] = { + {"ad5671r", ID_AD5671R}, + {"ad5675r", ID_AD5675R}, + {"ad5694", ID_AD5694}, + {"ad5694r", ID_AD5694R}, + {"ad5695r", ID_AD5695R}, + {"ad5696", ID_AD5696}, + {"ad5696r", ID_AD5696R}, + {} +}; +MODULE_DEVICE_TABLE(i2c, ad5686_i2c_id); + +static struct i2c_driver ad5686_i2c_driver = { + .driver = { + .name = "ad5696", + }, + .probe = ad5686_i2c_probe, + .remove = ad5686_i2c_remove, + .id_table = ad5686_i2c_id, +}; + +module_i2c_driver(ad5686_i2c_driver); + +MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>"); +MODULE_DESCRIPTION("Analog Devices AD5686 and similar multi-channel DACs"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/ti-dac5571.c b/drivers/iio/dac/ti-dac5571.c new file mode 100644 index 000000000000..dd21eebed6a8 --- /dev/null +++ b/drivers/iio/dac/ti-dac5571.c @@ -0,0 +1,439 @@ +/* + * ti-dac5571.c - Texas Instruments 8/10/12-bit 1/4-channel DAC driver + * + * Copyright (C) 2018 Prevas A/S + * + * http://www.ti.com/lit/ds/symlink/dac5571.pdf + * http://www.ti.com/lit/ds/symlink/dac6571.pdf + * http://www.ti.com/lit/ds/symlink/dac7571.pdf + * http://www.ti.com/lit/ds/symlink/dac5574.pdf + * http://www.ti.com/lit/ds/symlink/dac6574.pdf + * http://www.ti.com/lit/ds/symlink/dac7574.pdf + * http://www.ti.com/lit/ds/symlink/dac5573.pdf + * http://www.ti.com/lit/ds/symlink/dac6573.pdf + * http://www.ti.com/lit/ds/symlink/dac7573.pdf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License (version 2) as + * published by the Free Software Foundation. + */ + +#include <linux/iio/iio.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of.h> +#include <linux/regulator/consumer.h> + +enum chip_id { + single_8bit, single_10bit, single_12bit, + quad_8bit, quad_10bit, quad_12bit +}; + +struct dac5571_spec { + u8 num_channels; + u8 resolution; +}; + +static const struct dac5571_spec dac5571_spec[] = { + [single_8bit] = {.num_channels = 1, .resolution = 8}, + [single_10bit] = {.num_channels = 1, .resolution = 10}, + [single_12bit] = {.num_channels = 1, .resolution = 12}, + [quad_8bit] = {.num_channels = 4, .resolution = 8}, + [quad_10bit] = {.num_channels = 4, .resolution = 10}, + [quad_12bit] = {.num_channels = 4, .resolution = 12}, +}; + +struct dac5571_data { + struct i2c_client *client; + int id; + struct mutex lock; + struct regulator *vref; + u16 val[4]; + bool powerdown; + u8 powerdown_mode; + struct dac5571_spec const *spec; + int (*dac5571_cmd)(struct dac5571_data *data, int channel, u16 val); + int (*dac5571_pwrdwn)(struct dac5571_data *data, int channel, u8 pwrdwn); + u8 buf[3] ____cacheline_aligned; +}; + +#define DAC5571_POWERDOWN(mode) ((mode) + 1) +#define DAC5571_POWERDOWN_FLAG BIT(0) +#define DAC5571_CHANNEL_SELECT 1 +#define DAC5571_LOADMODE_DIRECT BIT(4) +#define DAC5571_SINGLE_PWRDWN_BITS 4 +#define DAC5571_QUAD_PWRDWN_BITS 6 + +static int dac5571_cmd_single(struct dac5571_data *data, int channel, u16 val) +{ + unsigned int shift; + + shift = 12 - data->spec->resolution; + data->buf[1] = val << shift; + data->buf[0] = val >> (8 - shift); + + if (i2c_master_send(data->client, data->buf, 2) != 2) + return -EIO; + + return 0; +} + +static int dac5571_cmd_quad(struct dac5571_data *data, int channel, u16 val) +{ + unsigned int shift; + + shift = 16 - data->spec->resolution; + data->buf[2] = val << shift; + data->buf[1] = (val >> (8 - shift)); + data->buf[0] = (channel << DAC5571_CHANNEL_SELECT) | + DAC5571_LOADMODE_DIRECT; + + if (i2c_master_send(data->client, data->buf, 3) != 3) + return -EIO; + + return 0; +} + +static int dac5571_pwrdwn_single(struct dac5571_data *data, int channel, u8 pwrdwn) +{ + unsigned int shift; + + shift = 12 - data->spec->resolution; + data->buf[1] = 0; + data->buf[0] = pwrdwn << DAC5571_SINGLE_PWRDWN_BITS; + + if (i2c_master_send(data->client, data->buf, 2) != 2) + return -EIO; + + return 0; +} + +static int dac5571_pwrdwn_quad(struct dac5571_data *data, int channel, u8 pwrdwn) +{ + unsigned int shift; + + shift = 16 - data->spec->resolution; + data->buf[2] = 0; + data->buf[1] = pwrdwn << DAC5571_QUAD_PWRDWN_BITS; + data->buf[0] = (channel << DAC5571_CHANNEL_SELECT) | + DAC5571_LOADMODE_DIRECT | DAC5571_POWERDOWN_FLAG; + + if (i2c_master_send(data->client, data->buf, 3) != 3) + return -EIO; + + return 0; +} + +static const char *const dac5571_powerdown_modes[] = { + "1kohm_to_gnd", "100kohm_to_gnd", "three_state", +}; + +static int dac5571_get_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct dac5571_data *data = iio_priv(indio_dev); + + return data->powerdown_mode; +} + +static int dac5571_set_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct dac5571_data *data = iio_priv(indio_dev); + int ret = 0; + + if (data->powerdown_mode == mode) + return 0; + + mutex_lock(&data->lock); + if (data->powerdown) { + ret = data->dac5571_pwrdwn(data, chan->channel, + DAC5571_POWERDOWN(mode)); + if (ret) + goto out; + } + data->powerdown_mode = mode; + + out: + mutex_unlock(&data->lock); + + return ret; +} + +static const struct iio_enum dac5571_powerdown_mode = { + .items = dac5571_powerdown_modes, + .num_items = ARRAY_SIZE(dac5571_powerdown_modes), + .get = dac5571_get_powerdown_mode, + .set = dac5571_set_powerdown_mode, +}; + +static ssize_t dac5571_read_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct dac5571_data *data = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", data->powerdown); +} + +static ssize_t dac5571_write_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct dac5571_data *data = iio_priv(indio_dev); + bool powerdown; + int ret; + + ret = strtobool(buf, &powerdown); + if (ret) + return ret; + + if (data->powerdown == powerdown) + return len; + + mutex_lock(&data->lock); + if (powerdown) + ret = data->dac5571_pwrdwn(data, chan->channel, + DAC5571_POWERDOWN(data->powerdown_mode)); + else + ret = data->dac5571_cmd(data, chan->channel, data->val[0]); + if (ret) + goto out; + + data->powerdown = powerdown; + + out: + mutex_unlock(&data->lock); + + return ret ? ret : len; +} + + +static const struct iio_chan_spec_ext_info dac5571_ext_info[] = { + { + .name = "powerdown", + .read = dac5571_read_powerdown, + .write = dac5571_write_powerdown, + .shared = IIO_SHARED_BY_TYPE, + }, + IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, &dac5571_powerdown_mode), + IIO_ENUM_AVAILABLE("powerdown_mode", &dac5571_powerdown_mode), + {}, +}; + +#define dac5571_CHANNEL(chan, name) { \ + .type = IIO_VOLTAGE, \ + .channel = (chan), \ + .address = (chan), \ + .indexed = true, \ + .output = true, \ + .datasheet_name = name, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .ext_info = dac5571_ext_info, \ +} + +static const struct iio_chan_spec dac5571_channels[] = { + dac5571_CHANNEL(0, "A"), + dac5571_CHANNEL(1, "B"), + dac5571_CHANNEL(2, "C"), + dac5571_CHANNEL(3, "D"), +}; + +static int dac5571_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct dac5571_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + *val = data->val[chan->channel]; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + ret = regulator_get_voltage(data->vref); + if (ret < 0) + return ret; + + *val = ret / 1000; + *val2 = data->spec->resolution; + return IIO_VAL_FRACTIONAL_LOG2; + + default: + return -EINVAL; + } +} + +static int dac5571_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct dac5571_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (data->val[chan->channel] == val) + return 0; + + if (val >= (1 << data->spec->resolution) || val < 0) + return -EINVAL; + + if (data->powerdown) + return -EBUSY; + + mutex_lock(&data->lock); + ret = data->dac5571_cmd(data, chan->channel, val); + if (ret == 0) + data->val[chan->channel] = val; + mutex_unlock(&data->lock); + return ret; + + default: + return -EINVAL; + } +} + +static int dac5571_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + return IIO_VAL_INT; +} + +static const struct iio_info dac5571_info = { + .read_raw = dac5571_read_raw, + .write_raw = dac5571_write_raw, + .write_raw_get_fmt = dac5571_write_raw_get_fmt, +}; + +static int dac5571_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + const struct dac5571_spec *spec; + struct dac5571_data *data; + struct iio_dev *indio_dev; + int ret, i; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + + indio_dev->dev.parent = dev; + indio_dev->dev.of_node = client->dev.of_node; + indio_dev->info = &dac5571_info; + indio_dev->name = id->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = dac5571_channels; + + spec = &dac5571_spec[id->driver_data]; + indio_dev->num_channels = spec->num_channels; + data->spec = spec; + + data->vref = devm_regulator_get(dev, "vref"); + if (IS_ERR(data->vref)) + return PTR_ERR(data->vref); + + ret = regulator_enable(data->vref); + if (ret < 0) + return ret; + + mutex_init(&data->lock); + + switch (spec->num_channels) { + case 1: + data->dac5571_cmd = dac5571_cmd_single; + data->dac5571_pwrdwn = dac5571_pwrdwn_single; + break; + case 4: + data->dac5571_cmd = dac5571_cmd_quad; + data->dac5571_pwrdwn = dac5571_pwrdwn_quad; + break; + default: + goto err; + } + + for (i = 0; i < spec->num_channels; i++) { + ret = data->dac5571_cmd(data, i, 0); + if (ret) { + dev_err(dev, "failed to initialize channel %d to 0\n", i); + goto err; + } + } + + ret = iio_device_register(indio_dev); + if (ret) + goto err; + + return 0; + + err: + regulator_disable(data->vref); + return ret; +} + +static int dac5571_remove(struct i2c_client *i2c) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(i2c); + struct dac5571_data *data = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + regulator_disable(data->vref); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id dac5571_of_id[] = { + {.compatible = "ti,dac5571"}, + {.compatible = "ti,dac6571"}, + {.compatible = "ti,dac7571"}, + {.compatible = "ti,dac5574"}, + {.compatible = "ti,dac6574"}, + {.compatible = "ti,dac7574"}, + {.compatible = "ti,dac5573"}, + {.compatible = "ti,dac6573"}, + {.compatible = "ti,dac7573"}, + {} +}; +MODULE_DEVICE_TABLE(of, dac5571_of_id); +#endif + +static const struct i2c_device_id dac5571_id[] = { + {"dac5571", single_8bit}, + {"dac6571", single_10bit}, + {"dac7571", single_12bit}, + {"dac5574", quad_8bit}, + {"dac6574", quad_10bit}, + {"dac7574", quad_12bit}, + {"dac5573", quad_8bit}, + {"dac6573", quad_10bit}, + {"dac7573", quad_12bit}, + {} +}; +MODULE_DEVICE_TABLE(i2c, dac5571_id); + +static struct i2c_driver dac5571_driver = { + .driver = { + .name = "ti-dac5571", + }, + .probe = dac5571_probe, + .remove = dac5571_remove, + .id_table = dac5571_id, +}; +module_i2c_driver(dac5571_driver); + +MODULE_AUTHOR("Sean Nyekjaer <sean.nyekjaer@prevas.dk>"); +MODULE_DESCRIPTION("Texas Instruments 8/10/12-bit 1/4-channel DAC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index 7d64be353403..43fba5f7532b 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -24,6 +24,7 @@ #include <linux/spinlock.h> #include <linux/iio/iio.h> #include <linux/acpi.h> +#include <linux/platform_device.h> #include "inv_mpu_iio.h" /* @@ -52,6 +53,7 @@ static const struct inv_mpu6050_reg_map reg_set_6500 = { .raw_accl = INV_MPU6050_REG_RAW_ACCEL, .temperature = INV_MPU6050_REG_TEMPERATURE, .int_enable = INV_MPU6050_REG_INT_ENABLE, + .int_status = INV_MPU6050_REG_INT_STATUS, .pwr_mgmt_1 = INV_MPU6050_REG_PWR_MGMT_1, .pwr_mgmt_2 = INV_MPU6050_REG_PWR_MGMT_2, .int_pin_cfg = INV_MPU6050_REG_INT_PIN_CFG, @@ -86,6 +88,7 @@ static const struct inv_mpu6050_chip_config chip_config_6050 = { .gyro_fifo_enable = false, .accl_fifo_enable = false, .accl_fs = INV_MPU6050_FS_02G, + .user_ctrl = 0, }; /* Indexed by enum inv_devices */ @@ -121,6 +124,12 @@ static const struct inv_mpu6050_hw hw_info[] = { .config = &chip_config_6050, }, { + .whoami = INV_MPU9255_WHOAMI_VALUE, + .name = "MPU9255", + .reg = ®_set_6500, + .config = &chip_config_6050, + }, + { .whoami = INV_ICM20608_WHOAMI_VALUE, .name = "ICM20608", .reg = ®_set_6500, @@ -168,7 +177,7 @@ int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask) return result; if (en) { - /* Wait for output stabilize */ + /* Wait for output to stabilize */ msleep(INV_MPU6050_TEMP_UP_TIME); if (mask == INV_MPU6050_BIT_PWR_GYRO_STBY) { /* switch internal clock to PLL */ @@ -185,26 +194,29 @@ int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask) int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on) { - int result = 0; + int result; if (power_on) { - if (!st->powerup_count) + if (!st->powerup_count) { result = regmap_write(st->map, st->reg->pwr_mgmt_1, 0); - if (!result) - st->powerup_count++; + if (result) + return result; + usleep_range(INV_MPU6050_REG_UP_TIME_MIN, + INV_MPU6050_REG_UP_TIME_MAX); + } + st->powerup_count++; } else { - st->powerup_count--; - if (!st->powerup_count) + if (st->powerup_count == 1) { result = regmap_write(st->map, st->reg->pwr_mgmt_1, INV_MPU6050_BIT_SLEEP); + if (result) + return result; + } + st->powerup_count--; } - if (result) - return result; - - if (power_on) - usleep_range(INV_MPU6050_REG_UP_TIME_MIN, - INV_MPU6050_REG_UP_TIME_MAX); + dev_dbg(regmap_get_device(st->map), "set power %d, count=%u\n", + power_on, st->powerup_count); return 0; } @@ -262,26 +274,33 @@ static int inv_mpu6050_init_config(struct iio_dev *indio_dev) d = (INV_MPU6050_FSR_2000DPS << INV_MPU6050_GYRO_CONFIG_FSR_SHIFT); result = regmap_write(st->map, st->reg->gyro_config, d); if (result) - return result; + goto error_power_off; result = inv_mpu6050_set_lpf_regs(st, INV_MPU6050_FILTER_20HZ); if (result) - return result; + goto error_power_off; d = INV_MPU6050_ONE_K_HZ / INV_MPU6050_INIT_FIFO_RATE - 1; result = regmap_write(st->map, st->reg->sample_rate_div, d); if (result) - return result; + goto error_power_off; d = (INV_MPU6050_FS_02G << INV_MPU6050_ACCL_CONFIG_FSR_SHIFT); result = regmap_write(st->map, st->reg->accl_config, d); if (result) + goto error_power_off; + + result = regmap_write(st->map, st->reg->int_pin_cfg, st->irq_mask); + if (result) return result; memcpy(&st->chip_config, hw_info[st->chip_type].config, sizeof(struct inv_mpu6050_chip_config)); - result = inv_mpu6050_set_power_itg(st, false); + return inv_mpu6050_set_power_itg(st, false); + +error_power_off: + inv_mpu6050_set_power_itg(st, false); return result; } @@ -314,6 +333,65 @@ static int inv_mpu6050_sensor_show(struct inv_mpu6050_state *st, int reg, return IIO_VAL_INT; } +static int inv_mpu6050_read_channel_data(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val) +{ + struct inv_mpu6050_state *st = iio_priv(indio_dev); + int result; + int ret; + + result = inv_mpu6050_set_power_itg(st, true); + if (result) + return result; + + switch (chan->type) { + case IIO_ANGL_VEL: + result = inv_mpu6050_switch_engine(st, true, + INV_MPU6050_BIT_PWR_GYRO_STBY); + if (result) + goto error_power_off; + ret = inv_mpu6050_sensor_show(st, st->reg->raw_gyro, + chan->channel2, val); + result = inv_mpu6050_switch_engine(st, false, + INV_MPU6050_BIT_PWR_GYRO_STBY); + if (result) + goto error_power_off; + break; + case IIO_ACCEL: + result = inv_mpu6050_switch_engine(st, true, + INV_MPU6050_BIT_PWR_ACCL_STBY); + if (result) + goto error_power_off; + ret = inv_mpu6050_sensor_show(st, st->reg->raw_accl, + chan->channel2, val); + result = inv_mpu6050_switch_engine(st, false, + INV_MPU6050_BIT_PWR_ACCL_STBY); + if (result) + goto error_power_off; + break; + case IIO_TEMP: + /* wait for stablization */ + msleep(INV_MPU6050_SENSOR_UP_TIME); + ret = inv_mpu6050_sensor_show(st, st->reg->temperature, + IIO_MOD_X, val); + break; + default: + ret = -EINVAL; + break; + } + + result = inv_mpu6050_set_power_itg(st, false); + if (result) + goto error_power_off; + + return ret; + +error_power_off: + inv_mpu6050_set_power_itg(st, false); + return result; +} + static int inv_mpu6050_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, @@ -324,63 +402,14 @@ inv_mpu6050_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - { - int result; - - ret = IIO_VAL_INT; + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; mutex_lock(&st->lock); - result = iio_device_claim_direct_mode(indio_dev); - if (result) - goto error_read_raw_unlock; - result = inv_mpu6050_set_power_itg(st, true); - if (result) - goto error_read_raw_release; - switch (chan->type) { - case IIO_ANGL_VEL: - result = inv_mpu6050_switch_engine(st, true, - INV_MPU6050_BIT_PWR_GYRO_STBY); - if (result) - goto error_read_raw_power_off; - ret = inv_mpu6050_sensor_show(st, st->reg->raw_gyro, - chan->channel2, val); - result = inv_mpu6050_switch_engine(st, false, - INV_MPU6050_BIT_PWR_GYRO_STBY); - if (result) - goto error_read_raw_power_off; - break; - case IIO_ACCEL: - result = inv_mpu6050_switch_engine(st, true, - INV_MPU6050_BIT_PWR_ACCL_STBY); - if (result) - goto error_read_raw_power_off; - ret = inv_mpu6050_sensor_show(st, st->reg->raw_accl, - chan->channel2, val); - result = inv_mpu6050_switch_engine(st, false, - INV_MPU6050_BIT_PWR_ACCL_STBY); - if (result) - goto error_read_raw_power_off; - break; - case IIO_TEMP: - /* wait for stablization */ - msleep(INV_MPU6050_SENSOR_UP_TIME); - ret = inv_mpu6050_sensor_show(st, st->reg->temperature, - IIO_MOD_X, val); - break; - default: - ret = -EINVAL; - break; - } -error_read_raw_power_off: - result |= inv_mpu6050_set_power_itg(st, false); -error_read_raw_release: - iio_device_release_direct_mode(indio_dev); -error_read_raw_unlock: + ret = inv_mpu6050_read_channel_data(indio_dev, chan, val); mutex_unlock(&st->lock); - if (result) - return result; - + iio_device_release_direct_mode(indio_dev); return ret; - } case IIO_CHAN_INFO_SCALE: switch (chan->type) { case IIO_ANGL_VEL: @@ -502,17 +531,18 @@ static int inv_mpu6050_write_raw(struct iio_dev *indio_dev, struct inv_mpu6050_state *st = iio_priv(indio_dev); int result; - mutex_lock(&st->lock); /* * we should only update scale when the chip is disabled, i.e. * not running */ result = iio_device_claim_direct_mode(indio_dev); if (result) - goto error_write_raw_unlock; + return result; + + mutex_lock(&st->lock); result = inv_mpu6050_set_power_itg(st, true); if (result) - goto error_write_raw_release; + goto error_write_raw_unlock; switch (mask) { case IIO_CHAN_INFO_SCALE: @@ -551,10 +581,9 @@ static int inv_mpu6050_write_raw(struct iio_dev *indio_dev, } result |= inv_mpu6050_set_power_itg(st, false); -error_write_raw_release: - iio_device_release_direct_mode(indio_dev); error_write_raw_unlock: mutex_unlock(&st->lock); + iio_device_release_direct_mode(indio_dev); return result; } @@ -613,17 +642,18 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr, fifo_rate > INV_MPU6050_MAX_FIFO_RATE) return -EINVAL; + result = iio_device_claim_direct_mode(indio_dev); + if (result) + return result; + mutex_lock(&st->lock); if (fifo_rate == st->chip_config.fifo_rate) { result = 0; goto fifo_rate_fail_unlock; } - result = iio_device_claim_direct_mode(indio_dev); - if (result) - goto fifo_rate_fail_unlock; result = inv_mpu6050_set_power_itg(st, true); if (result) - goto fifo_rate_fail_release; + goto fifo_rate_fail_unlock; d = INV_MPU6050_ONE_K_HZ / fifo_rate - 1; result = regmap_write(st->map, st->reg->sample_rate_div, d); @@ -637,10 +667,9 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr, fifo_rate_fail_power_off: result |= inv_mpu6050_set_power_itg(st, false); -fifo_rate_fail_release: - iio_device_release_direct_mode(indio_dev); fifo_rate_fail_unlock: mutex_unlock(&st->lock); + iio_device_release_direct_mode(indio_dev); if (result) return result; @@ -850,14 +879,11 @@ static int inv_check_and_setup_chip(struct inv_mpu6050_state *st) msleep(INV_MPU6050_POWER_UP_TIME); /* - * toggle power state. After reset, the sleep bit could be on - * or off depending on the OTP settings. Toggling power would + * Turn power on. After reset, the sleep bit could be on + * or off depending on the OTP settings. Turning power on * make it in a definite state as well as making the hardware * state align with the software state */ - result = inv_mpu6050_set_power_itg(st, false); - if (result) - return result; result = inv_mpu6050_set_power_itg(st, true); if (result) return result; @@ -865,13 +891,17 @@ static int inv_check_and_setup_chip(struct inv_mpu6050_state *st) result = inv_mpu6050_switch_engine(st, false, INV_MPU6050_BIT_PWR_ACCL_STBY); if (result) - return result; + goto error_power_off; result = inv_mpu6050_switch_engine(st, false, INV_MPU6050_BIT_PWR_GYRO_STBY); if (result) - return result; + goto error_power_off; - return 0; + return inv_mpu6050_set_power_itg(st, false); + +error_power_off: + inv_mpu6050_set_power_itg(st, false); + return result; } int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, @@ -882,6 +912,8 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, struct inv_mpu6050_platform_data *pdata; struct device *dev = regmap_get_device(regmap); int result; + struct irq_data *desc; + int irq_type; indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); if (!indio_dev) @@ -913,20 +945,43 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, st->plat_data = *pdata; } + desc = irq_get_irq_data(irq); + if (!desc) { + dev_err(dev, "Could not find IRQ %d\n", irq); + return -EINVAL; + } + + irq_type = irqd_get_trigger_type(desc); + if (irq_type == IRQF_TRIGGER_RISING) + st->irq_mask = INV_MPU6050_ACTIVE_HIGH; + else if (irq_type == IRQF_TRIGGER_FALLING) + st->irq_mask = INV_MPU6050_ACTIVE_LOW; + else if (irq_type == IRQF_TRIGGER_HIGH) + st->irq_mask = INV_MPU6050_ACTIVE_HIGH | + INV_MPU6050_LATCH_INT_EN; + else if (irq_type == IRQF_TRIGGER_LOW) + st->irq_mask = INV_MPU6050_ACTIVE_LOW | + INV_MPU6050_LATCH_INT_EN; + else { + dev_err(dev, "Invalid interrupt type 0x%x specified\n", + irq_type); + return -EINVAL; + } + /* power is turned on inside check chip type*/ result = inv_check_and_setup_chip(st); if (result) return result; - if (inv_mpu_bus_setup) - inv_mpu_bus_setup(indio_dev); - result = inv_mpu6050_init_config(indio_dev); if (result) { dev_err(dev, "Could not initialize device.\n"); return result; } + if (inv_mpu_bus_setup) + inv_mpu_bus_setup(indio_dev); + dev_set_drvdata(dev, indio_dev); indio_dev->dev.parent = dev; /* name will be NULL when enumerated via ACPI */ @@ -940,50 +995,32 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, indio_dev->info = &mpu_info; indio_dev->modes = INDIO_BUFFER_TRIGGERED; - result = iio_triggered_buffer_setup(indio_dev, - inv_mpu6050_irq_handler, - inv_mpu6050_read_fifo, - NULL); + result = devm_iio_triggered_buffer_setup(dev, indio_dev, + inv_mpu6050_irq_handler, + inv_mpu6050_read_fifo, + NULL); if (result) { dev_err(dev, "configure buffer fail %d\n", result); return result; } - result = inv_mpu6050_probe_trigger(indio_dev); + result = inv_mpu6050_probe_trigger(indio_dev, irq_type); if (result) { dev_err(dev, "trigger probe fail %d\n", result); - goto out_unreg_ring; + return result; } INIT_KFIFO(st->timestamps); spin_lock_init(&st->time_stamp_lock); - result = iio_device_register(indio_dev); + result = devm_iio_device_register(dev, indio_dev); if (result) { dev_err(dev, "IIO register fail %d\n", result); - goto out_remove_trigger; + return result; } return 0; - -out_remove_trigger: - inv_mpu6050_remove_trigger(st); -out_unreg_ring: - iio_triggered_buffer_cleanup(indio_dev); - return result; } EXPORT_SYMBOL_GPL(inv_mpu_core_probe); -int inv_mpu_core_remove(struct device *dev) -{ - struct iio_dev *indio_dev = dev_get_drvdata(dev); - - iio_device_unregister(indio_dev); - inv_mpu6050_remove_trigger(iio_priv(indio_dev)); - iio_triggered_buffer_cleanup(indio_dev); - - return 0; -} -EXPORT_SYMBOL_GPL(inv_mpu_core_remove); - #ifdef CONFIG_PM_SLEEP static int inv_mpu_resume(struct device *dev) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c index fcd7a92b6cf8..495409d56207 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -29,25 +29,18 @@ static int inv_mpu6050_select_bypass(struct i2c_mux_core *muxc, u32 chan_id) { struct iio_dev *indio_dev = i2c_mux_priv(muxc); struct inv_mpu6050_state *st = iio_priv(indio_dev); - int ret = 0; + int ret; - /* Use the same mutex which was used everywhere to protect power-op */ mutex_lock(&st->lock); - if (!st->powerup_count) { - ret = regmap_write(st->map, st->reg->pwr_mgmt_1, 0); - if (ret) - goto write_error; - usleep_range(INV_MPU6050_REG_UP_TIME_MIN, - INV_MPU6050_REG_UP_TIME_MAX); - } - if (!ret) { - st->powerup_count++; - ret = regmap_write(st->map, st->reg->int_pin_cfg, - INV_MPU6050_INT_PIN_CFG | - INV_MPU6050_BIT_BYPASS_EN); - } -write_error: + ret = inv_mpu6050_set_power_itg(st, true); + if (ret) + goto error_unlock; + + ret = regmap_write(st->map, st->reg->int_pin_cfg, + st->irq_mask | INV_MPU6050_BIT_BYPASS_EN); + +error_unlock: mutex_unlock(&st->lock); return ret; @@ -59,12 +52,11 @@ static int inv_mpu6050_deselect_bypass(struct i2c_mux_core *muxc, u32 chan_id) struct inv_mpu6050_state *st = iio_priv(indio_dev); mutex_lock(&st->lock); - /* It doesn't really mattter, if any of the calls fails */ - regmap_write(st->map, st->reg->int_pin_cfg, INV_MPU6050_INT_PIN_CFG); - st->powerup_count--; - if (!st->powerup_count) - regmap_write(st->map, st->reg->pwr_mgmt_1, - INV_MPU6050_BIT_SLEEP); + + /* It doesn't really matter if any of the calls fail */ + regmap_write(st->map, st->reg->int_pin_cfg, st->irq_mask); + inv_mpu6050_set_power_itg(st, false); + mutex_unlock(&st->lock); return 0; @@ -133,29 +125,32 @@ static int inv_mpu_probe(struct i2c_client *client, return result; st = iio_priv(dev_get_drvdata(&client->dev)); - st->muxc = i2c_mux_alloc(client->adapter, &client->dev, - 1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE, - inv_mpu6050_select_bypass, - inv_mpu6050_deselect_bypass); - if (!st->muxc) { - result = -ENOMEM; - goto out_unreg_device; + switch (st->chip_type) { + case INV_ICM20608: + /* no i2c auxiliary bus on the chip */ + break; + default: + /* declare i2c auxiliary bus */ + st->muxc = i2c_mux_alloc(client->adapter, &client->dev, + 1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE, + inv_mpu6050_select_bypass, + inv_mpu6050_deselect_bypass); + if (!st->muxc) + return -ENOMEM; + st->muxc->priv = dev_get_drvdata(&client->dev); + result = i2c_mux_add_adapter(st->muxc, 0, 0, 0); + if (result) + return result; + result = inv_mpu_acpi_create_mux_client(client); + if (result) + goto out_del_mux; + break; } - st->muxc->priv = dev_get_drvdata(&client->dev); - result = i2c_mux_add_adapter(st->muxc, 0, 0, 0); - if (result) - goto out_unreg_device; - - result = inv_mpu_acpi_create_mux_client(client); - if (result) - goto out_del_mux; return 0; out_del_mux: i2c_mux_del_adapters(st->muxc); -out_unreg_device: - inv_mpu_core_remove(&client->dev); return result; } @@ -164,10 +159,12 @@ static int inv_mpu_remove(struct i2c_client *client) struct iio_dev *indio_dev = i2c_get_clientdata(client); struct inv_mpu6050_state *st = iio_priv(indio_dev); - inv_mpu_acpi_delete_mux_client(client); - i2c_mux_del_adapters(st->muxc); + if (st->muxc) { + inv_mpu_acpi_delete_mux_client(client); + i2c_mux_del_adapters(st->muxc); + } - return inv_mpu_core_remove(&client->dev); + return 0; } /* @@ -179,6 +176,7 @@ static const struct i2c_device_id inv_mpu_id[] = { {"mpu6500", INV_MPU6500}, {"mpu9150", INV_MPU9150}, {"mpu9250", INV_MPU9250}, + {"mpu9255", INV_MPU9255}, {"icm20608", INV_ICM20608}, {} }; @@ -203,6 +201,10 @@ static const struct of_device_id inv_of_match[] = { .data = (void *)INV_MPU9250 }, { + .compatible = "invensense,mpu9255", + .data = (void *)INV_MPU9255 + }, + { .compatible = "invensense,icm20608", .data = (void *)INV_ICM20608 }, diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index 065794162d65..c54da777945d 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -40,6 +40,7 @@ * @raw_accl: Address of first accel register. * @temperature: temperature register * @int_enable: Interrupt enable register. + * @int_status: Interrupt status register. * @pwr_mgmt_1: Controls chip's power state and clock source. * @pwr_mgmt_2: Controls power state of individual sensors. * @int_pin_cfg; Controls interrupt pin configuration. @@ -60,6 +61,7 @@ struct inv_mpu6050_reg_map { u8 raw_accl; u8 temperature; u8 int_enable; + u8 int_status; u8 pwr_mgmt_1; u8 pwr_mgmt_2; u8 int_pin_cfg; @@ -74,6 +76,7 @@ enum inv_devices { INV_MPU6000, INV_MPU9150, INV_MPU9250, + INV_MPU9255, INV_ICM20608, INV_NUM_PARTS }; @@ -94,6 +97,7 @@ struct inv_mpu6050_chip_config { unsigned int accl_fifo_enable:1; unsigned int gyro_fifo_enable:1; u16 fifo_rate; + u8 user_ctrl; }; /** @@ -125,6 +129,7 @@ struct inv_mpu6050_hw { * @timestamps: kfifo queue to store time stamp. * @map regmap pointer. * @irq interrupt number. + * @irq_mask the int_pin_cfg mask to configure interrupt type. */ struct inv_mpu6050_state { #define TIMESTAMP_FIFO_SIZE 16 @@ -143,6 +148,8 @@ struct inv_mpu6050_state { DECLARE_KFIFO(timestamps, long long, TIMESTAMP_FIFO_SIZE); struct regmap *map; int irq; + u8 irq_mask; + unsigned skip_samples; }; /*register and associated bit definition*/ @@ -166,6 +173,9 @@ struct inv_mpu6050_state { #define INV_MPU6050_REG_TEMPERATURE 0x41 #define INV_MPU6050_REG_RAW_GYRO 0x43 +#define INV_MPU6050_REG_INT_STATUS 0x3A +#define INV_MPU6050_BIT_RAW_DATA_RDY_INT 0x01 + #define INV_MPU6050_REG_USER_CTRL 0x6A #define INV_MPU6050_BIT_FIFO_RST 0x04 #define INV_MPU6050_BIT_DMP_RST 0x08 @@ -215,8 +225,12 @@ struct inv_mpu6050_state { #define INV_MPU6050_OUTPUT_DATA_SIZE 24 #define INV_MPU6050_REG_INT_PIN_CFG 0x37 +#define INV_MPU6050_ACTIVE_HIGH 0x00 +#define INV_MPU6050_ACTIVE_LOW 0x80 +/* enable level triggering */ +#define INV_MPU6050_LATCH_INT_EN 0x20 #define INV_MPU6050_BIT_BYPASS_EN 0x2 -#define INV_MPU6050_INT_PIN_CFG 0 + /* init parameters */ #define INV_MPU6050_INIT_FIFO_RATE 50 @@ -232,6 +246,7 @@ struct inv_mpu6050_state { #define INV_MPU6500_WHOAMI_VALUE 0x70 #define INV_MPU9150_WHOAMI_VALUE 0x68 #define INV_MPU9250_WHOAMI_VALUE 0x71 +#define INV_MPU9255_WHOAMI_VALUE 0x73 #define INV_ICM20608_WHOAMI_VALUE 0xAF /* scan element definition */ @@ -287,8 +302,7 @@ enum inv_mpu6050_clock_sel_e { irqreturn_t inv_mpu6050_irq_handler(int irq, void *p); irqreturn_t inv_mpu6050_read_fifo(int irq, void *p); -int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev); -void inv_mpu6050_remove_trigger(struct inv_mpu6050_state *st); +int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev, int irq_type); int inv_reset_fifo(struct iio_dev *indio_dev); int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask); int inv_mpu6050_write_reg(struct inv_mpu6050_state *st, int reg, u8 val); @@ -297,6 +311,4 @@ int inv_mpu_acpi_create_mux_client(struct i2c_client *client); void inv_mpu_acpi_delete_mux_client(struct i2c_client *client); int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, int (*inv_mpu_bus_setup)(struct iio_dev *), int chip_type); -int inv_mpu_core_remove(struct device *dev); -int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on); extern const struct dev_pm_ops inv_mpu_pmops; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c index ff81c6aa009d..97d965181635 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c @@ -51,13 +51,14 @@ int inv_reset_fifo(struct iio_dev *indio_dev) if (result) goto reset_fifo_fail; /* disable fifo reading */ - result = regmap_write(st->map, st->reg->user_ctrl, 0); + result = regmap_write(st->map, st->reg->user_ctrl, + st->chip_config.user_ctrl); if (result) goto reset_fifo_fail; /* reset FIFO*/ - result = regmap_write(st->map, st->reg->user_ctrl, - INV_MPU6050_BIT_FIFO_RST); + d = st->chip_config.user_ctrl | INV_MPU6050_BIT_FIFO_RST; + result = regmap_write(st->map, st->reg->user_ctrl, d); if (result) goto reset_fifo_fail; @@ -72,9 +73,9 @@ int inv_reset_fifo(struct iio_dev *indio_dev) if (result) return result; } - /* enable FIFO reading and I2C master interface*/ - result = regmap_write(st->map, st->reg->user_ctrl, - INV_MPU6050_BIT_FIFO_EN); + /* enable FIFO reading */ + d = st->chip_config.user_ctrl | INV_MPU6050_BIT_FIFO_EN; + result = regmap_write(st->map, st->reg->user_ctrl, d); if (result) goto reset_fifo_fail; /* enable sensor output to FIFO */ @@ -127,8 +128,23 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p) u8 data[INV_MPU6050_OUTPUT_DATA_SIZE]; u16 fifo_count; s64 timestamp; + int int_status; mutex_lock(&st->lock); + + /* ack interrupt and check status */ + result = regmap_read(st->map, st->reg->int_status, &int_status); + if (result) { + dev_err(regmap_get_device(st->map), + "failed to ack interrupt\n"); + goto flush_fifo; + } + if (!(int_status & INV_MPU6050_BIT_RAW_DATA_RDY_INT)) { + dev_warn(regmap_get_device(st->map), + "spurious interrupt with status 0x%x\n", int_status); + goto end_session; + } + if (!(st->chip_config.accl_fifo_enable | st->chip_config.gyro_fifo_enable)) goto end_session; @@ -140,7 +156,7 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p) bytes_per_datum += INV_MPU6050_BYTES_PER_3AXIS_SENSOR; /* - * read fifo_count register to know how many bytes inside FIFO + * read fifo_count register to know how many bytes are inside the FIFO * right now */ result = regmap_bulk_read(st->map, st->reg->fifo_count_h, data, @@ -150,7 +166,7 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p) fifo_count = be16_to_cpup((__be16 *)(&data[0])); if (fifo_count < bytes_per_datum) goto end_session; - /* fifo count can't be odd number, if it is odd, reset fifo*/ + /* fifo count can't be an odd number. If it is odd, reset the FIFO. */ if (fifo_count & 1) goto flush_fifo; if (fifo_count > INV_MPU6050_FIFO_THRESHOLD) @@ -170,10 +186,13 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p) if (result == 0) timestamp = 0; - result = iio_push_to_buffers_with_timestamp(indio_dev, data, - timestamp); - if (result) - goto flush_fifo; + /* skip first samples if needed */ + if (st->skip_samples) + st->skip_samples--; + else + iio_push_to_buffers_with_timestamp(indio_dev, data, + timestamp); + fifo_count -= bytes_per_datum; } diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c index 74506e5ac0db..227f50afff22 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c @@ -31,8 +31,9 @@ static int inv_mpu_i2c_disable(struct iio_dev *indio_dev) if (ret) return ret; - ret = regmap_write(st->map, INV_MPU6050_REG_USER_CTRL, - INV_MPU6050_BIT_I2C_IF_DIS); + st->chip_config.user_ctrl |= INV_MPU6050_BIT_I2C_IF_DIS; + ret = regmap_write(st->map, st->reg->user_ctrl, + st->chip_config.user_ctrl); if (ret) { inv_mpu6050_set_power_itg(st, false); return ret; @@ -69,11 +70,6 @@ static int inv_mpu_probe(struct spi_device *spi) inv_mpu_i2c_disable, chip_type); } -static int inv_mpu_remove(struct spi_device *spi) -{ - return inv_mpu_core_remove(&spi->dev); -} - /* * device id table is used to identify what device can be * supported by this driver @@ -83,6 +79,7 @@ static const struct spi_device_id inv_mpu_id[] = { {"mpu6500", INV_MPU6500}, {"mpu9150", INV_MPU9150}, {"mpu9250", INV_MPU9250}, + {"mpu9255", INV_MPU9255}, {"icm20608", INV_ICM20608}, {} }; @@ -97,7 +94,6 @@ MODULE_DEVICE_TABLE(acpi, inv_acpi_match); static struct spi_driver inv_mpu_driver = { .probe = inv_mpu_probe, - .remove = inv_mpu_remove, .id_table = inv_mpu_id, .driver = { .acpi_match_table = ACPI_PTR(inv_acpi_match), diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c index f963f9fc98c0..6c3e1652a687 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c @@ -49,49 +49,66 @@ static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable) if (result) return result; inv_scan_query(indio_dev); + st->skip_samples = 0; if (st->chip_config.gyro_fifo_enable) { result = inv_mpu6050_switch_engine(st, true, INV_MPU6050_BIT_PWR_GYRO_STBY); if (result) - return result; + goto error_power_off; + /* gyro first sample is out of specs, skip it */ + st->skip_samples = 1; } if (st->chip_config.accl_fifo_enable) { result = inv_mpu6050_switch_engine(st, true, INV_MPU6050_BIT_PWR_ACCL_STBY); if (result) - return result; + goto error_gyro_off; } result = inv_reset_fifo(indio_dev); if (result) - return result; + goto error_accl_off; } else { result = regmap_write(st->map, st->reg->fifo_en, 0); if (result) - return result; + goto error_accl_off; result = regmap_write(st->map, st->reg->int_enable, 0); if (result) - return result; + goto error_accl_off; - result = regmap_write(st->map, st->reg->user_ctrl, 0); + result = regmap_write(st->map, st->reg->user_ctrl, + st->chip_config.user_ctrl); if (result) - return result; + goto error_accl_off; result = inv_mpu6050_switch_engine(st, false, - INV_MPU6050_BIT_PWR_GYRO_STBY); + INV_MPU6050_BIT_PWR_ACCL_STBY); if (result) - return result; + goto error_accl_off; result = inv_mpu6050_switch_engine(st, false, - INV_MPU6050_BIT_PWR_ACCL_STBY); + INV_MPU6050_BIT_PWR_GYRO_STBY); if (result) - return result; + goto error_gyro_off; + result = inv_mpu6050_set_power_itg(st, false); if (result) - return result; + goto error_power_off; } return 0; + +error_accl_off: + if (st->chip_config.accl_fifo_enable) + inv_mpu6050_switch_engine(st, false, + INV_MPU6050_BIT_PWR_ACCL_STBY); +error_gyro_off: + if (st->chip_config.gyro_fifo_enable) + inv_mpu6050_switch_engine(st, false, + INV_MPU6050_BIT_PWR_GYRO_STBY); +error_power_off: + inv_mpu6050_set_power_itg(st, false); + return result; } /** @@ -117,7 +134,7 @@ static const struct iio_trigger_ops inv_mpu_trigger_ops = { .set_trigger_state = &inv_mpu_data_rdy_trigger_set_state, }; -int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev) +int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev, int irq_type) { int ret; struct inv_mpu6050_state *st = iio_priv(indio_dev); @@ -131,7 +148,7 @@ int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev) ret = devm_request_irq(&indio_dev->dev, st->irq, &iio_trigger_generic_data_rdy_poll, - IRQF_TRIGGER_RISING, + irq_type, "inv_mpu", st->trig); if (ret) @@ -141,7 +158,7 @@ int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev) st->trig->ops = &inv_mpu_trigger_ops; iio_trigger_set_drvdata(st->trig, indio_dev); - ret = iio_trigger_register(st->trig); + ret = devm_iio_trigger_register(&indio_dev->dev, st->trig); if (ret) return ret; @@ -149,8 +166,3 @@ int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev) return 0; } - -void inv_mpu6050_remove_trigger(struct inv_mpu6050_state *st) -{ - iio_trigger_unregister(st->trig); -} diff --git a/drivers/iio/imu/st_lsm6dsx/Kconfig b/drivers/iio/imu/st_lsm6dsx/Kconfig index 14f2eb6e9fb7..ccc817e17eb8 100644 --- a/drivers/iio/imu/st_lsm6dsx/Kconfig +++ b/drivers/iio/imu/st_lsm6dsx/Kconfig @@ -8,7 +8,8 @@ config IIO_ST_LSM6DSX select IIO_ST_LSM6DSX_SPI if (SPI_MASTER) help Say yes here to build support for STMicroelectronics LSM6DSx imu - sensor. Supported devices: lsm6ds3, lsm6ds3h, lsm6dsl, lsm6dsm + sensor. Supported devices: lsm6ds3, lsm6ds3h, lsm6dsl, lsm6dsm, + ism330dlc To compile this driver as a module, choose M here: the module will be called st_lsm6dsx. diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h index a3cc7cd97026..edcd838037cd 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h @@ -18,12 +18,14 @@ #define ST_LSM6DS3H_DEV_NAME "lsm6ds3h" #define ST_LSM6DSL_DEV_NAME "lsm6dsl" #define ST_LSM6DSM_DEV_NAME "lsm6dsm" +#define ST_ISM330DLC_DEV_NAME "ism330dlc" enum st_lsm6dsx_hw_id { ST_LSM6DS3_ID, ST_LSM6DS3H_ID, ST_LSM6DSL_ID, ST_LSM6DSM_ID, + ST_ISM330DLC_ID, ST_LSM6DSX_MAX_ID, }; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c index 1045e025e92b..4994f920a836 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c @@ -1,10 +1,10 @@ /* * STMicroelectronics st_lsm6dsx FIFO buffer library driver * - * LSM6DS3/LSM6DS3H/LSM6DSL/LSM6DSM: The FIFO buffer can be configured - * to store data from gyroscope and accelerometer. Samples are queued - * without any tag according to a specific pattern based on 'FIFO data sets' - * (6 bytes each): + * LSM6DS3/LSM6DS3H/LSM6DSL/LSM6DSM/ISM330DLC: The FIFO buffer can be + * configured to store data from gyroscope and accelerometer. Samples are + * queued without any tag according to a specific pattern based on + * 'FIFO data sets' (6 bytes each): * - 1st data set is reserved for gyroscope data * - 2nd data set is reserved for accelerometer data * The FIFO pattern changes depending on the ODRs and decimation factors @@ -276,7 +276,7 @@ static inline int st_lsm6dsx_read_block(struct st_lsm6dsx_hw *hw, u8 *data, #define ST_LSM6DSX_IIO_BUFF_SIZE (ALIGN(ST_LSM6DSX_SAMPLE_SIZE, \ sizeof(s64)) + sizeof(s64)) /** - * st_lsm6dsx_read_fifo() - LSM6DS3-LSM6DS3H-LSM6DSL-LSM6DSM read FIFO routine + * st_lsm6dsx_read_fifo() - hw FIFO read routine * @hw: Pointer to instance of struct st_lsm6dsx_hw. * * Read samples from the hw FIFO and push them to IIO buffers. diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index 8656d72ef4ee..aebbe0ddd8d8 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -17,7 +17,7 @@ * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000 * - FIFO size: 8KB * - * - LSM6DS3H/LSM6DSL/LSM6DSM: + * - LSM6DS3H/LSM6DSL/LSM6DSM/ISM330DLC: * - Accelerometer/Gyroscope supported ODR [Hz]: 13, 26, 52, 104, 208, 416 * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16 * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000 @@ -252,6 +252,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .id = { [0] = ST_LSM6DSL_ID, [1] = ST_LSM6DSM_ID, + [2] = ST_ISM330DLC_ID, }, .decimator = { [ST_LSM6DSX_ID_ACC] = { @@ -266,11 +267,11 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .fifo_ops = { .fifo_th = { .addr = 0x06, - .mask = GENMASK(11, 0), + .mask = GENMASK(10, 0), }, .fifo_diff = { .addr = 0x3a, - .mask = GENMASK(11, 0), + .mask = GENMASK(10, 0), }, .th_wl = 3, /* 1LSB = 2B */ }, diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c index 41525dd2aab7..377c4e9997da 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c @@ -57,6 +57,10 @@ static const struct of_device_id st_lsm6dsx_i2c_of_match[] = { .compatible = "st,lsm6dsm", .data = (void *)ST_LSM6DSM_ID, }, + { + .compatible = "st,ism330dlc", + .data = (void *)ST_ISM330DLC_ID, + }, {}, }; MODULE_DEVICE_TABLE(of, st_lsm6dsx_i2c_of_match); @@ -66,6 +70,7 @@ static const struct i2c_device_id st_lsm6dsx_i2c_id_table[] = { { ST_LSM6DS3H_DEV_NAME, ST_LSM6DS3H_ID }, { ST_LSM6DSL_DEV_NAME, ST_LSM6DSL_ID }, { ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID }, + { ST_ISM330DLC_DEV_NAME, ST_ISM330DLC_ID }, {}, }; MODULE_DEVICE_TABLE(i2c, st_lsm6dsx_i2c_id_table); diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c index 2c8135834479..fec5c6ce7eb7 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c @@ -57,6 +57,10 @@ static const struct of_device_id st_lsm6dsx_spi_of_match[] = { .compatible = "st,lsm6dsm", .data = (void *)ST_LSM6DSM_ID, }, + { + .compatible = "st,ism330dlc", + .data = (void *)ST_ISM330DLC_ID, + }, {}, }; MODULE_DEVICE_TABLE(of, st_lsm6dsx_spi_of_match); @@ -66,6 +70,7 @@ static const struct spi_device_id st_lsm6dsx_spi_id_table[] = { { ST_LSM6DS3H_DEV_NAME, ST_LSM6DS3H_ID }, { ST_LSM6DSL_DEV_NAME, ST_LSM6DSL_ID }, { ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID }, + { ST_ISM330DLC_DEV_NAME, ST_ISM330DLC_ID }, {}, }; MODULE_DEVICE_TABLE(spi, st_lsm6dsx_spi_id_table); diff --git a/drivers/iio/light/cros_ec_light_prox.c b/drivers/iio/light/cros_ec_light_prox.c index 8e8a0e7f78d1..fd1609e975ab 100644 --- a/drivers/iio/light/cros_ec_light_prox.c +++ b/drivers/iio/light/cros_ec_light_prox.c @@ -16,6 +16,7 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/iio/buffer.h> +#include <linux/iio/common/cros_ec_sensors_core.h> #include <linux/iio/iio.h> #include <linux/iio/kfifo_buf.h> #include <linux/iio/trigger.h> @@ -29,8 +30,6 @@ #include <linux/slab.h> #include <linux/sysfs.h> -#include "../common/cros_ec_sensors/cros_ec_sensors_core.h" - /* * We only represent one entry for light or proximity. EC is merging different * light sensors to return the what the eye would see. For proximity, we diff --git a/drivers/iio/potentiometer/mcp4018.c b/drivers/iio/potentiometer/mcp4018.c index 601b25d1f387..320a7c929777 100644 --- a/drivers/iio/potentiometer/mcp4018.c +++ b/drivers/iio/potentiometer/mcp4018.c @@ -99,6 +99,23 @@ static const struct iio_info mcp4018_info = { .write_raw = mcp4018_write_raw, }; +static const struct i2c_device_id mcp4018_id[] = { + { "mcp4017-502", MCP4018_502 }, + { "mcp4017-103", MCP4018_103 }, + { "mcp4017-503", MCP4018_503 }, + { "mcp4017-104", MCP4018_104 }, + { "mcp4018-502", MCP4018_502 }, + { "mcp4018-103", MCP4018_103 }, + { "mcp4018-503", MCP4018_503 }, + { "mcp4018-104", MCP4018_104 }, + { "mcp4019-502", MCP4018_502 }, + { "mcp4019-103", MCP4018_103 }, + { "mcp4019-503", MCP4018_503 }, + { "mcp4019-104", MCP4018_104 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, mcp4018_id); + #ifdef CONFIG_OF #define MCP4018_COMPATIBLE(of_compatible, cfg) { \ @@ -125,8 +142,7 @@ MODULE_DEVICE_TABLE(of, mcp4018_of_match); #endif -static int mcp4018_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int mcp4018_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct mcp4018_data *data; @@ -150,7 +166,7 @@ static int mcp4018_probe(struct i2c_client *client, if (match) data->cfg = of_device_get_match_data(dev); else - data->cfg = &mcp4018_cfg[id->driver_data]; + data->cfg = &mcp4018_cfg[i2c_match_id(mcp4018_id, client)->driver_data]; indio_dev->dev.parent = dev; indio_dev->info = &mcp4018_info; @@ -161,29 +177,12 @@ static int mcp4018_probe(struct i2c_client *client, return devm_iio_device_register(dev, indio_dev); } -static const struct i2c_device_id mcp4018_id[] = { - { "mcp4017-502", MCP4018_502 }, - { "mcp4017-103", MCP4018_103 }, - { "mcp4017-503", MCP4018_503 }, - { "mcp4017-104", MCP4018_104 }, - { "mcp4018-502", MCP4018_502 }, - { "mcp4018-103", MCP4018_103 }, - { "mcp4018-503", MCP4018_503 }, - { "mcp4018-104", MCP4018_104 }, - { "mcp4019-502", MCP4018_502 }, - { "mcp4019-103", MCP4018_103 }, - { "mcp4019-503", MCP4018_503 }, - { "mcp4019-104", MCP4018_104 }, - {} -}; -MODULE_DEVICE_TABLE(i2c, mcp4018_id); - static struct i2c_driver mcp4018_driver = { .driver = { .name = "mcp4018", .of_match_table = of_match_ptr(mcp4018_of_match), }, - .probe = mcp4018_probe, + .probe_new = mcp4018_probe, .id_table = mcp4018_id, }; diff --git a/drivers/iio/potentiometer/mcp4531.c b/drivers/iio/potentiometer/mcp4531.c index 114ab876fcc6..df894af6cccb 100644 --- a/drivers/iio/potentiometer/mcp4531.c +++ b/drivers/iio/potentiometer/mcp4531.c @@ -209,6 +209,75 @@ static const struct iio_info mcp4531_info = { .write_raw = mcp4531_write_raw, }; +static const struct i2c_device_id mcp4531_id[] = { + { "mcp4531-502", MCP453x_502 }, + { "mcp4531-103", MCP453x_103 }, + { "mcp4531-503", MCP453x_503 }, + { "mcp4531-104", MCP453x_104 }, + { "mcp4532-502", MCP453x_502 }, + { "mcp4532-103", MCP453x_103 }, + { "mcp4532-503", MCP453x_503 }, + { "mcp4532-104", MCP453x_104 }, + { "mcp4541-502", MCP454x_502 }, + { "mcp4541-103", MCP454x_103 }, + { "mcp4541-503", MCP454x_503 }, + { "mcp4541-104", MCP454x_104 }, + { "mcp4542-502", MCP454x_502 }, + { "mcp4542-103", MCP454x_103 }, + { "mcp4542-503", MCP454x_503 }, + { "mcp4542-104", MCP454x_104 }, + { "mcp4551-502", MCP455x_502 }, + { "mcp4551-103", MCP455x_103 }, + { "mcp4551-503", MCP455x_503 }, + { "mcp4551-104", MCP455x_104 }, + { "mcp4552-502", MCP455x_502 }, + { "mcp4552-103", MCP455x_103 }, + { "mcp4552-503", MCP455x_503 }, + { "mcp4552-104", MCP455x_104 }, + { "mcp4561-502", MCP456x_502 }, + { "mcp4561-103", MCP456x_103 }, + { "mcp4561-503", MCP456x_503 }, + { "mcp4561-104", MCP456x_104 }, + { "mcp4562-502", MCP456x_502 }, + { "mcp4562-103", MCP456x_103 }, + { "mcp4562-503", MCP456x_503 }, + { "mcp4562-104", MCP456x_104 }, + { "mcp4631-502", MCP463x_502 }, + { "mcp4631-103", MCP463x_103 }, + { "mcp4631-503", MCP463x_503 }, + { "mcp4631-104", MCP463x_104 }, + { "mcp4632-502", MCP463x_502 }, + { "mcp4632-103", MCP463x_103 }, + { "mcp4632-503", MCP463x_503 }, + { "mcp4632-104", MCP463x_104 }, + { "mcp4641-502", MCP464x_502 }, + { "mcp4641-103", MCP464x_103 }, + { "mcp4641-503", MCP464x_503 }, + { "mcp4641-104", MCP464x_104 }, + { "mcp4642-502", MCP464x_502 }, + { "mcp4642-103", MCP464x_103 }, + { "mcp4642-503", MCP464x_503 }, + { "mcp4642-104", MCP464x_104 }, + { "mcp4651-502", MCP465x_502 }, + { "mcp4651-103", MCP465x_103 }, + { "mcp4651-503", MCP465x_503 }, + { "mcp4651-104", MCP465x_104 }, + { "mcp4652-502", MCP465x_502 }, + { "mcp4652-103", MCP465x_103 }, + { "mcp4652-503", MCP465x_503 }, + { "mcp4652-104", MCP465x_104 }, + { "mcp4661-502", MCP466x_502 }, + { "mcp4661-103", MCP466x_103 }, + { "mcp4661-503", MCP466x_503 }, + { "mcp4661-104", MCP466x_104 }, + { "mcp4662-502", MCP466x_502 }, + { "mcp4662-103", MCP466x_103 }, + { "mcp4662-503", MCP466x_503 }, + { "mcp4662-104", MCP466x_104 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, mcp4531_id); + #ifdef CONFIG_OF #define MCP4531_COMPATIBLE(of_compatible, cfg) { \ @@ -286,8 +355,7 @@ static const struct of_device_id mcp4531_of_match[] = { MODULE_DEVICE_TABLE(of, mcp4531_of_match); #endif -static int mcp4531_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int mcp4531_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct mcp4531_data *data; @@ -311,7 +379,7 @@ static int mcp4531_probe(struct i2c_client *client, if (match) data->cfg = of_device_get_match_data(dev); else - data->cfg = &mcp4531_cfg[id->driver_data]; + data->cfg = &mcp4531_cfg[i2c_match_id(mcp4531_id, client)->driver_data]; indio_dev->dev.parent = dev; indio_dev->info = &mcp4531_info; @@ -322,81 +390,12 @@ static int mcp4531_probe(struct i2c_client *client, return devm_iio_device_register(dev, indio_dev); } -static const struct i2c_device_id mcp4531_id[] = { - { "mcp4531-502", MCP453x_502 }, - { "mcp4531-103", MCP453x_103 }, - { "mcp4531-503", MCP453x_503 }, - { "mcp4531-104", MCP453x_104 }, - { "mcp4532-502", MCP453x_502 }, - { "mcp4532-103", MCP453x_103 }, - { "mcp4532-503", MCP453x_503 }, - { "mcp4532-104", MCP453x_104 }, - { "mcp4541-502", MCP454x_502 }, - { "mcp4541-103", MCP454x_103 }, - { "mcp4541-503", MCP454x_503 }, - { "mcp4541-104", MCP454x_104 }, - { "mcp4542-502", MCP454x_502 }, - { "mcp4542-103", MCP454x_103 }, - { "mcp4542-503", MCP454x_503 }, - { "mcp4542-104", MCP454x_104 }, - { "mcp4551-502", MCP455x_502 }, - { "mcp4551-103", MCP455x_103 }, - { "mcp4551-503", MCP455x_503 }, - { "mcp4551-104", MCP455x_104 }, - { "mcp4552-502", MCP455x_502 }, - { "mcp4552-103", MCP455x_103 }, - { "mcp4552-503", MCP455x_503 }, - { "mcp4552-104", MCP455x_104 }, - { "mcp4561-502", MCP456x_502 }, - { "mcp4561-103", MCP456x_103 }, - { "mcp4561-503", MCP456x_503 }, - { "mcp4561-104", MCP456x_104 }, - { "mcp4562-502", MCP456x_502 }, - { "mcp4562-103", MCP456x_103 }, - { "mcp4562-503", MCP456x_503 }, - { "mcp4562-104", MCP456x_104 }, - { "mcp4631-502", MCP463x_502 }, - { "mcp4631-103", MCP463x_103 }, - { "mcp4631-503", MCP463x_503 }, - { "mcp4631-104", MCP463x_104 }, - { "mcp4632-502", MCP463x_502 }, - { "mcp4632-103", MCP463x_103 }, - { "mcp4632-503", MCP463x_503 }, - { "mcp4632-104", MCP463x_104 }, - { "mcp4641-502", MCP464x_502 }, - { "mcp4641-103", MCP464x_103 }, - { "mcp4641-503", MCP464x_503 }, - { "mcp4641-104", MCP464x_104 }, - { "mcp4642-502", MCP464x_502 }, - { "mcp4642-103", MCP464x_103 }, - { "mcp4642-503", MCP464x_503 }, - { "mcp4642-104", MCP464x_104 }, - { "mcp4651-502", MCP465x_502 }, - { "mcp4651-103", MCP465x_103 }, - { "mcp4651-503", MCP465x_503 }, - { "mcp4651-104", MCP465x_104 }, - { "mcp4652-502", MCP465x_502 }, - { "mcp4652-103", MCP465x_103 }, - { "mcp4652-503", MCP465x_503 }, - { "mcp4652-104", MCP465x_104 }, - { "mcp4661-502", MCP466x_502 }, - { "mcp4661-103", MCP466x_103 }, - { "mcp4661-503", MCP466x_503 }, - { "mcp4661-104", MCP466x_104 }, - { "mcp4662-502", MCP466x_502 }, - { "mcp4662-103", MCP466x_103 }, - { "mcp4662-503", MCP466x_503 }, - { "mcp4662-104", MCP466x_104 }, - {} -}; -MODULE_DEVICE_TABLE(i2c, mcp4531_id); - static struct i2c_driver mcp4531_driver = { .driver = { .name = "mcp4531", .of_match_table = of_match_ptr(mcp4531_of_match), }, - .probe = mcp4531_probe, + .probe_new = mcp4531_probe, .id_table = mcp4531_id, }; diff --git a/drivers/iio/pressure/cros_ec_baro.c b/drivers/iio/pressure/cros_ec_baro.c index 4599fde4dd25..87c07af9181f 100644 --- a/drivers/iio/pressure/cros_ec_baro.c +++ b/drivers/iio/pressure/cros_ec_baro.c @@ -16,6 +16,7 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/iio/buffer.h> +#include <linux/iio/common/cros_ec_sensors_core.h> #include <linux/iio/iio.h> #include <linux/iio/kfifo_buf.h> #include <linux/iio/trigger.h> @@ -28,8 +29,6 @@ #include <linux/slab.h> #include <linux/platform_device.h> -#include "../common/cros_ec_sensors/cros_ec_sensors_core.h" - /* * One channel for pressure, the other for timestamp. */ |