First round of IIO new device support, features and cleanups for the 4.10 cycle.

Fair number of outreachy related patches in here. Some of these may well
 have already been picked up by Greg but git will sort that out for us.
 
 Also some good staging cleanup work from other sources. Thanks Brian and Lars
 in particular for this.
 
 New device support
 * ACCES 104-quad-8
   - New driver for this 8 channel encoder input board. Lots of new ABI with
     this one.
 * AD7766
   - New driver supporting AD7766, AD7766-1, AD7766-2, AD7767, AD7767-1 and
     AD7767-2 24 bit ADCs.
 * dmard 10
   - New driver for this 3 axis accelerometer.
 * Honeywell ABP pressure sensors.
   - New driver covering 56 parts in this series (too many to list here!)
 * HTS221
   - New driver to support this relative humidiy and temperature device.
 * LMP91000
   - New driver for this potentiostat (form of chemical sensor). Nice example
     of use of the buffered consumer interfaces and the use of a consumer
     provided trigger.
 * MiraMEMS DA311
   - New driver for this 3 axis accelerometer.
 * MiraMEMS DA280
   - New driver for this 3 axis accelerometer. Follow up caught up with
   vendor prefixes for these.
 
 Staging graduations
 * isl29018 light sensor
   - Fixes and cleanups listed below (thanks for your hard work on this Brian!)
 * sca3000
   - Fixes and cleanups listed below.  This was one of the small set of drivers
   that went into staging when IIO was first added.  Turns out it had a few
   bugs and needed to be brought into the modern era!  Not clear if I am
   the only person who actually has one of these still wired to a board.
 
 New features (Core)
  - Add an iio_trigger_validate_own_device helper which relies on the device
    and trigger having the same parent.  Convenient to have this for some
    of the more complex trigger / device interactions. Was hand rolled in
    a few drivers already so good to bring it into the core.
  - Add an iio_read_channel_offset in kernel access helper (similar to
    the existing one for scale).
  - IIO_ATTR_{RO, WO, RW} and IIO_DEVICE_ATTR_{RO, WO, RW} macros.  These
    lead some rather contrived function naming, but there is no denying they
    do reduced boilerplate.  I'm going to resist their introduction in
    drivers 'unless' they form part of a larger set of cleanups.
  - Counter channel type and index type.
 
 New features (Drivers)
 * hdc100x
   - Triggered buffer support.
 * mcp4725
   - Device tree bindings and support.
   - Voltage reference selection.
 * ti-adc0832
   - Triggered buffer support.
 * ti-adc161s626
   - Add regulator support allowing _scale and _offset values to be established
   and exported.
 
 New features (Tools)
 * iio_generic_buffer
   - -A option to force enable all channels rather than faulting if some are
   already enabled (like -a does).  Followup patches tidied this support up.
 
 Cleanups (Core)
  - Use kmalloc_array in iio_scan_mask_set.
  - Take event_attrs field of iio_info structure constant
  - Staging todo list updates. Most of it was long done.
  - MAINTAINERS had a wrong directory listing.
 
 Cleanups (Drivers)
 * Missing i2c trivial devices entries.
 * ad5592r
   - Fix an endian type related sparse warnings.
 * ad7150
   - Constify the event attribute_group structures.
 * ad7152
   - Add some blank lines to improve readability.
   - Sampling frequency control via chan-info element rather than hand rolled
   attributes.
   - add a new lock to avoid use of mlock for non state change related locking.
 * ad7280
   - Constify atrribute_group structure (second patch covers the event ones)
 * ad7606 (Lars is driving most of the cleanup on this with some additions from
   Eva)
   - Fix improper setting of oversampling pins.  This has been broken a very
   long time in this staging driver, so not going to push this back to stable.
   - Implement oversampling configuration via the chan_info mask element.
   - Remove an unused int_vref_mv field.
   - Remove a reundant name field from ad7606_chip_info.
   - Remove default device configuration from platform_data in favour of
   whatever the power on defaults are.
   - Remove out of band error reporting in the kernel log as not providing
   much information.
   - Fix oversampling ratio by having 1 be the value for no oversampling.
   - Avoid allocating buffer for each data capture.
   - Factor out common code between periodic and one-shot capture.
   - Move set_drvdat into common code.
   - Let the common probe function return int rather than jumping through
   an ERR_PTR.
   - Pass struct device * into common remove to simplify code.
   - Always run trigger handler only once per event (no one can remember why
   it was being possibly done twice).
   - Move over to the GPIO descriptor API to shorten and clarify code.
   - Move the buffer code into the main file as it's not optional and is
   now rather short in this driver.
   - Fix the naming of the supply regulator.
   - Rework regulator handling to handle errors including deferred probing.
   - Tidy up a ptr_err or 0 return.
 * ad7746
   - Sampling frequency control via info_mask element rather than hand rolled
 * ad7758
   - Sampling frequency control via info_mask element rather than hand rolled
   attributes.
 * ad7816
   - Constify the event attribute_group structure.
 * adt7316
   - Constify the event attribute group structures.
 * ak8974
   - Cleanup some sparse warnings about endian types.
 * ak8975
   - Cleanup some sparse warnings about endian types.
 * bmi160
   - Spare endian warning cleanups.
 * isl29018 (towards staging graduation)
   - Remove unusedvariables and defines.
   - Improve consistency of error handling.
   - Signed / unsigned comparison fixes.
   - Use the IIO_DEVICE_ATTR_{RO, RW} macros
   - Fix a race in in_illuminance_scale_available_show.
   - Cleanup exit points of _read_raw
   - Sanity check if in suspended state during a write_raw call as was already
     done for read_raw.
   - Document device tree bidnings.
   - Document infrared supression controls.
   - Add some newlines to improve readability and drop one that shouldn't be
     there.
   - Fix a poorly named functions name.
   - Fix multiline coment syntax.
   - Tidy up a pair or return statements by unifying them.
   - Rename description in Kconfig for consistency with similar drivers.
 * lidar
   - cleanup power management by dropping unnecessary call.
 * ltr501
   - Use the claim_direct_mode helpers. Fix a race condition along the way.
 * max1027
   - Fix a dubious x | !y sparse warning.
   - Use the new iio_trigger_validate_own_device helper.
 * max440000
   - Clean up some sparse warnings about endian types.
 * mcp4725
   - Use the regulator framework to establish the reference voltage rather than
     getting it from platform data.
   - Tidy up a comment typo.
   - Fix a wrong PTR_ERR query (wrong regulator).
 * mma7660
   - Take a mma7660_nscale static.
 * mma8452
   - Use the new iio_trigger_validate_own_device helper.
   - Use claim_direct_mode helpers - fix a race condition along the way.
 * mpl3115
   - Use claim_direct_mode helpers - fix a race condition along the way.
 * ms65611
   - Tidy up regulator error handling and clean out a static warning in the mix.
 * sca3000
   - Avoid a potential unitialized variable if a hardware read returns a value
   that isn't actually supported (mostly warning supression).
   - Fix a use before setting of the indio_dev->buffer pointer. Broken for
   a very long time so not going to rush this into stable.
   - Merge buffer file with core file.  We used to always split these.
   Sometimes it's just not worth the hassle. In this case the device's main
   feature is it's hardware fifos so unlikely anyone would want to run it
   without.
   - Drop the sca3000_register_ring_funcs function as it's a pointless wrapper
   once we have only one file.
   - Fix cleaning of flag + setting of size of scan. Without this you can't
   start the buffer twice and expect sensible (or any) results.  Again,
   broken for a long time so not heading for stable.
   - Drop the custom watershed setting ABI - for now we'll just support one
   value.
   - Move to a hybrid hard / soft buffer design (how we've been doing it
   for similar devices for a while now!)
   - Cleanup some unusued variables.
   - Use a fake channel to support core handling of freefall event registration.
   - Cleanup the register defines.
   - Fix an off by one error in axis due to IIO_NO_MOD taking up the 0 value.
   Been broken since first admission of IIO to the staging tree.
   - Add readback of the 3db low pass filter frequency and later writing
     allowing droppign of custom measurement mode attributes as they can
     be represented by the filter choices that is their main characteristic.
   - Drop non standard revision attr and replace with dev_info on probe.
   - Avoid a race in probe.
   - Various formatting fixes.
   - Kernel-docify docs that were very nearly in the write format.
 * tsl2583
   - Constify attribute_group structure.
 * zpa2326
   - Drop a redundant DEBUG ifdef.
 
 Cleanups (Tools)
 * iio_generic_buffer
   - Fix the ? arguement. Previously it sort of worked as you got the help
     message as a result of it not recognising the arguement.
 -----BEGIN PGP SIGNATURE-----
 
 iQIuBAABCAAYBQJYDSX+ERxqaWMyM0BrZXJuZWwub3JnAAoJEFSFNJnE9BaIQSEP
 /2HRMqDXeIh2N0oVLBFK5pEaAZ52tjsEmf1XgfCJ3D1PczS0vzqkFKuG/078BU3v
 e9MTj39sF1EOyGJv4mdfnHygLPvjaTU/A6HoIaP1t9cUgSQ1jzPN0tO8/Hj/ESlU
 U3mNgUNUIa9cnVIfWk2cPJqgKJ/w52vRk00yRnOKZjHZkxbYQq9rPJ7ks12jQSGT
 IBEipiYMnRovc2w4NWbAry5pAXBmttVFKyi5FZ7uIM5EH34+SAY6laXSoZQl8E74
 LKgIwdPUn9yeujyX6ohBFRFloOf/4VYedV6NIUz8k5BzuTeAJcp4DSMLXSWIwZVd
 sdza6el3Tt0MkjIgioTg7N07FvatbQVIsnWnLjLb/zpMjGQPigfTnIi7kYcH3wHx
 NwpTy1MZg5U571LOYXsEhNvIFiQICoV6+8d/PzrO1J+rphuQYreOm0WzjvIqW4q/
 dU2uCAMOHd82gjm9QrKjvrc+4M3oGc3ENE/i5P2YNQhdkdNKp9PMWgL6RO9QXgeQ
 9TrjbDr9OSFLatZg5IswWFa5CHL9AZ6OuvaRyx/8n5cBdY3n8+wMIowRQJlMUdGY
 Aa5ByntMsvhwCu+M1+fDihZyx4suTNvW7+Emqd/8lCb+BRjHoamBb6uGBGx6Txvy
 6TPluNWE+cK5Tjkrn+jDbTzhwdaaSRtO4ECRwxXYAqfn
 =ZRUt
 -----END PGP SIGNATURE-----

Merge tag 'iio-for-4.10a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next

Jonathan writes:

First round of IIO new device support, features and cleanups for the 4.10 cycle.

Fair number of outreachy related patches in here. Some of these may well
have already been picked up by Greg but git will sort that out for us.

Also some good staging cleanup work from other sources. Thanks Brian and Lars
in particular for this.

New device support
* ACCES 104-quad-8
  - New driver for this 8 channel encoder input board. Lots of new ABI with
    this one.
* AD7766
  - New driver supporting AD7766, AD7766-1, AD7766-2, AD7767, AD7767-1 and
    AD7767-2 24 bit ADCs.
* dmard 10
  - New driver for this 3 axis accelerometer.
* Honeywell ABP pressure sensors.
  - New driver covering 56 parts in this series (too many to list here!)
* HTS221
  - New driver to support this relative humidiy and temperature device.
* LMP91000
  - New driver for this potentiostat (form of chemical sensor). Nice example
    of use of the buffered consumer interfaces and the use of a consumer
    provided trigger.
* MiraMEMS DA311
  - New driver for this 3 axis accelerometer.
* MiraMEMS DA280
  - New driver for this 3 axis accelerometer. Follow up caught up with
  vendor prefixes for these.

Staging graduations
* isl29018 light sensor
  - Fixes and cleanups listed below (thanks for your hard work on this Brian!)
* sca3000
  - Fixes and cleanups listed below.  This was one of the small set of drivers
  that went into staging when IIO was first added.  Turns out it had a few
  bugs and needed to be brought into the modern era!  Not clear if I am
  the only person who actually has one of these still wired to a board.

New features (Core)
 - Add an iio_trigger_validate_own_device helper which relies on the device
   and trigger having the same parent.  Convenient to have this for some
   of the more complex trigger / device interactions. Was hand rolled in
   a few drivers already so good to bring it into the core.
 - Add an iio_read_channel_offset in kernel access helper (similar to
   the existing one for scale).
 - IIO_ATTR_{RO, WO, RW} and IIO_DEVICE_ATTR_{RO, WO, RW} macros.  These
   lead some rather contrived function naming, but there is no denying they
   do reduced boilerplate.  I'm going to resist their introduction in
   drivers 'unless' they form part of a larger set of cleanups.
 - Counter channel type and index type.

New features (Drivers)
* hdc100x
  - Triggered buffer support.
* mcp4725
  - Device tree bindings and support.
  - Voltage reference selection.
* ti-adc0832
  - Triggered buffer support.
* ti-adc161s626
  - Add regulator support allowing _scale and _offset values to be established
  and exported.

New features (Tools)
* iio_generic_buffer
  - -A option to force enable all channels rather than faulting if some are
  already enabled (like -a does).  Followup patches tidied this support up.

Cleanups (Core)
 - Use kmalloc_array in iio_scan_mask_set.
 - Take event_attrs field of iio_info structure constant
 - Staging todo list updates. Most of it was long done.
 - MAINTAINERS had a wrong directory listing.

Cleanups (Drivers)
* Missing i2c trivial devices entries.
* ad5592r
  - Fix an endian type related sparse warnings.
* ad7150
  - Constify the event attribute_group structures.
* ad7152
  - Add some blank lines to improve readability.
  - Sampling frequency control via chan-info element rather than hand rolled
  attributes.
  - add a new lock to avoid use of mlock for non state change related locking.
* ad7280
  - Constify atrribute_group structure (second patch covers the event ones)
* ad7606 (Lars is driving most of the cleanup on this with some additions from
  Eva)
  - Fix improper setting of oversampling pins.  This has been broken a very
  long time in this staging driver, so not going to push this back to stable.
  - Implement oversampling configuration via the chan_info mask element.
  - Remove an unused int_vref_mv field.
  - Remove a reundant name field from ad7606_chip_info.
  - Remove default device configuration from platform_data in favour of
  whatever the power on defaults are.
  - Remove out of band error reporting in the kernel log as not providing
  much information.
  - Fix oversampling ratio by having 1 be the value for no oversampling.
  - Avoid allocating buffer for each data capture.
  - Factor out common code between periodic and one-shot capture.
  - Move set_drvdat into common code.
  - Let the common probe function return int rather than jumping through
  an ERR_PTR.
  - Pass struct device * into common remove to simplify code.
  - Always run trigger handler only once per event (no one can remember why
  it was being possibly done twice).
  - Move over to the GPIO descriptor API to shorten and clarify code.
  - Move the buffer code into the main file as it's not optional and is
  now rather short in this driver.
  - Fix the naming of the supply regulator.
  - Rework regulator handling to handle errors including deferred probing.
  - Tidy up a ptr_err or 0 return.
* ad7746
  - Sampling frequency control via info_mask element rather than hand rolled
* ad7758
  - Sampling frequency control via info_mask element rather than hand rolled
  attributes.
* ad7816
  - Constify the event attribute_group structure.
* adt7316
  - Constify the event attribute group structures.
* ak8974
  - Cleanup some sparse warnings about endian types.
* ak8975
  - Cleanup some sparse warnings about endian types.
* bmi160
  - Spare endian warning cleanups.
* isl29018 (towards staging graduation)
  - Remove unusedvariables and defines.
  - Improve consistency of error handling.
  - Signed / unsigned comparison fixes.
  - Use the IIO_DEVICE_ATTR_{RO, RW} macros
  - Fix a race in in_illuminance_scale_available_show.
  - Cleanup exit points of _read_raw
  - Sanity check if in suspended state during a write_raw call as was already
    done for read_raw.
  - Document device tree bidnings.
  - Document infrared supression controls.
  - Add some newlines to improve readability and drop one that shouldn't be
    there.
  - Fix a poorly named functions name.
  - Fix multiline coment syntax.
  - Tidy up a pair or return statements by unifying them.
  - Rename description in Kconfig for consistency with similar drivers.
* lidar
  - cleanup power management by dropping unnecessary call.
* ltr501
  - Use the claim_direct_mode helpers. Fix a race condition along the way.
* max1027
  - Fix a dubious x | !y sparse warning.
  - Use the new iio_trigger_validate_own_device helper.
* max440000
  - Clean up some sparse warnings about endian types.
* mcp4725
  - Use the regulator framework to establish the reference voltage rather than
    getting it from platform data.
  - Tidy up a comment typo.
  - Fix a wrong PTR_ERR query (wrong regulator).
* mma7660
  - Take a mma7660_nscale static.
* mma8452
  - Use the new iio_trigger_validate_own_device helper.
  - Use claim_direct_mode helpers - fix a race condition along the way.
* mpl3115
  - Use claim_direct_mode helpers - fix a race condition along the way.
* ms65611
  - Tidy up regulator error handling and clean out a static warning in the mix.
* sca3000
  - Avoid a potential unitialized variable if a hardware read returns a value
  that isn't actually supported (mostly warning supression).
  - Fix a use before setting of the indio_dev->buffer pointer. Broken for
  a very long time so not going to rush this into stable.
  - Merge buffer file with core file.  We used to always split these.
  Sometimes it's just not worth the hassle. In this case the device's main
  feature is it's hardware fifos so unlikely anyone would want to run it
  without.
  - Drop the sca3000_register_ring_funcs function as it's a pointless wrapper
  once we have only one file.
  - Fix cleaning of flag + setting of size of scan. Without this you can't
  start the buffer twice and expect sensible (or any) results.  Again,
  broken for a long time so not heading for stable.
  - Drop the custom watershed setting ABI - for now we'll just support one
  value.
  - Move to a hybrid hard / soft buffer design (how we've been doing it
  for similar devices for a while now!)
  - Cleanup some unusued variables.
  - Use a fake channel to support core handling of freefall event registration.
  - Cleanup the register defines.
  - Fix an off by one error in axis due to IIO_NO_MOD taking up the 0 value.
  Been broken since first admission of IIO to the staging tree.
  - Add readback of the 3db low pass filter frequency and later writing
    allowing droppign of custom measurement mode attributes as they can
    be represented by the filter choices that is their main characteristic.
  - Drop non standard revision attr and replace with dev_info on probe.
  - Avoid a race in probe.
  - Various formatting fixes.
  - Kernel-docify docs that were very nearly in the write format.
* tsl2583
  - Constify attribute_group structure.
* zpa2326
  - Drop a redundant DEBUG ifdef.

Cleanups (Tools)
* iio_generic_buffer
  - Fix the ? arguement. Previously it sort of worked as you got the help
    message as a result of it not recognising the arguement.
This commit is contained in:
Greg Kroah-Hartman 2016-10-24 11:34:22 +02:00
commit c7c88e998a
93 changed files with 6842 additions and 2875 deletions

View file

@ -73,6 +73,7 @@ source "drivers/iio/adc/Kconfig"
source "drivers/iio/amplifiers/Kconfig"
source "drivers/iio/chemical/Kconfig"
source "drivers/iio/common/Kconfig"
source "drivers/iio/counter/Kconfig"
source "drivers/iio/dac/Kconfig"
source "drivers/iio/dummy/Kconfig"
source "drivers/iio/frequency/Kconfig"
@ -87,6 +88,7 @@ if IIO_TRIGGER
source "drivers/iio/trigger/Kconfig"
endif #IIO_TRIGGER
source "drivers/iio/potentiometer/Kconfig"
source "drivers/iio/potentiostat/Kconfig"
source "drivers/iio/pressure/Kconfig"
source "drivers/iio/proximity/Kconfig"
source "drivers/iio/temperature/Kconfig"

View file

@ -18,6 +18,7 @@ obj-y += amplifiers/
obj-y += buffer/
obj-y += chemical/
obj-y += common/
obj-y += counter/
obj-y += dac/
obj-y += dummy/
obj-y += gyro/
@ -29,6 +30,7 @@ obj-y += light/
obj-y += magnetometer/
obj-y += orientation/
obj-y += potentiometer/
obj-y += potentiostat/
obj-y += pressure/
obj-y += proximity/
obj-y += temperature/

View file

@ -52,6 +52,26 @@ config BMC150_ACCEL_SPI
tristate
select REGMAP_SPI
config DA280
tristate "MiraMEMS DA280 3-axis 14-bit digital accelerometer driver"
depends on I2C
help
Say yes here to build support for the MiraMEMS DA280 3-axis 14-bit
digital accelerometer.
To compile this driver as a module, choose M here: the
module will be called da280.
config DA311
tristate "MiraMEMS DA311 3-axis 12-bit digital accelerometer driver"
depends on I2C
help
Say yes here to build support for the MiraMEMS DA311 3-axis 12-bit
digital accelerometer.
To compile this driver as a module, choose M here: the
module will be called da311.
config DMARD06
tristate "Domintech DMARD06 Digital Accelerometer Driver"
depends on OF || COMPILE_TEST
@ -73,6 +93,16 @@ config DMARD09
Choosing M will build the driver as a module. If so, the module
will be called dmard09.
config DMARD10
tristate "Domintech DMARD10 3-axis Accelerometer Driver"
depends on I2C
help
Say yes here to get support for the Domintech DMARD10 3-axis
accelerometer.
Choosing M will build the driver as a module. If so, the module
will be called dmard10.
config HID_SENSOR_ACCEL_3D
depends on HID_SENSOR_HUB
select IIO_BUFFER
@ -273,6 +303,18 @@ config MXC6255
To compile this driver as a module, choose M here: the module will be
called mxc6255.
config SCA3000
select IIO_BUFFER
select IIO_KFIFO_BUF
depends on SPI
tristate "VTI SCA3000 series accelerometers"
help
Say Y here to build support for the VTI SCA3000 series of SPI
accelerometers. These devices use a hardware ring buffer.
To compile this driver as a module, say M here: the module will be
called sca3000.
config STK8312
tristate "Sensortek STK8312 3-Axis Accelerometer Driver"
depends on I2C

View file

@ -8,8 +8,11 @@ obj-$(CONFIG_BMA220) += bma220_spi.o
obj-$(CONFIG_BMC150_ACCEL) += bmc150-accel-core.o
obj-$(CONFIG_BMC150_ACCEL_I2C) += bmc150-accel-i2c.o
obj-$(CONFIG_BMC150_ACCEL_SPI) += bmc150-accel-spi.o
obj-$(CONFIG_DA280) += da280.o
obj-$(CONFIG_DA311) += da311.o
obj-$(CONFIG_DMARD06) += dmard06.o
obj-$(CONFIG_DMARD09) += dmard09.o
obj-$(CONFIG_DMARD10) += dmard10.o
obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o
obj-$(CONFIG_KXSD9) += kxsd9.o
@ -32,6 +35,8 @@ obj-$(CONFIG_MMA9553) += mma9553.o
obj-$(CONFIG_MXC4005) += mxc4005.o
obj-$(CONFIG_MXC6255) += mxc6255.o
obj-$(CONFIG_SCA3000) += sca3000.o
obj-$(CONFIG_STK8312) += stk8312.o
obj-$(CONFIG_STK8BA50) += stk8ba50.o

183
drivers/iio/accel/da280.c Normal file
View file

