diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2018-11-22 09:39:45 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2018-11-22 09:39:45 +0100 |
commit | 7c0bc65c84035cd2d7561ba47d7bad1cae62c4c3 (patch) | |
tree | dc3d2be87e28788beae297afecfa83a4846397b8 /drivers/iio | |
parent | 0e2c8fb54d7ae0c3e816dc8b1eedbeb345e9cdad (diff) | |
parent | 00426e99789357dbff7e719a092ce36a3ce49d94 (diff) | |
download | linux-7c0bc65c84035cd2d7561ba47d7bad1cae62c4c3.tar.bz2 |
Merge tag 'iio-for-4.21a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-testing
Jonathan writes:
First set of new device support, features and cleanups for IIO in the 4.21 cycle
Along with the headline feature of 5 new drivers, we have the
substantial addition of auxilliary sensor support on the lsm6sdx
parts for ST. There has also been a good set of staging cleanup
in this period with more underway.
An ever increasing number of devices supported with just a new
ID which is a good sign that at least some manufacturers are
continuing to stabilise their interfaces.
New device support,
* ad7124
- New driver supporting Analog Devices' ad7124-4 and ad7124-8 parts
with the inevitable DT binding.
* ad7949
- New driver supporting Analog Devices' ad7949, AD7682 and AD7689 ADCs.
* rm3100
- New driver supporting PNIs RM3100 magnometer with bindings and
vendor prefix.
* ti-dac7311
- New driver supporting DAC7311, DAC6311 and DAC5311 TI DACs, with
DT bindings.
* vcnl5035
- New driver supporting the light sensor part of the VCNL4035, with
DT bindings
Features,
* bindings
- Add a generic ADC channel binding as we keep reinventing this
wheel.
* adc128s052
- Add IDs for additional pin compatible parts.
- Add APCI ID seen on E3940 UP squared boards.
* ad_sigma_delta
- Allow for custom data register overiding default.
* kxcjk1013
- Add KIOX0009 ACPI ID as seen on the Acer One 10.
* lsm6dsx
- Rework leading to...
- External sensor support using the built in I2C master.
- Initial support for a slave lis2mdl magnetometer.
* meson-saradc
- Add temperature sensor support and bindings.
* st_magn
- New ID for lsm9dsl_magn with bindings
- New ID for lis3de accelerometer
* tpl0102
- Add supprot for IIO_AVAIL_RANGE to report the range available
from this device to userspace and in kernel users.
Cleanups and minor fixes
* tools
- Allow outside specification of CFLAGS
* ad2s90
- Handle and spi_read error.
- Handle spi_setup failure
- Drop a pointless assignment.
- Prevent a potentail race by moving device registration to after
all other setup.
- Add missing scale attribute.
- Add a sanity check on channel type before trying to read it.
* ad2s1210
- Move to modern gpio descriptors.
- Drop a gpioin flag which made no sense as far as we can tell.
- Add dt table (bindings doc to follow when this is ready for
moving out of staging).
* ad5933
- Drop camel-case naming of ext_clk_hz.
- White space fixes.
* ad7150
- Local variable to shorten overly long line.
- Alignment and line break fixes.
* ad7280a
- Handle an error path that was previously ignored.
- Use crc8.h to build the crc table replacing custom code.
- Avoid unecessary cast.
- Power down the device if an error happens in probe
- Use devm routines to simplify probe and remove.
* ad7606
- Alignment fixes.
* ad7780
- This worked as long as by coincidence an uninitialized value
was 0. Lets not rely on that.
- Ensure gain update is only used with the ad778x chips that
actually support it.
- Tidy up pattern mask generation.
- Read regulator when scale is requested (which should be infrequent)
as it might have changed from initialization.
* ad7816
- Move to modern gpio descriptors
- Don't use a busy_pin for ad7818 as there isn't one.
- Ensure RD/WR and CONVST pins are outputs (previously they
were brought up as inputs which doesn't seem to make any sense)
- DT id table.
* adc128s052
- SPDX
* adt7316
- Alignment fix.
- Fix data reading. When using I2C the driver never actually
used the value read. This has been broken a very long time
hence no rush to fix it now + the driver is undergoing a lot
of cleanup.
- Sanity check that the i2c read didn't fail to actually read
anything.
* dpot-dac
- Mark a switch full through with slightly different text so that
gcc doesn't warn on it.
* gyro-adc
- Fix a wrong file in the MAINTAINERS entry and add binding doc to the
listed files.
* ina2xx
- Add some early returns to clarify error paths in switch.
* lsm6dsx
- MAINTAINERS entry.
* max11100
- SPDX
* max9611
- SPDX
* mcp4131
- use of_device_get_match_data in preference to spi_get_device_id
approach.
* rcar-adc
- SPDX
* sc27xx
- Add ADC conversion timeout support to avoid possible fault.
* ssp_sensors
- Don't free managed resources manually.
* st-magn
- Add a comment to avoid future confusion over when to use -magn
postfix (on multi chip in package parts)
- Add BDU register for LIS3MDL where it seems to have been missed.
* st-sensors
- Minor spelling, grammar etc fixes.
* tpl0102
- Use a pointer rather than an index of an array to improve conciseness.
* tag 'iio-for-4.21a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio: (80 commits)
Staging: iio: adt7316: Add an extra check for 'ret' equals to 0
Staging: iio: adt7316: Fix i2c data reading, set the data field
dt-bindings: iio: adc: Add docs for ad7124
iio: adc: Add ad7124 support
dt-bindings: iio: adc: Add common ADCs properties to a separate file
iio: ad_sigma_delta: Allow to provide custom data register address
staging: iio: ad7816: Add device tree table.
iio: imu: st_lsm6dsx: add entry in MAINTAINERS file
iio: potentiometer: mcp4131: use of_device_get_match_data()
staging: iio: adc: ad7280a: use devm_* APIs
staging: iio: adc: ad7280a: power down the device on error in probe
dt-bindings: iio: imu: st_lsm6dsx: add support to i2c pullup resistors
iio: imu: st_lsm6dsx: add hw FIFO support to i2c controller
iio: imu: st_lsm6dsx: add st_lsm6dsx_push_tagged_data routine
iio: imu: st_lsm6dsx: add i2c embedded controller support
iio: imu: st_lsm6dsx: introduce st_lsm6dsx_sensor_set_enable routine
iio: imu: st_lsm6dsx: introduce ST_LSM6DSX_ID_EXT sensor ids
iio: imu: st_lsm6dsx: remove static from st_lsm6dsx_set_watermark
iio: imu: st_lsm6dsx: reload trimming parameter at bootstrap
iio: imu: st_lsm6dsx: introduce locked read/write utility routines
...
Diffstat (limited to 'drivers/iio')
45 files changed, 4485 insertions, 232 deletions
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index 7993a67bd351..898839ca164a 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -223,7 +223,7 @@ config IIO_ST_ACCEL_3AXIS Say yes here to build support for STMicroelectronics accelerometers: LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LIS331DLH, LSM303DL, LSM303DLM, LSM330, LIS2DH12, H3LIS331DL, - LNG2DM + LNG2DM, LIS3DE This driver can also be built as a module. If so, these modules will be created: diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index af53a1084ee5..8600e4be88ad 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -1489,6 +1489,7 @@ static const struct acpi_device_id kx_acpi_match[] = { {"KXCJ1013", KXCJK1013}, {"KXCJ1008", KXCJ91008}, {"KXCJ9000", KXCJ91008}, + {"KIOX0009", KXTJ21009}, {"KIOX000A", KXCJ91008}, {"KXTJ1009", KXTJ21009}, {"SMO8500", KXCJ91008}, diff --git a/drivers/iio/accel/st_accel.h b/drivers/iio/accel/st_accel.h index 2f931e4837e5..fd53258656ca 100644 --- a/drivers/iio/accel/st_accel.h +++ b/drivers/iio/accel/st_accel.h @@ -56,6 +56,7 @@ enum st_accel_type { #define LNG2DM_ACCEL_DEV_NAME "lng2dm" #define LIS2DW12_ACCEL_DEV_NAME "lis2dw12" #define LIS3DHH_ACCEL_DEV_NAME "lis3dhh" +#define LIS3DE_ACCEL_DEV_NAME "lis3de" /** * struct st_sensors_platform_data - default accel platform data diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index 3e6fd5a8ac5b..f7b471121508 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -103,6 +103,7 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { [4] = LSM330DLC_ACCEL_DEV_NAME, [5] = LSM303AGR_ACCEL_DEV_NAME, [6] = LIS2DH12_ACCEL_DEV_NAME, + [7] = LIS3DE_ACCEL_DEV_NAME, }, .ch = (struct iio_chan_spec *)st_accel_12bit_channels, .odr = { diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c index 2ca5d1f6ade0..de8ae4327094 100644 --- a/drivers/iio/accel/st_accel_i2c.c +++ b/drivers/iio/accel/st_accel_i2c.c @@ -98,6 +98,10 @@ static const struct of_device_id st_accel_of_match[] = { .compatible = "st,lis2dw12", .data = LIS2DW12_ACCEL_DEV_NAME, }, + { + .compatible = "st,lis3de", + .data = LIS3DE_ACCEL_DEV_NAME, + }, {}, }; MODULE_DEVICE_TABLE(of, st_accel_of_match); @@ -135,6 +139,7 @@ static const struct i2c_device_id st_accel_id_table[] = { { LIS331DL_ACCEL_DEV_NAME }, { LIS3LV02DL_ACCEL_DEV_NAME }, { LIS2DW12_ACCEL_DEV_NAME }, + { LIS3DE_ACCEL_DEV_NAME }, {}, }; MODULE_DEVICE_TABLE(i2c, st_accel_id_table); diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c index dcc9bd243a52..73bfb5d04e2b 100644 --- a/drivers/iio/accel/st_accel_spi.c +++ b/drivers/iio/accel/st_accel_spi.c @@ -90,6 +90,10 @@ static const struct of_device_id st_accel_of_match[] = { .compatible = "st,lis3dhh", .data = LIS3DHH_ACCEL_DEV_NAME, }, + { + .compatible = "st,lis3de", + .data = LIS3DE_ACCEL_DEV_NAME, + }, {} }; MODULE_DEVICE_TABLE(of, st_accel_of_match); @@ -143,6 +147,7 @@ static const struct spi_device_id st_accel_id_table[] = { { LIS3LV02DL_ACCEL_DEV_NAME }, { LIS2DW12_ACCEL_DEV_NAME }, { LIS3DHH_ACCEL_DEV_NAME }, + { LIS3DE_ACCEL_DEV_NAME }, {}, }; MODULE_DEVICE_TABLE(spi, st_accel_id_table); diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index a52fea8749a9..da9644b50846 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -10,6 +10,17 @@ config AD_SIGMA_DELTA select IIO_BUFFER select IIO_TRIGGERED_BUFFER +config AD7124 + tristate "Analog Devices AD7124 and similar sigma-delta ADCs driver" + depends on SPI_MASTER + select AD_SIGMA_DELTA + help + Say yes here to build support for Analog Devices AD7124-4 and AD7124-8 + SPI analog to digital converters (ADC). + + To compile this driver as a module, choose M here: the module will be + called ad7124. + config AD7266 tristate "Analog Devices AD7265/AD7266 ADC driver" depends on SPI_MASTER @@ -116,6 +127,16 @@ config AD7923 To compile this driver as a module, choose M here: the module will be called ad7923. +config AD7949 + tristate "Analog Devices AD7949 and similar ADCs driver" + depends on SPI + help + Say yes here to build support for Analog Devices + AD7949, AD7682, AD7689 8 Channel ADCs. + + To compile this driver as a module, choose M here: the + module will be called ad7949. + config AD799X tristate "Analog Devices AD799x ADC driver" depends on I2C diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index a6e6a0b659e2..07df37f621bd 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -5,6 +5,7 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o +obj-$(CONFIG_AD7124) += ad7124.o obj-$(CONFIG_AD7266) += ad7266.o obj-$(CONFIG_AD7291) += ad7291.o obj-$(CONFIG_AD7298) += ad7298.o @@ -14,6 +15,7 @@ obj-$(CONFIG_AD7766) += ad7766.o obj-$(CONFIG_AD7791) += ad7791.o obj-$(CONFIG_AD7793) += ad7793.o obj-$(CONFIG_AD7887) += ad7887.o +obj-$(CONFIG_AD7949) += ad7949.o obj-$(CONFIG_AD799X) += ad799x.o obj-$(CONFIG_ASPEED_ADC) += aspeed_adc.o obj-$(CONFIG_AT91_ADC) += at91_adc.o diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c new file mode 100644 index 000000000000..7d5e5311d8de --- /dev/null +++ b/drivers/iio/adc/ad7124.c @@ -0,0 +1,684 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * AD7124 SPI ADC driver + * + * Copyright 2018 Analog Devices Inc. + */ +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> + +#include <linux/iio/iio.h> +#include <linux/iio/adc/ad_sigma_delta.h> +#include <linux/iio/sysfs.h> + +/* AD7124 registers */ +#define AD7124_COMMS 0x00 +#define AD7124_STATUS 0x00 +#define AD7124_ADC_CONTROL 0x01 +#define AD7124_DATA 0x02 +#define AD7124_IO_CONTROL_1 0x03 +#define AD7124_IO_CONTROL_2 0x04 +#define AD7124_ID 0x05 +#define AD7124_ERROR 0x06 +#define AD7124_ERROR_EN 0x07 +#define AD7124_MCLK_COUNT 0x08 +#define AD7124_CHANNEL(x) (0x09 + (x)) +#define AD7124_CONFIG(x) (0x19 + (x)) +#define AD7124_FILTER(x) (0x21 + (x)) +#define AD7124_OFFSET(x) (0x29 + (x)) +#define AD7124_GAIN(x) (0x31 + (x)) + +/* AD7124_STATUS */ +#define AD7124_STATUS_POR_FLAG_MSK BIT(4) + +/* AD7124_ADC_CONTROL */ +#define AD7124_ADC_CTRL_PWR_MSK GENMASK(7, 6) +#define AD7124_ADC_CTRL_PWR(x) FIELD_PREP(AD7124_ADC_CTRL_PWR_MSK, x) +#define AD7124_ADC_CTRL_MODE_MSK GENMASK(5, 2) +#define AD7124_ADC_CTRL_MODE(x) FIELD_PREP(AD7124_ADC_CTRL_MODE_MSK, x) + +/* AD7124_CHANNEL_X */ +#define AD7124_CHANNEL_EN_MSK BIT(15) +#define AD7124_CHANNEL_EN(x) FIELD_PREP(AD7124_CHANNEL_EN_MSK, x) +#define AD7124_CHANNEL_SETUP_MSK GENMASK(14, 12) +#define AD7124_CHANNEL_SETUP(x) FIELD_PREP(AD7124_CHANNEL_SETUP_MSK, x) +#define AD7124_CHANNEL_AINP_MSK GENMASK(9, 5) +#define AD7124_CHANNEL_AINP(x) FIELD_PREP(AD7124_CHANNEL_AINP_MSK, x) +#define AD7124_CHANNEL_AINM_MSK GENMASK(4, 0) +#define AD7124_CHANNEL_AINM(x) FIELD_PREP(AD7124_CHANNEL_AINM_MSK, x) + +/* AD7124_CONFIG_X */ +#define AD7124_CONFIG_BIPOLAR_MSK BIT(11) +#define AD7124_CONFIG_BIPOLAR(x) FIELD_PREP(AD7124_CONFIG_BIPOLAR_MSK, x) +#define AD7124_CONFIG_REF_SEL_MSK GENMASK(4, 3) +#define AD7124_CONFIG_REF_SEL(x) FIELD_PREP(AD7124_CONFIG_REF_SEL_MSK, x) +#define AD7124_CONFIG_PGA_MSK GENMASK(2, 0) +#define AD7124_CONFIG_PGA(x) FIELD_PREP(AD7124_CONFIG_PGA_MSK, x) + +/* AD7124_FILTER_X */ +#define AD7124_FILTER_FS_MSK GENMASK(10, 0) +#define AD7124_FILTER_FS(x) FIELD_PREP(AD7124_FILTER_FS_MSK, x) + +enum ad7124_ids { + ID_AD7124_4, + ID_AD7124_8, +}; + +enum ad7124_ref_sel { + AD7124_REFIN1, + AD7124_REFIN2, + AD7124_INT_REF, + AD7124_AVDD_REF, +}; + +enum ad7124_power_mode { + AD7124_LOW_POWER, + AD7124_MID_POWER, + AD7124_FULL_POWER, +}; + +static const unsigned int ad7124_gain[8] = { + 1, 2, 4, 8, 16, 32, 64, 128 +}; + +static const int ad7124_master_clk_freq_hz[3] = { + [AD7124_LOW_POWER] = 76800, + [AD7124_MID_POWER] = 153600, + [AD7124_FULL_POWER] = 614400, +}; + +static const char * const ad7124_ref_names[] = { + [AD7124_REFIN1] = "refin1", + [AD7124_REFIN2] = "refin2", + [AD7124_INT_REF] = "int", + [AD7124_AVDD_REF] = "avdd", +}; + +struct ad7124_chip_info { + unsigned int num_inputs; +}; + +struct ad7124_channel_config { + enum ad7124_ref_sel refsel; + bool bipolar; + unsigned int ain; + unsigned int vref_mv; + unsigned int pga_bits; + unsigned int odr; +}; + +struct ad7124_state { + const struct ad7124_chip_info *chip_info; + struct ad_sigma_delta sd; + struct ad7124_channel_config channel_config[4]; + struct regulator *vref[4]; + struct clk *mclk; + unsigned int adc_control; + unsigned int num_channels; +}; + +static const struct iio_chan_spec ad7124_channel_template = { + .type = IIO_VOLTAGE, + .indexed = 1, + .differential = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .scan_type = { + .sign = 'u', + .realbits = 24, + .storagebits = 32, + .shift = 8, + .endianness = IIO_BE, + }, +}; + +static struct ad7124_chip_info ad7124_chip_info_tbl[] = { + [ID_AD7124_4] = { + .num_inputs = 8, + }, + [ID_AD7124_8] = { + .num_inputs = 16, + }, +}; + +static int ad7124_find_closest_match(const int *array, + unsigned int size, int val) +{ + int i, idx; + unsigned int diff_new, diff_old; + + diff_old = U32_MAX; + idx = 0; + + for (i = 0; i < size; i++) { + diff_new = abs(val - array[i]); + if (diff_new < diff_old) { + diff_old = diff_new; + idx = i; + } + } + + return idx; +} + +static int ad7124_spi_write_mask(struct ad7124_state *st, + unsigned int addr, + unsigned long mask, + unsigned int val, + unsigned int bytes) +{ + unsigned int readval; + int ret; + + ret = ad_sd_read_reg(&st->sd, addr, bytes, &readval); + if (ret < 0) + return ret; + + readval &= ~mask; + readval |= val; + + return ad_sd_write_reg(&st->sd, addr, bytes, readval); +} + +static int ad7124_set_mode(struct ad_sigma_delta *sd, + enum ad_sigma_delta_mode mode) +{ + struct ad7124_state *st = container_of(sd, struct ad7124_state, sd); + + st->adc_control &= ~AD7124_ADC_CTRL_MODE_MSK; + st->adc_control |= AD7124_ADC_CTRL_MODE(mode); + + return ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, 2, st->adc_control); +} + +static int ad7124_set_channel(struct ad_sigma_delta *sd, unsigned int channel) +{ + struct ad7124_state *st = container_of(sd, struct ad7124_state, sd); + unsigned int val; + + val = st->channel_config[channel].ain | AD7124_CHANNEL_EN(1) | + AD7124_CHANNEL_SETUP(channel); + + return ad_sd_write_reg(&st->sd, AD7124_CHANNEL(channel), 2, val); +} + +static const struct ad_sigma_delta_info ad7124_sigma_delta_info = { + .set_channel = ad7124_set_channel, + .set_mode = ad7124_set_mode, + .has_registers = true, + .addr_shift = 0, + .read_mask = BIT(6), + .data_reg = AD7124_DATA, +}; + +static int ad7124_set_channel_odr(struct ad7124_state *st, + unsigned int channel, + unsigned int odr) +{ + unsigned int fclk, odr_sel_bits; + int ret; + + fclk = clk_get_rate(st->mclk); + /* + * FS[10:0] = fCLK / (fADC x 32) where: + * fADC is the output data rate + * fCLK is the master clock frequency + * FS[10:0] are the bits in the filter register + * FS[10:0] can have a value from 1 to 2047 + */ + odr_sel_bits = DIV_ROUND_CLOSEST(fclk, odr * 32); + if (odr_sel_bits < 1) + odr_sel_bits = 1; + else if (odr_sel_bits > 2047) + odr_sel_bits = 2047; + + ret = ad7124_spi_write_mask(st, AD7124_FILTER(channel), + AD7124_FILTER_FS_MSK, + AD7124_FILTER_FS(odr_sel_bits), 3); + if (ret < 0) + return ret; + /* fADC = fCLK / (FS[10:0] x 32) */ + st->channel_config[channel].odr = + DIV_ROUND_CLOSEST(fclk, odr_sel_bits * 32); + + return 0; +} + +static int ad7124_set_channel_gain(struct ad7124_state *st, + unsigned int channel, + unsigned int gain) +{ + unsigned int res; + int ret; + + res = ad7124_find_closest_match(ad7124_gain, + ARRAY_SIZE(ad7124_gain), gain); + ret = ad7124_spi_write_mask(st, AD7124_CONFIG(channel), + AD7124_CONFIG_PGA_MSK, + AD7124_CONFIG_PGA(res), 2); + if (ret < 0) + return ret; + + st->channel_config[channel].pga_bits = res; + + return 0; +} + +static int ad7124_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct ad7124_state *st = iio_priv(indio_dev); + int idx, ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + ret = ad_sigma_delta_single_conversion(indio_dev, chan, val); + if (ret < 0) + return ret; + + /* After the conversion is performed, disable the channel */ + ret = ad_sd_write_reg(&st->sd, + AD7124_CHANNEL(chan->address), 2, + st->channel_config[chan->address].ain | + AD7124_CHANNEL_EN(0)); + if (ret < 0) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + idx = st->channel_config[chan->address].pga_bits; + *val = st->channel_config[chan->address].vref_mv; + if (st->channel_config[chan->address].bipolar) + *val2 = chan->scan_type.realbits - 1 + idx; + else + *val2 = chan->scan_type.realbits + idx; + + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_OFFSET: + if (st->channel_config[chan->address].bipolar) + *val = -(1 << (chan->scan_type.realbits - 1)); + else + *val = 0; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->channel_config[chan->address].odr; + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad7124_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct ad7124_state *st = iio_priv(indio_dev); + unsigned int res, gain, full_scale, vref; + + switch (info) { + case IIO_CHAN_INFO_SAMP_FREQ: + if (val2 != 0) + return -EINVAL; + + return ad7124_set_channel_odr(st, chan->address, val); + case IIO_CHAN_INFO_SCALE: + if (val != 0) + return -EINVAL; + + if (st->channel_config[chan->address].bipolar) + full_scale = 1 << (chan->scan_type.realbits - 1); + else + full_scale = 1 << chan->scan_type.realbits; + + vref = st->channel_config[chan->address].vref_mv * 1000000LL; + res = DIV_ROUND_CLOSEST(vref, full_scale); + gain = DIV_ROUND_CLOSEST(res, val2); + + return ad7124_set_channel_gain(st, chan->address, gain); + default: + return -EINVAL; + } +} + +static IIO_CONST_ATTR(in_voltage_scale_available, + "0.000001164 0.000002328 0.000004656 0.000009313 0.000018626 0.000037252 0.000074505 0.000149011 0.000298023"); + +static struct attribute *ad7124_attributes[] = { + &iio_const_attr_in_voltage_scale_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ad7124_attrs_group = { + .attrs = ad7124_attributes, +}; + +static const struct iio_info ad7124_info = { + .read_raw = ad7124_read_raw, + .write_raw = ad7124_write_raw, + .validate_trigger = ad_sd_validate_trigger, + .attrs = &ad7124_attrs_group, +}; + +static int ad7124_soft_reset(struct ad7124_state *st) +{ + unsigned int readval, timeout; + int ret; + + ret = ad_sd_reset(&st->sd, 64); + if (ret < 0) + return ret; + + timeout = 100; + do { + ret = ad_sd_read_reg(&st->sd, AD7124_STATUS, 1, &readval); + if (ret < 0) + return ret; + + if (!(readval & AD7124_STATUS_POR_FLAG_MSK)) + return 0; + + /* The AD7124 requires typically 2ms to power up and settle */ + usleep_range(100, 2000); + } while (--timeout); + + dev_err(&st->sd.spi->dev, "Soft reset failed\n"); + + return -EIO; +} + +static int ad7124_init_channel_vref(struct ad7124_state *st, + unsigned int channel_number) +{ + unsigned int refsel = st->channel_config[channel_number].refsel; + + switch (refsel) { + case AD7124_REFIN1: + case AD7124_REFIN2: + case AD7124_AVDD_REF: + if (IS_ERR(st->vref[refsel])) { + dev_err(&st->sd.spi->dev, + "Error, trying to use external voltage reference without a %s regulator.\n", + ad7124_ref_names[refsel]); + return PTR_ERR(st->vref[refsel]); + } + st->channel_config[channel_number].vref_mv = + regulator_get_voltage(st->vref[refsel]); + /* Conversion from uV to mV */ + st->channel_config[channel_number].vref_mv /= 1000; + break; + case AD7124_INT_REF: + st->channel_config[channel_number].vref_mv = 2500; + break; + default: + dev_err(&st->sd.spi->dev, "Invalid reference %d\n", refsel); + return -EINVAL; + } + + return 0; +} + +static int ad7124_of_parse_channel_config(struct iio_dev *indio_dev, + struct device_node *np) +{ + struct ad7124_state *st = iio_priv(indio_dev); + struct device_node *child; + struct iio_chan_spec *chan; + unsigned int ain[2], channel = 0, tmp; + int ret; + + st->num_channels = of_get_available_child_count(np); + if (!st->num_channels) { + dev_err(indio_dev->dev.parent, "no channel children\n"); + return -ENODEV; + } + + chan = devm_kcalloc(indio_dev->dev.parent, st->num_channels, + sizeof(*chan), GFP_KERNEL); + if (!chan) + return -ENOMEM; + + indio_dev->channels = chan; + indio_dev->num_channels = st->num_channels; + + for_each_available_child_of_node(np, child) { + ret = of_property_read_u32(child, "reg", &channel); + if (ret) + goto err; + + ret = of_property_read_u32_array(child, "diff-channels", + ain, 2); + if (ret) + goto err; + + if (ain[0] >= st->chip_info->num_inputs || + ain[1] >= st->chip_info->num_inputs) { + dev_err(indio_dev->dev.parent, + "Input pin number out of range.\n"); + ret = -EINVAL; + goto err; + } + st->channel_config[channel].ain = AD7124_CHANNEL_AINP(ain[0]) | + AD7124_CHANNEL_AINM(ain[1]); + st->channel_config[channel].bipolar = + of_property_read_bool(child, "bipolar"); + + ret = of_property_read_u32(child, "adi,reference-select", &tmp); + if (ret) + st->channel_config[channel].refsel = AD7124_INT_REF; + else + st->channel_config[channel].refsel = tmp; + + *chan = ad7124_channel_template; + chan->address = channel; + chan->scan_index = channel; + chan->channel = ain[0]; + chan->channel2 = ain[1]; + + chan++; + } + + return 0; +err: + of_node_put(child); + + return ret; +} + +static int ad7124_setup(struct ad7124_state *st) +{ + unsigned int val, fclk, power_mode; + int i, ret; + + fclk = clk_get_rate(st->mclk); + if (!fclk) + return -EINVAL; + + /* The power mode changes the master clock frequency */ + power_mode = ad7124_find_closest_match(ad7124_master_clk_freq_hz, + ARRAY_SIZE(ad7124_master_clk_freq_hz), + fclk); + if (fclk != ad7124_master_clk_freq_hz[power_mode]) { + ret = clk_set_rate(st->mclk, fclk); + if (ret) + return ret; + } + + /* Set the power mode */ + st->adc_control &= ~AD7124_ADC_CTRL_PWR_MSK; + st->adc_control |= AD7124_ADC_CTRL_PWR(power_mode); + ret = ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, 2, st->adc_control); + if (ret < 0) + return ret; + + for (i = 0; i < st->num_channels; i++) { + val = st->channel_config[i].ain | AD7124_CHANNEL_SETUP(i); + ret = ad_sd_write_reg(&st->sd, AD7124_CHANNEL(i), 2, val); + if (ret < 0) + return ret; + + ret = ad7124_init_channel_vref(st, i); + if (ret < 0) + return ret; + + val = AD7124_CONFIG_BIPOLAR(st->channel_config[i].bipolar) | + AD7124_CONFIG_REF_SEL(st->channel_config[i].refsel); + ret = ad_sd_write_reg(&st->sd, AD7124_CONFIG(i), 2, val); + if (ret < 0) + return ret; + /* + * 9.38 SPS is the minimum output data rate supported + * regardless of the selected power mode. Round it up to 10 and + * set all the enabled channels to this default value. + */ + ret = ad7124_set_channel_odr(st, i, 10); + } + + return ret; +} + +static int ad7124_probe(struct spi_device *spi) +{ + const struct spi_device_id *id; + struct ad7124_state *st; + struct iio_dev *indio_dev; + int i, ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + id = spi_get_device_id(spi); + st->chip_info = &ad7124_chip_info_tbl[id->driver_data]; + + ad_sd_init(&st->sd, indio_dev, spi, &ad7124_sigma_delta_info); + + spi_set_drvdata(spi, indio_dev); + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &ad7124_info; + + ret = ad7124_of_parse_channel_config(indio_dev, spi->dev.of_node); + if (ret < 0) + return ret; + + for (i = 0; i < ARRAY_SIZE(st->vref); i++) { + if (i == AD7124_INT_REF) + continue; + + st->vref[i] = devm_regulator_get_optional(&spi->dev, + ad7124_ref_names[i]); + if (PTR_ERR(st->vref[i]) == -ENODEV) + continue; + else if (IS_ERR(st->vref[i])) + return PTR_ERR(st->vref[i]); + + ret = regulator_enable(st->vref[i]); + if (ret) + return ret; + } + + st->mclk = devm_clk_get(&spi->dev, "mclk"); + if (IS_ERR(st->mclk)) { + ret = PTR_ERR(st->mclk); + goto error_regulator_disable; + } + + ret = clk_prepare_enable(st->mclk); + if (ret < 0) + goto error_regulator_disable; + + ret = ad7124_soft_reset(st); + if (ret < 0) + goto error_clk_disable_unprepare; + + ret = ad7124_setup(st); + if (ret < 0) + goto error_clk_disable_unprepare; + + ret = ad_sd_setup_buffer_and_trigger(indio_dev); + if (ret < 0) + goto error_clk_disable_unprepare; + + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(&spi->dev, "Failed to register iio device\n"); + goto error_remove_trigger; + } + + return 0; + +error_remove_trigger: + ad_sd_cleanup_buffer_and_trigger(indio_dev); +error_clk_disable_unprepare: + clk_disable_unprepare(st->mclk); +error_regulator_disable: + for (i = ARRAY_SIZE(st->vref) - 1; i >= 0; i--) { + if (!IS_ERR_OR_NULL(st->vref[i])) + regulator_disable(st->vref[i]); + } + + return ret; +} + +static int ad7124_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad7124_state *st = iio_priv(indio_dev); + int i; + + iio_device_unregister(indio_dev); + ad_sd_cleanup_buffer_and_trigger(indio_dev); + clk_disable_unprepare(st->mclk); + + for (i = ARRAY_SIZE(st->vref) - 1; i >= 0; i--) { + if (!IS_ERR_OR_NULL(st->vref[i])) + regulator_disable(st->vref[i]); + } + + return 0; +} + +static const struct spi_device_id ad7124_id_table[] = { + { "ad7124-4", ID_AD7124_4 }, + { "ad7124-8", ID_AD7124_8 }, + {} +}; +MODULE_DEVICE_TABLE(spi, ad7124_id_table); + +static const struct of_device_id ad7124_of_match[] = { + { .compatible = "adi,ad7124-4" }, + { .compatible = "adi,ad7124-8" }, + { }, +}; +MODULE_DEVICE_TABLE(of, ad7124_of_match); + +static struct spi_driver ad71124_driver = { + .driver = { + .name = "ad7124", + .of_match_table = ad7124_of_match, + }, + .probe = ad7124_probe, + .remove = ad7124_remove, + .id_table = ad7124_id_table, +}; +module_spi_driver(ad71124_driver); + +MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>"); +MODULE_DESCRIPTION("Analog Devices AD7124 SPI driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/ad7949.c b/drivers/iio/adc/ad7949.c new file mode 100644 index 000000000000..ac0ffff6c5ae --- /dev/null +++ b/drivers/iio/adc/ad7949.c @@ -0,0 +1,347 @@ +// SPDX-License-Identifier: GPL-2.0 +/* ad7949.c - Analog Devices ADC driver 14/16 bits 4/8 channels + * + * Copyright (C) 2018 CMC NV + * + * http://www.analog.com/media/en/technical-documentation/data-sheets/AD7949.pdf + */ + +#include <linux/delay.h> +#include <linux/iio/iio.h> +#include <linux/module.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> + +#define AD7949_MASK_CHANNEL_SEL GENMASK(9, 7) +#define AD7949_MASK_TOTAL GENMASK(13, 0) +#define AD7949_OFFSET_CHANNEL_SEL 7 +#define AD7949_CFG_READ_BACK 0x1 +#define AD7949_CFG_REG_SIZE_BITS 14 + +enum { + ID_AD7949 = 0, + ID_AD7682, + ID_AD7689, +}; + +struct ad7949_adc_spec { + u8 num_channels; + u8 resolution; +}; + +static const struct ad7949_adc_spec ad7949_adc_spec[] = { + [ID_AD7949] = { .num_channels = 8, .resolution = 14 }, + [ID_AD7682] = { .num_channels = 4, .resolution = 16 }, + [ID_AD7689] = { .num_channels = 8, .resolution = 16 }, +}; + +/** + * struct ad7949_adc_chip - AD ADC chip + * @lock: protects write sequences + * @vref: regulator generating Vref + * @iio_dev: reference to iio structure + * @spi: reference to spi structure + * @resolution: resolution of the chip + * @cfg: copy of the configuration register + * @current_channel: current channel in use + * @buffer: buffer to send / receive data to / from device + */ +struct ad7949_adc_chip { + struct mutex lock; + struct regulator *vref; + struct iio_dev *indio_dev; + struct spi_device *spi; + u8 resolution; + u16 cfg; + unsigned int current_channel; + u32 buffer ____cacheline_aligned; +}; + +static bool ad7949_spi_cfg_is_read_back(struct ad7949_adc_chip *ad7949_adc) +{ + if (!(ad7949_adc->cfg & AD7949_CFG_READ_BACK)) + return true; + + return false; +} + +static int ad7949_spi_bits_per_word(struct ad7949_adc_chip *ad7949_adc) +{ + int ret = ad7949_adc->resolution; + + if (ad7949_spi_cfg_is_read_back(ad7949_adc)) + ret += AD7949_CFG_REG_SIZE_BITS; + + return ret; +} + +static int ad7949_spi_write_cfg(struct ad7949_adc_chip *ad7949_adc, u16 val, + u16 mask) +{ + int ret; + int bits_per_word = ad7949_spi_bits_per_word(ad7949_adc); + int shift = bits_per_word - AD7949_CFG_REG_SIZE_BITS; + struct spi_message msg; + struct spi_transfer tx[] = { + { + .tx_buf = &ad7949_adc->buffer, + .len = 4, + .bits_per_word = bits_per_word, + }, + }; + + ad7949_adc->cfg = (val & mask) | (ad7949_adc->cfg & ~mask); + ad7949_adc->buffer = ad7949_adc->cfg << shift; + spi_message_init_with_transfers(&msg, tx, 1); + ret = spi_sync(ad7949_adc->spi, &msg); + + /* + * This delay is to avoid a new request before the required time to + * send a new command to the device + */ + udelay(2); + return ret; +} + +static int ad7949_spi_read_channel(struct ad7949_adc_chip *ad7949_adc, int *val, + unsigned int channel) +{ + int ret; + int bits_per_word = ad7949_spi_bits_per_word(ad7949_adc); + int mask = GENMASK(ad7949_adc->resolution, 0); + struct spi_message msg; + struct spi_transfer tx[] = { + { + .rx_buf = &ad7949_adc->buffer, + .len = 4, + .bits_per_word = bits_per_word, + }, + }; + + ret = ad7949_spi_write_cfg(ad7949_adc, + channel << AD7949_OFFSET_CHANNEL_SEL, + AD7949_MASK_CHANNEL_SEL); + if (ret) + return ret; + + ad7949_adc->buffer = 0; + spi_message_init_with_transfers(&msg, tx, 1); + ret = spi_sync(ad7949_adc->spi, &msg); + if (ret) + return ret; + + /* + * This delay is to avoid a new request before the required time to + * send a new command to the device + */ + udelay(2); + + ad7949_adc->current_channel = channel; + + if (ad7949_spi_cfg_is_read_back(ad7949_adc)) + *val = (ad7949_adc->buffer >> AD7949_CFG_REG_SIZE_BITS) & mask; + else + *val = ad7949_adc->buffer & mask; + + return 0; +} + +#define AD7949_ADC_CHANNEL(chan) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (chan), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ +} + +static const struct iio_chan_spec ad7949_adc_channels[] = { + AD7949_ADC_CHANNEL(0), + AD7949_ADC_CHANNEL(1), + AD7949_ADC_CHANNEL(2), + AD7949_ADC_CHANNEL(3), + AD7949_ADC_CHANNEL(4), + AD7949_ADC_CHANNEL(5), + AD7949_ADC_CHANNEL(6), + AD7949_ADC_CHANNEL(7), +}; + +static int ad7949_spi_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ad7949_adc_chip *ad7949_adc = iio_priv(indio_dev); + int ret; + + if (!val) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&ad7949_adc->lock); + ret = ad7949_spi_read_channel(ad7949_adc, val, chan->channel); + mutex_unlock(&ad7949_adc->lock); + + if (ret < 0) + return ret; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + ret = regulator_get_voltage(ad7949_adc->vref); + if (ret < 0) + return ret; + + *val = ret / 5000; + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static int ad7949_spi_reg_access(struct iio_dev *indio_dev, + unsigned int reg, unsigned int writeval, + unsigned int *readval) +{ + struct ad7949_adc_chip *ad7949_adc = iio_priv(indio_dev); + int ret = 0; + + if (readval) + *readval = ad7949_adc->cfg; + else + ret = ad7949_spi_write_cfg(ad7949_adc, + writeval & AD7949_MASK_TOTAL, AD7949_MASK_TOTAL); + + return ret; +} + +static const struct iio_info ad7949_spi_info = { + .read_raw = ad7949_spi_read_raw, + .debugfs_reg_access = ad7949_spi_reg_access, +}; + +static int ad7949_spi_init(struct ad7949_adc_chip *ad7949_adc) +{ + int ret; + int val; + + /* Sequencer disabled, CFG readback disabled, IN0 as default channel */ + ad7949_adc->current_channel = 0; + ret = ad7949_spi_write_cfg(ad7949_adc, 0x3C79, AD7949_MASK_TOTAL); + + /* + * Do two dummy conversions to apply the first configuration setting. + * Required only after the start up of the device. + */ + ad7949_spi_read_channel(ad7949_adc, &val, ad7949_adc->current_channel); + ad7949_spi_read_channel(ad7949_adc, &val, ad7949_adc->current_channel); + + return ret; +} + +static int ad7949_spi_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + const struct ad7949_adc_spec *spec; + struct ad7949_adc_chip *ad7949_adc; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*ad7949_adc)); + if (!indio_dev) { + dev_err(dev, "can not allocate iio device\n"); + return -ENOMEM; + } + + indio_dev->dev.parent = dev; + indio_dev->dev.of_node = dev->of_node; + indio_dev->info = &ad7949_spi_info; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ad7949_adc_channels; + spi_set_drvdata(spi, indio_dev); + + ad7949_adc = iio_priv(indio_dev); + ad7949_adc->indio_dev = indio_dev; + ad7949_adc->spi = spi; + + spec = &ad7949_adc_spec[spi_get_device_id(spi)->driver_data]; + indio_dev->num_channels = spec->num_channels; + ad7949_adc->resolution = spec->resolution; + + ad7949_adc->vref = devm_regulator_get(dev, "vref"); + if (IS_ERR(ad7949_adc->vref)) { + dev_err(dev, "fail to request regulator\n"); + return PTR_ERR(ad7949_adc->vref); + } + + ret = regulator_enable(ad7949_adc->vref); + if (ret < 0) { + dev_err(dev, "fail to enable regulator\n"); + return ret; + } + + mutex_init(&ad7949_adc->lock); + + ret = ad7949_spi_init(ad7949_adc); + if (ret) { + dev_err(dev, "enable to init this device: %d\n", ret); + goto err; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(dev, "fail to register iio device: %d\n", ret); + goto err; + } + + return 0; + +err: + mutex_destroy(&ad7949_adc->lock); + regulator_disable(ad7949_adc->vref); + + return ret; +} + +static int ad7949_spi_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ad7949_adc_chip *ad7949_adc = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + mutex_destroy(&ad7949_adc->lock); + regulator_disable(ad7949_adc->vref); + + return 0; +} + +static const struct of_device_id ad7949_spi_of_id[] = { + { .compatible = "adi,ad7949" }, + { .compatible = "adi,ad7682" }, + { .compatible = "adi,ad7689" }, + { } +}; +MODULE_DEVICE_TABLE(of, ad7949_spi_of_id); + +static const struct spi_device_id ad7949_spi_id[] = { + { "ad7949", ID_AD7949 }, + { "ad7682", ID_AD7682 }, + { "ad7689", ID_AD7689 }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad7949_spi_id); + +static struct spi_driver ad7949_spi_driver = { + .driver = { + .name = "ad7949", + .of_match_table = ad7949_spi_of_id, + }, + .probe = ad7949_spi_probe, + .remove = ad7949_spi_remove, + .id_table = ad7949_spi_id, +}; +module_spi_driver(ad7949_spi_driver); + +MODULE_AUTHOR("Charles-Antoine Couret <charles-antoine.couret@essensium.com>"); +MODULE_DESCRIPTION("Analog Devices 14/16-bit 8-channel ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index fc9510716ac7..ff5f2da2e1b1 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -278,6 +278,7 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev, { struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); unsigned int sample, raw_sample; + unsigned int data_reg; int ret = 0; if (iio_buffer_enabled(indio_dev)) @@ -305,7 +306,12 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev, if (ret < 0) goto out; - ret = ad_sd_read_reg(sigma_delta, AD_SD_REG_DATA, + if (sigma_delta->info->data_reg != 0) + data_reg = sigma_delta->info->data_reg; + else + data_reg = AD_SD_REG_DATA; + + ret = ad_sd_read_reg(sigma_delta, data_reg, DIV_ROUND_UP(chan->scan_type.realbits + chan->scan_type.shift, 8), &raw_sample); @@ -392,6 +398,7 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p) struct iio_dev *indio_dev = pf->indio_dev; struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); unsigned int reg_size; + unsigned int data_reg; uint8_t data[16]; int ret; @@ -401,18 +408,23 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p) indio_dev->channels[0].scan_type.shift; reg_size = DIV_ROUND_UP(reg_size, 8); + if (sigma_delta->info->data_reg != 0) + data_reg = sigma_delta->info->data_reg; + else + data_reg = AD_SD_REG_DATA; + switch (reg_size) { case 4: case 2: case 1: - ret = ad_sd_read_reg_raw(sigma_delta, AD_SD_REG_DATA, - reg_size, &data[0]); + ret = ad_sd_read_reg_raw(sigma_delta, data_reg, reg_size, + &data[0]); break; case 3: /* We store 24 bit samples in a 32 bit word. Keep the upper * byte set to zero. */ - ret = ad_sd_read_reg_raw(sigma_delta, AD_SD_REG_DATA, - reg_size, &data[1]); + ret = ad_sd_read_reg_raw(sigma_delta, data_reg, reg_size, + &data[1]); break; } diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index d1239624187d..bdd7cba6f6b0 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -250,6 +250,7 @@ static int ina2xx_read_raw(struct iio_dev *indio_dev, *val2 = chip->shunt_resistor_uohm; return IIO_VAL_FRACTIONAL; } + return -EINVAL; case IIO_CHAN_INFO_HARDWAREGAIN: switch (chan->address) { @@ -262,6 +263,7 @@ static int ina2xx_read_raw(struct iio_dev *indio_dev, *val = chip->range_vbus == 32 ? 1 : 2; return IIO_VAL_INT; } + return -EINVAL; } return -EINVAL; diff --git a/drivers/iio/adc/max11100.c b/drivers/iio/adc/max11100.c index af59ab2e650c..3440539cfdba 100644 --- a/drivers/iio/adc/max11100.c +++ b/drivers/iio/adc/max11100.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * iio/adc/max11100.c * Maxim max11100 ADC Driver with IIO interface * * Copyright (C) 2016-17 Renesas Electronics Corporation * Copyright (C) 2016-17 Jacopo Mondi - * - * 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/delay.h> #include <linux/kernel.h> diff --git a/drivers/iio/adc/max9611.c b/drivers/iio/adc/max9611.c index 643a4e66eb80..917223d5ff5b 100644 --- a/drivers/iio/adc/max9611.c +++ b/drivers/iio/adc/max9611.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * iio/adc/max9611.c * @@ -5,10 +6,6 @@ * 12-bit ADC interface. * * Copyright (C) 2017 Jacopo Mondi - * - * 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. */ /* diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c index 028ccd218f82..26011254ffbc 100644 --- a/drivers/iio/adc/meson_saradc.c +++ b/drivers/iio/adc/meson_saradc.c @@ -18,6 +18,7 @@ #include <linux/io.h> #include <linux/iio/iio.h> #include <linux/module.h> +#include <linux/nvmem-consumer.h> #include <linux/interrupt.h> #include <linux/of.h> #include <linux/of_irq.h> @@ -165,6 +166,14 @@ #define MESON_SAR_ADC_MAX_FIFO_SIZE 32 #define MESON_SAR_ADC_TIMEOUT 100 /* ms */ +#define MESON_SAR_ADC_VOLTAGE_AND_TEMP_CHANNEL 6 +#define MESON_SAR_ADC_TEMP_OFFSET 27 + +/* temperature sensor calibration information in eFuse */ +#define MESON_SAR_ADC_EFUSE_BYTES 4 +#define MESON_SAR_ADC_EFUSE_BYTE3_UPPER_ADC_VAL GENMASK(6, 0) +#define MESON_SAR_ADC_EFUSE_BYTE3_IS_CALIBRATED BIT(7) + /* for use with IIO_VAL_INT_PLUS_MICRO */ #define MILLION 1000000 @@ -175,16 +184,25 @@ .address = _chan, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_AVERAGE_RAW), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ - BIT(IIO_CHAN_INFO_CALIBBIAS) | \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_CALIBBIAS) | \ BIT(IIO_CHAN_INFO_CALIBSCALE), \ .datasheet_name = "SAR_ADC_CH"#_chan, \ } -/* - * TODO: the hardware supports IIO_TEMP for channel 6 as well which is - * currently not supported by this driver. - */ +#define MESON_SAR_ADC_TEMP_CHAN(_chan) { \ + .type = IIO_TEMP, \ + .channel = _chan, \ + .address = MESON_SAR_ADC_VOLTAGE_AND_TEMP_CHANNEL, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_AVERAGE_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_CALIBBIAS) | \ + BIT(IIO_CHAN_INFO_CALIBSCALE), \ + .datasheet_name = "TEMP_SENSOR", \ +} + static const struct iio_chan_spec meson_sar_adc_iio_channels[] = { MESON_SAR_ADC_CHAN(0), MESON_SAR_ADC_CHAN(1), @@ -197,6 +215,19 @@ static const struct iio_chan_spec meson_sar_adc_iio_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(8), }; +static const struct iio_chan_spec meson_sar_adc_and_temp_iio_channels[] = { + MESON_SAR_ADC_CHAN(0), + MESON_SAR_ADC_CHAN(1), + MESON_SAR_ADC_CHAN(2), + MESON_SAR_ADC_CHAN(3), + MESON_SAR_ADC_CHAN(4), + MESON_SAR_ADC_CHAN(5), + MESON_SAR_ADC_CHAN(6), + MESON_SAR_ADC_CHAN(7), + MESON_SAR_ADC_TEMP_CHAN(8), + IIO_CHAN_SOFT_TIMESTAMP(9), +}; + enum meson_sar_adc_avg_mode { NO_AVERAGING = 0x0, MEAN_AVERAGING = 0x1, @@ -225,6 +256,9 @@ struct meson_sar_adc_param { u32 bandgap_reg; unsigned int resolution; const struct regmap_config *regmap_config; + u8 temperature_trimming_bits; + unsigned int temperature_multiplier; + unsigned int temperature_divider; }; struct meson_sar_adc_data { @@ -246,6 +280,9 @@ struct meson_sar_adc_priv { struct completion done; int calibbias; int calibscale; + bool temperature_sensor_calibrated; + u8 temperature_sensor_coefficient; + u16 temperature_sensor_adc_val; }; static const struct regmap_config meson_sar_adc_regmap_config_gxbb = { @@ -389,9 +426,16 @@ static void meson_sar_adc_enable_channel(struct iio_dev *indio_dev, MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK, regval); - if (chan->address == 6) - regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10, - MESON_SAR_ADC_DELTA_10_TEMP_SEL, 0); + if (chan->address == MESON_SAR_ADC_VOLTAGE_AND_TEMP_CHANNEL) { + if (chan->type == IIO_TEMP) + regval = MESON_SAR_ADC_DELTA_10_TEMP_SEL; + else + regval = 0; + + regmap_update_bits(priv->regmap, + MESON_SAR_ADC_DELTA_10, + MESON_SAR_ADC_DELTA_10_TEMP_SEL, regval); + } } static void meson_sar_adc_set_chan7_mux(struct iio_dev *indio_dev, @@ -506,8 +550,12 @@ static int meson_sar_adc_get_sample(struct iio_dev *indio_dev, enum meson_sar_adc_num_samples avg_samples, int *val) { + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); int ret; + if (chan->type == IIO_TEMP && !priv->temperature_sensor_calibrated) + return -ENOTSUPP; + ret = meson_sar_adc_lock(indio_dev); if (ret) return ret; @@ -555,17 +603,31 @@ static int meson_sar_adc_iio_info_read_raw(struct iio_dev *indio_dev, break; case IIO_CHAN_INFO_SCALE: - ret = regulator_get_voltage(priv->vref); - if (ret < 0) { - dev_err(indio_dev->dev.parent, - "failed to get vref voltage: %d\n", ret); - return ret; + if (chan->type == IIO_VOLTAGE) { + ret = regulator_get_voltage(priv->vref); + if (ret < 0) { + dev_err(indio_dev->dev.parent, + "failed to get vref voltage: %d\n", + ret); + return ret; + } + + *val = ret / 1000; + *val2 = priv->param->resolution; + return IIO_VAL_FRACTIONAL_LOG2; + } else if (chan->type == IIO_TEMP) { + /* SoC specific multiplier and divider */ + *val = priv->param->temperature_multiplier; + *val2 = priv->param->temperature_divider; + + /* celsius to millicelsius */ + *val *= 1000; + + return IIO_VAL_FRACTIONAL; + } else { + return -EINVAL; } - *val = ret / 1000; - *val2 = priv->param->resolution; - return IIO_VAL_FRACTIONAL_LOG2; - case IIO_CHAN_INFO_CALIBBIAS: *val = priv->calibbias; return IIO_VAL_INT; @@ -575,6 +637,13 @@ static int meson_sar_adc_iio_info_read_raw(struct iio_dev *indio_dev, *val2 = priv->calibscale % MILLION; return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_OFFSET: + *val = DIV_ROUND_CLOSEST(MESON_SAR_ADC_TEMP_OFFSET * + priv->param->temperature_divider, + priv->param->temperature_multiplier); + *val -= priv->temperature_sensor_adc_val; + return IIO_VAL_INT; + default: return -EINVAL; } @@ -625,6 +694,65 @@ static int meson_sar_adc_clk_init(struct iio_dev *indio_dev, return 0; } +static int meson_sar_adc_temp_sensor_init(struct iio_dev *indio_dev) +{ + struct meson_sar_adc_priv *priv = iio_priv(indio_dev); + u8 *buf, trimming_bits, trimming_mask, upper_adc_val; + struct nvmem_cell *temperature_calib; + size_t read_len; + int ret; + + temperature_calib = devm_nvmem_cell_get(&indio_dev->dev, + "temperature_calib"); + if (IS_ERR(temperature_calib)) { + ret = PTR_ERR(temperature_calib); + + /* + * leave the temperature sensor disabled if no calibration data + * was passed via nvmem-cells. + */ + if (ret == -ENODEV) + return 0; + + if (ret != -EPROBE_DEFER) + dev_err(indio_dev->dev.parent, + "failed to get temperature_calib cell\n"); + + return ret; + } + + read_len = MESON_SAR_ADC_EFUSE_BYTES; + buf = nvmem_cell_read(temperature_calib, &read_len); + if (IS_ERR(buf)) { + dev_err(indio_dev->dev.parent, + "failed to read temperature_calib cell\n"); + return PTR_ERR(buf); + } else if (read_len != MESON_SAR_ADC_EFUSE_BYTES) { + kfree(buf); + dev_err(indio_dev->dev.parent, + "invalid read size of temperature_calib cell\n"); + return -EINVAL; + } + + trimming_bits = priv->param->temperature_trimming_bits; + trimming_mask = BIT(trimming_bits) - 1; + + priv->temperature_sensor_calibrated = + buf[3] & MESON_SAR_ADC_EFUSE_BYTE3_IS_CALIBRATED; + priv->temperature_sensor_coefficient = buf[2] & trimming_mask; + + upper_adc_val = FIELD_GET(MESON_SAR_ADC_EFUSE_BYTE3_UPPER_ADC_VAL, + buf[3]); + + priv->temperature_sensor_adc_val = buf[2]; + priv->temperature_sensor_adc_val |= upper_adc_val << BITS_PER_BYTE; + priv->temperature_sensor_adc_val >>= trimming_bits; + + kfree(buf); + + return 0; +} + static int meson_sar_adc_init(struct iio_dev *indio_dev) { struct meson_sar_adc_priv *priv = iio_priv(indio_dev); @@ -649,10 +777,12 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev) meson_sar_adc_stop_sample_engine(indio_dev); - /* update the channel 6 MUX to select the temperature sensor */ + /* + * disable this bit as seems to be only relevant for Meson6 (based + * on the vendor driver), which we don't support at the moment. + */ regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0, - MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL, - MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL); + MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL, 0); /* disable all channels by default */ regmap_write(priv->regmap, MESON_SAR_ADC_CHAN_LIST, 0x0); @@ -709,6 +839,29 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev) regval |= MESON_SAR_ADC_AUX_SW_XP_DRIVE_SW; regmap_write(priv->regmap, MESON_SAR_ADC_AUX_SW, regval); + if (priv->temperature_sensor_calibrated) { + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10, + MESON_SAR_ADC_DELTA_10_TS_REVE1, + MESON_SAR_ADC_DELTA_10_TS_REVE1); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10, + MESON_SAR_ADC_DELTA_10_TS_REVE0, + MESON_SAR_ADC_DELTA_10_TS_REVE0); + + /* + * set bits [3:0] of the TSC (temperature sensor coefficient) + * to get the correct values when reading the temperature. + */ + regval = FIELD_PREP(MESON_SAR_ADC_DELTA_10_TS_C_MASK, + priv->temperature_sensor_coefficient); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10, + MESON_SAR_ADC_DELTA_10_TS_C_MASK, regval); + } else { + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10, + MESON_SAR_ADC_DELTA_10_TS_REVE1, 0); + regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10, + MESON_SAR_ADC_DELTA_10_TS_REVE0, 0); + } + ret = clk_set_parent(priv->adc_sel_clk, priv->clkin); if (ret) { dev_err(indio_dev->dev.parent, @@ -894,6 +1047,17 @@ static const struct meson_sar_adc_param meson_sar_adc_meson8_param = { .bandgap_reg = MESON_SAR_ADC_DELTA_10, .regmap_config = &meson_sar_adc_regmap_config_meson8, .resolution = 10, + .temperature_trimming_bits = 4, + .temperature_multiplier = 18 * 10000, + .temperature_divider = 1024 * 10 * 85, +}; + +static const struct meson_sar_adc_param meson_sar_adc_meson8b_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, }; static const struct meson_sar_adc_param meson_sar_adc_gxbb_param = { @@ -918,12 +1082,12 @@ static const struct meson_sar_adc_data meson_sar_adc_meson8_data = { }; static const struct meson_sar_adc_data meson_sar_adc_meson8b_data = { - .param = &meson_sar_adc_meson8_param, + .param = &meson_sar_adc_meson8b_param, .name = "meson-meson8b-saradc", }; static const struct meson_sar_adc_data meson_sar_adc_meson8m2_data = { - .param = &meson_sar_adc_meson8_param, + .param = &meson_sar_adc_meson8b_param, .name = "meson-meson8m2-saradc", }; @@ -1009,9 +1173,6 @@ static int meson_sar_adc_probe(struct platform_device *pdev) indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &meson_sar_adc_iio_info; - indio_dev->channels = meson_sar_adc_iio_channels; - indio_dev->num_channels = ARRAY_SIZE(meson_sar_adc_iio_channels); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) @@ -1078,6 +1239,22 @@ static int meson_sar_adc_probe(struct platform_device *pdev) priv->calibscale = MILLION; + if (priv->param->temperature_trimming_bits) { + ret = meson_sar_adc_temp_sensor_init(indio_dev); + if (ret) + return ret; + } + + if (priv->temperature_sensor_calibrated) { + indio_dev->channels = meson_sar_adc_and_temp_iio_channels; + indio_dev->num_channels = + ARRAY_SIZE(meson_sar_adc_and_temp_iio_channels); + } else { + indio_dev->channels = meson_sar_adc_iio_channels; + indio_dev->num_channels = + ARRAY_SIZE(meson_sar_adc_iio_channels); + } + ret = meson_sar_adc_init(indio_dev); if (ret) goto err; diff --git a/drivers/iio/adc/rcar-gyroadc.c b/drivers/iio/adc/rcar-gyroadc.c index 4e982b51bcda..2c0d0316d149 100644 --- a/drivers/iio/adc/rcar-gyroadc.c +++ b/drivers/iio/adc/rcar-gyroadc.c @@ -1,17 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Renesas R-Car GyroADC driver * * Copyright 2016 Marek Vasut <marek.vasut@gmail.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/module.h> diff --git a/drivers/iio/adc/sc27xx_adc.c b/drivers/iio/adc/sc27xx_adc.c index 7940b23dcad9..f7f7a18904b4 100644 --- a/drivers/iio/adc/sc27xx_adc.c +++ b/drivers/iio/adc/sc27xx_adc.c @@ -52,6 +52,9 @@ /* Timeout (ms) for the trylock of hardware spinlocks */ #define SC27XX_ADC_HWLOCK_TIMEOUT 5000 +/* Timeout (ms) for ADC data conversion according to ADC datasheet */ +#define SC27XX_ADC_RDY_TIMEOUT 100 + /* Maximum ADC channel number */ #define SC27XX_ADC_CHANNEL_MAX 32 @@ -223,7 +226,14 @@ static int sc27xx_adc_read(struct sc27xx_adc_data *data, int channel, if (ret) goto disable_adc; - wait_for_completion(&data->completion); + ret = wait_for_completion_timeout(&data->completion, + msecs_to_jiffies(SC27XX_ADC_RDY_TIMEOUT)); + if (!ret) { + dev_err(data->dev, "read ADC data timeout\n"); + ret = -ETIMEDOUT; + } else { + ret = 0; + } disable_adc: regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CTL, diff --git a/drivers/iio/adc/ti-adc128s052.c b/drivers/iio/adc/ti-adc128s052.c index 7cf39b3e2416..1e5a936b5b6a 100644 --- a/drivers/iio/adc/ti-adc128s052.c +++ b/drivers/iio/adc/ti-adc128s052.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2014 Angelo Compagnucci <angelo.compagnucci@gmail.com> * @@ -6,16 +7,14 @@ * http://www.ti.com/lit/ds/symlink/adc128s052.pdf * http://www.ti.com/lit/ds/symlink/adc122s021.pdf * http://www.ti.com/lit/ds/symlink/adc124s021.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/acpi.h> #include <linux/err.h> #include <linux/spi/spi.h> #include <linux/module.h> #include <linux/iio/iio.h> +#include <linux/property.h> #include <linux/regulator/consumer.h> struct adc128_configuration { @@ -135,10 +134,15 @@ static const struct iio_info adc128_info = { static int adc128_probe(struct spi_device *spi) { struct iio_dev *indio_dev; + unsigned int config; struct adc128 *adc; - int config = spi_get_device_id(spi)->driver_data; int ret; + if (dev_fwnode(&spi->dev)) + config = (unsigned long) device_get_match_data(&spi->dev); + else + config = spi_get_device_id(spi)->driver_data; + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc)); if (!indio_dev) return -ENOMEM; @@ -186,23 +190,40 @@ static int adc128_remove(struct spi_device *spi) static const struct of_device_id adc128_of_match[] = { { .compatible = "ti,adc128s052", }, { .compatible = "ti,adc122s021", }, + { .compatible = "ti,adc122s051", }, + { .compatible = "ti,adc122s101", }, { .compatible = "ti,adc124s021", }, + { .compatible = "ti,adc124s051", }, + { .compatible = "ti,adc124s101", }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, adc128_of_match); static const struct spi_device_id adc128_id[] = { - { "adc128s052", 0}, /* index into adc128_config */ - { "adc122s021", 1}, - { "adc124s021", 2}, + { "adc128s052", 0 }, /* index into adc128_config */ + { "adc122s021", 1 }, + { "adc122s051", 1 }, + { "adc122s101", 1 }, + { "adc124s021", 2 }, + { "adc124s051", 2 }, + { "adc124s101", 2 }, { } }; MODULE_DEVICE_TABLE(spi, adc128_id); +#ifdef CONFIG_ACPI +static const struct acpi_device_id adc128_acpi_match[] = { + { "AANT1280", 2 }, /* ADC124S021 compatible ACPI ID */ + { } +}; +MODULE_DEVICE_TABLE(acpi, adc128_acpi_match); +#endif + static struct spi_driver adc128_driver = { .driver = { .name = "adc128s052", .of_match_table = of_match_ptr(adc128_of_match), + .acpi_match_table = ACPI_PTR(adc128_acpi_match), }, .probe = adc128_probe, .remove = adc128_remove, diff --git a/drivers/iio/common/ssp_sensors/ssp_dev.c b/drivers/iio/common/ssp_sensors/ssp_dev.c index af3aa38f67cd..9e13be2c0cb9 100644 --- a/drivers/iio/common/ssp_sensors/ssp_dev.c +++ b/drivers/iio/common/ssp_sensors/ssp_dev.c @@ -462,43 +462,35 @@ static struct ssp_data *ssp_parse_dt(struct device *dev) data->mcu_ap_gpio = of_get_named_gpio(node, "mcu-ap-gpios", 0); if (data->mcu_ap_gpio < 0) - goto err_free_pd; + return NULL; data->ap_mcu_gpio = of_get_named_gpio(node, "ap-mcu-gpios", 0); if (data->ap_mcu_gpio < 0) - goto err_free_pd; + return NULL; data->mcu_reset_gpio = of_get_named_gpio(node, "mcu-reset-gpios", 0); if (data->mcu_reset_gpio < 0) - goto err_free_pd; + return NULL; ret = devm_gpio_request_one(dev, data->ap_mcu_gpio, GPIOF_OUT_INIT_HIGH, "ap-mcu-gpios"); if (ret) - goto err_free_pd; + return NULL; ret = devm_gpio_request_one(dev, data->mcu_reset_gpio, GPIOF_OUT_INIT_HIGH, "mcu-reset-gpios"); if (ret) - goto err_ap_mcu; + return NULL; match = of_match_node(ssp_of_match, node); if (!match) - goto err_mcu_reset_gpio; + return NULL; data->sensorhub_info = match->data; dev_set_drvdata(dev, data); return data; - -err_mcu_reset_gpio: - devm_gpio_free(dev, data->mcu_reset_gpio); -err_ap_mcu: - devm_gpio_free(dev, data->ap_mcu_gpio); -err_free_pd: - devm_kfree(dev, data); - return NULL; } #else static struct ssp_data *ssp_parse_dt(struct device *pdev) diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index 26fbd1bd9413..e50c975250e9 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -133,7 +133,7 @@ static int st_sensors_match_fs(struct st_sensor_settings *sensor_settings, for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) { if (sensor_settings->fs.fs_avl[i].num == 0) - goto st_sensors_match_odr_error; + return ret; if (sensor_settings->fs.fs_avl[i].num == fs) { *index_fs_avl = i; @@ -142,7 +142,6 @@ static int st_sensors_match_fs(struct st_sensor_settings *sensor_settings, } } -st_sensors_match_odr_error: return ret; } diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c index fdcc5a891958..224596b0e189 100644 --- a/drivers/iio/common/st_sensors/st_sensors_trigger.c +++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c @@ -104,7 +104,7 @@ static irqreturn_t st_sensors_irq_thread(int irq, void *p) return IRQ_HANDLED; /* - * If we are using egde IRQs, new samples arrived while processing + * If we are using edge IRQs, new samples arrived while processing * the IRQ and those may be missed unless we pick them here, so poll * again. If the sensor delivery frequency is very high, this thread * turns into a polled loop handler. @@ -148,7 +148,7 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev, if (!sdata->sensor_settings->drdy_irq.addr_ihl) { dev_err(&indio_dev->dev, "falling/low specified for IRQ " - "but hardware only support rising/high: " + "but hardware supports only rising/high: " "will request rising/high\n"); if (irq_trig == IRQF_TRIGGER_FALLING) irq_trig = IRQF_TRIGGER_RISING; diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index bb2057fd1b6f..851b61eaf3da 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -366,6 +366,15 @@ config TI_DAC5571 If compiled as a module, it will be called ti-dac5571. +config TI_DAC7311 + tristate "Texas Instruments 8/10/12-bit 1-channel DAC driver" + depends on SPI + help + Driver for the Texas Instruments + DAC7311, DAC6311, DAC5311. + + If compiled as a module, it will be called ti-dac7311. + config VF610_DAC tristate "Vybrid vf610 DAC driver" depends on OF diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 2ac93cc4a389..f0a37c93de8e 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -40,4 +40,5 @@ 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_TI_DAC7311) += ti-dac7311.o obj-$(CONFIG_VF610_DAC) += vf610_dac.o diff --git a/drivers/iio/dac/dpot-dac.c b/drivers/iio/dac/dpot-dac.c index a791d0a09d3b..4a6111b7e86c 100644 --- a/drivers/iio/dac/dpot-dac.c +++ b/drivers/iio/dac/dpot-dac.c @@ -74,11 +74,11 @@ static int dpot_dac_read_raw(struct iio_dev *indio_dev, case IIO_VAL_INT: /* * Convert integer scale to fractional scale by - * setting the denominator (val2) to one... + * setting the denominator (val2) to one, and... */ *val2 = 1; ret = IIO_VAL_FRACTIONAL; - /* ...and fall through. */ + /* fall through */ case IIO_VAL_FRACTIONAL: *val *= regulator_get_voltage(dac->vref) / 1000; *val2 *= dac->max_ohms; diff --git a/drivers/iio/dac/ti-dac7311.c b/drivers/iio/dac/ti-dac7311.c new file mode 100644 index 000000000000..6f5df1a30a1c --- /dev/null +++ b/drivers/iio/dac/ti-dac7311.c @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: GPL-2.0 +/* ti-dac7311.c - Texas Instruments 8/10/12-bit 1-channel DAC driver + * + * Copyright (C) 2018 CMC NV + * + * http://www.ti.com/lit/ds/symlink/dac7311.pdf + */ + +#include <linux/iio/iio.h> +#include <linux/module.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> + +enum { + ID_DAC5311 = 0, + ID_DAC6311, + ID_DAC7311, +}; + +enum { + POWER_1KOHM_TO_GND = 0, + POWER_100KOHM_TO_GND, + POWER_TRI_STATE, +}; + +struct ti_dac_spec { + u8 resolution; +}; + +static const struct ti_dac_spec ti_dac_spec[] = { + [ID_DAC5311] = { .resolution = 8 }, + [ID_DAC6311] = { .resolution = 10 }, + [ID_DAC7311] = { .resolution = 12 }, +}; + +/** + * struct ti_dac_chip - TI DAC chip + * @lock: protects write sequences + * @vref: regulator generating Vref + * @spi: SPI device to send data to the device + * @val: cached value + * @powerdown: whether the chip is powered down + * @powerdown_mode: selected by the user + * @resolution: resolution of the chip + * @buf: buffer for transfer data + */ +struct ti_dac_chip { + struct mutex lock; + struct regulator *vref; + struct spi_device *spi; + u16 val; + bool powerdown; + u8 powerdown_mode; + u8 resolution; + u8 buf[2] ____cacheline_aligned; +}; + +static u8 ti_dac_get_power(struct ti_dac_chip *ti_dac, bool powerdown) +{ + if (powerdown) + return ti_dac->powerdown_mode + 1; + + return 0; +} + +static int ti_dac_cmd(struct ti_dac_chip *ti_dac, u8 power, u16 val) +{ + u8 shift = 14 - ti_dac->resolution; + + ti_dac->buf[0] = (val << shift) & 0xFF; + ti_dac->buf[1] = (power << 6) | (val >> (8 - shift)); + return spi_write(ti_dac->spi, ti_dac->buf, 2); +} + +static const char * const ti_dac_powerdown_modes[] = { + "1kohm_to_gnd", + "100kohm_to_gnd", + "three_state", +}; + +static int ti_dac_get_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ti_dac_chip *ti_dac = iio_priv(indio_dev); + + return ti_dac->powerdown_mode; +} + +static int ti_dac_set_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct ti_dac_chip *ti_dac = iio_priv(indio_dev); + + ti_dac->powerdown_mode = mode; + return 0; +} + +static const struct iio_enum ti_dac_powerdown_mode = { + .items = ti_dac_powerdown_modes, + .num_items = ARRAY_SIZE(ti_dac_powerdown_modes), + .get = ti_dac_get_powerdown_mode, + .set = ti_dac_set_powerdown_mode, +}; + +static ssize_t ti_dac_read_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct ti_dac_chip *ti_dac = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", ti_dac->powerdown); +} + +static ssize_t ti_dac_write_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ti_dac_chip *ti_dac = iio_priv(indio_dev); + bool powerdown; + u8 power; + int ret; + + ret = strtobool(buf, &powerdown); + if (ret) + return ret; + + power = ti_dac_get_power(ti_dac, powerdown); + + mutex_lock(&ti_dac->lock); + ret = ti_dac_cmd(ti_dac, power, 0); + if (!ret) + ti_dac->powerdown = powerdown; + mutex_unlock(&ti_dac->lock); + + return ret ? ret : len; +} + +static const struct iio_chan_spec_ext_info ti_dac_ext_info[] = { + { + .name = "powerdown", + .read = ti_dac_read_powerdown, + .write = ti_dac_write_powerdown, + .shared = IIO_SHARED_BY_TYPE, + }, + IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, &ti_dac_powerdown_mode), + IIO_ENUM_AVAILABLE("powerdown_mode", &ti_dac_powerdown_mode), + { }, +}; + +#define TI_DAC_CHANNEL(chan) { \ + .type = IIO_VOLTAGE, \ + .channel = (chan), \ + .output = true, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .ext_info = ti_dac_ext_info, \ +} + +static const struct iio_chan_spec ti_dac_channels[] = { + TI_DAC_CHANNEL(0), +}; + +static int ti_dac_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ti_dac_chip *ti_dac = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + *val = ti_dac->val; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + ret = regulator_get_voltage(ti_dac->vref); + if (ret < 0) + return ret; + + *val = ret / 1000; + *val2 = ti_dac->resolution; + return IIO_VAL_FRACTIONAL_LOG2; + } + + return -EINVAL; +} + +static int ti_dac_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ti_dac_chip *ti_dac = iio_priv(indio_dev); + u8 power = ti_dac_get_power(ti_dac, ti_dac->powerdown); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (ti_dac->val == val) + return 0; + + if (val >= (1 << ti_dac->resolution) || val < 0) + return -EINVAL; + + if (ti_dac->powerdown) + return -EBUSY; + + mutex_lock(&ti_dac->lock); + ret = ti_dac_cmd(ti_dac, power, val); + if (!ret) + ti_dac->val = val; + mutex_unlock(&ti_dac->lock); + break; + + default: + ret = -EINVAL; + } + + return ret; +} + +static int ti_dac_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 ti_dac_info = { + .read_raw = ti_dac_read_raw, + .write_raw = ti_dac_write_raw, + .write_raw_get_fmt = ti_dac_write_raw_get_fmt, +}; + +static int ti_dac_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + const struct ti_dac_spec *spec; + struct ti_dac_chip *ti_dac; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*ti_dac)); + if (!indio_dev) { + dev_err(dev, "can not allocate iio device\n"); + return -ENOMEM; + } + + spi->mode = SPI_MODE_1; + spi->bits_per_word = 16; + spi_setup(spi); + + indio_dev->dev.parent = dev; + indio_dev->dev.of_node = spi->dev.of_node; + indio_dev->info = &ti_dac_info; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ti_dac_channels; + spi_set_drvdata(spi, indio_dev); + + ti_dac = iio_priv(indio_dev); + ti_dac->powerdown = false; + ti_dac->spi = spi; + + spec = &ti_dac_spec[spi_get_device_id(spi)->driver_data]; + indio_dev->num_channels = 1; + ti_dac->resolution = spec->resolution; + + ti_dac->vref = devm_regulator_get(dev, "vref"); + if (IS_ERR(ti_dac->vref)) { + dev_err(dev, "error to get regulator\n"); + return PTR_ERR(ti_dac->vref); + } + + ret = regulator_enable(ti_dac->vref); + if (ret < 0) { + dev_err(dev, "can not enable regulator\n"); + return ret; + } + + mutex_init(&ti_dac->lock); + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(dev, "fail to register iio device: %d\n", ret); + goto err; + } + + return 0; + +err: + mutex_destroy(&ti_dac->lock); + regulator_disable(ti_dac->vref); + return ret; +} + +static int ti_dac_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ti_dac_chip *ti_dac = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + mutex_destroy(&ti_dac->lock); + regulator_disable(ti_dac->vref); + return 0; +} + +static const struct of_device_id ti_dac_of_id[] = { + { .compatible = "ti,dac5311" }, + { .compatible = "ti,dac6311" }, + { .compatible = "ti,dac7311" }, + { } +}; +MODULE_DEVICE_TABLE(of, ti_dac_of_id); + +static const struct spi_device_id ti_dac_spi_id[] = { + { "dac5311", ID_DAC5311 }, + { "dac6311", ID_DAC6311 }, + { "dac7311", ID_DAC7311 }, + { } +}; +MODULE_DEVICE_TABLE(spi, ti_dac_spi_id); + +static struct spi_driver ti_dac_driver = { + .driver = { + .name = "ti-dac7311", + .of_match_table = ti_dac_of_id, + }, + .probe = ti_dac_probe, + .remove = ti_dac_remove, + .id_table = ti_dac_spi_id, +}; +module_spi_driver(ti_dac_driver); + +MODULE_AUTHOR("Charles-Antoine Couret <charles-antoine.couret@essensium.com>"); +MODULE_DESCRIPTION("Texas Instruments 8/10/12-bit 1-channel DAC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/imu/st_lsm6dsx/Makefile b/drivers/iio/imu/st_lsm6dsx/Makefile index 35919febea2a..e5f733ce6e11 100644 --- a/drivers/iio/imu/st_lsm6dsx/Makefile +++ b/drivers/iio/imu/st_lsm6dsx/Makefile @@ -1,4 +1,5 @@ -st_lsm6dsx-y := st_lsm6dsx_core.o st_lsm6dsx_buffer.o +st_lsm6dsx-y := st_lsm6dsx_core.o st_lsm6dsx_buffer.o \ + st_lsm6dsx_shub.o obj-$(CONFIG_IIO_ST_LSM6DSX) += st_lsm6dsx.o obj-$(CONFIG_IIO_ST_LSM6DSX_I2C) += st_lsm6dsx_i2c.o diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h index ef73519a0fb6..d1d8d07a0714 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h @@ -43,6 +43,24 @@ enum st_lsm6dsx_hw_id { * ST_LSM6DSX_TAGGED_SAMPLE_SIZE) #define ST_LSM6DSX_SHIFT_VAL(val, mask) (((val) << __ffs(mask)) & (mask)) +#define ST_LSM6DSX_CHANNEL(chan_type, addr, mod, scan_idx) \ +{ \ + .type = chan_type, \ + .address = addr, \ + .modified = 1, \ + .channel2 = mod, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = scan_idx, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_LE, \ + }, \ +} + struct st_lsm6dsx_reg { u8 addr; u8 mask; @@ -50,6 +68,28 @@ struct st_lsm6dsx_reg { struct st_lsm6dsx_hw; +struct st_lsm6dsx_odr { + u16 hz; + u8 val; +}; + +#define ST_LSM6DSX_ODR_LIST_SIZE 6 +struct st_lsm6dsx_odr_table_entry { + struct st_lsm6dsx_reg reg; + struct st_lsm6dsx_odr odr_avl[ST_LSM6DSX_ODR_LIST_SIZE]; +}; + +struct st_lsm6dsx_fs { + u32 gain; + u8 val; +}; + +#define ST_LSM6DSX_FS_LIST_SIZE 4 +struct st_lsm6dsx_fs_table_entry { + struct st_lsm6dsx_reg reg; + struct st_lsm6dsx_fs fs_avl[ST_LSM6DSX_FS_LIST_SIZE]; +}; + /** * struct st_lsm6dsx_fifo_ops - ST IMU FIFO settings * @read_fifo: Read FIFO callback. @@ -85,6 +125,70 @@ struct st_lsm6dsx_hw_ts_settings { }; /** + * struct st_lsm6dsx_shub_settings - ST IMU hw i2c controller settings + * @page_mux: register page mux info (addr + mask). + * @master_en: master config register info (addr + mask). + * @pullup_en: i2c controller pull-up register info (addr + mask). + * @aux_sens: aux sensor register info (addr + mask). + * @wr_once: write_once register info (addr + mask). + * @shub_out: sensor hub first output register info. + * @slv0_addr: slave0 address in secondary page. + * @dw_slv0_addr: slave0 write register address in secondary page. + * @batch_en: Enable/disable FIFO batching. + */ +struct st_lsm6dsx_shub_settings { + struct st_lsm6dsx_reg page_mux; + struct st_lsm6dsx_reg master_en; + struct st_lsm6dsx_reg pullup_en; + struct st_lsm6dsx_reg aux_sens; + struct st_lsm6dsx_reg wr_once; + u8 shub_out; + u8 slv0_addr; + u8 dw_slv0_addr; + u8 batch_en; +}; + +enum st_lsm6dsx_ext_sensor_id { + ST_LSM6DSX_ID_MAGN, +}; + +/** + * struct st_lsm6dsx_ext_dev_settings - i2c controller slave settings + * @i2c_addr: I2c slave address list. + * @wai: Wai address info. + * @id: external sensor id. + * @odr: Output data rate of the sensor [Hz]. + * @gain: Configured sensor sensitivity. + * @temp_comp: Temperature compensation register info (addr + mask). + * @pwr_table: Power on register info (addr + mask). + * @off_canc: Offset cancellation register info (addr + mask). + * @bdu: Block data update register info (addr + mask). + * @out: Output register info. + */ +struct st_lsm6dsx_ext_dev_settings { + u8 i2c_addr[2]; + struct { + u8 addr; + u8 val; + } wai; + enum st_lsm6dsx_ext_sensor_id id; + struct st_lsm6dsx_odr_table_entry odr_table; + struct st_lsm6dsx_fs_table_entry fs_table; + struct st_lsm6dsx_reg temp_comp; + struct { + struct st_lsm6dsx_reg reg; + u8 off_val; + u8 on_val; + } pwr_table; + struct st_lsm6dsx_reg off_canc; + struct st_lsm6dsx_reg bdu; + struct { + u8 addr; + u8 len; + } out; +}; + +/** * struct st_lsm6dsx_settings - ST IMU sensor settings * @wai: Sensor WhoAmI default value. * @max_fifo_size: Sensor max fifo length in FIFO words. @@ -93,6 +197,7 @@ struct st_lsm6dsx_hw_ts_settings { * @batch: List of FIFO batching register info (addr + mask). * @fifo_ops: Sensor hw FIFO parameters. * @ts_settings: Hw timer related settings. + * @shub_settings: i2c controller related settings. */ struct st_lsm6dsx_settings { u8 wai; @@ -102,11 +207,15 @@ struct st_lsm6dsx_settings { struct st_lsm6dsx_reg batch[ST_LSM6DSX_MAX_ID]; struct st_lsm6dsx_fifo_ops fifo_ops; struct st_lsm6dsx_hw_ts_settings ts_settings; + struct st_lsm6dsx_shub_settings shub_settings; }; enum st_lsm6dsx_sensor_id { - ST_LSM6DSX_ID_ACC, ST_LSM6DSX_ID_GYRO, + ST_LSM6DSX_ID_ACC, + ST_LSM6DSX_ID_EXT0, + ST_LSM6DSX_ID_EXT1, + ST_LSM6DSX_ID_EXT2, ST_LSM6DSX_ID_MAX, }; @@ -126,6 +235,7 @@ enum st_lsm6dsx_fifo_mode { * @sip: Number of samples in a given pattern. * @decimator: FIFO decimation factor. * @ts_ref: Sensor timestamp reference for hw one. + * @ext_info: Sensor settings if it is connected to i2c controller */ struct st_lsm6dsx_sensor { char name[32]; @@ -139,6 +249,11 @@ struct st_lsm6dsx_sensor { u8 sip; u8 decimator; s64 ts_ref; + + struct { + const struct st_lsm6dsx_ext_dev_settings *settings; + u8 addr; + } ext_info; }; /** @@ -148,6 +263,7 @@ struct st_lsm6dsx_sensor { * @irq: Device interrupt line (I2C or SPI). * @fifo_lock: Mutex to prevent concurrent access to the hw FIFO. * @conf_lock: Mutex to prevent concurrent FIFO configuration update. + * @page_lock: Mutex to prevent concurrent memory page configuration. * @fifo_mode: FIFO operating mode supported by the device. * @enable_mask: Enabled sensor bitmask. * @ts_sip: Total number of timestamp samples in a given pattern. @@ -163,6 +279,7 @@ struct st_lsm6dsx_hw { struct mutex fifo_lock; struct mutex conf_lock; + struct mutex page_lock; enum st_lsm6dsx_fifo_mode fifo_mode; u8 enable_mask; @@ -176,13 +293,15 @@ struct st_lsm6dsx_hw { const struct st_lsm6dsx_settings *settings; }; +static const unsigned long st_lsm6dsx_available_scan_masks[] = {0x7, 0x0}; extern const struct dev_pm_ops st_lsm6dsx_pm_ops; int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name, struct regmap *regmap); -int st_lsm6dsx_sensor_enable(struct st_lsm6dsx_sensor *sensor); -int st_lsm6dsx_sensor_disable(struct st_lsm6dsx_sensor *sensor); +int st_lsm6dsx_sensor_set_enable(struct st_lsm6dsx_sensor *sensor, + bool enable); int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw); +int st_lsm6dsx_set_watermark(struct iio_dev *iio_dev, unsigned int val); int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, u16 watermark); int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw); @@ -191,5 +310,47 @@ int st_lsm6dsx_set_fifo_mode(struct st_lsm6dsx_hw *hw, int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw); int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw); int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr, u8 *val); +int st_lsm6dsx_shub_probe(struct st_lsm6dsx_hw *hw, const char *name); +int st_lsm6dsx_shub_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable); +int st_lsm6dsx_set_page(struct st_lsm6dsx_hw *hw, bool enable); + +static inline int +st_lsm6dsx_update_bits_locked(struct st_lsm6dsx_hw *hw, unsigned int addr, + unsigned int mask, unsigned int val) +{ + int err; + + mutex_lock(&hw->page_lock); + err = regmap_update_bits(hw->regmap, addr, mask, val); + mutex_unlock(&hw->page_lock); + + return err; +} + +static inline int +st_lsm6dsx_read_locked(struct st_lsm6dsx_hw *hw, unsigned int addr, + void *val, unsigned int len) +{ + int err; + + mutex_lock(&hw->page_lock); + err = regmap_bulk_read(hw->regmap, addr, val, len); + mutex_unlock(&hw->page_lock); + + return err; +} + +static inline int +st_lsm6dsx_write_locked(struct st_lsm6dsx_hw *hw, unsigned int addr, + unsigned int val) +{ + int err; + + mutex_lock(&hw->page_lock); + err = regmap_write(hw->regmap, addr, val); + mutex_unlock(&hw->page_lock); + + return err; +} #endif /* ST_LSM6DSX_H */ diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c index b5263fc522ca..2c0d3763405a 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c @@ -68,6 +68,9 @@ enum st_lsm6dsx_fifo_tag { ST_LSM6DSX_GYRO_TAG = 0x01, ST_LSM6DSX_ACC_TAG = 0x02, ST_LSM6DSX_TS_TAG = 0x04, + ST_LSM6DSX_EXT0_TAG = 0x0f, + ST_LSM6DSX_EXT1_TAG = 0x10, + ST_LSM6DSX_EXT2_TAG = 0x11, }; static const @@ -102,6 +105,9 @@ static void st_lsm6dsx_get_max_min_odr(struct st_lsm6dsx_hw *hw, *max_odr = 0, *min_odr = ~0; for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { + if (!hw->iio_devs[i]) + continue; + sensor = iio_priv(hw->iio_devs[i]); if (!(hw->enable_mask & BIT(sensor->id))) @@ -125,6 +131,9 @@ static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw) for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { const struct st_lsm6dsx_reg *dec_reg; + if (!hw->iio_devs[i]) + continue; + sensor = iio_priv(hw->iio_devs[i]); /* update fifo decimators and sample in pattern */ if (hw->enable_mask & BIT(sensor->id)) { @@ -142,8 +151,9 @@ static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw) if (dec_reg->addr) { int val = ST_LSM6DSX_SHIFT_VAL(data, dec_reg->mask); - err = regmap_update_bits(hw->regmap, dec_reg->addr, - dec_reg->mask, val); + err = st_lsm6dsx_update_bits_locked(hw, dec_reg->addr, + dec_reg->mask, + val); if (err < 0) return err; } @@ -162,8 +172,8 @@ static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw) int val, ts_dec = !!hw->ts_sip; val = ST_LSM6DSX_SHIFT_VAL(ts_dec, ts_dec_reg->mask); - err = regmap_update_bits(hw->regmap, ts_dec_reg->addr, - ts_dec_reg->mask, val); + err = st_lsm6dsx_update_bits_locked(hw, ts_dec_reg->addr, + ts_dec_reg->mask, val); } return err; } @@ -171,12 +181,12 @@ static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw) int st_lsm6dsx_set_fifo_mode(struct st_lsm6dsx_hw *hw, enum st_lsm6dsx_fifo_mode fifo_mode) { + unsigned int data; int err; - err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_FIFO_MODE_ADDR, - ST_LSM6DSX_FIFO_MODE_MASK, - FIELD_PREP(ST_LSM6DSX_FIFO_MODE_MASK, - fifo_mode)); + data = FIELD_PREP(ST_LSM6DSX_FIFO_MODE_MASK, fifo_mode); + err = st_lsm6dsx_update_bits_locked(hw, ST_LSM6DSX_REG_FIFO_MODE_ADDR, + ST_LSM6DSX_FIFO_MODE_MASK, data); if (err < 0) return err; @@ -207,15 +217,15 @@ static int st_lsm6dsx_set_fifo_odr(struct st_lsm6dsx_sensor *sensor, data = 0; } val = ST_LSM6DSX_SHIFT_VAL(data, batch_reg->mask); - return regmap_update_bits(hw->regmap, batch_reg->addr, - batch_reg->mask, val); + return st_lsm6dsx_update_bits_locked(hw, batch_reg->addr, + batch_reg->mask, val); } else { data = hw->enable_mask ? ST_LSM6DSX_MAX_FIFO_ODR_VAL : 0; - return regmap_update_bits(hw->regmap, - ST_LSM6DSX_REG_FIFO_MODE_ADDR, - ST_LSM6DSX_FIFO_ODR_MASK, - FIELD_PREP(ST_LSM6DSX_FIFO_ODR_MASK, - data)); + return st_lsm6dsx_update_bits_locked(hw, + ST_LSM6DSX_REG_FIFO_MODE_ADDR, + ST_LSM6DSX_FIFO_ODR_MASK, + FIELD_PREP(ST_LSM6DSX_FIFO_ODR_MASK, + data)); } } @@ -231,6 +241,9 @@ int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, u16 watermark) return 0; for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { + if (!hw->iio_devs[i]) + continue; + cur_sensor = iio_priv(hw->iio_devs[i]); if (!(hw->enable_mask & BIT(cur_sensor->id))) @@ -246,19 +259,23 @@ int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, u16 watermark) fifo_watermark = (fifo_watermark / hw->sip) * hw->sip; fifo_watermark = fifo_watermark * hw->settings->fifo_ops.th_wl; + mutex_lock(&hw->page_lock); err = regmap_read(hw->regmap, hw->settings->fifo_ops.fifo_th.addr + 1, &data); if (err < 0) - return err; + goto out; fifo_th_mask = hw->settings->fifo_ops.fifo_th.mask; fifo_watermark = ((data << 8) & ~fifo_th_mask) | (fifo_watermark & fifo_th_mask); wdata = cpu_to_le16(fifo_watermark); - return regmap_bulk_write(hw->regmap, - hw->settings->fifo_ops.fifo_th.addr, - &wdata, sizeof(wdata)); + err = regmap_bulk_write(hw->regmap, + hw->settings->fifo_ops.fifo_th.addr, + &wdata, sizeof(wdata)); +out: + mutex_unlock(&hw->page_lock); + return err; } static int st_lsm6dsx_reset_hw_ts(struct st_lsm6dsx_hw *hw) @@ -267,12 +284,15 @@ static int st_lsm6dsx_reset_hw_ts(struct st_lsm6dsx_hw *hw) int i, err; /* reset hw ts counter */ - err = regmap_write(hw->regmap, ST_LSM6DSX_REG_TS_RESET_ADDR, - ST_LSM6DSX_TS_RESET_VAL); + err = st_lsm6dsx_write_locked(hw, ST_LSM6DSX_REG_TS_RESET_ADDR, + ST_LSM6DSX_TS_RESET_VAL); if (err < 0) return err; for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { + if (!hw->iio_devs[i]) + continue; + sensor = iio_priv(hw->iio_devs[i]); /* * store enable buffer timestamp as reference for @@ -297,8 +317,8 @@ static inline int st_lsm6dsx_read_block(struct st_lsm6dsx_hw *hw, u8 addr, while (read_len < data_len) { word_len = min_t(unsigned int, data_len - read_len, max_word_len); - err = regmap_bulk_read(hw->regmap, addr, data + read_len, - word_len); + err = st_lsm6dsx_read_locked(hw, addr, data + read_len, + word_len); if (err < 0) return err; read_len += word_len; @@ -328,9 +348,9 @@ int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw) __le16 fifo_status; s64 ts = 0; - err = regmap_bulk_read(hw->regmap, - hw->settings->fifo_ops.fifo_diff.addr, - &fifo_status, sizeof(fifo_status)); + err = st_lsm6dsx_read_locked(hw, + hw->settings->fifo_ops.fifo_diff.addr, + &fifo_status, sizeof(fifo_status)); if (err < 0) { dev_err(hw->dev, "failed to read fifo status (err=%d)\n", err); @@ -436,6 +456,55 @@ int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw) return read_len; } +static int +st_lsm6dsx_push_tagged_data(struct st_lsm6dsx_hw *hw, u8 tag, + u8 *data, s64 ts) +{ + struct st_lsm6dsx_sensor *sensor; + struct iio_dev *iio_dev; + + /* + * EXT_TAG are managed in FIFO fashion so ST_LSM6DSX_EXT0_TAG + * corresponds to the first enabled channel, ST_LSM6DSX_EXT1_TAG + * to the second one and ST_LSM6DSX_EXT2_TAG to the last enabled + * channel + */ + switch (tag) { + case ST_LSM6DSX_GYRO_TAG: + iio_dev = hw->iio_devs[ST_LSM6DSX_ID_GYRO]; + break; + case ST_LSM6DSX_ACC_TAG: + iio_dev = hw->iio_devs[ST_LSM6DSX_ID_ACC]; + break; + case ST_LSM6DSX_EXT0_TAG: + if (hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT0)) + iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT0]; + else if (hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT1)) + iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT1]; + else + iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT2]; + break; + case ST_LSM6DSX_EXT1_TAG: + if ((hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT0)) && + (hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT1))) + iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT1]; + else + iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT2]; + break; + case ST_LSM6DSX_EXT2_TAG: + iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT2]; + break; + default: + return -EINVAL; + } + + sensor = iio_priv(iio_dev); + iio_push_to_buffers_with_timestamp(iio_dev, data, + ts + sensor->ts_ref); + + return 0; +} + /** * st_lsm6dsx_read_tagged_fifo() - LSM6DSO read FIFO routine * @hw: Pointer to instance of struct st_lsm6dsx_hw. @@ -455,9 +524,9 @@ int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw) __le16 fifo_status; s64 ts = 0; - err = regmap_bulk_read(hw->regmap, - hw->settings->fifo_ops.fifo_diff.addr, - &fifo_status, sizeof(fifo_status)); + err = st_lsm6dsx_read_locked(hw, + hw->settings->fifo_ops.fifo_diff.addr, + &fifo_status, sizeof(fifo_status)); if (err < 0) { dev_err(hw->dev, "failed to read fifo status (err=%d)\n", err); @@ -491,8 +560,7 @@ int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw) ST_LSM6DSX_SAMPLE_SIZE); tag = hw->buff[i] >> 3; - switch (tag) { - case ST_LSM6DSX_TS_TAG: + if (tag == ST_LSM6DSX_TS_TAG) { /* * hw timestamp is 4B long and it is stored * in FIFO according to this schema: @@ -509,19 +577,9 @@ int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw) if (!reset_ts && ts >= 0xffff0000) reset_ts = true; ts *= ST_LSM6DSX_TS_SENSITIVITY; - break; - case ST_LSM6DSX_GYRO_TAG: - iio_push_to_buffers_with_timestamp( - hw->iio_devs[ST_LSM6DSX_ID_GYRO], - iio_buff, gyro_sensor->ts_ref + ts); - break; - case ST_LSM6DSX_ACC_TAG: - iio_push_to_buffers_with_timestamp( - hw->iio_devs[ST_LSM6DSX_ID_ACC], - iio_buff, acc_sensor->ts_ref + ts); - break; - default: - break; + } else { + st_lsm6dsx_push_tagged_data(hw, tag, iio_buff, + ts); } } } @@ -562,19 +620,21 @@ static int st_lsm6dsx_update_fifo(struct iio_dev *iio_dev, bool enable) goto out; } - if (enable) { - err = st_lsm6dsx_sensor_enable(sensor); + if (sensor->id == ST_LSM6DSX_ID_EXT0 || + sensor->id == ST_LSM6DSX_ID_EXT1 || + sensor->id == ST_LSM6DSX_ID_EXT2) { + err = st_lsm6dsx_shub_set_enable(sensor, enable); if (err < 0) goto out; } else { - err = st_lsm6dsx_sensor_disable(sensor); + err = st_lsm6dsx_sensor_set_enable(sensor, enable); if (err < 0) goto out; - } - err = st_lsm6dsx_set_fifo_odr(sensor, enable); - if (err < 0) - goto out; + err = st_lsm6dsx_set_fifo_odr(sensor, enable); + if (err < 0) + goto out; + } err = st_lsm6dsx_update_decimators(hw); if (err < 0) @@ -690,6 +750,9 @@ int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw) } for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { + if (!hw->iio_devs[i]) + continue; + buffer = devm_iio_kfifo_allocate(hw->dev); if (!buffer) return -ENOMEM; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index 2ad3c610e4b6..12e29dda9b98 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -56,6 +56,7 @@ #define ST_LSM6DSX_REG_WHOAMI_ADDR 0x0f #define ST_LSM6DSX_REG_RESET_ADDR 0x12 #define ST_LSM6DSX_REG_RESET_MASK BIT(0) +#define ST_LSM6DSX_REG_BOOT_MASK BIT(7) #define ST_LSM6DSX_REG_BDU_ADDR 0x12 #define ST_LSM6DSX_REG_BDU_MASK BIT(6) #define ST_LSM6DSX_REG_INT2_ON_INT1_ADDR 0x13 @@ -87,17 +88,6 @@ #define ST_LSM6DSX_GYRO_FS_1000_GAIN IIO_DEGREE_TO_RAD(35000) #define ST_LSM6DSX_GYRO_FS_2000_GAIN IIO_DEGREE_TO_RAD(70000) -struct st_lsm6dsx_odr { - u16 hz; - u8 val; -}; - -#define ST_LSM6DSX_ODR_LIST_SIZE 6 -struct st_lsm6dsx_odr_table_entry { - struct st_lsm6dsx_reg reg; - struct st_lsm6dsx_odr odr_avl[ST_LSM6DSX_ODR_LIST_SIZE]; -}; - static const struct st_lsm6dsx_odr_table_entry st_lsm6dsx_odr_table[] = { [ST_LSM6DSX_ID_ACC] = { .reg = { @@ -125,17 +115,6 @@ static const struct st_lsm6dsx_odr_table_entry st_lsm6dsx_odr_table[] = { } }; -struct st_lsm6dsx_fs { - u32 gain; - u8 val; -}; - -#define ST_LSM6DSX_FS_LIST_SIZE 4 -struct st_lsm6dsx_fs_table_entry { - struct st_lsm6dsx_reg reg; - struct st_lsm6dsx_fs fs_avl[ST_LSM6DSX_FS_LIST_SIZE]; -}; - static const struct st_lsm6dsx_fs_table_entry st_lsm6dsx_fs_table[] = { [ST_LSM6DSX_ID_ACC] = { .reg = { @@ -341,27 +320,35 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .mask = GENMASK(7, 6), }, }, + .shub_settings = { + .page_mux = { + .addr = 0x01, + .mask = BIT(6), + }, + .master_en = { + .addr = 0x14, + .mask = BIT(2), + }, + .pullup_en = { + .addr = 0x14, + .mask = BIT(3), + }, + .aux_sens = { + .addr = 0x14, + .mask = GENMASK(1, 0), + }, + .wr_once = { + .addr = 0x14, + .mask = BIT(6), + }, + .shub_out = 0x02, + .slv0_addr = 0x15, + .dw_slv0_addr = 0x21, + .batch_en = BIT(3), + } }, }; -#define ST_LSM6DSX_CHANNEL(chan_type, addr, mod, scan_idx) \ -{ \ - .type = chan_type, \ - .address = addr, \ - .modified = 1, \ - .channel2 = mod, \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ - BIT(IIO_CHAN_INFO_SCALE), \ - .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ - .scan_index = scan_idx, \ - .scan_type = { \ - .sign = 's', \ - .realbits = 16, \ - .storagebits = 16, \ - .endianness = IIO_LE, \ - }, \ -} - static const struct iio_chan_spec st_lsm6dsx_acc_channels[] = { ST_LSM6DSX_CHANNEL(IIO_ACCEL, ST_LSM6DSX_REG_ACC_OUT_X_L_ADDR, IIO_MOD_X, 0), @@ -382,6 +369,21 @@ static const struct iio_chan_spec st_lsm6dsx_gyro_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(3), }; +int st_lsm6dsx_set_page(struct st_lsm6dsx_hw *hw, bool enable) +{ + const struct st_lsm6dsx_shub_settings *hub_settings; + unsigned int data; + int err; + + hub_settings = &hw->settings->shub_settings; + data = ST_LSM6DSX_SHIFT_VAL(enable, hub_settings->page_mux.mask); + err = regmap_update_bits(hw->regmap, hub_settings->page_mux.addr, + hub_settings->page_mux.mask, data); + usleep_range(100, 150); + + return err; +} + static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id) { int err, i, j, data; @@ -421,6 +423,7 @@ static int st_lsm6dsx_set_full_scale(struct st_lsm6dsx_sensor *sensor, { struct st_lsm6dsx_hw *hw = sensor->hw; const struct st_lsm6dsx_reg *reg; + unsigned int data; int i, err; u8 val; @@ -433,8 +436,8 @@ static int st_lsm6dsx_set_full_scale(struct st_lsm6dsx_sensor *sensor, val = st_lsm6dsx_fs_table[sensor->id].fs_avl[i].val; reg = &st_lsm6dsx_fs_table[sensor->id].reg; - err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, - ST_LSM6DSX_SHIFT_VAL(val, reg->mask)); + data = ST_LSM6DSX_SHIFT_VAL(val, reg->mask); + err = st_lsm6dsx_update_bits_locked(hw, reg->addr, reg->mask, data); if (err < 0) return err; @@ -448,7 +451,11 @@ int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr, u8 *val) int i; for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++) - if (st_lsm6dsx_odr_table[sensor->id].odr_avl[i].hz == odr) + /* + * ext devices can run at different odr respect to + * accel sensor + */ + if (st_lsm6dsx_odr_table[sensor->id].odr_avl[i].hz >= odr) break; if (i == ST_LSM6DSX_ODR_LIST_SIZE) @@ -459,48 +466,86 @@ int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr, u8 *val) return 0; } -static int st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u16 odr) +static u16 st_lsm6dsx_check_odr_dependency(struct st_lsm6dsx_hw *hw, u16 odr, + enum st_lsm6dsx_sensor_id id) { - struct st_lsm6dsx_hw *hw = sensor->hw; - const struct st_lsm6dsx_reg *reg; - int err; - u8 val; - - err = st_lsm6dsx_check_odr(sensor, odr, &val); - if (err < 0) - return err; - - reg = &st_lsm6dsx_odr_table[sensor->id].reg; - return regmap_update_bits(hw->regmap, reg->addr, reg->mask, - ST_LSM6DSX_SHIFT_VAL(val, reg->mask)); + struct st_lsm6dsx_sensor *ref = iio_priv(hw->iio_devs[id]); + + if (odr > 0) { + if (hw->enable_mask & BIT(id)) + return max_t(u16, ref->odr, odr); + else + return odr; + } else { + return (hw->enable_mask & BIT(id)) ? ref->odr : 0; + } } -int st_lsm6dsx_sensor_enable(struct st_lsm6dsx_sensor *sensor) +static int st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u16 req_odr) { + struct st_lsm6dsx_sensor *ref_sensor = sensor; + struct st_lsm6dsx_hw *hw = sensor->hw; + const struct st_lsm6dsx_reg *reg; + unsigned int data; + u8 val = 0; int err; - err = st_lsm6dsx_set_odr(sensor, sensor->odr); - if (err < 0) - return err; + switch (sensor->id) { + case ST_LSM6DSX_ID_EXT0: + case ST_LSM6DSX_ID_EXT1: + case ST_LSM6DSX_ID_EXT2: + case ST_LSM6DSX_ID_ACC: { + u16 odr; + int i; + + /* + * i2c embedded controller relies on the accelerometer sensor as + * bus read/write trigger so we need to enable accel device + * at odr = max(accel_odr, ext_odr) in order to properly + * communicate with i2c slave devices + */ + ref_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]); + for (i = ST_LSM6DSX_ID_ACC; i < ST_LSM6DSX_ID_MAX; i++) { + if (!hw->iio_devs[i] || i == sensor->id) + continue; + + odr = st_lsm6dsx_check_odr_dependency(hw, req_odr, i); + if (odr != req_odr) + /* device already configured */ + return 0; + } + break; + } + default: + break; + } - sensor->hw->enable_mask |= BIT(sensor->id); + if (req_odr > 0) { + err = st_lsm6dsx_check_odr(ref_sensor, req_odr, &val); + if (err < 0) + return err; + } - return 0; + reg = &st_lsm6dsx_odr_table[ref_sensor->id].reg; + data = ST_LSM6DSX_SHIFT_VAL(val, reg->mask); + return st_lsm6dsx_update_bits_locked(hw, reg->addr, reg->mask, data); } -int st_lsm6dsx_sensor_disable(struct st_lsm6dsx_sensor *sensor) +int st_lsm6dsx_sensor_set_enable(struct st_lsm6dsx_sensor *sensor, + bool enable) { struct st_lsm6dsx_hw *hw = sensor->hw; - const struct st_lsm6dsx_reg *reg; + u16 odr = enable ? sensor->odr : 0; int err; - reg = &st_lsm6dsx_odr_table[sensor->id].reg; - err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, - ST_LSM6DSX_SHIFT_VAL(0, reg->mask)); + err = st_lsm6dsx_set_odr(sensor, odr); if (err < 0) return err; - sensor->hw->enable_mask &= ~BIT(sensor->id); + if (enable) + hw->enable_mask |= BIT(sensor->id); + else + hw->enable_mask &= ~BIT(sensor->id); return 0; } @@ -512,18 +557,18 @@ static int st_lsm6dsx_read_oneshot(struct st_lsm6dsx_sensor *sensor, int err, delay; __le16 data; - err = st_lsm6dsx_sensor_enable(sensor); + err = st_lsm6dsx_sensor_set_enable(sensor, true); if (err < 0) return err; delay = 1000000 / sensor->odr; usleep_range(delay, 2 * delay); - err = regmap_bulk_read(hw->regmap, addr, &data, sizeof(data)); + err = st_lsm6dsx_read_locked(hw, addr, &data, sizeof(data)); if (err < 0) return err; - st_lsm6dsx_sensor_disable(sensor); + st_lsm6dsx_sensor_set_enable(sensor, false); *val = (s16)le16_to_cpu(data); @@ -596,7 +641,7 @@ static int st_lsm6dsx_write_raw(struct iio_dev *iio_dev, return err; } -static int st_lsm6dsx_set_watermark(struct iio_dev *iio_dev, unsigned int val) +int st_lsm6dsx_set_watermark(struct iio_dev *iio_dev, unsigned int val) { struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); struct st_lsm6dsx_hw *hw = sensor->hw; @@ -692,8 +737,6 @@ static const struct iio_info st_lsm6dsx_gyro_info = { .hwfifo_set_watermark = st_lsm6dsx_set_watermark, }; -static const unsigned long st_lsm6dsx_available_scan_masks[] = {0x7, 0x0}; - static int st_lsm6dsx_of_get_drdy_pin(struct st_lsm6dsx_hw *hw, int *drdy_pin) { struct device_node *np = hw->dev->of_node; @@ -732,6 +775,51 @@ static int st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, u8 *drdy_reg) return err; } +static int st_lsm6dsx_init_shub(struct st_lsm6dsx_hw *hw) +{ + const struct st_lsm6dsx_shub_settings *hub_settings; + struct device_node *np = hw->dev->of_node; + struct st_sensors_platform_data *pdata; + unsigned int data; + int err = 0; + + hub_settings = &hw->settings->shub_settings; + + pdata = (struct st_sensors_platform_data *)hw->dev->platform_data; + if ((np && of_property_read_bool(np, "st,pullups")) || + (pdata && pdata->pullups)) { + err = st_lsm6dsx_set_page(hw, true); + if (err < 0) + return err; + + data = ST_LSM6DSX_SHIFT_VAL(1, hub_settings->pullup_en.mask); + err = regmap_update_bits(hw->regmap, + hub_settings->pullup_en.addr, + hub_settings->pullup_en.mask, data); + + st_lsm6dsx_set_page(hw, false); + + if (err < 0) + return err; + } + + if (hub_settings->aux_sens.addr) { + /* configure aux sensors */ + err = st_lsm6dsx_set_page(hw, true); + if (err < 0) + return err; + + data = ST_LSM6DSX_SHIFT_VAL(3, hub_settings->aux_sens.mask); + err = regmap_update_bits(hw->regmap, + hub_settings->aux_sens.addr, + hub_settings->aux_sens.mask, data); + + st_lsm6dsx_set_page(hw, false); + } + + return err; +} + static int st_lsm6dsx_init_hw_timer(struct st_lsm6dsx_hw *hw) { const struct st_lsm6dsx_hw_ts_settings *ts_settings; @@ -775,12 +863,23 @@ static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw) u8 drdy_int_reg; int err; - err = regmap_write(hw->regmap, ST_LSM6DSX_REG_RESET_ADDR, - ST_LSM6DSX_REG_RESET_MASK); + /* device sw reset */ + err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_RESET_ADDR, + ST_LSM6DSX_REG_RESET_MASK, + FIELD_PREP(ST_LSM6DSX_REG_RESET_MASK, 1)); + if (err < 0) + return err; + + msleep(50); + + /* reload trimming parameter */ + err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_RESET_ADDR, + ST_LSM6DSX_REG_BOOT_MASK, + FIELD_PREP(ST_LSM6DSX_REG_BOOT_MASK, 1)); if (err < 0) return err; - msleep(200); + msleep(50); /* enable Block Data Update */ err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_BDU_ADDR, @@ -801,6 +900,10 @@ static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw) if (err < 0) return err; + err = st_lsm6dsx_init_shub(hw); + if (err < 0) + return err; + return st_lsm6dsx_init_hw_timer(hw); } @@ -854,6 +957,7 @@ static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw, int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name, struct regmap *regmap) { + const struct st_lsm6dsx_shub_settings *hub_settings; struct st_lsm6dsx_hw *hw; int i, err; @@ -865,6 +969,7 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name, mutex_init(&hw->fifo_lock); mutex_init(&hw->conf_lock); + mutex_init(&hw->page_lock); hw->buff = devm_kzalloc(dev, ST_LSM6DSX_BUFF_SIZE, GFP_KERNEL); if (!hw->buff) @@ -878,7 +983,7 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name, if (err < 0) return err; - for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { + for (i = 0; i < ST_LSM6DSX_ID_EXT0; i++) { hw->iio_devs[i] = st_lsm6dsx_alloc_iiodev(hw, i, name); if (!hw->iio_devs[i]) return -ENOMEM; @@ -888,6 +993,13 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name, if (err < 0) return err; + hub_settings = &hw->settings->shub_settings; + if (hub_settings->master_en.addr) { + err = st_lsm6dsx_shub_probe(hw, name); + if (err < 0) + return err; + } + if (hw->irq > 0) { err = st_lsm6dsx_fifo_setup(hw); if (err < 0) @@ -895,6 +1007,9 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name, } for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { + if (!hw->iio_devs[i]) + continue; + err = devm_iio_device_register(hw->dev, hw->iio_devs[i]); if (err) return err; @@ -909,16 +1024,21 @@ static int __maybe_unused st_lsm6dsx_suspend(struct device *dev) struct st_lsm6dsx_hw *hw = dev_get_drvdata(dev); struct st_lsm6dsx_sensor *sensor; const struct st_lsm6dsx_reg *reg; + unsigned int data; int i, err = 0; for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { + if (!hw->iio_devs[i]) + continue; + sensor = iio_priv(hw->iio_devs[i]); if (!(hw->enable_mask & BIT(sensor->id))) continue; reg = &st_lsm6dsx_odr_table[sensor->id].reg; - err = regmap_update_bits(hw->regmap, reg->addr, reg->mask, - ST_LSM6DSX_SHIFT_VAL(0, reg->mask)); + data = ST_LSM6DSX_SHIFT_VAL(0, reg->mask); + err = st_lsm6dsx_update_bits_locked(hw, reg->addr, reg->mask, + data); if (err < 0) return err; } @@ -936,6 +1056,9 @@ static int __maybe_unused st_lsm6dsx_resume(struct device *dev) int i, err = 0; for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { + if (!hw->iio_devs[i]) + continue; + sensor = iio_priv(hw->iio_devs[i]); if (!(hw->enable_mask & BIT(sensor->id))) continue; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c new file mode 100644 index 000000000000..ee59b0cac84f --- /dev/null +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c @@ -0,0 +1,777 @@ +/* + * STMicroelectronics st_lsm6dsx i2c controller driver + * + * i2c controller embedded in lsm6dx series can connect up to four + * slave devices using accelerometer sensor as trigger for i2c + * read/write operations. Current implementation relies on SLV0 channel + * for slave configuration and SLV{1,2,3} to read data and push them into + * the hw FIFO + * + * Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/bitfield.h> + +#include "st_lsm6dsx.h" + +#define ST_LSM6DSX_MAX_SLV_NUM 3 +#define ST_LSM6DSX_SLV_ADDR(n, base) ((base) + (n) * 3) +#define ST_LSM6DSX_SLV_SUB_ADDR(n, base) ((base) + 1 + (n) * 3) +#define ST_LSM6DSX_SLV_CONFIG(n, base) ((base) + 2 + (n) * 3) + +#define ST_LS6DSX_READ_OP_MASK GENMASK(2, 0) + +static const struct st_lsm6dsx_ext_dev_settings st_lsm6dsx_ext_dev_table[] = { + /* LIS2MDL */ + { + .i2c_addr = { 0x1e }, + .wai = { + .addr = 0x4f, + .val = 0x40, + }, + .id = ST_LSM6DSX_ID_MAGN, + .odr_table = { + .reg = { + .addr = 0x60, + .mask = GENMASK(3, 2), + }, + .odr_avl[0] = { 10, 0x0 }, + .odr_avl[1] = { 20, 0x1 }, + .odr_avl[2] = { 50, 0x2 }, + .odr_avl[3] = { 100, 0x3 }, + }, + .fs_table = { + .fs_avl[0] = { + .gain = 1500, + .val = 0x0, + }, /* 1500 uG/LSB */ + }, + .temp_comp = { + .addr = 0x60, + .mask = BIT(7), + }, + .pwr_table = { + .reg = { + .addr = 0x60, + .mask = GENMASK(1, 0), + }, + .off_val = 0x2, + .on_val = 0x0, + }, + .off_canc = { + .addr = 0x61, + .mask = BIT(1), + }, + .bdu = { + .addr = 0x62, + .mask = BIT(4), + }, + .out = { + .addr = 0x68, + .len = 6, + }, + }, +}; + +static void st_lsm6dsx_shub_wait_complete(struct st_lsm6dsx_hw *hw) +{ + struct st_lsm6dsx_sensor *sensor; + + sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]); + msleep((2000U / sensor->odr) + 1); +} + +/** + * st_lsm6dsx_shub_read_reg - read i2c controller register + * + * Read st_lsm6dsx i2c controller register + */ +static int st_lsm6dsx_shub_read_reg(struct st_lsm6dsx_hw *hw, u8 addr, + u8 *data, int len) +{ + const struct st_lsm6dsx_shub_settings *hub_settings; + int err; + + mutex_lock(&hw->page_lock); + + hub_settings = &hw->settings->shub_settings; + err = st_lsm6dsx_set_page(hw, true); + if (err < 0) + goto out; + + err = regmap_bulk_read(hw->regmap, addr, data, len); + + st_lsm6dsx_set_page(hw, false); +out: + mutex_unlock(&hw->page_lock); + + return err; +} + +/** + * st_lsm6dsx_shub_write_reg - write i2c controller register + * + * Write st_lsm6dsx i2c controller register + */ +static int st_lsm6dsx_shub_write_reg(struct st_lsm6dsx_hw *hw, u8 addr, + u8 *data, int len) +{ + int err; + + mutex_lock(&hw->page_lock); + err = st_lsm6dsx_set_page(hw, true); + if (err < 0) + goto out; + + err = regmap_bulk_write(hw->regmap, addr, data, len); + + st_lsm6dsx_set_page(hw, false); +out: + mutex_unlock(&hw->page_lock); + + return err; +} + +static int +st_lsm6dsx_shub_write_reg_with_mask(struct st_lsm6dsx_hw *hw, u8 addr, + u8 mask, u8 val) +{ + int err; + + mutex_lock(&hw->page_lock); + err = st_lsm6dsx_set_page(hw, true); + if (err < 0) + goto out; + + err = regmap_update_bits(hw->regmap, addr, mask, val); + + st_lsm6dsx_set_page(hw, false); +out: + mutex_unlock(&hw->page_lock); + + return err; +} + +static int st_lsm6dsx_shub_master_enable(struct st_lsm6dsx_sensor *sensor, + bool enable) +{ + const struct st_lsm6dsx_shub_settings *hub_settings; + struct st_lsm6dsx_hw *hw = sensor->hw; + unsigned int data; + int err; + + /* enable acc sensor as trigger */ + err = st_lsm6dsx_sensor_set_enable(sensor, enable); + if (err < 0) + return err; + + mutex_lock(&hw->page_lock); + + hub_settings = &hw->settings->shub_settings; + err = st_lsm6dsx_set_page(hw, true); + if (err < 0) + goto out; + + data = ST_LSM6DSX_SHIFT_VAL(enable, hub_settings->master_en.mask); + err = regmap_update_bits(hw->regmap, hub_settings->master_en.addr, + hub_settings->master_en.mask, data); + + st_lsm6dsx_set_page(hw, false); +out: + mutex_unlock(&hw->page_lock); + + return err; +} + +/** + * st_lsm6dsx_shub_read - read data from slave device register + * + * Read data from slave device register. SLV0 is used for + * one-shot read operation + */ +static int +st_lsm6dsx_shub_read(struct st_lsm6dsx_sensor *sensor, u8 addr, + u8 *data, int len) +{ + const struct st_lsm6dsx_shub_settings *hub_settings; + struct st_lsm6dsx_hw *hw = sensor->hw; + u8 config[3], slv_addr; + int err; + + hub_settings = &hw->settings->shub_settings; + slv_addr = ST_LSM6DSX_SLV_ADDR(0, hub_settings->slv0_addr); + + config[0] = (sensor->ext_info.addr << 1) | 1; + config[1] = addr; + config[2] = len & ST_LS6DSX_READ_OP_MASK; + + err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config, + sizeof(config)); + if (err < 0) + return err; + + err = st_lsm6dsx_shub_master_enable(sensor, true); + if (err < 0) + return err; + + st_lsm6dsx_shub_wait_complete(hw); + + err = st_lsm6dsx_shub_read_reg(hw, hub_settings->shub_out, data, + len & ST_LS6DSX_READ_OP_MASK); + + st_lsm6dsx_shub_master_enable(sensor, false); + + memset(config, 0, sizeof(config)); + return st_lsm6dsx_shub_write_reg(hw, slv_addr, config, + sizeof(config)); +} + +/** + * st_lsm6dsx_shub_write - write data to slave device register + * + * Write data from slave device register. SLV0 is used for + * one-shot write operation + */ +static int +st_lsm6dsx_shub_write(struct st_lsm6dsx_sensor *sensor, u8 addr, + u8 *data, int len) +{ + const struct st_lsm6dsx_shub_settings *hub_settings; + struct st_lsm6dsx_hw *hw = sensor->hw; + u8 config[2], slv_addr; + int err, i; + + hub_settings = &hw->settings->shub_settings; + if (hub_settings->wr_once.addr) { + unsigned int data; + + data = ST_LSM6DSX_SHIFT_VAL(1, hub_settings->wr_once.mask); + err = st_lsm6dsx_shub_write_reg_with_mask(hw, + hub_settings->wr_once.addr, + hub_settings->wr_once.mask, + data); + if (err < 0) + return err; + } + + slv_addr = ST_LSM6DSX_SLV_ADDR(0, hub_settings->slv0_addr); + config[0] = sensor->ext_info.addr << 1; + for (i = 0 ; i < len; i++) { + config[1] = addr + i; + + err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config, + sizeof(config)); + if (err < 0) + return err; + + err = st_lsm6dsx_shub_write_reg(hw, hub_settings->dw_slv0_addr, + &data[i], 1); + if (err < 0) + return err; + + err = st_lsm6dsx_shub_master_enable(sensor, true); + if (err < 0) + return err; + + st_lsm6dsx_shub_wait_complete(hw); + + st_lsm6dsx_shub_master_enable(sensor, false); + } + + memset(config, 0, sizeof(config)); + return st_lsm6dsx_shub_write_reg(hw, slv_addr, config, sizeof(config)); +} + +static int +st_lsm6dsx_shub_write_with_mask(struct st_lsm6dsx_sensor *sensor, + u8 addr, u8 mask, u8 val) +{ + int err; + u8 data; + + err = st_lsm6dsx_shub_read(sensor, addr, &data, sizeof(data)); + if (err < 0) + return err; + + data = ((data & ~mask) | (val << __ffs(mask) & mask)); + + return st_lsm6dsx_shub_write(sensor, addr, &data, sizeof(data)); +} + +static int +st_lsm6dsx_shub_get_odr_val(struct st_lsm6dsx_sensor *sensor, + u16 odr, u16 *val) +{ + const struct st_lsm6dsx_ext_dev_settings *settings; + int i; + + settings = sensor->ext_info.settings; + for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++) + if (settings->odr_table.odr_avl[i].hz == odr) + break; + + if (i == ST_LSM6DSX_ODR_LIST_SIZE) + return -EINVAL; + + *val = settings->odr_table.odr_avl[i].val; + return 0; +} + +static int +st_lsm6dsx_shub_set_odr(struct st_lsm6dsx_sensor *sensor, u16 odr) +{ + const struct st_lsm6dsx_ext_dev_settings *settings; + u16 val; + int err; + + err = st_lsm6dsx_shub_get_odr_val(sensor, odr, &val); + if (err < 0) + return err; + + settings = sensor->ext_info.settings; + return st_lsm6dsx_shub_write_with_mask(sensor, + settings->odr_table.reg.addr, + settings->odr_table.reg.mask, + val); +} + +/* use SLV{1,2,3} for FIFO read operations */ +static int +st_lsm6dsx_shub_config_channels(struct st_lsm6dsx_sensor *sensor, + bool enable) +{ + const struct st_lsm6dsx_shub_settings *hub_settings; + const struct st_lsm6dsx_ext_dev_settings *settings; + u8 config[9] = {}, enable_mask, slv_addr; + struct st_lsm6dsx_hw *hw = sensor->hw; + struct st_lsm6dsx_sensor *cur_sensor; + int i, j = 0; + + hub_settings = &hw->settings->shub_settings; + if (enable) + enable_mask = hw->enable_mask | BIT(sensor->id); + else + enable_mask = hw->enable_mask & ~BIT(sensor->id); + + for (i = ST_LSM6DSX_ID_EXT0; i <= ST_LSM6DSX_ID_EXT2; i++) { + if (!hw->iio_devs[i]) + continue; + + cur_sensor = iio_priv(hw->iio_devs[i]); + if (!(enable_mask & BIT(cur_sensor->id))) + continue; + + settings = cur_sensor->ext_info.settings; + config[j] = (sensor->ext_info.addr << 1) | 1; + config[j + 1] = settings->out.addr; + config[j + 2] = (settings->out.len & ST_LS6DSX_READ_OP_MASK) | + hub_settings->batch_en; + j += 3; + } + + slv_addr = ST_LSM6DSX_SLV_ADDR(1, hub_settings->slv0_addr); + return st_lsm6dsx_shub_write_reg(hw, slv_addr, config, + sizeof(config)); +} + +int st_lsm6dsx_shub_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable) +{ + const struct st_lsm6dsx_ext_dev_settings *settings; + int err; + + err = st_lsm6dsx_shub_config_channels(sensor, enable); + if (err < 0) + return err; + + settings = sensor->ext_info.settings; + if (enable) { + err = st_lsm6dsx_shub_set_odr(sensor, sensor->odr); + if (err < 0) + return err; + } else { + err = st_lsm6dsx_shub_write_with_mask(sensor, + settings->odr_table.reg.addr, + settings->odr_table.reg.mask, 0); + if (err < 0) + return err; + } + + if (settings->pwr_table.reg.addr) { + u8 val; + + val = enable ? settings->pwr_table.on_val + : settings->pwr_table.off_val; + err = st_lsm6dsx_shub_write_with_mask(sensor, + settings->pwr_table.reg.addr, + settings->pwr_table.reg.mask, val); + if (err < 0) + return err; + } + + return st_lsm6dsx_shub_master_enable(sensor, enable); +} + +static int +st_lsm6dsx_shub_read_oneshot(struct st_lsm6dsx_sensor *sensor, + struct iio_chan_spec const *ch, + int *val) +{ + int err, delay, len = ch->scan_type.realbits >> 3; + __le16 data; + + err = st_lsm6dsx_shub_set_enable(sensor, true); + if (err < 0) + return err; + + delay = 1000000 / sensor->odr; + usleep_range(delay, 2 * delay); + + err = st_lsm6dsx_shub_read(sensor, ch->address, (u8 *)&data, len); + if (err < 0) + return err; + + st_lsm6dsx_shub_set_enable(sensor, false); + + switch (len) { + case 2: + *val = (s16)le16_to_cpu(data); + break; + default: + return -EINVAL; + } + + return IIO_VAL_INT; +} + +static int +st_lsm6dsx_shub_read_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *ch, + int *val, int *val2, long mask) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(iio_dev); + if (ret) + break; + + ret = st_lsm6dsx_shub_read_oneshot(sensor, ch, val); + iio_device_release_direct_mode(iio_dev); + break; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = sensor->odr; + ret = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = sensor->gain; + ret = IIO_VAL_INT_PLUS_MICRO; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int +st_lsm6dsx_shub_write_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); + int err; + + err = iio_device_claim_direct_mode(iio_dev); + if (err) + return err; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: { + u16 data; + + err = st_lsm6dsx_shub_get_odr_val(sensor, val, &data); + if (!err) + sensor->odr = val; + break; + } + default: + err = -EINVAL; + break; + } + + iio_device_release_direct_mode(iio_dev); + + return err; +} + +static ssize_t +st_lsm6dsx_shub_sampling_freq_avail(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev)); + const struct st_lsm6dsx_ext_dev_settings *settings; + int i, len = 0; + + settings = sensor->ext_info.settings; + for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++) { + u16 val = settings->odr_table.odr_avl[i].hz; + + if (val > 0) + len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", + val); + } + buf[len - 1] = '\n'; + + return len; +} + +static ssize_t st_lsm6dsx_shub_scale_avail(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev)); + const struct st_lsm6dsx_ext_dev_settings *settings; + int i, len = 0; + + settings = sensor->ext_info.settings; + for (i = 0; i < ST_LSM6DSX_FS_LIST_SIZE; i++) { + u16 val = settings->fs_table.fs_avl[i].gain; + + if (val > 0) + len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ", + val); + } + buf[len - 1] = '\n'; + + return len; +} + +static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_lsm6dsx_shub_sampling_freq_avail); +static IIO_DEVICE_ATTR(in_scale_available, 0444, + st_lsm6dsx_shub_scale_avail, NULL, 0); +static struct attribute *st_lsm6dsx_ext_attributes[] = { + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_in_scale_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group st_lsm6dsx_ext_attribute_group = { + .attrs = st_lsm6dsx_ext_attributes, +}; + +static const struct iio_info st_lsm6dsx_ext_info = { + .attrs = &st_lsm6dsx_ext_attribute_group, + .read_raw = st_lsm6dsx_shub_read_raw, + .write_raw = st_lsm6dsx_shub_write_raw, + .hwfifo_set_watermark = st_lsm6dsx_set_watermark, +}; + +static struct iio_dev * +st_lsm6dsx_shub_alloc_iiodev(struct st_lsm6dsx_hw *hw, + enum st_lsm6dsx_sensor_id id, + const struct st_lsm6dsx_ext_dev_settings *info, + u8 i2c_addr, const char *name) +{ + struct iio_chan_spec *ext_channels; + struct st_lsm6dsx_sensor *sensor; + struct iio_dev *iio_dev; + + iio_dev = devm_iio_device_alloc(hw->dev, sizeof(*sensor)); + if (!iio_dev) + return NULL; + + iio_dev->modes = INDIO_DIRECT_MODE; + iio_dev->dev.parent = hw->dev; + iio_dev->info = &st_lsm6dsx_ext_info; + + sensor = iio_priv(iio_dev); + sensor->id = id; + sensor->hw = hw; + sensor->odr = info->odr_table.odr_avl[0].hz; + sensor->gain = info->fs_table.fs_avl[0].gain; + sensor->ext_info.settings = info; + sensor->ext_info.addr = i2c_addr; + sensor->watermark = 1; + + switch (info->id) { + case ST_LSM6DSX_ID_MAGN: { + const struct iio_chan_spec magn_channels[] = { + ST_LSM6DSX_CHANNEL(IIO_MAGN, info->out.addr, + IIO_MOD_X, 0), + ST_LSM6DSX_CHANNEL(IIO_MAGN, info->out.addr + 2, + IIO_MOD_Y, 1), + ST_LSM6DSX_CHANNEL(IIO_MAGN, info->out.addr + 4, + IIO_MOD_Z, 2), + IIO_CHAN_SOFT_TIMESTAMP(3), + }; + + ext_channels = devm_kzalloc(hw->dev, sizeof(magn_channels), + GFP_KERNEL); + if (!ext_channels) + return NULL; + + memcpy(ext_channels, magn_channels, sizeof(magn_channels)); + iio_dev->available_scan_masks = st_lsm6dsx_available_scan_masks; + iio_dev->channels = ext_channels; + iio_dev->num_channels = ARRAY_SIZE(magn_channels); + + scnprintf(sensor->name, sizeof(sensor->name), "%s_magn", + name); + break; + } + default: + return NULL; + } + iio_dev->name = sensor->name; + + return iio_dev; +} + +static int st_lsm6dsx_shub_init_device(struct st_lsm6dsx_sensor *sensor) +{ + const struct st_lsm6dsx_ext_dev_settings *settings; + int err; + + settings = sensor->ext_info.settings; + if (settings->bdu.addr) { + err = st_lsm6dsx_shub_write_with_mask(sensor, + settings->bdu.addr, + settings->bdu.mask, 1); + if (err < 0) + return err; + } + + if (settings->temp_comp.addr) { + err = st_lsm6dsx_shub_write_with_mask(sensor, + settings->temp_comp.addr, + settings->temp_comp.mask, 1); + if (err < 0) + return err; + } + + if (settings->off_canc.addr) { + err = st_lsm6dsx_shub_write_with_mask(sensor, + settings->off_canc.addr, + settings->off_canc.mask, 1); + if (err < 0) + return err; + } + + return 0; +} + +static int +st_lsm6dsx_shub_check_wai(struct st_lsm6dsx_hw *hw, u8 *i2c_addr, + const struct st_lsm6dsx_ext_dev_settings *settings) +{ + const struct st_lsm6dsx_shub_settings *hub_settings; + struct st_lsm6dsx_sensor *sensor; + u8 config[3], data, slv_addr; + bool found = false; + int i, err; + + hub_settings = &hw->settings->shub_settings; + slv_addr = ST_LSM6DSX_SLV_ADDR(0, hub_settings->slv0_addr); + sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]); + + for (i = 0; i < ARRAY_SIZE(settings->i2c_addr); i++) { + if (!settings->i2c_addr[i]) + continue; + + /* read wai slave register */ + config[0] = (settings->i2c_addr[i] << 1) | 0x1; + config[1] = settings->wai.addr; + config[2] = 0x1; + + err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config, + sizeof(config)); + if (err < 0) + return err; + + err = st_lsm6dsx_shub_master_enable(sensor, true); + if (err < 0) + return err; + + st_lsm6dsx_shub_wait_complete(hw); + + err = st_lsm6dsx_shub_read_reg(hw, + hub_settings->shub_out, + &data, sizeof(data)); + + st_lsm6dsx_shub_master_enable(sensor, false); + + if (err < 0) + return err; + + if (data != settings->wai.val) + continue; + + *i2c_addr = settings->i2c_addr[i]; + found = true; + break; + } + + /* reset SLV0 channel */ + memset(config, 0, sizeof(config)); + err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config, + sizeof(config)); + if (err < 0) + return err; + + return found ? 0 : -ENODEV; +} + +int st_lsm6dsx_shub_probe(struct st_lsm6dsx_hw *hw, const char *name) +{ + enum st_lsm6dsx_sensor_id id = ST_LSM6DSX_ID_EXT0; + struct st_lsm6dsx_sensor *sensor; + int err, i, num_ext_dev = 0; + u8 i2c_addr = 0; + + for (i = 0; i < ARRAY_SIZE(st_lsm6dsx_ext_dev_table); i++) { + err = st_lsm6dsx_shub_check_wai(hw, &i2c_addr, + &st_lsm6dsx_ext_dev_table[i]); + if (err == -ENODEV) + continue; + else if (err < 0) + return err; + + hw->iio_devs[id] = st_lsm6dsx_shub_alloc_iiodev(hw, id, + &st_lsm6dsx_ext_dev_table[i], + i2c_addr, name); + if (!hw->iio_devs[id]) + return -ENOMEM; + + sensor = iio_priv(hw->iio_devs[id]); + err = st_lsm6dsx_shub_init_device(sensor); + if (err < 0) + return err; + + if (++num_ext_dev >= ST_LSM6DSX_MAX_SLV_NUM) + break; + id++; + } + + return 0; +} diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index d66ea754ffff..36f458433480 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -460,6 +460,19 @@ config VCNL4000 To compile this driver as a module, choose M here: the module will be called vcnl4000. +config VCNL4035 + tristate "VCNL4035 combined ALS and proximity sensor" + select IIO_TRIGGERED_BUFFER + select REGMAP_I2C + depends on I2C + help + Say Y here if you want to build a driver for the Vishay VCNL4035, + combined ambient light (ALS) and proximity sensor. Currently only ALS + function is available. + + To compile this driver as a module, choose M here: the + module will be called vcnl4035. + config VEML6070 tristate "VEML6070 UV A light sensor" depends on I2C diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index 86337b114bc4..286bf3975372 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_TSL2772) += tsl2772.o obj-$(CONFIG_TSL4531) += tsl4531.o obj-$(CONFIG_US5182D) += us5182d.o obj-$(CONFIG_VCNL4000) += vcnl4000.o +obj-$(CONFIG_VCNL4035) += vcnl4035.o obj-$(CONFIG_VEML6070) += veml6070.o obj-$(CONFIG_VL6180) += vl6180.o obj-$(CONFIG_ZOPT2201) += zopt2201.o diff --git a/drivers/iio/light/vcnl4035.c b/drivers/iio/light/vcnl4035.c new file mode 100644 index 000000000000..cca4db312bd3 --- /dev/null +++ b/drivers/iio/light/vcnl4035.c @@ -0,0 +1,676 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * VCNL4035 Ambient Light and Proximity Sensor - 7-bit I2C slave address 0x60 + * + * Copyright (c) 2018, DENX Software Engineering GmbH + * Author: Parthiban Nallathambi <pn@denx.de> + * + * TODO: Proximity + */ +#include <linux/bitops.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> + +#include <linux/iio/buffer.h> +#include <linux/iio/events.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +#define VCNL4035_DRV_NAME "vcnl4035" +#define VCNL4035_IRQ_NAME "vcnl4035_event" +#define VCNL4035_REGMAP_NAME "vcnl4035_regmap" + +/* Device registers */ +#define VCNL4035_ALS_CONF 0x00 +#define VCNL4035_ALS_THDH 0x01 +#define VCNL4035_ALS_THDL 0x02 +#define VCNL4035_ALS_DATA 0x0B +#define VCNL4035_WHITE_DATA 0x0C +#define VCNL4035_INT_FLAG 0x0D +#define VCNL4035_DEV_ID 0x0E + +/* Register masks */ +#define VCNL4035_MODE_ALS_MASK BIT(0) +#define VCNL4035_MODE_ALS_WHITE_CHAN BIT(8) +#define VCNL4035_MODE_ALS_INT_MASK BIT(1) +#define VCNL4035_ALS_IT_MASK GENMASK(7, 5) +#define VCNL4035_ALS_PERS_MASK GENMASK(3, 2) +#define VCNL4035_INT_ALS_IF_H_MASK BIT(12) +#define VCNL4035_INT_ALS_IF_L_MASK BIT(13) + +/* Default values */ +#define VCNL4035_MODE_ALS_ENABLE BIT(0) +#define VCNL4035_MODE_ALS_DISABLE 0x00 +#define VCNL4035_MODE_ALS_INT_ENABLE BIT(1) +#define VCNL4035_MODE_ALS_INT_DISABLE 0 +#define VCNL4035_DEV_ID_VAL 0x80 +#define VCNL4035_ALS_IT_DEFAULT 0x01 +#define VCNL4035_ALS_PERS_DEFAULT 0x00 +#define VCNL4035_ALS_THDH_DEFAULT 5000 +#define VCNL4035_ALS_THDL_DEFAULT 100 +#define VCNL4035_SLEEP_DELAY_MS 2000 + +struct vcnl4035_data { + struct i2c_client *client; + struct regmap *regmap; + unsigned int als_it_val; + unsigned int als_persistence; + unsigned int als_thresh_low; + unsigned int als_thresh_high; + struct iio_trigger *drdy_trigger0; +}; + +static inline bool vcnl4035_is_triggered(struct vcnl4035_data *data) +{ + int ret; + int reg; + + ret = regmap_read(data->regmap, VCNL4035_INT_FLAG, ®); + if (ret < 0) + return false; + + return !!(reg & + (VCNL4035_INT_ALS_IF_H_MASK | VCNL4035_INT_ALS_IF_L_MASK)); +} + +static irqreturn_t vcnl4035_drdy_irq_thread(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct vcnl4035_data *data = iio_priv(indio_dev); + + if (vcnl4035_is_triggered(data)) { + iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_LIGHT, + 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER), + iio_get_time_ns(indio_dev)); + iio_trigger_poll_chained(data->drdy_trigger0); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +/* Triggered buffer */ +static irqreturn_t vcnl4035_trigger_consumer_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct vcnl4035_data *data = iio_priv(indio_dev); + u8 buffer[ALIGN(sizeof(u16), sizeof(s64)) + sizeof(s64)]; + int ret; + + ret = regmap_read(data->regmap, VCNL4035_ALS_DATA, (int *)buffer); + if (ret < 0) { + dev_err(&data->client->dev, + "Trigger consumer can't read from sensor.\n"); + goto fail_read; + } + iio_push_to_buffers_with_timestamp(indio_dev, buffer, + iio_get_time_ns(indio_dev)); + +fail_read: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int vcnl4035_als_drdy_set_state(struct iio_trigger *trigger, + bool enable_drdy) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trigger); + struct vcnl4035_data *data = iio_priv(indio_dev); + int val = enable_drdy ? VCNL4035_MODE_ALS_INT_ENABLE : + VCNL4035_MODE_ALS_INT_DISABLE; + + return regmap_update_bits(data->regmap, VCNL4035_ALS_CONF, + VCNL4035_MODE_ALS_INT_MASK, + val); +} + +static const struct iio_trigger_ops vcnl4035_trigger_ops = { + .validate_device = iio_trigger_validate_own_device, + .set_trigger_state = vcnl4035_als_drdy_set_state, +}; + +static int vcnl4035_set_pm_runtime_state(struct vcnl4035_data *data, bool on) +{ + int ret; + struct device *dev = &data->client->dev; + + if (on) { + ret = pm_runtime_get_sync(dev); + if (ret < 0) + pm_runtime_put_noidle(dev); + } else { + pm_runtime_mark_last_busy(dev); + ret = pm_runtime_put_autosuspend(dev); + } + + return ret; +} + +/* + * Device IT INT Time (ms) Scale (lux/step) + * 000 50 0.064 + * 001 100 0.032 + * 010 200 0.016 + * 100 400 0.008 + * 101 - 111 800 0.004 + * Values are proportional, so ALS INT is selected for input due to + * simplicity reason. Integration time value and scaling is + * calculated based on device INT value + * + * Raw value needs to be scaled using ALS steps + */ +static int vcnl4035_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct vcnl4035_data *data = iio_priv(indio_dev); + int ret; + int raw_data; + unsigned int reg; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = vcnl4035_set_pm_runtime_state(data, true); + if (ret < 0) + return ret; + + ret = iio_device_claim_direct_mode(indio_dev); + if (!ret) { + if (chan->channel) + reg = VCNL4035_ALS_DATA; + else + reg = VCNL4035_WHITE_DATA; + ret = regmap_read(data->regmap, reg, &raw_data); + iio_device_release_direct_mode(indio_dev); + if (!ret) { + *val = raw_data; + ret = IIO_VAL_INT; + } + } + vcnl4035_set_pm_runtime_state(data, false); + return ret; + case IIO_CHAN_INFO_INT_TIME: + *val = 50; + if (data->als_it_val) + *val = data->als_it_val * 100; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 64; + if (!data->als_it_val) + *val2 = 1000; + else + *val2 = data->als_it_val * 2 * 1000; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } +} + +static int vcnl4035_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + int ret; + struct vcnl4035_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + if (val <= 0 || val > 800) + return -EINVAL; + + ret = vcnl4035_set_pm_runtime_state(data, true); + if (ret < 0) + return ret; + + ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF, + VCNL4035_ALS_IT_MASK, + val / 100); + if (!ret) + data->als_it_val = val / 100; + + vcnl4035_set_pm_runtime_state(data, false); + return ret; + default: + return -EINVAL; + } +} + +/* No direct ABI for persistence and threshold, so eventing */ +static int vcnl4035_read_thresh(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, enum iio_event_info info, + int *val, int *val2) +{ + struct vcnl4035_data *data = iio_priv(indio_dev); + + switch (info) { + case IIO_EV_INFO_VALUE: + switch (dir) { + case IIO_EV_DIR_RISING: + *val = data->als_thresh_high; + return IIO_VAL_INT; + case IIO_EV_DIR_FALLING: + *val = data->als_thresh_low; + return IIO_VAL_INT; + default: + return -EINVAL; + } + break; + case IIO_EV_INFO_PERIOD: + *val = data->als_persistence; + return IIO_VAL_INT; + default: + return -EINVAL; + } + +} + +static int vcnl4035_write_thresh(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, enum iio_event_info info, int val, + int val2) +{ + struct vcnl4035_data *data = iio_priv(indio_dev); + int ret; + + switch (info) { + case IIO_EV_INFO_VALUE: + /* 16 bit threshold range 0 - 65535 */ + if (val < 0 || val > 65535) + return -EINVAL; + if (dir == IIO_EV_DIR_RISING) { + if (val < data->als_thresh_low) + return -EINVAL; + ret = regmap_write(data->regmap, VCNL4035_ALS_THDH, + val); + if (ret) + return ret; + data->als_thresh_high = val; + } else { + if (val > data->als_thresh_high) + return -EINVAL; + ret = regmap_write(data->regmap, VCNL4035_ALS_THDL, + val); + if (ret) + return ret; + data->als_thresh_low = val; + } + return ret; + case IIO_EV_INFO_PERIOD: + /* allow only 1 2 4 8 as persistence value */ + if (val < 0 || val > 8 || hweight8(val) != 1) + return -EINVAL; + ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF, + VCNL4035_ALS_PERS_MASK, val); + if (!ret) + data->als_persistence = val; + return ret; + default: + return -EINVAL; + } +} + +static IIO_CONST_ATTR_INT_TIME_AVAIL("50 100 200 400 800"); + +static struct attribute *vcnl4035_attributes[] = { + &iio_const_attr_integration_time_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group vcnl4035_attribute_group = { + .attrs = vcnl4035_attributes, +}; + +static const struct iio_info vcnl4035_info = { + .read_raw = vcnl4035_read_raw, + .write_raw = vcnl4035_write_raw, + .read_event_value = vcnl4035_read_thresh, + .write_event_value = vcnl4035_write_thresh, + .attrs = &vcnl4035_attribute_group, +}; + +static const struct iio_event_spec vcnl4035_event_spec[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_PERIOD), + }, +}; + +enum vcnl4035_scan_index_order { + VCNL4035_CHAN_INDEX_LIGHT, + VCNL4035_CHAN_INDEX_WHITE_LED, +}; + +static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = { + .validate_scan_mask = &iio_validate_scan_mask_onehot, +}; + +static const struct iio_chan_spec vcnl4035_channels[] = { + { + .type = IIO_LIGHT, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_SCALE), + .event_spec = vcnl4035_event_spec, + .num_event_specs = ARRAY_SIZE(vcnl4035_event_spec), + .scan_index = VCNL4035_CHAN_INDEX_LIGHT, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_LE, + }, + }, + { + .type = IIO_INTENSITY, + .channel = 1, + .modified = 1, + .channel2 = IIO_MOD_LIGHT_BOTH, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .scan_index = VCNL4035_CHAN_INDEX_WHITE_LED, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_LE, + }, + }, +}; + +static int vcnl4035_set_als_power_state(struct vcnl4035_data *data, u8 status) +{ + return regmap_update_bits(data->regmap, VCNL4035_ALS_CONF, + VCNL4035_MODE_ALS_MASK, + status); +} + +static int vcnl4035_init(struct vcnl4035_data *data) +{ + int ret; + int id; + + ret = regmap_read(data->regmap, VCNL4035_DEV_ID, &id); + if (ret < 0) { + dev_err(&data->client->dev, "Failed to read DEV_ID register\n"); + return ret; + } + + if (id != VCNL4035_DEV_ID_VAL) { + dev_err(&data->client->dev, "Wrong id, got %x, expected %x\n", + id, VCNL4035_DEV_ID_VAL); + return -ENODEV; + } + + ret = vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_ENABLE); + if (ret < 0) + return ret; + + /* ALS white channel enable */ + ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF, + VCNL4035_MODE_ALS_WHITE_CHAN, + 1); + if (ret) { + dev_err(&data->client->dev, "set white channel enable %d\n", + ret); + return ret; + } + + /* set default integration time - 100 ms for ALS */ + ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF, + VCNL4035_ALS_IT_MASK, + VCNL4035_ALS_IT_DEFAULT); + if (ret) { + dev_err(&data->client->dev, "set default ALS IT returned %d\n", + ret); + return ret; + } + data->als_it_val = VCNL4035_ALS_IT_DEFAULT; + + /* set default persistence time - 1 for ALS */ + ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF, + VCNL4035_ALS_PERS_MASK, + VCNL4035_ALS_PERS_DEFAULT); + if (ret) { + dev_err(&data->client->dev, "set default PERS returned %d\n", + ret); + return ret; + } + data->als_persistence = VCNL4035_ALS_PERS_DEFAULT; + + /* set default HIGH threshold for ALS */ + ret = regmap_write(data->regmap, VCNL4035_ALS_THDH, + VCNL4035_ALS_THDH_DEFAULT); + if (ret) { + dev_err(&data->client->dev, "set default THDH returned %d\n", + ret); + return ret; + } + data->als_thresh_high = VCNL4035_ALS_THDH_DEFAULT; + + /* set default LOW threshold for ALS */ + ret = regmap_write(data->regmap, VCNL4035_ALS_THDL, + VCNL4035_ALS_THDL_DEFAULT); + if (ret) { + dev_err(&data->client->dev, "set default THDL returned %d\n", + ret); + return ret; + } + data->als_thresh_low = VCNL4035_ALS_THDL_DEFAULT; + + return 0; +} + +static bool vcnl4035_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case VCNL4035_ALS_CONF: + case VCNL4035_DEV_ID: + return false; + default: + return true; + } +} + +static const struct regmap_config vcnl4035_regmap_config = { + .name = VCNL4035_REGMAP_NAME, + .reg_bits = 8, + .val_bits = 16, + .max_register = VCNL4035_DEV_ID, + .cache_type = REGCACHE_RBTREE, + .volatile_reg = vcnl4035_is_volatile_reg, + .val_format_endian = REGMAP_ENDIAN_LITTLE, +}; + +static int vcnl4035_probe_trigger(struct iio_dev *indio_dev) +{ + int ret; + struct vcnl4035_data *data = iio_priv(indio_dev); + + data->drdy_trigger0 = devm_iio_trigger_alloc( + indio_dev->dev.parent, + "%s-dev%d", indio_dev->name, indio_dev->id); + if (!data->drdy_trigger0) + return -ENOMEM; + + data->drdy_trigger0->dev.parent = indio_dev->dev.parent; + data->drdy_trigger0->ops = &vcnl4035_trigger_ops; + iio_trigger_set_drvdata(data->drdy_trigger0, indio_dev); + ret = devm_iio_trigger_register(indio_dev->dev.parent, + data->drdy_trigger0); + if (ret) { + dev_err(&data->client->dev, "iio trigger register failed\n"); + return ret; + } + + /* Trigger setup */ + ret = devm_iio_triggered_buffer_setup(indio_dev->dev.parent, indio_dev, + NULL, vcnl4035_trigger_consumer_handler, + &iio_triggered_buffer_setup_ops); + if (ret < 0) { + dev_err(&data->client->dev, "iio triggered buffer setup failed\n"); + return ret; + } + + /* IRQ to trigger mapping */ + ret = devm_request_threaded_irq(&data->client->dev, data->client->irq, + NULL, vcnl4035_drdy_irq_thread, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + VCNL4035_IRQ_NAME, indio_dev); + if (ret < 0) + dev_err(&data->client->dev, "request irq %d for trigger0 failed\n", + data->client->irq); + return ret; +} + +static int vcnl4035_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct vcnl4035_data *data; + struct iio_dev *indio_dev; + struct regmap *regmap; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + regmap = devm_regmap_init_i2c(client, &vcnl4035_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&client->dev, "regmap_init failed!\n"); + return PTR_ERR(regmap); + } + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + data->regmap = regmap; + + indio_dev->dev.parent = &client->dev; + indio_dev->info = &vcnl4035_info; + indio_dev->name = VCNL4035_DRV_NAME; + indio_dev->channels = vcnl4035_channels; + indio_dev->num_channels = ARRAY_SIZE(vcnl4035_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = vcnl4035_init(data); + if (ret < 0) { + dev_err(&client->dev, "vcnl4035 chip init failed\n"); + return ret; + } + + if (client->irq > 0) { + ret = vcnl4035_probe_trigger(indio_dev); + if (ret < 0) { + dev_err(&client->dev, "vcnl4035 unable init trigger\n"); + goto fail_poweroff; + } + } + + ret = pm_runtime_set_active(&client->dev); + if (ret < 0) + goto fail_poweroff; + + ret = iio_device_register(indio_dev); + if (ret < 0) + goto fail_poweroff; + + pm_runtime_enable(&client->dev); + pm_runtime_set_autosuspend_delay(&client->dev, VCNL4035_SLEEP_DELAY_MS); + pm_runtime_use_autosuspend(&client->dev); + + return 0; + +fail_poweroff: + vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_DISABLE); + return ret; +} + +static int vcnl4035_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + + pm_runtime_dont_use_autosuspend(&client->dev); + pm_runtime_disable(&client->dev); + iio_device_unregister(indio_dev); + pm_runtime_set_suspended(&client->dev); + + return vcnl4035_set_als_power_state(iio_priv(indio_dev), + VCNL4035_MODE_ALS_DISABLE); +} + +static int __maybe_unused vcnl4035_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct vcnl4035_data *data = iio_priv(indio_dev); + int ret; + + ret = vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_DISABLE); + regcache_mark_dirty(data->regmap); + + return ret; +} + +static int __maybe_unused vcnl4035_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct vcnl4035_data *data = iio_priv(indio_dev); + int ret; + + regcache_sync(data->regmap); + ret = vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_ENABLE); + if (ret < 0) + return ret; + + /* wait for 1 ALS integration cycle */ + msleep(data->als_it_val * 100); + + return 0; +} + +static const struct dev_pm_ops vcnl4035_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(vcnl4035_runtime_suspend, + vcnl4035_runtime_resume, NULL) +}; + +static const struct of_device_id vcnl4035_of_match[] = { + { .compatible = "vishay,vcnl4035", }, + { } +}; +MODULE_DEVICE_TABLE(of, vcnl4035_of_match); + +static struct i2c_driver vcnl4035_driver = { + .driver = { + .name = VCNL4035_DRV_NAME, + .pm = &vcnl4035_pm_ops, + .of_match_table = vcnl4035_of_match, + }, + .probe = vcnl4035_probe, + .remove = vcnl4035_remove, +}; + +module_i2c_driver(vcnl4035_driver); + +MODULE_AUTHOR("Parthiban Nallathambi <pn@denx.de>"); +MODULE_DESCRIPTION("VCNL4035 Ambient Light Sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig index ed9d776d01af..8a63cbbca4b7 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -175,4 +175,33 @@ config SENSORS_HMC5843_SPI - hmc5843_core (core functions) - hmc5843_spi (support for HMC5983) +config SENSORS_RM3100 + tristate + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + +config SENSORS_RM3100_I2C + tristate "PNI RM3100 3-Axis Magnetometer (I2C)" + depends on I2C + select SENSORS_RM3100 + select REGMAP_I2C + help + Say Y here to add support for the PNI RM3100 3-Axis Magnetometer. + + This driver can also be compiled as a module. + To compile this driver as a module, choose M here: the module + will be called rm3100-i2c. + +config SENSORS_RM3100_SPI + tristate "PNI RM3100 3-Axis Magnetometer (SPI)" + depends on SPI_MASTER + select SENSORS_RM3100 + select REGMAP_SPI + help + Say Y here to add support for the PNI RM3100 3-Axis Magnetometer. + + This driver can also be compiled as a module. + To compile this driver as a module, choose M here: the module + will be called rm3100-spi. + endmenu diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile index 664b2f866472..ba1bc34b82fa 100644 --- a/drivers/iio/magnetometer/Makefile +++ b/drivers/iio/magnetometer/Makefile @@ -24,3 +24,7 @@ obj-$(CONFIG_IIO_ST_MAGN_SPI_3AXIS) += st_magn_spi.o obj-$(CONFIG_SENSORS_HMC5843) += hmc5843_core.o obj-$(CONFIG_SENSORS_HMC5843_I2C) += hmc5843_i2c.o obj-$(CONFIG_SENSORS_HMC5843_SPI) += hmc5843_spi.o + +obj-$(CONFIG_SENSORS_RM3100) += rm3100-core.o +obj-$(CONFIG_SENSORS_RM3100_I2C) += rm3100-i2c.o +obj-$(CONFIG_SENSORS_RM3100_SPI) += rm3100-spi.o diff --git a/drivers/iio/magnetometer/rm3100-core.c b/drivers/iio/magnetometer/rm3100-core.c new file mode 100644 index 000000000000..7c20918d8108 --- /dev/null +++ b/drivers/iio/magnetometer/rm3100-core.c @@ -0,0 +1,616 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PNI RM3100 3-axis geomagnetic sensor driver core. + * + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com> + * + * User Manual available at + * <https://www.pnicorp.com/download/rm3100-user-manual/> + * + * TODO: event generation, pm. + */ + +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/slab.h> + +#include <linux/iio/buffer.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/trigger_consumer.h> + +#include "rm3100.h" + +/* Cycle Count Registers. */ +#define RM3100_REG_CC_X 0x05 +#define RM3100_REG_CC_Y 0x07 +#define RM3100_REG_CC_Z 0x09 + +/* Poll Measurement Mode register. */ +#define RM3100_REG_POLL 0x00 +#define RM3100_POLL_X BIT(4) +#define RM3100_POLL_Y BIT(5) +#define RM3100_POLL_Z BIT(6) + +/* Continuous Measurement Mode register. */ +#define RM3100_REG_CMM 0x01 +#define RM3100_CMM_START BIT(0) +#define RM3100_CMM_X BIT(4) +#define RM3100_CMM_Y BIT(5) +#define RM3100_CMM_Z BIT(6) + +/* TiMe Rate Configuration register. */ +#define RM3100_REG_TMRC 0x0B +#define RM3100_TMRC_OFFSET 0x92 + +/* Result Status register. */ +#define RM3100_REG_STATUS 0x34 +#define RM3100_STATUS_DRDY BIT(7) + +/* Measurement result registers. */ +#define RM3100_REG_MX2 0x24 +#define RM3100_REG_MY2 0x27 +#define RM3100_REG_MZ2 0x2a + +#define RM3100_W_REG_START RM3100_REG_POLL +#define RM3100_W_REG_END RM3100_REG_TMRC +#define RM3100_R_REG_START RM3100_REG_POLL +#define RM3100_R_REG_END RM3100_REG_STATUS +#define RM3100_V_REG_START RM3100_REG_POLL +#define RM3100_V_REG_END RM3100_REG_STATUS + +/* + * This is computed by hand, is the sum of channel storage bits and padding + * bits, which is 4+4+4+12=24 in here. + */ +#define RM3100_SCAN_BYTES 24 + +#define RM3100_CMM_AXIS_SHIFT 4 + +struct rm3100_data { + struct regmap *regmap; + struct completion measuring_done; + bool use_interrupt; + int conversion_time; + int scale; + u8 buffer[RM3100_SCAN_BYTES]; + struct iio_trigger *drdy_trig; + + /* + * This lock is for protecting the consistency of series of i2c + * operations, that is, to make sure a measurement process will + * not be interrupted by a set frequency operation, which should + * be taken where a series of i2c operation starts, released where + * the operation ends. + */ + struct mutex lock; +}; + +static const struct regmap_range rm3100_readable_ranges[] = { + regmap_reg_range(RM3100_R_REG_START, RM3100_R_REG_END), +}; + +const struct regmap_access_table rm3100_readable_table = { + .yes_ranges = rm3100_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(rm3100_readable_ranges), +}; +EXPORT_SYMBOL_GPL(rm3100_readable_table); + +static const struct regmap_range rm3100_writable_ranges[] = { + regmap_reg_range(RM3100_W_REG_START, RM3100_W_REG_END), +}; + +const struct regmap_access_table rm3100_writable_table = { + .yes_ranges = rm3100_writable_ranges, + .n_yes_ranges = ARRAY_SIZE(rm3100_writable_ranges), +}; +EXPORT_SYMBOL_GPL(rm3100_writable_table); + +static const struct regmap_range rm3100_volatile_ranges[] = { + regmap_reg_range(RM3100_V_REG_START, RM3100_V_REG_END), +}; + +const struct regmap_access_table rm3100_volatile_table = { + .yes_ranges = rm3100_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(rm3100_volatile_ranges), +}; +EXPORT_SYMBOL_GPL(rm3100_volatile_table); + +static irqreturn_t rm3100_thread_fn(int irq, void *d) +{ + struct iio_dev *indio_dev = d; + struct rm3100_data *data = iio_priv(indio_dev); + + /* + * Write operation to any register or read operation + * to first byte of results will clear the interrupt. + */ + regmap_write(data->regmap, RM3100_REG_POLL, 0); + + return IRQ_HANDLED; +} + +static irqreturn_t rm3100_irq_handler(int irq, void *d) +{ + struct iio_dev *indio_dev = d; + struct rm3100_data *data = iio_priv(indio_dev); + + switch (indio_dev->currentmode) { + case INDIO_DIRECT_MODE: + complete(&data->measuring_done); + break; + case INDIO_BUFFER_TRIGGERED: + iio_trigger_poll(data->drdy_trig); + break; + default: + dev_err(indio_dev->dev.parent, + "device mode out of control, current mode: %d", + indio_dev->currentmode); + } + + return IRQ_WAKE_THREAD; +} + +static int rm3100_wait_measurement(struct rm3100_data *data) +{ + struct regmap *regmap = data->regmap; + unsigned int val; + int tries = 20; + int ret; + + /* + * A read cycle of 400kbits i2c bus is about 20us, plus the time + * used for scheduling, a read cycle of fast mode of this device + * can reach 1.7ms, it may be possible for data to arrive just + * after we check the RM3100_REG_STATUS. In this case, irq_handler is + * called before measuring_done is reinitialized, it will wait + * forever for data that has already been ready. + * Reinitialize measuring_done before looking up makes sure we + * will always capture interrupt no matter when it happens. + */ + if (data->use_interrupt) + reinit_completion(&data->measuring_done); + + ret = regmap_read(regmap, RM3100_REG_STATUS, &val); + if (ret < 0) + return ret; + + if ((val & RM3100_STATUS_DRDY) != RM3100_STATUS_DRDY) { + if (data->use_interrupt) { + ret = wait_for_completion_timeout(&data->measuring_done, + msecs_to_jiffies(data->conversion_time)); + if (!ret) + return -ETIMEDOUT; + } else { + do { + usleep_range(1000, 5000); + + ret = regmap_read(regmap, RM3100_REG_STATUS, + &val); + if (ret < 0) + return ret; + + if (val & RM3100_STATUS_DRDY) + break; + } while (--tries); + if (!tries) + return -ETIMEDOUT; + } + } + return 0; +} + +static int rm3100_read_mag(struct rm3100_data *data, int idx, int *val) +{ + struct regmap *regmap = data->regmap; + u8 buffer[3]; + int ret; + + mutex_lock(&data->lock); + ret = regmap_write(regmap, RM3100_REG_POLL, BIT(4 + idx)); + if (ret < 0) + goto unlock_return; + + ret = rm3100_wait_measurement(data); + if (ret < 0) + goto unlock_return; + + ret = regmap_bulk_read(regmap, RM3100_REG_MX2 + 3 * idx, buffer, 3); + if (ret < 0) + goto unlock_return; + mutex_unlock(&data->lock); + + *val = sign_extend32((buffer[0] << 16) | (buffer[1] << 8) | buffer[2], + 23); + + return IIO_VAL_INT; + +unlock_return: + mutex_unlock(&data->lock); + return ret; +} + +#define RM3100_CHANNEL(axis, idx) \ + { \ + .type = IIO_MAGN, \ + .modified = 1, \ + .channel2 = IIO_MOD_##axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = idx, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 24, \ + .storagebits = 32, \ + .shift = 8, \ + .endianness = IIO_BE, \ + }, \ + } + +static const struct iio_chan_spec rm3100_channels[] = { + RM3100_CHANNEL(X, 0), + RM3100_CHANNEL(Y, 1), + RM3100_CHANNEL(Z, 2), + IIO_CHAN_SOFT_TIMESTAMP(3), +}; + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL( + "600 300 150 75 37 18 9 4.5 2.3 1.2 0.6 0.3 0.015 0.075" +); + +static struct attribute *rm3100_attributes[] = { + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group rm3100_attribute_group = { + .attrs = rm3100_attributes, +}; + +#define RM3100_SAMP_NUM 14 + +/* + * Frequency : rm3100_samp_rates[][0].rm3100_samp_rates[][1]Hz. + * Time between reading: rm3100_sam_rates[][2]ms. + * The first one is actually 1.7ms. + */ +static const int rm3100_samp_rates[RM3100_SAMP_NUM][3] = { + {600, 0, 2}, {300, 0, 3}, {150, 0, 7}, {75, 0, 13}, {37, 0, 27}, + {18, 0, 55}, {9, 0, 110}, {4, 500000, 220}, {2, 300000, 440}, + {1, 200000, 800}, {0, 600000, 1600}, {0, 300000, 3300}, + {0, 15000, 6700}, {0, 75000, 13000} +}; + +static int rm3100_get_samp_freq(struct rm3100_data *data, int *val, int *val2) +{ + unsigned int tmp; + int ret; + + mutex_lock(&data->lock); + ret = regmap_read(data->regmap, RM3100_REG_TMRC, &tmp); + mutex_unlock(&data->lock); + if (ret < 0) + return ret; + *val = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][0]; + *val2 = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][1]; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int rm3100_set_cycle_count(struct rm3100_data *data, int val) +{ + int ret; + u8 i; + + for (i = 0; i < 3; i++) { + ret = regmap_write(data->regmap, RM3100_REG_CC_X + 2 * i, val); + if (ret < 0) + return ret; + } + + /* + * The scale of this sensor depends on the cycle count value, these + * three values are corresponding to the cycle count value 50, 100, + * 200. scale = output / gain * 10^4. + */ + switch (val) { + case 50: + data->scale = 500; + break; + case 100: + data->scale = 263; + break; + /* + * case 200: + * This function will never be called by users' code, so here we + * assume that it will never get a wrong parameter. + */ + default: + data->scale = 133; + } + + return 0; +} + +static int rm3100_set_samp_freq(struct iio_dev *indio_dev, int val, int val2) +{ + struct rm3100_data *data = iio_priv(indio_dev); + struct regmap *regmap = data->regmap; + unsigned int cycle_count; + int ret; + int i; + + mutex_lock(&data->lock); + /* All cycle count registers use the same value. */ + ret = regmap_read(regmap, RM3100_REG_CC_X, &cycle_count); + if (ret < 0) + goto unlock_return; + + for (i = 0; i < RM3100_SAMP_NUM; i++) { + if (val == rm3100_samp_rates[i][0] && + val2 == rm3100_samp_rates[i][1]) + break; + } + if (i == RM3100_SAMP_NUM) { + ret = -EINVAL; + goto unlock_return; + } + + ret = regmap_write(regmap, RM3100_REG_TMRC, i + RM3100_TMRC_OFFSET); + if (ret < 0) + goto unlock_return; + + /* Checking if cycle count registers need changing. */ + if (val == 600 && cycle_count == 200) { + ret = rm3100_set_cycle_count(data, 100); + if (ret < 0) + goto unlock_return; + } else if (val != 600 && cycle_count == 100) { + ret = rm3100_set_cycle_count(data, 200); + if (ret < 0) + goto unlock_return; + } + + if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) { + /* Writing TMRC registers requires CMM reset. */ + ret = regmap_write(regmap, RM3100_REG_CMM, 0); + if (ret < 0) + goto unlock_return; + ret = regmap_write(data->regmap, RM3100_REG_CMM, + (*indio_dev->active_scan_mask & 0x7) << + RM3100_CMM_AXIS_SHIFT | RM3100_CMM_START); + if (ret < 0) + goto unlock_return; + } + mutex_unlock(&data->lock); + + data->conversion_time = rm3100_samp_rates[i][2] * 2; + return 0; + +unlock_return: + mutex_unlock(&data->lock); + return ret; +} + +static int rm3100_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct rm3100_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret < 0) + return ret; + + ret = rm3100_read_mag(data, chan->scan_index, val); + iio_device_release_direct_mode(indio_dev); + + return ret; + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = data->scale; + + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_SAMP_FREQ: + return rm3100_get_samp_freq(data, val, val2); + default: + return -EINVAL; + } +} + +static int rm3100_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + return rm3100_set_samp_freq(indio_dev, val, val2); + default: + return -EINVAL; + } +} + +static const struct iio_info rm3100_info = { + .attrs = &rm3100_attribute_group, + .read_raw = rm3100_read_raw, + .write_raw = rm3100_write_raw, +}; + +static int rm3100_buffer_preenable(struct iio_dev *indio_dev) +{ + struct rm3100_data *data = iio_priv(indio_dev); + + /* Starting channels enabled. */ + return regmap_write(data->regmap, RM3100_REG_CMM, + (*indio_dev->active_scan_mask & 0x7) << RM3100_CMM_AXIS_SHIFT | + RM3100_CMM_START); +} + +static int rm3100_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct rm3100_data *data = iio_priv(indio_dev); + + return regmap_write(data->regmap, RM3100_REG_CMM, 0); +} + +static const struct iio_buffer_setup_ops rm3100_buffer_ops = { + .preenable = rm3100_buffer_preenable, + .postenable = iio_triggered_buffer_postenable, + .predisable = iio_triggered_buffer_predisable, + .postdisable = rm3100_buffer_postdisable, +}; + +static irqreturn_t rm3100_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + unsigned long scan_mask = *indio_dev->active_scan_mask; + unsigned int mask_len = indio_dev->masklength; + struct rm3100_data *data = iio_priv(indio_dev); + struct regmap *regmap = data->regmap; + int ret, i, bit; + + mutex_lock(&data->lock); + switch (scan_mask) { + case BIT(0) | BIT(1) | BIT(2): + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 9); + mutex_unlock(&data->lock); + if (ret < 0) + goto done; + /* Convert XXXYYYZZZxxx to XXXxYYYxZZZx. x for paddings. */ + for (i = 2; i > 0; i--) + memmove(data->buffer + i * 4, data->buffer + i * 3, 3); + break; + case BIT(0) | BIT(1): + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 6); + mutex_unlock(&data->lock); + if (ret < 0) + goto done; + memmove(data->buffer + 4, data->buffer + 3, 3); + break; + case BIT(1) | BIT(2): + ret = regmap_bulk_read(regmap, RM3100_REG_MY2, data->buffer, 6); + mutex_unlock(&data->lock); + if (ret < 0) + goto done; + memmove(data->buffer + 4, data->buffer + 3, 3); + break; + case BIT(0) | BIT(2): + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 9); + mutex_unlock(&data->lock); + if (ret < 0) + goto done; + memmove(data->buffer + 4, data->buffer + 6, 3); + break; + default: + for_each_set_bit(bit, &scan_mask, mask_len) { + ret = regmap_bulk_read(regmap, RM3100_REG_MX2 + 3 * bit, + data->buffer, 3); + if (ret < 0) { + mutex_unlock(&data->lock); + goto done; + } + } + mutex_unlock(&data->lock); + } + /* + * Always using the same buffer so that we wouldn't need to set the + * paddings to 0 in case of leaking any data. + */ + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + pf->timestamp); +done: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq) +{ + struct iio_dev *indio_dev; + struct rm3100_data *data; + unsigned int tmp; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->regmap = regmap; + + mutex_init(&data->lock); + + indio_dev->dev.parent = dev; + indio_dev->name = "rm3100"; + indio_dev->info = &rm3100_info; + indio_dev->channels = rm3100_channels; + indio_dev->num_channels = ARRAY_SIZE(rm3100_channels); + indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_TRIGGERED; + indio_dev->currentmode = INDIO_DIRECT_MODE; + + if (!irq) + data->use_interrupt = false; + else { + data->use_interrupt = true; + + init_completion(&data->measuring_done); + ret = devm_request_threaded_irq(dev, + irq, + rm3100_irq_handler, + rm3100_thread_fn, + IRQF_TRIGGER_HIGH | + IRQF_ONESHOT, + indio_dev->name, + indio_dev); + if (ret < 0) { + dev_err(dev, "request irq line failed.\n"); + return ret; + } + + data->drdy_trig = devm_iio_trigger_alloc(dev, "%s-drdy%d", + indio_dev->name, + indio_dev->id); + if (!data->drdy_trig) + return -ENOMEM; + + data->drdy_trig->dev.parent = dev; + ret = devm_iio_trigger_register(dev, data->drdy_trig); + if (ret < 0) + return ret; + } + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + &iio_pollfunc_store_time, + rm3100_trigger_handler, + &rm3100_buffer_ops); + if (ret < 0) + return ret; + + ret = regmap_read(regmap, RM3100_REG_TMRC, &tmp); + if (ret < 0) + return ret; + /* Initializing max wait time, which is double conversion time. */ + data->conversion_time = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][2] + * 2; + + /* Cycle count values may not be what we want. */ + if ((tmp - RM3100_TMRC_OFFSET) == 0) + rm3100_set_cycle_count(data, 100); + else + rm3100_set_cycle_count(data, 200); + + return devm_iio_device_register(dev, indio_dev); +} +EXPORT_SYMBOL_GPL(rm3100_common_probe); + +MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>"); +MODULE_DESCRIPTION("PNI RM3100 3-axis magnetometer i2c driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/magnetometer/rm3100-i2c.c b/drivers/iio/magnetometer/rm3100-i2c.c new file mode 100644 index 000000000000..1ac622c6d6c9 --- /dev/null +++ b/drivers/iio/magnetometer/rm3100-i2c.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Support for PNI RM3100 3-axis geomagnetic sensor on a i2c bus. + * + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com> + * + * i2c slave address: 0x20 + SA1 << 1 + SA0. + */ + +#include <linux/i2c.h> +#include <linux/module.h> + +#include "rm3100.h" + +static const struct regmap_config rm3100_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .rd_table = &rm3100_readable_table, + .wr_table = &rm3100_writable_table, + .volatile_table = &rm3100_volatile_table, + + .cache_type = REGCACHE_RBTREE, +}; + +static int rm3100_probe(struct i2c_client *client) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(client, &rm3100_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return rm3100_common_probe(&client->dev, regmap, client->irq); +} + +static const struct of_device_id rm3100_dt_match[] = { + { .compatible = "pni,rm3100", }, + { } +}; +MODULE_DEVICE_TABLE(of, rm3100_dt_match); + +static struct i2c_driver rm3100_driver = { + .driver = { + .name = "rm3100-i2c", + .of_match_table = rm3100_dt_match, + }, + .probe_new = rm3100_probe, +}; +module_i2c_driver(rm3100_driver); + +MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>"); +MODULE_DESCRIPTION("PNI RM3100 3-axis magnetometer i2c driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/magnetometer/rm3100-spi.c b/drivers/iio/magnetometer/rm3100-spi.c new file mode 100644 index 000000000000..65d5eb9e4f5e --- /dev/null +++ b/drivers/iio/magnetometer/rm3100-spi.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Support for PNI RM3100 3-axis geomagnetic sensor on a spi bus. + * + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com> + */ + +#include <linux/module.h> +#include <linux/spi/spi.h> + +#include "rm3100.h" + +static const struct regmap_config rm3100_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .rd_table = &rm3100_readable_table, + .wr_table = &rm3100_writable_table, + .volatile_table = &rm3100_volatile_table, + + .read_flag_mask = 0x80, + + .cache_type = REGCACHE_RBTREE, +}; + +static int rm3100_probe(struct spi_device *spi) +{ + struct regmap *regmap; + int ret; + + /* Actually this device supports both mode 0 and mode 3. */ + spi->mode = SPI_MODE_0; + /* Data rates cannot exceed 1Mbits. */ + spi->max_speed_hz = 1000000; + spi->bits_per_word = 8; + ret = spi_setup(spi); + if (ret) + return ret; + + regmap = devm_regmap_init_spi(spi, &rm3100_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return rm3100_common_probe(&spi->dev, regmap, spi->irq); +} + +static const struct of_device_id rm3100_dt_match[] = { + { .compatible = "pni,rm3100", }, + { } +}; +MODULE_DEVICE_TABLE(of, rm3100_dt_match); + +static struct spi_driver rm3100_driver = { + .driver = { + .name = "rm3100-spi", + .of_match_table = rm3100_dt_match, + }, + .probe = rm3100_probe, +}; +module_spi_driver(rm3100_driver); + +MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>"); +MODULE_DESCRIPTION("PNI RM3100 3-axis magnetometer spi driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/magnetometer/rm3100.h b/drivers/iio/magnetometer/rm3100.h new file mode 100644 index 000000000000..c3508218bc77 --- /dev/null +++ b/drivers/iio/magnetometer/rm3100.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com> + */ + +#ifndef RM3100_CORE_H +#define RM3100_CORE_H + +#include <linux/regmap.h> + +extern const struct regmap_access_table rm3100_readable_table; +extern const struct regmap_access_table rm3100_writable_table; +extern const struct regmap_access_table rm3100_volatile_table; + +int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq); + +#endif /* RM3100_CORE_H */ diff --git a/drivers/iio/magnetometer/st_magn.h b/drivers/iio/magnetometer/st_magn.h index 8fe51ce427bd..bc14ad4f1b26 100644 --- a/drivers/iio/magnetometer/st_magn.h +++ b/drivers/iio/magnetometer/st_magn.h @@ -20,6 +20,7 @@ #define LIS3MDL_MAGN_DEV_NAME "lis3mdl" #define LSM303AGR_MAGN_DEV_NAME "lsm303agr_magn" #define LIS2MDL_MAGN_DEV_NAME "lis2mdl" +#define LSM9DS1_MAGN_DEV_NAME "lsm9ds1_magn" int st_magn_common_probe(struct iio_dev *indio_dev); void st_magn_common_remove(struct iio_dev *indio_dev); diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c index 72f6d1335a04..5d056bdb3b37 100644 --- a/drivers/iio/magnetometer/st_magn_core.c +++ b/drivers/iio/magnetometer/st_magn_core.c @@ -29,9 +29,9 @@ #define ST_MAGN_NUMBER_DATA_CHANNELS 3 /* DEFAULT VALUE FOR SENSORS */ -#define ST_MAGN_DEFAULT_OUT_X_H_ADDR 0X03 -#define ST_MAGN_DEFAULT_OUT_Y_H_ADDR 0X07 -#define ST_MAGN_DEFAULT_OUT_Z_H_ADDR 0X05 +#define ST_MAGN_DEFAULT_OUT_X_H_ADDR 0x03 +#define ST_MAGN_DEFAULT_OUT_Y_H_ADDR 0x07 +#define ST_MAGN_DEFAULT_OUT_Z_H_ADDR 0x05 /* FULLSCALE */ #define ST_MAGN_FS_AVL_1300MG 1300 @@ -267,6 +267,7 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = { .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS, .sensors_supported = { [0] = LIS3MDL_MAGN_DEV_NAME, + [1] = LSM9DS1_MAGN_DEV_NAME, }, .ch = (struct iio_chan_spec *)st_magn_2_16bit_channels, .odr = { @@ -315,6 +316,10 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = { }, }, }, + .bdu = { + .addr = 0x24, + .mask = 0x40, + }, .drdy_irq = { /* drdy line is routed drdy pin */ .stat_drdy = { diff --git a/drivers/iio/magnetometer/st_magn_i2c.c b/drivers/iio/magnetometer/st_magn_i2c.c index feaa28cf6a77..68650f5f5c19 100644 --- a/drivers/iio/magnetometer/st_magn_i2c.c +++ b/drivers/iio/magnetometer/st_magn_i2c.c @@ -44,6 +44,10 @@ static const struct of_device_id st_magn_of_match[] = { .compatible = "st,lis2mdl", .data = LIS2MDL_MAGN_DEV_NAME, }, + { + .compatible = "st,lsm9ds1-magn", + .data = LSM9DS1_MAGN_DEV_NAME, + }, {}, }; MODULE_DEVICE_TABLE(of, st_magn_of_match); @@ -90,6 +94,7 @@ static const struct i2c_device_id st_magn_id_table[] = { { LIS3MDL_MAGN_DEV_NAME }, { LSM303AGR_MAGN_DEV_NAME }, { LIS2MDL_MAGN_DEV_NAME }, + { LSM9DS1_MAGN_DEV_NAME }, {}, }; MODULE_DEVICE_TABLE(i2c, st_magn_id_table); diff --git a/drivers/iio/magnetometer/st_magn_spi.c b/drivers/iio/magnetometer/st_magn_spi.c index 7b7cd08fcc32..cb05fcd9ddfe 100644 --- a/drivers/iio/magnetometer/st_magn_spi.c +++ b/drivers/iio/magnetometer/st_magn_spi.c @@ -23,6 +23,8 @@ * For new single-chip sensors use <device_name> as compatible string. * For old single-chip devices keep <device_name>-magn to maintain * compatibility + * For multi-chip devices, use <device_name>-magn to distinguish which + * capability is being used */ static const struct of_device_id st_magn_of_match[] = { { @@ -37,6 +39,10 @@ static const struct of_device_id st_magn_of_match[] = { .compatible = "st,lis2mdl", .data = LIS2MDL_MAGN_DEV_NAME, }, + { + .compatible = "st,lsm9ds1-magn", + .data = LSM9DS1_MAGN_DEV_NAME, + }, {} }; MODULE_DEVICE_TABLE(of, st_magn_of_match); @@ -79,6 +85,7 @@ static const struct spi_device_id st_magn_id_table[] = { { LIS3MDL_MAGN_DEV_NAME }, { LSM303AGR_MAGN_DEV_NAME }, { LIS2MDL_MAGN_DEV_NAME }, + { LSM9DS1_MAGN_DEV_NAME }, {}, }; MODULE_DEVICE_TABLE(spi, st_magn_id_table); diff --git a/drivers/iio/potentiometer/mcp4131.c b/drivers/iio/potentiometer/mcp4131.c index b3e30db246cc..efe035ce010d 100644 --- a/drivers/iio/potentiometer/mcp4131.c +++ b/drivers/iio/potentiometer/mcp4131.c @@ -42,6 +42,7 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/of.h> +#include <linux/of_device.h> #include <linux/spi/spi.h> #define MCP4131_WRITE (0x00 << 2) @@ -243,7 +244,7 @@ static int mcp4131_probe(struct spi_device *spi) { int err; struct device *dev = &spi->dev; - unsigned long devid = spi_get_device_id(spi)->driver_data; + unsigned long devid; struct mcp4131_data *data; struct iio_dev *indio_dev; @@ -254,7 +255,11 @@ static int mcp4131_probe(struct spi_device *spi) data = iio_priv(indio_dev); spi_set_drvdata(spi, indio_dev); data->spi = spi; - data->cfg = &mcp4131_cfg[devid]; + data->cfg = of_device_get_match_data(&spi->dev); + if (!data->cfg) { + devid = spi_get_device_id(spi)->driver_data; + data->cfg = &mcp4131_cfg[devid]; + } mutex_init(&data->lock); @@ -273,7 +278,6 @@ static int mcp4131_probe(struct spi_device *spi) return 0; } -#if defined(CONFIG_OF) static const struct of_device_id mcp4131_dt_ids[] = { { .compatible = "microchip,mcp4131-502", .data = &mcp4131_cfg[MCP413x_502] }, @@ -406,7 +410,6 @@ static const struct of_device_id mcp4131_dt_ids[] = { {} }; MODULE_DEVICE_TABLE(of, mcp4131_dt_ids); -#endif /* CONFIG_OF */ static const struct spi_device_id mcp4131_id[] = { { "mcp4131-502", MCP413x_502 }, diff --git a/drivers/iio/potentiometer/tpl0102.c b/drivers/iio/potentiometer/tpl0102.c index ca1cce58fe20..a0a07e47f13f 100644 --- a/drivers/iio/potentiometer/tpl0102.c +++ b/drivers/iio/potentiometer/tpl0102.c @@ -15,7 +15,7 @@ struct tpl0102_cfg { int wipers; - int max_pos; + int avail[3]; int kohms; }; @@ -28,16 +28,16 @@ enum tpl0102_type { static const struct tpl0102_cfg tpl0102_cfg[] = { /* on-semiconductor parts */ - [CAT5140_503] = { .wipers = 1, .max_pos = 256, .kohms = 50, }, - [CAT5140_104] = { .wipers = 1, .max_pos = 256, .kohms = 100, }, + [CAT5140_503] = { .wipers = 1, .avail = { 0, 1, 255 }, .kohms = 50, }, + [CAT5140_104] = { .wipers = 1, .avail = { 0, 1, 255 }, .kohms = 100, }, /* ti parts */ - [TPL0102_104] = { .wipers = 2, .max_pos = 256, .kohms = 100 }, - [TPL0401_103] = { .wipers = 1, .max_pos = 128, .kohms = 10, }, + [TPL0102_104] = { .wipers = 2, .avail = { 0, 1, 255 }, .kohms = 100 }, + [TPL0401_103] = { .wipers = 1, .avail = { 0, 1, 127 }, .kohms = 10, }, }; struct tpl0102_data { struct regmap *regmap; - unsigned long devid; + const struct tpl0102_cfg *cfg; }; static const struct regmap_config tpl0102_regmap_config = { @@ -52,6 +52,7 @@ static const struct regmap_config tpl0102_regmap_config = { .channel = (ch), \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW), \ } static const struct iio_chan_spec tpl0102_channels[] = { @@ -72,14 +73,32 @@ static int tpl0102_read_raw(struct iio_dev *indio_dev, return ret ? ret : IIO_VAL_INT; } case IIO_CHAN_INFO_SCALE: - *val = 1000 * tpl0102_cfg[data->devid].kohms; - *val2 = tpl0102_cfg[data->devid].max_pos; + *val = 1000 * data->cfg->kohms; + *val2 = data->cfg->avail[2] + 1; return IIO_VAL_FRACTIONAL; } return -EINVAL; } +static int tpl0102_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct tpl0102_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + *length = ARRAY_SIZE(data->cfg->avail); + *vals = data->cfg->avail; + *type = IIO_VAL_INT; + return IIO_AVAIL_RANGE; + } + + return -EINVAL; +} + static int tpl0102_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) @@ -89,7 +108,7 @@ static int tpl0102_write_raw(struct iio_dev *indio_dev, if (mask != IIO_CHAN_INFO_RAW) return -EINVAL; - if (val >= tpl0102_cfg[data->devid].max_pos || val < 0) + if (val > data->cfg->avail[2] || val < 0) return -EINVAL; return regmap_write(data->regmap, chan->channel, val); @@ -97,6 +116,7 @@ static int tpl0102_write_raw(struct iio_dev *indio_dev, static const struct iio_info tpl0102_info = { .read_raw = tpl0102_read_raw, + .read_avail = tpl0102_read_avail, .write_raw = tpl0102_write_raw, }; @@ -113,7 +133,7 @@ static int tpl0102_probe(struct i2c_client *client, data = iio_priv(indio_dev); i2c_set_clientdata(client, indio_dev); - data->devid = id->driver_data; + data->cfg = &tpl0102_cfg[id->driver_data]; data->regmap = devm_regmap_init_i2c(client, &tpl0102_regmap_config); if (IS_ERR(data->regmap)) { dev_err(dev, "regmap initialization failed\n"); @@ -123,7 +143,7 @@ static int tpl0102_probe(struct i2c_client *client, indio_dev->dev.parent = dev; indio_dev->info = &tpl0102_info; indio_dev->channels = tpl0102_channels; - indio_dev->num_channels = tpl0102_cfg[data->devid].wipers; + indio_dev->num_channels = data->cfg->wipers; indio_dev->name = client->name; return devm_iio_device_register(dev, indio_dev); |