Commit bf402c08 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge tag 'iio-for-5.2b' of...

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

Jonathan writes:

Second set of IIO new device support, features and cleanup for the 5.2 cycle.

New device suport
* ad7606
  - Support the AD7616 16 channel, 12bit ADC.
* fxas21002c
  - New driver for this gyroscope with I2C and SPI support.
* lsm6dsx
  - Support the lsm6dsr, new device information structure and dt bindings.
* srf04
  - Addition device IDs for mb1000, mb1010, mb1020, mb1030 and mb1040 +
    support of different required trigger pulse lengths.
* st-accel
  - Support the ls2de12, new device info and dt bindings.
* ti-ads8344
  - New driver for this 8 channel, 16 bit SPI ADC.

Binding conversions to yaml - we have started doing these in general for IIO.
* avia-hx711
* bmp085

Cleanups and minor fixes / additions
* ad5758
  - Fixup for some changes between preproduction parts and final part.
* ad7606
  - Refactor handling of oversampling to make it easy to vary between
    supported devices.
* ad9832
  - Organise includes.
  - Clock framework to handle clocks.
* ad9834
  - Drop unnecessary parenthesis.
* bmc150
  - Use __func__ rather than hardcoding.
* dummy_evgen.
  - Fix a memleak on error in probe.
* kxcjk1013
  - Add KXCJ91008 ACPI ID as seen in the wild.
  - Use __func__ rather than hardcoding.
* imx7d
  - Local dev variable to simplify code a bit.
  - dev_err replaces pr_err to give more info.
  - devm_platform_ioremap_resource for small reduction in boilerplate.
  - Simplify probe and remove by sharing suspend / resume logic.
  - Devm for iio_device_register as remove only contains the unregister.
* lsm6dsx
  - Remove a variable that was never read.
  - Open code values where they are effectively described by what is assigned
    to them rather than using uninformative defines.
* max31856
  - Avoid an unintialized ret variable in a path that can't actually occur
    but is hard for a static checker to know.
* max9611
  - White space
* mpu3050
  - Reduce a sleep worst case by switching from msleep to usleep_range.
* qcom-spmi-adc5
  - Add MODULE_DEVICE_TABLE to assist autoloading of this as a module.
* stm32-dfsdm
  - Fix missing dependencies.
* stm32-timer trigger
  - Fix a build issue when disabled.
* ti-ads7950
  - Fix mising dependency on CONFIG_GPIOLIB.

* tag 'iio-for-5.2b' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio: (42 commits)
  iio: adc: qcom-spmi-adc5: Fix of-based module autoloading
  iio: dummy_evgen: fix possible memleak in evgen init
  iio:accel:Switch hardcoded function name with a reference to __func__ making the code more maintainable
  iio: adc: stm32-dfsdm: fix triggered buffer build dependency
  iio: adc: stm32-dfsdm: fix unmet direct dependencies detected
  iio: trigger: stm32-timer: fix build issue when disabled
  iio: imx7d_adc: Use devm_iio_device_register()
  iio: imx7d_adc: Simplify imx7d_adc_remove() with imx7d_adc_suspend()
  iio: imx7d_adc: Simplify imx7d_adc_probe() with imx7d_adc_resume()
  drivers/iio/gyro/mpu3050-core.c: This patch fix the following checkpatch warning.
  iio: dac: ad5758: Modifications for new revision
  iio: imu: st_lsm6dsx: inline per-sensor data
  iio: adc: Add driver for the TI ADS8344 A/DC chips
  dt-bindings: iio: adc: Add bindings for TI ADS8344 A/DC chips
  MAINTAINERS: add entry for fxas21002c gyro driver
  iio: gyro: fxas21002c: add spi driver
  iio: gyro: fxas21002c: add i2c driver
  iio: gyro: add core driver for fxas21002c
  iio: gyro: add DT bindings to fxas21002c
  Kconfig: change configuration of srf04 ultrasonic iio sensor
  ...