@ -0,0 +1,183 @@
/**
* IIO driver for the MiraMEMS DA280 3-axis accelerometer and
* IIO driver for the MiraMEMS DA226 2-axis accelerometer
*
* Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/byteorder/generic.h>
#define DA280_REG_CHIP_ID 0x01
#define DA280_REG_ACC_X_LSB 0x02
#define DA280_REG_ACC_Y_LSB 0x04
#define DA280_REG_ACC_Z_LSB 0x06
#define DA280_REG_MODE_BW 0x11
#define DA280_CHIP_ID 0x13
#define DA280_MODE_ENABLE 0x1e
#define DA280_MODE_DISABLE 0x9e
enum { da226, da280 };
/*
* a value of + or -4096 corresponds to + or - 1G
* scale = 9.81 / 4096 = 0.002395019
*/
static const int da280_nscale = 2395019;
#define DA280_CHANNEL(reg, axis) { \
.type = IIO_ACCEL, \
.address = reg, \
.modified = 1, \
.channel2 = IIO_MOD_##axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
}
static const struct iio_chan_spec da280_channels[] = {
DA280_CHANNEL(DA280_REG_ACC_X_LSB, X),
DA280_CHANNEL(DA280_REG_ACC_Y_LSB, Y),
DA280_CHANNEL(DA280_REG_ACC_Z_LSB, Z),
};
struct da280_data {
struct i2c_client *client;
};
static int da280_enable(struct i2c_client *client, bool enable)
{
u8 data = enable ? DA280_MODE_ENABLE : DA280_MODE_DISABLE;
return i2c_smbus_write_byte_data(client, DA280_REG_MODE_BW, data);
}
static int da280_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct da280_data *data = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = i2c_smbus_read_word_data(data->client, chan->address);
if (ret < 0)
return ret;
/*
* Values are 14 bits, stored as 16 bits with the 2
* least significant bits always 0.
*/
*val = (short)ret >> 2;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 0;
*val2 = da280_nscale;
return IIO_VAL_INT_PLUS_NANO;
default:
return -EINVAL;
}
}
static const struct iio_info da280_info = {
.driver_module = THIS_MODULE,
.read_raw = da280_read_raw,
};
static int da280_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret;
struct iio_dev *indio_dev;
struct da280_data *data;
ret = i2c_smbus_read_byte_data(client, DA280_REG_CHIP_ID);
if (ret != DA280_CHIP_ID)
return (ret < 0) ? ret : -ENODEV;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
data->client = client;
i2c_set_clientdata(client, indio_dev);
indio_dev->dev.parent = &client->dev;
indio_dev->info = &da280_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = da280_channels;
if (id->driver_data == da226) {
indio_dev->name = "da226";
indio_dev->num_channels = 2;
} else {
indio_dev->name = "da280";
indio_dev->num_channels = 3;
}
ret = da280_enable(client, true);
if (ret < 0)
return ret;
ret = iio_device_register(indio_dev);
if (ret < 0) {
dev_err(&client->dev, "device_register failed\n");
da280_enable(client, false);
}
return ret;
}
static int da280_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
iio_device_unregister(indio_dev);
return da280_enable(client, false);
}
#ifdef CONFIG_PM_SLEEP
static int da280_suspend(struct device *dev)
{
return da280_enable(to_i2c_client(dev), false);
}
static int da280_resume(struct device *dev)
{
return da280_enable(to_i2c_client(dev), true);
}
#endif
static SIMPLE_DEV_PM_OPS(da280_pm_ops, da280_suspend, da280_resume);
static const struct i2c_device_id da280_i2c_id[] = {
{ "da226", da226 },
{ "da280", da280 },
{}
};
MODULE_DEVICE_TABLE(i2c, da280_i2c_id);
static struct i2c_driver da280_driver = {
.driver = {
.name = "da280",
.pm = &da280_pm_ops,
},
.probe = da280_probe,
.remove = da280_remove,
.id_table = da280_i2c_id,
};
module_i2c_driver(da280_driver);
MODULE_AUTHOR("Hans de Goede <[email protected]>");
MODULE_DESCRIPTION("MiraMEMS DA280 3-Axis Accelerometer driver");
MODULE_LICENSE("GPL v2");

305
drivers/iio/accel/da311.c Normal file
View file

@ -0,0 +1,305 @@
/**
* IIO driver for the MiraMEMS DA311 3-axis accelerometer
*
* Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com>
* Copyright (c) 2011-2013 MiraMEMS Sensing Technology Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/byteorder/generic.h>
#define DA311_CHIP_ID 0x13
/*
* Note register addressed go from 0 - 0x3f and then wrap.
* For some reason there are 2 banks with 0 - 0x3f addresses,
* rather then a single 0-0x7f bank.
*/
/* Bank 0 regs */
#define DA311_REG_BANK 0x0000
#define DA311_REG_LDO_REG 0x0006
#define DA311_REG_CHIP_ID 0x000f
#define DA311_REG_TEMP_CFG_REG 0x001f
#define DA311_REG_CTRL_REG1 0x0020
#define DA311_REG_CTRL_REG3 0x0022
#define DA311_REG_CTRL_REG4 0x0023
#define DA311_REG_CTRL_REG5 0x0024
#define DA311_REG_CTRL_REG6 0x0025
#define DA311_REG_STATUS_REG 0x0027
#define DA311_REG_OUT_X_L 0x0028
#define DA311_REG_OUT_X_H 0x0029
#define DA311_REG_OUT_Y_L 0x002a
#define DA311_REG_OUT_Y_H 0x002b
#define DA311_REG_OUT_Z_L 0x002c
#define DA311_REG_OUT_Z_H 0x002d
#define DA311_REG_INT1_CFG 0x0030
#define DA311_REG_INT1_SRC 0x0031
#define DA311_REG_INT1_THS 0x0032
#define DA311_REG_INT1_DURATION 0x0033
#define DA311_REG_INT2_CFG 0x0034
#define DA311_REG_INT2_SRC 0x0035
#define DA311_REG_INT2_THS 0x0036
#define DA311_REG_INT2_DURATION 0x0037
#define DA311_REG_CLICK_CFG 0x0038
#define DA311_REG_CLICK_SRC 0x0039
#define DA311_REG_CLICK_THS 0x003a
#define DA311_REG_TIME_LIMIT 0x003b
#define DA311_REG_TIME_LATENCY 0x003c
#define DA311_REG_TIME_WINDOW 0x003d
/* Bank 1 regs */
#define DA311_REG_SOFT_RESET 0x0105
#define DA311_REG_OTP_XOFF_L 0x0110
#define DA311_REG_OTP_XOFF_H 0x0111
#define DA311_REG_OTP_YOFF_L 0x0112
#define DA311_REG_OTP_YOFF_H 0x0113
#define DA311_REG_OTP_ZOFF_L 0x0114
#define DA311_REG_OTP_ZOFF_H 0x0115
#define DA311_REG_OTP_XSO 0x0116
#define DA311_REG_OTP_YSO 0x0117
#define DA311_REG_OTP_ZSO 0x0118
#define DA311_REG_OTP_TRIM_OSC 0x011b
#define DA311_REG_LPF_ABSOLUTE 0x011c
#define DA311_REG_TEMP_OFF1 0x0127
#define DA311_REG_TEMP_OFF2 0x0128
#define DA311_REG_TEMP_OFF3 0x0129
#define DA311_REG_OTP_TRIM_THERM_H 0x011a
/*
* a value of + or -1024 corresponds to + or - 1G
* scale = 9.81 / 1024 = 0.009580078
*/
static const int da311_nscale = 9580078;
#define DA311_CHANNEL(reg, axis) { \
.type = IIO_ACCEL, \
.address = reg, \
.modified = 1, \
.channel2 = IIO_MOD_##axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
}
static const struct iio_chan_spec da311_channels[] = {
/* | 0x80 comes from the android driver */
DA311_CHANNEL(DA311_REG_OUT_X_L | 0x80, X),
DA311_CHANNEL(DA311_REG_OUT_Y_L | 0x80, Y),
DA311_CHANNEL(DA311_REG_OUT_Z_L | 0x80, Z),
};
struct da311_data {
struct i2c_client *client;
};
static int da311_register_mask_write(struct i2c_client *client, u16 addr,
u8 mask, u8 data)
{
int ret;
u8 tmp_data = 0;
if (addr & 0xff00) {
/* Select bank 1 */
ret = i2c_smbus_write_byte_data(client, DA311_REG_BANK, 0x01);
if (ret < 0)
return ret;
}
if (mask != 0xff) {
ret = i2c_smbus_read_byte_data(client, addr);
if (ret < 0)
return ret;
tmp_data = ret;
}
tmp_data &= ~mask;
tmp_data |= data & mask;
ret = i2c_smbus_write_byte_data(client, addr & 0xff, tmp_data);
if (ret < 0)
return ret;
if (addr & 0xff00) {
/* Back to bank 0 */
ret = i2c_smbus_write_byte_data(client, DA311_REG_BANK, 0x00);
if (ret < 0)
return ret;
}
return 0;
}
/* Init sequence taken from the android driver */
static int da311_reset(struct i2c_client *client)
{
const struct {
u16 addr;
u8 mask;
u8 data;
} init_data[] = {
{ DA311_REG_TEMP_CFG_REG, 0xff, 0x08 },
{ DA311_REG_CTRL_REG5, 0xff, 0x80 },
{ DA311_REG_CTRL_REG4, 0x30, 0x00 },
{ DA311_REG_CTRL_REG1, 0xff, 0x6f },
{ DA311_REG_TEMP_CFG_REG, 0xff, 0x88 },
{ DA311_REG_LDO_REG, 0xff, 0x02 },
{ DA311_REG_OTP_TRIM_OSC, 0xff, 0x27 },
{ DA311_REG_LPF_ABSOLUTE, 0xff, 0x30 },
{ DA311_REG_TEMP_OFF1, 0xff, 0x3f },
{ DA311_REG_TEMP_OFF2, 0xff, 0xff },
{ DA311_REG_TEMP_OFF3, 0xff, 0x0f },
};
int i, ret;
/* Reset */
ret = da311_register_mask_write(client, DA311_REG_SOFT_RESET,
0xff, 0xaa);
if (ret < 0)
return ret;
for (i = 0; i < ARRAY_SIZE(init_data); i++) {
ret = da311_register_mask_write(client,
init_data[i].addr,
init_data[i].mask,
init_data[i].data);
if (ret < 0)
return ret;
}
return 0;
}
static int da311_enable(struct i2c_client *client, bool enable)
{
u8 data = enable ? 0x00 : 0x20;
return da311_register_mask_write(client, DA311_REG_TEMP_CFG_REG,
0x20, data);
}
static int da311_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct da311_data *data = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = i2c_smbus_read_word_data(data->client, chan->address);
if (ret < 0)
return ret;
/*
* Values are 12 bits, stored as 16 bits with the 4
* least significant bits always 0.
*/
*val = (short)ret >> 4;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 0;
*val2 = da311_nscale;
return IIO_VAL_INT_PLUS_NANO;
default:
return -EINVAL;
}
}
static const struct iio_info da311_info = {
.driver_module = THIS_MODULE,
.read_raw = da311_read_raw,
};
static int da311_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret;
struct iio_dev *indio_dev;
struct da311_data *data;
ret = i2c_smbus_read_byte_data(client, DA311_REG_CHIP_ID);
if (ret != DA311_CHIP_ID)
return (ret < 0) ? ret : -ENODEV;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
data->client = client;
i2c_set_clientdata(client, indio_dev);
indio_dev->dev.parent = &client->dev;
indio_dev->info = &da311_info;
indio_dev->name = "da311";
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = da311_channels;
indio_dev->num_channels = ARRAY_SIZE(da311_channels);
ret = da311_reset(client);
if (ret < 0)
return ret;
ret = da311_enable(client, true);
if (ret < 0)
return ret;
ret = iio_device_register(indio_dev);
if (ret < 0) {
dev_err(&client->dev, "device_register failed\n");
da311_enable(client, false);
}
return ret;
}
static int da311_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
iio_device_unregister(indio_dev);
return da311_enable(client, false);
}
#ifdef CONFIG_PM_SLEEP
static int da311_suspend(struct device *dev)
{
return da311_enable(to_i2c_client(dev), false);
}
static int da311_resume(struct device *dev)
{
return da311_enable(to_i2c_client(dev), true);
}
#endif
static SIMPLE_DEV_PM_OPS(da311_pm_ops, da311_suspend, da311_resume);
static const struct i2c_device_id da311_i2c_id[] = {
{"da311", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, da311_i2c_id);
static struct i2c_driver da311_driver = {
.driver = {
.name = "da311",
.pm = &da311_pm_ops,
},
.probe = da311_probe,
.remove = da311_remove,
.id_table = da311_i2c_id,
};
module_i2c_driver(da311_driver);
MODULE_AUTHOR("Hans de Goede <[email protected]>");
MODULE_DESCRIPTION("MiraMEMS DA311 3-Axis Accelerometer driver");
MODULE_LICENSE("GPL v2");

266
drivers/iio/accel/dmard10.c Normal file
View file

@ -0,0 +1,266 @@
/**
* IIO driver for the 3-axis accelerometer Domintech ARD10.
*
* Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com>
* Copyright (c) 2012 Domintech Technology Co., Ltd
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/byteorder/generic.h>
#define DMARD10_REG_ACTR 0x00
#define DMARD10_REG_AFEM 0x0c
#define DMARD10_REG_STADR 0x12
#define DMARD10_REG_STAINT 0x1c
#define DMARD10_REG_MISC2 0x1f
#define DMARD10_REG_PD 0x21
#define DMARD10_MODE_OFF 0x00
#define DMARD10_MODE_STANDBY 0x02
#define DMARD10_MODE_ACTIVE 0x06
#define DMARD10_MODE_READ_OTP 0x12
#define DMARD10_MODE_RESET_DATA_PATH 0x82
/* AFEN set 1, ATM[2:0]=b'000 (normal), EN_Z/Y/X/T=1 */
#define DMARD10_VALUE_AFEM_AFEN_NORMAL 0x8f
/* ODR[3:0]=b'0111 (100Hz), CCK[3:0]=b'0100 (204.8kHZ) */
#define DMARD10_VALUE_CKSEL_ODR_100_204 0x74
/* INTC[6:5]=b'00 */
#define DMARD10_VALUE_INTC 0x00
/* TAP1/TAP2 Average 2 */
#define DMARD10_VALUE_TAPNS_AVE_2 0x11
#define DMARD10_VALUE_STADR 0x55
#define DMARD10_VALUE_STAINT 0xaa
#define DMARD10_VALUE_MISC2_OSCA_EN 0x08
#define DMARD10_VALUE_PD_RST 0x52
/* Offsets into the buffer read in dmard10_read_raw() */
#define DMARD10_X_OFFSET 1
#define DMARD10_Y_OFFSET 2
#define DMARD10_Z_OFFSET 3
/*
* a value of + or -128 corresponds to + or - 1G
* scale = 9.81 / 128 = 0.076640625
*/
static const int dmard10_nscale = 76640625;
#define DMARD10_CHANNEL(reg, axis) { \
.type = IIO_ACCEL, \
.address = reg, \
.modified = 1, \
.channel2 = IIO_MOD_##axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
}
static const struct iio_chan_spec dmard10_channels[] = {
DMARD10_CHANNEL(DMARD10_X_OFFSET, X),
DMARD10_CHANNEL(DMARD10_Y_OFFSET, Y),
DMARD10_CHANNEL(DMARD10_Z_OFFSET, Z),
};
struct dmard10_data {
struct i2c_client *client;
};
/* Init sequence taken from the android driver */
static int dmard10_reset(struct i2c_client *client)
{
unsigned char buffer[7];
int ret;
/* 1. Powerdown reset */
ret = i2c_smbus_write_byte_data(client, DMARD10_REG_PD,
DMARD10_VALUE_PD_RST);
if (ret < 0)
return ret;
/*
* 2. ACTR => Standby mode => Download OTP to parameter reg =>
* Standby mode => Reset data path => Standby mode
*/
buffer[0] = DMARD10_REG_ACTR;
buffer[1] = DMARD10_MODE_STANDBY;
buffer[2] = DMARD10_MODE_READ_OTP;
buffer[3] = DMARD10_MODE_STANDBY;
buffer[4] = DMARD10_MODE_RESET_DATA_PATH;
buffer[5] = DMARD10_MODE_STANDBY;
ret = i2c_master_send(client, buffer, 6);
if (ret < 0)
return ret;
/* 3. OSCA_EN = 1, TSTO = b'000 (INT1 = normal, TEST0 = normal) */
ret = i2c_smbus_write_byte_data(client, DMARD10_REG_MISC2,
DMARD10_VALUE_MISC2_OSCA_EN);
if (ret < 0)
return ret;
/* 4. AFEN = 1 (AFE will powerdown after ADC) */
buffer[0] = DMARD10_REG_AFEM;
buffer[1] = DMARD10_VALUE_AFEM_AFEN_NORMAL;
buffer[2] = DMARD10_VALUE_CKSEL_ODR_100_204;
buffer[3] = DMARD10_VALUE_INTC;
buffer[4] = DMARD10_VALUE_TAPNS_AVE_2;
buffer[5] = 0x00; /* DLYC, no delay timing */
buffer[6] = 0x07; /* INTD=1 push-pull, INTA=1 active high, AUTOT=1 */
ret = i2c_master_send(client, buffer, 7);
if (ret < 0)
return ret;
/* 5. Activation mode */
ret = i2c_smbus_write_byte_data(client, DMARD10_REG_ACTR,
DMARD10_MODE_ACTIVE);
if (ret < 0)
return ret;
return 0;
}
/* Shutdown sequence taken from the android driver */
static int dmard10_shutdown(struct i2c_client *client)
{
unsigned char buffer[3];
buffer[0] = DMARD10_REG_ACTR;
buffer[1] = DMARD10_MODE_STANDBY;
buffer[2] = DMARD10_MODE_OFF;
return i2c_master_send(client, buffer, 3);
}
static int dmard10_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct dmard10_data *data = iio_priv(indio_dev);
__le16 buf[4];
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
/*
* Read 8 bytes starting at the REG_STADR register, trying to
* read the individual X, Y, Z registers will always read 0.
*/
ret = i2c_smbus_read_i2c_block_data(data->client,
DMARD10_REG_STADR,
sizeof(buf), (u8 *)buf);
if (ret < 0)
return ret;
ret = le16_to_cpu(buf[chan->address]);
*val = sign_extend32(ret, 12);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 0;
*val2 = dmard10_nscale;
return IIO_VAL_INT_PLUS_NANO;
default:
return -EINVAL;
}
}
static const struct iio_info dmard10_info = {
.driver_module = THIS_MODULE,
.read_raw = dmard10_read_raw,
};
static int dmard10_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret;
struct iio_dev *indio_dev;
struct dmard10_data *data;
/* These 2 registers have special POR reset values used for id */
ret = i2c_smbus_read_byte_data(client, DMARD10_REG_STADR);
if (ret != DMARD10_VALUE_STADR)
return (ret < 0) ? ret : -ENODEV;
ret = i2c_smbus_read_byte_data(client, DMARD10_REG_STAINT);
if (ret != DMARD10_VALUE_STAINT)
return (ret < 0) ? ret : -ENODEV;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev) {
dev_err(&client->dev, "iio allocation failed!\n");
return -ENOMEM;
}
data = iio_priv(indio_dev);
data->client = client;
i2c_set_clientdata(client, indio_dev);
indio_dev->dev.parent = &client->dev;
indio_dev->info = &dmard10_info;
indio_dev->name = "dmard10";
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = dmard10_channels;
indio_dev->num_channels = ARRAY_SIZE(dmard10_channels);
ret = dmard10_reset(client);
if (ret < 0)
return ret;
ret = iio_device_register(indio_dev);
if (ret < 0) {
dev_err(&client->dev, "device_register failed\n");
dmard10_shutdown(client);
}
return ret;
}
static int dmard10_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
iio_device_unregister(indio_dev);
return dmard10_shutdown(client);
}
#ifdef CONFIG_PM_SLEEP
static int dmard10_suspend(struct device *dev)
{
return dmard10_shutdown(to_i2c_client(dev));
}
static int dmard10_resume(struct device *dev)
{
return dmard10_reset(to_i2c_client(dev));
}
#endif
static SIMPLE_DEV_PM_OPS(dmard10_pm_ops, dmard10_suspend, dmard10_resume);
static const struct i2c_device_id dmard10_i2c_id[] = {
{"dmard10", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, dmard10_i2c_id);
static struct i2c_driver dmard10_driver = {
.driver = {
.name = "dmard10",
.pm = &dmard10_pm_ops,
},
.probe = dmard10_probe,
.remove = dmard10_remove,
.id_table = dmard10_i2c_id,
};
module_i2c_driver(dmard10_driver);
MODULE_AUTHOR("Hans de Goede <[email protected]>");
MODULE_DESCRIPTION("Domintech ARD10 3-Axis Accelerometer driver");
MODULE_LICENSE("GPL v2");

View file

@ -39,7 +39,7 @@
#define MMA7660_SCALE_AVAIL "0.467142857"
const int mma7660_nscale = 467142857;
static const int mma7660_nscale = 467142857;
#define MMA7660_CHANNEL(reg, axis) { \
.type = IIO_ACCEL, \

View file

@ -459,12 +459,14 @@ static int mma8452_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (iio_buffer_enabled(indio_dev))
return -EBUSY;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
mutex_lock(&data->lock);
ret = mma8452_read(data, buffer);
mutex_unlock(&data->lock);
iio_device_release_direct_mode(indio_dev);
if (ret < 0)
return ret;
@ -664,37 +666,46 @@ static int mma8452_write_raw(struct iio_dev *indio_dev,
struct mma8452_data *data = iio_priv(indio_dev);
int i, ret;
if (iio_buffer_enabled(indio_dev))
return -EBUSY;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
i = mma8452_get_samp_freq_index(data, val, val2);
if (i < 0)
return i;
if (i < 0) {
ret = i;
break;
}
data->ctrl_reg1 &= ~MMA8452_CTRL_DR_MASK;
data->ctrl_reg1 |= i << MMA8452_CTRL_DR_SHIFT;
return mma8452_change_config(data, MMA8452_CTRL_REG1,
data->ctrl_reg1);
ret = mma8452_change_config(data, MMA8452_CTRL_REG1,
data->ctrl_reg1);
break;
case IIO_CHAN_INFO_SCALE:
i = mma8452_get_scale_index(data, val, val2);
if (i < 0)
return i;
if (i < 0) {
ret = i;
break;
}
data->data_cfg &= ~MMA8452_DATA_CFG_FS_MASK;
data->data_cfg |= i;
return mma8452_change_config(data, MMA8452_DATA_CFG,
data->data_cfg);
ret = mma8452_change_config(data, MMA8452_DATA_CFG,
data->data_cfg);
break;
case IIO_CHAN_INFO_CALIBBIAS:
if (val < -128 || val > 127)
return -EINVAL;
if (val < -128 || val > 127) {
ret = -EINVAL;
break;
}
return mma8452_change_config(data,
MMA8452_OFF_X + chan->scan_index,
val);
ret = mma8452_change_config(data,
MMA8452_OFF_X + chan->scan_index,
val);
break;
case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
if (val == 0 && val2 == 0) {
@ -703,23 +714,30 @@ static int mma8452_write_raw(struct iio_dev *indio_dev,
data->data_cfg |= MMA8452_DATA_CFG_HPF_MASK;
ret = mma8452_set_hp_filter_frequency(data, val, val2);
if (ret < 0)
return ret;
break;
}
return mma8452_change_config(data, MMA8452_DATA_CFG,
ret = mma8452_change_config(data, MMA8452_DATA_CFG,
data->data_cfg);
break;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
ret = mma8452_get_odr_index(data);
for (i = 0; i < ARRAY_SIZE(mma8452_os_ratio); i++) {
if (mma8452_os_ratio[i][ret] == val)
return mma8452_set_power_mode(data, i);
if (mma8452_os_ratio[i][ret] == val) {
ret = mma8452_set_power_mode(data, i);
break;
}
}
break;
default:
return -EINVAL;
ret = -EINVAL;
break;
}
iio_device_release_direct_mode(indio_dev);
return ret;
}
static int mma8452_read_thresh(struct iio_dev *indio_dev,
@ -1347,20 +1365,9 @@ static int mma8452_data_rdy_trigger_set_state(struct iio_trigger *trig,
return mma8452_change_config(data, MMA8452_CTRL_REG4, reg);
}
static int mma8452_validate_device(struct iio_trigger *trig,
struct iio_dev *indio_dev)
{
struct iio_dev *indio = iio_trigger_get_drvdata(trig);
if (indio != indio_dev)
return -EINVAL;
return 0;
}
static const struct iio_trigger_ops mma8452_trigger_ops = {
.set_trigger_state = mma8452_data_rdy_trigger_set_state,
.validate_device = mma8452_validate_device,
.validate_device = iio_trigger_validate_own_device,
.owner = THIS_MODULE,
};

1576
drivers/iio/accel/sca3000.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -58,6 +58,18 @@ config AD7476
To compile this driver as a module, choose M here: the
module will be called ad7476.
config AD7766
tristate "Analog Devices AD7766/AD7767 ADC driver"
depends on SPI_MASTER
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for Analog Devices AD7766, AD7766-1,
AD7766-2, AD7767, AD7767-1, AD7767-2 SPI analog to digital converters.
To compile this driver as a module, choose M here: the module will be
called ad7766.
config AD7791
tristate "Analog Devices AD7791 ADC driver"
depends on SPI
@ -447,6 +459,8 @@ config TI_ADC081C
config TI_ADC0832
tristate "Texas Instruments ADC0831/ADC0832/ADC0834/ADC0838"
depends on SPI
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
If you say yes here you get support for Texas Instruments ADC0831,
ADC0832, ADC0834, ADC0838 ADC chips.

View file

@ -9,6 +9,7 @@ obj-$(CONFIG_AD7291) += ad7291.o
obj-$(CONFIG_AD7298) += ad7298.o
obj-$(CONFIG_AD7923) += ad7923.o
obj-$(CONFIG_AD7476) += ad7476.o
obj-$(CONFIG_AD7766) += ad7766.o
obj-$(CONFIG_AD7791) += ad7791.o
obj-$(CONFIG_AD7793) += ad7793.o
obj-$(CONFIG_AD7887) += ad7887.o

330
drivers/iio/adc/ad7766.c Normal file
View file

@ -0,0 +1,330 @@
/*
* AD7766/AD7767 SPI ADC driver
*
* Copyright 2016 Analog Devices Inc.
*
* Licensed under the GPL-2 or later.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
struct ad7766_chip_info {
unsigned int decimation_factor;
};
enum {
AD7766_SUPPLY_AVDD = 0,
AD7766_SUPPLY_DVDD = 1,
AD7766_SUPPLY_VREF = 2,
AD7766_NUM_SUPPLIES = 3
};
struct ad7766 {
const struct ad7766_chip_info *chip_info;
struct spi_device *spi;
struct clk *mclk;
struct gpio_desc *pd_gpio;
struct regulator_bulk_data reg[AD7766_NUM_SUPPLIES];
struct iio_trigger *trig;
struct spi_transfer xfer;
struct spi_message msg;
/*
* DMA (thus cache coherency maintenance) requires the
* transfer buffers to live in their own cache lines.
* Make the buffer large enough for one 24 bit sample and one 64 bit
* aligned 64 bit timestamp.
*/
unsigned char data[ALIGN(3, sizeof(s64)) + sizeof(s64)]
____cacheline_aligned;
};
/*
* AD7766 and AD7767 variations are interface compatible, the main difference is
* analog performance. Both parts will use the same ID.
*/
enum ad7766_device_ids {
ID_AD7766,
ID_AD7766_1,
ID_AD7766_2,
};
static irqreturn_t ad7766_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct ad7766 *ad7766 = iio_priv(indio_dev);
int ret;
ret = spi_sync(ad7766->spi, &ad7766->msg);
if (ret < 0)
goto done;
iio_push_to_buffers_with_timestamp(indio_dev, ad7766->data,
pf->timestamp);
done:
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
static int ad7766_preenable(struct iio_dev *indio_dev)
{
struct ad7766 *ad7766 = iio_priv(indio_dev);
int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(ad7766->reg), ad7766->reg);
if (ret < 0) {
dev_err(&ad7766->spi->dev, "Failed to enable supplies: %d\n",
ret);
return ret;
}
ret = clk_prepare_enable(ad7766->mclk);
if (ret < 0) {
dev_err(&ad7766->spi->dev, "Failed to enable MCLK: %d\n", ret);
regulator_bulk_disable(ARRAY_SIZE(ad7766->reg), ad7766->reg);
return ret;
}
if (ad7766->pd_gpio)
gpiod_set_value(ad7766->pd_gpio, 0);
return 0;
}
static int ad7766_postdisable(struct iio_dev *indio_dev)
{
struct ad7766 *ad7766 = iio_priv(indio_dev);
if (ad7766->pd_gpio)
gpiod_set_value(ad7766->pd_gpio, 1);
/*
* The PD pin is synchronous to the clock, so give it some time to
* notice the change before we disable the clock.
*/
msleep(20);
clk_disable_unprepare(ad7766->mclk);
regulator_bulk_disable(ARRAY_SIZE(ad7766->reg), ad7766->reg);
return 0;
}
static int ad7766_read_raw(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, int *val, int *val2, long info)
{
struct ad7766 *ad7766 = iio_priv(indio_dev);
struct regulator *vref = ad7766->reg[AD7766_SUPPLY_VREF].consumer;
int scale_uv;
switch (info) {
case IIO_CHAN_INFO_SCALE:
scale_uv = regulator_get_voltage(vref);
if (scale_uv < 0)
return scale_uv;
*val = scale_uv / 1000;
*val2 = chan->scan_type.realbits;
return IIO_VAL_FRACTIONAL_LOG2;
case IIO_CHAN_INFO_SAMP_FREQ:
*val = clk_get_rate(ad7766->mclk) /
ad7766->chip_info->decimation_factor;
return IIO_VAL_INT;
}
return -EINVAL;
}
static const struct iio_chan_spec ad7766_channels[] = {
{
.type = IIO_VOLTAGE,
.indexed = 1,
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
.scan_type = {
.sign = 's',
.realbits = 24,
.storagebits = 32,
.endianness = IIO_BE,
},
},
IIO_CHAN_SOFT_TIMESTAMP(1),
};
static const struct ad7766_chip_info ad7766_chip_info[] = {
[ID_AD7766] = {
.decimation_factor = 8,
},
[ID_AD7766_1] = {
.decimation_factor = 16,
},
[ID_AD7766_2] = {
.decimation_factor = 32,
},
};
static const struct iio_buffer_setup_ops ad7766_buffer_setup_ops = {
.preenable = &ad7766_preenable,
.postenable = &iio_triggered_buffer_postenable,
.predisable = &iio_triggered_buffer_predisable,
.postdisable = &ad7766_postdisable,
};
static const struct iio_info ad7766_info = {
.driver_module = THIS_MODULE,
.read_raw = &ad7766_read_raw,
};
static irqreturn_t ad7766_irq(int irq, void *private)
{
iio_trigger_poll(private);
return IRQ_HANDLED;
}
static int ad7766_set_trigger_state(struct iio_trigger *trig, bool enable)
{
struct ad7766 *ad7766 = iio_trigger_get_drvdata(trig);
if (enable)
enable_irq(ad7766->spi->irq);
else
disable_irq(ad7766->spi->irq);
return 0;
}
static const struct iio_trigger_ops ad7766_trigger_ops = {
.owner = THIS_MODULE,
.set_trigger_state = ad7766_set_trigger_state,
.validate_device = iio_trigger_validate_own_device,
};
static int ad7766_probe(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);
struct iio_dev *indio_dev;
struct ad7766 *ad7766;
int ret;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*ad7766));
if (!indio_dev)
return -ENOMEM;
ad7766 = iio_priv(indio_dev);
ad7766->chip_info = &ad7766_chip_info[id->driver_data];
ad7766->mclk = devm_clk_get(&spi->dev, "mclk");
if (IS_ERR(ad7766->mclk))
return PTR_ERR(ad7766->mclk);
ad7766->reg[AD7766_SUPPLY_AVDD].supply = "avdd";
ad7766->reg[AD7766_SUPPLY_DVDD].supply = "dvdd";
ad7766->reg[AD7766_SUPPLY_VREF].supply = "vref";
ret = devm_regulator_bulk_get(&spi->dev, ARRAY_SIZE(ad7766->reg),
ad7766->reg);
if (IS_ERR(ad7766->reg))
return PTR_ERR(ad7766->reg);
ad7766->pd_gpio = devm_gpiod_get_optional(&spi->dev, "powerdown",
GPIOD_OUT_HIGH);
if (IS_ERR(ad7766->pd_gpio))
return PTR_ERR(ad7766->pd_gpio);
indio_dev->dev.parent = &spi->dev;
indio_dev->name = spi_get_device_id(spi)->name;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = ad7766_channels;
indio_dev->num_channels = ARRAY_SIZE(ad7766_channels);
indio_dev->info = &ad7766_info;
if (spi->irq > 0) {
ad7766->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d",
indio_dev->name, indio_dev->id);
if (!ad7766->trig)
return -ENOMEM;
ad7766->trig->ops = &ad7766_trigger_ops;
ad7766->trig->dev.parent = &spi->dev;
iio_trigger_set_drvdata(ad7766->trig, ad7766);
ret = devm_request_irq(&spi->dev, spi->irq, ad7766_irq,
IRQF_TRIGGER_FALLING, dev_name(&spi->dev),
ad7766->trig);
if (ret < 0)
return ret;
/*
* The device generates interrupts as long as it is powered up.
* Some platforms might not allow the option to power it down so
* disable the interrupt to avoid extra load on the system
*/
disable_irq(spi->irq);
ret = devm_iio_trigger_register(&spi->dev, ad7766->trig);
if (ret)
return ret;
}
spi_set_drvdata(spi, indio_dev);
ad7766->spi = spi;
/* First byte always 0 */
ad7766->xfer.rx_buf = &ad7766->data[1];
ad7766->xfer.len = 3;
spi_message_init(&ad7766->msg);
spi_message_add_tail(&ad7766->xfer, &ad7766->msg);
ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
&iio_pollfunc_store_time, &ad7766_trigger_handler,
&ad7766_buffer_setup_ops);
if (ret)
return ret;
ret = devm_iio_device_register(&spi->dev, indio_dev);
if (ret)
return ret;
return 0;
}
static const struct spi_device_id ad7766_id[] = {
{"ad7766", ID_AD7766},
{"ad7766-1", ID_AD7766_1},
{"ad7766-2", ID_AD7766_2},
{"ad7767", ID_AD7766},
{"ad7767-1", ID_AD7766_1},
{"ad7767-2", ID_AD7766_2},
{}
};
MODULE_DEVICE_TABLE(spi, ad7766_id);
static struct spi_driver ad7766_driver = {
.driver = {
.name = "ad7766",
},
.probe = ad7766_probe,
.id_table = ad7766_id,
};
module_spi_driver(ad7766_driver);
MODULE_AUTHOR("Lars-Peter Clausen <[email protected]>");
MODULE_DESCRIPTION("Analog Devices AD7766 and AD7767 ADCs driver support");
MODULE_LICENSE("GPL v2");

View file

@ -238,7 +238,9 @@ static int max1027_read_single_value(struct iio_dev *indio_dev,
/* Configure conversion register with the requested chan */
st->reg = MAX1027_CONV_REG | MAX1027_CHAN(chan->channel) |
MAX1027_NOSCAN | !!(chan->type == IIO_TEMP);
MAX1027_NOSCAN;
if (chan->type == IIO_TEMP)
st->reg |= MAX1027_TEMP;
ret = spi_write(st->spi, &st->reg, 1);
if (ret < 0) {
dev_err(&indio_dev->dev,
@ -360,17 +362,6 @@ static int max1027_set_trigger_state(struct iio_trigger *trig, bool state)
return 0;
}
static int max1027_validate_device(struct iio_trigger *trig,
struct iio_dev *indio_dev)
{
struct iio_dev *indio = iio_trigger_get_drvdata(trig);
if (indio != indio_dev)
return -EINVAL;
return 0;
}
static irqreturn_t max1027_trigger_handler(int irq, void *private)
{
struct iio_poll_func *pf = (struct iio_poll_func *)private;
@ -391,7 +382,7 @@ static irqreturn_t max1027_trigger_handler(int irq, void *private)
static const struct iio_trigger_ops max1027_trigger_ops = {
.owner = THIS_MODULE,
.validate_device = &max1027_validate_device,
.validate_device = &iio_trigger_validate_own_device,
.set_trigger_state = &max1027_set_trigger_state,
};

View file

@ -14,6 +14,10 @@
#include <linux/spi/spi.h>
#include <linux/iio/iio.h>
#include <linux/regulator/consumer.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
enum {
adc0831,
@ -38,10 +42,16 @@ struct adc0832 {
.indexed = 1, \
.channel = chan, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
.scan_index = chan, \
.scan_type = { \
.sign = 'u', \
.realbits = 8, \
.storagebits = 8, \
}, \
}
#define ADC0832_VOLTAGE_CHANNEL_DIFF(chan1, chan2) \
#define ADC0832_VOLTAGE_CHANNEL_DIFF(chan1, chan2, si) \
{ \
.type = IIO_VOLTAGE, \
.indexed = 1, \
@ -49,18 +59,26 @@ struct adc0832 {
.channel2 = (chan2), \
.differential = 1, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
.scan_index = si, \
.scan_type = { \
.sign = 'u', \
.realbits = 8, \
.storagebits = 8, \
}, \
}
static const struct iio_chan_spec adc0831_channels[] = {
ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1),
ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1, 0),
IIO_CHAN_SOFT_TIMESTAMP(1),
};
static const struct iio_chan_spec adc0832_channels[] = {
ADC0832_VOLTAGE_CHANNEL(0),
ADC0832_VOLTAGE_CHANNEL(1),
ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1),
ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0),
ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1, 2),
ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0, 3),
IIO_CHAN_SOFT_TIMESTAMP(4),
};
static const struct iio_chan_spec adc0834_channels[] = {
@ -68,10 +86,11 @@ static const struct iio_chan_spec adc0834_channels[] = {
ADC0832_VOLTAGE_CHANNEL(1),
ADC0832_VOLTAGE_CHANNEL(2),
ADC0832_VOLTAGE_CHANNEL(3),
ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1),
ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0),
ADC0832_VOLTAGE_CHANNEL_DIFF(2, 3),
ADC0832_VOLTAGE_CHANNEL_DIFF(3, 2),
ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1, 4),
ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0, 5),
ADC0832_VOLTAGE_CHANNEL_DIFF(2, 3, 6),
ADC0832_VOLTAGE_CHANNEL_DIFF(3, 2, 7),
IIO_CHAN_SOFT_TIMESTAMP(8),
};
static const struct iio_chan_spec adc0838_channels[] = {
@ -83,14 +102,15 @@ static const struct iio_chan_spec adc0838_channels[] = {
ADC0832_VOLTAGE_CHANNEL(5),
ADC0832_VOLTAGE_CHANNEL(6),
ADC0832_VOLTAGE_CHANNEL(7),
ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1),
ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0),
ADC0832_VOLTAGE_CHANNEL_DIFF(2, 3),
ADC0832_VOLTAGE_CHANNEL_DIFF(3, 2),
ADC0832_VOLTAGE_CHANNEL_DIFF(4, 5),
ADC0832_VOLTAGE_CHANNEL_DIFF(5, 4),
ADC0832_VOLTAGE_CHANNEL_DIFF(6, 7),
ADC0832_VOLTAGE_CHANNEL_DIFF(7, 6),
ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1, 8),
ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0, 9),
ADC0832_VOLTAGE_CHANNEL_DIFF(2, 3, 10),
ADC0832_VOLTAGE_CHANNEL_DIFF(3, 2, 11),
ADC0832_VOLTAGE_CHANNEL_DIFF(4, 5, 12),
ADC0832_VOLTAGE_CHANNEL_DIFF(5, 4, 13),
ADC0832_VOLTAGE_CHANNEL_DIFF(6, 7, 14),
ADC0832_VOLTAGE_CHANNEL_DIFF(7, 6, 15),
IIO_CHAN_SOFT_TIMESTAMP(16),
};
static int adc0831_adc_conversion(struct adc0832 *adc)
@ -178,6 +198,42 @@ static const struct iio_info adc0832_info = {
.driver_module = THIS_MODULE,
};
static irqreturn_t adc0832_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct adc0832 *adc = iio_priv(indio_dev);
u8 data[24] = { }; /* 16x 1 byte ADC data + 8 bytes timestamp */
int scan_index;
int i = 0;
mutex_lock(&adc->lock);
for_each_set_bit(scan_index, indio_dev->active_scan_mask,
indio_dev->masklength) {
const struct iio_chan_spec *scan_chan =
&indio_dev->channels[scan_index];
int ret = adc0832_adc_conversion(adc, scan_chan->channel,
scan_chan->differential);
if (ret < 0) {
dev_warn(&adc->spi->dev,
"failed to get conversion data\n");
goto out;
}
data[i] = ret;
i++;
}
iio_push_to_buffers_with_timestamp(indio_dev, data,
iio_get_time_ns(indio_dev));
out:
mutex_unlock(&adc->lock);
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
static int adc0832_probe(struct spi_device *spi)
{
struct iio_dev *indio_dev;
@ -233,9 +289,20 @@ static int adc0832_probe(struct spi_device *spi)
spi_set_drvdata(spi, indio_dev);
ret = iio_triggered_buffer_setup(indio_dev, NULL,
adc0832_trigger_handler, NULL);
if (ret)
goto err_reg_disable;
ret = iio_device_register(indio_dev);
if (ret)
regulator_disable(adc->reg);
goto err_buffer_cleanup;
return 0;
err_buffer_cleanup:
iio_triggered_buffer_cleanup(indio_dev);
err_reg_disable:
regulator_disable(adc->reg);
return ret;
}
@ -246,6 +313,7 @@ static int adc0832_remove(struct spi_device *spi)
struct adc0832 *adc = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
regulator_disable(adc->reg);
return 0;

View file

@ -27,6 +27,7 @@
#include <linux/iio/buffer.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/regulator/consumer.h>
#define TI_ADC_DRV_NAME "ti-adc161s626"
@ -39,7 +40,9 @@ static const struct iio_chan_spec ti_adc141s626_channels[] = {
{
.type = IIO_VOLTAGE,
.channel = 0,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OFFSET),
.scan_index = 0,
.scan_type = {
.sign = 's',
@ -54,7 +57,9 @@ static const struct iio_chan_spec ti_adc161s626_channels[] = {
{
.type = IIO_VOLTAGE,
.channel = 0,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OFFSET),
.scan_index = 0,
.scan_type = {
.sign = 's',
@ -68,6 +73,8 @@ static const struct iio_chan_spec ti_adc161s626_channels[] = {
struct ti_adc_data {
struct iio_dev *indio_dev;
struct spi_device *spi;
struct regulator *ref;
u8 read_size;
u8 shift;
@ -135,18 +142,32 @@ static int ti_adc_read_raw(struct iio_dev *indio_dev,
struct ti_adc_data *data = iio_priv(indio_dev);
int ret;
if (mask != IIO_CHAN_INFO_RAW)
return -EINVAL;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
ret = ti_adc_read_measurement(data, chan, val);
iio_device_release_direct_mode(indio_dev);
ret = ti_adc_read_measurement(data, chan, val);
iio_device_release_direct_mode(indio_dev);
if (ret)
return ret;
if (!ret)
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
ret = regulator_get_voltage(data->ref);
if (ret < 0)
return ret;
*val = ret / 1000;
*val2 = chan->scan_type.realbits;
return IIO_VAL_FRACTIONAL_LOG2;
case IIO_CHAN_INFO_OFFSET:
*val = 1 << (chan->scan_type.realbits - 1);
return IIO_VAL_INT;
}
return 0;
}
@ -191,10 +212,17 @@ static int ti_adc_probe(struct spi_device *spi)
break;
}
data->ref = devm_regulator_get(&spi->dev, "vdda");
if (!IS_ERR(data->ref)) {
ret = regulator_enable(data->ref);
if (ret < 0)
return ret;
}
ret = iio_triggered_buffer_setup(indio_dev, NULL,
ti_adc_trigger_handler, NULL);
if (ret)
return ret;
goto error_regulator_disable;
ret = iio_device_register(indio_dev);
if (ret)
@ -205,15 +233,20 @@ static int ti_adc_probe(struct spi_device *spi)
error_unreg_buffer:
iio_triggered_buffer_cleanup(indio_dev);
error_regulator_disable:
regulator_disable(data->ref);
return ret;
}
static int ti_adc_remove(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct ti_adc_data *data = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
regulator_disable(data->ref);
return 0;
}

View file

@ -0,0 +1,593 @@
/*
* IIO driver for the ACCES 104-QUAD-8
* Copyright (C) 2016 William Breathitt Gray
*
* 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.
*
* 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.
*
* This driver supports the ACCES 104-QUAD-8 and ACCES 104-QUAD-4.
*/
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/iio/iio.h>
#include <linux/iio/types.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/isa.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#define QUAD8_EXTENT 32
static unsigned int base[max_num_isa_dev(QUAD8_EXTENT)];
static unsigned int num_quad8;
module_param_array(base, uint, &num_quad8, 0);
MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
#define QUAD8_NUM_COUNTERS 8
/**
* struct quad8_iio - IIO device private data structure
* @preset: array of preset values
* @count_mode: array of count mode configurations
* @quadrature_mode: array of quadrature mode configurations
* @quadrature_scale: array of quadrature mode scale configurations
* @ab_enable: array of A and B inputs enable configurations
* @preset_enable: array of set_to_preset_on_index attribute configurations
* @synchronous_mode: array of index function synchronous mode configurations
* @index_polarity: array of index function polarity configurations
* @base: base port address of the IIO device
*/
struct quad8_iio {
unsigned int preset[QUAD8_NUM_COUNTERS];
unsigned int count_mode[QUAD8_NUM_COUNTERS];
unsigned int quadrature_mode[QUAD8_NUM_COUNTERS];
unsigned int quadrature_scale[QUAD8_NUM_COUNTERS];
unsigned int ab_enable[QUAD8_NUM_COUNTERS];
unsigned int preset_enable[QUAD8_NUM_COUNTERS];
unsigned int synchronous_mode[QUAD8_NUM_COUNTERS];
unsigned int index_polarity[QUAD8_NUM_COUNTERS];
unsigned int base;
};
static int quad8_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val, int *val2, long mask)
{
struct quad8_iio *const priv = iio_priv(indio_dev);
const int base_offset = priv->base + 2 * chan->channel;
unsigned int flags;
unsigned int borrow;
unsigned int carry;
int i;
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (chan->type == IIO_INDEX) {
*val = !!(inb(priv->base + 0x16) & BIT(chan->channel));
return IIO_VAL_INT;
}
flags = inb(base_offset);
borrow = flags & BIT(0);
carry = !!(flags & BIT(1));
/* Borrow XOR Carry effectively doubles count range */
*val = (borrow ^ carry) << 24;
/* Reset Byte Pointer; transfer Counter to Output Latch */
outb(0x11, base_offset + 1);
for (i = 0; i < 3; i++)
*val |= (unsigned int)inb(base_offset) << (8 * i);
return IIO_VAL_INT;
case IIO_CHAN_INFO_ENABLE:
*val = priv->ab_enable[chan->channel];
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 1;
*val2 = priv->quadrature_scale[chan->channel];
return IIO_VAL_FRACTIONAL_LOG2;
}
return -EINVAL;
}
static int quad8_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val, int val2, long mask)
{
struct quad8_iio *const priv = iio_priv(indio_dev);
const int base_offset = priv->base + 2 * chan->channel;
int i;
unsigned int ior_cfg;
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (chan->type == IIO_INDEX)
return -EINVAL;
/* Only 24-bit values are supported */
if ((unsigned int)val > 0xFFFFFF)
return -EINVAL;
/* Reset Byte Pointer */
outb(0x01, base_offset + 1);
/* Counter can only be set via Preset Register */
for (i = 0; i < 3; i++)
outb(val >> (8 * i), base_offset);
/* Transfer Preset Register to Counter */
outb(0x08, base_offset + 1);
/* Reset Byte Pointer */
outb(0x01, base_offset + 1);
/* Set Preset Register back to original value */
val = priv->preset[chan->channel];
for (i = 0; i < 3; i++)
outb(val >> (8 * i), base_offset);
/* Reset Borrow, Carry, Compare, and Sign flags */
outb(0x02, base_offset + 1);
/* Reset Error flag */
outb(0x06, base_offset + 1);
return 0;
case IIO_CHAN_INFO_ENABLE:
/* only boolean values accepted */
if (val < 0 || val > 1)
return -EINVAL;
priv->ab_enable[chan->channel] = val;
ior_cfg = val | priv->preset_enable[chan->channel] << 1;
/* Load I/O control configuration */
outb(0x40 | ior_cfg, base_offset);
return 0;
case IIO_CHAN_INFO_SCALE:
/* Quadrature scaling only available in quadrature mode */
if (!priv->quadrature_mode[chan->channel] && (val2 || val != 1))
return -EINVAL;
/* Only three gain states (1, 0.5, 0.25) */
if (val == 1 && !val2)
priv->quadrature_scale[chan->channel] = 0;
else if (!val)
switch (val2) {
case 500000:
priv->quadrature_scale[chan->channel] = 1;
break;
case 250000:
priv->quadrature_scale[chan->channel] = 2;
break;
default:
return -EINVAL;
}
else
return -EINVAL;
return 0;
}
return -EINVAL;
}
static const struct iio_info quad8_info = {
.driver_module = THIS_MODULE,
.read_raw = quad8_read_raw,
.write_raw = quad8_write_raw
};
static ssize_t quad8_read_preset(struct iio_dev *indio_dev, uintptr_t private,
const struct iio_chan_spec *chan, char *buf)
{
const struct quad8_iio *const priv = iio_priv(indio_dev);
return snprintf(buf, PAGE_SIZE, "%u\n", priv->preset[chan->channel]);
}
static ssize_t quad8_write_preset(struct iio_dev *indio_dev, uintptr_t private,
const struct iio_chan_spec *chan, const char *buf, size_t len)
{
struct quad8_iio *const priv = iio_priv(indio_dev);
const int base_offset = priv->base + 2 * chan->channel;
unsigned int preset;
int ret;
int i;
ret = kstrtouint(buf, 0, &preset);
if (ret)
return ret;
/* Only 24-bit values are supported */
if (preset > 0xFFFFFF)
return -EINVAL;
priv->preset[chan->channel] = preset;
/* Reset Byte Pointer */
outb(0x01, base_offset + 1);
/* Set Preset Register */
for (i = 0; i < 3; i++)
outb(preset >> (8 * i), base_offset);
return len;
}
static ssize_t quad8_read_set_to_preset_on_index(struct iio_dev *indio_dev,
uintptr_t private, const struct iio_chan_spec *chan, char *buf)
{
const struct quad8_iio *const priv = iio_priv(indio_dev);
return snprintf(buf, PAGE_SIZE, "%u\n",
priv->preset_enable[chan->channel]);
}
static ssize_t quad8_write_set_to_preset_on_index(struct iio_dev *indio_dev,
uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
size_t len)
{
struct quad8_iio *const priv = iio_priv(indio_dev);
const int base_offset = priv->base + 2 * chan->channel;
bool preset_enable;
int ret;
unsigned int ior_cfg;
ret = kstrtobool(buf, &preset_enable);
if (ret)
return ret;
priv->preset_enable[chan->channel] = preset_enable;
ior_cfg = priv->ab_enable[chan->channel] |
(unsigned int)preset_enable << 1;
/* Load I/O control configuration to Input / Output Control Register */
outb(0x40 | ior_cfg, base_offset);
return len;
}
static const char *const quad8_noise_error_states[] = {
"No excessive noise is present at the count inputs",
"Excessive noise is present at the count inputs"
};
static int quad8_get_noise_error(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
struct quad8_iio *const priv = iio_priv(indio_dev);
const int base_offset = priv->base + 2 * chan->channel + 1;
return !!(inb(base_offset) & BIT(4));
}
static const struct iio_enum quad8_noise_error_enum = {
.items = quad8_noise_error_states,
.num_items = ARRAY_SIZE(quad8_noise_error_states),
.get = quad8_get_noise_error
};
static const char *const quad8_count_direction_states[] = {
"down",
"up"
};
static int quad8_get_count_direction(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
struct quad8_iio *const priv = iio_priv(indio_dev);
const int base_offset = priv->base + 2 * chan->channel + 1;
return !!(inb(base_offset) & BIT(5));
}
static const struct iio_enum quad8_count_direction_enum = {
.items = quad8_count_direction_states,
.num_items = ARRAY_SIZE(quad8_count_direction_states),
.get = quad8_get_count_direction
};
static const char *const quad8_count_modes[] = {
"normal",
"range limit",
"non-recycle",
"modulo-n"
};
static int quad8_set_count_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, unsigned int count_mode)
{
struct quad8_iio *const priv = iio_priv(indio_dev);
unsigned int mode_cfg = count_mode << 1;
const int base_offset = priv->base + 2 * chan->channel + 1;
priv->count_mode[chan->channel] = count_mode;
/* Add quadrature mode configuration */
if (priv->quadrature_mode[chan->channel])
mode_cfg |= (priv->quadrature_scale[chan->channel] + 1) << 3;
/* Load mode configuration to Counter Mode Register */
outb(0x20 | mode_cfg, base_offset);
return 0;
}
static int quad8_get_count_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
const struct quad8_iio *const priv = iio_priv(indio_dev);
return priv->count_mode[chan->channel];
}
static const struct iio_enum quad8_count_mode_enum = {
.items = quad8_count_modes,
.num_items = ARRAY_SIZE(quad8_count_modes),
.set = quad8_set_count_mode,
.get = quad8_get_count_mode
};
static const char *const quad8_synchronous_modes[] = {
"non-synchronous",
"synchronous"
};
static int quad8_set_synchronous_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, unsigned int synchronous_mode)
{
struct quad8_iio *const priv = iio_priv(indio_dev);
const unsigned int idr_cfg = synchronous_mode |
priv->index_polarity[chan->channel] << 1;
const int base_offset = priv->base + 2 * chan->channel + 1;
/* Index function must be non-synchronous in non-quadrature mode */
if (synchronous_mode && !priv->quadrature_mode[chan->channel])
return -EINVAL;
priv->synchronous_mode[chan->channel] = synchronous_mode;
/* Load Index Control configuration to Index Control Register */
outb(0x40 | idr_cfg, base_offset);
return 0;
}
static int quad8_get_synchronous_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
const struct quad8_iio *const priv = iio_priv(indio_dev);
return priv->synchronous_mode[chan->channel];
}
static const struct iio_enum quad8_synchronous_mode_enum = {
.items = quad8_synchronous_modes,
.num_items = ARRAY_SIZE(quad8_synchronous_modes),
.set = quad8_set_synchronous_mode,
.get = quad8_get_synchronous_mode
};
static const char *const quad8_quadrature_modes[] = {
"non-quadrature",
"quadrature"
};
static int quad8_set_quadrature_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, unsigned int quadrature_mode)
{
struct quad8_iio *const priv = iio_priv(indio_dev);
unsigned int mode_cfg = priv->count_mode[chan->channel] << 1;
const int base_offset = priv->base + 2 * chan->channel + 1;
if (quadrature_mode)
mode_cfg |= (priv->quadrature_scale[chan->channel] + 1) << 3;
else {
/* Quadrature scaling only available in quadrature mode */
priv->quadrature_scale[chan->channel] = 0;
/* Synchronous function not supported in non-quadrature mode */
if (priv->synchronous_mode[chan->channel])
quad8_set_synchronous_mode(indio_dev, chan, 0);
}
priv->quadrature_mode[chan->channel] = quadrature_mode;
/* Load mode configuration to Counter Mode Register */
outb(0x20 | mode_cfg, base_offset);
return 0;
}
static int quad8_get_quadrature_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
const struct quad8_iio *const priv = iio_priv(indio_dev);
return priv->quadrature_mode[chan->channel];
}
static const struct iio_enum quad8_quadrature_mode_enum = {
.items = quad8_quadrature_modes,
.num_items = ARRAY_SIZE(quad8_quadrature_modes),
.set = quad8_set_quadrature_mode,
.get = quad8_get_quadrature_mode
};
static const char *const quad8_index_polarity_modes[] = {
"negative",
"positive"
};
static int quad8_set_index_polarity(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, unsigned int index_polarity)
{
struct quad8_iio *const priv = iio_priv(indio_dev);
const unsigned int idr_cfg = priv->synchronous_mode[chan->channel] |
index_polarity << 1;
const int base_offset = priv->base + 2 * chan->channel + 1;
priv->index_polarity[chan->channel] = index_polarity;
/* Load Index Control configuration to Index Control Register */
outb(0x40 | idr_cfg, base_offset);
return 0;
}
static int quad8_get_index_polarity(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
const struct quad8_iio *const priv = iio_priv(indio_dev);
return priv->index_polarity[chan->channel];
}
static const struct iio_enum quad8_index_polarity_enum = {
.items = quad8_index_polarity_modes,
.num_items = ARRAY_SIZE(quad8_index_polarity_modes),
.set = quad8_set_index_polarity,
.get = quad8_get_index_polarity
};
static const struct iio_chan_spec_ext_info quad8_count_ext_info[] = {
{
.name = "preset",
.shared = IIO_SEPARATE,
.read = quad8_read_preset,
.write = quad8_write_preset
},
{
.name = "set_to_preset_on_index",
.shared = IIO_SEPARATE,
.read = quad8_read_set_to_preset_on_index,
.write = quad8_write_set_to_preset_on_index
},
IIO_ENUM("noise_error", IIO_SEPARATE, &quad8_noise_error_enum),
IIO_ENUM_AVAILABLE("noise_error", &quad8_noise_error_enum),
IIO_ENUM("count_direction", IIO_SEPARATE, &quad8_count_direction_enum),
IIO_ENUM_AVAILABLE("count_direction", &quad8_count_direction_enum),
IIO_ENUM("count_mode", IIO_SEPARATE, &quad8_count_mode_enum),
IIO_ENUM_AVAILABLE("count_mode", &quad8_count_mode_enum),
IIO_ENUM("quadrature_mode", IIO_SEPARATE, &quad8_quadrature_mode_enum),
IIO_ENUM_AVAILABLE("quadrature_mode", &quad8_quadrature_mode_enum),
{}
};
static const struct iio_chan_spec_ext_info quad8_index_ext_info[] = {
IIO_ENUM("synchronous_mode", IIO_SEPARATE,
&quad8_synchronous_mode_enum),
IIO_ENUM_AVAILABLE("synchronous_mode", &quad8_synchronous_mode_enum),
IIO_ENUM("index_polarity", IIO_SEPARATE, &quad8_index_polarity_enum),
IIO_ENUM_AVAILABLE("index_polarity", &quad8_index_polarity_enum),
{}
};
#define QUAD8_COUNT_CHAN(_chan) { \
.type = IIO_COUNT, \
.channel = (_chan), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_ENABLE) | BIT(IIO_CHAN_INFO_SCALE), \
.ext_info = quad8_count_ext_info, \
.indexed = 1 \
}
#define QUAD8_INDEX_CHAN(_chan) { \
.type = IIO_INDEX, \
.channel = (_chan), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.ext_info = quad8_index_ext_info, \
.indexed = 1 \
}
static const struct iio_chan_spec quad8_channels[] = {
QUAD8_COUNT_CHAN(0), QUAD8_INDEX_CHAN(0),
QUAD8_COUNT_CHAN(1), QUAD8_INDEX_CHAN(1),
QUAD8_COUNT_CHAN(2), QUAD8_INDEX_CHAN(2),
QUAD8_COUNT_CHAN(3), QUAD8_INDEX_CHAN(3),
QUAD8_COUNT_CHAN(4), QUAD8_INDEX_CHAN(4),
QUAD8_COUNT_CHAN(5), QUAD8_INDEX_CHAN(5),
QUAD8_COUNT_CHAN(6), QUAD8_INDEX_CHAN(6),
QUAD8_COUNT_CHAN(7), QUAD8_INDEX_CHAN(7)
};
static int quad8_probe(struct device *dev, unsigned int id)
{
struct iio_dev *indio_dev;
struct quad8_iio *priv;
int i, j;
unsigned int base_offset;
indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
if (!indio_dev)
return -ENOMEM;
if (!devm_request_region(dev, base[id], QUAD8_EXTENT,
dev_name(dev))) {
dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
base[id], base[id] + QUAD8_EXTENT);
return -EBUSY;
}
indio_dev->info = &quad8_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->num_channels = ARRAY_SIZE(quad8_channels);
indio_dev->channels = quad8_channels;
indio_dev->name = dev_name(dev);
priv = iio_priv(indio_dev);
priv->base = base[id];
/* Reset all counters and disable interrupt function */
outb(0x01, base[id] + 0x11);
/* Set initial configuration for all counters */
for (i = 0; i < QUAD8_NUM_COUNTERS; i++) {
base_offset = base[id] + 2 * i;
/* Reset Byte Pointer */
outb(0x01, base_offset + 1);
/* Reset Preset Register */
for (j = 0; j < 3; j++)
outb(0x00, base_offset);
/* Reset Borrow, Carry, Compare, and Sign flags */
outb(0x04, base_offset + 1);
/* Reset Error flag */
outb(0x06, base_offset + 1);
/* Binary encoding; Normal count; non-quadrature mode */
outb(0x20, base_offset + 1);
/* Disable A and B inputs; preset on index; FLG1 as Carry */
outb(0x40, base_offset + 1);
/* Disable index function; negative index polarity */
outb(0x60, base_offset + 1);
}
/* Enable all counters */
outb(0x00, base[id] + 0x11);
return devm_iio_device_register(dev, indio_dev);
}
static struct isa_driver quad8_driver = {
.probe = quad8_probe,
.driver = {
.name = "104-quad-8"
}
};
module_isa_driver(quad8_driver, num_quad8);
MODULE_AUTHOR("William Breathitt Gray <[email protected]>");
MODULE_DESCRIPTION("ACCES 104-QUAD-8 IIO driver");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,24 @@
#
# Counter devices
#
# When adding new entries keep the list in alphabetical order
menu "Counters"
config 104_QUAD_8
tristate "ACCES 104-QUAD-8 driver"
depends on X86 && ISA_BUS_API
help
Say yes here to build support for the ACCES 104-QUAD-8 quadrature
encoder counter/interface device family (104-QUAD-8, 104-QUAD-4).
Performing a write to a counter's IIO_CHAN_INFO_RAW sets the counter and
also clears the counter's respective error flag. Although the counters
have a 25-bit range, only the lower 24 bits may be set, either directly
or via a counter's preset attribute. Interrupts are not supported by
this driver.
The base port addresses for the devices may be configured via the base
array module parameter.
endmenu

View file

@ -0,0 +1,7 @@
#
# Makefile for IIO counter devices
#
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o

View file

@ -17,7 +17,7 @@
#define AD5592R_GPIO_READBACK_EN BIT(10)
#define AD5592R_LDAC_READBACK_EN BIT(6)
static int ad5592r_spi_wnop_r16(struct ad5592r_state *st, u16 *buf)
static int ad5592r_spi_wnop_r16(struct ad5592r_state *st, __be16 *buf)
{
struct spi_device *spi = container_of(st->dev, struct spi_device, dev);
struct spi_transfer t = {

View file

@ -18,6 +18,8 @@
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/regulator/consumer.h>
#include <linux/of.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@ -26,12 +28,20 @@
#define MCP4725_DRV_NAME "mcp4725"
#define MCP472X_REF_VDD 0x00
#define MCP472X_REF_VREF_UNBUFFERED 0x02
#define MCP472X_REF_VREF_BUFFERED 0x03
struct mcp4725_data {
struct i2c_client *client;
u16 vref_mv;
int id;
unsigned ref_mode;
bool vref_buffered;
u16 dac_value;
bool powerdown;
unsigned powerdown_mode;
struct regulator *vdd_reg;
struct regulator *vref_reg;
};
static int mcp4725_suspend(struct device *dev)
@ -86,6 +96,7 @@ static ssize_t mcp4725_store_eeprom(struct device *dev,
return 0;
inoutbuf[0] = 0x60; /* write EEPROM */
inoutbuf[0] |= data->ref_mode << 3;
inoutbuf[1] = data->dac_value >> 4;
inoutbuf[2] = (data->dac_value & 0xf) << 4;
@ -278,18 +289,49 @@ static int mcp4725_set_value(struct iio_dev *indio_dev, int val)
return 0;
}
static int mcp4726_set_cfg(struct iio_dev *indio_dev)
{
struct mcp4725_data *data = iio_priv(indio_dev);
u8 outbuf[3];
int ret;
outbuf[0] = 0x40;
outbuf[0] |= data->ref_mode << 3;
if (data->powerdown)
outbuf[0] |= data->powerdown << 1;
outbuf[1] = data->dac_value >> 4;
outbuf[2] = (data->dac_value & 0xf) << 4;
ret = i2c_master_send(data->client, outbuf, 3);
if (ret < 0)
return ret;
else if (ret != 3)
return -EIO;
else
return 0;
}
static int mcp4725_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct mcp4725_data *data = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
*val = data->dac_value;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = data->vref_mv;
if (data->ref_mode == MCP472X_REF_VDD)
ret = regulator_get_voltage(data->vdd_reg);
else
ret = regulator_get_voltage(data->vref_reg);
if (ret < 0)
return ret;
*val = ret / 1000;
*val2 = 12;
return IIO_VAL_FRACTIONAL_LOG2;
}
@ -323,27 +365,98 @@ static const struct iio_info mcp4725_info = {
.driver_module = THIS_MODULE,
};
#ifdef CONFIG_OF
static int mcp4725_probe_dt(struct device *dev,
struct mcp4725_platform_data *pdata)
{
struct device_node *np = dev->of_node;
if (!np)
return -ENODEV;
/* check if is the vref-supply defined */
pdata->use_vref = of_property_read_bool(np, "vref-supply");
pdata->vref_buffered =
of_property_read_bool(np, "microchip,vref-buffered");
return 0;
}
#else
static int mcp4725_probe_dt(struct device *dev,
struct mcp4725_platform_data *platform_data)
{
return -ENODEV;
}
#endif
static int mcp4725_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct mcp4725_data *data;
struct iio_dev *indio_dev;
struct mcp4725_platform_data *platform_data = client->dev.platform_data;
u8 inbuf[3];
struct mcp4725_platform_data *pdata, pdata_dt;
u8 inbuf[4];
u8 pd;
u8 ref;
int err;
if (!platform_data || !platform_data->vref_mv) {
dev_err(&client->dev, "invalid platform data");
return -EINVAL;
}
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (indio_dev == NULL)
return -ENOMEM;
data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
data->client = client;
data->id = id->driver_data;
pdata = dev_get_platdata(&client->dev);
if (!pdata) {
err = mcp4725_probe_dt(&client->dev, &pdata_dt);
if (err) {
dev_err(&client->dev,
"invalid platform or devicetree data");
return err;
}
pdata = &pdata_dt;
}
if (data->id == MCP4725 && pdata->use_vref) {
dev_err(&client->dev,
"external reference is unavailable on MCP4725");
return -EINVAL;
}
if (!pdata->use_vref && pdata->vref_buffered) {
dev_err(&client->dev,
"buffering is unavailable on the internal reference");
return -EINVAL;
}
if (!pdata->use_vref)
data->ref_mode = MCP472X_REF_VDD;
else
data->ref_mode = pdata->vref_buffered ?
MCP472X_REF_VREF_BUFFERED :
MCP472X_REF_VREF_UNBUFFERED;
data->vdd_reg = devm_regulator_get(&client->dev, "vdd");
if (IS_ERR(data->vdd_reg))
return PTR_ERR(data->vdd_reg);
err = regulator_enable(data->vdd_reg);
if (err)
return err;
if (pdata->use_vref) {
data->vref_reg = devm_regulator_get(&client->dev, "vref");
if (IS_ERR(data->vref_reg)) {
err = PTR_ERR(data->vref_reg);
goto err_disable_vdd_reg;
}
err = regulator_enable(data->vref_reg);
if (err)
goto err_disable_vdd_reg;
}
indio_dev->dev.parent = &client->dev;
indio_dev->name = id->name;
@ -352,25 +465,56 @@ static int mcp4725_probe(struct i2c_client *client,
indio_dev->num_channels = 1;
indio_dev->modes = INDIO_DIRECT_MODE;
data->vref_mv = platform_data->vref_mv;
/* read current DAC value and settings */
err = i2c_master_recv(client, inbuf, data->id == MCP4725 ? 3 : 4);
/* read current DAC value */
err = i2c_master_recv(client, inbuf, 3);
if (err < 0) {
dev_err(&client->dev, "failed to read DAC value");
return err;
goto err_disable_vref_reg;
}
pd = (inbuf[0] >> 1) & 0x3;
data->powerdown = pd > 0 ? true : false;
data->powerdown_mode = pd ? pd - 1 : 2; /* largest register to gnd */
data->powerdown_mode = pd ? pd - 1 : 2; /* largest resistor to gnd */
data->dac_value = (inbuf[1] << 4) | (inbuf[2] >> 4);
if (data->id == MCP4726)
ref = (inbuf[3] >> 3) & 0x3;
return iio_device_register(indio_dev);
if (data->id == MCP4726 && ref != data->ref_mode) {
dev_info(&client->dev,
"voltage reference mode differs (conf: %u, eeprom: %u), setting %u",
data->ref_mode, ref, data->ref_mode);
err = mcp4726_set_cfg(indio_dev);
if (err < 0)
goto err_disable_vref_reg;
}
err = iio_device_register(indio_dev);
if (err)
goto err_disable_vref_reg;
return 0;
err_disable_vref_reg:
if (data->vref_reg)
regulator_disable(data->vref_reg);
err_disable_vdd_reg:
regulator_disable(data->vdd_reg);
return err;
}
static int mcp4725_remove(struct i2c_client *client)
{
iio_device_unregister(i2c_get_clientdata(client));
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct mcp4725_data *data = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
if (data->vref_reg)
regulator_disable(data->vref_reg);
regulator_disable(data->vdd_reg);
return 0;
}

View file

@ -27,6 +27,8 @@ config DHT11
config HDC100X
tristate "TI HDC100x relative humidity and temperature sensor"
depends on I2C
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for the Texas Instruments
HDC1000 and HDC1008 relative humidity and temperature sensors.
@ -34,6 +36,28 @@ config HDC100X
To compile this driver as a module, choose M here: the module
will be called hdc100x.
config HTS221
tristate "STMicroelectronics HTS221 sensor Driver"
depends on (I2C || SPI)
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
select HTS221_I2C if (I2C)
select HTS221_SPI if (SPI_MASTER)
help
Say yes here to build support for STMicroelectronics HTS221
temperature-humidity sensor
To compile this driver as a module, choose M here: the module
will be called hts221.
config HTS221_I2C
tristate
depends on HTS221
config HTS221_SPI
tristate
depends on HTS221
config HTU21
tristate "Measurement Specialties HTU21 humidity & temperature sensor"
depends on I2C

View file

@ -5,6 +5,13 @@
obj-$(CONFIG_AM2315) += am2315.o
obj-$(CONFIG_DHT11) += dht11.o
obj-$(CONFIG_HDC100X) += hdc100x.o
hts221-y := hts221_core.o \
hts221_buffer.o
obj-$(CONFIG_HTS221) += hts221.o
obj-$(CONFIG_HTS221_I2C) += hts221_i2c.o
obj-$(CONFIG_HTS221_SPI) += hts221_spi.o
obj-$(CONFIG_HTU21) += htu21.o
obj-$(CONFIG_SI7005) += si7005.o
obj-$(CONFIG_SI7020) += si7020.o

View file

@ -22,11 +22,15 @@
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#define HDC100X_REG_TEMP 0x00
#define HDC100X_REG_HUMIDITY 0x01
#define HDC100X_REG_CONFIG 0x02
#define HDC100X_REG_CONFIG_ACQ_MODE BIT(12)
#define HDC100X_REG_CONFIG_HEATER_EN BIT(13)
struct hdc100x_data {
@ -87,22 +91,40 @@ static const struct iio_chan_spec hdc100x_channels[] = {
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_INT_TIME) |
BIT(IIO_CHAN_INFO_OFFSET),
.scan_index = 0,
.scan_type = {
.sign = 's',
.realbits = 16,
.storagebits = 16,
.endianness = IIO_BE,
},
},
{
.type = IIO_HUMIDITYRELATIVE,
.address = HDC100X_REG_HUMIDITY,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_INT_TIME)
BIT(IIO_CHAN_INFO_INT_TIME),
.scan_index = 1,
.scan_type = {
.sign = 'u',
.realbits = 16,
.storagebits = 16,
.endianness = IIO_BE,
},
},
{
.type = IIO_CURRENT,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.extend_name = "heater",
.output = 1,
.scan_index = -1,
},
IIO_CHAN_SOFT_TIMESTAMP(2),
};
static const unsigned long hdc100x_scan_masks[] = {0x3, 0};
static int hdc100x_update_config(struct hdc100x_data *data, int mask, int val)
{
int tmp = (~mask & data->config) | val;
@ -183,7 +205,14 @@ static int hdc100x_read_raw(struct iio_dev *indio_dev,
*val = hdc100x_get_heater_status(data);
ret = IIO_VAL_INT;
} else {
ret = iio_device_claim_direct_mode(indio_dev);
if (ret) {
mutex_unlock(&data->lock);
return ret;
}
ret = hdc100x_get_measurement(data, chan);
iio_device_release_direct_mode(indio_dev);
if (ret >= 0) {
*val = ret;
ret = IIO_VAL_INT;
@ -246,6 +275,78 @@ static int hdc100x_write_raw(struct iio_dev *indio_dev,
}
}
static int hdc100x_buffer_postenable(struct iio_dev *indio_dev)
{
struct hdc100x_data *data = iio_priv(indio_dev);
int ret;
/* Buffer is enabled. First set ACQ Mode, then attach poll func */
mutex_lock(&data->lock);
ret = hdc100x_update_config(data, HDC100X_REG_CONFIG_ACQ_MODE,
HDC100X_REG_CONFIG_ACQ_MODE);
mutex_unlock(&data->lock);
if (ret)
return ret;
return iio_triggered_buffer_postenable(indio_dev);
}
static int hdc100x_buffer_predisable(struct iio_dev *indio_dev)
{
struct hdc100x_data *data = iio_priv(indio_dev);
int ret;
/* First detach poll func, then reset ACQ mode. OK to disable buffer */
ret = iio_triggered_buffer_predisable(indio_dev);
if (ret)
return ret;
mutex_lock(&data->lock);
ret = hdc100x_update_config(data, HDC100X_REG_CONFIG_ACQ_MODE, 0);
mutex_unlock(&data->lock);
return ret;
}
static const struct iio_buffer_setup_ops hdc_buffer_setup_ops = {
.postenable = hdc100x_buffer_postenable,
.predisable = hdc100x_buffer_predisable,
};
static irqreturn_t hdc100x_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct hdc100x_data *data = iio_priv(indio_dev);
struct i2c_client *client = data->client;
int delay = data->adc_int_us[0] + data->adc_int_us[1];
int ret;
s16 buf[8]; /* 2x s16 + padding + 8 byte timestamp */
/* dual read starts at temp register */
mutex_lock(&data->lock);
ret = i2c_smbus_write_byte(client, HDC100X_REG_TEMP);
if (ret < 0) {
dev_err(&client->dev, "cannot start measurement\n");
goto err;
}
usleep_range(delay, delay + 1000);
ret = i2c_master_recv(client, (u8 *)buf, 4);
if (ret < 0) {
dev_err(&client->dev, "cannot read sensor data\n");
goto err;
}
iio_push_to_buffers_with_timestamp(indio_dev, buf,
iio_get_time_ns(indio_dev));
err:
mutex_unlock(&data->lock);
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
static const struct iio_info hdc100x_info = {
.read_raw = hdc100x_read_raw,
.write_raw = hdc100x_write_raw,
@ -258,6 +359,7 @@ static int hdc100x_probe(struct i2c_client *client,
{
struct iio_dev *indio_dev;
struct hdc100x_data *data;
int ret;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BYTE | I2C_FUNC_I2C))
@ -279,12 +381,35 @@ static int hdc100x_probe(struct i2c_client *client,
indio_dev->channels = hdc100x_channels;
indio_dev->num_channels = ARRAY_SIZE(hdc100x_channels);
indio_dev->available_scan_masks = hdc100x_scan_masks;
/* be sure we are in a known state */
hdc100x_set_it_time(data, 0, hdc100x_int_time[0][0]);
hdc100x_set_it_time(data, 1, hdc100x_int_time[1][0]);
hdc100x_update_config(data, HDC100X_REG_CONFIG_ACQ_MODE, 0);
return devm_iio_device_register(&client->dev, indio_dev);
ret = iio_triggered_buffer_setup(indio_dev, NULL,
hdc100x_trigger_handler,
&hdc_buffer_setup_ops);
if (ret < 0) {
dev_err(&client->dev, "iio triggered buffer setup failed\n");
return ret;
}
ret = iio_device_register(indio_dev);
if (ret < 0)
iio_triggered_buffer_cleanup(indio_dev);
return ret;
}
static int hdc100x_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
return 0;
}
static const struct i2c_device_id hdc100x_id[] = {
@ -298,6 +423,7 @@ static struct i2c_driver hdc100x_driver = {
.name = "hdc100x",
},
.probe = hdc100x_probe,
.remove = hdc100x_remove,
.id_table = hdc100x_id,
};
module_i2c_driver(hdc100x_driver);

View file

@ -0,0 +1,73 @@
/*
* STMicroelectronics hts221 sensor driver
*
* Copyright 2016 STMicroelectronics Inc.
*
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
*
* Licensed under the GPL-2.
*/
#ifndef HTS221_H
#define HTS221_H
#define HTS221_DEV_NAME "hts221"
#include <linux/iio/iio.h>
#define HTS221_RX_MAX_LENGTH 8
#define HTS221_TX_MAX_LENGTH 8
#define HTS221_DATA_SIZE 2
struct hts221_transfer_buffer {
u8 rx_buf[HTS221_RX_MAX_LENGTH];
u8 tx_buf[HTS221_TX_MAX_LENGTH] ____cacheline_aligned;
};
struct hts221_transfer_function {
int (*read)(struct device *dev, u8 addr, int len, u8 *data);
int (*write)(struct device *dev, u8 addr, int len, u8 *data);
};
#define HTS221_AVG_DEPTH 8
struct hts221_avg_avl {
u16 avg;
u8 val;
};
enum hts221_sensor_type {
HTS221_SENSOR_H,
HTS221_SENSOR_T,
HTS221_SENSOR_MAX,
};
struct hts221_sensor {
u8 cur_avg_idx;
int slope, b_gen;
};
struct hts221_hw {
const char *name;
struct device *dev;
struct mutex lock;
struct iio_trigger *trig;
int irq;
struct hts221_sensor sensors[HTS221_SENSOR_MAX];
u8 odr;
const struct hts221_transfer_function *tf;
struct hts221_transfer_buffer tb;
};
int hts221_config_drdy(struct hts221_hw *hw, bool enable);
int hts221_probe(struct iio_dev *iio_dev);
int hts221_power_on(struct hts221_hw *hw);
int hts221_power_off(struct hts221_hw *hw);
int hts221_allocate_buffers(struct hts221_hw *hw);
int hts221_allocate_trigger(struct hts221_hw *hw);
#endif /* HTS221_H */

View file

@ -0,0 +1,169 @@
/*
* STMicroelectronics hts221 sensor driver
*
* Copyright 2016 STMicroelectronics Inc.
*
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
*
* Licensed under the GPL-2.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger.h>
#include <linux/interrupt.h>
#include <linux/iio/events.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/buffer.h>
#include "hts221.h"
#define HTS221_REG_STATUS_ADDR 0x27
#define HTS221_RH_DRDY_MASK BIT(1)
#define HTS221_TEMP_DRDY_MASK BIT(0)
static int hts221_trig_set_state(struct iio_trigger *trig, bool state)
{
struct iio_dev *iio_dev = iio_trigger_get_drvdata(trig);
struct hts221_hw *hw = iio_priv(iio_dev);
return hts221_config_drdy(hw, state);
}
static const struct iio_trigger_ops hts221_trigger_ops = {
.owner = THIS_MODULE,
.set_trigger_state = hts221_trig_set_state,
};
static irqreturn_t hts221_trigger_handler_thread(int irq, void *private)
{
struct hts221_hw *hw = (struct hts221_hw *)private;
u8 status;
int err;
err = hw->tf->read(hw->dev, HTS221_REG_STATUS_ADDR, sizeof(status),
&status);
if (err < 0)
return IRQ_HANDLED;
/*
* H_DA bit (humidity data available) is routed to DRDY line.
* Humidity sample is computed after temperature one.
* Here we can assume data channels are both available if H_DA bit
* is set in status register
*/
if (!(status & HTS221_RH_DRDY_MASK))
return IRQ_NONE;
iio_trigger_poll_chained(hw->trig);
return IRQ_HANDLED;
}
int hts221_allocate_trigger(struct hts221_hw *hw)
{
struct iio_dev *iio_dev = iio_priv_to_dev(hw);
unsigned long irq_type;
int err;
irq_type = irqd_get_trigger_type(irq_get_irq_data(hw->irq));
switch (irq_type) {
case IRQF_TRIGGER_HIGH:
case IRQF_TRIGGER_RISING:
break;
default:
dev_info(hw->dev,
"mode %lx unsupported, using IRQF_TRIGGER_RISING\n",
irq_type);
irq_type = IRQF_TRIGGER_RISING;
break;
}
err = devm_request_threaded_irq(hw->dev, hw->irq, NULL,
hts221_trigger_handler_thread,
irq_type | IRQF_ONESHOT,
hw->name, hw);
if (err) {
dev_err(hw->dev, "failed to request trigger irq %d\n",
hw->irq);
return err;
}
hw->trig = devm_iio_trigger_alloc(hw->dev, "%s-trigger",
iio_dev->name);
if (!hw->trig)
return -ENOMEM;
iio_trigger_set_drvdata(hw->trig, iio_dev);
hw->trig->ops = &hts221_trigger_ops;
hw->trig->dev.parent = hw->dev;
iio_dev->trig = iio_trigger_get(hw->trig);
return devm_iio_trigger_register(hw->dev, hw->trig);
}
static int hts221_buffer_preenable(struct iio_dev *iio_dev)
{
return hts221_power_on(iio_priv(iio_dev));
}
static int hts221_buffer_postdisable(struct iio_dev *iio_dev)
{
return hts221_power_off(iio_priv(iio_dev));
}
static const struct iio_buffer_setup_ops hts221_buffer_ops = {
.preenable = hts221_buffer_preenable,
.postenable = iio_triggered_buffer_postenable,
.predisable = iio_triggered_buffer_predisable,
.postdisable = hts221_buffer_postdisable,
};
static irqreturn_t hts221_buffer_handler_thread(int irq, void *p)
{
u8 buffer[ALIGN(2 * HTS221_DATA_SIZE, sizeof(s64)) + sizeof(s64)];
struct iio_poll_func *pf = p;
struct iio_dev *iio_dev = pf->indio_dev;
struct hts221_hw *hw = iio_priv(iio_dev);
struct iio_chan_spec const *ch;
int err;
/* humidity data */
ch = &iio_dev->channels[HTS221_SENSOR_H];
err = hw->tf->read(hw->dev, ch->address, HTS221_DATA_SIZE,
buffer);
if (err < 0)
goto out;
/* temperature data */
ch = &iio_dev->channels[HTS221_SENSOR_T];
err = hw->tf->read(hw->dev, ch->address, HTS221_DATA_SIZE,
buffer + HTS221_DATA_SIZE);
if (err < 0)
goto out;
iio_push_to_buffers_with_timestamp(iio_dev, buffer,
iio_get_time_ns(iio_dev));
out:
iio_trigger_notify_done(hw->trig);
return IRQ_HANDLED;
}
int hts221_allocate_buffers(struct hts221_hw *hw)
{
return devm_iio_triggered_buffer_setup(hw->dev, iio_priv_to_dev(hw),
NULL, hts221_buffer_handler_thread,
&hts221_buffer_ops);
}
MODULE_AUTHOR("Lorenzo Bianconi <[email protected]>");
MODULE_DESCRIPTION("STMicroelectronics hts221 buffer driver");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,687 @@
/*
* STMicroelectronics hts221 sensor driver
*
* Copyright 2016 STMicroelectronics Inc.
*
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
*
* Licensed under the GPL-2.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/iio/sysfs.h>
#include <linux/delay.h>
#include <asm/unaligned.h>
#include "hts221.h"
#define HTS221_REG_WHOAMI_ADDR 0x0f
#define HTS221_REG_WHOAMI_VAL 0xbc
#define HTS221_REG_CNTRL1_ADDR 0x20
#define HTS221_REG_CNTRL2_ADDR 0x21
#define HTS221_REG_CNTRL3_ADDR 0x22
#define HTS221_REG_AVG_ADDR 0x10
#define HTS221_REG_H_OUT_L 0x28
#define HTS221_REG_T_OUT_L 0x2a
#define HTS221_HUMIDITY_AVG_MASK 0x07
#define HTS221_TEMP_AVG_MASK 0x38
#define HTS221_ODR_MASK 0x87
#define HTS221_BDU_MASK BIT(2)
#define HTS221_DRDY_MASK BIT(2)
#define HTS221_ENABLE_SENSOR BIT(7)
#define HTS221_HUMIDITY_AVG_4 0x00 /* 0.4 %RH */
#define HTS221_HUMIDITY_AVG_8 0x01 /* 0.3 %RH */
#define HTS221_HUMIDITY_AVG_16 0x02 /* 0.2 %RH */
#define HTS221_HUMIDITY_AVG_32 0x03 /* 0.15 %RH */
#define HTS221_HUMIDITY_AVG_64 0x04 /* 0.1 %RH */
#define HTS221_HUMIDITY_AVG_128 0x05 /* 0.07 %RH */
#define HTS221_HUMIDITY_AVG_256 0x06 /* 0.05 %RH */
#define HTS221_HUMIDITY_AVG_512 0x07 /* 0.03 %RH */
#define HTS221_TEMP_AVG_2 0x00 /* 0.08 degC */
#define HTS221_TEMP_AVG_4 0x08 /* 0.05 degC */
#define HTS221_TEMP_AVG_8 0x10 /* 0.04 degC */
#define HTS221_TEMP_AVG_16 0x18 /* 0.03 degC */
#define HTS221_TEMP_AVG_32 0x20 /* 0.02 degC */
#define HTS221_TEMP_AVG_64 0x28 /* 0.015 degC */
#define HTS221_TEMP_AVG_128 0x30 /* 0.01 degC */
#define HTS221_TEMP_AVG_256 0x38 /* 0.007 degC */
/* calibration registers */
#define HTS221_REG_0RH_CAL_X_H 0x36
#define HTS221_REG_1RH_CAL_X_H 0x3a
#define HTS221_REG_0RH_CAL_Y_H 0x30
#define HTS221_REG_1RH_CAL_Y_H 0x31
#define HTS221_REG_0T_CAL_X_L 0x3c
#define HTS221_REG_1T_CAL_X_L 0x3e
#define HTS221_REG_0T_CAL_Y_H 0x32
#define HTS221_REG_1T_CAL_Y_H 0x33
#define HTS221_REG_T1_T0_CAL_Y_H 0x35
struct hts221_odr {
u8 hz;
u8 val;
};
struct hts221_avg {
u8 addr;
u8 mask;
struct hts221_avg_avl avg_avl[HTS221_AVG_DEPTH];
};
static const struct hts221_odr hts221_odr_table[] = {
{ 1, 0x01 }, /* 1Hz */
{ 7, 0x02 }, /* 7Hz */
{ 13, 0x03 }, /* 12.5Hz */
};
static const struct hts221_avg hts221_avg_list[] = {
{
.addr = HTS221_REG_AVG_ADDR,
.mask = HTS221_HUMIDITY_AVG_MASK,
.avg_avl = {
{ 4, HTS221_HUMIDITY_AVG_4 },
{ 8, HTS221_HUMIDITY_AVG_8 },
{ 16, HTS221_HUMIDITY_AVG_16 },
{ 32, HTS221_HUMIDITY_AVG_32 },
{ 64, HTS221_HUMIDITY_AVG_64 },
{ 128, HTS221_HUMIDITY_AVG_128 },
{ 256, HTS221_HUMIDITY_AVG_256 },
{ 512, HTS221_HUMIDITY_AVG_512 },
},
},
{
.addr = HTS221_REG_AVG_ADDR,
.mask = HTS221_TEMP_AVG_MASK,
.avg_avl = {
{ 2, HTS221_TEMP_AVG_2 },
{ 4, HTS221_TEMP_AVG_4 },
{ 8, HTS221_TEMP_AVG_8 },
{ 16, HTS221_TEMP_AVG_16 },
{ 32, HTS221_TEMP_AVG_32 },
{ 64, HTS221_TEMP_AVG_64 },
{ 128, HTS221_TEMP_AVG_128 },
{ 256, HTS221_TEMP_AVG_256 },
},
},
};
static const struct iio_chan_spec hts221_channels[] = {
{
.type = IIO_HUMIDITYRELATIVE,
.address = HTS221_REG_H_OUT_L,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
.scan_index = 0,
.scan_type = {
.sign = 's',
.realbits = 16,
.storagebits = 16,
.endianness = IIO_LE,
},
},
{
.type = IIO_TEMP,
.address = HTS221_REG_T_OUT_L,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
.scan_index = 1,
.scan_type = {
.sign = 's',
.realbits = 16,
.storagebits = 16,
.endianness = IIO_LE,
},
},
IIO_CHAN_SOFT_TIMESTAMP(2),
};
static int hts221_write_with_mask(struct hts221_hw *hw, u8 addr, u8 mask,
u8 val)
{
u8 data;
int err;
mutex_lock(&hw->lock);
err = hw->tf->read(hw->dev, addr, sizeof(data), &data);
if (err < 0) {
dev_err(hw->dev, "failed to read %02x register\n", addr);
goto unlock;
}
data = (data & ~mask) | (val & mask);
err = hw->tf->write(hw->dev, addr, sizeof(data), &data);
if (err < 0)
dev_err(hw->dev, "failed to write %02x register\n", addr);
unlock:
mutex_unlock(&hw->lock);
return err;
}
static int hts221_check_whoami(struct hts221_hw *hw)
{
u8 data;
int err;
err = hw->tf->read(hw->dev, HTS221_REG_WHOAMI_ADDR, sizeof(data),
&data);
if (err < 0) {
dev_err(hw->dev, "failed to read whoami register\n");
return err;
}
if (data != HTS221_REG_WHOAMI_VAL) {
dev_err(hw->dev, "wrong whoami {%02x vs %02x}\n",
data, HTS221_REG_WHOAMI_VAL);
return -ENODEV;
}
return 0;
}
int hts221_config_drdy(struct hts221_hw *hw, bool enable)
{
u8 val = enable ? BIT(2) : 0;
int err;
err = hts221_write_with_mask(hw, HTS221_REG_CNTRL3_ADDR,
HTS221_DRDY_MASK, val);
return err < 0 ? err : 0;
}
static int hts221_update_odr(struct hts221_hw *hw, u8 odr)
{
int i, err;
u8 val;
for (i = 0; i < ARRAY_SIZE(hts221_odr_table); i++)
if (hts221_odr_table[i].hz == odr)
break;
if (i == ARRAY_SIZE(hts221_odr_table))
return -EINVAL;
val = HTS221_ENABLE_SENSOR | HTS221_BDU_MASK | hts221_odr_table[i].val;
err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR,
HTS221_ODR_MASK, val);
if (err < 0)
return err;
hw->odr = odr;
return 0;
}
static int hts221_update_avg(struct hts221_hw *hw,
enum hts221_sensor_type type,
u16 val)
{
int i, err;
const struct hts221_avg *avg = &hts221_avg_list[type];
for (i = 0; i < HTS221_AVG_DEPTH; i++)
if (avg->avg_avl[i].avg == val)
break;
if (i == HTS221_AVG_DEPTH)
return -EINVAL;
err = hts221_write_with_mask(hw, avg->addr, avg->mask,
avg->avg_avl[i].val);
if (err < 0)
return err;
hw->sensors[type].cur_avg_idx = i;
return 0;
}
static ssize_t hts221_sysfs_sampling_freq(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int i;
ssize_t len = 0;
for (i = 0; i < ARRAY_SIZE(hts221_odr_table); i++)
len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
hts221_odr_table[i].hz);
buf[len - 1] = '\n';
return len;
}
static ssize_t
hts221_sysfs_rh_oversampling_avail(struct device *dev,
struct device_attribute *attr,
char *buf)
{
const struct hts221_avg *avg = &hts221_avg_list[HTS221_SENSOR_H];
ssize_t len = 0;
int i;
for (i = 0; i < ARRAY_SIZE(avg->avg_avl); i++)
len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
avg->avg_avl[i].avg);
buf[len - 1] = '\n';
return len;
}
static ssize_t
hts221_sysfs_temp_oversampling_avail(struct device *dev,
struct device_attribute *attr,
char *buf)
{
const struct hts221_avg *avg = &hts221_avg_list[HTS221_SENSOR_T];
ssize_t len = 0;
int i;
for (i = 0; i < ARRAY_SIZE(avg->avg_avl); i++)
len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
avg->avg_avl[i].avg);
buf[len - 1] = '\n';
return len;
}
int hts221_power_on(struct hts221_hw *hw)
{
return hts221_update_odr(hw, hw->odr);
}
int hts221_power_off(struct hts221_hw *hw)
{
u8 data[] = {0x00, 0x00};
return hw->tf->write(hw->dev, HTS221_REG_CNTRL1_ADDR, sizeof(data),
data);
}
static int hts221_parse_temp_caldata(struct hts221_hw *hw)
{
int err, *slope, *b_gen;
s16 cal_x0, cal_x1, cal_y0, cal_y1;
u8 cal0, cal1;
err = hw->tf->read(hw->dev, HTS221_REG_0T_CAL_Y_H,
sizeof(cal0), &cal0);
if (err < 0)
return err;
err = hw->tf->read(hw->dev, HTS221_REG_T1_T0_CAL_Y_H,
sizeof(cal1), &cal1);
if (err < 0)
return err;
cal_y0 = (le16_to_cpu(cal1 & 0x3) << 8) | cal0;
err = hw->tf->read(hw->dev, HTS221_REG_1T_CAL_Y_H,
sizeof(cal0), &cal0);
if (err < 0)
return err;
cal_y1 = (((cal1 & 0xc) >> 2) << 8) | cal0;
err = hw->tf->read(hw->dev, HTS221_REG_0T_CAL_X_L, sizeof(cal_x0),
(u8 *)&cal_x0);
if (err < 0)
return err;
cal_x0 = le16_to_cpu(cal_x0);
err = hw->tf->read(hw->dev, HTS221_REG_1T_CAL_X_L, sizeof(cal_x1),
(u8 *)&cal_x1);
if (err < 0)
return err;
cal_x1 = le16_to_cpu(cal_x1);
slope = &hw->sensors[HTS221_SENSOR_T].slope;
b_gen = &hw->sensors[HTS221_SENSOR_T].b_gen;
*slope = ((cal_y1 - cal_y0) * 8000) / (cal_x1 - cal_x0);
*b_gen = (((s32)cal_x1 * cal_y0 - (s32)cal_x0 * cal_y1) * 1000) /
(cal_x1 - cal_x0);
*b_gen *= 8;
return 0;
}
static int hts221_parse_rh_caldata(struct hts221_hw *hw)
{
int err, *slope, *b_gen;
s16 cal_x0, cal_x1, cal_y0, cal_y1;
u8 data;
err = hw->tf->read(hw->dev, HTS221_REG_0RH_CAL_Y_H, sizeof(data),
&data);
if (err < 0)
return err;
cal_y0 = data;
err = hw->tf->read(hw->dev, HTS221_REG_1RH_CAL_Y_H, sizeof(data),
&data);
if (err < 0)
return err;
cal_y1 = data;
err = hw->tf->read(hw->dev, HTS221_REG_0RH_CAL_X_H, sizeof(cal_x0),
(u8 *)&cal_x0);
if (err < 0)
return err;
cal_x0 = le16_to_cpu(cal_x0);
err = hw->tf->read(hw->dev, HTS221_REG_1RH_CAL_X_H, sizeof(cal_x1),
(u8 *)&cal_x1);
if (err < 0)
return err;
cal_x1 = le16_to_cpu(cal_x1);
slope = &hw->sensors[HTS221_SENSOR_H].slope;
b_gen = &hw->sensors[HTS221_SENSOR_H].b_gen;
*slope = ((cal_y1 - cal_y0) * 8000) / (cal_x1 - cal_x0);
*b_gen = (((s32)cal_x1 * cal_y0 - (s32)cal_x0 * cal_y1) * 1000) /
(cal_x1 - cal_x0);
*b_gen *= 8;
return 0;
}
static int hts221_get_sensor_scale(struct hts221_hw *hw,
enum iio_chan_type ch_type,
int *val, int *val2)
{
s64 tmp;
s32 rem, div, data;
switch (ch_type) {
case IIO_HUMIDITYRELATIVE:
data = hw->sensors[HTS221_SENSOR_H].slope;
div = (1 << 4) * 1000;
break;
case IIO_TEMP:
data = hw->sensors[HTS221_SENSOR_T].slope;
div = (1 << 6) * 1000;
break;
default:
return -EINVAL;
}
tmp = div_s64(data * 1000000000LL, div);
tmp = div_s64_rem(tmp, 1000000000LL, &rem);
*val = tmp;
*val2 = rem;
return IIO_VAL_INT_PLUS_NANO;
}
static int hts221_get_sensor_offset(struct hts221_hw *hw,
enum iio_chan_type ch_type,
int *val, int *val2)
{
s64 tmp;
s32 rem, div, data;
switch (ch_type) {
case IIO_HUMIDITYRELATIVE:
data = hw->sensors[HTS221_SENSOR_H].b_gen;
div = hw->sensors[HTS221_SENSOR_H].slope;
break;
case IIO_TEMP:
data = hw->sensors[HTS221_SENSOR_T].b_gen;
div = hw->sensors[HTS221_SENSOR_T].slope;
break;
default:
return -EINVAL;
}
tmp = div_s64(data * 1000000000LL, div);
tmp = div_s64_rem(tmp, 1000000000LL, &rem);
*val = tmp;
*val2 = rem;
return IIO_VAL_INT_PLUS_NANO;
}
static int hts221_read_oneshot(struct hts221_hw *hw, u8 addr, int *val)
{
u8 data[HTS221_DATA_SIZE];
int err;
err = hts221_power_on(hw);
if (err < 0)
return err;
msleep(50);
err = hw->tf->read(hw->dev, addr, sizeof(data), data);
if (err < 0)
return err;
hts221_power_off(hw);
*val = (s16)get_unaligned_le16(data);
return IIO_VAL_INT;
}
static int hts221_read_raw(struct iio_dev *iio_dev,
struct iio_chan_spec const *ch,
int *val, int *val2, long mask)
{
struct hts221_hw *hw = iio_priv(iio_dev);
int ret;
ret = iio_device_claim_direct_mode(iio_dev);
if (ret)
return ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = hts221_read_oneshot(hw, ch->address, val);
break;
case IIO_CHAN_INFO_SCALE:
ret = hts221_get_sensor_scale(hw, ch->type, val, val2);
break;
case IIO_CHAN_INFO_OFFSET:
ret = hts221_get_sensor_offset(hw, ch->type, val, val2);
break;
case IIO_CHAN_INFO_SAMP_FREQ:
*val = hw->odr;
ret = IIO_VAL_INT;
break;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO: {
u8 idx;
const struct hts221_avg *avg;
switch (ch->type) {
case IIO_HUMIDITYRELATIVE:
avg = &hts221_avg_list[HTS221_SENSOR_H];
idx = hw->sensors[HTS221_SENSOR_H].cur_avg_idx;
*val = avg->avg_avl[idx].avg;
ret = IIO_VAL_INT;
break;
case IIO_TEMP:
avg = &hts221_avg_list[HTS221_SENSOR_T];
idx = hw->sensors[HTS221_SENSOR_T].cur_avg_idx;
*val = avg->avg_avl[idx].avg;
ret = IIO_VAL_INT;
break;
default:
ret = -EINVAL;
break;
}
break;
}
default:
ret = -EINVAL;
break;
}
iio_device_release_direct_mode(iio_dev);
return ret;
}
static int hts221_write_raw(struct iio_dev *iio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct hts221_hw *hw = iio_priv(iio_dev);
int ret;
ret = iio_device_claim_direct_mode(iio_dev);
if (ret)
return ret;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
ret = hts221_update_odr(hw, val);
break;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
switch (chan->type) {
case IIO_HUMIDITYRELATIVE:
ret = hts221_update_avg(hw, HTS221_SENSOR_H, val);
break;
case IIO_TEMP:
ret = hts221_update_avg(hw, HTS221_SENSOR_T, val);
break;
default:
ret = -EINVAL;
break;
}
break;
default:
ret = -EINVAL;
break;
}
iio_device_release_direct_mode(iio_dev);
return ret;
}
static int hts221_validate_trigger(struct iio_dev *iio_dev,
struct iio_trigger *trig)
{
struct hts221_hw *hw = iio_priv(iio_dev);
return hw->trig == trig ? 0 : -EINVAL;
}
static IIO_DEVICE_ATTR(in_humidity_oversampling_ratio_available, S_IRUGO,
hts221_sysfs_rh_oversampling_avail, NULL, 0);
static IIO_DEVICE_ATTR(in_temp_oversampling_ratio_available, S_IRUGO,
hts221_sysfs_temp_oversampling_avail, NULL, 0);
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(hts221_sysfs_sampling_freq);
static struct attribute *hts221_attributes[] = {
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
&iio_dev_attr_in_humidity_oversampling_ratio_available.dev_attr.attr,
&iio_dev_attr_in_temp_oversampling_ratio_available.dev_attr.attr,
NULL,
};
static const struct attribute_group hts221_attribute_group = {
.attrs = hts221_attributes,
};
static const struct iio_info hts221_info = {
.driver_module = THIS_MODULE,
.attrs = &hts221_attribute_group,
.read_raw = hts221_read_raw,
.write_raw = hts221_write_raw,
.validate_trigger = hts221_validate_trigger,
};
static const unsigned long hts221_scan_masks[] = {0x3, 0x0};
int hts221_probe(struct iio_dev *iio_dev)
{
struct hts221_hw *hw = iio_priv(iio_dev);
int err;
u8 data;
mutex_init(&hw->lock);
err = hts221_check_whoami(hw);
if (err < 0)
return err;
hw->odr = hts221_odr_table[0].hz;
iio_dev->modes = INDIO_DIRECT_MODE;
iio_dev->dev.parent = hw->dev;
iio_dev->available_scan_masks = hts221_scan_masks;
iio_dev->channels = hts221_channels;
iio_dev->num_channels = ARRAY_SIZE(hts221_channels);
iio_dev->name = HTS221_DEV_NAME;
iio_dev->info = &hts221_info;
/* configure humidity sensor */
err = hts221_parse_rh_caldata(hw);
if (err < 0) {
dev_err(hw->dev, "failed to get rh calibration data\n");
return err;
}
data = hts221_avg_list[HTS221_SENSOR_H].avg_avl[3].avg;
err = hts221_update_avg(hw, HTS221_SENSOR_H, data);
if (err < 0) {
dev_err(hw->dev, "failed to set rh oversampling ratio\n");
return err;
}
/* configure temperature sensor */
err = hts221_parse_temp_caldata(hw);
if (err < 0) {
dev_err(hw->dev,
"failed to get temperature calibration data\n");
return err;
}
data = hts221_avg_list[HTS221_SENSOR_T].avg_avl[3].avg;
err = hts221_update_avg(hw, HTS221_SENSOR_T, data);
if (err < 0) {
dev_err(hw->dev,
"failed to set temperature oversampling ratio\n");
return err;
}
if (hw->irq > 0) {
err = hts221_allocate_buffers(hw);
if (err < 0)
return err;
err = hts221_allocate_trigger(hw);
if (err)
return err;
}
return devm_iio_device_register(hw->dev, iio_dev);
}
EXPORT_SYMBOL(hts221_probe);
MODULE_AUTHOR("Lorenzo Bianconi <[email protected]>");
MODULE_DESCRIPTION("STMicroelectronics hts221 sensor driver");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,110 @@
/*
* STMicroelectronics hts221 i2c driver
*
* Copyright 2016 STMicroelectronics Inc.
*
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
*
* Licensed under the GPL-2.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include "hts221.h"
#define I2C_AUTO_INCREMENT 0x80
static int hts221_i2c_read(struct device *dev, u8 addr, int len, u8 *data)
{
struct i2c_msg msg[2];
struct i2c_client *client = to_i2c_client(dev);
if (len > 1)
addr |= I2C_AUTO_INCREMENT;
msg[0].addr = client->addr;
msg[0].flags = client->flags;
msg[0].len = 1;
msg[0].buf = &addr;
msg[1].addr = client->addr;
msg[1].flags = client->flags | I2C_M_RD;
msg[1].len = len;
msg[1].buf = data;
return i2c_transfer(client->adapter, msg, 2);
}
static int hts221_i2c_write(struct device *dev, u8 addr, int len, u8 *data)
{
u8 send[len + 1];
struct i2c_msg msg;
struct i2c_client *client = to_i2c_client(dev);
if (len > 1)
addr |= I2C_AUTO_INCREMENT;
send[0] = addr;
memcpy(&send[1], data, len * sizeof(u8));
msg.addr = client->addr;
msg.flags = client->flags;
msg.len = len + 1;
msg.buf = send;
return i2c_transfer(client->adapter, &msg, 1);
}
static const struct hts221_transfer_function hts221_transfer_fn = {
.read = hts221_i2c_read,
.write = hts221_i2c_write,
};
static int hts221_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct hts221_hw *hw;
struct iio_dev *iio_dev;
iio_dev = devm_iio_device_alloc(&client->dev, sizeof(*hw));
if (!iio_dev)
return -ENOMEM;
i2c_set_clientdata(client, iio_dev);
hw = iio_priv(iio_dev);
hw->name = client->name;
hw->dev = &client->dev;
hw->irq = client->irq;
hw->tf = &hts221_transfer_fn;
return hts221_probe(iio_dev);
}
static const struct of_device_id hts221_i2c_of_match[] = {
{ .compatible = "st,hts221", },
{},
};
MODULE_DEVICE_TABLE(of, hts221_i2c_of_match);
static const struct i2c_device_id hts221_i2c_id_table[] = {
{ HTS221_DEV_NAME },
{},
};
MODULE_DEVICE_TABLE(i2c, hts221_i2c_id_table);
static struct i2c_driver hts221_driver = {
.driver = {
.name = "hts221_i2c",
.of_match_table = of_match_ptr(hts221_i2c_of_match),
},
.probe = hts221_i2c_probe,
.id_table = hts221_i2c_id_table,
};
module_i2c_driver(hts221_driver);
MODULE_AUTHOR("Lorenzo Bianconi <[email protected]>");
MODULE_DESCRIPTION("STMicroelectronics hts221 i2c driver");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,125 @@
/*
* STMicroelectronics hts221 spi driver
*
* Copyright 2016 STMicroelectronics Inc.
*
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
*
* Licensed under the GPL-2.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/slab.h>
#include "hts221.h"
#define SENSORS_SPI_READ 0x80
#define SPI_AUTO_INCREMENT 0x40
static int hts221_spi_read(struct device *dev, u8 addr, int len, u8 *data)
{
int err;
struct spi_device *spi = to_spi_device(dev);
struct iio_dev *iio_dev = spi_get_drvdata(spi);
struct hts221_hw *hw = iio_priv(iio_dev);
struct spi_transfer xfers[] = {
{
.tx_buf = hw->tb.tx_buf,
.bits_per_word = 8,
.len = 1,
},
{
.rx_buf = hw->tb.rx_buf,
.bits_per_word = 8,
.len = len,
}
};
if (len > 1)
addr |= SPI_AUTO_INCREMENT;
hw->tb.tx_buf[0] = addr | SENSORS_SPI_READ;
err = spi_sync_transfer(spi, xfers, ARRAY_SIZE(xfers));
if (err < 0)
return err;
memcpy(data, hw->tb.rx_buf, len * sizeof(u8));
return len;
}
static int hts221_spi_write(struct device *dev, u8 addr, int len, u8 *data)
{
struct spi_device *spi = to_spi_device(dev);
struct iio_dev *iio_dev = spi_get_drvdata(spi);
struct hts221_hw *hw = iio_priv(iio_dev);
struct spi_transfer xfers = {
.tx_buf = hw->tb.tx_buf,
.bits_per_word = 8,
.len = len + 1,
};
if (len >= HTS221_TX_MAX_LENGTH)
return -ENOMEM;
if (len > 1)
addr |= SPI_AUTO_INCREMENT;
hw->tb.tx_buf[0] = addr;
memcpy(&hw->tb.tx_buf[1], data, len);
return spi_sync_transfer(spi, &xfers, 1);
}
static const struct hts221_transfer_function hts221_transfer_fn = {
.read = hts221_spi_read,
.write = hts221_spi_write,
};
static int hts221_spi_probe(struct spi_device *spi)
{
struct hts221_hw *hw;
struct iio_dev *iio_dev;
iio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*hw));
if (!iio_dev)
return -ENOMEM;
spi_set_drvdata(spi, iio_dev);
hw = iio_priv(iio_dev);
hw->name = spi->modalias;
hw->dev = &spi->dev;
hw->irq = spi->irq;
hw->tf = &hts221_transfer_fn;
return hts221_probe(iio_dev);
}
static const struct of_device_id hts221_spi_of_match[] = {
{ .compatible = "st,hts221", },
{},
};
MODULE_DEVICE_TABLE(of, hts221_spi_of_match);
static const struct spi_device_id hts221_spi_id_table[] = {
{ HTS221_DEV_NAME },
{},
};
MODULE_DEVICE_TABLE(spi, hts221_spi_id_table);
static struct spi_driver hts221_driver = {
.driver = {
.name = "hts221_spi",
.of_match_table = of_match_ptr(hts221_spi_of_match),
},
.probe = hts221_spi_probe,
.id_table = hts221_spi_id_table,
};
module_spi_driver(hts221_driver);
MODULE_AUTHOR("Lorenzo Bianconi <[email protected]>");
MODULE_DESCRIPTION("STMicroelectronics hts221 spi driver");
MODULE_LICENSE("GPL v2");

View file

@ -398,7 +398,8 @@ static irqreturn_t bmi160_trigger_handler(int irq, void *p)
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct bmi160_data *data = iio_priv(indio_dev);
s16 buf[16]; /* 3 sens x 3 axis x s16 + 3 x s16 pad + 4 x s16 tstamp */
__le16 buf[16];
/* 3 sens x 3 axis x __le16 + 3 x __le16 pad + 4 x __le16 tstamp */
int i, ret, j = 0, base = BMI160_REG_DATA_MAGN_XOUT_L;
__le16 sample;

View file

@ -307,10 +307,9 @@ static int iio_scan_mask_set(struct iio_dev *indio_dev,
const unsigned long *mask;
unsigned long *trialmask;
trialmask = kmalloc(sizeof(*trialmask)*
BITS_TO_LONGS(indio_dev->masklength),
GFP_KERNEL);
trialmask = kmalloc_array(BITS_TO_LONGS(indio_dev->masklength),
sizeof(*trialmask),
GFP_KERNEL);
if (trialmask == NULL)
return -ENOMEM;
if (!indio_dev->masklength) {

View file

@ -81,6 +81,8 @@ static const char * const iio_chan_type_name_spec[] = {
[IIO_PH] = "ph",
[IIO_UVINDEX] = "uvindex",
[IIO_ELECTRICALCONDUCTIVITY] = "electricalconductivity",
[IIO_COUNT] = "count",
[IIO_INDEX] = "index",
};
static const char * const iio_modifier_names[] = {

View file

@ -717,6 +717,27 @@ bool iio_trigger_using_own(struct iio_dev *indio_dev)
}
EXPORT_SYMBOL(iio_trigger_using_own);
/**
* iio_trigger_validate_own_device - Check if a trigger and IIO device belong to
* the same device
* @trig: The IIO trigger to check
* @indio_dev: the IIO device to check
*
* This function can be used as the validate_device callback for triggers that
* can only be attached to their own device.
*
* Return: 0 if both the trigger and the IIO device belong to the same
* device, -EINVAL otherwise.
*/
int iio_trigger_validate_own_device(struct iio_trigger *trig,
struct iio_dev *indio_dev)
{
if (indio_dev->dev.parent != trig->dev.parent)
return -EINVAL;
return 0;
}
EXPORT_SYMBOL(iio_trigger_validate_own_device);
void iio_device_register_trigger_consumer(struct iio_dev *indio_dev)
{
indio_dev->groups[indio_dev->groupcounter++] =

View file

@ -658,6 +658,31 @@ err_unlock:
}
EXPORT_SYMBOL_GPL(iio_convert_raw_to_processed);
static int iio_read_channel_attribute(struct iio_channel *chan,
int *val, int *val2,
enum iio_chan_info_enum attribute)
{
int ret;
mutex_lock(&chan->indio_dev->info_exist_lock);
if (chan->indio_dev->info == NULL) {
ret = -ENODEV;
goto err_unlock;
}
ret = iio_channel_read(chan, val, val2, attribute);
err_unlock:
mutex_unlock(&chan->indio_dev->info_exist_lock);
return ret;
}
int iio_read_channel_offset(struct iio_channel *chan, int *val, int *val2)
{
return iio_read_channel_attribute(chan, val, val2, IIO_CHAN_INFO_OFFSET);
}
EXPORT_SYMBOL_GPL(iio_read_channel_offset);
int iio_read_channel_processed(struct iio_channel *chan, int *val)
{
int ret;
@ -687,19 +712,7 @@ EXPORT_SYMBOL_GPL(iio_read_channel_processed);
int iio_read_channel_scale(struct iio_channel *chan, int *val, int *val2)
{
int ret;
mutex_lock(&chan->indio_dev->info_exist_lock);
if (chan->indio_dev->info == NULL) {
ret = -ENODEV;
goto err_unlock;
}
ret = iio_channel_read(chan, val, val2, IIO_CHAN_INFO_SCALE);
err_unlock:
mutex_unlock(&chan->indio_dev->info_exist_lock);
return ret;
return iio_read_channel_attribute(chan, val, val2, IIO_CHAN_INFO_SCALE);
}
EXPORT_SYMBOL_GPL(iio_read_channel_scale);

View file

@ -140,6 +140,18 @@ config GP2AP020A00F
To compile this driver as a module, choose M here: the
module will be called gp2ap020a00f.
config SENSORS_ISL29018
tristate "Intersil 29018 light and proximity sensor"
depends on I2C
select REGMAP_I2C
default n
help
If you say yes here you get support for ambient light sensing and
proximity infrared sensing from Intersil ISL29018.
This driver will provide the measurements of ambient light intensity
in lux, proximity infrared sensing and normal infrared sensing.
Data from sensor is accessible via sysfs.
config ISL29125
tristate "Intersil ISL29125 digital color light sensor"
depends on I2C

View file

@ -17,6 +17,7 @@ obj-$(CONFIG_CM36651) += cm36651.o
obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o
obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o
obj-$(CONFIG_HID_SENSOR_PROX) += hid-sensor-prox.o
obj-$(CONFIG_SENSORS_ISL29018) += isl29018.o
obj-$(CONFIG_ISL29125) += isl29125.o
obj-$(CONFIG_JSA1212) += jsa1212.o
obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o

View file

@ -0,0 +1,847 @@
/*
* A iio driver for the light sensor ISL 29018/29023/29035.
*
* IIO driver for monitoring ambient light intensity in luxi, proximity
* sensing and infrared sensing.
*
* Copyright (c) 2010, NVIDIA Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/acpi.h>
#define ISL29018_CONV_TIME_MS 100
#define ISL29018_REG_ADD_COMMAND1 0x00
#define ISL29018_CMD1_OPMODE_SHIFT 5
#define ISL29018_CMD1_OPMODE_MASK (7 << ISL29018_CMD1_OPMODE_SHIFT)
#define ISL29018_CMD1_OPMODE_POWER_DOWN 0
#define ISL29018_CMD1_OPMODE_ALS_ONCE 1
#define ISL29018_CMD1_OPMODE_IR_ONCE 2
#define ISL29018_CMD1_OPMODE_PROX_ONCE 3
#define ISL29018_REG_ADD_COMMAND2 0x01
#define ISL29018_CMD2_RESOLUTION_SHIFT 2
#define ISL29018_CMD2_RESOLUTION_MASK (0x3 << ISL29018_CMD2_RESOLUTION_SHIFT)
#define ISL29018_CMD2_RANGE_SHIFT 0
#define ISL29018_CMD2_RANGE_MASK (0x3 << ISL29018_CMD2_RANGE_SHIFT)
#define ISL29018_CMD2_SCHEME_SHIFT 7
#define ISL29018_CMD2_SCHEME_MASK (0x1 << ISL29018_CMD2_SCHEME_SHIFT)
#define ISL29018_REG_ADD_DATA_LSB 0x02
#define ISL29018_REG_ADD_DATA_MSB 0x03
#define ISL29018_REG_TEST 0x08
#define ISL29018_TEST_SHIFT 0
#define ISL29018_TEST_MASK (0xFF << ISL29018_TEST_SHIFT)
#define ISL29035_REG_DEVICE_ID 0x0F
#define ISL29035_DEVICE_ID_SHIFT 0x03
#define ISL29035_DEVICE_ID_MASK (0x7 << ISL29035_DEVICE_ID_SHIFT)
#define ISL29035_DEVICE_ID 0x5
#define ISL29035_BOUT_SHIFT 0x07
#define ISL29035_BOUT_MASK (0x01 << ISL29035_BOUT_SHIFT)
enum isl29018_int_time {
ISL29018_INT_TIME_16,
ISL29018_INT_TIME_12,
ISL29018_INT_TIME_8,
ISL29018_INT_TIME_4,
};
static const unsigned int isl29018_int_utimes[3][4] = {
{90000, 5630, 351, 21},
{90000, 5600, 352, 22},
{105000, 6500, 410, 25},
};
static const struct isl29018_scale {
unsigned int scale;
unsigned int uscale;
} isl29018_scales[4][4] = {
{ {0, 15258}, {0, 61035}, {0, 244140}, {0, 976562} },
{ {0, 244140}, {0, 976562}, {3, 906250}, {15, 625000} },
{ {3, 906250}, {15, 625000}, {62, 500000}, {250, 0} },
{ {62, 500000}, {250, 0}, {1000, 0}, {4000, 0} }
};
struct isl29018_chip {
struct regmap *regmap;
struct mutex lock;
int type;
unsigned int calibscale;
unsigned int ucalibscale;
unsigned int int_time;
struct isl29018_scale scale;
int prox_scheme;
bool suspended;
};
static int isl29018_set_integration_time(struct isl29018_chip *chip,
unsigned int utime)
{
unsigned int i;
int ret;
unsigned int int_time, new_int_time;
for (i = 0; i < ARRAY_SIZE(isl29018_int_utimes[chip->type]); ++i) {
if (utime == isl29018_int_utimes[chip->type][i]) {
new_int_time = i;
break;
}
}
if (i >= ARRAY_SIZE(isl29018_int_utimes[chip->type]))
return -EINVAL;
ret = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMAND2,
ISL29018_CMD2_RESOLUTION_MASK,
i << ISL29018_CMD2_RESOLUTION_SHIFT);
if (ret < 0)
return ret;
/* Keep the same range when integration time changes */
int_time = chip->int_time;
for (i = 0; i < ARRAY_SIZE(isl29018_scales[int_time]); ++i) {
if (chip->scale.scale == isl29018_scales[int_time][i].scale &&
chip->scale.uscale == isl29018_scales[int_time][i].uscale) {
chip->scale = isl29018_scales[new_int_time][i];
break;
}
}
chip->int_time = new_int_time;
return 0;
}
static int isl29018_set_scale(struct isl29018_chip *chip, int scale, int uscale)
{
unsigned int i;
int ret;
struct isl29018_scale new_scale;
for (i = 0; i < ARRAY_SIZE(isl29018_scales[chip->int_time]); ++i) {
if (scale == isl29018_scales[chip->int_time][i].scale &&
uscale == isl29018_scales[chip->int_time][i].uscale) {
new_scale = isl29018_scales[chip->int_time][i];
break;
}
}
if (i >= ARRAY_SIZE(isl29018_scales[chip->int_time]))
return -EINVAL;
ret = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMAND2,
ISL29018_CMD2_RANGE_MASK,
i << ISL29018_CMD2_RANGE_SHIFT);
if (ret < 0)
return ret;
chip->scale = new_scale;
return 0;
}
static int isl29018_read_sensor_input(struct isl29018_chip *chip, int mode)
{
int status;
unsigned int lsb;
unsigned int msb;
struct device *dev = regmap_get_device(chip->regmap);
/* Set mode */
status = regmap_write(chip->regmap, ISL29018_REG_ADD_COMMAND1,
mode << ISL29018_CMD1_OPMODE_SHIFT);
if (status) {
dev_err(dev,
"Error in setting operating mode err %d\n", status);
return status;
}
msleep(ISL29018_CONV_TIME_MS);
status = regmap_read(chip->regmap, ISL29018_REG_ADD_DATA_LSB, &lsb);
if (status < 0) {
dev_err(dev,
"Error in reading LSB DATA with err %d\n", status);
return status;
}
status = regmap_read(chip->regmap, ISL29018_REG_ADD_DATA_MSB, &msb);
if (status < 0) {
dev_err(dev,
"Error in reading MSB DATA with error %d\n", status);
return status;
}
dev_vdbg(dev, "MSB 0x%x and LSB 0x%x\n", msb, lsb);
return (msb << 8) | lsb;
}
static int isl29018_read_lux(struct isl29018_chip *chip, int *lux)
{
int lux_data;
unsigned int data_x_range;
lux_data = isl29018_read_sensor_input(chip,
ISL29018_CMD1_OPMODE_ALS_ONCE);
if (lux_data < 0)
return lux_data;
data_x_range = lux_data * chip->scale.scale +
lux_data * chip->scale.uscale / 1000000;
*lux = data_x_range * chip->calibscale +
data_x_range * chip->ucalibscale / 1000000;
return 0;
}
static int isl29018_read_ir(struct isl29018_chip *chip, int *ir)
{
int ir_data;
ir_data = isl29018_read_sensor_input(chip,
ISL29018_CMD1_OPMODE_IR_ONCE);
if (ir_data < 0)
return ir_data;
*ir = ir_data;
return 0;
}
static int isl29018_read_proximity_ir(struct isl29018_chip *chip, int scheme,
int *near_ir)
{
int status;
int prox_data = -1;
int ir_data = -1;
struct device *dev = regmap_get_device(chip->regmap);
/* Do proximity sensing with required scheme */
status = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMAND2,
ISL29018_CMD2_SCHEME_MASK,
scheme << ISL29018_CMD2_SCHEME_SHIFT);
if (status) {
dev_err(dev, "Error in setting operating mode\n");
return status;
}
prox_data = isl29018_read_sensor_input(chip,
ISL29018_CMD1_OPMODE_PROX_ONCE);
if (prox_data < 0)
return prox_data;
if (scheme == 1) {
*near_ir = prox_data;
return 0;
}
ir_data = isl29018_read_sensor_input(chip,
ISL29018_CMD1_OPMODE_IR_ONCE);
if (ir_data < 0)
return ir_data;
if (prox_data >= ir_data)
*near_ir = prox_data - ir_data;
else
*near_ir = 0;
return 0;
}
static ssize_t in_illuminance_scale_available_show
(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct isl29018_chip *chip = iio_priv(indio_dev);
unsigned int i;
int len = 0;
mutex_lock(&chip->lock);
for (i = 0; i < ARRAY_SIZE(isl29018_scales[chip->int_time]); ++i)
len += sprintf(buf + len, "%d.%06d ",
isl29018_scales[chip->int_time][i].scale,
isl29018_scales[chip->int_time][i].uscale);
mutex_unlock(&chip->lock);
buf[len - 1] = '\n';
return len;
}
static ssize_t in_illuminance_integration_time_available_show
(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct isl29018_chip *chip = iio_priv(indio_dev);
unsigned int i;
int len = 0;
for (i = 0; i < ARRAY_SIZE(isl29018_int_utimes[chip->type]); ++i)
len += sprintf(buf + len, "0.%06d ",
isl29018_int_utimes[chip->type][i]);
buf[len - 1] = '\n';
return len;
}
/*
* From ISL29018 Data Sheet (FN6619.4, Oct 8, 2012) regarding the
* infrared suppression:
*
* Proximity Sensing Scheme: Bit 7. This bit programs the function
* of the proximity detection. Logic 0 of this bit, Scheme 0, makes
* full n (4, 8, 12, 16) bits (unsigned) proximity detection. The range
* of Scheme 0 proximity count is from 0 to 2^n. Logic 1 of this bit,
* Scheme 1, makes n-1 (3, 7, 11, 15) bits (2's complementary)
* proximity_less_ambient detection. The range of Scheme 1
* proximity count is from -2^(n-1) to 2^(n-1) . The sign bit is extended
* for resolutions less than 16. While Scheme 0 has wider dynamic
* range, Scheme 1 proximity detection is less affected by the
* ambient IR noise variation.
*
* 0 Sensing IR from LED and ambient
* 1 Sensing IR from LED with ambient IR rejection
*/
static ssize_t proximity_on_chip_ambient_infrared_suppression_show
(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct isl29018_chip *chip = iio_priv(indio_dev);
/*
* Return the "proximity scheme" i.e. if the chip does on chip
* infrared suppression (1 means perform on chip suppression)
*/
return sprintf(buf, "%d\n", chip->prox_scheme);
}
static ssize_t proximity_on_chip_ambient_infrared_suppression_store
(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct isl29018_chip *chip = iio_priv(indio_dev);
int val;
if (kstrtoint(buf, 10, &val))
return -EINVAL;
if (!(val == 0 || val == 1))
return -EINVAL;
/*
* Get the "proximity scheme" i.e. if the chip does on chip
* infrared suppression (1 means perform on chip suppression)
*/
mutex_lock(&chip->lock);
chip->prox_scheme = val;
mutex_unlock(&chip->lock);
return count;
}
static int isl29018_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val,
int val2,
long mask)
{
struct isl29018_chip *chip = iio_priv(indio_dev);
int ret = -EINVAL;
mutex_lock(&chip->lock);
if (chip->suspended) {
ret = -EBUSY;
goto write_done;
}
switch (mask) {
case IIO_CHAN_INFO_CALIBSCALE:
if (chan->type == IIO_LIGHT) {
chip->calibscale = val;
chip->ucalibscale = val2;
ret = 0;
}
break;
case IIO_CHAN_INFO_INT_TIME:
if (chan->type == IIO_LIGHT && !val)
ret = isl29018_set_integration_time(chip, val2);
break;
case IIO_CHAN_INFO_SCALE:
if (chan->type == IIO_LIGHT)
ret = isl29018_set_scale(chip, val, val2);
break;
default:
break;
}
write_done:
mutex_unlock(&chip->lock);
return ret;
}
static int isl29018_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val,
int *val2,
long mask)
{
int ret = -EINVAL;
struct isl29018_chip *chip = iio_priv(indio_dev);
mutex_lock(&chip->lock);
if (chip->suspended) {
ret = -EBUSY;
goto read_done;
}
switch (mask) {
case IIO_CHAN_INFO_RAW:
case IIO_CHAN_INFO_PROCESSED:
switch (chan->type) {
case IIO_LIGHT:
ret = isl29018_read_lux(chip, val);
break;
case IIO_INTENSITY:
ret = isl29018_read_ir(chip, val);
break;
case IIO_PROXIMITY:
ret = isl29018_read_proximity_ir(chip,
chip->prox_scheme,
val);
break;
default:
break;
}
if (!ret)
ret = IIO_VAL_INT;
break;
case IIO_CHAN_INFO_INT_TIME:
if (chan->type == IIO_LIGHT) {
*val = 0;
*val2 = isl29018_int_utimes[chip->type][chip->int_time];
ret = IIO_VAL_INT_PLUS_MICRO;
}
break;
case IIO_CHAN_INFO_SCALE:
if (chan->type == IIO_LIGHT) {
*val = chip->scale.scale;
*val2 = chip->scale.uscale;
ret = IIO_VAL_INT_PLUS_MICRO;
}
break;
case IIO_CHAN_INFO_CALIBSCALE:
if (chan->type == IIO_LIGHT) {
*val = chip->calibscale;
*val2 = chip->ucalibscale;
ret = IIO_VAL_INT_PLUS_MICRO;
}
break;
default:
break;
}
read_done:
mutex_unlock(&chip->lock);
return ret;
}
#define ISL29018_LIGHT_CHANNEL { \
.type = IIO_LIGHT, \
.indexed = 1, \
.channel = 0, \
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | \
BIT(IIO_CHAN_INFO_CALIBSCALE) | \
BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_INT_TIME), \
}
#define ISL29018_IR_CHANNEL { \
.type = IIO_INTENSITY, \
.modified = 1, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.channel2 = IIO_MOD_LIGHT_IR, \
}
#define ISL29018_PROXIMITY_CHANNEL { \
.type = IIO_PROXIMITY, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
}
static const struct iio_chan_spec isl29018_channels[] = {
ISL29018_LIGHT_CHANNEL,
ISL29018_IR_CHANNEL,
ISL29018_PROXIMITY_CHANNEL,
};
static const struct iio_chan_spec isl29023_channels[] = {
ISL29018_LIGHT_CHANNEL,
ISL29018_IR_CHANNEL,
};
static IIO_DEVICE_ATTR_RO(in_illuminance_integration_time_available, 0);
static IIO_DEVICE_ATTR_RO(in_illuminance_scale_available, 0);
static IIO_DEVICE_ATTR_RW(proximity_on_chip_ambient_infrared_suppression, 0);
#define ISL29018_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr)
static struct attribute *isl29018_attributes[] = {
ISL29018_DEV_ATTR(in_illuminance_scale_available),
ISL29018_DEV_ATTR(in_illuminance_integration_time_available),
ISL29018_DEV_ATTR(proximity_on_chip_ambient_infrared_suppression),
NULL
};
static struct attribute *isl29023_attributes[] = {
ISL29018_DEV_ATTR(in_illuminance_scale_available),
ISL29018_DEV_ATTR(in_illuminance_integration_time_available),
NULL
};
static const struct attribute_group isl29018_group = {
.attrs = isl29018_attributes,
};
static const struct attribute_group isl29023_group = {
.attrs = isl29023_attributes,
};
enum {
isl29018,
isl29023,
isl29035,
};
static int isl29018_chip_init(struct isl29018_chip *chip)
{
int status;
struct device *dev = regmap_get_device(chip->regmap);
if (chip->type == isl29035) {
unsigned int id;
status = regmap_read(chip->regmap, ISL29035_REG_DEVICE_ID, &id);
if (status < 0) {
dev_err(dev,
"Error reading ID register with error %d\n",
status);
return status;
}
id = (id & ISL29035_DEVICE_ID_MASK) >> ISL29035_DEVICE_ID_SHIFT;
if (id != ISL29035_DEVICE_ID)
return -ENODEV;
/* Clear brownout bit */
status = regmap_update_bits(chip->regmap,
ISL29035_REG_DEVICE_ID,
ISL29035_BOUT_MASK, 0);
if (status < 0)
return status;
}
/*
* Code added per Intersil Application Note 1534:
* When VDD sinks to approximately 1.8V or below, some of
* the part's registers may change their state. When VDD
* recovers to 2.25V (or greater), the part may thus be in an
* unknown mode of operation. The user can return the part to
* a known mode of operation either by (a) setting VDD = 0V for
* 1 second or more and then powering back up with a slew rate
* of 0.5V/ms or greater, or (b) via I2C disable all ALS/PROX
* conversions, clear the test registers, and then rewrite all
* registers to the desired values.
* ...
* For ISL29011, ISL29018, ISL29021, ISL29023
* 1. Write 0x00 to register 0x08 (TEST)
* 2. Write 0x00 to register 0x00 (CMD1)
* 3. Rewrite all registers to the desired values
*
* ISL29018 Data Sheet (FN6619.1, Feb 11, 2010) essentially says
* the same thing EXCEPT the data sheet asks for a 1ms delay after
* writing the CMD1 register.
*/
status = regmap_write(chip->regmap, ISL29018_REG_TEST, 0x0);
if (status < 0) {
dev_err(dev, "Failed to clear isl29018 TEST reg.(%d)\n",
status);
return status;
}
/*
* See Intersil AN1534 comments above.
* "Operating Mode" (COMMAND1) register is reprogrammed when
* data is read from the device.
*/
status = regmap_write(chip->regmap, ISL29018_REG_ADD_COMMAND1, 0);
if (status < 0) {
dev_err(dev, "Failed to clear isl29018 CMD1 reg.(%d)\n",
status);
return status;
}
usleep_range(1000, 2000); /* per data sheet, page 10 */
/* Set defaults */
status = isl29018_set_scale(chip, chip->scale.scale,
chip->scale.uscale);
if (status < 0) {
dev_err(dev, "Init of isl29018 fails\n");
return status;
}
status = isl29018_set_integration_time(chip,
isl29018_int_utimes[chip->type][chip->int_time]);
if (status < 0)
dev_err(dev, "Init of isl29018 fails\n");
return status;
}
static const struct iio_info isl29018_info = {
.attrs = &isl29018_group,
.driver_module = THIS_MODULE,
.read_raw = isl29018_read_raw,
.write_raw = isl29018_write_raw,
};
static const struct iio_info isl29023_info = {
.attrs = &isl29023_group,
.driver_module = THIS_MODULE,
.read_raw = isl29018_read_raw,
.write_raw = isl29018_write_raw,
};
static bool isl29018_is_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case ISL29018_REG_ADD_DATA_LSB:
case ISL29018_REG_ADD_DATA_MSB:
case ISL29018_REG_ADD_COMMAND1:
case ISL29018_REG_TEST:
case ISL29035_REG_DEVICE_ID:
return true;
default:
return false;
}
}
static const struct regmap_config isl29018_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.volatile_reg = isl29018_is_volatile_reg,
.max_register = ISL29018_REG_TEST,
.num_reg_defaults_raw = ISL29018_REG_TEST + 1,
.cache_type = REGCACHE_RBTREE,
};
static const struct regmap_config isl29035_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.volatile_reg = isl29018_is_volatile_reg,
.max_register = ISL29035_REG_DEVICE_ID,
.num_reg_defaults_raw = ISL29035_REG_DEVICE_ID + 1,
.cache_type = REGCACHE_RBTREE,
};
struct isl29018_chip_info {
const struct iio_chan_spec *channels;
int num_channels;
const struct iio_info *indio_info;
const struct regmap_config *regmap_cfg;
};
static const struct isl29018_chip_info isl29018_chip_info_tbl[] = {
[isl29018] = {
.channels = isl29018_channels,
.num_channels = ARRAY_SIZE(isl29018_channels),
.indio_info = &isl29018_info,
.regmap_cfg = &isl29018_regmap_config,
},
[isl29023] = {
.channels = isl29023_channels,
.num_channels = ARRAY_SIZE(isl29023_channels),
.indio_info = &isl29023_info,
.regmap_cfg = &isl29018_regmap_config,
},
[isl29035] = {
.channels = isl29023_channels,
.num_channels = ARRAY_SIZE(isl29023_channels),
.indio_info = &isl29023_info,
.regmap_cfg = &isl29035_regmap_config,
},
};
static const char *isl29018_match_acpi_device(struct device *dev, int *data)
{
const struct acpi_device_id *id;
id = acpi_match_device(dev->driver->acpi_match_table, dev);
if (!id)
return NULL;
*data = (int)id->driver_data;
return dev_name(dev);
}
static int isl29018_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct isl29018_chip *chip;
struct iio_dev *indio_dev;
int err;
const char *name = NULL;
int dev_id = 0;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
if (!indio_dev)
return -ENOMEM;
chip = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
if (id) {
name = id->name;
dev_id = id->driver_data;
}
if (ACPI_HANDLE(&client->dev))
name = isl29018_match_acpi_device(&client->dev, &dev_id);
mutex_init(&chip->lock);
chip->type = dev_id;
chip->calibscale = 1;
chip->ucalibscale = 0;
chip->int_time = ISL29018_INT_TIME_16;
chip->scale = isl29018_scales[chip->int_time][0];
chip->suspended = false;
chip->regmap = devm_regmap_init_i2c(client,
isl29018_chip_info_tbl[dev_id].regmap_cfg);
if (IS_ERR(chip->regmap)) {
err = PTR_ERR(chip->regmap);
dev_err(&client->dev, "regmap initialization fails: %d\n", err);
return err;
}
err = isl29018_chip_init(chip);
if (err)
return err;
indio_dev->info = isl29018_chip_info_tbl[dev_id].indio_info;
indio_dev->channels = isl29018_chip_info_tbl[dev_id].channels;
indio_dev->num_channels = isl29018_chip_info_tbl[dev_id].num_channels;
indio_dev->name = name;
indio_dev->dev.parent = &client->dev;
indio_dev->modes = INDIO_DIRECT_MODE;
return devm_iio_device_register(&client->dev, indio_dev);
}
#ifdef CONFIG_PM_SLEEP
static int isl29018_suspend(struct device *dev)
{
struct isl29018_chip *chip = iio_priv(dev_get_drvdata(dev));
mutex_lock(&chip->lock);
/*
* Since this driver uses only polling commands, we are by default in
* auto shutdown (ie, power-down) mode.
* So we do not have much to do here.
*/
chip->suspended = true;
mutex_unlock(&chip->lock);
return 0;
}
static int isl29018_resume(struct device *dev)
{
struct isl29018_chip *chip = iio_priv(dev_get_drvdata(dev));
int err;
mutex_lock(&chip->lock);
err = isl29018_chip_init(chip);
if (!err)
chip->suspended = false;
mutex_unlock(&chip->lock);
return err;
}
static SIMPLE_DEV_PM_OPS(isl29018_pm_ops, isl29018_suspend, isl29018_resume);
#define ISL29018_PM_OPS (&isl29018_pm_ops)
#else
#define ISL29018_PM_OPS NULL
#endif
static const struct acpi_device_id isl29018_acpi_match[] = {
{"ISL29018", isl29018},
{"ISL29023", isl29023},
{"ISL29035", isl29035},
{},
};
MODULE_DEVICE_TABLE(acpi, isl29018_acpi_match);
static const struct i2c_device_id isl29018_id[] = {
{"isl29018", isl29018},
{"isl29023", isl29023},
{"isl29035", isl29035},
{}
};
MODULE_DEVICE_TABLE(i2c, isl29018_id);
static const struct of_device_id isl29018_of_match[] = {
{ .compatible = "isil,isl29018", },
{ .compatible = "isil,isl29023", },
{ .compatible = "isil,isl29035", },
{ },
};
MODULE_DEVICE_TABLE(of, isl29018_of_match);
static struct i2c_driver isl29018_driver = {
.driver = {
.name = "isl29018",
.acpi_match_table = ACPI_PTR(isl29018_acpi_match),
.pm = ISL29018_PM_OPS,
.of_match_table = isl29018_of_match,
},
.probe = isl29018_probe,
.id_table = isl29018_id,
};
module_i2c_driver(isl29018_driver);
MODULE_DESCRIPTION("ISL29018 Ambient Light Sensor driver");
MODULE_LICENSE("GPL");

View file

@ -631,14 +631,16 @@ static int ltr501_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_PROCESSED:
if (iio_buffer_enabled(indio_dev))
return -EBUSY;
switch (chan->type) {
case IIO_LIGHT:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
mutex_lock(&data->lock_als);
ret = ltr501_read_als(data, buf);
mutex_unlock(&data->lock_als);
iio_device_release_direct_mode(indio_dev);
if (ret < 0)
return ret;
*val = ltr501_calculate_lux(le16_to_cpu(buf[1]),
@ -648,8 +650,9 @@ static int ltr501_read_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
case IIO_CHAN_INFO_RAW:
if (iio_buffer_enabled(indio_dev))
return -EBUSY;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
switch (chan->type) {
case IIO_INTENSITY:
@ -657,21 +660,28 @@ static int ltr501_read_raw(struct iio_dev *indio_dev,
ret = ltr501_read_als(data, buf);
mutex_unlock(&data->lock_als);
if (ret < 0)
return ret;
break;
*val = le16_to_cpu(chan->address == LTR501_ALS_DATA1 ?
buf[0] : buf[1]);
return IIO_VAL_INT;
ret = IIO_VAL_INT;
break;
case IIO_PROXIMITY:
mutex_lock(&data->lock_ps);
ret = ltr501_read_ps(data);
mutex_unlock(&data->lock_ps);
if (ret < 0)
return ret;
break;
*val = ret & LTR501_PS_DATA_MASK;
return IIO_VAL_INT;
ret = IIO_VAL_INT;
break;
default:
return -EINVAL;
ret = -EINVAL;
break;
}
iio_device_release_direct_mode(indio_dev);
return ret;
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_INTENSITY:
@ -729,8 +739,9 @@ static int ltr501_write_raw(struct iio_dev *indio_dev,
int i, ret, freq_val, freq_val2;
struct ltr501_chip_info *info = data->chip_info;
if (iio_buffer_enabled(indio_dev))
return -EBUSY;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
switch (mask) {
case IIO_CHAN_INFO_SCALE:
@ -739,85 +750,105 @@ static int ltr501_write_raw(struct iio_dev *indio_dev,
i = ltr501_get_gain_index(info->als_gain,
info->als_gain_tbl_size,
val, val2);
if (i < 0)
return -EINVAL;
if (i < 0) {
ret = -EINVAL;
break;
}
data->als_contr &= ~info->als_gain_mask;
data->als_contr |= i << info->als_gain_shift;
return regmap_write(data->regmap, LTR501_ALS_CONTR,
data->als_contr);
ret = regmap_write(data->regmap, LTR501_ALS_CONTR,
data->als_contr);
break;
case IIO_PROXIMITY:
i = ltr501_get_gain_index(info->ps_gain,
info->ps_gain_tbl_size,
val, val2);
if (i < 0)
return -EINVAL;
if (i < 0) {
ret = -EINVAL;
break;
}
data->ps_contr &= ~LTR501_CONTR_PS_GAIN_MASK;
data->ps_contr |= i << LTR501_CONTR_PS_GAIN_SHIFT;
return regmap_write(data->regmap, LTR501_PS_CONTR,
data->ps_contr);
ret = regmap_write(data->regmap, LTR501_PS_CONTR,
data->ps_contr);
break;
default:
return -EINVAL;
ret = -EINVAL;
break;
}
break;
case IIO_CHAN_INFO_INT_TIME:
switch (chan->type) {
case IIO_INTENSITY:
if (val != 0)
return -EINVAL;
if (val != 0) {
ret = -EINVAL;
break;
}
mutex_lock(&data->lock_als);
i = ltr501_set_it_time(data, val2);
ret = ltr501_set_it_time(data, val2);
mutex_unlock(&data->lock_als);
return i;
break;
default:
return -EINVAL;
ret = -EINVAL;
break;
}
break;
case IIO_CHAN_INFO_SAMP_FREQ:
switch (chan->type) {
case IIO_INTENSITY:
ret = ltr501_als_read_samp_freq(data, &freq_val,
&freq_val2);
if (ret < 0)
return ret;
break;
ret = ltr501_als_write_samp_freq(data, val, val2);
if (ret < 0)
return ret;
break;
/* update persistence count when changing frequency */
ret = ltr501_write_intr_prst(data, chan->type,
0, data->als_period);
if (ret < 0)
return ltr501_als_write_samp_freq(data,
freq_val,
freq_val2);
return ret;
ret = ltr501_als_write_samp_freq(data, freq_val,
freq_val2);
break;
case IIO_PROXIMITY:
ret = ltr501_ps_read_samp_freq(data, &freq_val,
&freq_val2);
if (ret < 0)
return ret;
break;
ret = ltr501_ps_write_samp_freq(data, val, val2);
if (ret < 0)
return ret;
break;
/* update persistence count when changing frequency */
ret = ltr501_write_intr_prst(data, chan->type,
0, data->ps_period);
if (ret < 0)
return ltr501_ps_write_samp_freq(data,
freq_val,
freq_val2);
return ret;
ret = ltr501_ps_write_samp_freq(data, freq_val,
freq_val2);
break;
default:
return -EINVAL;
ret = -EINVAL;
break;
}
break;
default:
ret = -EINVAL;
break;
}
return -EINVAL;
iio_device_release_direct_mode(indio_dev);
return ret;
}
static int ltr501_read_thresh(struct iio_dev *indio_dev,

View file

@ -204,17 +204,18 @@ static int max44000_write_alspga(struct max44000_data *data, int val)
static int max44000_read_alsval(struct max44000_data *data)
{
u16 regval;
__be16 val;
int alstim, ret;
ret = regmap_bulk_read(data->regmap, MAX44000_REG_ALS_DATA_HI,
&regval, sizeof(regval));
&val, sizeof(val));
if (ret < 0)
return ret;
alstim = ret = max44000_read_alstim(data);
if (ret < 0)
return ret;
regval = be16_to_cpu(regval);
regval = be16_to_cpu(val);
/*
* Overflow is explained on datasheet page 17.

View file

@ -287,7 +287,7 @@ static int ak8974_await_drdy(struct ak8974 *ak8974)
return 0;
}
static int ak8974_getresult(struct ak8974 *ak8974, s16 *result)
static int ak8974_getresult(struct ak8974 *ak8974, __le16 *result)
{
unsigned int src;
int ret;
@ -395,7 +395,7 @@ static int ak8974_selftest(struct ak8974 *ak8974)
static int ak8974_get_u16_val(struct ak8974 *ak8974, u8 reg, u16 *val)
{
int ret;
u16 bulk;
__le16 bulk;
ret = regmap_bulk_read(ak8974->map, reg, &bulk, 2);
if (ret)
@ -453,7 +453,7 @@ static int ak8974_read_raw(struct iio_dev *indio_dev,
long mask)
{
struct ak8974 *ak8974 = iio_priv(indio_dev);
s16 hw_values[3];
__le16 hw_values[3];
int ret = -EINVAL;
pm_runtime_get_sync(&ak8974->i2c->dev);
@ -494,7 +494,7 @@ static void ak8974_fill_buffer(struct iio_dev *indio_dev)
{
struct ak8974 *ak8974 = iio_priv(indio_dev);
int ret;
s16 hw_values[8]; /* Three axes + 64bit padding */
__le16 hw_values[8]; /* Three axes + 64bit padding */
pm_runtime_get_sync(&ak8974->i2c->dev);
mutex_lock(&ak8974->lock);

View file

@ -690,6 +690,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
struct ak8975_data *data = iio_priv(indio_dev);
const struct i2c_client *client = data->client;
const struct ak_def *def = data->def;
__le16 rval;
u16 buff;
int ret;
@ -703,7 +704,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
ret = i2c_smbus_read_i2c_block_data_or_emulated(
client, def->data_regs[index],
sizeof(buff), (u8*)&buff);
sizeof(rval), (u8*)&rval);
if (ret < 0)
goto exit;
@ -713,7 +714,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
pm_runtime_put_autosuspend(&data->client->dev);
/* Swap bytes and convert to valid range. */
buff = le16_to_cpu(buff);
buff = le16_to_cpu(rval);
*val = clamp_t(s16, buff, -def->range, def->range);
return IIO_VAL_INT;
@ -813,6 +814,7 @@ static void ak8975_fill_buffer(struct iio_dev *indio_dev)
const struct ak_def *def = data->def;
int ret;
s16 buff[8]; /* 3 x 16 bits axis values + 1 aligned 64 bits timestamp */
__le16 fval[3];
mutex_lock(&data->lock);
@ -826,17 +828,17 @@ static void ak8975_fill_buffer(struct iio_dev *indio_dev)
*/
ret = i2c_smbus_read_i2c_block_data_or_emulated(client,
def->data_regs[0],
3 * sizeof(buff[0]),
(u8 *)buff);
3 * sizeof(fval[0]),
(u8 *)fval);
if (ret < 0)
goto unlock;
mutex_unlock(&data->lock);
/* Clamp to valid range. */
buff[0] = clamp_t(s16, le16_to_cpu(buff[0]), -def->range, def->range);
buff[1] = clamp_t(s16, le16_to_cpu(buff[1]), -def->range, def->range);
buff[2] = clamp_t(s16, le16_to_cpu(buff[2]), -def->range, def->range);
buff[0] = clamp_t(s16, le16_to_cpu(fval[0]), -def->range, def->range);
buff[1] = clamp_t(s16, le16_to_cpu(fval[1]), -def->range, def->range);
buff[2] = clamp_t(s16, le16_to_cpu(fval[2]), -def->range, def->range);
iio_push_to_buffers_with_timestamp(indio_dev, buff,
iio_get_time_ns(indio_dev));

View file

@ -0,0 +1,22 @@
#
# Potentiostat drivers
#
# When adding new entries keep the list in alphabetical order
menu "Digital potentiostats"
config LMP91000
tristate "Texas Instruments LMP91000 potentiostat driver"
depends on I2C
select REGMAP_I2C
select IIO_BUFFER
select IIO_BUFFER_CB
select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for the Texas Instruments
LMP91000 digital potentiostat chip.
To compile this driver as a module, choose M here: the
module will be called lmp91000
endmenu

View file

@ -0,0 +1,6 @@
#
# Makefile for industrial I/O potentiostat drivers
#
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_LMP91000) += lmp91000.o

View file

@ -0,0 +1,446 @@
/*
* lmp91000.c - Support for Texas Instruments digital potentiostats
*
* Copyright (C) 2016 Matt Ranostay <mranostay@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.
*
* TODO: bias voltage + polarity control, and multiple chip support
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/consumer.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#define LMP91000_REG_LOCK 0x01
#define LMP91000_REG_TIACN 0x10
#define LMP91000_REG_TIACN_GAIN_SHIFT 2
#define LMP91000_REG_REFCN 0x11
#define LMP91000_REG_REFCN_EXT_REF 0x20
#define LMP91000_REG_REFCN_50_ZERO 0x80
#define LMP91000_REG_MODECN 0x12
#define LMP91000_REG_MODECN_3LEAD 0x03
#define LMP91000_REG_MODECN_TEMP 0x07
#define LMP91000_DRV_NAME "lmp91000"
static const int lmp91000_tia_gain[] = { 0, 2750, 3500, 7000, 14000, 35000,
120000, 350000 };
static const int lmp91000_rload[] = { 10, 33, 50, 100 };
#define LMP91000_TEMP_BASE -40
static const u16 lmp91000_temp_lut[] = {
1875, 1867, 1860, 1852, 1844, 1836, 1828, 1821, 1813, 1805,
1797, 1789, 1782, 1774, 1766, 1758, 1750, 1742, 1734, 1727,
1719, 1711, 1703, 1695, 1687, 1679, 1671, 1663, 1656, 1648,
1640, 1632, 1624, 1616, 1608, 1600, 1592, 1584, 1576, 1568,
1560, 1552, 1544, 1536, 1528, 1520, 1512, 1504, 1496, 1488,
1480, 1472, 1464, 1456, 1448, 1440, 1432, 1424, 1415, 1407,
1399, 1391, 1383, 1375, 1367, 1359, 1351, 1342, 1334, 1326,
1318, 1310, 1302, 1293, 1285, 1277, 1269, 1261, 1253, 1244,
1236, 1228, 1220, 1212, 1203, 1195, 1187, 1179, 1170, 1162,
1154, 1146, 1137, 1129, 1121, 1112, 1104, 1096, 1087, 1079,
1071, 1063, 1054, 1046, 1038, 1029, 1021, 1012, 1004, 996,
987, 979, 971, 962, 954, 945, 937, 929, 920, 912,
903, 895, 886, 878, 870, 861 };
static const struct regmap_config lmp91000_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
};
struct lmp91000_data {
struct regmap *regmap;
struct device *dev;
struct iio_trigger *trig;
struct iio_cb_buffer *cb_buffer;
struct iio_channel *adc_chan;
struct completion completion;
u8 chan_select;
u32 buffer[4]; /* 64-bit data + 64-bit timestamp */
};
static const struct iio_chan_spec lmp91000_channels[] = {
{ /* chemical channel mV */
.type = IIO_VOLTAGE,
.channel = 0,
.address = LMP91000_REG_MODECN_3LEAD,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE),
.scan_index = 0,
.scan_type = {
.sign = 's',
.realbits = 32,
.storagebits = 32,
},
},
IIO_CHAN_SOFT_TIMESTAMP(1),
{ /* temperature channel mV */
.type = IIO_TEMP,
.channel = 1,
.address = LMP91000_REG_MODECN_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
.scan_index = -1,
},
};
static int lmp91000_read(struct lmp91000_data *data, int channel, int *val)
{
int state, ret;
ret = regmap_read(data->regmap, LMP91000_REG_MODECN, &state);
if (ret)
return -EINVAL;
ret = regmap_write(data->regmap, LMP91000_REG_MODECN, channel);
if (ret)
return -EINVAL;
/* delay till first temperature reading is complete */
if ((state != channel) && (channel == LMP91000_REG_MODECN_TEMP))
usleep_range(3000, 4000);
data->chan_select = channel != LMP91000_REG_MODECN_3LEAD;
iio_trigger_poll_chained(data->trig);
ret = wait_for_completion_timeout(&data->completion, HZ);
reinit_completion(&data->completion);
if (!ret)
return -ETIMEDOUT;
*val = data->buffer[data->chan_select];
return 0;
}
static irqreturn_t lmp91000_buffer_handler(int irq, void *private)
{
struct iio_poll_func *pf = private;
struct iio_dev *indio_dev = pf->indio_dev;
struct lmp91000_data *data = iio_priv(indio_dev);
int ret, val;
memset(data->buffer, 0, sizeof(data->buffer));
ret = lmp91000_read(data, LMP91000_REG_MODECN_3LEAD, &val);
if (!ret) {
data->buffer[0] = val;
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
iio_get_time_ns(indio_dev));
}
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
static int lmp91000_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct lmp91000_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
case IIO_CHAN_INFO_PROCESSED: {
int ret = iio_channel_start_all_cb(data->cb_buffer);
if (ret)
return ret;
ret = lmp91000_read(data, chan->address, val);
iio_channel_stop_all_cb(data->cb_buffer);
if (ret)
return ret;
if (mask == IIO_CHAN_INFO_PROCESSED) {
int tmp, i;
ret = iio_convert_raw_to_processed(data->adc_chan,
*val, &tmp, 1);
if (ret)
return ret;
for (i = 0; i < ARRAY_SIZE(lmp91000_temp_lut); i++)
if (lmp91000_temp_lut[i] < tmp)
break;
*val = (LMP91000_TEMP_BASE + i) * 1000;
}
return IIO_VAL_INT;
}
case IIO_CHAN_INFO_OFFSET:
return iio_read_channel_offset(data->adc_chan, val, val2);
case IIO_CHAN_INFO_SCALE:
return iio_read_channel_scale(data->adc_chan, val, val2);
}
return -EINVAL;
}
static const struct iio_info lmp91000_info = {
.driver_module = THIS_MODULE,
.read_raw = lmp91000_read_raw,
};
static int lmp91000_read_config(struct lmp91000_data *data)
{
struct device *dev = data->dev;
struct device_node *np = dev->of_node;
unsigned int reg, val;
int i, ret;
ret = of_property_read_u32(np, "ti,tia-gain-ohm", &val);
if (ret) {
if (of_property_read_bool(np, "ti,external-tia-resistor"))
val = 0;
else {
dev_err(dev, "no ti,tia-gain-ohm defined");
return ret;
}
}
ret = -EINVAL;
for (i = 0; i < ARRAY_SIZE(lmp91000_tia_gain); i++) {
if (lmp91000_tia_gain[i] == val) {
reg = i << LMP91000_REG_TIACN_GAIN_SHIFT;
ret = 0;
break;
}
}
if (ret) {
dev_err(dev, "invalid ti,tia-gain-ohm %d\n", val);
return ret;
}
ret = of_property_read_u32(np, "ti,rload-ohm", &val);
if (ret) {
val = 100;
dev_info(dev, "no ti,rload-ohm defined, default to %d\n", val);
}
ret = -EINVAL;
for (i = 0; i < ARRAY_SIZE(lmp91000_rload); i++) {
if (lmp91000_rload[i] == val) {
reg |= i;
ret = 0;
break;
}
}
if (ret) {
dev_err(dev, "invalid ti,rload-ohm %d\n", val);
return ret;
}
regmap_write(data->regmap, LMP91000_REG_LOCK, 0);
regmap_write(data->regmap, LMP91000_REG_TIACN, reg);
regmap_write(data->regmap, LMP91000_REG_REFCN, LMP91000_REG_REFCN_EXT_REF
| LMP91000_REG_REFCN_50_ZERO);
regmap_write(data->regmap, LMP91000_REG_LOCK, 1);
return 0;
}
static int lmp91000_buffer_cb(const void *val, void *private)
{
struct iio_dev *indio_dev = private;
struct lmp91000_data *data = iio_priv(indio_dev);
data->buffer[data->chan_select] = *((int *)val);
complete_all(&data->completion);
return 0;
}
static const struct iio_trigger_ops lmp91000_trigger_ops = {
.owner = THIS_MODULE,
};
static int lmp91000_buffer_preenable(struct iio_dev *indio_dev)
{
struct lmp91000_data *data = iio_priv(indio_dev);
return iio_channel_start_all_cb(data->cb_buffer);
}
static int lmp91000_buffer_predisable(struct iio_dev *indio_dev)
{
struct lmp91000_data *data = iio_priv(indio_dev);
iio_channel_stop_all_cb(data->cb_buffer);
return 0;
}
static const struct iio_buffer_setup_ops lmp91000_buffer_setup_ops = {
.preenable = lmp91000_buffer_preenable,
.postenable = iio_triggered_buffer_postenable,
.predisable = lmp91000_buffer_predisable,
};
static int lmp91000_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct lmp91000_data *data;
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
indio_dev->info = &lmp91000_info;
indio_dev->channels = lmp91000_channels;
indio_dev->num_channels = ARRAY_SIZE(lmp91000_channels);
indio_dev->name = LMP91000_DRV_NAME;
indio_dev->modes = INDIO_DIRECT_MODE;
i2c_set_clientdata(client, indio_dev);
data = iio_priv(indio_dev);
data->dev = dev;
data->regmap = devm_regmap_init_i2c(client, &lmp91000_regmap_config);
if (IS_ERR(data->regmap)) {
dev_err(dev, "regmap initialization failed.\n");
return PTR_ERR(data->regmap);
}
data->trig = devm_iio_trigger_alloc(data->dev, "%s-mux%d",
indio_dev->name, indio_dev->id);
if (!data->trig) {
dev_err(dev, "cannot allocate iio trigger.\n");
return -ENOMEM;
}
data->trig->ops = &lmp91000_trigger_ops;
data->trig->dev.parent = dev;
init_completion(&data->completion);
ret = lmp91000_read_config(data);
if (ret)
return ret;
ret = iio_trigger_set_immutable(iio_channel_cb_get_iio_dev(data->cb_buffer),
data->trig);
if (ret) {
dev_err(dev, "cannot set immutable trigger.\n");
return ret;
}
ret = iio_trigger_register(data->trig);
if (ret) {
dev_err(dev, "cannot register iio trigger.\n");
return ret;
}
ret = iio_triggered_buffer_setup(indio_dev, NULL,
&lmp91000_buffer_handler,
&lmp91000_buffer_setup_ops);
if (ret)
goto error_unreg_trigger;
data->cb_buffer = iio_channel_get_all_cb(dev, &lmp91000_buffer_cb,
indio_dev);
if (IS_ERR(data->cb_buffer)) {
if (PTR_ERR(data->cb_buffer) == -ENODEV)
ret = -EPROBE_DEFER;
else
ret = PTR_ERR(data->cb_buffer);
goto error_unreg_buffer;
}
data->adc_chan = iio_channel_cb_get_channels(data->cb_buffer);
ret = iio_device_register(indio_dev);
if (ret)
goto error_unreg_cb_buffer;
return 0;
error_unreg_cb_buffer:
iio_channel_release_all_cb(data->cb_buffer);
error_unreg_buffer:
iio_triggered_buffer_cleanup(indio_dev);
error_unreg_trigger:
iio_trigger_unregister(data->trig);
return ret;
}
static int lmp91000_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct lmp91000_data *data = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
iio_channel_stop_all_cb(data->cb_buffer);
iio_channel_release_all_cb(data->cb_buffer);
iio_triggered_buffer_cleanup(indio_dev);
iio_trigger_unregister(data->trig);
return 0;
}
static const struct of_device_id lmp91000_of_match[] = {
{ .compatible = "ti,lmp91000", },
{ },
};
MODULE_DEVICE_TABLE(of, lmp91000_of_match);
static const struct i2c_device_id lmp91000_id[] = {
{ "lmp91000", 0 },
{}
};
MODULE_DEVICE_TABLE(i2c, lmp91000_id);
static struct i2c_driver lmp91000_driver = {
.driver = {
.name = LMP91000_DRV_NAME,
.of_match_table = of_match_ptr(lmp91000_of_match),
},
.probe = lmp91000_probe,
.remove = lmp91000_remove,
.id_table = lmp91000_id,
};
module_i2c_driver(lmp91000_driver);
MODULE_AUTHOR("Matt Ranostay <[email protected]>");
MODULE_DESCRIPTION("LMP91000 digital potentiostat");
MODULE_LICENSE("GPL");

View file

@ -5,6 +5,16 @@
menu "Pressure sensors"
config ABP060MG
tristate "Honeywell ABP pressure sensor driver"
depends on I2C
help
Say yes here to build support for the Honeywell ABP pressure
sensors.
To compile this driver as a module, choose M here: the module
will be called abp060mg.
config BMP280
tristate "Bosch Sensortec BMP180/BMP280 pressure sensor I2C driver"
depends on (I2C || SPI_MASTER)

View file

@ -3,6 +3,7 @@
#
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_ABP060MG) += abp060mg.o
obj-$(CONFIG_BMP280) += bmp280.o
bmp280-objs := bmp280-core.o bmp280-regmap.o
obj-$(CONFIG_BMP280_I2C) += bmp280-i2c.o

View file

@ -0,0 +1,276 @@
/*
* Copyright (C) 2016 - Marcin Malagowski <mrc@bourne.st>
*
* 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/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/iio/iio.h>
#define ABP060MG_ERROR_MASK 0xC000
#define ABP060MG_RESP_TIME_MS 40
#define ABP060MG_MIN_COUNTS 1638 /* = 0x0666 (10% of u14) */
#define ABP060MG_MAX_COUNTS 14745 /* = 0x3999 (90% of u14) */
#define ABP060MG_NUM_COUNTS (ABP060MG_MAX_COUNTS - ABP060MG_MIN_COUNTS)
enum abp_variant {
/* gage [kPa] */
ABP006KG, ABP010KG, ABP016KG, ABP025KG, ABP040KG, ABP060KG, ABP100KG,
ABP160KG, ABP250KG, ABP400KG, ABP600KG, ABP001GG,
/* differential [kPa] */
ABP006KD, ABP010KD, ABP016KD, ABP025KD, ABP040KD, ABP060KD, ABP100KD,
ABP160KD, ABP250KD, ABP400KD,
/* gage [psi] */
ABP001PG, ABP005PG, ABP015PG, ABP030PG, ABP060PG, ABP100PG, ABP150PG,
/* differential [psi] */
ABP001PD, ABP005PD, ABP015PD, ABP030PD, ABP060PD,
};
struct abp_config {
int min;
int max;
};
static struct abp_config abp_config[] = {
/* mbar & kPa variants */
[ABP006KG] = { .min = 0, .max = 6000 },
[ABP010KG] = { .min = 0, .max = 10000 },
[ABP016KG] = { .min = 0, .max = 16000 },
[ABP025KG] = { .min = 0, .max = 25000 },
[ABP040KG] = { .min = 0, .max = 40000 },
[ABP060KG] = { .min = 0, .max = 60000 },
[ABP100KG] = { .min = 0, .max = 100000 },
[ABP160KG] = { .min = 0, .max = 160000 },
[ABP250KG] = { .min = 0, .max = 250000 },
[ABP400KG] = { .min = 0, .max = 400000 },
[ABP600KG] = { .min = 0, .max = 600000 },
[ABP001GG] = { .min = 0, .max = 1000000 },
[ABP006KD] = { .min = -6000, .max = 6000 },
[ABP010KD] = { .min = -10000, .max = 10000 },
[ABP016KD] = { .min = -16000, .max = 16000 },
[ABP025KD] = { .min = -25000, .max = 25000 },
[ABP040KD] = { .min = -40000, .max = 40000 },
[ABP060KD] = { .min = -60000, .max = 60000 },
[ABP100KD] = { .min = -100000, .max = 100000 },
[ABP160KD] = { .min = -160000, .max = 160000 },
[ABP250KD] = { .min = -250000, .max = 250000 },
[ABP400KD] = { .min = -400000, .max = 400000 },
/* psi variants (1 psi ~ 6895 Pa) */
[ABP001PG] = { .min = 0, .max = 6985 },
[ABP005PG] = { .min = 0, .max = 34474 },
[ABP015PG] = { .min = 0, .max = 103421 },
[ABP030PG] = { .min = 0, .max = 206843 },
[ABP060PG] = { .min = 0, .max = 413686 },
[ABP100PG] = { .min = 0, .max = 689476 },
[ABP150PG] = { .min = 0, .max = 1034214 },
[ABP001PD] = { .min = -6895, .max = 6895 },
[ABP005PD] = { .min = -34474, .max = 34474 },
[ABP015PD] = { .min = -103421, .max = 103421 },
[ABP030PD] = { .min = -206843, .max = 206843 },
[ABP060PD] = { .min = -413686, .max = 413686 },
};
struct abp_state {
struct i2c_client *client;
struct mutex lock;
/*
* bus-dependent MEASURE_REQUEST length.
* If no SMBUS_QUICK support, need to send dummy byte
*/
int mreq_len;
/* model-dependent values (calculated on probe) */
int scale;
int offset;
};
static const struct iio_chan_spec abp060mg_channels[] = {
{
.type = IIO_PRESSURE,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE),
},
};
static int abp060mg_get_measurement(struct abp_state *state, int *val)
{
struct i2c_client *client = state->client;
__be16 buf[2];
u16 pressure;
int ret;
buf[0] = 0;
ret = i2c_master_send(client, (u8 *)&buf, state->mreq_len);
if (ret < 0)
return ret;
msleep_interruptible(ABP060MG_RESP_TIME_MS);
ret = i2c_master_recv(client, (u8 *)&buf, sizeof(buf));
if (ret < 0)
return ret;
pressure = be16_to_cpu(buf[0]);
if (pressure & ABP060MG_ERROR_MASK)
return -EIO;
if (pressure < ABP060MG_MIN_COUNTS || pressure > ABP060MG_MAX_COUNTS)
return -EIO;
*val = pressure;
return IIO_VAL_INT;
}
static int abp060mg_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
struct abp_state *state = iio_priv(indio_dev);
int ret;
mutex_lock(&state->lock);
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = abp060mg_get_measurement(state, val);
break;
case IIO_CHAN_INFO_OFFSET:
*val = state->offset;
ret = IIO_VAL_INT;
break;
case IIO_CHAN_INFO_SCALE:
*val = state->scale;
*val2 = ABP060MG_NUM_COUNTS * 1000; /* to kPa */
ret = IIO_VAL_FRACTIONAL;
break;
default:
ret = -EINVAL;
break;
}
mutex_unlock(&state->lock);
return ret;
}
static const struct iio_info abp060mg_info = {
.driver_module = THIS_MODULE,
.read_raw = abp060mg_read_raw,
};
static void abp060mg_init_device(struct iio_dev *indio_dev, unsigned long id)
{
struct abp_state *state = iio_priv(indio_dev);
struct abp_config *cfg = &abp_config[id];
state->scale = cfg->max - cfg->min;
state->offset = -ABP060MG_MIN_COUNTS;
if (cfg->min < 0) /* differential */
state->offset -= ABP060MG_NUM_COUNTS >> 1;
}
static int abp060mg_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct iio_dev *indio_dev;
struct abp_state *state;
unsigned long cfg_id = id->driver_data;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*state));
if (!indio_dev)
return -ENOMEM;
state = iio_priv(indio_dev);
i2c_set_clientdata(client, state);
state->client = client;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_QUICK))
state->mreq_len = 1;
abp060mg_init_device(indio_dev, cfg_id);
indio_dev->dev.parent = &client->dev;
indio_dev->name = dev_name(&client->dev);
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &abp060mg_info;
indio_dev->channels = abp060mg_channels;
indio_dev->num_channels = ARRAY_SIZE(abp060mg_channels);
mutex_init(&state->lock);
return devm_iio_device_register(&client->dev, indio_dev);
}
static const struct i2c_device_id abp060mg_id_table[] = {
/* mbar & kPa variants (abp060m [60 mbar] == abp006k [6 kPa]) */
/* gage: */
{ "abp060mg", ABP006KG }, { "abp006kg", ABP006KG },
{ "abp100mg", ABP010KG }, { "abp010kg", ABP010KG },
{ "abp160mg", ABP016KG }, { "abp016kg", ABP016KG },
{ "abp250mg", ABP025KG }, { "abp025kg", ABP025KG },
{ "abp400mg", ABP040KG }, { "abp040kg", ABP040KG },
{ "abp600mg", ABP060KG }, { "abp060kg", ABP060KG },
{ "abp001bg", ABP100KG }, { "abp100kg", ABP100KG },
{ "abp1_6bg", ABP160KG }, { "abp160kg", ABP160KG },
{ "abp2_5bg", ABP250KG }, { "abp250kg", ABP250KG },
{ "abp004bg", ABP400KG }, { "abp400kg", ABP400KG },
{ "abp006bg", ABP600KG }, { "abp600kg", ABP600KG },
{ "abp010bg", ABP001GG }, { "abp001gg", ABP001GG },
/* differential: */
{ "abp060md", ABP006KD }, { "abp006kd", ABP006KD },
{ "abp100md", ABP010KD }, { "abp010kd", ABP010KD },
{ "abp160md", ABP016KD }, { "abp016kd", ABP016KD },
{ "abp250md", ABP025KD }, { "abp025kd", ABP025KD },
{ "abp400md", ABP040KD }, { "abp040kd", ABP040KD },
{ "abp600md", ABP060KD }, { "abp060kd", ABP060KD },
{ "abp001bd", ABP100KD }, { "abp100kd", ABP100KD },
{ "abp1_6bd", ABP160KD }, { "abp160kd", ABP160KD },
{ "abp2_5bd", ABP250KD }, { "abp250kd", ABP250KD },
{ "abp004bd", ABP400KD }, { "abp400kd", ABP400KD },
/* psi variants */
/* gage: */
{ "abp001pg", ABP001PG },
{ "abp005pg", ABP005PG },
{ "abp015pg", ABP015PG },
{ "abp030pg", ABP030PG },
{ "abp060pg", ABP060PG },
{ "abp100pg", ABP100PG },
{ "abp150pg", ABP150PG },
/* differential: */
{ "abp001pd", ABP001PD },
{ "abp005pd", ABP005PD },
{ "abp015pd", ABP015PD },
{ "abp030pd", ABP030PD },
{ "abp060pd", ABP060PD },
{ /* empty */ },
};
MODULE_DEVICE_TABLE(i2c, abp060mg_id_table);
static struct i2c_driver abp060mg_driver = {
.driver = {
.name = "abp060mg",
},
.probe = abp060mg_probe,
.id_table = abp060mg_id_table,
};
module_i2c_driver(abp060mg_driver);
MODULE_AUTHOR("Marcin Malagowski <[email protected]>");
MODULE_DESCRIPTION("Honeywell ABP pressure sensor driver");
MODULE_LICENSE("GPL");

View file

@ -82,8 +82,9 @@ static int mpl3115_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (iio_buffer_enabled(indio_dev))
return -EBUSY;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
switch (chan->type) {
case IIO_PRESSURE: /* in 0.25 pascal / LSB */
@ -91,32 +92,39 @@ static int mpl3115_read_raw(struct iio_dev *indio_dev,
ret = mpl3115_request(data);
if (ret < 0) {
mutex_unlock(&data->lock);
return ret;
break;
}
ret = i2c_smbus_read_i2c_block_data(data->client,
MPL3115_OUT_PRESS, 3, (u8 *) &tmp);
mutex_unlock(&data->lock);
if (ret < 0)
return ret;
break;
*val = be32_to_cpu(tmp) >> 12;
return IIO_VAL_INT;
ret = IIO_VAL_INT;
break;
case IIO_TEMP: /* in 0.0625 celsius / LSB */
mutex_lock(&data->lock);
ret = mpl3115_request(data);
if (ret < 0) {
mutex_unlock(&data->lock);
return ret;
break;
}
ret = i2c_smbus_read_i2c_block_data(data->client,
MPL3115_OUT_TEMP, 2, (u8 *) &tmp);
mutex_unlock(&data->lock);
if (ret < 0)
return ret;
break;
*val = sign_extend32(be32_to_cpu(tmp) >> 20, 11);
return IIO_VAL_INT;
ret = IIO_VAL_INT;
break;
default:
return -EINVAL;
ret = -EINVAL;
break;
}
iio_device_release_direct_mode(indio_dev);
return ret;
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_PRESSURE:

View file

@ -392,17 +392,14 @@ static int ms5611_init(struct iio_dev *indio_dev)
/* Enable attached regulator if any. */
st->vdd = devm_regulator_get(indio_dev->dev.parent, "vdd");
if (!IS_ERR(st->vdd)) {
ret = regulator_enable(st->vdd);
if (ret) {
dev_err(indio_dev->dev.parent,
"failed to enable Vdd supply: %d\n", ret);
return ret;
}
} else {
ret = PTR_ERR(st->vdd);
if (ret != -ENODEV)
return ret;
if (IS_ERR(st->vdd))
return PTR_ERR(st->vdd);
ret = regulator_enable(st->vdd);
if (ret) {
dev_err(indio_dev->dev.parent,
"failed to enable Vdd supply: %d\n", ret);
return ret;
}
ret = ms5611_reset(indio_dev);

View file

@ -147,12 +147,8 @@ struct zpa2326_private {
#define zpa2326_warn(_idev, _format, _arg...) \
dev_warn(_idev->dev.parent, _format, ##_arg)
#ifdef DEBUG
#define zpa2326_dbg(_idev, _format, _arg...) \
dev_dbg(_idev->dev.parent, _format, ##_arg)
#else
#define zpa2326_dbg(_idev, _format, _arg...)
#endif
bool zpa2326_isreg_writeable(struct device *dev, unsigned int reg)
{

View file

@ -301,8 +301,6 @@ static int lidar_probe(struct i2c_client *client,
if (ret)
goto error_unreg_buffer;
pm_runtime_enable(&client->dev);
pm_runtime_mark_last_busy(&client->dev);
pm_runtime_idle(&client->dev);
return 0;