parents 817de6b8 447ccb4e
......@@ -7,6 +7,7 @@ Required properties for the AD7606:
* "adi,ad7606-8"
* "adi,ad7606-6"
* "adi,ad7606-4"
* "adi,ad7616"
- reg: SPI chip select number for the device
- spi-max-frequency: Max SPI frequency to use
see: Documentation/devicetree/bindings/spi/spi-bus.txt
......
* AVIA HX711 ADC chip for weight cells
Bit-banging driver
Required properties:
- compatible: Should be "avia,hx711"
- sck-gpios: Definition of the GPIO for the clock
- dout-gpios: Definition of the GPIO for data-out
See Documentation/devicetree/bindings/gpio/gpio.txt
- avdd-supply: Definition of the regulator used as analog supply
Optional properties:
- clock-frequency: Frequency of PD_SCK in Hz
Minimum value allowed is 10 kHz because of maximum
high time of 50 microseconds.
Example:
weight {
compatible = "avia,hx711";
sck-gpios = <&gpio3 10 GPIO_ACTIVE_HIGH>;
dout-gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>;
avdd-suppy = <&avdd>;
clock-frequency = <100000>;
};
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: "http://devicetree.org/schemas/iio/adc/avia-hx711.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: AVIA HX711 ADC chip for weight cells
maintainers:
- Andreas Klinger <ak@it-klinger.de>
description: |
Bit-banging driver using two GPIOs:
- sck-gpio gives a clock to the sensor with 24 cycles for data retrieval
and up to 3 cycles for selection of the input channel and gain for the
next measurement
- dout-gpio is the sensor data the sensor responds to the clock
Specifications about the driver can be found at:
http://www.aviaic.com/ENProducts.aspx
properties:
compatible:
enum:
- avia,hx711
sck-gpios:
description:
Definition of the GPIO for the clock (output). In the datasheet it is
named PD_SCK
maxItems: 1
dout-gpios:
description:
Definition of the GPIO for the data-out sent by the sensor in
response to the clock (input).
See Documentation/devicetree/bindings/gpio/gpio.txt for information
on how to specify a consumer gpio.
maxItems: 1
avdd-supply:
description:
Definition of the regulator used as analog supply
maxItems: 1
clock-frequency:
minimum: 20000
maximum: 2500000
default: 400000
required:
- compatible
- sck-gpios
- dout-gpios
- avdd-supply
examples:
- |
#include <dt-bindings/gpio/gpio.h>
weight {
compatible = "avia,hx711";
sck-gpios = <&gpio3 10 GPIO_ACTIVE_HIGH>;
dout-gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>;
avdd-suppy = <&avdd>;
clock-frequency = <100000>;
};
* Texas Instruments ADS8344 A/DC chip
Required properties:
- compatible: Must be "ti,ads8344"
- reg: SPI chip select number for the device
- vref-supply: phandle to a regulator node that supplies the
reference voltage
Recommended properties:
- spi-max-frequency: Definition as per
Documentation/devicetree/bindings/spi/spi-bus.txt
Example:
adc@0 {
compatible = "ti,ads8344";
reg = <0>;
vref-supply = <&refin_supply>;
spi-max-frequency = <10000000>;
};
* NXP FXAS21002C Gyroscope device tree bindings
http://www.nxp.com/products/sensors/gyroscopes/3-axis-digital-gyroscope:FXAS21002C
Required properties:
- compatible : should be "nxp,fxas21002c"
- reg : the I2C address of the sensor or SPI chip select number for the
device.
- vdd-supply: phandle to the regulator that provides power to the sensor.
- vddio-supply: phandle to the regulator that provides power to the bus.
Optional properties:
- reset-gpios : gpio used to reset the device, see gpio/gpio.txt
- interrupts : device support 2 interrupts, INT1 and INT2,
the interrupts can be triggered on rising or falling edges.
See interrupt-controller/interrupts.txt
- interrupt-names: should contain "INT1" or "INT2", the gyroscope interrupt
line in use.
- drive-open-drain: the interrupt/data ready line will be configured
as open drain, which is useful if several sensors share
the same interrupt line. This is a boolean property.
(This binding is taken from pinctrl/pinctrl-bindings.txt)
Example:
gyroscope@20 {
compatible = "nxp,fxas21002c";
reg = <0x20>;
vdd-supply = <&reg_peri_3p15v>;
vddio-supply = <&reg_peri_3p15v>;
};
......@@ -10,6 +10,7 @@ Required properties:
"st,lsm6dso"
"st,asm330lhh"
"st,lsm6dsox"
"st,lsm6dsr"
- reg: i2c address of the sensor / spi cs line
Optional properties:
......
BMP085/BMP18x/BMP28x digital pressure sensors
Required properties:
- compatible: must be one of:
"bosch,bmp085"
"bosch,bmp180"
"bosch,bmp280"
"bosch,bme280"
Optional properties:
- interrupts: interrupt mapping for IRQ
- reset-gpios: a GPIO line handling reset of the sensor: as the line is
active low, it should be marked GPIO_ACTIVE_LOW (see gpio/gpio.txt)
- vddd-supply: digital voltage regulator (see regulator/regulator.txt)
- vdda-supply: analog voltage regulator (see regulator/regulator.txt)
Example:
pressure@77 {
compatible = "bosch,bmp085";
reg = <0x77>;
interrupt-parent = <&gpio0>;
interrupts = <25 IRQ_TYPE_EDGE_RISING>;
reset-gpios = <&gpio0 26 GPIO_ACTIVE_LOW>;
vddd-supply = <&foo>;
vdda-supply = <&bar>;
};
# SPDX-License-Identifier: GPL-2.0
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/pressure/bmp085.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: BMP085/BMP180/BMP280/BME280 pressure iio sensors
maintainers:
- Andreas Klinger <ak@it-klinger.de>
description: |
Pressure, temperature and humidity iio sensors with i2c and spi interfaces
Specifications about the sensor can be found at:
https://www.bosch-sensortec.com/bst/products/all_products/bmp180
https://www.bosch-sensortec.com/bst/products/all_products/bmp280
https://www.bosch-sensortec.com/bst/products/all_products/bme280
properties:
compatible:
enum:
- bosch,bmp085
- bosch,bmp180
- bosch,bmp280
- bosch,bme280
vddd-supply:
description:
digital voltage regulator (see regulator/regulator.txt)
maxItems: 1
vdda-supply:
description:
analog voltage regulator (see regulator/regulator.txt)
maxItems: 1
reset-gpios:
description:
A GPIO line handling reset of the sensor. As the line is active low,
it should be marked GPIO_ACTIVE_LOW (see gpio/gpio.txt)
maxItems: 1
interrupts:
description:
interrupt mapping for IRQ (BMP085 only)
maxItems: 1
required:
- compatible
- vddd-supply
- vdda-supply
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c0 {
#address-cells = <1>;
#size-cells = <0>;
pressure@77 {
compatible = "bosch,bmp085";
reg = <0x77>;
interrupt-parent = <&gpio0>;
interrupts = <25 IRQ_TYPE_EDGE_RISING>;
reset-gpios = <&gpio0 26 GPIO_ACTIVE_LOW>;
vddd-supply = <&foo>;
vdda-supply = <&bar>;
};
};
......@@ -4,7 +4,7 @@
$id: http://devicetree.org/schemas/iio/proximity/devantech-srf04.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Devantech SRF04 ultrasonic range finder
title: Devantech SRF04 and Maxbotix mb1000 ultrasonic range finder
maintainers:
- Andreas Klinger <ak@it-klinger.de>
......@@ -16,19 +16,28 @@ description: |
- echo-gpio is held high by the sensor after sending ultrasonic burst
until it is received once again
Specifications about the driver can be found at:
Specifications about the devices can be found at:
http://www.robot-electronics.co.uk/htm/srf04tech.htm
http://www.maxbotix.com/documents/LV-MaxSonar-EZ_Datasheet.pdf
properties:
compatible:
items:
- const: devantech,srf04
enum:
- devantech,srf04
- maxbotix,mb1000
- maxbotix,mb1010
- maxbotix,mb1020
- maxbotix,mb1030
- maxbotix,mb1040
trig-gpios:
description:
Definition of the GPIO for the triggering (output) This GPIO is set
for about 10 us by the driver to tell the device it should initiate
the measurement cycle.
Definition of the GPIO for the triggering (output)
This GPIO is set for about 10 us by the driver to tell the device it
should initiate the measurement cycle.
See Documentation/devicetree/bindings/gpio/gpio.txt for information
on how to specify a consumer gpio.
maxItems: 1
echo-gpios:
......@@ -40,8 +49,6 @@ properties:
trip.
It needs to be an GPIO which is able to deliver an interrupt because
the time between two interrupts is measured in the driver.
See Documentation/devicetree/bindings/gpio/gpio.txt for information
on how to specify a consumer gpio.
maxItems: 1
required:
......
......@@ -49,6 +49,7 @@ Accelerometers:
- st,lis2dw12
- st,lis3dhh
- st,lis3de
- st,lis2de12
Gyroscopes:
- st,l3g4200d-gyro
......
......@@ -11124,6 +11124,16 @@ F: Documentation/ABI/stable/sysfs-bus-nvmem
F: include/linux/nvmem-consumer.h
F: include/linux/nvmem-provider.h
NXP FXAS21002C DRIVER
M: Rui Miguel Silva <rmfrfs@gmail.com>
L: linux-iio@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/iio/gyroscope/fxas21002c.txt
F: drivers/iio/gyro/fxas21002c_core.c
F: drivers/iio/gyro/fxas21002c.h
F: drivers/iio/gyro/fxas21002c_i2c.c
F: drivers/iio/gyro/fxas21002c_spi.c
NXP SGTL5000 DRIVER
M: Fabio Estevam <festevam@gmail.com>
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
......
......@@ -223,7 +223,7 @@ config IIO_ST_ACCEL_3AXIS
Say yes here to build support for STMicroelectronics accelerometers:
LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC,
LIS331DLH, LSM303DL, LSM303DLM, LSM330, LIS2DH12, H3LIS331DL,
LNG2DM, LIS3DE
LNG2DM, LIS3DE, LIS2DE12
This driver can also be built as a module. If so, these modules
will be created:
......
......@@ -394,7 +394,7 @@ static int bmc150_accel_set_power_state(struct bmc150_accel_data *data, bool on)
if (ret < 0) {
dev_err(dev,
"Failed: bmc150_accel_set_power_state for %d\n", on);
"Failed: %s for %d\n", __func__, on);
if (on)
pm_runtime_put_noidle(dev);
......
......@@ -451,7 +451,7 @@ static int kxcjk1013_set_power_state(struct kxcjk1013_data *data, bool on)
}
if (ret < 0) {
dev_err(&data->client->dev,
"Failed: kxcjk1013_set_power_state for %d\n", on);
"Failed: %s for %d\n", __func__, on);
if (on)
pm_runtime_put_noidle(&data->client->dev);
return ret;
......@@ -1491,6 +1491,7 @@ static const struct acpi_device_id kx_acpi_match[] = {
{"KXCJ1013", KXCJK1013},
{"KXCJ1008", KXCJ91008},
{"KXCJ9000", KXCJ91008},
{"KIOX0008", KXCJ91008},
{"KIOX0009", KXTJ21009},
{"KIOX000A", KXCJ91008},
{"KIOX010A", KXCJ91008}, /* KXCJ91008 inside the display of a 2-in-1 */
......
......@@ -34,6 +34,7 @@ enum st_accel_type {
LIS3LV02DL,
LIS2DW12,
LIS3DHH,
LIS2DE12,
ST_ACCEL_MAX,
};
......@@ -57,6 +58,7 @@ enum st_accel_type {
#define LIS2DW12_ACCEL_DEV_NAME "lis2dw12"
#define LIS3DHH_ACCEL_DEV_NAME "lis3dhh"
#define LIS3DE_ACCEL_DEV_NAME "lis3de"
#define LIS2DE12_ACCEL_DEV_NAME "lis2de12"
/**
* struct st_sensors_platform_data - default accel platform data
......
......@@ -831,6 +831,82 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
.multi_read_bit = false,
.bootime = 2,
},
{
.wai = 0x33,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0] = LIS2DE12_ACCEL_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_accel_8bit_channels,
.odr = {
.addr = 0x20,
.mask = 0xf0,
.odr_avl = {
{ .hz = 1, .value = 0x01, },
{ .hz = 10, .value = 0x02, },
{ .hz = 25, .value = 0x03, },
{ .hz = 50, .value = 0x04, },
{ .hz = 100, .value = 0x05, },
{ .hz = 200, .value = 0x06, },
{ .hz = 400, .value = 0x07, },
{ .hz = 1620, .value = 0x08, },
{ .hz = 5376, .value = 0x09, },
},
},
.pw = {
.addr = 0x20,
.mask = 0xf0,
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
},
.enable_axis = {
.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
},
.fs = {
.addr = 0x23,
.mask = 0x30,
.fs_avl = {
[0] = {
.num = ST_ACCEL_FS_AVL_2G,
.value = 0x00,
.gain = IIO_G_TO_M_S_2(15600),
},
[1] = {
.num = ST_ACCEL_FS_AVL_4G,
.value = 0x01,
.gain = IIO_G_TO_M_S_2(31200),
},
[2] = {
.num = ST_ACCEL_FS_AVL_8G,
.value = 0x02,
.gain = IIO_G_TO_M_S_2(62500),
},
[3] = {
.num = ST_ACCEL_FS_AVL_16G,
.value = 0x03,
.gain = IIO_G_TO_M_S_2(187500),
},
},
},
.drdy_irq = {
.int1 = {
.addr = 0x22,
.mask = 0x10,
},
.addr_ihl = 0x25,
.mask_ihl = 0x02,
.stat_drdy = {
.addr = ST_SENSORS_DEFAULT_STAT_ADDR,
.mask = 0x07,
},
},
.sim = {
.addr = 0x23,
.value = BIT(0),
},
.multi_read_bit = true,
.bootime = 2,
},
};
static int st_accel_read_raw(struct iio_dev *indio_dev,
......
......@@ -102,6 +102,10 @@ static const struct of_device_id st_accel_of_match[] = {
.compatible = "st,lis3de",
.data = LIS3DE_ACCEL_DEV_NAME,
},
{
.compatible = "st,lis2de12",
.data = LIS2DE12_ACCEL_DEV_NAME,
},
{},
};
MODULE_DEVICE_TABLE(of, st_accel_of_match);
......@@ -140,6 +144,7 @@ static const struct i2c_device_id st_accel_id_table[] = {
{ LIS3LV02DL_ACCEL_DEV_NAME },
{ LIS2DW12_ACCEL_DEV_NAME },
{ LIS3DE_ACCEL_DEV_NAME },
{ LIS2DE12_ACCEL_DEV_NAME },
{},
};
MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
......
......@@ -553,7 +553,7 @@ config MAX1363
To compile this driver as a module, choose M here: the module will be
called max1363.
config MAX9611
config MAX9611
tristate "Maxim max9611/max9612 ADC driver"
depends on I2C
help
......@@ -821,7 +821,9 @@ config STM32_DFSDM_ADC
depends on (ARCH_STM32 && OF) || COMPILE_TEST
select STM32_DFSDM_CORE
select REGMAP_MMIO
select IIO_BUFFER
select IIO_BUFFER_HW_CONSUMER
select IIO_TRIGGERED_BUFFER
help
Select this option to support ADCSigma delta modulator for
STMicroelectronics STM32 digital filter for sigma delta converter.
......@@ -968,7 +970,7 @@ config TI_ADS1015
config TI_ADS7950
tristate "Texas Instruments ADS7950 ADC driver"
depends on SPI
depends on SPI && GPIOLIB
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
......@@ -979,6 +981,16 @@ config TI_ADS7950
To compile this driver as a module, choose M here: the
module will be called ti-ads7950.
config TI_ADS8344
tristate "Texas Instruments ADS8344"
depends on SPI && OF
help
If you say yes here you get support for Texas Instruments ADS8344
ADC chips
This driver can also be built as a module. If so, the module will be
called ti-ads8344.
config TI_ADS8688
tristate "Texas Instruments ADS8688"
depends on SPI && OF
......
......@@ -88,6 +88,7 @@ obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o
obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o
obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o
obj-$(CONFIG_TI_ADS8344) += ti-ads8344.o
obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o
obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o
obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
......
......@@ -31,7 +31,7 @@
* Scales are computed as 5000/32768 and 10000/32768 respectively,
* so that when applied to the raw values they provide mV values
*/
static const unsigned int scale_avail[2] = {
static const unsigned int ad7606_scale_avail[2] = {
152588, 305176
};
......@@ -39,6 +39,10 @@ static const unsigned int ad7606_oversampling_avail[7] = {
1, 2, 4, 8, 16, 32, 64,
};
static const unsigned int ad7616_oversampling_avail[8] = {
1, 2, 4, 8, 16, 32, 64, 128,
};
static int ad7606_reset(struct ad7606_state *st)
{
if (st->gpio_reset) {
......@@ -154,7 +158,7 @@ static int ad7606_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 0;
*val2 = scale_avail[st->range];
*val2 = st->scale_avail[st->range];
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
*val = st->oversampling;
......@@ -163,21 +167,31 @@ static int ad7606_read_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
static ssize_t in_voltage_scale_available_show(struct device *dev,
struct device_attribute *attr,
char *buf)
static ssize_t ad7606_show_avail(char *buf, const unsigned int *vals,
unsigned int n, bool micros)
{
int i, len = 0;
for (i = 0; i < ARRAY_SIZE(scale_avail); i++)
len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ",
scale_avail[i]);
size_t len = 0;
int i;
for (i = 0; i < n; i++) {
len += scnprintf(buf + len, PAGE_SIZE - len,
micros ? "0.%06u " : "%u ", vals[i]);
}
buf[len - 1] = '\n';
return len;
}
static ssize_t in_voltage_scale_available_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct ad7606_state *st = iio_priv(indio_dev);
return ad7606_show_avail(buf, st->scale_avail, st->num_scales, true);
}
static IIO_DEVICE_ATTR_RO(in_voltage_scale_available, 0);
static int ad7606_write_raw(struct iio_dev *indio_dev,
......@@ -193,7 +207,7 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_SCALE:
mutex_lock(&st->lock);
i = find_closest(val2, scale_avail, ARRAY_SIZE(scale_avail));
i = find_closest(val2, st->scale_avail, st->num_scales);
gpiod_set_value(st->gpio_range, i);
st->range = i;
mutex_unlock(&st->lock);
......@@ -202,15 +216,20 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
if (val2)
return -EINVAL;
i = find_closest(val, ad7606_oversampling_avail,
ARRAY_SIZE(ad7606_oversampling_avail));
i = find_closest(val, st->oversampling_avail,
st->num_os_ratios);
values[0] = i;
mutex_lock(&st->lock);
gpiod_set_array_value(ARRAY_SIZE(values), st->gpio_os->desc,
st->gpio_os->info, values);
st->oversampling = ad7606_oversampling_avail[i];
/* AD7616 requires a reset to update value */
if (st->chip_info->os_req_reset)
ad7606_reset(st);
st->oversampling = st->oversampling_avail[i];
mutex_unlock(&st->lock);
return 0;
......@@ -219,11 +238,23 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
}
}
static IIO_CONST_ATTR(oversampling_ratio_available, "1 2 4 8 16 32 64");
static ssize_t ad7606_oversampling_ratio_avail(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct ad7606_state *st = iio_priv(indio_dev);
return ad7606_show_avail(buf, st->oversampling_avail,
st->num_os_ratios, false);
}
static IIO_DEVICE_ATTR(oversampling_ratio_available, 0444,
ad7606_oversampling_ratio_avail, NULL, 0);
static struct attribute *ad7606_attributes_os_and_range[] = {
&iio_dev_attr_in_voltage_scale_available.dev_attr.attr,
&iio_const_attr_oversampling_ratio_available.dev_attr.attr,
&iio_dev_attr_oversampling_ratio_available.dev_attr.attr,
NULL,
};
......@@ -232,7 +263,7 @@ static const struct attribute_group ad7606_attribute_group_os_and_range = {
};
static struct attribute *ad7606_attributes_os[] = {
&iio_const_attr_oversampling_ratio_available.dev_attr.attr,
&iio_dev_attr_oversampling_ratio_available.dev_attr.attr,
NULL,
};
......@@ -292,6 +323,36 @@ static const struct iio_chan_spec ad7606_channels[] = {
AD7606_CHANNEL(7),
};
/*
* The current assumption that this driver makes for AD7616, is that it's
* working in Hardware Mode with Serial, Burst and Sequencer modes activated.
* To activate them, following pins must be pulled high:
* -SER/PAR
* -SEQEN
* And following pins must be pulled low:
* -WR/BURST
* -DB4/SER1W
*/
static const struct iio_chan_spec ad7616_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(16),
AD7606_CHANNEL(0),
AD7606_CHANNEL(1),
AD7606_CHANNEL(2),
AD7606_CHANNEL(3),
AD7606_CHANNEL(4),
AD7606_CHANNEL(5),
AD7606_CHANNEL(6),
AD7606_CHANNEL(7),
AD7606_CHANNEL(8),
AD7606_CHANNEL(9),
AD7606_CHANNEL(10),
AD7606_CHANNEL(11),
AD7606_CHANNEL(12),
AD7606_CHANNEL(13),
AD7606_CHANNEL(14),
AD7606_CHANNEL(15),
};
static const struct ad7606_chip_info ad7606_chip_info_tbl[] = {
/* More devices added in future */
[ID_AD7605_4] = {
......@@ -301,17 +362,27 @@ static const struct ad7606_chip_info ad7606_chip_info_tbl[] = {
[ID_AD7606_8] = {
.channels = ad7606_channels,
.num_channels = 9,
.has_oversampling = true,
.oversampling_avail = ad7606_oversampling_avail,
.oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
},
[ID_AD7606_6] = {
.channels = ad7606_channels,
.num_channels = 7,
.has_oversampling = true,
.oversampling_avail = ad7606_oversampling_avail,
.oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
},
[ID_AD7606_4] = {
.channels = ad7606_channels,
.num_channels = 5,
.has_oversampling = true,
.oversampling_avail = ad7606_oversampling_avail,
.oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
},
[ID_AD7616] = {
.channels = ad7616_channels,
.num_channels = 17,
.oversampling_avail = ad7616_oversampling_avail,
.oversampling_num = ARRAY_SIZE(ad7616_oversampling_avail),
.os_req_reset = true,
},
};
......@@ -343,7 +414,7 @@ static int ad7606_request_gpios(struct ad7606_state *st)
if (IS_ERR(st->gpio_frstdata))
return PTR_ERR(st->gpio_frstdata);
if (!st->chip_info->has_oversampling)
if (!st->chip_info->oversampling_num)
return 0;
st->gpio_os = devm_gpiod_get_array_optional(dev,
......@@ -467,6 +538,8 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
/* tied to logic low, analog input range is +/- 5V */
st->range = 0;
st->oversampling = 1;
st->scale_avail = ad7606_scale_avail;
st->num_scales = ARRAY_SIZE(ad7606_scale_avail);
st->reg = devm_regulator_get(dev, "avcc");
if (IS_ERR(st->reg))
......@@ -484,6 +557,11 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
st->chip_info = &ad7606_chip_info_tbl[id];
if (st->chip_info->oversampling_num) {
st->oversampling_avail = st->chip_info->oversampling_avail;
st->num_os_ratios = st->chip_info->oversampling_num;
}
ret = ad7606_request_gpios(st);
if (ret)
return ret;
......
......@@ -12,12 +12,17 @@
* struct ad7606_chip_info - chip specific information
* @channels: channel specification
* @num_channels: number of channels
* @has_oversampling: whether the device has oversampling support
* @oversampling_avail pointer to the array which stores the available
* oversampling ratios.
* @oversampling_num number of elements stored in oversampling_avail array
* @os_req_reset some devices require a reset to update oversampling
*/
struct ad7606_chip_info {
const struct iio_chan_spec *channels;
unsigned int num_channels;
bool has_oversampling;
const unsigned int *oversampling_avail;
unsigned int oversampling_num;
bool os_req_reset;
};
/**
......@@ -29,6 +34,11 @@ struct ad7606_chip_info {
* @range voltage range selection, selects which scale to apply
* @oversampling oversampling selection
* @base_address address from where to read data in parallel operation
* @scale_avail pointer to the array which stores the available scales
* @num_scales number of elements stored in the scale_avail array
* @oversampling_avail pointer to the array which stores the available
* oversampling ratios.
* @num_os_ratios number of elements stored in oversampling_avail array
* @lock protect sensor state from concurrent accesses to GPIOs
* @gpio_convst GPIO descriptor for conversion start signal (CONVST)
* @gpio_reset GPIO descriptor for device hard-reset
......@@ -50,6 +60,10 @@ struct ad7606_state {
unsigned int range;
unsigned int oversampling;
void __iomem *base_address;
const unsigned int *scale_avail;
unsigned int num_scales;
const unsigned int *oversampling_avail;
unsigned int num_os_ratios;
struct mutex lock; /* protect sensor state */
struct gpio_desc *gpio_convst;
......@@ -64,9 +78,9 @@ struct ad7606_state {
/*
* DMA (thus cache coherency maintenance) requires the
* transfer buffers to live in their own cache lines.
* 8 * 16-bit samples + 64-bit timestamp
* 16 * 16-bit samples + 64-bit timestamp
*/
unsigned short data[12] ____cacheline_aligned;
unsigned short data[20] ____cacheline_aligned;
};
/**
......@@ -86,7 +100,8 @@ enum ad7606_supported_device_ids {
ID_AD7605_4,
ID_AD7606_8,
ID_AD7606_6,
ID_AD7606_4
ID_AD7606_4,
ID_AD7616,
};
#ifdef CONFIG_PM_SLEEP
......
......@@ -53,6 +53,7 @@ static const struct spi_device_id ad7606_id_table[] = {
{ "ad7606-4", ID_AD7606_4 },
{ "ad7606-6", ID_AD7606_6 },
{ "ad7606-8", ID_AD7606_8 },
{ "ad7616", ID_AD7616 },
{}
};
MODULE_DEVICE_TABLE(spi, ad7606_id_table);
......@@ -62,6 +63,7 @@ static const struct of_device_id ad7606_of_match[] = {
{ .compatible = "adi,ad7606-4" },
{ .compatible = "adi,ad7606-6" },
{ .compatible = "adi,ad7606-8" },
{ .compatible = "adi,ad7616" },
{ },
};
MODULE_DEVICE_TABLE(of, ad7606_of_match);
......
......@@ -388,8 +388,9 @@ static irqreturn_t imx7d_adc_isr(int irq, void *dev_id)
* timeout flags.
*/
if (status & IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT) {
pr_err("%s: ADC got conversion time out interrupt: 0x%08x\n",
dev_name(info->dev), status);
dev_err(info->dev,
"ADC got conversion time out interrupt: 0x%08x\n",
status);
status &= ~IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT;
writel(status, info->regs + IMX7D_REG_ADC_INT_STATUS);
}
......@@ -433,167 +434,139 @@ static void imx7d_adc_power_down(struct imx7d_adc *info)
writel(adc_cfg, info->regs + IMX7D_REG_ADC_ADC_CFG);
}
static int imx7d_adc_enable(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct imx7d_adc *info = iio_priv(indio_dev);
int ret;
ret = regulator_enable(info->vref);
if (ret) {
dev_err(info->dev,
"Can't enable adc reference top voltage, err = %d\n",
ret);
return ret;
}
ret = clk_prepare_enable(info->clk);
if (ret) {
dev_err(info->dev,
"Could not prepare or enable clock.\n");
regulator_disable(info->vref);
return ret;
}
imx7d_adc_hw_init(info);
return 0;
}
static int imx7d_adc_disable(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct imx7d_adc *info = iio_priv(indio_dev);
imx7d_adc_power_down(info);
clk_disable_unprepare(info->clk);
regulator_disable(info->vref);
return 0;
}
static void __imx7d_adc_disable(void *data)
{
imx7d_adc_disable(data);
}
static int imx7d_adc_probe(struct platform_device *pdev)
{
struct imx7d_adc *info;
struct iio_dev *indio_dev;
struct resource *mem;
struct device *dev = &pdev->dev;
int irq;
int ret;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
indio_dev = devm_iio_device_alloc(dev, sizeof(*info));
if (!indio_dev) {
dev_err(&pdev->dev, "Failed allocating iio device\n");
return -ENOMEM;
}
info = iio_priv(indio_dev);
info->dev = &pdev->dev;
info->dev = dev;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
info->regs = devm_ioremap_resource(&pdev->dev, mem);
info->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(info->regs)) {
ret = PTR_ERR(info->regs);
dev_err(&pdev->dev,
"Failed to remap adc memory, err = %d\n", ret);
dev_err(dev, "Failed to remap adc memory, err = %d\n", ret);
return ret;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "No irq resource?\n");
dev_err(dev, "No irq resource?\n");
return irq;
}
info->clk = devm_clk_get(&pdev->dev, "adc");
info->clk = devm_clk_get(dev, "adc");
if (IS_ERR(info->clk)) {
ret = PTR_ERR(info->clk);
dev_err(&pdev->dev, "Failed getting clock, err = %d\n", ret);
dev_err(dev, "Failed getting clock, err = %d\n", ret);
return ret;
}
info->vref = devm_regulator_get(&pdev->dev, "vref");
info->vref = devm_regulator_get(dev, "vref");
if (IS_ERR(info->vref)) {
ret = PTR_ERR(info->vref);
dev_err(&pdev->dev,
dev_err(dev,
"Failed getting reference voltage, err = %d\n", ret);
return ret;
}
ret = regulator_enable(info->vref);
if (ret) {
dev_err(&pdev->dev,
"Can't enable adc reference top voltage, err = %d\n",
ret);
return ret;
}
platform_set_drvdata(pdev, indio_dev);
init_completion(&info->completion);
indio_dev->name = dev_name(&pdev->dev);
indio_dev->dev.parent = &pdev->dev;
indio_dev->name = dev_name(dev);
indio_dev->dev.parent = dev;
indio_dev->info = &imx7d_adc_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = imx7d_adc_iio_channels;
indio_dev->num_channels = ARRAY_SIZE(imx7d_adc_iio_channels);
ret = clk_prepare_enable(info->clk);
if (ret) {
dev_err(&pdev->dev,
"Could not prepare or enable the clock.\n");
goto error_adc_clk_enable;
}
ret = devm_request_irq(info->dev, irq,
imx7d_adc_isr, 0,
dev_name(&pdev->dev), info);
ret = devm_request_irq(dev, irq,
imx7d_adc_isr, 0,
dev_name(dev), info);
if (ret < 0) {
dev_err(&pdev->dev, "Failed requesting irq, irq = %d\n", irq);
goto error_iio_device_register;
dev_err(dev, "Failed requesting irq, irq = %d\n", irq);
return ret;
}
imx7d_adc_feature_config(info);
imx7d_adc_hw_init(info);
ret = iio_device_register(indio_dev);
if (ret) {
imx7d_adc_power_down(info);
dev_err(&pdev->dev, "Couldn't register the device.\n");
goto error_iio_device_register;
}
return 0;
error_iio_device_register:
clk_disable_unprepare(info->clk);
error_adc_clk_enable:
regulator_disable(info->vref);
return ret;
}
static int imx7d_adc_remove(struct platform_device *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct imx7d_adc *info = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
imx7d_adc_power_down(info);
clk_disable_unprepare(info->clk);
regulator_disable(info->vref);
return 0;
}
static int __maybe_unused imx7d_adc_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct imx7d_adc *info = iio_priv(indio_dev);
imx7d_adc_power_down(info);
clk_disable_unprepare(info->clk);
regulator_disable(info->vref);
return 0;
}
static int __maybe_unused imx7d_adc_resume(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct imx7d_adc *info = iio_priv(indio_dev);
int ret;
ret = imx7d_adc_enable(&indio_dev->dev);
if (ret)
return ret;
ret = regulator_enable(info->vref);
if (ret) {
dev_err(info->dev,
"Can't enable adc reference top voltage, err = %d\n",
ret);
ret = devm_add_action_or_reset(dev, __imx7d_adc_disable,
&indio_dev->dev);
if (ret)
return ret;
}
ret = clk_prepare_enable(info->clk);
ret = devm_iio_device_register(dev, indio_dev);
if (ret) {
dev_err(info->dev,
"Could not prepare or enable clock.\n");
regulator_disable(info->vref);
dev_err(&pdev->dev, "Couldn't register the device.\n");
return ret;
}
imx7d_adc_hw_init(info);
return 0;
}
static SIMPLE_DEV_PM_OPS(imx7d_adc_pm_ops, imx7d_adc_suspend, imx7d_adc_resume);
static SIMPLE_DEV_PM_OPS(imx7d_adc_pm_ops, imx7d_adc_disable, imx7d_adc_enable);
static struct platform_driver imx7d_adc_driver = {
.probe = imx7d_adc_probe,
.remove = imx7d_adc_remove,
.driver = {
.name = "imx7d_adc",
.of_match_table = imx7d_adc_match,
......
......@@ -664,6 +664,7 @@ static const struct of_device_id adc5_match_table[] = {
},
{ }
};
MODULE_DEVICE_TABLE(of, adc5_match_table);
static int adc5_get_dt_data(struct adc5_chip *adc, struct device_node *node)
{
......
// SPDX-License-Identifier: GPL-2.0+
/*
* ADS8344 16-bit 8-Channel ADC driver
*
* Author: Gregory CLEMENT <gregory.clement@bootlin.com>
*
* Datasheet: http://www.ti.com/lit/ds/symlink/ads8344.pdf
*/
#include <linux/delay.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/module.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
#define ADS8344_START BIT(7)
#define ADS8344_SINGLE_END BIT(2)
#define ADS8344_CHANNEL(channel) ((channel) << 4)
#define ADS8344_CLOCK_INTERNAL 0x2 /* PD1 = 1 and PD0 = 0 */
struct ads8344 {
struct spi_device *spi;
struct regulator *reg;
/*
* Lock protecting access to adc->tx_buff and rx_buff,
* especially from concurrent read on sysfs file.
*/
struct mutex lock;
u8 tx_buf ____cacheline_aligned;
u16 rx_buf;
};
#define ADS8344_VOLTAGE_CHANNEL(chan, si) \
{ \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = chan, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
}
#define ADS8344_VOLTAGE_CHANNEL_DIFF(chan1, chan2, si) \
{ \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = (chan1), \
.channel2 = (chan2), \
.differential = 1, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
}
static const struct iio_chan_spec ads8344_channels[] = {
ADS8344_VOLTAGE_CHANNEL(0, 0),
ADS8344_VOLTAGE_CHANNEL(1, 4),
ADS8344_VOLTAGE_CHANNEL(2, 1),
ADS8344_VOLTAGE_CHANNEL(3, 5),
ADS8344_VOLTAGE_CHANNEL(4, 2),
ADS8344_VOLTAGE_CHANNEL(5, 6),
ADS8344_VOLTAGE_CHANNEL(6, 3),
ADS8344_VOLTAGE_CHANNEL(7, 7),
ADS8344_VOLTAGE_CHANNEL_DIFF(0, 1, 8),
ADS8344_VOLTAGE_CHANNEL_DIFF(2, 3, 9),
ADS8344_VOLTAGE_CHANNEL_DIFF(4, 5, 10),
ADS8344_VOLTAGE_CHANNEL_DIFF(6, 7, 11),
ADS8344_VOLTAGE_CHANNEL_DIFF(1, 0, 12),
ADS8344_VOLTAGE_CHANNEL_DIFF(3, 2, 13),
ADS8344_VOLTAGE_CHANNEL_DIFF(5, 4, 14),
ADS8344_VOLTAGE_CHANNEL_DIFF(7, 6, 15),
};
static int ads8344_adc_conversion(struct ads8344 *adc, int channel,
bool differential)
{
struct spi_device *spi = adc->spi;
int ret;
adc->tx_buf = ADS8344_START;
if (!differential)
adc->tx_buf |= ADS8344_SINGLE_END;
adc->tx_buf |= ADS8344_CHANNEL(channel);
adc->tx_buf |= ADS8344_CLOCK_INTERNAL;
ret = spi_write(spi, &adc->tx_buf, 1);
if (ret)
return ret;
udelay(9);
ret = spi_read(spi, &adc->rx_buf, 2);
if (ret)
return ret;
return adc->rx_buf;
}
static int ads8344_read_raw(struct iio_dev *iio,
struct iio_chan_spec const *channel, int *value,
int *shift, long mask)
{
struct ads8344 *adc = iio_priv(iio);
switch (mask) {
case IIO_CHAN_INFO_RAW:
mutex_lock(&adc->lock);
*value = ads8344_adc_conversion(adc, channel->scan_index,
channel->differential);
mutex_unlock(&adc->lock);
if (*value < 0)
return *value;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*value = regulator_get_voltage(adc->reg);
if (*value < 0)
return *value;
/* convert regulator output voltage to mV */
*value /= 1000;
*shift = 16;
return IIO_VAL_FRACTIONAL_LOG2;
default:
return -EINVAL;
}
}
static const struct iio_info ads8344_info = {
.read_raw = ads8344_read_raw,
};
static int ads8344_probe(struct spi_device *spi)
{
struct iio_dev *indio_dev;
struct ads8344 *adc;
int ret;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
if (!indio_dev)
return -ENOMEM;
adc = iio_priv(indio_dev);
adc->spi = spi;
mutex_init(&adc->lock);
indio_dev->name = dev_name(&spi->dev);
indio_dev->dev.parent = &spi->dev;
indio_dev->dev.of_node = spi->dev.of_node;
indio_dev->info = &ads8344_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = ads8344_channels;
indio_dev->num_channels = ARRAY_SIZE(ads8344_channels);
adc->reg = devm_regulator_get(&spi->dev, "vref");
if (IS_ERR(adc->reg))
return PTR_ERR(adc->reg);
ret = regulator_enable(adc->reg);
if (ret)
return ret;
spi_set_drvdata(spi, indio_dev);
ret = iio_device_register(indio_dev);
if (ret) {
regulator_disable(adc->reg);
return ret;
}
return 0;
}
static int ads8344_remove(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct ads8344 *adc = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
regulator_disable(adc->reg);
return 0;
}
static const struct of_device_id ads8344_of_match[] = {
{ .compatible = "ti,ads8344", },
{}
};
MODULE_DEVICE_TABLE(of, ads8344_of_match);
static struct spi_driver ads8344_driver = {
.driver = {
.name = "ads8344",
.of_match_table = ads8344_of_match,
},
.probe = ads8344_probe,
.remove = ads8344_remove,
};
module_spi_driver(ads8344_driver);
MODULE_AUTHOR("Gregory CLEMENT <gregory.clement@bootlin.com>");
MODULE_DESCRIPTION("ADS8344 driver");
MODULE_LICENSE("GPL");
......@@ -72,8 +72,6 @@
#define AD5758_DCDC_CONFIG1_DCDC_VPROG_MODE(x) (((x) & 0x1F) << 0)
#define AD5758_DCDC_CONFIG1_DCDC_MODE_MSK GENMASK(6, 5)
#define AD5758_DCDC_CONFIG1_DCDC_MODE_MODE(x) (((x) & 0x3) << 5)
#define AD5758_DCDC_CONFIG1_PROT_SW_EN_MSK BIT(7)
#define AD5758_DCDC_CONFIG1_PROT_SW_EN_MODE(x) (((x) & 0x1) << 7)
/* AD5758_DCDC_CONFIG2 */
#define AD5758_DCDC_CONFIG2_ILIMIT_MSK GENMASK(3, 1)
......@@ -84,6 +82,10 @@
/* AD5758_DIGITAL_DIAG_RESULTS */
#define AD5758_CAL_MEM_UNREFRESHED_MSK BIT(15)
/* AD5758_ADC_CONFIG */
#define AD5758_ADC_CONFIG_PPC_BUF_EN(x) (((x) & 0x1) << 11)
#define AD5758_ADC_CONFIG_PPC_BUF_MSK BIT(11)
#define AD5758_WR_FLAG_MSK(x) (0x80 | ((x) & 0x1F))
#define AD5758_FULL_SCALE_MICRO 65535000000ULL
......@@ -315,6 +317,18 @@ static int ad5758_set_dc_dc_conv_mode(struct ad5758_state *st,
{
int ret;
/*
* The ENABLE_PPC_BUFFERS bit must be set prior to enabling PPC current
* mode.
*/
if (mode == AD5758_DCDC_MODE_PPC_CURRENT) {
ret = ad5758_spi_write_mask(st, AD5758_ADC_CONFIG,
AD5758_ADC_CONFIG_PPC_BUF_MSK,
AD5758_ADC_CONFIG_PPC_BUF_EN(1));
if (ret < 0)
return ret;
}
ret = ad5758_spi_write_mask(st, AD5758_DCDC_CONFIG1,
AD5758_DCDC_CONFIG1_DCDC_MODE_MSK,
AD5758_DCDC_CONFIG1_DCDC_MODE_MODE(mode));
......@@ -444,23 +458,6 @@ static int ad5758_set_out_range(struct ad5758_state *st, int range)
AD5758_CAL_MEM_UNREFRESHED_MSK);
}
static int ad5758_fault_prot_switch_en(struct ad5758_state *st, bool enable)
{
int ret;
ret = ad5758_spi_write_mask(st, AD5758_DCDC_CONFIG1,
AD5758_DCDC_CONFIG1_PROT_SW_EN_MSK,
AD5758_DCDC_CONFIG1_PROT_SW_EN_MODE(enable));
if (ret < 0)
return ret;
/*
* Poll the BUSY_3WI bit in the DCDC_CONFIG2 register until it is 0.
* This allows the 3-wire interface communication to complete.
*/
return ad5758_wait_for_task_complete(st, AD5758_DCDC_CONFIG2,
AD5758_DCDC_CONFIG2_BUSY_3WI_MSK);
}
static int ad5758_internal_buffers_en(struct ad5758_state *st, bool enable)
{
int ret;
......@@ -585,8 +582,8 @@ static ssize_t ad5758_write_powerdown(struct iio_dev *indio_dev,
{
struct ad5758_state *st = iio_priv(indio_dev);
bool pwr_down;
unsigned int dcdc_config1_mode, dc_dc_mode, dac_config_mode, val;
unsigned long int dcdc_config1_msk, dac_config_msk;
unsigned int dc_dc_mode, dac_config_mode, val;
unsigned long int dac_config_msk;
int ret;
ret = kstrtobool(buf, &pwr_down);
......@@ -602,17 +599,6 @@ static ssize_t ad5758_write_powerdown(struct iio_dev *indio_dev,
val = 1;
}
dcdc_config1_mode = AD5758_DCDC_CONFIG1_DCDC_MODE_MODE(dc_dc_mode) |
AD5758_DCDC_CONFIG1_PROT_SW_EN_MODE(val);
dcdc_config1_msk = AD5758_DCDC_CONFIG1_DCDC_MODE_MSK |
AD5758_DCDC_CONFIG1_PROT_SW_EN_MSK;
ret = ad5758_spi_write_mask(st, AD5758_DCDC_CONFIG1,
dcdc_config1_msk,
dcdc_config1_mode);
if (ret < 0)
goto err_unlock;
dac_config_mode = AD5758_DAC_CONFIG_OUT_EN_MODE(val) |
AD5758_DAC_CONFIG_INT_EN_MODE(val);
dac_config_msk = AD5758_DAC_CONFIG_OUT_EN_MSK |
......@@ -841,11 +827,6 @@ static int ad5758_init(struct ad5758_state *st)
return ret;
}
/* Enable the VIOUT fault protection switch (FPS is closed) */
ret = ad5758_fault_prot_switch_en(st, 1);
if (ret < 0)
return ret;
/* Power up the DAC and internal (INT) amplifiers */
ret = ad5758_internal_buffers_en(st, 1);
if (ret < 0)
......
......@@ -196,7 +196,10 @@ static __init int iio_dummy_evgen_init(void)
return ret;
device_initialize(&iio_evgen_dev);
dev_set_name(&iio_evgen_dev, "iio_evgen");
return device_add(&iio_evgen_dev);
ret = device_add(&iio_evgen_dev);
if (ret)
put_device(&iio_evgen_dev);
return ret;
}
module_init(iio_dummy_evgen_init);
......
......@@ -73,6 +73,28 @@ config BMG160_SPI
tristate
select REGMAP_SPI
config FXAS21002C
tristate "NXP FXAS21002C Gyro Sensor"
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
select FXAS21002C_I2C if (I2C)
select FXAS21002C_SPI if (SPI)
depends on (I2C || SPI_MASTER)
help
Say yes here to build support for NXP FXAS21002C Tri-axis Gyro
Sensor driver connected via I2C or SPI.
This driver can also be built as a module. If so, the module
will be called fxas21002c_i2c or fxas21002c_spi.
config FXAS21002C_I2C
tristate
select REGMAP_I2C
config FXAS21002C_SPI
tristate
select REGMAP_SPI
config HID_SENSOR_GYRO_3D
depends on HID_SENSOR_HUB
select IIO_BUFFER
......
......@@ -12,6 +12,9 @@ obj-$(CONFIG_ADXRS450) += adxrs450.o
obj-$(CONFIG_BMG160) += bmg160_core.o
obj-$(CONFIG_BMG160_I2C) += bmg160_i2c.o
obj-$(CONFIG_BMG160_SPI) += bmg160_spi.o
obj-$(CONFIG_FXAS21002C) += fxas21002c_core.o
obj-$(CONFIG_FXAS21002C_I2C) += fxas21002c_i2c.o
obj-$(CONFIG_FXAS21002C_SPI) += fxas21002c_spi.o
obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Driver for NXP FXAS21002C Gyroscope - Header
*
* Copyright (C) 2019 Linaro Ltd.
*/
#ifndef FXAS21002C_H_
#define FXAS21002C_H_
#include <linux/regmap.h>
#define FXAS21002C_REG_STATUS 0x00
#define FXAS21002C_REG_OUT_X_MSB 0x01
#define FXAS21002C_REG_OUT_X_LSB 0x02
#define FXAS21002C_REG_OUT_Y_MSB 0x03
#define FXAS21002C_REG_OUT_Y_LSB 0x04
#define FXAS21002C_REG_OUT_Z_MSB 0x05
#define FXAS21002C_REG_OUT_Z_LSB 0x06
#define FXAS21002C_REG_DR_STATUS 0x07
#define FXAS21002C_REG_F_STATUS 0x08
#define FXAS21002C_REG_F_SETUP 0x09
#define FXAS21002C_REG_F_EVENT 0x0A
#define FXAS21002C_REG_INT_SRC_FLAG 0x0B
#define FXAS21002C_REG_WHO_AM_I 0x0C
#define FXAS21002C_REG_CTRL0 0x0D
#define FXAS21002C_REG_RT_CFG 0x0E
#define FXAS21002C_REG_RT_SRC 0x0F
#define FXAS21002C_REG_RT_THS 0x10
#define FXAS21002C_REG_RT_COUNT 0x11
#define FXAS21002C_REG_TEMP 0x12
#define FXAS21002C_REG_CTRL1 0x13
#define FXAS21002C_REG_CTRL2 0x14
#define FXAS21002C_REG_CTRL3 0x15
enum fxas21002c_fields {
F_DR_STATUS,
F_OUT_X_MSB,
F_OUT_X_LSB,
F_OUT_Y_MSB,
F_OUT_Y_LSB,
F_OUT_Z_MSB,
F_OUT_Z_LSB,
/* DR_STATUS */
F_ZYX_OW, F_Z_OW, F_Y_OW, F_X_OW, F_ZYX_DR, F_Z_DR, F_Y_DR, F_X_DR,
/* F_STATUS */
F_OVF, F_WMKF, F_CNT,
/* F_SETUP */
F_MODE, F_WMRK,
/* F_EVENT */
F_EVENT, FE_TIME,
/* INT_SOURCE_FLAG */
F_BOOTEND, F_SRC_FIFO, F_SRC_RT, F_SRC_DRDY,
/* WHO_AM_I */
F_WHO_AM_I,
/* CTRL_REG0 */
F_BW, F_SPIW, F_SEL, F_HPF_EN, F_FS,
/* RT_CFG */
F_ELE, F_ZTEFE, F_YTEFE, F_XTEFE,
/* RT_SRC */
F_EA, F_ZRT, F_ZRT_POL, F_YRT, F_YRT_POL, F_XRT, F_XRT_POL,
/* RT_THS */
F_DBCNTM, F_THS,
/* RT_COUNT */
F_RT_COUNT,
/* TEMP */
F_TEMP,
/* CTRL_REG1 */
F_RST, F_ST, F_DR, F_ACTIVE, F_READY,
/* CTRL_REG2 */
F_INT_CFG_FIFO, F_INT_EN_FIFO, F_INT_CFG_RT, F_INT_EN_RT,
F_INT_CFG_DRDY, F_INT_EN_DRDY, F_IPOL, F_PP_OD,
/* CTRL_REG3 */
F_WRAPTOONE, F_EXTCTRLEN, F_FS_DOUBLE,
/* MAX FIELDS */
F_MAX_FIELDS,
};
static const struct reg_field fxas21002c_reg_fields[] = {
[F_DR_STATUS] = REG_FIELD(FXAS21002C_REG_STATUS, 0, 7),
[F_OUT_X_MSB] = REG_FIELD(FXAS21002C_REG_OUT_X_MSB, 0, 7),
[F_OUT_X_LSB] = REG_FIELD(FXAS21002C_REG_OUT_X_LSB, 0, 7),
[F_OUT_Y_MSB] = REG_FIELD(FXAS21002C_REG_OUT_Y_MSB, 0, 7),
[F_OUT_Y_LSB] = REG_FIELD(FXAS21002C_REG_OUT_Y_LSB, 0, 7),
[F_OUT_Z_MSB] = REG_FIELD(FXAS21002C_REG_OUT_Z_MSB, 0, 7),
[F_OUT_Z_LSB] = REG_FIELD(FXAS21002C_REG_OUT_Z_LSB, 0, 7),
[F_ZYX_OW] = REG_FIELD(FXAS21002C_REG_DR_STATUS, 7, 7),
[F_Z_OW] = REG_FIELD(FXAS21002C_REG_DR_STATUS, 6, 6),
[F_Y_OW] = REG_FIELD(FXAS21002C_REG_DR_STATUS, 5, 5),
[F_X_OW] = REG_FIELD(FXAS21002C_REG_DR_STATUS, 4, 4),
[F_ZYX_DR] = REG_FIELD(FXAS21002C_REG_DR_STATUS, 3, 3),
[F_Z_DR] = REG_FIELD(FXAS21002C_REG_DR_STATUS, 2, 2),
[F_Y_DR] = REG_FIELD(FXAS21002C_REG_DR_STATUS, 1, 1),
[F_X_DR] = REG_FIELD(FXAS21002C_REG_DR_STATUS, 0, 0),
[F_OVF] = REG_FIELD(FXAS21002C_REG_F_STATUS, 7, 7),
[F_WMKF] = REG_FIELD(FXAS21002C_REG_F_STATUS, 6, 6),
[F_CNT] = REG_FIELD(FXAS21002C_REG_F_STATUS, 0, 5),
[F_MODE] = REG_FIELD(FXAS21002C_REG_F_SETUP, 6, 7),
[F_WMRK] = REG_FIELD(FXAS21002C_REG_F_SETUP, 0, 5),
[F_EVENT] = REG_FIELD(FXAS21002C_REG_F_EVENT, 5, 5),
[FE_TIME] = REG_FIELD(FXAS21002C_REG_F_EVENT, 0, 4),
[F_BOOTEND] = REG_FIELD(FXAS21002C_REG_INT_SRC_FLAG, 3, 3),
[F_SRC_FIFO] = REG_FIELD(FXAS21002C_REG_INT_SRC_FLAG, 2, 2),
[F_SRC_RT] = REG_FIELD(FXAS21002C_REG_INT_SRC_FLAG, 1, 1),
[F_SRC_DRDY] = REG_FIELD(FXAS21002C_REG_INT_SRC_FLAG, 0, 0),
[F_WHO_AM_I] = REG_FIELD(FXAS21002C_REG_WHO_AM_I, 0, 7),
[F_BW] = REG_FIELD(FXAS21002C_REG_CTRL0, 6, 7),
[F_SPIW] = REG_FIELD(FXAS21002C_REG_CTRL0, 5, 5),
[F_SEL] = REG_FIELD(FXAS21002C_REG_CTRL0, 3, 4),
[F_HPF_EN] = REG_FIELD(FXAS21002C_REG_CTRL0, 2, 2),
[F_FS] = REG_FIELD(FXAS21002C_REG_CTRL0, 0, 1),
[F_ELE] = REG_FIELD(FXAS21002C_REG_RT_CFG, 3, 3),
[F_ZTEFE] = REG_FIELD(FXAS21002C_REG_RT_CFG, 2, 2),
[F_YTEFE] = REG_FIELD(FXAS21002C_REG_RT_CFG, 1, 1),
[F_XTEFE] = REG_FIELD(FXAS21002C_REG_RT_CFG, 0, 0),
[F_EA] = REG_FIELD(FXAS21002C_REG_RT_SRC, 6, 6),
[F_ZRT] = REG_FIELD(FXAS21002C_REG_RT_SRC, 5, 5),
[F_ZRT_POL] = REG_FIELD(FXAS21002C_REG_RT_SRC, 4, 4),
[F_YRT] = REG_FIELD(FXAS21002C_REG_RT_SRC, 3, 3),
[F_YRT_POL] = REG_FIELD(FXAS21002C_REG_RT_SRC, 2, 2),
[F_XRT] = REG_FIELD(FXAS21002C_REG_RT_SRC, 1, 1),
[F_XRT_POL] = REG_FIELD(FXAS21002C_REG_RT_SRC, 0, 0),
[F_DBCNTM] = REG_FIELD(FXAS21002C_REG_RT_THS, 7, 7),
[F_THS] = REG_FIELD(FXAS21002C_REG_RT_SRC, 0, 6),
[F_RT_COUNT] = REG_FIELD(FXAS21002C_REG_RT_COUNT, 0, 7),
[F_TEMP] = REG_FIELD(FXAS21002C_REG_TEMP, 0, 7),
[F_RST] = REG_FIELD(FXAS21002C_REG_CTRL1, 6, 6),
[F_ST] = REG_FIELD(FXAS21002C_REG_CTRL1, 5, 5),
[F_DR] = REG_FIELD(FXAS21002C_REG_CTRL1, 2, 4),
[F_ACTIVE] = REG_FIELD(FXAS21002C_REG_CTRL1, 1, 1),
[F_READY] = REG_FIELD(FXAS21002C_REG_CTRL1, 0, 0),
[F_INT_CFG_FIFO] = REG_FIELD(FXAS21002C_REG_CTRL2, 7, 7),
[F_INT_EN_FIFO] = REG_FIELD(FXAS21002C_REG_CTRL2, 6, 6),
[F_INT_CFG_RT] = REG_FIELD(FXAS21002C_REG_CTRL2, 5, 5),
[F_INT_EN_RT] = REG_FIELD(FXAS21002C_REG_CTRL2, 4, 4),
[F_INT_CFG_DRDY] = REG_FIELD(FXAS21002C_REG_CTRL2, 3, 3),
[F_INT_EN_DRDY] = REG_FIELD(FXAS21002C_REG_CTRL2, 2, 2),
[F_IPOL] = REG_FIELD(FXAS21002C_REG_CTRL2, 1, 1),
[F_PP_OD] = REG_FIELD(FXAS21002C_REG_CTRL2, 0, 0),
[F_WRAPTOONE] = REG_FIELD(FXAS21002C_REG_CTRL3, 3, 3),
[F_EXTCTRLEN] = REG_FIELD(FXAS21002C_REG_CTRL3, 2, 2),
[F_FS_DOUBLE] = REG_FIELD(FXAS21002C_REG_CTRL3, 0, 0),
};
extern const struct dev_pm_ops fxas21002c_pm_ops;
int fxas21002c_core_probe(struct device *dev, struct regmap *regmap, int irq,
const char *name);
void fxas21002c_core_remove(struct device *dev);
#endif
// SPDX-License-Identifier: GPL-2.0
/*
* Driver for NXP FXAS21002C Gyroscope - Core
*
* Copyright (C) 2019 Linaro Ltd.
*/
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of_irq.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/iio/events.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include "fxas21002c.h"
#define FXAS21002C_CHIP_ID_1 0xD6
#define FXAS21002C_CHIP_ID_2 0xD7
enum fxas21002c_mode_state {
FXAS21002C_MODE_STANDBY,
FXAS21002C_MODE_READY,
FXAS21002C_MODE_ACTIVE,
};
#define FXAS21002C_STANDBY_ACTIVE_TIME_MS 62
#define FXAS21002C_READY_ACTIVE_TIME_MS 7
#define FXAS21002C_ODR_LIST_MAX 10
#define FXAS21002C_SCALE_FRACTIONAL 32
#define FXAS21002C_RANGE_LIMIT_DOUBLE 2000
#define FXAS21002C_AXIS_TO_REG(axis) (FXAS21002C_REG_OUT_X_MSB + ((axis) * 2))
static const int fxas21002c_odr_values[] = {
800, 400, 200, 100, 50, 25, 12, 12
};
/*
* These values are taken from the low-pass filter cutoff frequency calculated
* ODR * 0.lpf_values. So, for ODR = 800Hz with a lpf value = 0.32
* => LPF cutoff frequency = 800 * 0.32 = 256 Hz
*/
static const int fxas21002c_lpf_values[] = {
32, 16, 8
};
/*
* These values are taken from the high-pass filter cutoff frequency calculated
* ODR * 0.0hpf_values. So, for ODR = 800Hz with a hpf value = 0.018750
* => HPF cutoff frequency = 800 * 0.018750 = 15 Hz
*/
static const int fxas21002c_hpf_values[] = {
18750, 9625, 4875, 2475
};
static const int fxas21002c_range_values[] = {
4000, 2000, 1000, 500, 250
};
struct fxas21002c_data {
u8 chip_id;
enum fxas21002c_mode_state mode;
enum fxas21002c_mode_state prev_mode;
struct mutex lock; /* serialize data access */
struct regmap *regmap;
struct regmap_field *regmap_fields[F_MAX_FIELDS];
struct iio_trigger *dready_trig;
s64 timestamp;
int irq;
struct regulator *vdd;
struct regulator *vddio;
/*
* DMA (thus cache coherency maintenance) requires the
* transfer buffers to live in their own cache lines.
*/
s16 buffer[8] ____cacheline_aligned;
};
enum fxas21002c_channel_index {
CHANNEL_SCAN_INDEX_X,
CHANNEL_SCAN_INDEX_Y,
CHANNEL_SCAN_INDEX_Z,
CHANNEL_SCAN_MAX,
};
static int fxas21002c_odr_hz_from_value(struct fxas21002c_data *data, u8 value)
{
int odr_value_max = ARRAY_SIZE(fxas21002c_odr_values) - 1;
value = min_t(u8, value, odr_value_max);
return fxas21002c_odr_values[value];
}
static int fxas21002c_odr_value_from_hz(struct fxas21002c_data *data,
unsigned int hz)
{
int odr_table_size = ARRAY_SIZE(fxas21002c_odr_values);
int i;
for (i = 0; i < odr_table_size; i++)
if (fxas21002c_odr_values[i] == hz)
return i;
return -EINVAL;
}
static int fxas21002c_lpf_bw_from_value(struct fxas21002c_data *data, u8 value)
{
int lpf_value_max = ARRAY_SIZE(fxas21002c_lpf_values) - 1;
value = min_t(u8, value, lpf_value_max);
return fxas21002c_lpf_values[value];
}
static int fxas21002c_lpf_value_from_bw(struct fxas21002c_data *data,
unsigned int hz)
{
int lpf_table_size = ARRAY_SIZE(fxas21002c_lpf_values);
int i;
for (i = 0; i < lpf_table_size; i++)
if (fxas21002c_lpf_values[i] == hz)
return i;
return -EINVAL;
}
static int fxas21002c_hpf_sel_from_value(struct fxas21002c_data *data, u8 value)
{
int hpf_value_max = ARRAY_SIZE(fxas21002c_hpf_values) - 1;
value = min_t(u8, value, hpf_value_max);
return fxas21002c_hpf_values[value];
}
static int fxas21002c_hpf_value_from_sel(struct fxas21002c_data *data,
unsigned int hz)
{
int hpf_table_size = ARRAY_SIZE(fxas21002c_hpf_values);
int i;
for (i = 0; i < hpf_table_size; i++)
if (fxas21002c_hpf_values[i] == hz)
return i;
return -EINVAL;
}
static int fxas21002c_range_fs_from_value(struct fxas21002c_data *data,
u8 value)
{
int range_value_max = ARRAY_SIZE(fxas21002c_range_values) - 1;
unsigned int fs_double;
int ret;
/* We need to check if FS_DOUBLE is enabled to offset the value */
ret = regmap_field_read(data->regmap_fields[F_FS_DOUBLE], &fs_double);
if (ret < 0)
return ret;
if (!fs_double)
value += 1;
value = min_t(u8, value, range_value_max);
return fxas21002c_range_values[value];
}
static int fxas21002c_range_value_from_fs(struct fxas21002c_data *data,
unsigned int range)
{
int range_table_size = ARRAY_SIZE(fxas21002c_range_values);
bool found = false;
int fs_double = 0;
int ret;
int i;
for (i = 0; i < range_table_size; i++)
if (fxas21002c_range_values[i] == range) {
found = true;
break;
}
if (!found)
return -EINVAL;
if (range > FXAS21002C_RANGE_LIMIT_DOUBLE)
fs_double = 1;
ret = regmap_field_write(data->regmap_fields[F_FS_DOUBLE], fs_double);
if (ret < 0)
return ret;
return i;
}
static int fxas21002c_mode_get(struct fxas21002c_data *data)
{
unsigned int active;
unsigned int ready;
int ret;
ret = regmap_field_read(data->regmap_fields[F_ACTIVE], &active);
if (ret < 0)
return ret;
if (active)
return FXAS21002C_MODE_ACTIVE;
ret = regmap_field_read(data->regmap_fields[F_READY], &ready);
if (ret < 0)
return ret;
if (ready)
return FXAS21002C_MODE_READY;
return FXAS21002C_MODE_STANDBY;
}
static int fxas21002c_mode_set(struct fxas21002c_data *data,
enum fxas21002c_mode_state mode)
{
int ret;
if (mode == data->mode)
return 0;
if (mode == FXAS21002C_MODE_READY)
ret = regmap_field_write(data->regmap_fields[F_READY], 1);
else
ret = regmap_field_write(data->regmap_fields[F_READY], 0);
if (ret < 0)
return ret;
if (mode == FXAS21002C_MODE_ACTIVE)
ret = regmap_field_write(data->regmap_fields[F_ACTIVE], 1);
else
ret = regmap_field_write(data->regmap_fields[F_ACTIVE], 0);
if (ret < 0)
return ret;
/* if going to active wait the setup times */
if (mode == FXAS21002C_MODE_ACTIVE &&
data->mode == FXAS21002C_MODE_STANDBY)
msleep_interruptible(FXAS21002C_STANDBY_ACTIVE_TIME_MS);
if (data->mode == FXAS21002C_MODE_READY)
msleep_interruptible(FXAS21002C_READY_ACTIVE_TIME_MS);
data->prev_mode = data->mode;
data->mode = mode;
return ret;
}
static int fxas21002c_write(struct fxas21002c_data *data,
enum fxas21002c_fields field, int bits)
{
int actual_mode;
int ret;
mutex_lock(&data->lock);
actual_mode = fxas21002c_mode_get(data);
if (actual_mode < 0) {
ret = actual_mode;
goto out_unlock;
}
ret = fxas21002c_mode_set(data, FXAS21002C_MODE_READY);
if (ret < 0)
goto out_unlock;
ret = regmap_field_write(data->regmap_fields[field], bits);
if (ret < 0)
goto out_unlock;
ret = fxas21002c_mode_set(data, data->prev_mode);
out_unlock:
mutex_unlock(&data->lock);
return ret;
}
static int fxas21002c_pm_get(struct fxas21002c_data *data)
{
struct device *dev = regmap_get_device(data->regmap);
int ret;
ret = pm_runtime_get_sync(dev);
if (ret < 0)
pm_runtime_put_noidle(dev);
return ret;
}
static int fxas21002c_pm_put(struct fxas21002c_data *data)
{
struct device *dev = regmap_get_device(data->regmap);
pm_runtime_mark_last_busy(dev);
return pm_runtime_put_autosuspend(dev);
}
static int fxas21002c_temp_get(struct fxas21002c_data *data, int *val)
{
struct device *dev = regmap_get_device(data->regmap);
unsigned int temp;
int ret;
mutex_lock(&data->lock);
ret = fxas21002c_pm_get(data);
if (ret < 0)
goto data_unlock;
ret = regmap_field_read(data->regmap_fields[F_TEMP], &temp);
if (ret < 0) {
dev_err(dev, "failed to read temp: %d\n", ret);
goto data_unlock;
}
*val = sign_extend32(temp, 7);
ret = fxas21002c_pm_put(data);
if (ret < 0)
goto data_unlock;
ret = IIO_VAL_INT;
data_unlock:
mutex_unlock(&data->lock);
return ret;
}
static int fxas21002c_axis_get(struct fxas21002c_data *data,
int index, int *val)
{
struct device *dev = regmap_get_device(data->regmap);
__be16 axis_be;
int ret;
mutex_lock(&data->lock);
ret = fxas21002c_pm_get(data);
if (ret < 0)
goto data_unlock;
ret = regmap_bulk_read(data->regmap, FXAS21002C_AXIS_TO_REG(index),
&axis_be, sizeof(axis_be));
if (ret < 0) {
dev_err(dev, "failed to read axis: %d: %d\n", index, ret);
goto data_unlock;
}
*val = sign_extend32(be16_to_cpu(axis_be), 15);
ret = fxas21002c_pm_put(data);
if (ret < 0)
goto data_unlock;
ret = IIO_VAL_INT;
data_unlock:
mutex_unlock(&data->lock);
return ret;
}
static int fxas21002c_odr_get(struct fxas21002c_data *data, int *odr)
{
unsigned int odr_bits;
int ret;
mutex_lock(&data->lock);
ret = regmap_field_read(data->regmap_fields[F_DR], &odr_bits);
if (ret < 0)
goto data_unlock;
*odr = fxas21002c_odr_hz_from_value(data, odr_bits);
ret = IIO_VAL_INT;
data_unlock:
mutex_unlock(&data->lock);
return ret;
}
static int fxas21002c_odr_set(struct fxas21002c_data *data, int odr)
{
int odr_bits;
odr_bits = fxas21002c_odr_value_from_hz(data, odr);
if (odr_bits < 0)
return odr_bits;
return fxas21002c_write(data, F_DR, odr_bits);
}
static int fxas21002c_lpf_get(struct fxas21002c_data *data, int *val2)
{
unsigned int bw_bits;
int ret;
mutex_lock(&data->lock);
ret = regmap_field_read(data->regmap_fields[F_BW], &bw_bits);
if (ret < 0)
goto data_unlock;
*val2 = fxas21002c_lpf_bw_from_value(data, bw_bits) * 10000;
ret = IIO_VAL_INT_PLUS_MICRO;
data_unlock:
mutex_unlock(&data->lock);
return ret;
}
static int fxas21002c_lpf_set(struct fxas21002c_data *data, int bw)
{
int bw_bits;
int odr;
int ret;
bw_bits = fxas21002c_lpf_value_from_bw(data, bw);
if (bw_bits < 0)
return bw_bits;
/*
* From table 33 of the device spec, for ODR = 25Hz and 12.5 value 0.08
* is not allowed and for ODR = 12.5 value 0.16 is also not allowed
*/
ret = fxas21002c_odr_get(data, &odr);
if (ret < 0)
return -EINVAL;
if ((odr == 25 && bw_bits > 0x01) || (odr == 12 && bw_bits > 0))
return -EINVAL;
return fxas21002c_write(data, F_BW, bw_bits);
}
static int fxas21002c_hpf_get(struct fxas21002c_data *data, int *val2)
{
unsigned int sel_bits;
int ret;
mutex_lock(&data->lock);
ret = regmap_field_read(data->regmap_fields[F_SEL], &sel_bits);
if (ret < 0)
goto data_unlock;
*val2 = fxas21002c_hpf_sel_from_value(data, sel_bits);
ret = IIO_VAL_INT_PLUS_MICRO;
data_unlock:
mutex_unlock(&data->lock);
return ret;
}
static int fxas21002c_hpf_set(struct fxas21002c_data *data, int sel)
{
int sel_bits;
sel_bits = fxas21002c_hpf_value_from_sel(data, sel);
if (sel_bits < 0)
return sel_bits;
return fxas21002c_write(data, F_SEL, sel_bits);
}
static int fxas21002c_scale_get(struct fxas21002c_data *data, int *val)
{
int fs_bits;
int scale;
int ret;
mutex_lock(&data->lock);
ret = regmap_field_read(data->regmap_fields[F_FS], &fs_bits);
if (ret < 0)
goto data_unlock;
scale = fxas21002c_range_fs_from_value(data, fs_bits);
if (scale < 0) {
ret = scale;
goto data_unlock;
}
*val = scale;
data_unlock:
mutex_unlock(&data->lock);
return ret;
}
static int fxas21002c_scale_set(struct fxas21002c_data *data, int range)
{
int fs_bits;
fs_bits = fxas21002c_range_value_from_fs(data, range);
if (fs_bits < 0)
return fs_bits;
return fxas21002c_write(data, F_FS, fs_bits);
}
static int fxas21002c_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
struct fxas21002c_data *data = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
switch (chan->type) {
case IIO_TEMP:
return fxas21002c_temp_get(data, val);
case IIO_ANGL_VEL:
return fxas21002c_axis_get(data, chan->scan_index, val);
default:
return -EINVAL;
}
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_ANGL_VEL:
*val2 = FXAS21002C_SCALE_FRACTIONAL;
ret = fxas21002c_scale_get(data, val);
if (ret < 0)
return ret;
return IIO_VAL_FRACTIONAL;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
*val = 0;
return fxas21002c_lpf_get(data, val2);
case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
*val = 0;
return fxas21002c_hpf_get(data, val2);
case IIO_CHAN_INFO_SAMP_FREQ:
*val2 = 0;
return fxas21002c_odr_get(data, val);
default:
return -EINVAL;
}
}
static int fxas21002c_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val,
int val2, long mask)
{
struct fxas21002c_data *data = iio_priv(indio_dev);
int range;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
if (val2)
return -EINVAL;
return fxas21002c_odr_set(data, val);
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
if (val)
return -EINVAL;
val2 = val2 / 10000;
return fxas21002c_lpf_set(data, val2);
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_ANGL_VEL:
range = (((val * 1000 + val2 / 1000) *
FXAS21002C_SCALE_FRACTIONAL) / 1000);
return fxas21002c_scale_set(data, range);
default:
return -EINVAL;
}
case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
return fxas21002c_hpf_set(data, val2);
default:
return -EINVAL;
}
}
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("12.5 25 50 100 200 400 800");
static IIO_CONST_ATTR(in_anglvel_filter_low_pass_3db_frequency_available,
"0.32 0.16 0.08");
static IIO_CONST_ATTR(in_anglvel_filter_high_pass_3db_frequency_available,
"0.018750 0.009625 0.004875 0.002475");
static IIO_CONST_ATTR(in_anglvel_scale_available,
"125.0 62.5 31.25 15.625 7.8125");
static struct attribute *fxas21002c_attributes[] = {
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
&iio_const_attr_in_anglvel_filter_low_pass_3db_frequency_available.dev_attr.attr,
&iio_const_attr_in_anglvel_filter_high_pass_3db_frequency_available.dev_attr.attr,
&iio_const_attr_in_anglvel_scale_available.dev_attr.attr,
NULL,
};
static const struct attribute_group fxas21002c_attrs_group = {
.attrs = fxas21002c_attributes,
};
#define FXAS21002C_CHANNEL(_axis) { \
.type = IIO_ANGL_VEL, \
.modified = 1, \
.channel2 = IIO_MOD_##_axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | \
BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY) | \
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.scan_index = CHANNEL_SCAN_INDEX_##_axis, \
.scan_type = { \
.sign = 's', \
.realbits = 16, \
.storagebits = 16, \
.endianness = IIO_BE, \
}, \
}
static const struct iio_chan_spec fxas21002c_channels[] = {
{
.type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.scan_index = -1,
},
FXAS21002C_CHANNEL(X),
FXAS21002C_CHANNEL(Y),
FXAS21002C_CHANNEL(Z),
};
static const struct iio_info fxas21002c_info = {
.attrs = &fxas21002c_attrs_group,
.read_raw = &fxas21002c_read_raw,
.write_raw = &fxas21002c_write_raw,
};
static irqreturn_t fxas21002c_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct fxas21002c_data *data = iio_priv(indio_dev);
int ret;
mutex_lock(&data->lock);
ret = regmap_bulk_read(data->regmap, FXAS21002C_REG_OUT_X_MSB,
data->buffer, CHANNEL_SCAN_MAX * sizeof(s16));
if (ret < 0)
goto out_unlock;
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
data->timestamp);
out_unlock:
mutex_unlock(&data->lock);
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
static int fxas21002c_chip_init(struct fxas21002c_data *data)
{
struct device *dev = regmap_get_device(data->regmap);
unsigned int chip_id;
int ret;
ret = regmap_field_read(data->regmap_fields[F_WHO_AM_I], &chip_id);
if (ret < 0)
return ret;
if (chip_id != FXAS21002C_CHIP_ID_1 &&
chip_id != FXAS21002C_CHIP_ID_2) {
dev_err(dev, "chip id 0x%02x is not supported\n", chip_id);
return -EINVAL;
}
data->chip_id = chip_id;
ret = fxas21002c_mode_set(data, FXAS21002C_MODE_STANDBY);
if (ret < 0)
return ret;
/* Set ODR to 200HZ as default */
ret = fxas21002c_odr_set(data, 200);
if (ret < 0)
dev_err(dev, "failed to set ODR: %d\n", ret);
return ret;
}
static int fxas21002c_data_rdy_trigger_set_state(struct iio_trigger *trig,
bool state)
{
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
struct fxas21002c_data *data = iio_priv(indio_dev);
return regmap_field_write(data->regmap_fields[F_INT_EN_DRDY], state);
}
static const struct iio_trigger_ops fxas21002c_trigger_ops = {
.set_trigger_state = &fxas21002c_data_rdy_trigger_set_state,
};
static irqreturn_t fxas21002c_data_rdy_handler(int irq, void *private)
{
struct iio_dev *indio_dev = private;
struct fxas21002c_data *data = iio_priv(indio_dev);
data->timestamp = iio_get_time_ns(indio_dev);
return IRQ_WAKE_THREAD;
}
static irqreturn_t fxas21002c_data_rdy_thread(int irq, void *private)
{
struct iio_dev *indio_dev = private;
struct fxas21002c_data *data = iio_priv(indio_dev);
unsigned int data_ready;
int ret;
ret = regmap_field_read(data->regmap_fields[F_SRC_DRDY], &data_ready);
if (ret < 0)
return IRQ_NONE;
if (!data_ready)
return IRQ_NONE;
iio_trigger_poll_chained(data->dready_trig);
return IRQ_HANDLED;
}
static int fxas21002c_trigger_probe(struct fxas21002c_data *data)
{
struct device *dev = regmap_get_device(data->regmap);
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct device_node *np = indio_dev->dev.of_node;
unsigned long irq_trig;
bool irq_open_drain;
int irq1;
int ret;
if (!data->irq)
return 0;
irq1 = of_irq_get_byname(np, "INT1");
if (irq1 == data->irq) {
dev_info(dev, "using interrupt line INT1\n");
ret = regmap_field_write(data->regmap_fields[F_INT_CFG_DRDY],
1);
if (ret < 0)
return ret;
}
dev_info(dev, "using interrupt line INT2\n");
irq_open_drain = of_property_read_bool(np, "drive-open-drain");
data->dready_trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
indio_dev->name,
indio_dev->id);
if (!data->dready_trig)
return -ENOMEM;
irq_trig = irqd_get_trigger_type(irq_get_irq_data(data->irq));
if (irq_trig == IRQF_TRIGGER_RISING) {
ret = regmap_field_write(data->regmap_fields[F_IPOL], 1);
if (ret < 0)
return ret;
}
if (irq_open_drain)
irq_trig |= IRQF_SHARED;
ret = devm_request_threaded_irq(dev, data->irq,
fxas21002c_data_rdy_handler,
fxas21002c_data_rdy_thread,
irq_trig, "fxas21002c_data_ready",
indio_dev);
if (ret < 0)
return ret;
data->dready_trig->dev.parent = dev;
data->dready_trig->ops = &fxas21002c_trigger_ops;
iio_trigger_set_drvdata(data->dready_trig, indio_dev);
return devm_iio_trigger_register(dev, data->dready_trig);
}
static int fxas21002c_power_enable(struct fxas21002c_data *data)
{
int ret;
ret = regulator_enable(data->vdd);
if (ret < 0)
return ret;
ret = regulator_enable(data->vddio);
if (ret < 0) {
regulator_disable(data->vdd);
return ret;
}
return 0;
}
static void fxas21002c_power_disable(struct fxas21002c_data *data)
{
regulator_disable(data->vdd);
regulator_disable(data->vddio);
}
static void fxas21002c_power_disable_action(void *_data)
{
struct fxas21002c_data *data = _data;
fxas21002c_power_disable(data);
}
static int fxas21002c_regulators_get(struct fxas21002c_data *data)
{
struct device *dev = regmap_get_device(data->regmap);
data->vdd = devm_regulator_get(dev->parent, "vdd");
if (IS_ERR(data->vdd))
return PTR_ERR(data->vdd);
data->vddio = devm_regulator_get(dev->parent, "vddio");
return PTR_ERR_OR_ZERO(data->vddio);
}
int fxas21002c_core_probe(struct device *dev, struct regmap *regmap, int irq,
const char *name)
{
struct fxas21002c_data *data;
struct iio_dev *indio_dev;
struct regmap_field *f;
int i;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
dev_set_drvdata(dev, indio_dev);
data->irq = irq;
data->regmap = regmap;
for (i = 0; i < F_MAX_FIELDS; i++) {
f = devm_regmap_field_alloc(dev, data->regmap,
fxas21002c_reg_fields[i]);
if (IS_ERR(f))
return PTR_ERR(f);
data->regmap_fields[i] = f;
}
mutex_init(&data->lock);
ret = fxas21002c_regulators_get(data);
if (ret < 0)
return ret;
ret = fxas21002c_power_enable(data);
if (ret < 0)
return ret;
ret = devm_add_action_or_reset(dev, fxas21002c_power_disable_action,
data);
if (ret < 0)
return ret;
ret = fxas21002c_chip_init(data);
if (ret < 0)
return ret;
indio_dev->dev.parent = dev;
indio_dev->channels = fxas21002c_channels;
indio_dev->num_channels = ARRAY_SIZE(fxas21002c_channels);
indio_dev->name = name;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &fxas21002c_info;
ret = fxas21002c_trigger_probe(data);
if (ret < 0)
return ret;
ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
fxas21002c_trigger_handler, NULL);
if (ret < 0)
return ret;
ret = pm_runtime_set_active(dev);
if (ret)
return ret;
pm_runtime_enable(dev);
pm_runtime_set_autosuspend_delay(dev, 2000);
pm_runtime_use_autosuspend(dev);
ret = iio_device_register(indio_dev);
if (ret < 0)
goto pm_disable;
return 0;
pm_disable:
pm_runtime_disable(dev);
pm_runtime_set_suspended(dev);
pm_runtime_put_noidle(dev);
return ret;
}
EXPORT_SYMBOL_GPL(fxas21002c_core_probe);
void fxas21002c_core_remove(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
iio_device_unregister(indio_dev);
pm_runtime_disable(dev);
pm_runtime_set_suspended(dev);
pm_runtime_put_noidle(dev);
}
EXPORT_SYMBOL_GPL(fxas21002c_core_remove);
static int __maybe_unused fxas21002c_suspend(struct device *dev)
{
struct fxas21002c_data *data = iio_priv(dev_get_drvdata(dev));
fxas21002c_mode_set(data, FXAS21002C_MODE_STANDBY);
fxas21002c_power_disable(data);
return 0;
}
static int __maybe_unused fxas21002c_resume(struct device *dev)
{
struct fxas21002c_data *data = iio_priv(dev_get_drvdata(dev));
int ret;
ret = fxas21002c_power_enable(data);
if (ret < 0)
return ret;
return fxas21002c_mode_set(data, data->prev_mode);
}
static int __maybe_unused fxas21002c_runtime_suspend(struct device *dev)
{
struct fxas21002c_data *data = iio_priv(dev_get_drvdata(dev));
return fxas21002c_mode_set(data, FXAS21002C_MODE_READY);
}
static int __maybe_unused fxas21002c_runtime_resume(struct device *dev)
{
struct fxas21002c_data *data = iio_priv(dev_get_drvdata(dev));
return fxas21002c_mode_set(data, FXAS21002C_MODE_ACTIVE);
}
const struct dev_pm_ops fxas21002c_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(fxas21002c_suspend, fxas21002c_resume)
SET_RUNTIME_PM_OPS(fxas21002c_runtime_suspend,
fxas21002c_runtime_resume, NULL)
};
EXPORT_SYMBOL_GPL(fxas21002c_pm_ops);
MODULE_AUTHOR("Rui Miguel Silva <rui.silva@linaro.org>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("FXAS21002C Gyro driver");
// SPDX-License-Identifier: GPL-2.0
/*
* Driver for NXP FXAS21002C Gyroscope - I2C
*
* Copyright (C) 2018 Linaro Ltd.
*/
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include "fxas21002c.h"
static const struct regmap_config fxas21002c_regmap_i2c_conf = {
.reg_bits = 8,
.val_bits = 8,
.max_register = FXAS21002C_REG_CTRL3,
};
static int fxas21002c_i2c_probe(struct i2c_client *i2c)
{
struct regmap *regmap;
regmap = devm_regmap_init_i2c(i2c, &fxas21002c_regmap_i2c_conf);
if (IS_ERR(regmap)) {
dev_err(&i2c->dev, "Failed to register i2c regmap: %ld\n",
PTR_ERR(regmap));
return PTR_ERR(regmap);
}
return fxas21002c_core_probe(&i2c->dev, regmap, i2c->irq, i2c->name);
}
static int fxas21002c_i2c_remove(struct i2c_client *i2c)
{
fxas21002c_core_remove(&i2c->dev);
return 0;
}
static const struct i2c_device_id fxas21002c_i2c_id[] = {
{ "fxas21002c", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, fxas21002c_i2c_id);
static const struct of_device_id fxas21002c_i2c_of_match[] = {
{ .compatible = "nxp,fxas21002c", },
{ }
};
MODULE_DEVICE_TABLE(of, fxas21002c_i2c_of_match);
static struct i2c_driver fxas21002c_i2c_driver = {
.driver = {
.name = "fxas21002c_i2c",
.pm = &fxas21002c_pm_ops,
.of_match_table = fxas21002c_i2c_of_match,
},
.probe_new = fxas21002c_i2c_probe,
.remove = fxas21002c_i2c_remove,
.id_table = fxas21002c_i2c_id,
};
module_i2c_driver(fxas21002c_i2c_driver);
MODULE_AUTHOR("Rui Miguel Silva <rui.silva@linaro.org>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("FXAS21002C I2C Gyro driver");
// SPDX-License-Identifier: GPL-2.0
/*
* Driver for NXP Fxas21002c Gyroscope - SPI
*
* Copyright (C) 2019 Linaro Ltd.
*/
#include <linux/err.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include "fxas21002c.h"
static const struct regmap_config fxas21002c_regmap_spi_conf = {
.reg_bits = 8,
.val_bits = 8,
.max_register = FXAS21002C_REG_CTRL3,
};
static int fxas21002c_spi_probe(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);
struct regmap *regmap;
regmap = devm_regmap_init_spi(spi, &fxas21002c_regmap_spi_conf);
if (IS_ERR(regmap)) {
dev_err(&spi->dev, "Failed to register spi regmap: %ld\n",
PTR_ERR(regmap));
return PTR_ERR(regmap);
}
return fxas21002c_core_probe(&spi->dev, regmap, spi->irq, id->name);
}
static int fxas21002c_spi_remove(struct spi_device *spi)
{
fxas21002c_core_remove(&spi->dev);
return 0;
}
static const struct spi_device_id fxas21002c_spi_id[] = {
{ "fxas21002c", 0 },
{ }
};
MODULE_DEVICE_TABLE(spi, fxas21002c_spi_id);
static const struct of_device_id fxas21002c_spi_of_match[] = {
{ .compatible = "nxp,fxas21002c", },
{ }
};
MODULE_DEVICE_TABLE(of, fxas21002c_spi_of_match);
static struct spi_driver fxas21002c_spi_driver = {
.driver = {
.name = "fxas21002c_spi",
.pm = &fxas21002c_pm_ops,
.of_match_table = fxas21002c_spi_of_match,
},
.probe = fxas21002c_spi_probe,
.remove = fxas21002c_spi_remove,
.id_table = fxas21002c_spi_id,
};
module_spi_driver(fxas21002c_spi_driver);
MODULE_AUTHOR("Rui Miguel Silva <rui.silva@linaro.org>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("FXAS21002C SPI Gyro driver");
......@@ -865,7 +865,7 @@ static int mpu3050_power_up(struct mpu3050 *mpu3050)
dev_err(mpu3050->dev, "error setting power mode\n");
return ret;
}
msleep(10);
usleep_range(10000, 20000);
return 0;
}
......
......@@ -9,7 +9,7 @@ config IIO_ST_LSM6DSX
help
Say yes here to build support for STMicroelectronics LSM6DSx imu
sensor. Supported devices: lsm6ds3, lsm6ds3h, lsm6dsl, lsm6dsm,
ism330dlc, lsm6dso, lsm6dsox, asm330lhh
ism330dlc, lsm6dso, lsm6dsox, asm330lhh, lsm6dsr
To compile this driver as a module, choose M here: the module
will be called st_lsm6dsx.
......
......@@ -22,6 +22,7 @@
#define ST_LSM6DSO_DEV_NAME "lsm6dso"
#define ST_ASM330LHH_DEV_NAME "asm330lhh"
#define ST_LSM6DSOX_DEV_NAME "lsm6dsox"
#define ST_LSM6DSR_DEV_NAME "lsm6dsr"
enum st_lsm6dsx_hw_id {
ST_LSM6DS3_ID,
......@@ -32,6 +33,7 @@ enum st_lsm6dsx_hw_id {
ST_LSM6DSO_ID,
ST_ASM330LHH_ID,
ST_LSM6DSOX_ID,
ST_LSM6DSR_ID,
ST_LSM6DSX_MAX_ID,
};
......
......@@ -13,9 +13,9 @@
* (e.g. Gx, Gy, Gz, Ax, Ay, Az), then data are repeated depending on the
* value of the decimation factor and ODR set for each FIFO data set.
*
* LSM6DSO/LSM6DSOX/ASM330LHH: The FIFO buffer can be configured to store data
* from gyroscope and accelerometer. Each sample is queued with a tag (1B)
* indicating data source (gyroscope, accelerometer, hw timer).
* LSM6DSO/LSM6DSOX/ASM330LHH/LSM6DSR: The FIFO buffer can be configured to
* store data from gyroscope and accelerometer. Each sample is queued with
* a tag (1B) indicating data source (gyroscope, accelerometer, hw timer).
*
* FIFO supported modes:
* - BYPASS: FIFO disabled
......@@ -506,7 +506,7 @@ st_lsm6dsx_push_tagged_data(struct st_lsm6dsx_hw *hw, u8 tag,
}
/**
* st_lsm6dsx_read_tagged_fifo() - LSM6DSO/LSM6DSOX/ASM330LHH read FIFO routine
* st_lsm6dsx_read_tagged_fifo() - tagged hw FIFO read routine
* @hw: Pointer to instance of struct st_lsm6dsx_hw.
*
* Read samples from the hw FIFO and push them to IIO buffers.
......@@ -517,7 +517,6 @@ int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw)
{
u16 pattern_len = hw->sip * ST_LSM6DSX_TAGGED_SAMPLE_SIZE;
u16 fifo_len, fifo_diff_mask;
struct st_lsm6dsx_sensor *acc_sensor, *gyro_sensor;
u8 iio_buff[ST_LSM6DSX_IIO_BUFF_SIZE], tag;
bool reset_ts = false;
int i, err, read_len;
......@@ -539,9 +538,6 @@ int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw)
if (!fifo_len)
return 0;
acc_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
gyro_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_GYRO]);
for (read_len = 0; read_len < fifo_len; read_len += pattern_len) {
err = st_lsm6dsx_read_block(hw,
ST_LSM6DSX_REG_FIFO_OUT_TAG_ADDR,
......
......@@ -23,7 +23,7 @@
* - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000
* - FIFO size: 4KB
*
* - LSM6DSO/LSM6DSOX/ASM330LHH
* - LSM6DSO/LSM6DSOX/ASM330LHH/LSM6DSR
* - Accelerometer/Gyroscope supported ODR [Hz]: 13, 26, 52, 104, 208, 416
* - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16
* - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000
......@@ -62,37 +62,19 @@
#define ST_LSM6DSX_REG_INT2_ON_INT1_ADDR 0x13
#define ST_LSM6DSX_REG_INT2_ON_INT1_MASK BIT(5)
#define ST_LSM6DSX_REG_ACC_ODR_ADDR 0x10
#define ST_LSM6DSX_REG_ACC_ODR_MASK GENMASK(7, 4)
#define ST_LSM6DSX_REG_ACC_FS_ADDR 0x10
#define ST_LSM6DSX_REG_ACC_FS_MASK GENMASK(3, 2)
#define ST_LSM6DSX_REG_ACC_OUT_X_L_ADDR 0x28
#define ST_LSM6DSX_REG_ACC_OUT_Y_L_ADDR 0x2a
#define ST_LSM6DSX_REG_ACC_OUT_Z_L_ADDR 0x2c
#define ST_LSM6DSX_REG_GYRO_ODR_ADDR 0x11
#define ST_LSM6DSX_REG_GYRO_ODR_MASK GENMASK(7, 4)
#define ST_LSM6DSX_REG_GYRO_FS_ADDR 0x11
#define ST_LSM6DSX_REG_GYRO_FS_MASK GENMASK(3, 2)
#define ST_LSM6DSX_REG_GYRO_OUT_X_L_ADDR 0x22
#define ST_LSM6DSX_REG_GYRO_OUT_Y_L_ADDR 0x24
#define ST_LSM6DSX_REG_GYRO_OUT_Z_L_ADDR 0x26
#define ST_LSM6DSX_ACC_FS_2G_GAIN IIO_G_TO_M_S_2(61)
#define ST_LSM6DSX_ACC_FS_4G_GAIN IIO_G_TO_M_S_2(122)
#define ST_LSM6DSX_ACC_FS_8G_GAIN IIO_G_TO_M_S_2(244)
#define ST_LSM6DSX_ACC_FS_16G_GAIN IIO_G_TO_M_S_2(488)
#define ST_LSM6DSX_GYRO_FS_245_GAIN IIO_DEGREE_TO_RAD(8750)
#define ST_LSM6DSX_GYRO_FS_500_GAIN IIO_DEGREE_TO_RAD(17500)
#define ST_LSM6DSX_GYRO_FS_1000_GAIN IIO_DEGREE_TO_RAD(35000)
#define ST_LSM6DSX_GYRO_FS_2000_GAIN IIO_DEGREE_TO_RAD(70000)
static const struct st_lsm6dsx_odr_table_entry st_lsm6dsx_odr_table[] = {
[ST_LSM6DSX_ID_ACC] = {
.reg = {
.addr = ST_LSM6DSX_REG_ACC_ODR_ADDR,
.mask = ST_LSM6DSX_REG_ACC_ODR_MASK,
.addr = 0x10,
.mask = GENMASK(7, 4),
},
.odr_avl[0] = { 13, 0x01 },
.odr_avl[1] = { 26, 0x02 },
......@@ -103,8 +85,8 @@ static const struct st_lsm6dsx_odr_table_entry st_lsm6dsx_odr_table[] = {
},
[ST_LSM6DSX_ID_GYRO] = {
.reg = {
.addr = ST_LSM6DSX_REG_GYRO_ODR_ADDR,
.mask = ST_LSM6DSX_REG_GYRO_ODR_MASK,
.addr = 0x11,
.mask = GENMASK(7, 4),
},
.odr_avl[0] = { 13, 0x01 },
.odr_avl[1] = { 26, 0x02 },
......@@ -118,23 +100,23 @@ static const struct st_lsm6dsx_odr_table_entry st_lsm6dsx_odr_table[] = {
static const struct st_lsm6dsx_fs_table_entry st_lsm6dsx_fs_table[] = {
[ST_LSM6DSX_ID_ACC] = {
.reg = {
.addr = ST_LSM6DSX_REG_ACC_FS_ADDR,
.mask = ST_LSM6DSX_REG_ACC_FS_MASK,
.addr = 0x10,
.mask = GENMASK(3, 2),
},
.fs_avl[0] = { ST_LSM6DSX_ACC_FS_2G_GAIN, 0x0 },
.fs_avl[1] = { ST_LSM6DSX_ACC_FS_4G_GAIN, 0x2 },
.fs_avl[2] = { ST_LSM6DSX_ACC_FS_8G_GAIN, 0x3 },
.fs_avl[3] = { ST_LSM6DSX_ACC_FS_16G_GAIN, 0x1 },
.fs_avl[0] = { IIO_G_TO_M_S_2(61), 0x0 },
.fs_avl[1] = { IIO_G_TO_M_S_2(122), 0x2 },
.fs_avl[2] = { IIO_G_TO_M_S_2(244), 0x3 },
.fs_avl[3] = { IIO_G_TO_M_S_2(488), 0x1 },
},
[ST_LSM6DSX_ID_GYRO] = {
.reg = {
.addr = ST_LSM6DSX_REG_GYRO_FS_ADDR,
.mask = ST_LSM6DSX_REG_GYRO_FS_MASK,
.addr = 0x11,
.mask = GENMASK(3, 2),
},
.fs_avl[0] = { ST_LSM6DSX_GYRO_FS_245_GAIN, 0x0 },
.fs_avl[1] = { ST_LSM6DSX_GYRO_FS_500_GAIN, 0x1 },
.fs_avl[2] = { ST_LSM6DSX_GYRO_FS_1000_GAIN, 0x2 },
.fs_avl[3] = { ST_LSM6DSX_GYRO_FS_2000_GAIN, 0x3 },
.fs_avl[0] = { IIO_DEGREE_TO_RAD(8750), 0x0 },
.fs_avl[1] = { IIO_DEGREE_TO_RAD(17500), 0x1 },
.fs_avl[2] = { IIO_DEGREE_TO_RAD(35000), 0x2 },
.fs_avl[3] = { IIO_DEGREE_TO_RAD(70000), 0x3 },
}
};
......@@ -387,6 +369,71 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
},
},
},
{
.wai = 0x6b,
.max_fifo_size = 512,
.id = {
[0] = ST_LSM6DSR_ID,
},
.batch = {
[ST_LSM6DSX_ID_ACC] = {
.addr = 0x09,
.mask = GENMASK(3, 0),
},
[ST_LSM6DSX_ID_GYRO] = {
.addr = 0x09,
.mask = GENMASK(7, 4),
},
},
.fifo_ops = {
.read_fifo = st_lsm6dsx_read_tagged_fifo,
.fifo_th = {
.addr = 0x07,
.mask = GENMASK(8, 0),
},
.fifo_diff = {
.addr = 0x3a,
.mask = GENMASK(8, 0),
},
.th_wl = 1,
},
.ts_settings = {
.timer_en = {
.addr = 0x19,
.mask = BIT(5),
},
.decimator = {
.addr = 0x0a,
.mask = GENMASK(7, 6),
},
},
.shub_settings = {
.page_mux = {
.addr = 0x01,
.mask = BIT(6),
},
.master_en = {
.addr = 0x14,
.mask = BIT(2),
},
.pullup_en = {
.addr = 0x14,
.mask = BIT(3),
},
.aux_sens = {
.addr = 0x14,
.mask = GENMASK(1, 0),
},
.wr_once = {
.addr = 0x14,
.mask = BIT(6),
},
.shub_out = 0x02,
.slv0_addr = 0x15,
.dw_slv0_addr = 0x21,
.batch_en = BIT(3),
}
},
};
static const struct iio_chan_spec st_lsm6dsx_acc_channels[] = {
......
......@@ -73,6 +73,10 @@ static const struct of_device_id st_lsm6dsx_i2c_of_match[] = {
.compatible = "st,lsm6dsox",
.data = (void *)ST_LSM6DSOX_ID,
},
{
.compatible = "st,lsm6dsr",
.data = (void *)ST_LSM6DSR_ID,
},
{},
};
MODULE_DEVICE_TABLE(of, st_lsm6dsx_i2c_of_match);
......@@ -86,6 +90,7 @@ static const struct i2c_device_id st_lsm6dsx_i2c_id_table[] = {
{ ST_LSM6DSO_DEV_NAME, ST_LSM6DSO_ID },
{ ST_ASM330LHH_DEV_NAME, ST_ASM330LHH_ID },
{ ST_LSM6DSOX_DEV_NAME, ST_LSM6DSOX_ID },
{ ST_LSM6DSR_DEV_NAME, ST_LSM6DSR_ID },
{},
};
MODULE_DEVICE_TABLE(i2c, st_lsm6dsx_i2c_id_table);
......
......@@ -73,6 +73,10 @@ static const struct of_device_id st_lsm6dsx_spi_of_match[] = {
.compatible = "st,lsm6dsox",
.data = (void *)ST_LSM6DSOX_ID,
},
{
.compatible = "st,lsm6dsr",
.data = (void *)ST_LSM6DSR_ID,
},
{},
};
MODULE_DEVICE_TABLE(of, st_lsm6dsx_spi_of_match);
......@@ -86,6 +90,7 @@ static const struct spi_device_id st_lsm6dsx_spi_id_table[] = {
{ ST_LSM6DSO_DEV_NAME, ST_LSM6DSO_ID },
{ ST_ASM330LHH_DEV_NAME, ST_ASM330LHH_ID },
{ ST_LSM6DSOX_DEV_NAME, ST_LSM6DSOX_ID },
{ ST_LSM6DSR_DEV_NAME, ST_LSM6DSR_ID },
{},
};
MODULE_DEVICE_TABLE(spi, st_lsm6dsx_spi_id_table);
......
......@@ -68,12 +68,19 @@ config RFD77402
module will be called rfd77402.
config SRF04
tristate "Devantech SRF04 ultrasonic ranger sensor"
tristate "GPIO bitbanged ultrasonic ranger sensor (SRF04, MB1000)"
depends on GPIOLIB
help
Say Y here to build a driver for Devantech SRF04 ultrasonic
Say Y here to build a driver for GPIO bitbanged ultrasonic
ranger sensor. This driver can be used to measure the distance
of objects. It is using two GPIOs.
Actually Supported types are:
- Devantech SRF04
- Maxbotix mb1000
- Maxbotix mb1010
- Maxbotix mb1020
- Maxbotix mb1030
- Maxbotix mb1040
To compile this driver as a module, choose M here: the
module will be called srf04.
......
......@@ -23,7 +23,7 @@
* trig: --+ +------------------------------------------------------
* ^ ^
* |<->|
* udelay(10)
* udelay(trigger_pulse_us)
*
* ultra +-+ +-+ +-+
* sonic | | | | | |
......@@ -48,6 +48,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/sched.h>
......@@ -56,6 +57,10 @@
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
struct srf04_cfg {
unsigned long trigger_pulse_us;
};
struct srf04_data {
struct device *dev;
struct gpio_desc *gpiod_trig;
......@@ -66,6 +71,15 @@ struct srf04_data {
ktime_t ts_falling;
struct completion rising;
struct completion falling;
const struct srf04_cfg *cfg;
};
static const struct srf04_cfg srf04_cfg = {
.trigger_pulse_us = 10,
};
static const struct srf04_cfg mb_lv_cfg = {
.trigger_pulse_us = 20,
};
static irqreturn_t srf04_handle_irq(int irq, void *dev_id)
......@@ -102,7 +116,7 @@ static int srf04_read(struct srf04_data *data)
reinit_completion(&data->falling);
gpiod_set_value(data->gpiod_trig, 1);
udelay(10);
udelay(data->cfg->trigger_pulse_us);
gpiod_set_value(data->gpiod_trig, 0);
/* it cannot take more than 20 ms */
......@@ -215,6 +229,18 @@ static const struct iio_chan_spec srf04_chan_spec[] = {
},
};
static const struct of_device_id of_srf04_match[] = {
{ .compatible = "devantech,srf04", .data = &srf04_cfg},
{ .compatible = "maxbotix,mb1000", .data = &mb_lv_cfg},
{ .compatible = "maxbotix,mb1010", .data = &mb_lv_cfg},
{ .compatible = "maxbotix,mb1020", .data = &mb_lv_cfg},
{ .compatible = "maxbotix,mb1030", .data = &mb_lv_cfg},
{ .compatible = "maxbotix,mb1040", .data = &mb_lv_cfg},
{},
};
MODULE_DEVICE_TABLE(of, of_srf04_match);
static int srf04_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
......@@ -230,6 +256,7 @@ static int srf04_probe(struct platform_device *pdev)
data = iio_priv(indio_dev);
data->dev = dev;
data->cfg = of_match_device(of_srf04_match, dev)->data;
mutex_init(&data->lock);
init_completion(&data->rising);
......@@ -280,13 +307,6 @@ static int srf04_probe(struct platform_device *pdev)
return devm_iio_device_register(dev, indio_dev);
}
static const struct of_device_id of_srf04_match[] = {
{ .compatible = "devantech,srf04", },
{},
};
MODULE_DEVICE_TABLE(of, of_srf04_match);
static struct platform_driver srf04_driver = {
.probe = srf04_probe,
.driver = {
......
......@@ -210,6 +210,9 @@ static int max31856_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT_PLUS_MICRO;
}
break;
default:
ret = -EINVAL;
break;
}
return ret;
......
......@@ -5,22 +5,25 @@
* Copyright 2011 Analog Devices Inc.
*/
#include <asm/div64.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/spi/spi.h>
#include <linux/regulator/consumer.h>
#include <linux/err.h>
#include <linux/module.h>
#include <asm/div64.h>
#include <linux/sysfs.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include "dds.h"
#include "ad9832.h"
#include "dds.h"
/* Registers */
#define AD9832_FREQ0LL 0x0
......@@ -93,7 +96,7 @@ struct ad9832_state {
struct spi_device *spi;
struct regulator *avdd;
struct regulator *dvdd;
unsigned long mclk;
struct clk *mclk;
unsigned short ctrl_fp;
unsigned short ctrl_ss;
unsigned short ctrl_src;
......@@ -128,10 +131,10 @@ static int ad9832_write_frequency(struct ad9832_state *st,
{
unsigned long regval;
if (fout > (st->mclk / 2))
if (fout > (clk_get_rate(st->mclk) / 2))
return -EINVAL;
regval = ad9832_calc_freqreg(st->mclk, fout);
regval = ad9832_calc_freqreg(clk_get_rate(st->mclk), fout);
st->freq_data[0] = cpu_to_be16((AD9832_CMD_FRE8BITSW << CMD_SHIFT) |
(addr << ADD_SHIFT) |
......@@ -332,7 +335,16 @@ static int ad9832_probe(struct spi_device *spi)
goto error_disable_avdd;
}
st->mclk = pdata->mclk;
st->mclk = devm_clk_get(&spi->dev, "mclk");
if (IS_ERR(st->mclk)) {
ret = PTR_ERR(st->mclk);
goto error_disable_dvdd;
}
ret = clk_prepare_enable(st->mclk);
if (ret < 0)
goto error_disable_dvdd;
st->spi = spi;
mutex_init(&st->lock);
......@@ -383,39 +395,41 @@ static int ad9832_probe(struct spi_device *spi)
ret = spi_sync(st->spi, &st->msg);
if (ret) {
dev_err(&spi->dev, "device init failed\n");
goto error_disable_dvdd;
goto error_unprepare_mclk;
}
ret = ad9832_write_frequency(st, AD9832_FREQ0HM, pdata->freq0);
if (ret)
goto error_disable_dvdd;
goto error_unprepare_mclk;
ret = ad9832_write_frequency(st, AD9832_FREQ1HM, pdata->freq1);
if (ret)
goto error_disable_dvdd;
goto error_unprepare_mclk;
ret = ad9832_write_phase(st, AD9832_PHASE0H, pdata->phase0);
if (ret)
goto error_disable_dvdd;
goto error_unprepare_mclk;
ret = ad9832_write_phase(st, AD9832_PHASE1H, pdata->phase1);
if (ret)
goto error_disable_dvdd;
goto error_unprepare_mclk;
ret = ad9832_write_phase(st, AD9832_PHASE2H, pdata->phase2);
if (ret)
goto error_disable_dvdd;
goto error_unprepare_mclk;
ret = ad9832_write_phase(st, AD9832_PHASE3H, pdata->phase3);
if (ret)
goto error_disable_dvdd;
goto error_unprepare_mclk;
ret = iio_device_register(indio_dev);
if (ret)
goto error_disable_dvdd;
goto error_unprepare_mclk;
return 0;
error_unprepare_mclk:
clk_disable_unprepare(st->mclk);
error_disable_dvdd:
regulator_disable(st->dvdd);
error_disable_avdd:
......@@ -430,6 +444,7 @@ static int ad9832_remove(struct spi_device *spi)
struct ad9832_state *st = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
clk_disable_unprepare(st->mclk);
regulator_disable(st->dvdd);
regulator_disable(st->avdd);
......
......@@ -23,7 +23,6 @@
*/
struct ad9832_platform_data {
unsigned long mclk;
unsigned long freq0;
unsigned long freq1;
unsigned short phase0;
......
......@@ -285,7 +285,7 @@ ssize_t ad9834_show_out0_wavetype_available(struct device *dev,
struct ad9834_state *st = iio_priv(indio_dev);
char *str;
if ((st->devid == ID_AD9833) || (st->devid == ID_AD9837))
if (st->devid == ID_AD9833 || st->devid == ID_AD9837)
str = "sine triangle square";
else if (st->control & AD9834_OPBITEN)
str = "sine";
......
......@@ -73,6 +73,15 @@
#define TIM17_OC1 "tim17_oc1"
#if IS_REACHABLE(CONFIG_IIO_STM32_TIMER_TRIGGER)
bool is_stm32_timer_trigger(struct iio_trigger *trig);
#else
static inline bool is_stm32_timer_trigger(struct iio_trigger *trig)
{
#if IS_ENABLED(CONFIG_IIO_STM32_TIMER_TRIGGER)
pr_warn_once("stm32-timer-trigger not linked in\n");
#endif
return false;
}
#endif
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment