1
0
Fork 0

Staging patches for 4.5-rc1

Here is the big staging driver pull request for 4.5-rc1.  Lots of
 cleanups and fixes here, not as many as some releases, but 800+ isn't
 that bad.  Full details in the shortlog.  All of these have been in
 linux-next for a while.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iEYEABECAAYFAlaV0VgACgkQMUfUDdst+ykvAwCeIN/Ot3OuWD/d1RQrcUzh028H
 rlUAn3YOHCFlQhAnUI+9KMNjH0CwAJKp
 =Oij1
 -----END PGP SIGNATURE-----

Merge tag 'staging-4.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging

Pull staging driver updates from Greg KH:
 "Here is the big staging driver pull request for 4.5-rc1.

  Lots of cleanups and fixes here, not as many as some releases, but
  800+ isn't that bad.  Full details in the shortlog.  All of these have
  been in linux-next for a while"

* tag 'staging-4.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (843 commits)
  Revert "arm64: dts: Add dts files to enable ION on Hi6220 SoC."
  staging: gdm724x: constify tty_port_operations structs
  staging: gdm72xx: add userspace data struct
  staging: gdm72xx: Replace timeval with ktime_t
  iio: adc: ina2xx: Fix incorrect report of data endianness to userspace.
  iio: light: us5182d: Refactor read_raw function
  iio: light: us5182d: Add interrupt support and events
  iio: light: us5182d: Fix enable status inconcistency
  iio: Make IIO value formating function globally available.
  staging: emxx_udc: use list_first_entry_or_null()
  staging/emxx_udc: fix 64-bit warnings
  STAGING: COMEDI: Using kernel types in plx9080.h
  STAGING: COMEDI: Added spaces around binary operators in plx9080.h
  STAGING: COMEDI: Fixed format of comments in plx9080.h
  staging: comedi: comedilib.h: Coding style warning fix for block comments
  staging: comedi: s526: add macros for counter control reg values
  staging: comedi: s526: replace counter mode bitfield struct
  staging: comedi: check for more errors for zero-length write
  staging: comedi: simplify returned errors for comedi_write()
  staging: comedi: return error on "write" if no command set up
  ...
hifive-unleashed-5.1
Linus Torvalds 2016-01-13 10:13:36 -08:00
commit 39272dde8f
504 changed files with 20846 additions and 13902 deletions

View File

@ -0,0 +1,21 @@
What: /config/iio
Date: October 2015
KernelVersion: 4.4
Contact: linux-iio@vger.kernel.org
Description:
This represents Industrial IO configuration entry point
directory. It contains sub-groups corresponding to IIO
objects.
What: /config/iio/triggers
Date: October 2015
KernelVersion: 4.4
Description:
Industrial IO software triggers directory.
What: /config/iio/triggers/hrtimers
Date: October 2015
KernelVersion: 4.4
Description:
High resolution timers directory. Creating a directory here
will result in creating a hrtimer trigger in the IIO subsystem.

View File

@ -0,0 +1,24 @@
What: /sys/bus/iio/devices/iio:deviceX/in_allow_async_readout
Date: December 2015
KernelVersion: 4.4
Contact: linux-iio@vger.kernel.org
Description:
By default (value '0'), the capture thread checks for the Conversion
Ready Flag to being set prior to committing a new value to the sample
buffer. This synchronizes the in-chip conversion rate with the
in-driver readout rate at the cost of an additional register read.
Writing '1' will remove the polling for the Conversion Ready Flags to
save the additional i2c transaction, which will improve the bandwidth
available for reading data. However, samples can be occasionally skipped
or repeated, depending on the beat between the capture and conversion
rates.
What: /sys/bus/iio/devices/iio:deviceX/in_shunt_resistor
Date: December 2015
KernelVersion: 4.4
Contact: linux-iio@vger.kernel.org
Description:
The value of the shunt resistor may be known only at runtime fom an
eeprom content read by a client application. This attribute allows to
set its value in ohms.

View File

@ -20,6 +20,7 @@ adi,adt7476 +/-1C TDM Extended Temp Range I.C
adi,adt7490 +/-1C TDM Extended Temp Range I.C
adi,adxl345 Three-Axis Digital Accelerometer
adi,adxl346 Three-Axis Digital Accelerometer (backward-compatibility value "adi,adxl345" must be listed too)
ams,iaq-core AMS iAQ-Core VOC Sensor
at,24c08 i2c serial eeprom (24cxx)
atmel,24c00 i2c serial eeprom (24cxx)
atmel,24c01 i2c serial eeprom (24cxx)

View File

@ -7,13 +7,18 @@ Required properties:
* "fsl,mma8453"
* "fsl,mma8652"
* "fsl,mma8653"
- reg: the I2C address of the chip
Optional properties:
- interrupt-parent: should be the phandle for the interrupt controller
- interrupts: interrupt mapping for GPIO IRQ
- interrupt-names: should contain "INT1" and/or "INT2", the accelerometer's
interrupt line in use.
Example:
mma8453fc@1d {
@ -21,4 +26,5 @@ Example:
reg = <0x1d>;
interrupt-parent = <&gpio1>;
interrupts = <5 0>;
interrupt-names = "INT2";
};

View File

@ -0,0 +1,22 @@
Freescale imx7d ADC bindings
The devicetree bindings are for the ADC driver written for
imx7d SoC.
Required properties:
- compatible: Should be "fsl,imx7d-adc"
- reg: Offset and length of the register set for the ADC device
- interrupts: The interrupt number for the ADC device
- clocks: The root clock of the ADC controller
- clock-names: Must contain "adc", matching entry in the clocks property
- vref-supply: The regulator supply ADC reference voltage
Example:
adc1: adc@30610000 {
compatible = "fsl,imx7d-adc";
reg = <0x30610000 0x10000>;
interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX7D_ADC_ROOT_CLK>;
clock-names = "adc";
vref-supply = <&reg_vcc_3v3_mcu>;
};

View File

@ -10,16 +10,28 @@ must be specified.
Required properties:
- compatible: Must be one of the following, depending on the
model:
"mcp3001"
"mcp3002"
"mcp3004"
"mcp3008"
"mcp3201"
"mcp3202"
"mcp3204"
"mcp3208"
"mcp3301"
"mcp3001" (DEPRECATED)
"mcp3002" (DEPRECATED)
"mcp3004" (DEPRECATED)
"mcp3008" (DEPRECATED)
"mcp3201" (DEPRECATED)
"mcp3202" (DEPRECATED)
"mcp3204" (DEPRECATED)
"mcp3208" (DEPRECATED)
"mcp3301" (DEPRECATED)
"microchip,mcp3001"
"microchip,mcp3002"
"microchip,mcp3004"
"microchip,mcp3008"
"microchip,mcp3201"
"microchip,mcp3202"
"microchip,mcp3204"
"microchip,mcp3208"
"microchip,mcp3301"
NOTE: The use of the compatibles with no vendor prefix
is deprecated and only listed because old DT use them.
Examples:
spi_controller {

View File

@ -1,7 +1,8 @@
* Microchip mcp3422/3/4/6/7/8 chip family (ADC)
* Microchip mcp3421/2/3/4/6/7/8 chip family (ADC)
Required properties:
- compatible: Should be
"microchip,mcp3421" or
"microchip,mcp3422" or
"microchip,mcp3423" or
"microchip,mcp3424" or

View File

@ -0,0 +1,48 @@
* Palmas general purpose ADC IP block devicetree bindings
Channels list:
0 battery type
1 battery temp NTC (optional current source)
2 GP
3 temp (with ext. diode, optional current source)
4 GP
5 GP
6 VBAT_SENSE
7 VCC_SENSE
8 Backup Battery voltage
9 external charger (VCHG)
10 VBUS
11 DC-DC current probe (how does this work?)
12 internal die temp
13 internal die temp
14 USB ID pin voltage
15 test network
Required properties:
- compatible : Must be "ti,palmas-gpadc".
- #io-channel-cells: Should be set to <1>.
Optional sub-nodes:
ti,channel0-current-microamp: Channel 0 current in uA.
Values are rounded to derive 0uA, 5uA, 15uA, 20uA.
ti,channel3-current-microamp: Channel 3 current in uA.
Values are rounded to derive 0uA, 10uA, 400uA, 800uA.
ti,enable-extended-delay: Enable extended delay.
Example:
pmic {
compatible = "ti,twl6035-pmic", "ti,palmas-pmic";
...
gpadc {
compatible = "ti,palmas-gpadc";
interrupts = <18 0
16 0
17 0>;
#io-channel-cells = <1>;
ti,channel0-current-microamp = <5>;
ti,channel3-current-microamp = <10>;
};
};
...
};

View File

@ -1,7 +1,7 @@
* Texas Instruments' ADC128S052 and ADC122S021 ADC chip
* Texas Instruments' ADC128S052, ADC122S021 and ADC124S021 ADC chip
Required properties:
- compatible: Should be "ti,adc128s052" or "ti,adc122s021"
- compatible: Should be "ti,adc128s052", "ti,adc122s021" or "ti,adc124s021"
- reg: spi chip select number for the device
- vref-supply: The regulator supply for ADC reference voltage

View File

@ -0,0 +1,20 @@
* Texas Instruments' ADS8684 and ADS8688 ADC chip
Required properties:
- compatible: Should be "ti,ads8684" or "ti,ads8688"
- reg: spi chip select number for the device
Recommended properties:
- spi-max-frequency: Definition as per
Documentation/devicetree/bindings/spi/spi-bus.txt
Optional properties:
- vref-supply: The regulator supply for ADC reference voltage
Example:
adc@0 {
compatible = "ti,ads8688";
reg = <0>;
vref-supply = <&vdd_supply>;
spi-max-frequency = <1000000>;
};

View File

@ -0,0 +1,21 @@
Maxim MAX30100 heart rate and pulse oximeter sensor
* https://datasheets.maximintegrated.com/en/ds/MAX30100.pdf
Required properties:
- compatible: must be "maxim,max30100"
- reg: the I2C address of the sensor
- interrupt-parent: should be the phandle for the interrupt controller
- interrupts: the sole interrupt generated by the device
Refer to interrupt-controller/interrupts.txt for generic
interrupt client node bindings.
Example:
max30100@057 {
compatible = "maxim,max30100";
reg = <57>;
interrupt-parent = <&gpio1>;
interrupts = <16 2>;
};

View File

@ -7,13 +7,24 @@ Required properties:
Optional properties:
- upisemi,glass-coef: glass attenuation factor - compensation factor of
resolution 1000 for material transmittance.
- upisemi,dark-ths: array of 8 elements containing 16-bit thresholds (adc
counts) corresponding to every scale.
- upisemi,upper-dark-gain: 8-bit dark gain compensation factor(4 int and 4
fractional bits - Q4.4) applied when light > threshold
- upisemi,lower-dark-gain: 8-bit dark gain compensation factor(4 int and 4
fractional bits - Q4.4) applied when light < threshold
- upisemi,continuous: This chip has two power modes: one-shot (chip takes one
measurement and then shuts itself down) and continuous (
chip takes continuous measurements). The one-shot mode is
more power-friendly but the continuous mode may be more
reliable. If this property is specified the continuous
mode will be used instead of the default one-shot one for
raw reads.
If the optional properties are not specified these factors will default to the
values in the below example.
The glass-coef defaults to no compensation for the covering material.

View File

@ -36,6 +36,7 @@ Accelerometers:
- st,lsm303dlm-accel
- st,lsm330-accel
- st,lsm303agr-accel
- st,lis2dh12-accel
Gyroscopes:
- st,l3g4200d-gyro

View File

@ -0,0 +1,31 @@
Hi6220 SoC ION
===================================================================
Required properties:
- compatible : "hisilicon,hi6220-ion"
- list of the ION heaps
- heap name : maybe heap_sys_user@0
- heap id : id should be unique in the system.
- heap base : base ddr address of the heap,0 means that
it is dynamic.
- heap size : memory size and 0 means it is dynamic.
- heap type : the heap type of the heap, please also
see the define in ion.h(drivers/staging/android/uapi/ion.h)
-------------------------------------------------------------------
Example:
hi6220-ion {
compatible = "hisilicon,hi6220-ion";
heap_sys_user@0 {
heap-name = "sys_user";
heap-id = <0x0>;
heap-base = <0x0>;
heap-size = <0x0>;
heap-type = "ion_system";
};
heap_sys_contig@0 {
heap-name = "sys_contig";
heap-id = <0x1>;
heap-base = <0x0>;
heap-size = <0x0>;
heap-type = "ion_system_contig";
};
};

View File

@ -0,0 +1,93 @@
Industrial IIO configfs support
1. Overview
Configfs is a filesystem-based manager of kernel objects. IIO uses some
objects that could be easily configured using configfs (e.g.: devices,
triggers).
See Documentation/filesystems/configfs/configfs.txt for more information
about how configfs works.
2. Usage
In order to use configfs support in IIO we need to select it at compile
time via CONFIG_IIO_CONFIGFS config option.
Then, mount the configfs filesystem (usually under /config directory):
$ mkdir /config
$ mount -t configfs none /config
At this point, all default IIO groups will be created and can be accessed
under /config/iio. Next chapters will describe available IIO configuration
objects.
3. Software triggers
One of the IIO default configfs groups is the "triggers" group. It is
automagically accessible when the configfs is mounted and can be found
under /config/iio/triggers.
IIO software triggers implementation offers support for creating multiple
trigger types. A new trigger type is usually implemented as a separate
kernel module following the interface in include/linux/iio/sw_trigger.h:
/*
* drivers/iio/trigger/iio-trig-sample.c
* sample kernel module implementing a new trigger type
*/
#include <linux/iio/sw_trigger.h>
static struct iio_sw_trigger *iio_trig_sample_probe(const char *name)
{
/*
* This allocates and registers an IIO trigger plus other
* trigger type specific initialization.
*/
}
static int iio_trig_hrtimer_remove(struct iio_sw_trigger *swt)
{
/*
* This undoes the actions in iio_trig_sample_probe
*/
}
static const struct iio_sw_trigger_ops iio_trig_sample_ops = {
.probe = iio_trig_sample_probe,
.remove = iio_trig_sample_remove,
};
static struct iio_sw_trigger_type iio_trig_sample = {
.name = "trig-sample",
.owner = THIS_MODULE,
.ops = &iio_trig_sample_ops,
};
module_iio_sw_trigger_driver(iio_trig_sample);
Each trigger type has its own directory under /config/iio/triggers. Loading
iio-trig-sample module will create 'trig-sample' trigger type directory
/config/iio/triggers/trig-sample.
We support the following interrupt sources (trigger types):
* hrtimer, uses high resolution timers as interrupt source
3.1 Hrtimer triggers creation and destruction
Loading iio-trig-hrtimer module will register hrtimer trigger types allowing
users to create hrtimer triggers under /config/iio/triggers/hrtimer.
e.g:
$ mkdir /config/triggers/hrtimer/instance1
$ rmdir /config/triggers/hrtimer/instance1
Each trigger can have one or more attributes specific to the trigger type.
3.2 "hrtimer" trigger types attributes
"hrtimer" trigger type doesn't have any configurable attribute from /config dir.
It does introduce the sampling_frequency attribute to trigger directory.

View File

@ -22,6 +22,14 @@ if IIO_BUFFER
source "drivers/iio/buffer/Kconfig"
endif # IIO_BUFFER
config IIO_CONFIGFS
tristate "Enable IIO configuration via configfs"
select CONFIGFS_FS
help
This allows configuring various IIO bits through configfs
(e.g. software triggers). For more info see
Documentation/iio/iio_configfs.txt.
config IIO_TRIGGER
bool "Enable triggered sampling support"
help
@ -38,6 +46,14 @@ config IIO_CONSUMERS_PER_TRIGGER
This value controls the maximum number of consumers that a
given trigger may handle. Default is 2.
config IIO_SW_TRIGGER
tristate "Enable software triggers support"
select IIO_CONFIGFS
help
Provides IIO core support for software triggers. A software
trigger can be created via configfs or directly by a driver
using the API provided.
config IIO_TRIGGERED_EVENT
tristate
select IIO_TRIGGER
@ -50,8 +66,10 @@ source "drivers/iio/amplifiers/Kconfig"
source "drivers/iio/chemical/Kconfig"
source "drivers/iio/common/Kconfig"
source "drivers/iio/dac/Kconfig"
source "drivers/iio/dummy/Kconfig"
source "drivers/iio/frequency/Kconfig"
source "drivers/iio/gyro/Kconfig"
source "drivers/iio/health/Kconfig"
source "drivers/iio/humidity/Kconfig"
source "drivers/iio/imu/Kconfig"
source "drivers/iio/light/Kconfig"

View File

@ -7,6 +7,8 @@ industrialio-y := industrialio-core.o industrialio-event.o inkern.o
industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o
industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o
obj-$(CONFIG_IIO_SW_TRIGGER) += industrialio-sw-trigger.o
obj-$(CONFIG_IIO_TRIGGERED_EVENT) += industrialio-triggered-event.o
obj-y += accel/
@ -16,8 +18,10 @@ obj-y += buffer/
obj-y += chemical/
obj-y += common/
obj-y += dac/
obj-y += dummy/
obj-y += gyro/
obj-y += frequency/
obj-y += health/
obj-y += humidity/
obj-y += imu/
obj-y += light/

View File

@ -64,7 +64,7 @@ config IIO_ST_ACCEL_3AXIS
help
Say yes here to build support for STMicroelectronics accelerometers:
LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC,
LIS331DLH, LSM303DL, LSM303DLM, LSM330.
LIS331DLH, LSM303DL, LSM303DLM, LSM330, LIS2DH12.
This driver can also be built as a module. If so, these modules
will be created:
@ -107,6 +107,35 @@ config KXCJK1013
To compile this driver as a module, choose M here: the module will
be called kxcjk-1013.
config MMA7455
tristate
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
config MMA7455_I2C
tristate "Freescale MMA7455L/MMA7456L Accelerometer I2C Driver"
depends on I2C
select MMA7455
select REGMAP_I2C
help
Say yes here to build support for the Freescale MMA7455L and
MMA7456L 3-axis accelerometer.
To compile this driver as a module, choose M here: the module
will be called mma7455_i2c.
config MMA7455_SPI
tristate "Freescale MMA7455L/MMA7456L Accelerometer SPI Driver"
depends on SPI_MASTER
select MMA7455
select REGMAP_SPI
help
Say yes here to build support for the Freescale MMA7455L and
MMA7456L 3-axis accelerometer.
To compile this driver as a module, choose M here: the module
will be called mma7455_spi.
config MMA8452
tristate "Freescale MMA8452Q and similar Accelerometers Driver"
depends on I2C
@ -158,6 +187,17 @@ config MXC4005
To compile this driver as a module, choose M. The module will be
called mxc4005.
config MXC6255
tristate "Memsic MXC6255 Orientation Sensing Accelerometer Driver"
depends on I2C
select REGMAP_I2C
help
Say yes here to build support for the Memsic MXC6255 Orientation
Sensing Accelerometer Driver.
To compile this driver as a module, choose M here: the module will be
called mxc6255.
config STK8312
tristate "Sensortek STK8312 3-Axis Accelerometer Driver"
depends on I2C

View File

@ -10,6 +10,11 @@ obj-$(CONFIG_BMC150_ACCEL_SPI) += bmc150-accel-spi.o
obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o
obj-$(CONFIG_KXSD9) += kxsd9.o
obj-$(CONFIG_MMA7455) += mma7455_core.o
obj-$(CONFIG_MMA7455_I2C) += mma7455_i2c.o
obj-$(CONFIG_MMA7455_SPI) += mma7455_spi.o
obj-$(CONFIG_MMA8452) += mma8452.o
obj-$(CONFIG_MMA9551_CORE) += mma9551_core.o
@ -17,6 +22,7 @@ obj-$(CONFIG_MMA9551) += mma9551.o
obj-$(CONFIG_MMA9553) += mma9553.o
obj-$(CONFIG_MXC4005) += mxc4005.o
obj-$(CONFIG_MXC6255) += mxc6255.o
obj-$(CONFIG_STK8312) += stk8312.o
obj-$(CONFIG_STK8BA50) += stk8ba50.o

View File

@ -1623,24 +1623,22 @@ int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq,
}
}
ret = pm_runtime_set_active(dev);
if (ret)
goto err_trigger_unregister;
pm_runtime_enable(dev);
pm_runtime_set_autosuspend_delay(dev, BMC150_AUTO_SUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(dev);
ret = iio_device_register(indio_dev);
if (ret < 0) {
dev_err(dev, "Unable to register iio device\n");
goto err_trigger_unregister;
}
ret = pm_runtime_set_active(dev);
if (ret)
goto err_iio_unregister;
pm_runtime_enable(dev);
pm_runtime_set_autosuspend_delay(dev, BMC150_AUTO_SUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(dev);
return 0;
err_iio_unregister:
iio_device_unregister(indio_dev);
err_trigger_unregister:
bmc150_accel_unregister_triggers(data, BMC150_ACCEL_TRIGGERS - 1);
err_buffer_cleanup:
@ -1655,12 +1653,12 @@ int bmc150_accel_core_remove(struct device *dev)
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct bmc150_accel_data *data = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
pm_runtime_disable(data->dev);
pm_runtime_set_suspended(data->dev);
pm_runtime_put_noidle(data->dev);
iio_device_unregister(indio_dev);
bmc150_accel_unregister_triggers(data, BMC150_ACCEL_TRIGGERS - 1);
iio_triggered_buffer_cleanup(indio_dev);

View File

@ -1264,25 +1264,23 @@ static int kxcjk1013_probe(struct i2c_client *client,
goto err_trigger_unregister;
}
ret = iio_device_register(indio_dev);
if (ret < 0) {
dev_err(&client->dev, "unable to register iio device\n");
goto err_buffer_cleanup;
}
ret = pm_runtime_set_active(&client->dev);
if (ret)
goto err_iio_unregister;
goto err_buffer_cleanup;
pm_runtime_enable(&client->dev);
pm_runtime_set_autosuspend_delay(&client->dev,
KXCJK1013_SLEEP_DELAY_MS);
pm_runtime_use_autosuspend(&client->dev);
ret = iio_device_register(indio_dev);
if (ret < 0) {
dev_err(&client->dev, "unable to register iio device\n");
goto err_buffer_cleanup;
}
return 0;
err_iio_unregister:
iio_device_unregister(indio_dev);
err_buffer_cleanup:
if (data->dready_trig)
iio_triggered_buffer_cleanup(indio_dev);
@ -1302,12 +1300,12 @@ static int kxcjk1013_remove(struct i2c_client *client)
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct kxcjk1013_data *data = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
pm_runtime_disable(&client->dev);
pm_runtime_set_suspended(&client->dev);
pm_runtime_put_noidle(&client->dev);
iio_device_unregister(indio_dev);
if (data->dready_trig) {
iio_triggered_buffer_cleanup(indio_dev);
iio_trigger_unregister(data->dready_trig);

View File

@ -0,0 +1,19 @@
/*
* IIO accel driver for Freescale MMA7455L 3-axis 10-bit accelerometer
* Copyright 2015 Joachim Eastwood <manabian@gmail.com>
*
* 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.
*/
#ifndef __MMA7455_H
#define __MMA7455_H
extern const struct regmap_config mma7455_core_regmap;
int mma7455_core_probe(struct device *dev, struct regmap *regmap,
const char *name);
int mma7455_core_remove(struct device *dev);
#endif

View File

@ -0,0 +1,311 @@
/*
* IIO accel core driver for Freescale MMA7455L 3-axis 10-bit accelerometer
* Copyright 2015 Joachim Eastwood <manabian@gmail.com>
*
* 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.
*
* UNSUPPORTED hardware features:
* - 8-bit mode with different scales
* - INT1/INT2 interrupts
* - Offset calibration
* - Events
*/
#include <linux/delay.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include "mma7455.h"
#define MMA7455_REG_XOUTL 0x00
#define MMA7455_REG_XOUTH 0x01
#define MMA7455_REG_YOUTL 0x02
#define MMA7455_REG_YOUTH 0x03
#define MMA7455_REG_ZOUTL 0x04
#define MMA7455_REG_ZOUTH 0x05
#define MMA7455_REG_STATUS 0x09
#define MMA7455_STATUS_DRDY BIT(0)
#define MMA7455_REG_WHOAMI 0x0f
#define MMA7455_WHOAMI_ID 0x55
#define MMA7455_REG_MCTL 0x16
#define MMA7455_MCTL_MODE_STANDBY 0x00
#define MMA7455_MCTL_MODE_MEASURE 0x01
#define MMA7455_REG_CTL1 0x18
#define MMA7455_CTL1_DFBW_MASK BIT(7)
#define MMA7455_CTL1_DFBW_125HZ BIT(7)
#define MMA7455_CTL1_DFBW_62_5HZ 0
#define MMA7455_REG_TW 0x1e
/*
* When MMA7455 is used in 10-bit it has a fullscale of -8g
* corresponding to raw value -512. The userspace interface
* uses m/s^2 and we declare micro units.
* So scale factor is given by:
* g * 8 * 1e6 / 512 = 153228.90625, with g = 9.80665
*/
#define MMA7455_10BIT_SCALE 153229
struct mma7455_data {
struct regmap *regmap;
struct device *dev;
};
static int mma7455_drdy(struct mma7455_data *mma7455)
{
unsigned int reg;
int tries = 3;
int ret;
while (tries-- > 0) {
ret = regmap_read(mma7455->regmap, MMA7455_REG_STATUS, &reg);
if (ret)
return ret;
if (reg & MMA7455_STATUS_DRDY)
return 0;
msleep(20);
}
dev_warn(mma7455->dev, "data not ready\n");
return -EIO;
}
static irqreturn_t mma7455_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct mma7455_data *mma7455 = iio_priv(indio_dev);
u8 buf[16]; /* 3 x 16-bit channels + padding + ts */
int ret;
ret = mma7455_drdy(mma7455);
if (ret)
goto done;
ret = regmap_bulk_read(mma7455->regmap, MMA7455_REG_XOUTL, buf,
sizeof(__le16) * 3);
if (ret)
goto done;
iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns());
done:
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
static int mma7455_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct mma7455_data *mma7455 = iio_priv(indio_dev);
unsigned int reg;
__le16 data;
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (iio_buffer_enabled(indio_dev))
return -EBUSY;
ret = mma7455_drdy(mma7455);
if (ret)
return ret;
ret = regmap_bulk_read(mma7455->regmap, chan->address, &data,
sizeof(data));
if (ret)
return ret;
*val = sign_extend32(le16_to_cpu(data), 9);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 0;
*val2 = MMA7455_10BIT_SCALE;
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_SAMP_FREQ:
ret = regmap_read(mma7455->regmap, MMA7455_REG_CTL1, &reg);
if (ret)
return ret;
if (reg & MMA7455_CTL1_DFBW_MASK)
*val = 250;
else
*val = 125;
return IIO_VAL_INT;
}
return -EINVAL;
}
static int mma7455_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct mma7455_data *mma7455 = iio_priv(indio_dev);
int i;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
if (val == 250 && val2 == 0)
i = MMA7455_CTL1_DFBW_125HZ;
else if (val == 125 && val2 == 0)
i = MMA7455_CTL1_DFBW_62_5HZ;
else
return -EINVAL;
return regmap_update_bits(mma7455->regmap, MMA7455_REG_CTL1,
MMA7455_CTL1_DFBW_MASK, i);
case IIO_CHAN_INFO_SCALE:
/* In 10-bit mode there is only one scale available */
if (val == 0 && val2 == MMA7455_10BIT_SCALE)
return 0;
break;
}
return -EINVAL;
}
static IIO_CONST_ATTR(sampling_frequency_available, "125 250");
static struct attribute *mma7455_attributes[] = {
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
NULL
};
static const struct attribute_group mma7455_group = {
.attrs = mma7455_attributes,
};
static const struct iio_info mma7455_info = {
.attrs = &mma7455_group,
.read_raw = mma7455_read_raw,
.write_raw = mma7455_write_raw,
.driver_module = THIS_MODULE,
};
#define MMA7455_CHANNEL(axis, idx) { \
.type = IIO_ACCEL, \
.modified = 1, \
.address = MMA7455_REG_##axis##OUTL,\
.channel2 = IIO_MOD_##axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
BIT(IIO_CHAN_INFO_SCALE), \
.scan_index = idx, \
.scan_type = { \
.sign = 's', \
.realbits = 10, \
.storagebits = 16, \
.endianness = IIO_LE, \
}, \
}
static const struct iio_chan_spec mma7455_channels[] = {
MMA7455_CHANNEL(X, 0),
MMA7455_CHANNEL(Y, 1),
MMA7455_CHANNEL(Z, 2),
IIO_CHAN_SOFT_TIMESTAMP(3),
};
static const unsigned long mma7455_scan_masks[] = {0x7, 0};
const struct regmap_config mma7455_core_regmap = {
.reg_bits = 8,
.val_bits = 8,
.max_register = MMA7455_REG_TW,
};
EXPORT_SYMBOL_GPL(mma7455_core_regmap);
int mma7455_core_probe(struct device *dev, struct regmap *regmap,
const char *name)
{
struct mma7455_data *mma7455;
struct iio_dev *indio_dev;
unsigned int reg;
int ret;
ret = regmap_read(regmap, MMA7455_REG_WHOAMI, &reg);
if (ret) {
dev_err(dev, "unable to read reg\n");
return ret;
}
if (reg != MMA7455_WHOAMI_ID) {
dev_err(dev, "device id mismatch\n");
return -ENODEV;
}
indio_dev = devm_iio_device_alloc(dev, sizeof(*mma7455));
if (!indio_dev)
return -ENOMEM;
dev_set_drvdata(dev, indio_dev);
mma7455 = iio_priv(indio_dev);
mma7455->regmap = regmap;
mma7455->dev = dev;
indio_dev->info = &mma7455_info;
indio_dev->name = name;
indio_dev->dev.parent = dev;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = mma7455_channels;
indio_dev->num_channels = ARRAY_SIZE(mma7455_channels);
indio_dev->available_scan_masks = mma7455_scan_masks;
regmap_write(mma7455->regmap, MMA7455_REG_MCTL,
MMA7455_MCTL_MODE_MEASURE);
ret = iio_triggered_buffer_setup(indio_dev, NULL,
mma7455_trigger_handler, NULL);
if (ret) {
dev_err(dev, "unable to setup triggered buffer\n");
return ret;
}
ret = iio_device_register(indio_dev);
if (ret) {
dev_err(dev, "unable to register device\n");
iio_triggered_buffer_cleanup(indio_dev);
return ret;
}
return 0;
}
EXPORT_SYMBOL_GPL(mma7455_core_probe);
int mma7455_core_remove(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct mma7455_data *mma7455 = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
regmap_write(mma7455->regmap, MMA7455_REG_MCTL,
MMA7455_MCTL_MODE_STANDBY);
return 0;
}
EXPORT_SYMBOL_GPL(mma7455_core_remove);
MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
MODULE_DESCRIPTION("Freescale MMA7455L core accelerometer driver");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,56 @@
/*
* IIO accel I2C driver for Freescale MMA7455L 3-axis 10-bit accelerometer
* Copyright 2015 Joachim Eastwood <manabian@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include "mma7455.h"
static int mma7455_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct regmap *regmap;
const char *name = NULL;
regmap = devm_regmap_init_i2c(i2c, &mma7455_core_regmap);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
if (id)
name = id->name;
return mma7455_core_probe(&i2c->dev, regmap, name);
}
static int mma7455_i2c_remove(struct i2c_client *i2c)
{
return mma7455_core_remove(&i2c->dev);
}
static const struct i2c_device_id mma7455_i2c_ids[] = {
{ "mma7455", 0 },
{ "mma7456", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, mma7455_i2c_ids);
static struct i2c_driver mma7455_i2c_driver = {
.probe = mma7455_i2c_probe,
.remove = mma7455_i2c_remove,
.id_table = mma7455_i2c_ids,
.driver = {
.name = "mma7455-i2c",
},
};
module_i2c_driver(mma7455_i2c_driver);
MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
MODULE_DESCRIPTION("Freescale MMA7455L I2C accelerometer driver");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,52 @@
/*
* IIO accel SPI driver for Freescale MMA7455L 3-axis 10-bit accelerometer
* Copyright 2015 Joachim Eastwood <manabian@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include "mma7455.h"
static int mma7455_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, &mma7455_core_regmap);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
return mma7455_core_probe(&spi->dev, regmap, id->name);
}
static int mma7455_spi_remove(struct spi_device *spi)
{
return mma7455_core_remove(&spi->dev);
}
static const struct spi_device_id mma7455_spi_ids[] = {
{ "mma7455", 0 },
{ "mma7456", 0 },
{ }
};
MODULE_DEVICE_TABLE(spi, mma7455_spi_ids);
static struct spi_driver mma7455_spi_driver = {
.probe = mma7455_spi_probe,
.remove = mma7455_spi_remove,
.id_table = mma7455_spi_ids,
.driver = {
.name = "mma7455-spi",
},
};
module_spi_driver(mma7455_spi_driver);
MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
MODULE_DESCRIPTION("Freescale MMA7455L SPI accelerometer driver");
MODULE_LICENSE("GPL v2");

View File

@ -29,6 +29,7 @@
#include <linux/iio/events.h>
#include <linux/delay.h>
#include <linux/of_device.h>
#include <linux/of_irq.h>
#define MMA8452_STATUS 0x00
#define MMA8452_STATUS_DRDY (BIT(2) | BIT(1) | BIT(0))
@ -57,7 +58,6 @@
#define MMA8452_FF_MT_COUNT 0x18
#define MMA8452_TRANSIENT_CFG 0x1d
#define MMA8452_TRANSIENT_CFG_HPF_BYP BIT(0)
#define MMA8452_TRANSIENT_CFG_CHAN(chan) BIT(chan + 1)
#define MMA8452_TRANSIENT_CFG_ELE BIT(4)
#define MMA8452_TRANSIENT_SRC 0x1e
#define MMA8452_TRANSIENT_SRC_XTRANSE BIT(1)
@ -143,6 +143,13 @@ struct mma_chip_info {
u8 ev_count;
};
enum {
idx_x,
idx_y,
idx_z,
idx_ts,
};
static int mma8452_drdy(struct mma8452_data *data)
{
int tries = 150;
@ -816,31 +823,31 @@ static struct attribute_group mma8452_event_attribute_group = {
}
static const struct iio_chan_spec mma8452_channels[] = {
MMA8452_CHANNEL(X, 0, 12),
MMA8452_CHANNEL(Y, 1, 12),
MMA8452_CHANNEL(Z, 2, 12),
IIO_CHAN_SOFT_TIMESTAMP(3),
MMA8452_CHANNEL(X, idx_x, 12),
MMA8452_CHANNEL(Y, idx_y, 12),
MMA8452_CHANNEL(Z, idx_z, 12),
IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
};
static const struct iio_chan_spec mma8453_channels[] = {
MMA8452_CHANNEL(X, 0, 10),
MMA8452_CHANNEL(Y, 1, 10),
MMA8452_CHANNEL(Z, 2, 10),
IIO_CHAN_SOFT_TIMESTAMP(3),
MMA8452_CHANNEL(X, idx_x, 10),
MMA8452_CHANNEL(Y, idx_y, 10),
MMA8452_CHANNEL(Z, idx_z, 10),
IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
};
static const struct iio_chan_spec mma8652_channels[] = {
MMA8652_CHANNEL(X, 0, 12),
MMA8652_CHANNEL(Y, 1, 12),
MMA8652_CHANNEL(Z, 2, 12),
IIO_CHAN_SOFT_TIMESTAMP(3),
MMA8652_CHANNEL(X, idx_x, 12),
MMA8652_CHANNEL(Y, idx_y, 12),
MMA8652_CHANNEL(Z, idx_z, 12),
IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
};
static const struct iio_chan_spec mma8653_channels[] = {
MMA8652_CHANNEL(X, 0, 10),
MMA8652_CHANNEL(Y, 1, 10),
MMA8652_CHANNEL(Z, 2, 10),
IIO_CHAN_SOFT_TIMESTAMP(3),
MMA8652_CHANNEL(X, idx_x, 10),
MMA8652_CHANNEL(Y, idx_y, 10),
MMA8652_CHANNEL(Z, idx_z, 10),
IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
};
enum {
@ -1130,13 +1137,21 @@ static int mma8452_probe(struct i2c_client *client,
MMA8452_INT_FF_MT;
int enabled_interrupts = MMA8452_INT_TRANS |
MMA8452_INT_FF_MT;
int irq2;
/* Assume wired to INT1 pin */
ret = i2c_smbus_write_byte_data(client,
MMA8452_CTRL_REG5,
supported_interrupts);
if (ret < 0)
return ret;
irq2 = of_irq_get_byname(client->dev.of_node, "INT2");
if (irq2 == client->irq) {
dev_dbg(&client->dev, "using interrupt line INT2\n");
} else {
ret = i2c_smbus_write_byte_data(client,
MMA8452_CTRL_REG5,
supported_interrupts);
if (ret < 0)
return ret;
dev_dbg(&client->dev, "using interrupt line INT1\n");
}
ret = i2c_smbus_write_byte_data(client,
MMA8452_CTRL_REG4,

View File

@ -495,25 +495,23 @@ static int mma9551_probe(struct i2c_client *client,
if (ret < 0)
goto out_poweroff;
ret = iio_device_register(indio_dev);
if (ret < 0) {
dev_err(&client->dev, "unable to register iio device\n");
goto out_poweroff;
}
ret = pm_runtime_set_active(&client->dev);
if (ret < 0)
goto out_iio_unregister;
goto out_poweroff;
pm_runtime_enable(&client->dev);
pm_runtime_set_autosuspend_delay(&client->dev,
MMA9551_AUTO_SUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(&client->dev);
ret = iio_device_register(indio_dev);
if (ret < 0) {
dev_err(&client->dev, "unable to register iio device\n");
goto out_poweroff;
}
return 0;
out_iio_unregister:
iio_device_unregister(indio_dev);
out_poweroff:
mma9551_set_device_state(client, false);
@ -525,11 +523,12 @@ static int mma9551_remove(struct i2c_client *client)
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct mma9551_data *data = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
pm_runtime_disable(&client->dev);
pm_runtime_set_suspended(&client->dev);
pm_runtime_put_noidle(&client->dev);
iio_device_unregister(indio_dev);
mutex_lock(&data->mutex);
mma9551_set_device_state(data->client, false);
mutex_unlock(&data->mutex);

View File

@ -1133,27 +1133,24 @@ static int mma9553_probe(struct i2c_client *client,
}
}
ret = iio_device_register(indio_dev);
if (ret < 0) {
dev_err(&client->dev, "unable to register iio device\n");
goto out_poweroff;
}
ret = pm_runtime_set_active(&client->dev);
if (ret < 0)
goto out_iio_unregister;
goto out_poweroff;
pm_runtime_enable(&client->dev);
pm_runtime_set_autosuspend_delay(&client->dev,
MMA9551_AUTO_SUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(&client->dev);
dev_dbg(&indio_dev->dev, "Registered device %s\n", name);
ret = iio_device_register(indio_dev);
if (ret < 0) {
dev_err(&client->dev, "unable to register iio device\n");
goto out_poweroff;
}
dev_dbg(&indio_dev->dev, "Registered device %s\n", name);
return 0;
out_iio_unregister:
iio_device_unregister(indio_dev);
out_poweroff:
mma9551_set_device_state(client, false);
return ret;
@ -1164,11 +1161,12 @@ static int mma9553_remove(struct i2c_client *client)
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct mma9553_data *data = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
pm_runtime_disable(&client->dev);
pm_runtime_set_suspended(&client->dev);
pm_runtime_put_noidle(&client->dev);
iio_device_unregister(indio_dev);
mutex_lock(&data->mutex);
mma9551_set_device_state(data->client, false);
mutex_unlock(&data->mutex);

View File

@ -0,0 +1,198 @@
/*
* MXC6255 - MEMSIC orientation sensing accelerometer
*
* Copyright (c) 2015, Intel Corporation.
*
* This file is subject to the terms and conditions of version 2 of
* the GNU General Public License. See the file COPYING in the main
* directory of this archive for more details.
*
* IIO driver for MXC6255 (7-bit I2C slave address 0x15).
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/iio/iio.h>
#include <linux/delay.h>
#include <linux/acpi.h>
#include <linux/regmap.h>
#include <linux/iio/sysfs.h>
#define MXC6255_DRV_NAME "mxc6255"
#define MXC6255_REGMAP_NAME "mxc6255_regmap"
#define MXC6255_REG_XOUT 0x00
#define MXC6255_REG_YOUT 0x01
#define MXC6255_REG_CHIP_ID 0x08
#define MXC6255_CHIP_ID 0x05
/*
* MXC6255 has only one measurement range: +/- 2G.
* The acceleration output is an 8-bit value.
*
* Scale is calculated as follows:
* (2 + 2) * 9.80665 / (2^8 - 1) = 0.153829
*
* Scale value for +/- 2G measurement range
*/
#define MXC6255_SCALE 153829
enum mxc6255_axis {
AXIS_X,
AXIS_Y,
};
struct mxc6255_data {
struct i2c_client *client;
struct regmap *regmap;
};
static int mxc6255_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct mxc6255_data *data = iio_priv(indio_dev);
unsigned int reg;
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = regmap_read(data->regmap, chan->address, &reg);
if (ret < 0) {
dev_err(&data->client->dev,
"Error reading reg %lu\n", chan->address);
return ret;
}
*val = sign_extend32(reg, 7);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 0;
*val2 = MXC6255_SCALE;
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
}
static const struct iio_info mxc6255_info = {
.driver_module = THIS_MODULE,
.read_raw = mxc6255_read_raw,
};
#define MXC6255_CHANNEL(_axis, reg) { \
.type = IIO_ACCEL, \
.modified = 1, \
.channel2 = IIO_MOD_##_axis, \
.address = reg, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
}
static const struct iio_chan_spec mxc6255_channels[] = {
MXC6255_CHANNEL(X, MXC6255_REG_XOUT),
MXC6255_CHANNEL(Y, MXC6255_REG_YOUT),
};
static bool mxc6255_is_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case MXC6255_REG_XOUT:
case MXC6255_REG_YOUT:
case MXC6255_REG_CHIP_ID:
return true;
default:
return false;
}
}
static const struct regmap_config mxc6255_regmap_config = {
.name = MXC6255_REGMAP_NAME,
.reg_bits = 8,
.val_bits = 8,
.readable_reg = mxc6255_is_readable_reg,
};
static int mxc6255_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct mxc6255_data *data;
struct iio_dev *indio_dev;
struct regmap *regmap;
unsigned int chip_id;
int ret;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
regmap = devm_regmap_init_i2c(client, &mxc6255_regmap_config);
if (IS_ERR(regmap)) {
dev_err(&client->dev, "Error initializing regmap\n");
return PTR_ERR(regmap);
}
data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
data->client = client;
data->regmap = regmap;
indio_dev->name = MXC6255_DRV_NAME;
indio_dev->dev.parent = &client->dev;
indio_dev->channels = mxc6255_channels;
indio_dev->num_channels = ARRAY_SIZE(mxc6255_channels);
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &mxc6255_info;
ret = regmap_read(data->regmap, MXC6255_REG_CHIP_ID, &chip_id);
if (ret < 0) {
dev_err(&client->dev, "Error reading chip id %d\n", ret);
return ret;
}
if (chip_id != MXC6255_CHIP_ID) {
dev_err(&client->dev, "Invalid chip id %x\n", chip_id);
return -ENODEV;
}
dev_dbg(&client->dev, "Chip id %x\n", chip_id);
ret = devm_iio_device_register(&client->dev, indio_dev);
if (ret < 0) {
dev_err(&client->dev, "Could not register IIO device\n");
return ret;
}
return 0;
}
static const struct acpi_device_id mxc6255_acpi_match[] = {
{"MXC6255", 0},
{ }
};
MODULE_DEVICE_TABLE(acpi, mxc6255_acpi_match);
static const struct i2c_device_id mxc6255_id[] = {
{"mxc6255", 0},
{ }
};
MODULE_DEVICE_TABLE(i2c, mxc6255_id);
static struct i2c_driver mxc6255_driver = {
.driver = {
.name = MXC6255_DRV_NAME,
.acpi_match_table = ACPI_PTR(mxc6255_acpi_match),
},
.probe = mxc6255_probe,
.id_table = mxc6255_id,
};
module_i2c_driver(mxc6255_driver);
MODULE_AUTHOR("Teodora Baluta <teodora.baluta@intel.com>");
MODULE_DESCRIPTION("MEMSIC MXC6255 orientation sensing accelerometer driver");
MODULE_LICENSE("GPL v2");

View File

@ -27,6 +27,7 @@
#define LSM303DLM_ACCEL_DEV_NAME "lsm303dlm_accel"
#define LSM330_ACCEL_DEV_NAME "lsm330_accel"
#define LSM303AGR_ACCEL_DEV_NAME "lsm303agr_accel"
#define LIS2DH12_ACCEL_DEV_NAME "lis2dh12_accel"
/**
* struct st_sensors_platform_data - default accel platform data

View File

@ -232,6 +232,7 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
[3] = LSM330DL_ACCEL_DEV_NAME,
[4] = LSM330DLC_ACCEL_DEV_NAME,
[5] = LSM303AGR_ACCEL_DEV_NAME,
[6] = LIS2DH12_ACCEL_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
.odr = {

View File

@ -72,6 +72,10 @@ static const struct of_device_id st_accel_of_match[] = {
.compatible = "st,lsm303agr-accel",
.data = LSM303AGR_ACCEL_DEV_NAME,
},
{
.compatible = "st,lis2dh12-accel",
.data = LIS2DH12_ACCEL_DEV_NAME,
},
{},
};
MODULE_DEVICE_TABLE(of, st_accel_of_match);
@ -121,6 +125,7 @@ static const struct i2c_device_id st_accel_id_table[] = {
{ LSM303DLM_ACCEL_DEV_NAME },
{ LSM330_ACCEL_DEV_NAME },
{ LSM303AGR_ACCEL_DEV_NAME },
{ LIS2DH12_ACCEL_DEV_NAME },
{},
};
MODULE_DEVICE_TABLE(i2c, st_accel_id_table);

View File

@ -58,6 +58,7 @@ static const struct spi_device_id st_accel_id_table[] = {
{ LSM303DLM_ACCEL_DEV_NAME },
{ LSM330_ACCEL_DEV_NAME },
{ LSM303AGR_ACCEL_DEV_NAME },
{ LIS2DH12_ACCEL_DEV_NAME },
{},
};
MODULE_DEVICE_TABLE(spi, st_accel_id_table);

View File

@ -194,6 +194,25 @@ config HI8435
This driver can also be built as a module. If so, the module will be
called hi8435.
config INA2XX_ADC
tristate "Texas Instruments INA2xx Power Monitors IIO driver"
depends on I2C && !SENSORS_INA2XX
select REGMAP_I2C
select IIO_BUFFER
select IIO_KFIFO_BUF
help
Say yes here to build support for TI INA2xx family of Power Monitors.
This driver is mutually exclusive with the HWMON version.
config IMX7D_ADC
tristate "IMX7D ADC driver"
depends on ARCH_MXC || COMPILE_TEST
help
Say yes here to build support for IMX7D ADC.
This driver can also be built as a module. If so, the module will be
called imx7d_adc.
config LP8788_ADC
tristate "LP8788 ADC driver"
depends on MFD_LP8788
@ -275,6 +294,14 @@ config NAU7802
To compile this driver as a module, choose M here: the
module will be called nau7802.
config PALMAS_GPADC
tristate "TI Palmas General Purpose ADC"
depends on MFD_PALMAS
help
Palmas series pmic chip by Texas Instruments (twl6035/6037)
is used in smartphones and tablets and supports a 16 channel
general purpose ADC.
config QCOM_SPMI_IADC
tristate "Qualcomm SPMI PMIC current ADC"
depends on SPMI
@ -324,15 +351,25 @@ config TI_ADC081C
called ti-adc081c.
config TI_ADC128S052
tristate "Texas Instruments ADC128S052/ADC122S021"
tristate "Texas Instruments ADC128S052/ADC122S021/ADC124S021"
depends on SPI
help
If you say yes here you get support for Texas Instruments ADC128S052
and ADC122S021 chips.
If you say yes here you get support for Texas Instruments ADC128S052,
ADC122S021 and ADC124S021 chips.
This driver can also be built as a module. If so, the module will be
called ti-adc128s052.
config TI_ADS8688
tristate "Texas Instruments ADS8688"
depends on SPI && OF
help
If you say yes here you get support for Texas Instruments ADS8684 and
and ADS8688 ADC chips
This driver can also be built as a module. If so, the module will be
called ti-ads8688.
config TI_AM335X_ADC
tristate "TI's AM335X ADC driver"
depends on MFD_TI_AM335X_TSCADC

View File

@ -20,6 +20,8 @@ obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
obj-$(CONFIG_HI8435) += hi8435.o
obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
obj-$(CONFIG_MAX1027) += max1027.o
obj-$(CONFIG_MAX1363) += max1363.o
@ -27,11 +29,13 @@ obj-$(CONFIG_MCP320X) += mcp320x.o
obj-$(CONFIG_MCP3422) += mcp3422.o
obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
obj-$(CONFIG_NAU7802) += nau7802.o
obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o
obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o

View File

@ -478,10 +478,9 @@ static int ad7793_read_raw(struct iio_dev *indio_dev,
*val2 = st->
scale_avail[(st->conf >> 8) & 0x7][1];
return IIO_VAL_INT_PLUS_NANO;
} else {
/* 1170mV / 2^23 * 6 */
scale_uv = (1170ULL * 1000000000ULL * 6ULL);
}
/* 1170mV / 2^23 * 6 */
scale_uv = (1170ULL * 1000000000ULL * 6ULL);
break;
case IIO_TEMP:
/* 1170mV / 0.81 mV/C / 2^23 */

View File

@ -742,7 +742,7 @@ static int at91_adc_of_get_resolution(struct at91_adc_state *st,
return count;
}
resolutions = kmalloc(count * sizeof(*resolutions), GFP_KERNEL);
resolutions = kmalloc_array(count, sizeof(*resolutions), GFP_KERNEL);
if (!resolutions)
return -ENOMEM;

View File

@ -0,0 +1,609 @@
/*
* Freescale i.MX7D ADC driver
*
* Copyright (C) 2015 Freescale Semiconductor, Inc.
*
* 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.
*/
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/iio/iio.h>
#include <linux/iio/driver.h>
#include <linux/iio/sysfs.h>
/* ADC register */
#define IMX7D_REG_ADC_CH_A_CFG1 0x00
#define IMX7D_REG_ADC_CH_A_CFG2 0x10
#define IMX7D_REG_ADC_CH_B_CFG1 0x20
#define IMX7D_REG_ADC_CH_B_CFG2 0x30
#define IMX7D_REG_ADC_CH_C_CFG1 0x40
#define IMX7D_REG_ADC_CH_C_CFG2 0x50
#define IMX7D_REG_ADC_CH_D_CFG1 0x60
#define IMX7D_REG_ADC_CH_D_CFG2 0x70
#define IMX7D_REG_ADC_CH_SW_CFG 0x80
#define IMX7D_REG_ADC_TIMER_UNIT 0x90
#define IMX7D_REG_ADC_DMA_FIFO 0xa0
#define IMX7D_REG_ADC_FIFO_STATUS 0xb0
#define IMX7D_REG_ADC_INT_SIG_EN 0xc0
#define IMX7D_REG_ADC_INT_EN 0xd0
#define IMX7D_REG_ADC_INT_STATUS 0xe0
#define IMX7D_REG_ADC_CHA_B_CNV_RSLT 0xf0
#define IMX7D_REG_ADC_CHC_D_CNV_RSLT 0x100
#define IMX7D_REG_ADC_CH_SW_CNV_RSLT 0x110
#define IMX7D_REG_ADC_DMA_FIFO_DAT 0x120
#define IMX7D_REG_ADC_ADC_CFG 0x130
#define IMX7D_REG_ADC_CHANNEL_CFG2_BASE 0x10
#define IMX7D_EACH_CHANNEL_REG_OFFSET 0x20
#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN (0x1 << 31)
#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_SINGLE BIT(30)
#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_AVG_EN BIT(29)
#define IMX7D_REG_ADC_CH_CFG1_CHANNEL_SEL(x) ((x) << 24)
#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_4 (0x0 << 12)
#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_8 (0x1 << 12)
#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_16 (0x2 << 12)
#define IMX7D_REG_ADC_CH_CFG2_AVG_NUM_32 (0x3 << 12)
#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_4 (0x0 << 29)
#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_8 (0x1 << 29)
#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_16 (0x2 << 29)
#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_32 (0x3 << 29)
#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_64 (0x4 << 29)
#define IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_128 (0x5 << 29)
#define IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN BIT(31)
#define IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN BIT(1)
#define IMX7D_REG_ADC_ADC_CFG_ADC_EN BIT(0)
#define IMX7D_REG_ADC_INT_CHA_COV_INT_EN BIT(8)
#define IMX7D_REG_ADC_INT_CHB_COV_INT_EN BIT(9)
#define IMX7D_REG_ADC_INT_CHC_COV_INT_EN BIT(10)
#define IMX7D_REG_ADC_INT_CHD_COV_INT_EN BIT(11)
#define IMX7D_REG_ADC_INT_CHANNEL_INT_EN \
(IMX7D_REG_ADC_INT_CHA_COV_INT_EN | \
IMX7D_REG_ADC_INT_CHB_COV_INT_EN | \
IMX7D_REG_ADC_INT_CHC_COV_INT_EN | \
IMX7D_REG_ADC_INT_CHD_COV_INT_EN)
#define IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS 0xf00
#define IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT 0xf0000
#define IMX7D_ADC_TIMEOUT msecs_to_jiffies(100)
enum imx7d_adc_clk_pre_div {
IMX7D_ADC_ANALOG_CLK_PRE_DIV_4,
IMX7D_ADC_ANALOG_CLK_PRE_DIV_8,
IMX7D_ADC_ANALOG_CLK_PRE_DIV_16,
IMX7D_ADC_ANALOG_CLK_PRE_DIV_32,
IMX7D_ADC_ANALOG_CLK_PRE_DIV_64,
IMX7D_ADC_ANALOG_CLK_PRE_DIV_128,
};
enum imx7d_adc_average_num {
IMX7D_ADC_AVERAGE_NUM_4,
IMX7D_ADC_AVERAGE_NUM_8,
IMX7D_ADC_AVERAGE_NUM_16,
IMX7D_ADC_AVERAGE_NUM_32,
};
struct imx7d_adc_feature {
enum imx7d_adc_clk_pre_div clk_pre_div;
enum imx7d_adc_average_num avg_num;
u32 core_time_unit; /* impact the sample rate */
bool average_en;
};
struct imx7d_adc {
struct device *dev;
void __iomem *regs;
struct clk *clk;
u32 vref_uv;
u32 value;
u32 channel;
u32 pre_div_num;
struct regulator *vref;
struct imx7d_adc_feature adc_feature;
struct completion completion;
};
struct imx7d_adc_analogue_core_clk {
u32 pre_div;
u32 reg_config;
};
#define IMX7D_ADC_ANALOGUE_CLK_CONFIG(_pre_div, _reg_conf) { \
.pre_div = (_pre_div), \
.reg_config = (_reg_conf), \
}
static const struct imx7d_adc_analogue_core_clk imx7d_adc_analogue_clk[] = {
IMX7D_ADC_ANALOGUE_CLK_CONFIG(4, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_4),
IMX7D_ADC_ANALOGUE_CLK_CONFIG(8, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_8),
IMX7D_ADC_ANALOGUE_CLK_CONFIG(16, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_16),
IMX7D_ADC_ANALOGUE_CLK_CONFIG(32, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_32),
IMX7D_ADC_ANALOGUE_CLK_CONFIG(64, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_64),
IMX7D_ADC_ANALOGUE_CLK_CONFIG(128, IMX7D_REG_ADC_TIMER_UNIT_PRE_DIV_128),
};
#define IMX7D_ADC_CHAN(_idx) { \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = (_idx), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
}
static const struct iio_chan_spec imx7d_adc_iio_channels[] = {
IMX7D_ADC_CHAN(0),
IMX7D_ADC_CHAN(1),
IMX7D_ADC_CHAN(2),
IMX7D_ADC_CHAN(3),
IMX7D_ADC_CHAN(4),
IMX7D_ADC_CHAN(5),
IMX7D_ADC_CHAN(6),
IMX7D_ADC_CHAN(7),
IMX7D_ADC_CHAN(8),
IMX7D_ADC_CHAN(9),
IMX7D_ADC_CHAN(10),
IMX7D_ADC_CHAN(11),
IMX7D_ADC_CHAN(12),
IMX7D_ADC_CHAN(13),
IMX7D_ADC_CHAN(14),
IMX7D_ADC_CHAN(15),
};
static const u32 imx7d_adc_average_num[] = {
IMX7D_REG_ADC_CH_CFG2_AVG_NUM_4,
IMX7D_REG_ADC_CH_CFG2_AVG_NUM_8,
IMX7D_REG_ADC_CH_CFG2_AVG_NUM_16,
IMX7D_REG_ADC_CH_CFG2_AVG_NUM_32,
};
static void imx7d_adc_feature_config(struct imx7d_adc *info)
{
info->adc_feature.clk_pre_div = IMX7D_ADC_ANALOG_CLK_PRE_DIV_4;
info->adc_feature.avg_num = IMX7D_ADC_AVERAGE_NUM_32;
info->adc_feature.core_time_unit = 1;
info->adc_feature.average_en = true;
}
static void imx7d_adc_sample_rate_set(struct imx7d_adc *info)
{
struct imx7d_adc_feature *adc_feature = &info->adc_feature;
struct imx7d_adc_analogue_core_clk adc_analogure_clk;
u32 i;
u32 tmp_cfg1;
u32 sample_rate = 0;
/*
* Before sample set, disable channel A,B,C,D. Here we
* clear the bit 31 of register REG_ADC_CH_A\B\C\D_CFG1.
*/
for (i = 0; i < 4; i++) {
tmp_cfg1 =
readl(info->regs + i * IMX7D_EACH_CHANNEL_REG_OFFSET);
tmp_cfg1 &= ~IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN;
writel(tmp_cfg1,
info->regs + i * IMX7D_EACH_CHANNEL_REG_OFFSET);
}
adc_analogure_clk = imx7d_adc_analogue_clk[adc_feature->clk_pre_div];
sample_rate |= adc_analogure_clk.reg_config;
info->pre_div_num = adc_analogure_clk.pre_div;
sample_rate |= adc_feature->core_time_unit;
writel(sample_rate, info->regs + IMX7D_REG_ADC_TIMER_UNIT);
}
static void imx7d_adc_hw_init(struct imx7d_adc *info)
{
u32 cfg;
/* power up and enable adc analogue core */
cfg = readl(info->regs + IMX7D_REG_ADC_ADC_CFG);
cfg &= ~(IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN |
IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN);
cfg |= IMX7D_REG_ADC_ADC_CFG_ADC_EN;
writel(cfg, info->regs + IMX7D_REG_ADC_ADC_CFG);
/* enable channel A,B,C,D interrupt */
writel(IMX7D_REG_ADC_INT_CHANNEL_INT_EN,
info->regs + IMX7D_REG_ADC_INT_SIG_EN);
writel(IMX7D_REG_ADC_INT_CHANNEL_INT_EN,
info->regs + IMX7D_REG_ADC_INT_EN);
imx7d_adc_sample_rate_set(info);
}
static void imx7d_adc_channel_set(struct imx7d_adc *info)
{
u32 cfg1 = 0;
u32 cfg2;
u32 channel;
channel = info->channel;
/* the channel choose single conversion, and enable average mode */
cfg1 |= (IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN |
IMX7D_REG_ADC_CH_CFG1_CHANNEL_SINGLE);
if (info->adc_feature.average_en)
cfg1 |= IMX7D_REG_ADC_CH_CFG1_CHANNEL_AVG_EN;
/*
* physical channel 0 chose logical channel A
* physical channel 1 chose logical channel B
* physical channel 2 chose logical channel C
* physical channel 3 chose logical channel D
*/
cfg1 |= IMX7D_REG_ADC_CH_CFG1_CHANNEL_SEL(channel);
/*
* read register REG_ADC_CH_A\B\C\D_CFG2, according to the
* channel chosen
*/
cfg2 = readl(info->regs + IMX7D_EACH_CHANNEL_REG_OFFSET * channel +
IMX7D_REG_ADC_CHANNEL_CFG2_BASE);
cfg2 |= imx7d_adc_average_num[info->adc_feature.avg_num];
/*
* write the register REG_ADC_CH_A\B\C\D_CFG2, according to
* the channel chosen
*/
writel(cfg2, info->regs + IMX7D_EACH_CHANNEL_REG_OFFSET * channel +
IMX7D_REG_ADC_CHANNEL_CFG2_BASE);
writel(cfg1, info->regs + IMX7D_EACH_CHANNEL_REG_OFFSET * channel);
}
static u32 imx7d_adc_get_sample_rate(struct imx7d_adc *info)
{
/* input clock is always 24MHz */
u32 input_clk = 24000000;
u32 analogue_core_clk;
u32 core_time_unit = info->adc_feature.core_time_unit;
u32 tmp;
analogue_core_clk = input_clk / info->pre_div_num;
tmp = (core_time_unit + 1) * 6;
return analogue_core_clk / tmp;
}
static int imx7d_adc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val,
int *val2,
long mask)
{
struct imx7d_adc *info = iio_priv(indio_dev);
u32 channel;
long ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
mutex_lock(&indio_dev->mlock);
reinit_completion(&info->completion);
channel = chan->channel & 0x03;
info->channel = channel;
imx7d_adc_channel_set(info);
ret = wait_for_completion_interruptible_timeout
(&info->completion, IMX7D_ADC_TIMEOUT);
if (ret == 0) {
mutex_unlock(&indio_dev->mlock);
return -ETIMEDOUT;
}
if (ret < 0) {
mutex_unlock(&indio_dev->mlock);
return ret;
}
*val = info->value;
mutex_unlock(&indio_dev->mlock);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
info->vref_uv = regulator_get_voltage(info->vref);
*val = info->vref_uv / 1000;
*val2 = 12;
return IIO_VAL_FRACTIONAL_LOG2;
case IIO_CHAN_INFO_SAMP_FREQ:
*val = imx7d_adc_get_sample_rate(info);
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static int imx7d_adc_read_data(struct imx7d_adc *info)
{
u32 channel;
u32 value;
channel = info->channel & 0x03;
/*
* channel A and B conversion result share one register,
* bit[27~16] is the channel B conversion result,
* bit[11~0] is the channel A conversion result.
* channel C and D is the same.
*/
if (channel < 2)
value = readl(info->regs + IMX7D_REG_ADC_CHA_B_CNV_RSLT);
else
value = readl(info->regs + IMX7D_REG_ADC_CHC_D_CNV_RSLT);
if (channel & 0x1) /* channel B or D */
value = (value >> 16) & 0xFFF;
else /* channel A or C */
value &= 0xFFF;
return value;
}
static irqreturn_t imx7d_adc_isr(int irq, void *dev_id)
{
struct imx7d_adc *info = (struct imx7d_adc *)dev_id;
int status;
status = readl(info->regs + IMX7D_REG_ADC_INT_STATUS);
if (status & IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS) {
info->value = imx7d_adc_read_data(info);
complete(&info->completion);
/*
* The register IMX7D_REG_ADC_INT_STATUS can't clear
* itself after read operation, need software to write
* 0 to the related bit. Here we clear the channel A/B/C/D
* conversion finished flag.
*/
status &= ~IMX7D_REG_ADC_INT_STATUS_CHANNEL_INT_STATUS;
writel(status, info->regs + IMX7D_REG_ADC_INT_STATUS);
}
/*
* If the channel A/B/C/D conversion timeout, report it and clear these
* 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);
status &= ~IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT;
writel(status, info->regs + IMX7D_REG_ADC_INT_STATUS);
}
return IRQ_HANDLED;
}
static int imx7d_adc_reg_access(struct iio_dev *indio_dev,
unsigned reg, unsigned writeval,
unsigned *readval)
{
struct imx7d_adc *info = iio_priv(indio_dev);
if (!readval || reg % 4 || reg > IMX7D_REG_ADC_ADC_CFG)
return -EINVAL;
*readval = readl(info->regs + reg);
return 0;
}
static const struct iio_info imx7d_adc_iio_info = {
.driver_module = THIS_MODULE,
.read_raw = &imx7d_adc_read_raw,
.debugfs_reg_access = &imx7d_adc_reg_access,
};
static const struct of_device_id imx7d_adc_match[] = {
{ .compatible = "fsl,imx7d-adc", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx7d_adc_match);
static void imx7d_adc_power_down(struct imx7d_adc *info)
{
u32 adc_cfg;
adc_cfg = readl(info->regs + IMX7D_REG_ADC_ADC_CFG);
adc_cfg |= IMX7D_REG_ADC_ADC_CFG_ADC_CLK_DOWN |
IMX7D_REG_ADC_ADC_CFG_ADC_POWER_DOWN;
adc_cfg &= ~IMX7D_REG_ADC_ADC_CFG_ADC_EN;
writel(adc_cfg, info->regs + IMX7D_REG_ADC_ADC_CFG);
}
static int imx7d_adc_probe(struct platform_device *pdev)
{
struct imx7d_adc *info;
struct iio_dev *indio_dev;
struct resource *mem;
int irq;
int ret;
indio_dev = devm_iio_device_alloc(&pdev->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;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
info->regs = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(info->regs)) {
ret = PTR_ERR(info->regs);
dev_err(&pdev->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");
return irq;
}
info->clk = devm_clk_get(&pdev->dev, "adc");
if (IS_ERR(info->clk)) {
ret = PTR_ERR(info->clk);
dev_err(&pdev->dev, "Failed getting clock, err = %d\n", ret);
return ret;
}
info->vref = devm_regulator_get(&pdev->dev, "vref");
if (IS_ERR(info->vref)) {
ret = PTR_ERR(info->vref);
dev_err(&pdev->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->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);
if (ret < 0) {
dev_err(&pdev->dev, "Failed requesting irq, irq = %d\n", irq);
goto error_iio_device_register;
}
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 = 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 SIMPLE_DEV_PM_OPS(imx7d_adc_pm_ops, imx7d_adc_suspend, imx7d_adc_resume);
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,
.pm = &imx7d_adc_pm_ops,
},
};
module_platform_driver(imx7d_adc_driver);
MODULE_AUTHOR("Haibo Chen <haibo.chen@freescale.com>");
MODULE_DESCRIPTION("Freeacale IMX7D ADC driver");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,745 @@
/*
* INA2XX Current and Power Monitors
*
* Copyright 2015 Baylibre SAS.
*
* 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.
*
* Based on linux/drivers/iio/adc/ad7291.c
* Copyright 2010-2011 Analog Devices Inc.
*
* Based on linux/drivers/hwmon/ina2xx.c
* Copyright 2012 Lothar Felten <l-felten@ti.com>
*
* Licensed under the GPL-2 or later.
*
* IIO driver for INA219-220-226-230-231
*
* Configurable 7-bit I2C slave address from 0x40 to 0x4F
*/
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/iio/kfifo_buf.h>
#include <linux/iio/sysfs.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/platform_data/ina2xx.h>
#include <linux/util_macros.h>
/* INA2XX registers definition */
#define INA2XX_CONFIG 0x00
#define INA2XX_SHUNT_VOLTAGE 0x01 /* readonly */
#define INA2XX_BUS_VOLTAGE 0x02 /* readonly */
#define INA2XX_POWER 0x03 /* readonly */
#define INA2XX_CURRENT 0x04 /* readonly */
#define INA2XX_CALIBRATION 0x05
#define INA226_ALERT_MASK 0x06
#define INA266_CVRF BIT(3)
#define INA2XX_MAX_REGISTERS 8
/* settings - depend on use case */
#define INA219_CONFIG_DEFAULT 0x399F /* PGA=8 */
#define INA226_CONFIG_DEFAULT 0x4327
#define INA226_DEFAULT_AVG 4
#define INA226_DEFAULT_IT 1110
#define INA2XX_RSHUNT_DEFAULT 10000
/*
* bit mask for reading the averaging setting in the configuration register
* FIXME: use regmap_fields.
*/
#define INA2XX_MODE_MASK GENMASK(3, 0)
#define INA226_AVG_MASK GENMASK(11, 9)
#define INA226_SHIFT_AVG(val) ((val) << 9)
/* Integration time for VBus */
#define INA226_ITB_MASK GENMASK(8, 6)
#define INA226_SHIFT_ITB(val) ((val) << 6)
/* Integration time for VShunt */
#define INA226_ITS_MASK GENMASK(5, 3)
#define INA226_SHIFT_ITS(val) ((val) << 3)
/* Cosmetic macro giving the sampling period for a full P=UxI cycle */
#define SAMPLING_PERIOD(c) ((c->int_time_vbus + c->int_time_vshunt) \
* c->avg)
static bool ina2xx_is_writeable_reg(struct device *dev, unsigned int reg)
{
return (reg == INA2XX_CONFIG) || (reg > INA2XX_CURRENT);
}
static bool ina2xx_is_volatile_reg(struct device *dev, unsigned int reg)
{
return (reg != INA2XX_CONFIG);
}
static inline bool is_signed_reg(unsigned int reg)
{
return (reg == INA2XX_SHUNT_VOLTAGE) || (reg == INA2XX_CURRENT);
}
static const struct regmap_config ina2xx_regmap_config = {
.reg_bits = 8,
.val_bits = 16,
.max_register = INA2XX_MAX_REGISTERS,
.writeable_reg = ina2xx_is_writeable_reg,
.volatile_reg = ina2xx_is_volatile_reg,
};
enum ina2xx_ids { ina219, ina226 };
struct ina2xx_config {
u16 config_default;
int calibration_factor;
int shunt_div;
int bus_voltage_shift;
int bus_voltage_lsb; /* uV */
int power_lsb; /* uW */
};
struct ina2xx_chip_info {
struct regmap *regmap;
struct task_struct *task;
const struct ina2xx_config *config;
struct mutex state_lock;
unsigned int shunt_resistor;
int avg;
s64 prev_ns; /* track buffer capture time, check for underruns*/
int int_time_vbus; /* Bus voltage integration time uS */
int int_time_vshunt; /* Shunt voltage integration time uS */
bool allow_async_readout;
};
static const struct ina2xx_config ina2xx_config[] = {
[ina219] = {
.config_default = INA219_CONFIG_DEFAULT,
.calibration_factor = 40960000,
.shunt_div = 100,
.bus_voltage_shift = 3,
.bus_voltage_lsb = 4000,
.power_lsb = 20000,
},
[ina226] = {
.config_default = INA226_CONFIG_DEFAULT,
.calibration_factor = 5120000,
.shunt_div = 400,
.bus_voltage_shift = 0,
.bus_voltage_lsb = 1250,
.power_lsb = 25000,
},
};
static int ina2xx_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
int ret;
struct ina2xx_chip_info *chip = iio_priv(indio_dev);
unsigned int regval;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = regmap_read(chip->regmap, chan->address, &regval);
if (ret < 0)
return ret;
if (is_signed_reg(chan->address))
*val = (s16) regval;
else
*val = regval;
return IIO_VAL_INT;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
*val = chip->avg;
return IIO_VAL_INT;
case IIO_CHAN_INFO_INT_TIME:
*val = 0;
if (chan->address == INA2XX_SHUNT_VOLTAGE)
*val2 = chip->int_time_vshunt;
else
*val2 = chip->int_time_vbus;
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_SAMP_FREQ:
/*
* Sample freq is read only, it is a consequence of
* 1/AVG*(CT_bus+CT_shunt).
*/
*val = DIV_ROUND_CLOSEST(1000000, SAMPLING_PERIOD(chip));
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
switch (chan->address) {
case INA2XX_SHUNT_VOLTAGE:
/* processed (mV) = raw*1000/shunt_div */
*val2 = chip->config->shunt_div;
*val = 1000;
return IIO_VAL_FRACTIONAL;
case INA2XX_BUS_VOLTAGE:
/* processed (mV) = raw*lsb (uV) / (1000 << shift) */
*val = chip->config->bus_voltage_lsb;
*val2 = 1000 << chip->config->bus_voltage_shift;
return IIO_VAL_FRACTIONAL;
case INA2XX_POWER:
/* processed (mW) = raw*lsb (uW) / 1000 */
*val = chip->config->power_lsb;
*val2 = 1000;
return IIO_VAL_FRACTIONAL;
case INA2XX_CURRENT:
/* processed (mA) = raw (mA) */
*val = 1;
return IIO_VAL_INT;
}
}
return -EINVAL;
}
/*
* Available averaging rates for ina226. The indices correspond with
* the bit values expected by the chip (according to the ina226 datasheet,
* table 3 AVG bit settings, found at
* http://www.ti.com/lit/ds/symlink/ina226.pdf.
*/
static const int ina226_avg_tab[] = { 1, 4, 16, 64, 128, 256, 512, 1024 };
static int ina226_set_average(struct ina2xx_chip_info *chip, unsigned int val,
unsigned int *config)
{
int bits;
if (val > 1024 || val < 1)
return -EINVAL;
bits = find_closest(val, ina226_avg_tab,
ARRAY_SIZE(ina226_avg_tab));
chip->avg = ina226_avg_tab[bits];
*config &= ~INA226_AVG_MASK;
*config |= INA226_SHIFT_AVG(bits) & INA226_AVG_MASK;
return 0;
}
/* Conversion times in uS */
static const int ina226_conv_time_tab[] = { 140, 204, 332, 588, 1100,
2116, 4156, 8244 };
static int ina226_set_int_time_vbus(struct ina2xx_chip_info *chip,
unsigned int val_us, unsigned int *config)
{
int bits;
if (val_us > 8244 || val_us < 140)
return -EINVAL;
bits = find_closest(val_us, ina226_conv_time_tab,
ARRAY_SIZE(ina226_conv_time_tab));
chip->int_time_vbus = ina226_conv_time_tab[bits];
*config &= ~INA226_ITB_MASK;
*config |= INA226_SHIFT_ITB(bits) & INA226_ITB_MASK;
return 0;
}
static int ina226_set_int_time_vshunt(struct ina2xx_chip_info *chip,
unsigned int val_us, unsigned int *config)
{
int bits;
if (val_us > 8244 || val_us < 140)
return -EINVAL;
bits = find_closest(val_us, ina226_conv_time_tab,
ARRAY_SIZE(ina226_conv_time_tab));
chip->int_time_vshunt = ina226_conv_time_tab[bits];
*config &= ~INA226_ITS_MASK;
*config |= INA226_SHIFT_ITS(bits) & INA226_ITS_MASK;
return 0;
}
static int ina2xx_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct ina2xx_chip_info *chip = iio_priv(indio_dev);
int ret;
unsigned int config, tmp;
if (iio_buffer_enabled(indio_dev))
return -EBUSY;
mutex_lock(&chip->state_lock);
ret = regmap_read(chip->regmap, INA2XX_CONFIG, &config);
if (ret < 0)
goto _err;
tmp = config;
switch (mask) {
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
ret = ina226_set_average(chip, val, &tmp);
break;
case IIO_CHAN_INFO_INT_TIME:
if (chan->address == INA2XX_SHUNT_VOLTAGE)
ret = ina226_set_int_time_vshunt(chip, val2, &tmp);
else
ret = ina226_set_int_time_vbus(chip, val2, &tmp);
break;
default:
ret = -EINVAL;
}
if (!ret && (tmp != config))
ret = regmap_write(chip->regmap, INA2XX_CONFIG, tmp);
_err:
mutex_unlock(&chip->state_lock);
return ret;
}
static ssize_t ina2xx_allow_async_readout_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct ina2xx_chip_info *chip = iio_priv(dev_to_iio_dev(dev));
return sprintf(buf, "%d\n", chip->allow_async_readout);
}
static ssize_t ina2xx_allow_async_readout_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
struct ina2xx_chip_info *chip = iio_priv(dev_to_iio_dev(dev));
bool val;
int ret;
ret = strtobool((const char *) buf, &val);
if (ret)
return ret;
chip->allow_async_readout = val;
return len;
}
static int set_shunt_resistor(struct ina2xx_chip_info *chip, unsigned int val)
{
if (val <= 0 || val > chip->config->calibration_factor)
return -EINVAL;
chip->shunt_resistor = val;
return 0;
}
static ssize_t ina2xx_shunt_resistor_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct ina2xx_chip_info *chip = iio_priv(dev_to_iio_dev(dev));
return sprintf(buf, "%d\n", chip->shunt_resistor);
}
static ssize_t ina2xx_shunt_resistor_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
struct ina2xx_chip_info *chip = iio_priv(dev_to_iio_dev(dev));
unsigned long val;
int ret;
ret = kstrtoul((const char *) buf, 10, &val);
if (ret)
return ret;
ret = set_shunt_resistor(chip, val);
if (ret)
return ret;
return len;
}
#define INA2XX_CHAN(_type, _index, _address) { \
.type = (_type), \
.address = (_address), \
.indexed = 1, \
.channel = (_index), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \
| BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.scan_index = (_index), \
.scan_type = { \
.sign = 'u', \
.realbits = 16, \
.storagebits = 16, \
.endianness = IIO_CPU, \
} \
}
/*
* Sampling Freq is a consequence of the integration times of
* the Voltage channels.
*/
#define INA2XX_CHAN_VOLTAGE(_index, _address) { \
.type = IIO_VOLTAGE, \
.address = (_address), \
.indexed = 1, \
.channel = (_index), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_INT_TIME), \
.scan_index = (_index), \
.scan_type = { \
.sign = 'u', \
.realbits = 16, \
.storagebits = 16, \
.endianness = IIO_LE, \
} \
}
static const struct iio_chan_spec ina2xx_channels[] = {
INA2XX_CHAN_VOLTAGE(0, INA2XX_SHUNT_VOLTAGE),
INA2XX_CHAN_VOLTAGE(1, INA2XX_BUS_VOLTAGE),
INA2XX_CHAN(IIO_POWER, 2, INA2XX_POWER),
INA2XX_CHAN(IIO_CURRENT, 3, INA2XX_CURRENT),
IIO_CHAN_SOFT_TIMESTAMP(4),
};
static int ina2xx_work_buffer(struct iio_dev *indio_dev)
{
struct ina2xx_chip_info *chip = iio_priv(indio_dev);
unsigned short data[8];
int bit, ret, i = 0;
unsigned long buffer_us, elapsed_us;
s64 time_a, time_b;
unsigned int alert;
time_a = iio_get_time_ns();
/*
* Because the timer thread and the chip conversion clock
* are asynchronous, the period difference will eventually
* result in reading V[k-1] again, or skip V[k] at time Tk.
* In order to resync the timer with the conversion process
* we check the ConVersionReadyFlag.
* On hardware that supports using the ALERT pin to toggle a
* GPIO a triggered buffer could be used instead.
* For now, we pay for that extra read of the ALERT register
*/
if (!chip->allow_async_readout)
do {
ret = regmap_read(chip->regmap, INA226_ALERT_MASK,
&alert);
if (ret < 0)
return ret;
alert &= INA266_CVRF;
trace_printk("Conversion ready: %d\n", !!alert);
} while (!alert);
/*
* Single register reads: bulk_read will not work with ina226
* as there is no auto-increment of the address register for
* data length longer than 16bits.
*/
for_each_set_bit(bit, indio_dev->active_scan_mask,
indio_dev->masklength) {
unsigned int val;
ret = regmap_read(chip->regmap,
INA2XX_SHUNT_VOLTAGE + bit, &val);
if (ret < 0)
return ret;
data[i++] = val;
}
time_b = iio_get_time_ns();
iio_push_to_buffers_with_timestamp(indio_dev,
(unsigned int *)data, time_a);
buffer_us = (unsigned long)(time_b - time_a) / 1000;
elapsed_us = (unsigned long)(time_a - chip->prev_ns) / 1000;
trace_printk("uS: elapsed: %lu, buf: %lu\n", elapsed_us, buffer_us);
chip->prev_ns = time_a;
return buffer_us;
};
static int ina2xx_capture_thread(void *data)
{
struct iio_dev *indio_dev = (struct iio_dev *)data;
struct ina2xx_chip_info *chip = iio_priv(indio_dev);
unsigned int sampling_us = SAMPLING_PERIOD(chip);
int buffer_us;
/*
* Poll a bit faster than the chip internal Fs, in case
* we wish to sync with the conversion ready flag.
*/
if (!chip->allow_async_readout)
sampling_us -= 200;
do {
buffer_us = ina2xx_work_buffer(indio_dev);
if (buffer_us < 0)
return buffer_us;
if (sampling_us > buffer_us)
udelay(sampling_us - buffer_us);
} while (!kthread_should_stop());
return 0;
}
static int ina2xx_buffer_enable(struct iio_dev *indio_dev)
{
struct ina2xx_chip_info *chip = iio_priv(indio_dev);
unsigned int sampling_us = SAMPLING_PERIOD(chip);
trace_printk("Enabling buffer w/ scan_mask %02x, freq = %d, avg =%u\n",
(unsigned int)(*indio_dev->active_scan_mask),
1000000/sampling_us, chip->avg);
trace_printk("Expected work period: %u us\n", sampling_us);
trace_printk("Async readout mode: %d\n", chip->allow_async_readout);
chip->prev_ns = iio_get_time_ns();
chip->task = kthread_run(ina2xx_capture_thread, (void *)indio_dev,
"%s:%d-%uus", indio_dev->name, indio_dev->id,
sampling_us);
return PTR_ERR_OR_ZERO(chip->task);
}
static int ina2xx_buffer_disable(struct iio_dev *indio_dev)
{
struct ina2xx_chip_info *chip = iio_priv(indio_dev);
if (chip->task) {
kthread_stop(chip->task);
chip->task = NULL;
}
return 0;
}
static const struct iio_buffer_setup_ops ina2xx_setup_ops = {
.postenable = &ina2xx_buffer_enable,
.predisable = &ina2xx_buffer_disable,
};
static int ina2xx_debug_reg(struct iio_dev *indio_dev,
unsigned reg, unsigned writeval, unsigned *readval)
{
struct ina2xx_chip_info *chip = iio_priv(indio_dev);
if (!readval)
return regmap_write(chip->regmap, reg, writeval);
return regmap_read(chip->regmap, reg, readval);
}
/* Possible integration times for vshunt and vbus */
static IIO_CONST_ATTR_INT_TIME_AVAIL \
("0.000140 0.000204 0.000332 0.000588 0.001100 0.002116 0.004156 0.008244");
static IIO_DEVICE_ATTR(in_allow_async_readout, S_IRUGO | S_IWUSR,
ina2xx_allow_async_readout_show,
ina2xx_allow_async_readout_store, 0);
static IIO_DEVICE_ATTR(in_shunt_resistor, S_IRUGO | S_IWUSR,
ina2xx_shunt_resistor_show,
ina2xx_shunt_resistor_store, 0);
static struct attribute *ina2xx_attributes[] = {
&iio_dev_attr_in_allow_async_readout.dev_attr.attr,
&iio_const_attr_integration_time_available.dev_attr.attr,
&iio_dev_attr_in_shunt_resistor.dev_attr.attr,
NULL,
};
static const struct attribute_group ina2xx_attribute_group = {
.attrs = ina2xx_attributes,
};
static const struct iio_info ina2xx_info = {
.debugfs_reg_access = &ina2xx_debug_reg,
.read_raw = &ina2xx_read_raw,
.write_raw = &ina2xx_write_raw,
.attrs = &ina2xx_attribute_group,
.driver_module = THIS_MODULE,
};
/* Initialize the configuration and calibration registers. */
static int ina2xx_init(struct ina2xx_chip_info *chip, unsigned int config)
{
u16 regval;
int ret = regmap_write(chip->regmap, INA2XX_CONFIG, config);
if (ret < 0)
return ret;
/*
* Set current LSB to 1mA, shunt is in uOhms
* (equation 13 in datasheet). We hardcode a Current_LSB
* of 1.0 x10-6. The only remaining parameter is RShunt.
* There is no need to expose the CALIBRATION register
* to the user for now.
*/
regval = DIV_ROUND_CLOSEST(chip->config->calibration_factor,
chip->shunt_resistor);
return regmap_write(chip->regmap, INA2XX_CALIBRATION, regval);
}
static int ina2xx_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct ina2xx_chip_info *chip;
struct iio_dev *indio_dev;
struct iio_buffer *buffer;
int ret;
unsigned int val;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
if (!indio_dev)
return -ENOMEM;
chip = iio_priv(indio_dev);
chip->config = &ina2xx_config[id->driver_data];
if (of_property_read_u32(client->dev.of_node,
"shunt-resistor", &val) < 0) {
struct ina2xx_platform_data *pdata =
dev_get_platdata(&client->dev);
if (pdata)
val = pdata->shunt_uohms;
else
val = INA2XX_RSHUNT_DEFAULT;
}
ret = set_shunt_resistor(chip, val);
if (ret)
return ret;
mutex_init(&chip->state_lock);
/* This is only used for device removal purposes. */
i2c_set_clientdata(client, indio_dev);
indio_dev->name = id->name;
indio_dev->channels = ina2xx_channels;
indio_dev->num_channels = ARRAY_SIZE(ina2xx_channels);
indio_dev->dev.parent = &client->dev;
indio_dev->info = &ina2xx_info;
indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
chip->regmap = devm_regmap_init_i2c(client, &ina2xx_regmap_config);
if (IS_ERR(chip->regmap)) {
dev_err(&client->dev, "failed to allocate register map\n");
return PTR_ERR(chip->regmap);
}
/* Patch the current config register with default. */
val = chip->config->config_default;
if (id->driver_data == ina226) {
ina226_set_average(chip, INA226_DEFAULT_AVG, &val);
ina226_set_int_time_vbus(chip, INA226_DEFAULT_IT, &val);
ina226_set_int_time_vshunt(chip, INA226_DEFAULT_IT, &val);
}
ret = ina2xx_init(chip, val);
if (ret < 0) {
dev_err(&client->dev, "error configuring the device: %d\n",
ret);
return -ENODEV;
}
buffer = devm_iio_kfifo_allocate(&indio_dev->dev);
if (!buffer)
return -ENOMEM;
indio_dev->setup_ops = &ina2xx_setup_ops;
iio_device_attach_buffer(indio_dev, buffer);
return iio_device_register(indio_dev);
}
static int ina2xx_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct ina2xx_chip_info *chip = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
/* Powerdown */
return regmap_update_bits(chip->regmap, INA2XX_CONFIG,
INA2XX_MODE_MASK, 0);
}
static const struct i2c_device_id ina2xx_id[] = {
{"ina219", ina219},
{"ina220", ina219},
{"ina226", ina226},
{"ina230", ina226},
{"ina231", ina226},
{}
};
MODULE_DEVICE_TABLE(i2c, ina2xx_id);
static struct i2c_driver ina2xx_driver = {
.driver = {
.name = KBUILD_MODNAME,
},
.probe = ina2xx_probe,
.remove = ina2xx_remove,
.id_table = ina2xx_id,
};
module_i2c_driver(ina2xx_driver);
MODULE_AUTHOR("Marc Titinger <marc.titinger@baylibre.com>");
MODULE_DESCRIPTION("Texas Instruments INA2XX ADC driver");
MODULE_LICENSE("GPL v2");

View File

@ -354,6 +354,7 @@ static int mcp320x_remove(struct spi_device *spi)
#if defined(CONFIG_OF)
static const struct of_device_id mcp320x_dt_ids[] = {
/* NOTE: The use of compatibles with no vendor prefix is deprecated. */
{
.compatible = "mcp3001",
.data = &mcp320x_chip_infos[mcp3001],
@ -381,6 +382,33 @@ static const struct of_device_id mcp320x_dt_ids[] = {
}, {
.compatible = "mcp3301",
.data = &mcp320x_chip_infos[mcp3301],
}, {
.compatible = "microchip,mcp3001",
.data = &mcp320x_chip_infos[mcp3001],
}, {
.compatible = "microchip,mcp3002",
.data = &mcp320x_chip_infos[mcp3002],
}, {
.compatible = "microchip,mcp3004",
.data = &mcp320x_chip_infos[mcp3004],
}, {
.compatible = "microchip,mcp3008",
.data = &mcp320x_chip_infos[mcp3008],
}, {
.compatible = "microchip,mcp3201",
.data = &mcp320x_chip_infos[mcp3201],
}, {
.compatible = "microchip,mcp3202",
.data = &mcp320x_chip_infos[mcp3202],
}, {
.compatible = "microchip,mcp3204",
.data = &mcp320x_chip_infos[mcp3204],
}, {
.compatible = "microchip,mcp3208",
.data = &mcp320x_chip_infos[mcp3208],
}, {
.compatible = "microchip,mcp3301",
.data = &mcp320x_chip_infos[mcp3301],
}, {
}
};

View File

@ -305,6 +305,10 @@ static const struct attribute_group mcp3422_attribute_group = {
.attrs = mcp3422_attributes,
};
static const struct iio_chan_spec mcp3421_channels[] = {
MCP3422_CHAN(0),
};
static const struct iio_chan_spec mcp3422_channels[] = {
MCP3422_CHAN(0),
MCP3422_CHAN(1),
@ -352,6 +356,10 @@ static int mcp3422_probe(struct i2c_client *client,
indio_dev->info = &mcp3422_info;
switch (adc->id) {
case 1:
indio_dev->channels = mcp3421_channels;
indio_dev->num_channels = ARRAY_SIZE(mcp3421_channels);
break;
case 2:
case 3:
case 6:
@ -383,6 +391,7 @@ static int mcp3422_probe(struct i2c_client *client,
}
static const struct i2c_device_id mcp3422_id[] = {
{ "mcp3421", 1 },
{ "mcp3422", 2 },
{ "mcp3423", 3 },
{ "mcp3424", 4 },

View File

@ -0,0 +1,859 @@
/*
* palmas-adc.c -- TI PALMAS GPADC.
*
* Copyright (c) 2013, NVIDIA Corporation. All rights reserved.
*
* Author: Pradeep Goudagunta <pgoudagunta@nvidia.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 version 2.
*/
#include <linux/module.h>
#include <linux/err.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/pm.h>
#include <linux/mfd/palmas.h>
#include <linux/completion.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/iio/iio.h>
#include <linux/iio/machine.h>
#include <linux/iio/driver.h>
#define MOD_NAME "palmas-gpadc"
#define PALMAS_ADC_CONVERSION_TIMEOUT (msecs_to_jiffies(5000))
#define PALMAS_TO_BE_CALCULATED 0
#define PALMAS_GPADC_TRIMINVALID -1
struct palmas_gpadc_info {
/* calibration codes and regs */
int x1; /* lower ideal code */
int x2; /* higher ideal code */
int v1; /* expected lower volt reading */
int v2; /* expected higher volt reading */
u8 trim1_reg; /* register number for lower trim */
u8 trim2_reg; /* register number for upper trim */
int gain; /* calculated from above (after reading trim regs) */
int offset; /* calculated from above (after reading trim regs) */
int gain_error; /* calculated from above (after reading trim regs) */
bool is_uncalibrated; /* if channel has calibration data */
};
#define PALMAS_ADC_INFO(_chan, _x1, _x2, _v1, _v2, _t1, _t2, _is_uncalibrated) \
[PALMAS_ADC_CH_##_chan] = { \
.x1 = _x1, \
.x2 = _x2, \
.v1 = _v1, \
.v2 = _v2, \
.gain = PALMAS_TO_BE_CALCULATED, \
.offset = PALMAS_TO_BE_CALCULATED, \
.gain_error = PALMAS_TO_BE_CALCULATED, \
.trim1_reg = PALMAS_GPADC_TRIM##_t1, \
.trim2_reg = PALMAS_GPADC_TRIM##_t2, \
.is_uncalibrated = _is_uncalibrated \
}
static struct palmas_gpadc_info palmas_gpadc_info[] = {
PALMAS_ADC_INFO(IN0, 2064, 3112, 630, 950, 1, 2, false),
PALMAS_ADC_INFO(IN1, 2064, 3112, 630, 950, 1, 2, false),
PALMAS_ADC_INFO(IN2, 2064, 3112, 1260, 1900, 3, 4, false),
PALMAS_ADC_INFO(IN3, 2064, 3112, 630, 950, 1, 2, false),
PALMAS_ADC_INFO(IN4, 2064, 3112, 630, 950, 1, 2, false),
PALMAS_ADC_INFO(IN5, 2064, 3112, 630, 950, 1, 2, false),
PALMAS_ADC_INFO(IN6, 2064, 3112, 2520, 3800, 5, 6, false),
PALMAS_ADC_INFO(IN7, 2064, 3112, 2520, 3800, 7, 8, false),
PALMAS_ADC_INFO(IN8, 2064, 3112, 3150, 4750, 9, 10, false),
PALMAS_ADC_INFO(IN9, 2064, 3112, 5670, 8550, 11, 12, false),
PALMAS_ADC_INFO(IN10, 2064, 3112, 3465, 5225, 13, 14, false),
PALMAS_ADC_INFO(IN11, 0, 0, 0, 0, INVALID, INVALID, true),
PALMAS_ADC_INFO(IN12, 0, 0, 0, 0, INVALID, INVALID, true),
PALMAS_ADC_INFO(IN13, 0, 0, 0, 0, INVALID, INVALID, true),
PALMAS_ADC_INFO(IN14, 2064, 3112, 3645, 5225, 15, 16, false),
PALMAS_ADC_INFO(IN15, 0, 0, 0, 0, INVALID, INVALID, true),
};
/**
* struct palmas_gpadc - the palmas_gpadc structure
* @ch0_current: channel 0 current source setting
* 0: 0 uA
* 1: 5 uA
* 2: 15 uA
* 3: 20 uA
* @ch3_current: channel 0 current source setting
* 0: 0 uA
* 1: 10 uA
* 2: 400 uA
* 3: 800 uA
* @extended_delay: enable the gpadc extended delay mode
* @auto_conversion_period: define the auto_conversion_period
*
* This is the palmas_gpadc structure to store run-time information
* and pointers for this driver instance.
*/
struct palmas_gpadc {
struct device *dev;
struct palmas *palmas;
u8 ch0_current;
u8 ch3_current;
bool extended_delay;
int irq;
int irq_auto_0;
int irq_auto_1;
struct palmas_gpadc_info *adc_info;
struct completion conv_completion;
struct palmas_adc_wakeup_property wakeup1_data;
struct palmas_adc_wakeup_property wakeup2_data;
bool wakeup1_enable;
bool wakeup2_enable;
int auto_conversion_period;
};
/*
* GPADC lock issue in AUTO mode.
* Impact: In AUTO mode, GPADC conversion can be locked after disabling AUTO
* mode feature.
* Details:
* When the AUTO mode is the only conversion mode enabled, if the AUTO
* mode feature is disabled with bit GPADC_AUTO_CTRL. AUTO_CONV1_EN = 0
* or bit GPADC_AUTO_CTRL. AUTO_CONV0_EN = 0 during a conversion, the
* conversion mechanism can be seen as locked meaning that all following
* conversion will give 0 as a result. Bit GPADC_STATUS.GPADC_AVAILABLE
* will stay at 0 meaning that GPADC is busy. An RT conversion can unlock
* the GPADC.
*
* Workaround(s):
* To avoid the lock mechanism, the workaround to follow before any stop
* conversion request is:
* Force the GPADC state machine to be ON by using the GPADC_CTRL1.
* GPADC_FORCE bit = 1
* Shutdown the GPADC AUTO conversion using
* GPADC_AUTO_CTRL.SHUTDOWN_CONV[01] = 0.
* After 100us, force the GPADC state machine to be OFF by using the
* GPADC_CTRL1. GPADC_FORCE bit = 0
*/
static int palmas_disable_auto_conversion(struct palmas_gpadc *adc)
{
int ret;
ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
PALMAS_GPADC_CTRL1,
PALMAS_GPADC_CTRL1_GPADC_FORCE,
PALMAS_GPADC_CTRL1_GPADC_FORCE);
if (ret < 0) {
dev_err(adc->dev, "GPADC_CTRL1 update failed: %d\n", ret);
return ret;
}
ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
PALMAS_GPADC_AUTO_CTRL,
PALMAS_GPADC_AUTO_CTRL_SHUTDOWN_CONV1 |
PALMAS_GPADC_AUTO_CTRL_SHUTDOWN_CONV0,
0);
if (ret < 0) {
dev_err(adc->dev, "AUTO_CTRL update failed: %d\n", ret);
return ret;
}
udelay(100);
ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
PALMAS_GPADC_CTRL1,
PALMAS_GPADC_CTRL1_GPADC_FORCE, 0);
if (ret < 0)
dev_err(adc->dev, "GPADC_CTRL1 update failed: %d\n", ret);
return ret;
}
static irqreturn_t palmas_gpadc_irq(int irq, void *data)
{
struct palmas_gpadc *adc = data;
complete(&adc->conv_completion);
return IRQ_HANDLED;
}
static irqreturn_t palmas_gpadc_irq_auto(int irq, void *data)
{
struct palmas_gpadc *adc = data;
dev_dbg(adc->dev, "Threshold interrupt %d occurs\n", irq);
palmas_disable_auto_conversion(adc);
return IRQ_HANDLED;
}
static int palmas_gpadc_start_mask_interrupt(struct palmas_gpadc *adc,
bool mask)
{
int ret;
if (!mask)
ret = palmas_update_bits(adc->palmas, PALMAS_INTERRUPT_BASE,
PALMAS_INT3_MASK,
PALMAS_INT3_MASK_GPADC_EOC_SW, 0);
else
ret = palmas_update_bits(adc->palmas, PALMAS_INTERRUPT_BASE,
PALMAS_INT3_MASK,
PALMAS_INT3_MASK_GPADC_EOC_SW,
PALMAS_INT3_MASK_GPADC_EOC_SW);
if (ret < 0)
dev_err(adc->dev, "GPADC INT MASK update failed: %d\n", ret);
return ret;
}
static int palmas_gpadc_enable(struct palmas_gpadc *adc, int adc_chan,
int enable)
{
unsigned int mask, val;
int ret;
if (enable) {
val = (adc->extended_delay
<< PALMAS_GPADC_RT_CTRL_EXTEND_DELAY_SHIFT);
ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
PALMAS_GPADC_RT_CTRL,
PALMAS_GPADC_RT_CTRL_EXTEND_DELAY, val);
if (ret < 0) {
dev_err(adc->dev, "RT_CTRL update failed: %d\n", ret);
return ret;
}
mask = (PALMAS_GPADC_CTRL1_CURRENT_SRC_CH0_MASK |
PALMAS_GPADC_CTRL1_CURRENT_SRC_CH3_MASK |
PALMAS_GPADC_CTRL1_GPADC_FORCE);
val = (adc->ch0_current
<< PALMAS_GPADC_CTRL1_CURRENT_SRC_CH0_SHIFT);
val |= (adc->ch3_current
<< PALMAS_GPADC_CTRL1_CURRENT_SRC_CH3_SHIFT);
val |= PALMAS_GPADC_CTRL1_GPADC_FORCE;
ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
PALMAS_GPADC_CTRL1, mask, val);
if (ret < 0) {
dev_err(adc->dev,
"Failed to update current setting: %d\n", ret);
return ret;
}
mask = (PALMAS_GPADC_SW_SELECT_SW_CONV0_SEL_MASK |
PALMAS_GPADC_SW_SELECT_SW_CONV_EN);
val = (adc_chan | PALMAS_GPADC_SW_SELECT_SW_CONV_EN);
ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
PALMAS_GPADC_SW_SELECT, mask, val);
if (ret < 0) {
dev_err(adc->dev, "SW_SELECT update failed: %d\n", ret);
return ret;
}
} else {
ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
PALMAS_GPADC_SW_SELECT, 0);
if (ret < 0)
dev_err(adc->dev, "SW_SELECT write failed: %d\n", ret);
ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
PALMAS_GPADC_CTRL1,
PALMAS_GPADC_CTRL1_GPADC_FORCE, 0);
if (ret < 0) {
dev_err(adc->dev, "CTRL1 update failed: %d\n", ret);
return ret;
}
}
return ret;
}
static int palmas_gpadc_read_prepare(struct palmas_gpadc *adc, int adc_chan)
{
int ret;
ret = palmas_gpadc_enable(adc, adc_chan, true);
if (ret < 0)
return ret;
return palmas_gpadc_start_mask_interrupt(adc, 0);
}
static void palmas_gpadc_read_done(struct palmas_gpadc *adc, int adc_chan)
{
palmas_gpadc_start_mask_interrupt(adc, 1);
palmas_gpadc_enable(adc, adc_chan, false);
}
static int palmas_gpadc_calibrate(struct palmas_gpadc *adc, int adc_chan)
{
int k;
int d1;
int d2;
int ret;
int gain;
int x1 = adc->adc_info[adc_chan].x1;
int x2 = adc->adc_info[adc_chan].x2;
int v1 = adc->adc_info[adc_chan].v1;
int v2 = adc->adc_info[adc_chan].v2;
ret = palmas_read(adc->palmas, PALMAS_TRIM_GPADC_BASE,
adc->adc_info[adc_chan].trim1_reg, &d1);
if (ret < 0) {
dev_err(adc->dev, "TRIM read failed: %d\n", ret);
goto scrub;
}
ret = palmas_read(adc->palmas, PALMAS_TRIM_GPADC_BASE,
adc->adc_info[adc_chan].trim2_reg, &d2);
if (ret < 0) {
dev_err(adc->dev, "TRIM read failed: %d\n", ret);
goto scrub;
}
/* gain error calculation */
k = (1000 + (1000 * (d2 - d1)) / (x2 - x1));
/* gain calculation */
gain = ((v2 - v1) * 1000) / (x2 - x1);
adc->adc_info[adc_chan].gain_error = k;
adc->adc_info[adc_chan].gain = gain;
/* offset Calculation */
adc->adc_info[adc_chan].offset = (d1 * 1000) - ((k - 1000) * x1);
scrub:
return ret;
}
static int palmas_gpadc_start_conversion(struct palmas_gpadc *adc, int adc_chan)
{
unsigned int val;
int ret;
init_completion(&adc->conv_completion);
ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
PALMAS_GPADC_SW_SELECT,
PALMAS_GPADC_SW_SELECT_SW_START_CONV0,
PALMAS_GPADC_SW_SELECT_SW_START_CONV0);
if (ret < 0) {
dev_err(adc->dev, "SELECT_SW_START write failed: %d\n", ret);
return ret;
}
ret = wait_for_completion_timeout(&adc->conv_completion,
PALMAS_ADC_CONVERSION_TIMEOUT);
if (ret == 0) {
dev_err(adc->dev, "conversion not completed\n");
return -ETIMEDOUT;
}
ret = palmas_bulk_read(adc->palmas, PALMAS_GPADC_BASE,
PALMAS_GPADC_SW_CONV0_LSB, &val, 2);
if (ret < 0) {
dev_err(adc->dev, "SW_CONV0_LSB read failed: %d\n", ret);
return ret;
}
ret = val & 0xFFF;
return ret;
}
static int palmas_gpadc_get_calibrated_code(struct palmas_gpadc *adc,
int adc_chan, int val)
{
if (!adc->adc_info[adc_chan].is_uncalibrated)
val = (val*1000 - adc->adc_info[adc_chan].offset) /
adc->adc_info[adc_chan].gain_error;
if (val < 0) {
dev_err(adc->dev, "Mismatch with calibration\n");
return 0;
}
val = (val * adc->adc_info[adc_chan].gain) / 1000;
return val;
}
static int palmas_gpadc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val, int *val2, long mask)
{
struct palmas_gpadc *adc = iio_priv(indio_dev);
int adc_chan = chan->channel;
int ret = 0;
if (adc_chan > PALMAS_ADC_CH_MAX)
return -EINVAL;
mutex_lock(&indio_dev->mlock);
switch (mask) {
case IIO_CHAN_INFO_RAW:
case IIO_CHAN_INFO_PROCESSED:
ret = palmas_gpadc_read_prepare(adc, adc_chan);
if (ret < 0)
goto out;
ret = palmas_gpadc_start_conversion(adc, adc_chan);
if (ret < 0) {
dev_err(adc->dev,
"ADC start conversion failed\n");
goto out;
}
if (mask == IIO_CHAN_INFO_PROCESSED)
ret = palmas_gpadc_get_calibrated_code(
adc, adc_chan, ret);
*val = ret;
ret = IIO_VAL_INT;
goto out;
}
mutex_unlock(&indio_dev->mlock);
return ret;
out:
palmas_gpadc_read_done(adc, adc_chan);
mutex_unlock(&indio_dev->mlock);
return ret;
}
static const struct iio_info palmas_gpadc_iio_info = {
.read_raw = palmas_gpadc_read_raw,
.driver_module = THIS_MODULE,
};
#define PALMAS_ADC_CHAN_IIO(chan, _type, chan_info) \
{ \
.datasheet_name = PALMAS_DATASHEET_NAME(chan), \
.type = _type, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(chan_info), \
.indexed = 1, \
.channel = PALMAS_ADC_CH_##chan, \
}
static const struct iio_chan_spec palmas_gpadc_iio_channel[] = {
PALMAS_ADC_CHAN_IIO(IN0, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
PALMAS_ADC_CHAN_IIO(IN1, IIO_TEMP, IIO_CHAN_INFO_RAW),
PALMAS_ADC_CHAN_IIO(IN2, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
PALMAS_ADC_CHAN_IIO(IN3, IIO_TEMP, IIO_CHAN_INFO_RAW),
PALMAS_ADC_CHAN_IIO(IN4, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
PALMAS_ADC_CHAN_IIO(IN5, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
PALMAS_ADC_CHAN_IIO(IN6, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
PALMAS_ADC_CHAN_IIO(IN7, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
PALMAS_ADC_CHAN_IIO(IN8, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
PALMAS_ADC_CHAN_IIO(IN9, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
PALMAS_ADC_CHAN_IIO(IN10, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
PALMAS_ADC_CHAN_IIO(IN11, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
PALMAS_ADC_CHAN_IIO(IN12, IIO_TEMP, IIO_CHAN_INFO_RAW),
PALMAS_ADC_CHAN_IIO(IN13, IIO_TEMP, IIO_CHAN_INFO_RAW),
PALMAS_ADC_CHAN_IIO(IN14, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
PALMAS_ADC_CHAN_IIO(IN15, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED),
};
static int palmas_gpadc_get_adc_dt_data(struct platform_device *pdev,
struct palmas_gpadc_platform_data **gpadc_pdata)
{
struct device_node *np = pdev->dev.of_node;
struct palmas_gpadc_platform_data *gp_data;
int ret;
u32 pval;
gp_data = devm_kzalloc(&pdev->dev, sizeof(*gp_data), GFP_KERNEL);
if (!gp_data)
return -ENOMEM;
ret = of_property_read_u32(np, "ti,channel0-current-microamp", &pval);
if (!ret)
gp_data->ch0_current = pval;
ret = of_property_read_u32(np, "ti,channel3-current-microamp", &pval);
if (!ret)
gp_data->ch3_current = pval;
gp_data->extended_delay = of_property_read_bool(np,
"ti,enable-extended-delay");
*gpadc_pdata = gp_data;
return 0;
}
static int palmas_gpadc_probe(struct platform_device *pdev)
{
struct palmas_gpadc *adc;
struct palmas_platform_data *pdata;
struct palmas_gpadc_platform_data *gpadc_pdata = NULL;
struct iio_dev *indio_dev;
int ret, i;
pdata = dev_get_platdata(pdev->dev.parent);
if (pdata && pdata->gpadc_pdata)
gpadc_pdata = pdata->gpadc_pdata;
if (!gpadc_pdata && pdev->dev.of_node) {
ret = palmas_gpadc_get_adc_dt_data(pdev, &gpadc_pdata);
if (ret < 0)
return ret;
}
if (!gpadc_pdata)
return -EINVAL;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
if (!indio_dev) {
dev_err(&pdev->dev, "iio_device_alloc failed\n");
return -ENOMEM;
}
adc = iio_priv(indio_dev);
adc->dev = &pdev->dev;
adc->palmas = dev_get_drvdata(pdev->dev.parent);
adc->adc_info = palmas_gpadc_info;
init_completion(&adc->conv_completion);
dev_set_drvdata(&pdev->dev, indio_dev);
adc->auto_conversion_period = gpadc_pdata->auto_conversion_period_ms;
adc->irq = palmas_irq_get_virq(adc->palmas, PALMAS_GPADC_EOC_SW_IRQ);
if (adc->irq < 0) {
dev_err(adc->dev,
"get virq failed: %d\n", adc->irq);
ret = adc->irq;
goto out;
}
ret = request_threaded_irq(adc->irq, NULL,
palmas_gpadc_irq,
IRQF_ONESHOT | IRQF_EARLY_RESUME, dev_name(adc->dev),
adc);
if (ret < 0) {
dev_err(adc->dev,
"request irq %d failed: %d\n", adc->irq, ret);
goto out;
}
if (gpadc_pdata->adc_wakeup1_data) {
memcpy(&adc->wakeup1_data, gpadc_pdata->adc_wakeup1_data,
sizeof(adc->wakeup1_data));
adc->wakeup1_enable = true;
adc->irq_auto_0 = platform_get_irq(pdev, 1);
ret = request_threaded_irq(adc->irq_auto_0, NULL,
palmas_gpadc_irq_auto,
IRQF_ONESHOT | IRQF_EARLY_RESUME,
"palmas-adc-auto-0", adc);
if (ret < 0) {
dev_err(adc->dev, "request auto0 irq %d failed: %d\n",
adc->irq_auto_0, ret);
goto out_irq_free;
}
}
if (gpadc_pdata->adc_wakeup2_data) {
memcpy(&adc->wakeup2_data, gpadc_pdata->adc_wakeup2_data,
sizeof(adc->wakeup2_data));
adc->wakeup2_enable = true;
adc->irq_auto_1 = platform_get_irq(pdev, 2);
ret = request_threaded_irq(adc->irq_auto_1, NULL,
palmas_gpadc_irq_auto,
IRQF_ONESHOT | IRQF_EARLY_RESUME,
"palmas-adc-auto-1", adc);
if (ret < 0) {
dev_err(adc->dev, "request auto1 irq %d failed: %d\n",
adc->irq_auto_1, ret);
goto out_irq_auto0_free;
}
}
/* set the current source 0 (value 0/5/15/20 uA => 0..3) */
if (gpadc_pdata->ch0_current <= 1)
adc->ch0_current = PALMAS_ADC_CH0_CURRENT_SRC_0;
else if (gpadc_pdata->ch0_current <= 5)
adc->ch0_current = PALMAS_ADC_CH0_CURRENT_SRC_5;
else if (gpadc_pdata->ch0_current <= 15)
adc->ch0_current = PALMAS_ADC_CH0_CURRENT_SRC_15;
else
adc->ch0_current = PALMAS_ADC_CH0_CURRENT_SRC_20;
/* set the current source 3 (value 0/10/400/800 uA => 0..3) */
if (gpadc_pdata->ch3_current <= 1)
adc->ch3_current = PALMAS_ADC_CH3_CURRENT_SRC_0;
else if (gpadc_pdata->ch3_current <= 10)
adc->ch3_current = PALMAS_ADC_CH3_CURRENT_SRC_10;
else if (gpadc_pdata->ch3_current <= 400)
adc->ch3_current = PALMAS_ADC_CH3_CURRENT_SRC_400;
else
adc->ch3_current = PALMAS_ADC_CH3_CURRENT_SRC_800;
adc->extended_delay = gpadc_pdata->extended_delay;
indio_dev->name = MOD_NAME;
indio_dev->dev.parent = &pdev->dev;
indio_dev->info = &palmas_gpadc_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = palmas_gpadc_iio_channel;
indio_dev->num_channels = ARRAY_SIZE(palmas_gpadc_iio_channel);
ret = iio_device_register(indio_dev);
if (ret < 0) {
dev_err(adc->dev, "iio_device_register() failed: %d\n", ret);
goto out_irq_auto1_free;
}
device_set_wakeup_capable(&pdev->dev, 1);
for (i = 0; i < PALMAS_ADC_CH_MAX; i++) {
if (!(adc->adc_info[i].is_uncalibrated))
palmas_gpadc_calibrate(adc, i);
}
if (adc->wakeup1_enable || adc->wakeup2_enable)
device_wakeup_enable(&pdev->dev);
return 0;
out_irq_auto1_free:
if (gpadc_pdata->adc_wakeup2_data)
free_irq(adc->irq_auto_1, adc);
out_irq_auto0_free:
if (gpadc_pdata->adc_wakeup1_data)
free_irq(adc->irq_auto_0, adc);
out_irq_free:
free_irq(adc->irq, adc);
out:
return ret;
}
static int palmas_gpadc_remove(struct platform_device *pdev)
{
struct iio_dev *indio_dev = dev_to_iio_dev(&pdev->dev);
struct palmas_gpadc *adc = iio_priv(indio_dev);
if (adc->wakeup1_enable || adc->wakeup2_enable)
device_wakeup_disable(&pdev->dev);
iio_device_unregister(indio_dev);
free_irq(adc->irq, adc);
if (adc->wakeup1_enable)
free_irq(adc->irq_auto_0, adc);
if (adc->wakeup2_enable)
free_irq(adc->irq_auto_1, adc);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int palmas_adc_wakeup_configure(struct palmas_gpadc *adc)
{
int adc_period, conv;
int i;
int ch0 = 0, ch1 = 0;
int thres;
int ret;
adc_period = adc->auto_conversion_period;
for (i = 0; i < 16; ++i) {
if (((1000 * (1 << i)) / 32) < adc_period)
continue;
}
if (i > 0)
i--;
adc_period = i;
ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
PALMAS_GPADC_AUTO_CTRL,
PALMAS_GPADC_AUTO_CTRL_COUNTER_CONV_MASK,
adc_period);
if (ret < 0) {
dev_err(adc->dev, "AUTO_CTRL write failed: %d\n", ret);
return ret;
}
conv = 0;
if (adc->wakeup1_enable) {
int polarity;
ch0 = adc->wakeup1_data.adc_channel_number;
conv |= PALMAS_GPADC_AUTO_CTRL_AUTO_CONV0_EN;
if (adc->wakeup1_data.adc_high_threshold > 0) {
thres = adc->wakeup1_data.adc_high_threshold;
polarity = 0;
} else {
thres = adc->wakeup1_data.adc_low_threshold;
polarity = PALMAS_GPADC_THRES_CONV0_MSB_THRES_CONV0_POL;
}
ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
PALMAS_GPADC_THRES_CONV0_LSB, thres & 0xFF);
if (ret < 0) {
dev_err(adc->dev,
"THRES_CONV0_LSB write failed: %d\n", ret);
return ret;
}
ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
PALMAS_GPADC_THRES_CONV0_MSB,
((thres >> 8) & 0xF) | polarity);
if (ret < 0) {
dev_err(adc->dev,
"THRES_CONV0_MSB write failed: %d\n", ret);
return ret;
}
}
if (adc->wakeup2_enable) {
int polarity;
ch1 = adc->wakeup2_data.adc_channel_number;
conv |= PALMAS_GPADC_AUTO_CTRL_AUTO_CONV1_EN;
if (adc->wakeup2_data.adc_high_threshold > 0) {
thres = adc->wakeup2_data.adc_high_threshold;
polarity = 0;
} else {
thres = adc->wakeup2_data.adc_low_threshold;
polarity = PALMAS_GPADC_THRES_CONV1_MSB_THRES_CONV1_POL;
}
ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
PALMAS_GPADC_THRES_CONV1_LSB, thres & 0xFF);
if (ret < 0) {
dev_err(adc->dev,
"THRES_CONV1_LSB write failed: %d\n", ret);
return ret;
}
ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
PALMAS_GPADC_THRES_CONV1_MSB,
((thres >> 8) & 0xF) | polarity);
if (ret < 0) {
dev_err(adc->dev,
"THRES_CONV1_MSB write failed: %d\n", ret);
return ret;
}
}
ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
PALMAS_GPADC_AUTO_SELECT, (ch1 << 4) | ch0);
if (ret < 0) {
dev_err(adc->dev, "AUTO_SELECT write failed: %d\n", ret);
return ret;
}
ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
PALMAS_GPADC_AUTO_CTRL,
PALMAS_GPADC_AUTO_CTRL_AUTO_CONV1_EN |
PALMAS_GPADC_AUTO_CTRL_AUTO_CONV0_EN, conv);
if (ret < 0)
dev_err(adc->dev, "AUTO_CTRL write failed: %d\n", ret);
return ret;
}
static int palmas_adc_wakeup_reset(struct palmas_gpadc *adc)
{
int ret;
ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
PALMAS_GPADC_AUTO_SELECT, 0);
if (ret < 0) {
dev_err(adc->dev, "AUTO_SELECT write failed: %d\n", ret);
return ret;
}
ret = palmas_disable_auto_conversion(adc);
if (ret < 0)
dev_err(adc->dev, "Disable auto conversion failed: %d\n", ret);
return ret;
}
static int palmas_gpadc_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct palmas_gpadc *adc = iio_priv(indio_dev);
int wakeup = adc->wakeup1_enable || adc->wakeup2_enable;
int ret;
if (!device_may_wakeup(dev) || !wakeup)
return 0;
ret = palmas_adc_wakeup_configure(adc);
if (ret < 0)
return ret;
if (adc->wakeup1_enable)
enable_irq_wake(adc->irq_auto_0);
if (adc->wakeup2_enable)
enable_irq_wake(adc->irq_auto_1);
return 0;
}
static int palmas_gpadc_resume(struct device *dev)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct palmas_gpadc *adc = iio_priv(indio_dev);
int wakeup = adc->wakeup1_enable || adc->wakeup2_enable;
int ret;
if (!device_may_wakeup(dev) || !wakeup)
return 0;
ret = palmas_adc_wakeup_reset(adc);
if (ret < 0)
return ret;
if (adc->wakeup1_enable)
disable_irq_wake(adc->irq_auto_0);
if (adc->wakeup2_enable)
disable_irq_wake(adc->irq_auto_1);
return 0;
};
#endif
static const struct dev_pm_ops palmas_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(palmas_gpadc_suspend,
palmas_gpadc_resume)
};
static const struct of_device_id of_palmas_gpadc_match_tbl[] = {
{ .compatible = "ti,palmas-gpadc", },
{ /* end */ }
};
MODULE_DEVICE_TABLE(of, of_palmas_gpadc_match_tbl);
static struct platform_driver palmas_gpadc_driver = {
.probe = palmas_gpadc_probe,
.remove = palmas_gpadc_remove,
.driver = {
.name = MOD_NAME,
.pm = &palmas_pm_ops,
.of_match_table = of_palmas_gpadc_match_tbl,
},
};
static int __init palmas_gpadc_init(void)
{
return platform_driver_register(&palmas_gpadc_driver);
}
module_init(palmas_gpadc_init);
static void __exit palmas_gpadc_exit(void)
{
platform_driver_unregister(&palmas_gpadc_driver);
}
module_exit(palmas_gpadc_exit);
MODULE_DESCRIPTION("palmas GPADC driver");
MODULE_AUTHOR("Pradeep Goudagunta<pgoudagunta@nvidia.com>");
MODULE_ALIAS("platform:palmas-gpadc");
MODULE_LICENSE("GPL v2");

View File

@ -1,10 +1,11 @@
/*
* Copyright (C) 2014 Angelo Compagnucci <angelo.compagnucci@gmail.com>
*
* Driver for Texas Instruments' ADC128S052 and ADC122S021 ADC chip.
* Driver for Texas Instruments' ADC128S052, ADC122S021 and ADC124S021 ADC chip.
* Datasheets can be found here:
* http://www.ti.com/lit/ds/symlink/adc128s052.pdf
* http://www.ti.com/lit/ds/symlink/adc122s021.pdf
* http://www.ti.com/lit/ds/symlink/adc124s021.pdf
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@ -114,9 +115,17 @@ static const struct iio_chan_spec adc122s021_channels[] = {
ADC128_VOLTAGE_CHANNEL(1),
};
static const struct iio_chan_spec adc124s021_channels[] = {
ADC128_VOLTAGE_CHANNEL(0),
ADC128_VOLTAGE_CHANNEL(1),
ADC128_VOLTAGE_CHANNEL(2),
ADC128_VOLTAGE_CHANNEL(3),
};
static const struct adc128_configuration adc128_config[] = {
{ adc128s052_channels, ARRAY_SIZE(adc128s052_channels) },
{ adc122s021_channels, ARRAY_SIZE(adc122s021_channels) },
{ adc124s021_channels, ARRAY_SIZE(adc124s021_channels) },
};
static const struct iio_info adc128_info = {
@ -177,6 +186,7 @@ static int adc128_remove(struct spi_device *spi)
static const struct of_device_id adc128_of_match[] = {
{ .compatible = "ti,adc128s052", },
{ .compatible = "ti,adc122s021", },
{ .compatible = "ti,adc124s021", },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, adc128_of_match);
@ -184,6 +194,7 @@ MODULE_DEVICE_TABLE(of, adc128_of_match);
static const struct spi_device_id adc128_id[] = {
{ "adc128s052", 0}, /* index into adc128_config */
{ "adc122s021", 1},
{ "adc124s021", 2},
{ }
};
MODULE_DEVICE_TABLE(spi, adc128_id);

View File

@ -0,0 +1,486 @@
/*
* Copyright (C) 2015 Prevas A/S
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/device.h>
#include <linux/kernel.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 <linux/of.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#define ADS8688_CMD_REG(x) (x << 8)
#define ADS8688_CMD_REG_NOOP 0x00
#define ADS8688_CMD_REG_RST 0x85
#define ADS8688_CMD_REG_MAN_CH(chan) (0xC0 | (4 * chan))
#define ADS8688_CMD_DONT_CARE_BITS 16
#define ADS8688_PROG_REG(x) (x << 9)
#define ADS8688_PROG_REG_RANGE_CH(chan) (0x05 + chan)
#define ADS8688_PROG_WR_BIT BIT(8)
#define ADS8688_PROG_DONT_CARE_BITS 8
#define ADS8688_REG_PLUSMINUS25VREF 0
#define ADS8688_REG_PLUSMINUS125VREF 1
#define ADS8688_REG_PLUSMINUS0625VREF 2
#define ADS8688_REG_PLUS25VREF 5
#define ADS8688_REG_PLUS125VREF 6
#define ADS8688_VREF_MV 4096
#define ADS8688_REALBITS 16
/*
* enum ads8688_range - ADS8688 reference voltage range
* @ADS8688_PLUSMINUS25VREF: Device is configured for input range ±2.5 * VREF
* @ADS8688_PLUSMINUS125VREF: Device is configured for input range ±1.25 * VREF
* @ADS8688_PLUSMINUS0625VREF: Device is configured for input range ±0.625 * VREF
* @ADS8688_PLUS25VREF: Device is configured for input range 0 - 2.5 * VREF
* @ADS8688_PLUS125VREF: Device is configured for input range 0 - 1.25 * VREF
*/
enum ads8688_range {
ADS8688_PLUSMINUS25VREF,
ADS8688_PLUSMINUS125VREF,
ADS8688_PLUSMINUS0625VREF,
ADS8688_PLUS25VREF,
ADS8688_PLUS125VREF,
};
struct ads8688_chip_info {
const struct iio_chan_spec *channels;
unsigned int num_channels;
};
struct ads8688_state {
struct mutex lock;
const struct ads8688_chip_info *chip_info;
struct spi_device *spi;
struct regulator *reg;
unsigned int vref_mv;
enum ads8688_range range[8];
union {
__be32 d32;
u8 d8[4];
} data[2] ____cacheline_aligned;
};
enum ads8688_id {
ID_ADS8684,
ID_ADS8688,
};
struct ads8688_ranges {
enum ads8688_range range;
unsigned int scale;
int offset;
u8 reg;
};
static const struct ads8688_ranges ads8688_range_def[5] = {
{
.range = ADS8688_PLUSMINUS25VREF,
.scale = 76295,
.offset = -(1 << (ADS8688_REALBITS - 1)),
.reg = ADS8688_REG_PLUSMINUS25VREF,
}, {
.range = ADS8688_PLUSMINUS125VREF,
.scale = 38148,
.offset = -(1 << (ADS8688_REALBITS - 1)),
.reg = ADS8688_REG_PLUSMINUS125VREF,
}, {
.range = ADS8688_PLUSMINUS0625VREF,
.scale = 19074,
.offset = -(1 << (ADS8688_REALBITS - 1)),
.reg = ADS8688_REG_PLUSMINUS0625VREF,
}, {
.range = ADS8688_PLUS25VREF,
.scale = 38148,
.offset = 0,
.reg = ADS8688_REG_PLUS25VREF,
}, {
.range = ADS8688_PLUS125VREF,
.scale = 19074,
.offset = 0,
.reg = ADS8688_REG_PLUS125VREF,
}
};
static ssize_t ads8688_show_scales(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ads8688_state *st = iio_priv(dev_to_iio_dev(dev));
return sprintf(buf, "0.%09u 0.%09u 0.%09u\n",
ads8688_range_def[0].scale * st->vref_mv,
ads8688_range_def[1].scale * st->vref_mv,
ads8688_range_def[2].scale * st->vref_mv);
}
static ssize_t ads8688_show_offsets(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%d %d\n", ads8688_range_def[0].offset,
ads8688_range_def[3].offset);
}
static IIO_DEVICE_ATTR(in_voltage_scale_available, S_IRUGO,
ads8688_show_scales, NULL, 0);
static IIO_DEVICE_ATTR(in_voltage_offset_available, S_IRUGO,
ads8688_show_offsets, NULL, 0);
static struct attribute *ads8688_attributes[] = {
&iio_dev_attr_in_voltage_scale_available.dev_attr.attr,
&iio_dev_attr_in_voltage_offset_available.dev_attr.attr,
NULL,
};
static const struct attribute_group ads8688_attribute_group = {
.attrs = ads8688_attributes,
};
#define ADS8688_CHAN(index) \
{ \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = index, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \
| BIT(IIO_CHAN_INFO_SCALE) \
| BIT(IIO_CHAN_INFO_OFFSET), \
}
static const struct iio_chan_spec ads8684_channels[] = {
ADS8688_CHAN(0),
ADS8688_CHAN(1),
ADS8688_CHAN(2),
ADS8688_CHAN(3),
};
static const struct iio_chan_spec ads8688_channels[] = {
ADS8688_CHAN(0),
ADS8688_CHAN(1),
ADS8688_CHAN(2),
ADS8688_CHAN(3),
ADS8688_CHAN(4),
ADS8688_CHAN(5),
ADS8688_CHAN(6),
ADS8688_CHAN(7),
};
static int ads8688_prog_write(struct iio_dev *indio_dev, unsigned int addr,
unsigned int val)
{
struct ads8688_state *st = iio_priv(indio_dev);
u32 tmp;
tmp = ADS8688_PROG_REG(addr) | ADS8688_PROG_WR_BIT | val;
tmp <<= ADS8688_PROG_DONT_CARE_BITS;
st->data[0].d32 = cpu_to_be32(tmp);
return spi_write(st->spi, &st->data[0].d8[1], 3);
}
static int ads8688_reset(struct iio_dev *indio_dev)
{
struct ads8688_state *st = iio_priv(indio_dev);
u32 tmp;
tmp = ADS8688_CMD_REG(ADS8688_CMD_REG_RST);
tmp <<= ADS8688_CMD_DONT_CARE_BITS;
st->data[0].d32 = cpu_to_be32(tmp);
return spi_write(st->spi, &st->data[0].d8[0], 4);
}
static int ads8688_read(struct iio_dev *indio_dev, unsigned int chan)
{
struct ads8688_state *st = iio_priv(indio_dev);
int ret;
u32 tmp;
struct spi_transfer t[] = {
{
.tx_buf = &st->data[0].d8[0],
.len = 4,
.cs_change = 1,
}, {
.tx_buf = &st->data[1].d8[0],
.rx_buf = &st->data[1].d8[0],
.len = 4,
},
};
tmp = ADS8688_CMD_REG(ADS8688_CMD_REG_MAN_CH(chan));
tmp <<= ADS8688_CMD_DONT_CARE_BITS;
st->data[0].d32 = cpu_to_be32(tmp);
tmp = ADS8688_CMD_REG(ADS8688_CMD_REG_NOOP);
tmp <<= ADS8688_CMD_DONT_CARE_BITS;
st->data[1].d32 = cpu_to_be32(tmp);
ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
if (ret < 0)
return ret;
return be32_to_cpu(st->data[1].d32) & 0xffff;
}
static int ads8688_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long m)
{
int ret, offset;
unsigned long scale_mv;
struct ads8688_state *st = iio_priv(indio_dev);
mutex_lock(&st->lock);
switch (m) {
case IIO_CHAN_INFO_RAW:
ret = ads8688_read(indio_dev, chan->channel);
mutex_unlock(&st->lock);
if (ret < 0)
return ret;
*val = ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
scale_mv = st->vref_mv;
scale_mv *= ads8688_range_def[st->range[chan->channel]].scale;
*val = 0;
*val2 = scale_mv;
mutex_unlock(&st->lock);
return IIO_VAL_INT_PLUS_NANO;
case IIO_CHAN_INFO_OFFSET:
offset = ads8688_range_def[st->range[chan->channel]].offset;
*val = offset;
mutex_unlock(&st->lock);
return IIO_VAL_INT;
}
mutex_unlock(&st->lock);
return -EINVAL;
}
static int ads8688_write_reg_range(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
enum ads8688_range range)
{
unsigned int tmp;
int ret;
tmp = ADS8688_PROG_REG_RANGE_CH(chan->channel);
ret = ads8688_prog_write(indio_dev, tmp, range);
return ret;
}
static int ads8688_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct ads8688_state *st = iio_priv(indio_dev);
unsigned int scale = 0;
int ret = -EINVAL, i, offset = 0;
mutex_lock(&st->lock);
switch (mask) {
case IIO_CHAN_INFO_SCALE:
/* If the offset is 0 the ±2.5 * VREF mode is not available */
offset = ads8688_range_def[st->range[chan->channel]].offset;
if (offset == 0 && val2 == ads8688_range_def[0].scale * st->vref_mv) {
mutex_unlock(&st->lock);
return -EINVAL;
}
/* Lookup new mode */
for (i = 0; i < ARRAY_SIZE(ads8688_range_def); i++)
if (val2 == ads8688_range_def[i].scale * st->vref_mv &&
offset == ads8688_range_def[i].offset) {
ret = ads8688_write_reg_range(indio_dev, chan,
ads8688_range_def[i].reg);
break;
}
break;
case IIO_CHAN_INFO_OFFSET:
/*
* There are only two available offsets:
* 0 and -(1 << (ADS8688_REALBITS - 1))
*/
if (!(ads8688_range_def[0].offset == val ||
ads8688_range_def[3].offset == val)) {
mutex_unlock(&st->lock);
return -EINVAL;
}
/*
* If the device are in ±2.5 * VREF mode, it's not allowed to
* switch to a mode where the offset is 0
*/
if (val == 0 &&
st->range[chan->channel] == ADS8688_PLUSMINUS25VREF) {
mutex_unlock(&st->lock);
return -EINVAL;
}
scale = ads8688_range_def[st->range[chan->channel]].scale;
/* Lookup new mode */
for (i = 0; i < ARRAY_SIZE(ads8688_range_def); i++)
if (val == ads8688_range_def[i].offset &&
scale == ads8688_range_def[i].scale) {
ret = ads8688_write_reg_range(indio_dev, chan,
ads8688_range_def[i].reg);
break;
}
break;
}
if (!ret)
st->range[chan->channel] = ads8688_range_def[i].range;
mutex_unlock(&st->lock);
return ret;
}
static int ads8688_write_raw_get_fmt(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
long mask)
{
switch (mask) {
case IIO_CHAN_INFO_SCALE:
return IIO_VAL_INT_PLUS_NANO;
case IIO_CHAN_INFO_OFFSET:
return IIO_VAL_INT;
}
return -EINVAL;
}
static const struct iio_info ads8688_info = {
.read_raw = &ads8688_read_raw,
.write_raw = &ads8688_write_raw,
.write_raw_get_fmt = &ads8688_write_raw_get_fmt,
.attrs = &ads8688_attribute_group,
.driver_module = THIS_MODULE,
};
static const struct ads8688_chip_info ads8688_chip_info_tbl[] = {
[ID_ADS8684] = {
.channels = ads8684_channels,
.num_channels = ARRAY_SIZE(ads8684_channels),
},
[ID_ADS8688] = {
.channels = ads8688_channels,
.num_channels = ARRAY_SIZE(ads8688_channels),
},
};
static int ads8688_probe(struct spi_device *spi)
{
struct ads8688_state *st;
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
if (indio_dev == NULL)
return -ENOMEM;
st = iio_priv(indio_dev);
st->reg = devm_regulator_get_optional(&spi->dev, "vref");
if (!IS_ERR(st->reg)) {
ret = regulator_enable(st->reg);
if (ret)
return ret;
ret = regulator_get_voltage(st->reg);
if (ret < 0)
goto error_out;
st->vref_mv = ret / 1000;
} else {
/* Use internal reference */
st->vref_mv = ADS8688_VREF_MV;
}
st->chip_info = &ads8688_chip_info_tbl[spi_get_device_id(spi)->driver_data];
spi->mode = SPI_MODE_1;
spi_set_drvdata(spi, indio_dev);
st->spi = spi;
indio_dev->name = spi_get_device_id(spi)->name;
indio_dev->dev.parent = &spi->dev;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = st->chip_info->channels;
indio_dev->num_channels = st->chip_info->num_channels;
indio_dev->info = &ads8688_info;
ads8688_reset(indio_dev);
mutex_init(&st->lock);
ret = iio_device_register(indio_dev);
if (ret)
goto error_out;
return 0;
error_out:
if (!IS_ERR_OR_NULL(st->reg))
regulator_disable(st->reg);
return ret;
}
static int ads8688_remove(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct ads8688_state *st = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
if (!IS_ERR_OR_NULL(st->reg))
regulator_disable(st->reg);
return 0;
}
static const struct spi_device_id ads8688_id[] = {
{"ads8684", ID_ADS8684},
{"ads8688", ID_ADS8688},
{}
};
MODULE_DEVICE_TABLE(spi, ads8688_id);
static const struct of_device_id ads8688_of_match[] = {
{ .compatible = "ti,ads8684" },
{ .compatible = "ti,ads8688" },
{ }
};
MODULE_DEVICE_TABLE(of, ads8688_of_match);
static struct spi_driver ads8688_driver = {
.driver = {
.name = "ads8688",
.owner = THIS_MODULE,
},
.probe = ads8688_probe,
.remove = ads8688_remove,
.id_table = ads8688_id,
};
module_spi_driver(ads8688_driver);
MODULE_AUTHOR("Sean Nyekjaer <sean.nyekjaer@prevas.dk>");
MODULE_DESCRIPTION("Texas Instruments ADS8688 driver");
MODULE_LICENSE("GPL v2");

View File

@ -803,7 +803,7 @@ err:
return ret;
}
static struct iio_buffer_setup_ops xadc_buffer_ops = {
static const struct iio_buffer_setup_ops xadc_buffer_ops = {
.preenable = &xadc_preenable,
.postenable = &iio_triggered_buffer_postenable,
.predisable = &iio_triggered_buffer_predisable,

View File

@ -9,6 +9,26 @@ config IIO_BUFFER_CB
Should be selected by any drivers that do in-kernel push
usage. That is, those where the data is pushed to the consumer.
config IIO_BUFFER_DMA
tristate
help
Provides the generic IIO DMA buffer infrastructure that can be used by
drivers for devices with DMA support to implement the IIO buffer.
Should be selected by drivers that want to use the generic DMA buffer
infrastructure.
config IIO_BUFFER_DMAENGINE
tristate
select IIO_BUFFER_DMA
help
Provides a bonding of the generic IIO DMA buffer infrastructure with the
DMAengine framework. This can be used by converter drivers with a DMA port
connected to an external DMA controller which is supported by the
DMAengine framework.
Should be selected by drivers that want to use this functionality.
config IIO_KFIFO_BUF
tristate "Industrial I/O buffering based on kfifo"
help

View File

@ -4,5 +4,7 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_IIO_BUFFER_CB) += industrialio-buffer-cb.o
obj-$(CONFIG_IIO_BUFFER_DMA) += industrialio-buffer-dma.o
obj-$(CONFIG_IIO_BUFFER_DMAENGINE) += industrialio-buffer-dmaengine.o
obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o
obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o

View File

@ -0,0 +1,683 @@
/*
* Copyright 2013-2015 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2.
*/
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/workqueue.h>
#include <linux/mutex.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/iio/buffer.h>
#include <linux/iio/buffer-dma.h>
#include <linux/dma-mapping.h>
#include <linux/sizes.h>
/*
* For DMA buffers the storage is sub-divided into so called blocks. Each block
* has its own memory buffer. The size of the block is the granularity at which
* memory is exchanged between the hardware and the application. Increasing the
* basic unit of data exchange from one sample to one block decreases the
* management overhead that is associated with each sample. E.g. if we say the
* management overhead for one exchange is x and the unit of exchange is one
* sample the overhead will be x for each sample. Whereas when using a block
* which contains n samples the overhead per sample is reduced to x/n. This
* allows to achieve much higher samplerates than what can be sustained with
* the one sample approach.
*
* Blocks are exchanged between the DMA controller and the application via the
* means of two queues. The incoming queue and the outgoing queue. Blocks on the
* incoming queue are waiting for the DMA controller to pick them up and fill
* them with data. Block on the outgoing queue have been filled with data and
* are waiting for the application to dequeue them and read the data.
*
* A block can be in one of the following states:
* * Owned by the application. In this state the application can read data from
* the block.
* * On the incoming list: Blocks on the incoming list are queued up to be
* processed by the DMA controller.
* * Owned by the DMA controller: The DMA controller is processing the block
* and filling it with data.
* * On the outgoing list: Blocks on the outgoing list have been successfully
* processed by the DMA controller and contain data. They can be dequeued by
* the application.
* * Dead: A block that is dead has been marked as to be freed. It might still
* be owned by either the application or the DMA controller at the moment.
* But once they are done processing it instead of going to either the
* incoming or outgoing queue the block will be freed.
*
* In addition to this blocks are reference counted and the memory associated
* with both the block structure as well as the storage memory for the block
* will be freed when the last reference to the block is dropped. This means a
* block must not be accessed without holding a reference.
*
* The iio_dma_buffer implementation provides a generic infrastructure for
* managing the blocks.
*
* A driver for a specific piece of hardware that has DMA capabilities need to
* implement the submit() callback from the iio_dma_buffer_ops structure. This
* callback is supposed to initiate the DMA transfer copying data from the
* converter to the memory region of the block. Once the DMA transfer has been
* completed the driver must call iio_dma_buffer_block_done() for the completed
* block.
*
* Prior to this it must set the bytes_used field of the block contains
* the actual number of bytes in the buffer. Typically this will be equal to the
* size of the block, but if the DMA hardware has certain alignment requirements
* for the transfer length it might choose to use less than the full size. In
* either case it is expected that bytes_used is a multiple of the bytes per
* datum, i.e. the block must not contain partial samples.
*
* The driver must call iio_dma_buffer_block_done() for each block it has
* received through its submit_block() callback, even if it does not actually
* perform a DMA transfer for the block, e.g. because the buffer was disabled
* before the block transfer was started. In this case it should set bytes_used
* to 0.
*
* In addition it is recommended that a driver implements the abort() callback.
* It will be called when the buffer is disabled and can be used to cancel
* pending and stop active transfers.
*
* The specific driver implementation should use the default callback
* implementations provided by this module for the iio_buffer_access_funcs
* struct. It may overload some callbacks with custom variants if the hardware
* has special requirements that are not handled by the generic functions. If a
* driver chooses to overload a callback it has to ensure that the generic
* callback is called from within the custom callback.
*/
static void iio_buffer_block_release(struct kref *kref)
{
struct iio_dma_buffer_block *block = container_of(kref,
struct iio_dma_buffer_block, kref);
WARN_ON(block->state != IIO_BLOCK_STATE_DEAD);
dma_free_coherent(block->queue->dev, PAGE_ALIGN(block->size),
block->vaddr, block->phys_addr);
iio_buffer_put(&block->queue->buffer);
kfree(block);
}
static void iio_buffer_block_get(struct iio_dma_buffer_block *block)
{
kref_get(&block->kref);
}
static void iio_buffer_block_put(struct iio_dma_buffer_block *block)
{
kref_put(&block->kref, iio_buffer_block_release);
}
/*
* dma_free_coherent can sleep, hence we need to take some special care to be
* able to drop a reference from an atomic context.
*/
static LIST_HEAD(iio_dma_buffer_dead_blocks);
static DEFINE_SPINLOCK(iio_dma_buffer_dead_blocks_lock);
static void iio_dma_buffer_cleanup_worker(struct work_struct *work)
{
struct iio_dma_buffer_block *block, *_block;
LIST_HEAD(block_list);
spin_lock_irq(&iio_dma_buffer_dead_blocks_lock);
list_splice_tail_init(&iio_dma_buffer_dead_blocks, &block_list);
spin_unlock_irq(&iio_dma_buffer_dead_blocks_lock);
list_for_each_entry_safe(block, _block, &block_list, head)
iio_buffer_block_release(&block->kref);
}
static DECLARE_WORK(iio_dma_buffer_cleanup_work, iio_dma_buffer_cleanup_worker);
static void iio_buffer_block_release_atomic(struct kref *kref)
{
struct iio_dma_buffer_block *block;
unsigned long flags;
block = container_of(kref, struct iio_dma_buffer_block, kref);
spin_lock_irqsave(&iio_dma_buffer_dead_blocks_lock, flags);
list_add_tail(&block->head, &iio_dma_buffer_dead_blocks);
spin_unlock_irqrestore(&iio_dma_buffer_dead_blocks_lock, flags);
schedule_work(&iio_dma_buffer_cleanup_work);
}
/*
* Version of iio_buffer_block_put() that can be called from atomic context
*/
static void iio_buffer_block_put_atomic(struct iio_dma_buffer_block *block)
{
kref_put(&block->kref, iio_buffer_block_release_atomic);
}
static struct iio_dma_buffer_queue *iio_buffer_to_queue(struct iio_buffer *buf)
{
return container_of(buf, struct iio_dma_buffer_queue, buffer);
}
static struct iio_dma_buffer_block *iio_dma_buffer_alloc_block(
struct iio_dma_buffer_queue *queue, size_t size)
{
struct iio_dma_buffer_block *block;
block = kzalloc(sizeof(*block), GFP_KERNEL);
if (!block)
return NULL;
block->vaddr = dma_alloc_coherent(queue->dev, PAGE_ALIGN(size),
&block->phys_addr, GFP_KERNEL);
if (!block->vaddr) {
kfree(block);
return NULL;
}
block->size = size;
block->state = IIO_BLOCK_STATE_DEQUEUED;
block->queue = queue;
INIT_LIST_HEAD(&block->head);
kref_init(&block->kref);
iio_buffer_get(&queue->buffer);
return block;
}
static void _iio_dma_buffer_block_done(struct iio_dma_buffer_block *block)
{
struct iio_dma_buffer_queue *queue = block->queue;
/*
* The buffer has already been freed by the application, just drop the
* reference.
*/
if (block->state != IIO_BLOCK_STATE_DEAD) {
block->state = IIO_BLOCK_STATE_DONE;
list_add_tail(&block->head, &queue->outgoing);
}
}
/**
* iio_dma_buffer_block_done() - Indicate that a block has been completed
* @block: The completed block
*
* Should be called when the DMA controller has finished handling the block to
* pass back ownership of the block to the queue.
*/
void iio_dma_buffer_block_done(struct iio_dma_buffer_block *block)
{
struct iio_dma_buffer_queue *queue = block->queue;
unsigned long flags;
spin_lock_irqsave(&queue->list_lock, flags);
_iio_dma_buffer_block_done(block);
spin_unlock_irqrestore(&queue->list_lock, flags);
iio_buffer_block_put_atomic(block);
wake_up_interruptible_poll(&queue->buffer.pollq, POLLIN | POLLRDNORM);
}
EXPORT_SYMBOL_GPL(iio_dma_buffer_block_done);
/**
* iio_dma_buffer_block_list_abort() - Indicate that a list block has been
* aborted
* @queue: Queue for which to complete blocks.
* @list: List of aborted blocks. All blocks in this list must be from @queue.
*
* Typically called from the abort() callback after the DMA controller has been
* stopped. This will set bytes_used to 0 for each block in the list and then
* hand the blocks back to the queue.
*/
void iio_dma_buffer_block_list_abort(struct iio_dma_buffer_queue *queue,
struct list_head *list)
{
struct iio_dma_buffer_block *block, *_block;
unsigned long flags;
spin_lock_irqsave(&queue->list_lock, flags);
list_for_each_entry_safe(block, _block, list, head) {
list_del(&block->head);
block->bytes_used = 0;
_iio_dma_buffer_block_done(block);
iio_buffer_block_put_atomic(block);
}
spin_unlock_irqrestore(&queue->list_lock, flags);
wake_up_interruptible_poll(&queue->buffer.pollq, POLLIN | POLLRDNORM);
}
EXPORT_SYMBOL_GPL(iio_dma_buffer_block_list_abort);
static bool iio_dma_block_reusable(struct iio_dma_buffer_block *block)
{
/*
* If the core owns the block it can be re-used. This should be the
* default case when enabling the buffer, unless the DMA controller does
* not support abort and has not given back the block yet.
*/
switch (block->state) {
case IIO_BLOCK_STATE_DEQUEUED:
case IIO_BLOCK_STATE_QUEUED:
case IIO_BLOCK_STATE_DONE:
return true;
default:
return false;
}
}
/**
* iio_dma_buffer_request_update() - DMA buffer request_update callback
* @buffer: The buffer which to request an update
*
* Should be used as the iio_dma_buffer_request_update() callback for
* iio_buffer_access_ops struct for DMA buffers.
*/
int iio_dma_buffer_request_update(struct iio_buffer *buffer)
{
struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buffer);
struct iio_dma_buffer_block *block;
bool try_reuse = false;
size_t size;
int ret = 0;
int i;
/*
* Split the buffer into two even parts. This is used as a double
* buffering scheme with usually one block at a time being used by the
* DMA and the other one by the application.
*/
size = DIV_ROUND_UP(queue->buffer.bytes_per_datum *
queue->buffer.length, 2);
mutex_lock(&queue->lock);
/* Allocations are page aligned */
if (PAGE_ALIGN(queue->fileio.block_size) == PAGE_ALIGN(size))
try_reuse = true;
queue->fileio.block_size = size;
queue->fileio.active_block = NULL;
spin_lock_irq(&queue->list_lock);
for (i = 0; i < 2; i++) {
block = queue->fileio.blocks[i];
/* If we can't re-use it free it */
if (block && (!iio_dma_block_reusable(block) || !try_reuse))
block->state = IIO_BLOCK_STATE_DEAD;
}
/*
* At this point all blocks are either owned by the core or marked as
* dead. This means we can reset the lists without having to fear
* corrution.
*/
INIT_LIST_HEAD(&queue->outgoing);
spin_unlock_irq(&queue->list_lock);
INIT_LIST_HEAD(&queue->incoming);
for (i = 0; i < 2; i++) {
if (queue->fileio.blocks[i]) {
block = queue->fileio.blocks[i];
if (block->state == IIO_BLOCK_STATE_DEAD) {
/* Could not reuse it */
iio_buffer_block_put(block);
block = NULL;
} else {
block->size = size;
}
} else {
block = NULL;
}
if (!block) {
block = iio_dma_buffer_alloc_block(queue, size);
if (!block) {
ret = -ENOMEM;
goto out_unlock;
}
queue->fileio.blocks[i] = block;
}
block->state = IIO_BLOCK_STATE_QUEUED;
list_add_tail(&block->head, &queue->incoming);
}
out_unlock:
mutex_unlock(&queue->lock);
return ret;
}
EXPORT_SYMBOL_GPL(iio_dma_buffer_request_update);
static void iio_dma_buffer_submit_block(struct iio_dma_buffer_queue *queue,
struct iio_dma_buffer_block *block)
{
int ret;
/*
* If the hardware has already been removed we put the block into
* limbo. It will neither be on the incoming nor outgoing list, nor will
* it ever complete. It will just wait to be freed eventually.
*/
if (!queue->ops)
return;
block->state = IIO_BLOCK_STATE_ACTIVE;
iio_buffer_block_get(block);
ret = queue->ops->submit(queue, block);
if (ret) {
/*
* This is a bit of a problem and there is not much we can do
* other then wait for the buffer to be disabled and re-enabled
* and try again. But it should not really happen unless we run
* out of memory or something similar.
*
* TODO: Implement support in the IIO core to allow buffers to
* notify consumers that something went wrong and the buffer
* should be disabled.
*/
iio_buffer_block_put(block);
}
}
/**
* iio_dma_buffer_enable() - Enable DMA buffer
* @buffer: IIO buffer to enable
* @indio_dev: IIO device the buffer is attached to
*
* Needs to be called when the device that the buffer is attached to starts
* sampling. Typically should be the iio_buffer_access_ops enable callback.
*
* This will allocate the DMA buffers and start the DMA transfers.
*/
int iio_dma_buffer_enable(struct iio_buffer *buffer,
struct iio_dev *indio_dev)
{
struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buffer);
struct iio_dma_buffer_block *block, *_block;
mutex_lock(&queue->lock);
queue->active = true;
list_for_each_entry_safe(block, _block, &queue->incoming, head) {
list_del(&block->head);
iio_dma_buffer_submit_block(queue, block);
}
mutex_unlock(&queue->lock);
return 0;
}
EXPORT_SYMBOL_GPL(iio_dma_buffer_enable);
/**
* iio_dma_buffer_disable() - Disable DMA buffer
* @buffer: IIO DMA buffer to disable
* @indio_dev: IIO device the buffer is attached to
*
* Needs to be called when the device that the buffer is attached to stops
* sampling. Typically should be the iio_buffer_access_ops disable callback.
*/
int iio_dma_buffer_disable(struct iio_buffer *buffer,
struct iio_dev *indio_dev)
{
struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buffer);
mutex_lock(&queue->lock);
queue->active = false;
if (queue->ops && queue->ops->abort)
queue->ops->abort(queue);
mutex_unlock(&queue->lock);
return 0;
}
EXPORT_SYMBOL_GPL(iio_dma_buffer_disable);
static void iio_dma_buffer_enqueue(struct iio_dma_buffer_queue *queue,
struct iio_dma_buffer_block *block)
{
if (block->state == IIO_BLOCK_STATE_DEAD) {
iio_buffer_block_put(block);
} else if (queue->active) {
iio_dma_buffer_submit_block(queue, block);
} else {
block->state = IIO_BLOCK_STATE_QUEUED;
list_add_tail(&block->head, &queue->incoming);
}
}
static struct iio_dma_buffer_block *iio_dma_buffer_dequeue(
struct iio_dma_buffer_queue *queue)
{
struct iio_dma_buffer_block *block;
spin_lock_irq(&queue->list_lock);
block = list_first_entry_or_null(&queue->outgoing, struct
iio_dma_buffer_block, head);
if (block != NULL) {
list_del(&block->head);
block->state = IIO_BLOCK_STATE_DEQUEUED;
}
spin_unlock_irq(&queue->list_lock);
return block;
}
/**
* iio_dma_buffer_read() - DMA buffer read callback
* @buffer: Buffer to read form
* @n: Number of bytes to read
* @user_buffer: Userspace buffer to copy the data to
*
* Should be used as the read_first_n callback for iio_buffer_access_ops
* struct for DMA buffers.
*/
int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
char __user *user_buffer)
{
struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buffer);
struct iio_dma_buffer_block *block;
int ret;
if (n < buffer->bytes_per_datum)
return -EINVAL;
mutex_lock(&queue->lock);
if (!queue->fileio.active_block) {
block = iio_dma_buffer_dequeue(queue);
if (block == NULL) {
ret = 0;
goto out_unlock;
}
queue->fileio.pos = 0;
queue->fileio.active_block = block;
} else {
block = queue->fileio.active_block;
}
n = rounddown(n, buffer->bytes_per_datum);
if (n > block->bytes_used - queue->fileio.pos)
n = block->bytes_used - queue->fileio.pos;
if (copy_to_user(user_buffer, block->vaddr + queue->fileio.pos, n)) {
ret = -EFAULT;
goto out_unlock;
}
queue->fileio.pos += n;
if (queue->fileio.pos == block->bytes_used) {
queue->fileio.active_block = NULL;
iio_dma_buffer_enqueue(queue, block);
}
ret = n;
out_unlock:
mutex_unlock(&queue->lock);
return ret;
}
EXPORT_SYMBOL_GPL(iio_dma_buffer_read);
/**
* iio_dma_buffer_data_available() - DMA buffer data_available callback
* @buf: Buffer to check for data availability
*
* Should be used as the data_available callback for iio_buffer_access_ops
* struct for DMA buffers.
*/
size_t iio_dma_buffer_data_available(struct iio_buffer *buf)
{
struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buf);
struct iio_dma_buffer_block *block;
size_t data_available = 0;
/*
* For counting the available bytes we'll use the size of the block not
* the number of actual bytes available in the block. Otherwise it is
* possible that we end up with a value that is lower than the watermark
* but won't increase since all blocks are in use.
*/
mutex_lock(&queue->lock);
if (queue->fileio.active_block)
data_available += queue->fileio.active_block->size;
spin_lock_irq(&queue->list_lock);
list_for_each_entry(block, &queue->outgoing, head)
data_available += block->size;
spin_unlock_irq(&queue->list_lock);
mutex_unlock(&queue->lock);
return data_available;
}
EXPORT_SYMBOL_GPL(iio_dma_buffer_data_available);
/**
* iio_dma_buffer_set_bytes_per_datum() - DMA buffer set_bytes_per_datum callback
* @buffer: Buffer to set the bytes-per-datum for
* @bpd: The new bytes-per-datum value
*
* Should be used as the set_bytes_per_datum callback for iio_buffer_access_ops
* struct for DMA buffers.
*/
int iio_dma_buffer_set_bytes_per_datum(struct iio_buffer *buffer, size_t bpd)
{
buffer->bytes_per_datum = bpd;
return 0;
}
EXPORT_SYMBOL_GPL(iio_dma_buffer_set_bytes_per_datum);
/**
* iio_dma_buffer_set_length - DMA buffer set_length callback
* @buffer: Buffer to set the length for
* @length: The new buffer length
*
* Should be used as the set_length callback for iio_buffer_access_ops
* struct for DMA buffers.
*/
int iio_dma_buffer_set_length(struct iio_buffer *buffer, int length)
{
/* Avoid an invalid state */
if (length < 2)
length = 2;
buffer->length = length;
buffer->watermark = length / 2;
return 0;
}
EXPORT_SYMBOL_GPL(iio_dma_buffer_set_length);
/**
* iio_dma_buffer_init() - Initialize DMA buffer queue
* @queue: Buffer to initialize
* @dev: DMA device
* @ops: DMA buffer queue callback operations
*
* The DMA device will be used by the queue to do DMA memory allocations. So it
* should refer to the device that will perform the DMA to ensure that
* allocations are done from a memory region that can be accessed by the device.
*/
int iio_dma_buffer_init(struct iio_dma_buffer_queue *queue,
struct device *dev, const struct iio_dma_buffer_ops *ops)
{
iio_buffer_init(&queue->buffer);
queue->buffer.length = PAGE_SIZE;
queue->buffer.watermark = queue->buffer.length / 2;
queue->dev = dev;
queue->ops = ops;
INIT_LIST_HEAD(&queue->incoming);
INIT_LIST_HEAD(&queue->outgoing);
mutex_init(&queue->lock);
spin_lock_init(&queue->list_lock);
return 0;
}
EXPORT_SYMBOL_GPL(iio_dma_buffer_init);
/**
* iio_dma_buffer_exit() - Cleanup DMA buffer queue
* @queue: Buffer to cleanup
*
* After this function has completed it is safe to free any resources that are
* associated with the buffer and are accessed inside the callback operations.
*/
void iio_dma_buffer_exit(struct iio_dma_buffer_queue *queue)
{
unsigned int i;
mutex_lock(&queue->lock);
spin_lock_irq(&queue->list_lock);
for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) {
if (!queue->fileio.blocks[i])
continue;
queue->fileio.blocks[i]->state = IIO_BLOCK_STATE_DEAD;
}
INIT_LIST_HEAD(&queue->outgoing);
spin_unlock_irq(&queue->list_lock);
INIT_LIST_HEAD(&queue->incoming);
for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) {
if (!queue->fileio.blocks[i])
continue;
iio_buffer_block_put(queue->fileio.blocks[i]);
queue->fileio.blocks[i] = NULL;
}
queue->fileio.active_block = NULL;
queue->ops = NULL;
mutex_unlock(&queue->lock);
}
EXPORT_SYMBOL_GPL(iio_dma_buffer_exit);
/**
* iio_dma_buffer_release() - Release final buffer resources
* @queue: Buffer to release
*
* Frees resources that can't yet be freed in iio_dma_buffer_exit(). Should be
* called in the buffers release callback implementation right before freeing
* the memory associated with the buffer.
*/
void iio_dma_buffer_release(struct iio_dma_buffer_queue *queue)
{
mutex_destroy(&queue->lock);
}
EXPORT_SYMBOL_GPL(iio_dma_buffer_release);
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("DMA buffer for the IIO framework");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,213 @@
/*
* Copyright 2014-2015 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2 or later.
*/
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/spinlock.h>
#include <linux/err.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/buffer-dma.h>
#include <linux/iio/buffer-dmaengine.h>
/*
* The IIO DMAengine buffer combines the generic IIO DMA buffer infrastructure
* with the DMAengine framework. The generic IIO DMA buffer infrastructure is
* used to manage the buffer memory and implement the IIO buffer operations
* while the DMAengine framework is used to perform the DMA transfers. Combined
* this results in a device independent fully functional DMA buffer
* implementation that can be used by device drivers for peripherals which are
* connected to a DMA controller which has a DMAengine driver implementation.
*/
struct dmaengine_buffer {
struct iio_dma_buffer_queue queue;
struct dma_chan *chan;
struct list_head active;
size_t align;
size_t max_size;
};
static struct dmaengine_buffer *iio_buffer_to_dmaengine_buffer(
struct iio_buffer *buffer)
{
return container_of(buffer, struct dmaengine_buffer, queue.buffer);
}
static void iio_dmaengine_buffer_block_done(void *data)
{
struct iio_dma_buffer_block *block = data;
unsigned long flags;
spin_lock_irqsave(&block->queue->list_lock, flags);
list_del(&block->head);
spin_unlock_irqrestore(&block->queue->list_lock, flags);
iio_dma_buffer_block_done(block);
}
static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue,
struct iio_dma_buffer_block *block)
{
struct dmaengine_buffer *dmaengine_buffer =
iio_buffer_to_dmaengine_buffer(&queue->buffer);
struct dma_async_tx_descriptor *desc;
dma_cookie_t cookie;
block->bytes_used = min(block->size, dmaengine_buffer->max_size);
block->bytes_used = rounddown(block->bytes_used,
dmaengine_buffer->align);
desc = dmaengine_prep_slave_single(dmaengine_buffer->chan,
block->phys_addr, block->bytes_used, DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT);
if (!desc)
return -ENOMEM;
desc->callback = iio_dmaengine_buffer_block_done;
desc->callback_param = block;
cookie = dmaengine_submit(desc);
if (dma_submit_error(cookie))
return dma_submit_error(cookie);
spin_lock_irq(&dmaengine_buffer->queue.list_lock);
list_add_tail(&block->head, &dmaengine_buffer->active);
spin_unlock_irq(&dmaengine_buffer->queue.list_lock);
dma_async_issue_pending(dmaengine_buffer->chan);
return 0;
}
static void iio_dmaengine_buffer_abort(struct iio_dma_buffer_queue *queue)
{
struct dmaengine_buffer *dmaengine_buffer =
iio_buffer_to_dmaengine_buffer(&queue->buffer);
dmaengine_terminate_all(dmaengine_buffer->chan);
/* FIXME: There is a slight chance of a race condition here.
* dmaengine_terminate_all() does not guarantee that all transfer
* callbacks have finished running. Need to introduce a
* dmaengine_terminate_all_sync().
*/
iio_dma_buffer_block_list_abort(queue, &dmaengine_buffer->active);
}
static void iio_dmaengine_buffer_release(struct iio_buffer *buf)
{
struct dmaengine_buffer *dmaengine_buffer =
iio_buffer_to_dmaengine_buffer(buf);
iio_dma_buffer_release(&dmaengine_buffer->queue);
kfree(dmaengine_buffer);
}
static const struct iio_buffer_access_funcs iio_dmaengine_buffer_ops = {
.read_first_n = iio_dma_buffer_read,
.set_bytes_per_datum = iio_dma_buffer_set_bytes_per_datum,
.set_length = iio_dma_buffer_set_length,
.request_update = iio_dma_buffer_request_update,
.enable = iio_dma_buffer_enable,
.disable = iio_dma_buffer_disable,
.data_available = iio_dma_buffer_data_available,
.release = iio_dmaengine_buffer_release,
.modes = INDIO_BUFFER_HARDWARE,
.flags = INDIO_BUFFER_FLAG_FIXED_WATERMARK,
};
static const struct iio_dma_buffer_ops iio_dmaengine_default_ops = {
.submit = iio_dmaengine_buffer_submit_block,
.abort = iio_dmaengine_buffer_abort,
};
/**
* iio_dmaengine_buffer_alloc() - Allocate new buffer which uses DMAengine
* @dev: Parent device for the buffer
* @channel: DMA channel name, typically "rx".
*
* This allocates a new IIO buffer which internally uses the DMAengine framework
* to perform its transfers. The parent device will be used to request the DMA
* channel.
*
* Once done using the buffer iio_dmaengine_buffer_free() should be used to
* release it.
*/
struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev,
const char *channel)
{
struct dmaengine_buffer *dmaengine_buffer;
unsigned int width, src_width, dest_width;
struct dma_slave_caps caps;
struct dma_chan *chan;
int ret;
dmaengine_buffer = kzalloc(sizeof(*dmaengine_buffer), GFP_KERNEL);
if (!dmaengine_buffer)
return ERR_PTR(-ENOMEM);
chan = dma_request_slave_channel_reason(dev, channel);
if (IS_ERR(chan)) {
ret = PTR_ERR(chan);
goto err_free;
}
ret = dma_get_slave_caps(chan, &caps);
if (ret < 0)
goto err_free;
/* Needs to be aligned to the maximum of the minimums */
if (caps.src_addr_widths)
src_width = __ffs(caps.src_addr_widths);
else
src_width = 1;
if (caps.dst_addr_widths)
dest_width = __ffs(caps.dst_addr_widths);
else
dest_width = 1;
width = max(src_width, dest_width);
INIT_LIST_HEAD(&dmaengine_buffer->active);
dmaengine_buffer->chan = chan;
dmaengine_buffer->align = width;
dmaengine_buffer->max_size = dma_get_max_seg_size(chan->device->dev);
iio_dma_buffer_init(&dmaengine_buffer->queue, chan->device->dev,
&iio_dmaengine_default_ops);
dmaengine_buffer->queue.buffer.access = &iio_dmaengine_buffer_ops;
return &dmaengine_buffer->queue.buffer;
err_free:
kfree(dmaengine_buffer);
return ERR_PTR(ret);
}
EXPORT_SYMBOL(iio_dmaengine_buffer_alloc);
/**
* iio_dmaengine_buffer_free() - Free dmaengine buffer
* @buffer: Buffer to free
*
* Frees a buffer previously allocated with iio_dmaengine_buffer_alloc().
*/
void iio_dmaengine_buffer_free(struct iio_buffer *buffer)
{
struct dmaengine_buffer *dmaengine_buffer =
iio_buffer_to_dmaengine_buffer(buffer);
iio_dma_buffer_exit(&dmaengine_buffer->queue);
dma_release_channel(dmaengine_buffer->chan);
iio_buffer_put(buffer);
}
EXPORT_SYMBOL_GPL(iio_dmaengine_buffer_free);

View File

@ -4,6 +4,14 @@
menu "Chemical Sensors"
config IAQCORE
tristate "AMS iAQ-Core VOC sensors"
depends on I2C
help
Say Y here to build I2C interface support for the AMS
iAQ-Core Continuous/Pulsed VOC (Volatile Organic Compounds)
sensors
config VZ89X
tristate "SGX Sensortech MiCS VZ89X VOC sensor"
depends on I2C

View File

@ -3,4 +3,5 @@
#
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_IAQCORE) += ams-iaq-core.o
obj-$(CONFIG_VZ89X) += vz89x.o

View File

@ -0,0 +1,200 @@
/*
* ams-iaq-core.c - Support for AMS iAQ-Core VOC sensors
*
* Copyright (C) 2015 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.
*
*/
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
#define AMS_IAQCORE_DATA_SIZE 9
#define AMS_IAQCORE_VOC_CO2_IDX 0
#define AMS_IAQCORE_VOC_RESISTANCE_IDX 1
#define AMS_IAQCORE_VOC_TVOC_IDX 2
struct ams_iaqcore_reading {
__be16 co2_ppm;
u8 status;
__be32 resistance;
__be16 voc_ppb;
} __attribute__((__packed__));
struct ams_iaqcore_data {
struct i2c_client *client;
struct mutex lock;
unsigned long last_update;
struct ams_iaqcore_reading buffer;
};
static const struct iio_chan_spec ams_iaqcore_channels[] = {
{
.type = IIO_CONCENTRATION,
.channel2 = IIO_MOD_CO2,
.modified = 1,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
.address = AMS_IAQCORE_VOC_CO2_IDX,
},
{
.type = IIO_RESISTANCE,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
.address = AMS_IAQCORE_VOC_RESISTANCE_IDX,
},
{
.type = IIO_CONCENTRATION,
.channel2 = IIO_MOD_VOC,
.modified = 1,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
.address = AMS_IAQCORE_VOC_TVOC_IDX,
},
};
static int ams_iaqcore_read_measurement(struct ams_iaqcore_data *data)
{
struct i2c_client *client = data->client;
int ret;
struct i2c_msg msg = {
.addr = client->addr,
.flags = client->flags | I2C_M_RD,
.len = AMS_IAQCORE_DATA_SIZE,
.buf = (char *) &data->buffer,
};
ret = i2c_transfer(client->adapter, &msg, 1);
return (ret == AMS_IAQCORE_DATA_SIZE) ? 0 : ret;
}
static int ams_iaqcore_get_measurement(struct ams_iaqcore_data *data)
{
int ret;
/* sensor can only be polled once a second max per datasheet */
if (!time_after(jiffies, data->last_update + HZ))
return 0;
ret = ams_iaqcore_read_measurement(data);
if (ret < 0)
return ret;
data->last_update = jiffies;
return 0;
}
static int ams_iaqcore_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
struct ams_iaqcore_data *data = iio_priv(indio_dev);
int ret;
if (mask != IIO_CHAN_INFO_PROCESSED)
return -EINVAL;
mutex_lock(&data->lock);
ret = ams_iaqcore_get_measurement(data);
if (ret)
goto err_out;
switch (chan->address) {
case AMS_IAQCORE_VOC_CO2_IDX:
*val = 0;
*val2 = be16_to_cpu(data->buffer.co2_ppm);
ret = IIO_VAL_INT_PLUS_MICRO;
break;
case AMS_IAQCORE_VOC_RESISTANCE_IDX:
*val = be32_to_cpu(data->buffer.resistance);
ret = IIO_VAL_INT;
break;
case AMS_IAQCORE_VOC_TVOC_IDX:
*val = 0;
*val2 = be16_to_cpu(data->buffer.voc_ppb);
ret = IIO_VAL_INT_PLUS_NANO;
break;
default:
ret = -EINVAL;
}
err_out:
mutex_unlock(&data->lock);
return ret;
}
static const struct iio_info ams_iaqcore_info = {
.read_raw = ams_iaqcore_read_raw,
.driver_module = THIS_MODULE,
};
static int ams_iaqcore_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct iio_dev *indio_dev;
struct ams_iaqcore_data *data;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
data->client = client;
/* so initial reading will complete */
data->last_update = jiffies - HZ;
mutex_init(&data->lock);
indio_dev->dev.parent = &client->dev;
indio_dev->info = &ams_iaqcore_info,
indio_dev->name = dev_name(&client->dev);
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = ams_iaqcore_channels;
indio_dev->num_channels = ARRAY_SIZE(ams_iaqcore_channels);
return devm_iio_device_register(&client->dev, indio_dev);
}
static const struct i2c_device_id ams_iaqcore_id[] = {
{ "ams-iaq-core", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ams_iaqcore_id);
static const struct of_device_id ams_iaqcore_dt_ids[] = {
{ .compatible = "ams,iaq-core" },
{ }
};
MODULE_DEVICE_TABLE(of, ams_iaqcore_dt_ids);
static struct i2c_driver ams_iaqcore_driver = {
.driver = {
.name = "ams-iaq-core",
.of_match_table = of_match_ptr(ams_iaqcore_dt_ids),
},
.probe = ams_iaqcore_probe,
.id_table = ams_iaqcore_id,
};
module_i2c_driver(ams_iaqcore_driver);
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
MODULE_DESCRIPTION("AMS iAQ-Core VOC sensors");
MODULE_LICENSE("GPL v2");

View File

@ -34,8 +34,9 @@
struct vz89x_data {
struct i2c_client *client;
struct mutex lock;
unsigned long last_update;
int (*xfer)(struct vz89x_data *data, u8 cmd);
unsigned long last_update;
u8 buffer[VZ89X_REG_MEASUREMENT_SIZE];
};
@ -100,27 +101,60 @@ static int vz89x_measurement_is_valid(struct vz89x_data *data)
return !!(data->buffer[VZ89X_REG_MEASUREMENT_SIZE - 1] > 0);
}
static int vz89x_i2c_xfer(struct vz89x_data *data, u8 cmd)
{
struct i2c_client *client = data->client;
struct i2c_msg msg[2];
int ret;
u8 buf[3] = { cmd, 0, 0};
msg[0].addr = client->addr;
msg[0].flags = client->flags;
msg[0].len = 3;
msg[0].buf = (char *) &buf;
msg[1].addr = client->addr;
msg[1].flags = client->flags | I2C_M_RD;
msg[1].len = VZ89X_REG_MEASUREMENT_SIZE;
msg[1].buf = (char *) &data->buffer;
ret = i2c_transfer(client->adapter, msg, 2);
return (ret == 2) ? 0 : ret;
}
static int vz89x_smbus_xfer(struct vz89x_data *data, u8 cmd)
{
struct i2c_client *client = data->client;
int ret;
int i;
ret = i2c_smbus_write_word_data(client, cmd, 0);
if (ret < 0)
return ret;
for (i = 0; i < VZ89X_REG_MEASUREMENT_SIZE; i++) {
ret = i2c_smbus_read_byte(client);
if (ret < 0)
return ret;
data->buffer[i] = ret;
}
return 0;
}
static int vz89x_get_measurement(struct vz89x_data *data)
{
int ret;
int i;
/* sensor can only be polled once a second max per datasheet */
if (!time_after(jiffies, data->last_update + HZ))
return 0;
ret = i2c_smbus_write_word_data(data->client,
VZ89X_REG_MEASUREMENT, 0);
ret = data->xfer(data, VZ89X_REG_MEASUREMENT);
if (ret < 0)
return ret;
for (i = 0; i < VZ89X_REG_MEASUREMENT_SIZE; i++) {
ret = i2c_smbus_read_byte(data->client);
if (ret < 0)
return ret;
data->buffer[i] = ret;
}
ret = vz89x_measurement_is_valid(data);
if (ret)
return -EAGAIN;
@ -204,15 +238,19 @@ static int vz89x_probe(struct i2c_client *client,
struct iio_dev *indio_dev;
struct vz89x_data *data;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BYTE))
return -ENODEV;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
data->xfer = vz89x_i2c_xfer;
else if (i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE))
data->xfer = vz89x_smbus_xfer;
else
return -ENOTSUPP;
i2c_set_clientdata(client, indio_dev);
data->client = client;
data->last_update = jiffies - HZ;

View File

@ -18,9 +18,6 @@
#include <asm/unaligned.h>
#include <linux/iio/common/st_sensors.h>
#define ST_SENSORS_WAI_ADDRESS 0x0f
static inline u32 st_sensors_get_unaligned_le24(const u8 *p)
{
return (s32)((p[0] | p[1] << 8 | p[2] << 16) << 8) >> 8;

View File

@ -0,0 +1,36 @@
#
# Industrial I/O subsystem Dummy Driver configuration
#
menu "IIO dummy driver"
depends on IIO
config IIO_DUMMY_EVGEN
select IRQ_WORK
tristate
config IIO_SIMPLE_DUMMY
tristate "An example driver with no hardware requirements"
help
Driver intended mainly as documentation for how to write
a driver. May also be useful for testing userspace code
without hardware.
if IIO_SIMPLE_DUMMY
config IIO_SIMPLE_DUMMY_EVENTS
bool "Event generation support"
select IIO_DUMMY_EVGEN
help
Add some dummy events to the simple dummy driver.
config IIO_SIMPLE_DUMMY_BUFFER
bool "Buffered capture support"
select IIO_BUFFER
select IIO_TRIGGER
select IIO_KFIFO_BUF
help
Add buffered data capture to the simple dummy driver.
endif # IIO_SIMPLE_DUMMY
endmenu

View File

@ -0,0 +1,10 @@
#
# Makefile for the IIO Dummy Driver
#
obj-$(CONFIG_IIO_SIMPLE_DUMMY) += iio_dummy.o
iio_dummy-y := iio_simple_dummy.o
iio_dummy-$(CONFIG_IIO_SIMPLE_DUMMY_EVENTS) += iio_simple_dummy_events.o
iio_dummy-$(CONFIG_IIO_SIMPLE_DUMMY_BUFFER) += iio_simple_dummy_buffer.o
obj-$(CONFIG_IIO_DUMMY_EVGEN) += iio_dummy_evgen.o

View File

@ -435,7 +435,9 @@ static int adis16136_initial_setup(struct iio_dev *indio_dev)
if (ret)
return ret;
sscanf(indio_dev->name, "adis%u\n", &device_id);
ret = sscanf(indio_dev->name, "adis%u\n", &device_id);
if (ret != 1)
return -EINVAL;
if (prod_id != device_id)
dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.",

View File

@ -1077,25 +1077,23 @@ int bmg160_core_probe(struct device *dev, struct regmap *regmap, int irq,
goto err_trigger_unregister;
}
ret = iio_device_register(indio_dev);
if (ret < 0) {
dev_err(dev, "unable to register iio device\n");
goto err_buffer_cleanup;
}
ret = pm_runtime_set_active(dev);
if (ret)
goto err_iio_unregister;
goto err_buffer_cleanup;
pm_runtime_enable(dev);
pm_runtime_set_autosuspend_delay(dev,
BMG160_AUTO_SUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(dev);
ret = iio_device_register(indio_dev);
if (ret < 0) {
dev_err(dev, "unable to register iio device\n");
goto err_buffer_cleanup;
}
return 0;
err_iio_unregister:
iio_device_unregister(indio_dev);
err_buffer_cleanup:
iio_triggered_buffer_cleanup(indio_dev);
err_trigger_unregister:
@ -1113,11 +1111,12 @@ void bmg160_core_remove(struct device *dev)
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct bmg160_data *data = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
pm_runtime_disable(dev);
pm_runtime_set_suspended(dev);
pm_runtime_put_noidle(dev);
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
if (data->dready_trig) {

View File

@ -0,0 +1,21 @@
#
# Health sensors
#
# When adding new entries keep the list in alphabetical order
menu "Health sensors"
config MAX30100
tristate "MAX30100 heart rate and pulse oximeter sensor"
depends on I2C
select REGMAP_I2C
select IIO_BUFFER
select IIO_KFIFO_BUF
help
Say Y here to build I2C interface support for the Maxim
MAX30100 heart rate, and pulse oximeter sensor.
To compile this driver as a module, choose M here: the
module will be called max30100.
endmenu

View File

@ -0,0 +1,7 @@
#
# Makefile for IIO Health sensors
#
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_MAX30100) += max30100.o

View File

@ -0,0 +1,453 @@
/*
* max30100.c - Support for MAX30100 heart rate and pulse oximeter sensor
*
* Copyright (C) 2015 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: allow LED current and pulse length controls via device tree properties
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/irq.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/kfifo_buf.h>
#define MAX30100_REGMAP_NAME "max30100_regmap"
#define MAX30100_DRV_NAME "max30100"
#define MAX30100_REG_INT_STATUS 0x00
#define MAX30100_REG_INT_STATUS_PWR_RDY BIT(0)
#define MAX30100_REG_INT_STATUS_SPO2_RDY BIT(4)
#define MAX30100_REG_INT_STATUS_HR_RDY BIT(5)
#define MAX30100_REG_INT_STATUS_FIFO_RDY BIT(7)
#define MAX30100_REG_INT_ENABLE 0x01
#define MAX30100_REG_INT_ENABLE_SPO2_EN BIT(0)
#define MAX30100_REG_INT_ENABLE_HR_EN BIT(1)
#define MAX30100_REG_INT_ENABLE_FIFO_EN BIT(3)
#define MAX30100_REG_INT_ENABLE_MASK 0xf0
#define MAX30100_REG_INT_ENABLE_MASK_SHIFT 4
#define MAX30100_REG_FIFO_WR_PTR 0x02
#define MAX30100_REG_FIFO_OVR_CTR 0x03
#define MAX30100_REG_FIFO_RD_PTR 0x04
#define MAX30100_REG_FIFO_DATA 0x05
#define MAX30100_REG_FIFO_DATA_ENTRY_COUNT 16
#define MAX30100_REG_FIFO_DATA_ENTRY_LEN 4
#define MAX30100_REG_MODE_CONFIG 0x06
#define MAX30100_REG_MODE_CONFIG_MODE_SPO2_EN BIT(0)
#define MAX30100_REG_MODE_CONFIG_MODE_HR_EN BIT(1)
#define MAX30100_REG_MODE_CONFIG_MODE_MASK 0x03
#define MAX30100_REG_MODE_CONFIG_TEMP_EN BIT(3)
#define MAX30100_REG_MODE_CONFIG_PWR BIT(7)
#define MAX30100_REG_SPO2_CONFIG 0x07
#define MAX30100_REG_SPO2_CONFIG_100HZ BIT(2)
#define MAX30100_REG_SPO2_CONFIG_HI_RES_EN BIT(6)
#define MAX30100_REG_SPO2_CONFIG_1600US 0x3
#define MAX30100_REG_LED_CONFIG 0x09
#define MAX30100_REG_LED_CONFIG_RED_LED_SHIFT 4
#define MAX30100_REG_LED_CONFIG_24MA 0x07
#define MAX30100_REG_LED_CONFIG_50MA 0x0f
#define MAX30100_REG_TEMP_INTEGER 0x16
#define MAX30100_REG_TEMP_FRACTION 0x17
struct max30100_data {
struct i2c_client *client;
struct iio_dev *indio_dev;
struct mutex lock;
struct regmap *regmap;
__be16 buffer[2]; /* 2 16-bit channels */
};
static bool max30100_is_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case MAX30100_REG_INT_STATUS:
case MAX30100_REG_MODE_CONFIG:
case MAX30100_REG_FIFO_WR_PTR:
case MAX30100_REG_FIFO_OVR_CTR:
case MAX30100_REG_FIFO_RD_PTR:
case MAX30100_REG_FIFO_DATA:
case MAX30100_REG_TEMP_INTEGER:
case MAX30100_REG_TEMP_FRACTION:
return true;
default:
return false;
}
}
static const struct regmap_config max30100_regmap_config = {
.name = MAX30100_REGMAP_NAME,
.reg_bits = 8,
.val_bits = 8,
.max_register = MAX30100_REG_TEMP_FRACTION,
.cache_type = REGCACHE_FLAT,
.volatile_reg = max30100_is_volatile_reg,
};
static const unsigned long max30100_scan_masks[] = {0x3, 0};
static const struct iio_chan_spec max30100_channels[] = {
{
.type = IIO_INTENSITY,
.channel2 = IIO_MOD_LIGHT_IR,
.modified = 1,
.scan_index = 0,
.scan_type = {
.sign = 'u',
.realbits = 16,
.storagebits = 16,
.endianness = IIO_BE,
},
},
{
.type = IIO_INTENSITY,
.channel2 = IIO_MOD_LIGHT_RED,
.modified = 1,
.scan_index = 1,
.scan_type = {
.sign = 'u',
.realbits = 16,
.storagebits = 16,
.endianness = IIO_BE,
},
},
{
.type = IIO_TEMP,
.info_mask_separate =
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
.scan_index = -1,
},
};
static int max30100_set_powermode(struct max30100_data *data, bool state)
{
return regmap_update_bits(data->regmap, MAX30100_REG_MODE_CONFIG,
MAX30100_REG_MODE_CONFIG_PWR,
state ? 0 : MAX30100_REG_MODE_CONFIG_PWR);
}
static int max30100_clear_fifo(struct max30100_data *data)
{
int ret;
ret = regmap_write(data->regmap, MAX30100_REG_FIFO_WR_PTR, 0);
if (ret)
return ret;
ret = regmap_write(data->regmap, MAX30100_REG_FIFO_OVR_CTR, 0);
if (ret)
return ret;
return regmap_write(data->regmap, MAX30100_REG_FIFO_RD_PTR, 0);
}
static int max30100_buffer_postenable(struct iio_dev *indio_dev)
{
struct max30100_data *data = iio_priv(indio_dev);
int ret;
ret = max30100_set_powermode(data, true);
if (ret)
return ret;
return max30100_clear_fifo(data);
}
static int max30100_buffer_predisable(struct iio_dev *indio_dev)
{
struct max30100_data *data = iio_priv(indio_dev);
return max30100_set_powermode(data, false);
}
static const struct iio_buffer_setup_ops max30100_buffer_setup_ops = {
.postenable = max30100_buffer_postenable,
.predisable = max30100_buffer_predisable,
};
static inline int max30100_fifo_count(struct max30100_data *data)
{
unsigned int val;
int ret;
ret = regmap_read(data->regmap, MAX30100_REG_INT_STATUS, &val);
if (ret)
return ret;
/* FIFO is almost full */
if (val & MAX30100_REG_INT_STATUS_FIFO_RDY)
return MAX30100_REG_FIFO_DATA_ENTRY_COUNT - 1;
return 0;
}
static int max30100_read_measurement(struct max30100_data *data)
{
int ret;
ret = i2c_smbus_read_i2c_block_data(data->client,
MAX30100_REG_FIFO_DATA,
MAX30100_REG_FIFO_DATA_ENTRY_LEN,
(u8 *) &data->buffer);
return (ret == MAX30100_REG_FIFO_DATA_ENTRY_LEN) ? 0 : ret;
}
static irqreturn_t max30100_interrupt_handler(int irq, void *private)
{
struct iio_dev *indio_dev = private;
struct max30100_data *data = iio_priv(indio_dev);
int ret, cnt = 0;
mutex_lock(&data->lock);
while (cnt-- || (cnt = max30100_fifo_count(data) > 0)) {
ret = max30100_read_measurement(data);
if (ret)
break;
iio_push_to_buffers(data->indio_dev, data->buffer);
}
mutex_unlock(&data->lock);
return IRQ_HANDLED;
}
static int max30100_chip_init(struct max30100_data *data)
{
int ret;
/* RED IR LED = 24mA, IR LED = 50mA */
ret = regmap_write(data->regmap, MAX30100_REG_LED_CONFIG,
(MAX30100_REG_LED_CONFIG_24MA <<
MAX30100_REG_LED_CONFIG_RED_LED_SHIFT) |
MAX30100_REG_LED_CONFIG_50MA);
if (ret)
return ret;
/* enable hi-res SPO2 readings at 100Hz */
ret = regmap_write(data->regmap, MAX30100_REG_SPO2_CONFIG,
MAX30100_REG_SPO2_CONFIG_HI_RES_EN |
MAX30100_REG_SPO2_CONFIG_100HZ);
if (ret)
return ret;
/* enable SPO2 mode */
ret = regmap_update_bits(data->regmap, MAX30100_REG_MODE_CONFIG,
MAX30100_REG_MODE_CONFIG_MODE_MASK,
MAX30100_REG_MODE_CONFIG_MODE_HR_EN |
MAX30100_REG_MODE_CONFIG_MODE_SPO2_EN);
if (ret)
return ret;
/* enable FIFO interrupt */
return regmap_update_bits(data->regmap, MAX30100_REG_INT_ENABLE,
MAX30100_REG_INT_ENABLE_MASK,
MAX30100_REG_INT_ENABLE_FIFO_EN
<< MAX30100_REG_INT_ENABLE_MASK_SHIFT);
}
static int max30100_read_temp(struct max30100_data *data, int *val)
{
int ret;
unsigned int reg;
ret = regmap_read(data->regmap, MAX30100_REG_TEMP_INTEGER, &reg);
if (ret < 0)
return ret;
*val = reg << 4;
ret = regmap_read(data->regmap, MAX30100_REG_TEMP_FRACTION, &reg);
if (ret < 0)
return ret;
*val |= reg & 0xf;
*val = sign_extend32(*val, 11);
return 0;
}
static int max30100_get_temp(struct max30100_data *data, int *val)
{
int ret;
/* start acquisition */
ret = regmap_update_bits(data->regmap, MAX30100_REG_MODE_CONFIG,
MAX30100_REG_MODE_CONFIG_TEMP_EN,
MAX30100_REG_MODE_CONFIG_TEMP_EN);
if (ret)
return ret;
usleep_range(35000, 50000);
return max30100_read_temp(data, val);
}
static int max30100_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct max30100_data *data = iio_priv(indio_dev);
int ret = -EINVAL;
switch (mask) {
case IIO_CHAN_INFO_RAW:
/*
* Temperature reading can only be acquired while engine
* is running
*/
mutex_lock(&indio_dev->mlock);
if (!iio_buffer_enabled(indio_dev))
ret = -EAGAIN;
else {
ret = max30100_get_temp(data, val);
if (!ret)
ret = IIO_VAL_INT;
}
mutex_unlock(&indio_dev->mlock);
break;
case IIO_CHAN_INFO_SCALE:
*val = 1; /* 0.0625 */
*val2 = 16;
ret = IIO_VAL_FRACTIONAL;
break;
}
return ret;
}
static const struct iio_info max30100_info = {
.driver_module = THIS_MODULE,
.read_raw = max30100_read_raw,
};
static int max30100_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct max30100_data *data;
struct iio_buffer *buffer;
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
buffer = devm_iio_kfifo_allocate(&client->dev);
if (!buffer)
return -ENOMEM;
iio_device_attach_buffer(indio_dev, buffer);
indio_dev->name = MAX30100_DRV_NAME;
indio_dev->channels = max30100_channels;
indio_dev->info = &max30100_info;
indio_dev->num_channels = ARRAY_SIZE(max30100_channels);
indio_dev->available_scan_masks = max30100_scan_masks;
indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE);
indio_dev->setup_ops = &max30100_buffer_setup_ops;
data = iio_priv(indio_dev);
data->indio_dev = indio_dev;
data->client = client;
mutex_init(&data->lock);
i2c_set_clientdata(client, indio_dev);
data->regmap = devm_regmap_init_i2c(client, &max30100_regmap_config);
if (IS_ERR(data->regmap)) {
dev_err(&client->dev, "regmap initialization failed.\n");
return PTR_ERR(data->regmap);
}
max30100_set_powermode(data, false);
ret = max30100_chip_init(data);
if (ret)
return ret;
if (client->irq <= 0) {
dev_err(&client->dev, "no valid irq defined\n");
return -EINVAL;
}
ret = devm_request_threaded_irq(&client->dev, client->irq,
NULL, max30100_interrupt_handler,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"max30100_irq", indio_dev);
if (ret) {
dev_err(&client->dev, "request irq (%d) failed\n", client->irq);
return ret;
}
return iio_device_register(indio_dev);
}
static int max30100_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct max30100_data *data = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
max30100_set_powermode(data, false);
return 0;
}
static const struct i2c_device_id max30100_id[] = {
{ "max30100", 0 },
{}
};
MODULE_DEVICE_TABLE(i2c, max30100_id);
static const struct of_device_id max30100_dt_ids[] = {
{ .compatible = "maxim,max30100" },
{ }
};
MODULE_DEVICE_TABLE(of, max30100_dt_ids);
static struct i2c_driver max30100_driver = {
.driver = {
.name = MAX30100_DRV_NAME,
.of_match_table = of_match_ptr(max30100_dt_ids),
},
.probe = max30100_probe,
.remove = max30100_remove,
.id_table = max30100_id,
};
module_i2c_driver(max30100_driver);
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
MODULE_DESCRIPTION("MAX30100 heart rate and pulse oximeter sensor");
MODULE_LICENSE("GPL");

View File

@ -288,7 +288,11 @@ static int adis16400_initial_setup(struct iio_dev *indio_dev)
if (ret)
goto err_ret;
sscanf(indio_dev->name, "adis%u\n", &device_id);
ret = sscanf(indio_dev->name, "adis%u\n", &device_id);
if (ret != 1) {
ret = -EINVAL;
goto err_ret;
}
if (prod_id != device_id)
dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.",

View File

@ -765,7 +765,9 @@ static int adis16480_initial_setup(struct iio_dev *indio_dev)
if (ret)
return ret;
sscanf(indio_dev->name, "adis%u\n", &device_id);
ret = sscanf(indio_dev->name, "adis%u\n", &device_id);
if (ret != 1)
return -EINVAL;
if (prod_id != device_id)
dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.",

View File

@ -1390,6 +1390,14 @@ static int kmx61_probe(struct i2c_client *client,
}
}
ret = pm_runtime_set_active(&client->dev);
if (ret < 0)
goto err_buffer_cleanup_mag;
pm_runtime_enable(&client->dev);
pm_runtime_set_autosuspend_delay(&client->dev, KMX61_SLEEP_DELAY_MS);
pm_runtime_use_autosuspend(&client->dev);
ret = iio_device_register(data->acc_indio_dev);
if (ret < 0) {
dev_err(&client->dev, "Failed to register acc iio device\n");
@ -1402,18 +1410,8 @@ static int kmx61_probe(struct i2c_client *client,
goto err_iio_unregister_acc;
}
ret = pm_runtime_set_active(&client->dev);
if (ret < 0)
goto err_iio_unregister_mag;
pm_runtime_enable(&client->dev);
pm_runtime_set_autosuspend_delay(&client->dev, KMX61_SLEEP_DELAY_MS);
pm_runtime_use_autosuspend(&client->dev);
return 0;
err_iio_unregister_mag:
iio_device_unregister(data->mag_indio_dev);
err_iio_unregister_acc:
iio_device_unregister(data->acc_indio_dev);
err_buffer_cleanup_mag:
@ -1437,13 +1435,13 @@ static int kmx61_remove(struct i2c_client *client)
{
struct kmx61_data *data = i2c_get_clientdata(client);
iio_device_unregister(data->acc_indio_dev);
iio_device_unregister(data->mag_indio_dev);
pm_runtime_disable(&client->dev);
pm_runtime_set_suspended(&client->dev);
pm_runtime_put_noidle(&client->dev);
iio_device_unregister(data->acc_indio_dev);
iio_device_unregister(data->mag_indio_dev);
if (client->irq > 0) {
iio_triggered_buffer_cleanup(data->acc_indio_dev);
iio_triggered_buffer_cleanup(data->mag_indio_dev);

View File

@ -193,7 +193,8 @@ void iio_buffer_init(struct iio_buffer *buffer)
INIT_LIST_HEAD(&buffer->buffer_list);
init_waitqueue_head(&buffer->pollq);
kref_init(&buffer->ref);
buffer->watermark = 1;
if (!buffer->watermark)
buffer->watermark = 1;
}
EXPORT_SYMBOL(iio_buffer_init);
@ -567,6 +568,22 @@ static void iio_buffer_deactivate_all(struct iio_dev *indio_dev)
iio_buffer_deactivate(buffer);
}
static int iio_buffer_enable(struct iio_buffer *buffer,
struct iio_dev *indio_dev)
{
if (!buffer->access->enable)
return 0;
return buffer->access->enable(buffer, indio_dev);
}
static int iio_buffer_disable(struct iio_buffer *buffer,
struct iio_dev *indio_dev)
{
if (!buffer->access->disable)
return 0;
return buffer->access->disable(buffer, indio_dev);
}
static void iio_buffer_update_bytes_per_datum(struct iio_dev *indio_dev,
struct iio_buffer *buffer)
{
@ -610,6 +627,7 @@ static void iio_free_scan_mask(struct iio_dev *indio_dev,
struct iio_device_config {
unsigned int mode;
unsigned int watermark;
const unsigned long *scan_mask;
unsigned int scan_bytes;
bool scan_timestamp;
@ -642,10 +660,14 @@ static int iio_verify_update(struct iio_dev *indio_dev,
if (buffer == remove_buffer)
continue;
modes &= buffer->access->modes;
config->watermark = min(config->watermark, buffer->watermark);
}
if (insert_buffer)
if (insert_buffer) {
modes &= insert_buffer->access->modes;
config->watermark = min(config->watermark,
insert_buffer->watermark);
}
/* Definitely possible for devices to support both of these. */
if ((modes & INDIO_BUFFER_TRIGGERED) && indio_dev->trig) {
@ -713,6 +735,7 @@ static int iio_verify_update(struct iio_dev *indio_dev,
static int iio_enable_buffers(struct iio_dev *indio_dev,
struct iio_device_config *config)
{
struct iio_buffer *buffer;
int ret;
indio_dev->active_scan_mask = config->scan_mask;
@ -743,6 +766,16 @@ static int iio_enable_buffers(struct iio_dev *indio_dev,
}
}
if (indio_dev->info->hwfifo_set_watermark)
indio_dev->info->hwfifo_set_watermark(indio_dev,
config->watermark);
list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) {
ret = iio_buffer_enable(buffer, indio_dev);
if (ret)
goto err_disable_buffers;
}
indio_dev->currentmode = config->mode;
if (indio_dev->setup_ops->postenable) {
@ -750,12 +783,16 @@ static int iio_enable_buffers(struct iio_dev *indio_dev,
if (ret) {
dev_dbg(&indio_dev->dev,
"Buffer not started: postenable failed (%d)\n", ret);
goto err_run_postdisable;
goto err_disable_buffers;
}
}
return 0;
err_disable_buffers:
list_for_each_entry_continue_reverse(buffer, &indio_dev->buffer_list,
buffer_list)
iio_buffer_disable(buffer, indio_dev);
err_run_postdisable:
indio_dev->currentmode = INDIO_DIRECT_MODE;
if (indio_dev->setup_ops->postdisable)
@ -768,6 +805,7 @@ err_undo_config:
static int iio_disable_buffers(struct iio_dev *indio_dev)
{
struct iio_buffer *buffer;
int ret = 0;
int ret2;
@ -788,6 +826,12 @@ static int iio_disable_buffers(struct iio_dev *indio_dev)
ret = ret2;
}
list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) {
ret2 = iio_buffer_disable(buffer, indio_dev);
if (ret2 && !ret)
ret = ret2;
}
indio_dev->currentmode = INDIO_DIRECT_MODE;
if (indio_dev->setup_ops->postdisable) {
@ -974,9 +1018,6 @@ static ssize_t iio_buffer_store_watermark(struct device *dev,
}
buffer->watermark = val;
if (indio_dev->info->hwfifo_set_watermark)
indio_dev->info->hwfifo_set_watermark(indio_dev, val);
out:
mutex_unlock(&indio_dev->mlock);
@ -991,6 +1032,8 @@ static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR,
iio_buffer_show_enable, iio_buffer_store_enable);
static DEVICE_ATTR(watermark, S_IRUGO | S_IWUSR,
iio_buffer_show_watermark, iio_buffer_store_watermark);
static struct device_attribute dev_attr_watermark_ro = __ATTR(watermark,
S_IRUGO, iio_buffer_show_watermark, NULL);
static struct attribute *iio_buffer_attrs[] = {
&dev_attr_length.attr,
@ -1033,6 +1076,9 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
if (!buffer->access->set_length)
attr[0] = &dev_attr_length_ro.attr;
if (buffer->access->flags & INDIO_BUFFER_FLAG_FIXED_WATERMARK)
attr[2] = &dev_attr_watermark_ro.attr;
if (buffer->attrs)
memcpy(&attr[ARRAY_SIZE(iio_buffer_attrs)], buffer->attrs,
sizeof(struct attribute *) * attrcount);

View File

@ -0,0 +1,51 @@
/*
* Industrial I/O configfs bits
*
* Copyright (c) 2015 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/configfs.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kmod.h>
#include <linux/slab.h>
#include <linux/iio/iio.h>
#include <linux/iio/configfs.h>
static struct config_item_type iio_root_group_type = {
.ct_owner = THIS_MODULE,
};
struct configfs_subsystem iio_configfs_subsys = {
.su_group = {
.cg_item = {
.ci_namebuf = "iio",
.ci_type = &iio_root_group_type,
},
},
.su_mutex = __MUTEX_INITIALIZER(iio_configfs_subsys.su_mutex),
};
EXPORT_SYMBOL(iio_configfs_subsys);
static int __init iio_configfs_init(void)
{
config_group_init(&iio_configfs_subsys.su_group);
return configfs_register_subsystem(&iio_configfs_subsys);
}
module_init(iio_configfs_init);
static void __exit iio_configfs_exit(void)
{
configfs_unregister_subsystem(&iio_configfs_subsys);
}
module_exit(iio_configfs_exit);
MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
MODULE_DESCRIPTION("Industrial I/O configfs support");
MODULE_LICENSE("GPL v2");

View File

@ -470,6 +470,7 @@ ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals)
return 0;
}
}
EXPORT_SYMBOL_GPL(iio_format_value);
static ssize_t iio_read_channel_info(struct device *dev,
struct device_attribute *attr,
@ -512,6 +513,12 @@ int iio_str_to_fixpoint(const char *str, int fract_mult,
int i = 0, f = 0;
bool integer_part = true, negative = false;
if (fract_mult == 0) {
*fract = 0;
return kstrtoint(str, 0, integer);
}
if (str[0] == '-') {
negative = true;
str++;
@ -571,6 +578,9 @@ static ssize_t iio_write_channel_info(struct device *dev,
if (indio_dev->info->write_raw_get_fmt)
switch (indio_dev->info->write_raw_get_fmt(indio_dev,
this_attr->c, this_attr->address)) {
case IIO_VAL_INT:
fract_mult = 0;
break;
case IIO_VAL_INT_PLUS_MICRO:
fract_mult = 100000;
break;

View File

@ -0,0 +1,184 @@
/*
* The Industrial I/O core, software trigger functions
*
* Copyright (c) 2015 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kmod.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/iio/sw_trigger.h>
#include <linux/iio/configfs.h>
#include <linux/configfs.h>
static struct config_group *iio_triggers_group;
static struct config_item_type iio_trigger_type_group_type;
static struct config_item_type iio_triggers_group_type = {
.ct_owner = THIS_MODULE,
};
static LIST_HEAD(iio_trigger_types_list);
static DEFINE_MUTEX(iio_trigger_types_lock);
static
struct iio_sw_trigger_type *__iio_find_sw_trigger_type(const char *name,
unsigned len)
{
struct iio_sw_trigger_type *t = NULL, *iter;
list_for_each_entry(iter, &iio_trigger_types_list, list)
if (!strcmp(iter->name, name)) {
t = iter;
break;
}
return t;
}
int iio_register_sw_trigger_type(struct iio_sw_trigger_type *t)
{
struct iio_sw_trigger_type *iter;
int ret = 0;
mutex_lock(&iio_trigger_types_lock);
iter = __iio_find_sw_trigger_type(t->name, strlen(t->name));
if (iter)
ret = -EBUSY;
else
list_add_tail(&t->list, &iio_trigger_types_list);
mutex_unlock(&iio_trigger_types_lock);
if (ret)
return ret;
t->group = configfs_register_default_group(iio_triggers_group, t->name,
&iio_trigger_type_group_type);
if (IS_ERR(t->group))
ret = PTR_ERR(t->group);
return ret;
}
EXPORT_SYMBOL(iio_register_sw_trigger_type);
void iio_unregister_sw_trigger_type(struct iio_sw_trigger_type *t)
{
struct iio_sw_trigger_type *iter;
mutex_lock(&iio_trigger_types_lock);
iter = __iio_find_sw_trigger_type(t->name, strlen(t->name));
if (iter)
list_del(&t->list);
mutex_unlock(&iio_trigger_types_lock);
configfs_unregister_default_group(t->group);
}
EXPORT_SYMBOL(iio_unregister_sw_trigger_type);
static
struct iio_sw_trigger_type *iio_get_sw_trigger_type(const char *name)
{
struct iio_sw_trigger_type *t;
mutex_lock(&iio_trigger_types_lock);
t = __iio_find_sw_trigger_type(name, strlen(name));
if (t && !try_module_get(t->owner))
t = NULL;
mutex_unlock(&iio_trigger_types_lock);
return t;
}
struct iio_sw_trigger *iio_sw_trigger_create(const char *type, const char *name)
{
struct iio_sw_trigger *t;
struct iio_sw_trigger_type *tt;
tt = iio_get_sw_trigger_type(type);
if (!tt) {
pr_err("Invalid trigger type: %s\n", type);
return ERR_PTR(-EINVAL);
}
t = tt->ops->probe(name);
if (IS_ERR(t))
goto out_module_put;
t->trigger_type = tt;
return t;
out_module_put:
module_put(tt->owner);
return t;
}
EXPORT_SYMBOL(iio_sw_trigger_create);
void iio_sw_trigger_destroy(struct iio_sw_trigger *t)
{
struct iio_sw_trigger_type *tt = t->trigger_type;
tt->ops->remove(t);
module_put(tt->owner);
}
EXPORT_SYMBOL(iio_sw_trigger_destroy);
static struct config_group *trigger_make_group(struct config_group *group,
const char *name)
{
struct iio_sw_trigger *t;
t = iio_sw_trigger_create(group->cg_item.ci_name, name);
if (IS_ERR(t))
return ERR_CAST(t);
config_item_set_name(&t->group.cg_item, "%s", name);
return &t->group;
}
static void trigger_drop_group(struct config_group *group,
struct config_item *item)
{
struct iio_sw_trigger *t = to_iio_sw_trigger(item);
iio_sw_trigger_destroy(t);
config_item_put(item);
}
static struct configfs_group_operations trigger_ops = {
.make_group = &trigger_make_group,
.drop_item = &trigger_drop_group,
};
static struct config_item_type iio_trigger_type_group_type = {
.ct_group_ops = &trigger_ops,
.ct_owner = THIS_MODULE,
};
static int __init iio_sw_trigger_init(void)
{
iio_triggers_group =
configfs_register_default_group(&iio_configfs_subsys.su_group,
"triggers",
&iio_triggers_group_type);
if (IS_ERR(iio_triggers_group))
return PTR_ERR(iio_triggers_group);
return 0;
}
module_init(iio_sw_trigger_init);
static void __exit iio_sw_trigger_exit(void)
{
configfs_unregister_default_group(iio_triggers_group);
}
module_exit(iio_sw_trigger_exit);
MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
MODULE_DESCRIPTION("Industrial I/O software triggers support");
MODULE_LICENSE("GPL v2");

View File

@ -61,12 +61,10 @@ EXPORT_SYMBOL_GPL(iio_map_array_register);
int iio_map_array_unregister(struct iio_dev *indio_dev)
{
int ret = -ENODEV;
struct iio_map_internal *mapi;
struct list_head *pos, *tmp;
struct iio_map_internal *mapi, *next;
mutex_lock(&iio_map_list_lock);
list_for_each_safe(pos, tmp, &iio_map_list) {
mapi = list_entry(pos, struct iio_map_internal, l);
list_for_each_entry_safe(mapi, next, &iio_map_list, l) {
if (indio_dev == mapi->indio_dev) {
list_del(&mapi->l);
kfree(mapi);

View File

@ -743,8 +743,10 @@ static int lm3533_als_set_resistor(struct lm3533_als *als, u8 val)
{
int ret;
if (val < LM3533_ALS_RESISTOR_MIN || val > LM3533_ALS_RESISTOR_MAX)
if (val < LM3533_ALS_RESISTOR_MIN || val > LM3533_ALS_RESISTOR_MAX) {
dev_err(&als->pdev->dev, "invalid resistor value\n");
return -EINVAL;
};
ret = lm3533_write(als->lm3533, LM3533_REG_ALS_RESISTOR_SELECT, val);
if (ret) {

View File

@ -381,17 +381,23 @@ static int pa12203001_probe(struct i2c_client *client,
return ret;
ret = pm_runtime_set_active(&client->dev);
if (ret < 0) {
pa12203001_power_chip(indio_dev, PA12203001_CHIP_DISABLE);
return ret;
}
if (ret < 0)
goto out_err;
pm_runtime_enable(&client->dev);
pm_runtime_set_autosuspend_delay(&client->dev,
PA12203001_SLEEP_DELAY_MS);
pm_runtime_use_autosuspend(&client->dev);
return iio_device_register(indio_dev);
ret = iio_device_register(indio_dev);
if (ret < 0)
goto out_err;
return 0;
out_err:
pa12203001_power_chip(indio_dev, PA12203001_CHIP_DISABLE);
return ret;
}
static int pa12203001_remove(struct i2c_client *client)

View File

@ -507,34 +507,28 @@ static int rpr0521_probe(struct i2c_client *client,
dev_err(&client->dev, "rpr0521 chip init failed\n");
return ret;
}
ret = iio_device_register(indio_dev);
if (ret < 0)
return ret;
ret = pm_runtime_set_active(&client->dev);
if (ret < 0)
goto err_iio_unregister;
return ret;
pm_runtime_enable(&client->dev);
pm_runtime_set_autosuspend_delay(&client->dev, RPR0521_SLEEP_DELAY_MS);
pm_runtime_use_autosuspend(&client->dev);
return 0;
err_iio_unregister:
iio_device_unregister(indio_dev);
return ret;
return iio_device_register(indio_dev);
}
static int rpr0521_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
iio_device_unregister(indio_dev);
pm_runtime_disable(&client->dev);
pm_runtime_set_suspended(&client->dev);
pm_runtime_put_noidle(&client->dev);
iio_device_unregister(indio_dev);
rpr0521_poweroff(iio_priv(indio_dev));
return 0;

View File

@ -20,14 +20,21 @@
#include <linux/acpi.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/iio/events.h>
#include <linux/iio/iio.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/iio/sysfs.h>
#include <linux/mutex.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#define US5182D_REG_CFG0 0x00
#define US5182D_CFG0_ONESHOT_EN BIT(6)
#define US5182D_CFG0_SHUTDOWN_EN BIT(7)
#define US5182D_CFG0_WORD_ENABLE BIT(0)
#define US5182D_CFG0_PROX BIT(3)
#define US5182D_CFG0_PX_IRQ BIT(2)
#define US5182D_REG_CFG1 0x01
#define US5182D_CFG1_ALS_RES16 BIT(4)
@ -39,6 +46,7 @@
#define US5182D_REG_CFG3 0x03
#define US5182D_CFG3_LED_CURRENT100 (BIT(4) | BIT(5))
#define US5182D_CFG3_INT_SOURCE_PX BIT(3)
#define US5182D_REG_CFG4 0x10
@ -53,6 +61,13 @@
#define US5182D_REG_AUTO_LDARK_GAIN 0x29
#define US5182D_REG_AUTO_HDARK_GAIN 0x2a
/* Thresholds for events: px low (0x08-l, 0x09-h), px high (0x0a-l 0x0b-h) */
#define US5182D_REG_PXL_TH 0x08
#define US5182D_REG_PXH_TH 0x0a
#define US5182D_REG_PXL_TH_DEFAULT 1000
#define US5182D_REG_PXH_TH_DEFAULT 30000
#define US5182D_OPMODE_ALS 0x01
#define US5182D_OPMODE_PX 0x02
#define US5182D_OPMODE_SHIFT 4
@ -81,6 +96,9 @@
#define US5182D_READ_BYTE 1
#define US5182D_READ_WORD 2
#define US5182D_OPSTORE_SLEEP_TIME 20 /* ms */
#define US5182D_SLEEP_MS 3000 /* ms */
#define US5182D_PXH_TH_DISABLE 0xffff
#define US5182D_PXL_TH_DISABLE 0x0000
/* Available ranges: [12354, 7065, 3998, 2202, 1285, 498, 256, 138] lux */
static const int us5182d_scales[] = {188500, 107800, 61000, 33600, 19600, 7600,
@ -99,6 +117,11 @@ enum mode {
US5182D_PX_ONLY
};
enum pmode {
US5182D_CONTINUOUS,
US5182D_ONESHOT
};
struct us5182d_data {
struct i2c_client *client;
struct mutex lock;
@ -111,7 +134,19 @@ struct us5182d_data {
u8 upper_dark_gain;
u16 *us5182d_dark_ths;
u16 px_low_th;
u16 px_high_th;
int rising_en;
int falling_en;
u8 opmode;
u8 power_mode;
bool als_enabled;
bool px_enabled;
bool default_continuous;
};
static IIO_CONST_ATTR(in_illuminance_scale_available,
@ -130,16 +165,30 @@ static const struct {
u8 reg;
u8 val;
} us5182d_regvals[] = {
{US5182D_REG_CFG0, (US5182D_CFG0_SHUTDOWN_EN |
US5182D_CFG0_WORD_ENABLE)},
{US5182D_REG_CFG0, US5182D_CFG0_WORD_ENABLE},
{US5182D_REG_CFG1, US5182D_CFG1_ALS_RES16},
{US5182D_REG_CFG2, (US5182D_CFG2_PX_RES16 |
US5182D_CFG2_PXGAIN_DEFAULT)},
{US5182D_REG_CFG3, US5182D_CFG3_LED_CURRENT100},
{US5182D_REG_MODE_STORE, US5182D_STORE_MODE},
{US5182D_REG_CFG3, US5182D_CFG3_LED_CURRENT100 |
US5182D_CFG3_INT_SOURCE_PX},
{US5182D_REG_CFG4, 0x00},
};
static const struct iio_event_spec us5182d_events[] = {
{
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_RISING,
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_ENABLE),
},
{
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_FALLING,
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_ENABLE),
},
};
static const struct iio_chan_spec us5182d_channels[] = {
{
.type = IIO_LIGHT,
@ -149,27 +198,12 @@ static const struct iio_chan_spec us5182d_channels[] = {
{
.type = IIO_PROXIMITY,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.event_spec = us5182d_events,
.num_event_specs = ARRAY_SIZE(us5182d_events),
}
};
static int us5182d_get_als(struct us5182d_data *data)
{
int ret;
unsigned long result;
ret = i2c_smbus_read_word_data(data->client,
US5182D_REG_ADL);
if (ret < 0)
return ret;
result = ret * data->ga / US5182D_GA_RESOLUTION;
if (result > 0xffff)
result = 0xffff;
return result;
}
static int us5182d_set_opmode(struct us5182d_data *data, u8 mode)
static int us5182d_oneshot_en(struct us5182d_data *data)
{
int ret;
@ -183,6 +217,20 @@ static int us5182d_set_opmode(struct us5182d_data *data, u8 mode)
*/
ret = ret | US5182D_CFG0_ONESHOT_EN;
return i2c_smbus_write_byte_data(data->client, US5182D_REG_CFG0, ret);
}
static int us5182d_set_opmode(struct us5182d_data *data, u8 mode)
{
int ret;
if (mode == data->opmode)
return 0;
ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CFG0);
if (ret < 0)
return ret;
/* update mode */
ret = ret & ~US5182D_OPMODE_MASK;
ret = ret | (mode << US5182D_OPMODE_SHIFT);
@ -196,9 +244,6 @@ static int us5182d_set_opmode(struct us5182d_data *data, u8 mode)
if (ret < 0)
return ret;
if (mode == data->opmode)
return 0;
ret = i2c_smbus_write_byte_data(data->client, US5182D_REG_MODE_STORE,
US5182D_STORE_MODE);
if (ret < 0)
@ -210,6 +255,177 @@ static int us5182d_set_opmode(struct us5182d_data *data, u8 mode)
return 0;
}
static int us5182d_als_enable(struct us5182d_data *data)
{
int ret;
u8 mode;
if (data->power_mode == US5182D_ONESHOT) {
ret = us5182d_set_opmode(data, US5182D_ALS_ONLY);
if (ret < 0)
return ret;
data->px_enabled = false;
}
if (data->als_enabled)
return 0;
mode = data->px_enabled ? US5182D_ALS_PX : US5182D_ALS_ONLY;
ret = us5182d_set_opmode(data, mode);
if (ret < 0)
return ret;
data->als_enabled = true;
return 0;
}
static int us5182d_px_enable(struct us5182d_data *data)
{
int ret;
u8 mode;
if (data->power_mode == US5182D_ONESHOT) {
ret = us5182d_set_opmode(data, US5182D_PX_ONLY);
if (ret < 0)
return ret;
data->als_enabled = false;
}
if (data->px_enabled)
return 0;
mode = data->als_enabled ? US5182D_ALS_PX : US5182D_PX_ONLY;
ret = us5182d_set_opmode(data, mode);
if (ret < 0)
return ret;
data->px_enabled = true;
return 0;
}
static int us5182d_get_als(struct us5182d_data *data)
{
int ret;
unsigned long result;
ret = us5182d_als_enable(data);
if (ret < 0)
return ret;
ret = i2c_smbus_read_word_data(data->client,
US5182D_REG_ADL);
if (ret < 0)
return ret;
result = ret * data->ga / US5182D_GA_RESOLUTION;
if (result > 0xffff)
result = 0xffff;
return result;
}
static int us5182d_get_px(struct us5182d_data *data)
{
int ret;
ret = us5182d_px_enable(data);
if (ret < 0)
return ret;
return i2c_smbus_read_word_data(data->client,
US5182D_REG_PDL);
}
static int us5182d_shutdown_en(struct us5182d_data *data, u8 state)
{
int ret;
if (data->power_mode == US5182D_ONESHOT)
return 0;
ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CFG0);
if (ret < 0)
return ret;
ret = ret & ~US5182D_CFG0_SHUTDOWN_EN;
ret = ret | state;
ret = i2c_smbus_write_byte_data(data->client, US5182D_REG_CFG0, ret);
if (ret < 0)
return ret;
if (state & US5182D_CFG0_SHUTDOWN_EN) {
data->als_enabled = false;
data->px_enabled = false;
}
return ret;
}
static int us5182d_set_power_state(struct us5182d_data *data, bool on)
{
int ret;
if (data->power_mode == US5182D_ONESHOT)
return 0;
if (on) {
ret = pm_runtime_get_sync(&data->client->dev);
if (ret < 0)
pm_runtime_put_noidle(&data->client->dev);
} else {
pm_runtime_mark_last_busy(&data->client->dev);
ret = pm_runtime_put_autosuspend(&data->client->dev);
}
return ret;
}
static int us5182d_read_value(struct us5182d_data *data,
struct iio_chan_spec const *chan)
{
int ret, value;
mutex_lock(&data->lock);
if (data->power_mode == US5182D_ONESHOT) {
ret = us5182d_oneshot_en(data);
if (ret < 0)
goto out_err;
}
ret = us5182d_set_power_state(data, true);
if (ret < 0)
goto out_err;
if (chan->type == IIO_LIGHT)
ret = us5182d_get_als(data);
else
ret = us5182d_get_px(data);
if (ret < 0)
goto out_poweroff;
value = ret;
ret = us5182d_set_power_state(data, false);
if (ret < 0)
goto out_err;
mutex_unlock(&data->lock);
return value;
out_poweroff:
us5182d_set_power_state(data, false);
out_err:
mutex_unlock(&data->lock);
return ret;
}
static int us5182d_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
@ -219,53 +435,21 @@ static int us5182d_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
switch (chan->type) {
case IIO_LIGHT:
mutex_lock(&data->lock);
ret = us5182d_set_opmode(data, US5182D_OPMODE_ALS);
if (ret < 0)
goto out_err;
ret = us5182d_get_als(data);
if (ret < 0)
goto out_err;
mutex_unlock(&data->lock);
*val = ret;
return IIO_VAL_INT;
case IIO_PROXIMITY:
mutex_lock(&data->lock);
ret = us5182d_set_opmode(data, US5182D_OPMODE_PX);
if (ret < 0)
goto out_err;
ret = i2c_smbus_read_word_data(data->client,
US5182D_REG_PDL);
if (ret < 0)
goto out_err;
mutex_unlock(&data->lock);
*val = ret;
return IIO_VAL_INT;
default:
return -EINVAL;
}
ret = us5182d_read_value(data, chan);
if (ret < 0)
return ret;
*val = ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CFG1);
if (ret < 0)
return ret;
*val = 0;
*val2 = us5182d_scales[ret & US5182D_AGAIN_MASK];
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
return -EINVAL;
out_err:
mutex_unlock(&data->lock);
return ret;
}
/**
@ -343,11 +527,201 @@ static int us5182d_write_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
static int us5182d_setup_prox(struct iio_dev *indio_dev,
enum iio_event_direction dir, u16 val)
{
struct us5182d_data *data = iio_priv(indio_dev);
if (dir == IIO_EV_DIR_FALLING)
return i2c_smbus_write_word_data(data->client,
US5182D_REG_PXL_TH, val);
else if (dir == IIO_EV_DIR_RISING)
return i2c_smbus_write_word_data(data->client,
US5182D_REG_PXH_TH, val);
return 0;
}
static int us5182d_read_thresh(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, enum iio_event_type type,
enum iio_event_direction dir, enum iio_event_info info, int *val,
int *val2)
{
struct us5182d_data *data = iio_priv(indio_dev);
switch (dir) {
case IIO_EV_DIR_RISING:
mutex_lock(&data->lock);
*val = data->px_high_th;
mutex_unlock(&data->lock);
break;
case IIO_EV_DIR_FALLING:
mutex_lock(&data->lock);
*val = data->px_low_th;
mutex_unlock(&data->lock);
break;
default:
return -EINVAL;
}
return IIO_VAL_INT;
}
static int us5182d_write_thresh(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, enum iio_event_type type,
enum iio_event_direction dir, enum iio_event_info info, int val,
int val2)
{
struct us5182d_data *data = iio_priv(indio_dev);
int ret;
if (val < 0 || val > USHRT_MAX || val2 != 0)
return -EINVAL;
switch (dir) {
case IIO_EV_DIR_RISING:
mutex_lock(&data->lock);
if (data->rising_en) {
ret = us5182d_setup_prox(indio_dev, dir, val);
if (ret < 0)
goto err;
}
data->px_high_th = val;
mutex_unlock(&data->lock);
break;
case IIO_EV_DIR_FALLING:
mutex_lock(&data->lock);
if (data->falling_en) {
ret = us5182d_setup_prox(indio_dev, dir, val);
if (ret < 0)
goto err;
}
data->px_low_th = val;
mutex_unlock(&data->lock);
break;
default:
return -EINVAL;
}
return 0;
err:
mutex_unlock(&data->lock);
return ret;
}
static int us5182d_read_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, enum iio_event_type type,
enum iio_event_direction dir)
{
struct us5182d_data *data = iio_priv(indio_dev);
int ret;
switch (dir) {
case IIO_EV_DIR_RISING:
mutex_lock(&data->lock);
ret = data->rising_en;
mutex_unlock(&data->lock);
break;
case IIO_EV_DIR_FALLING:
mutex_lock(&data->lock);
ret = data->falling_en;
mutex_unlock(&data->lock);
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static int us5182d_write_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, enum iio_event_type type,
enum iio_event_direction dir, int state)
{
struct us5182d_data *data = iio_priv(indio_dev);
int ret;
u16 new_th;
mutex_lock(&data->lock);
switch (dir) {
case IIO_EV_DIR_RISING:
if (data->rising_en == state) {
mutex_unlock(&data->lock);
return 0;
}
new_th = US5182D_PXH_TH_DISABLE;
if (state) {
data->power_mode = US5182D_CONTINUOUS;
ret = us5182d_set_power_state(data, true);
if (ret < 0)
goto err;
ret = us5182d_px_enable(data);
if (ret < 0)
goto err_poweroff;
new_th = data->px_high_th;
}
ret = us5182d_setup_prox(indio_dev, dir, new_th);
if (ret < 0)
goto err_poweroff;
data->rising_en = state;
break;
case IIO_EV_DIR_FALLING:
if (data->falling_en == state) {
mutex_unlock(&data->lock);
return 0;
}
new_th = US5182D_PXL_TH_DISABLE;
if (state) {
data->power_mode = US5182D_CONTINUOUS;
ret = us5182d_set_power_state(data, true);
if (ret < 0)
goto err;
ret = us5182d_px_enable(data);
if (ret < 0)
goto err_poweroff;
new_th = data->px_low_th;
}
ret = us5182d_setup_prox(indio_dev, dir, new_th);
if (ret < 0)
goto err_poweroff;
data->falling_en = state;
break;
default:
ret = -EINVAL;
goto err;
}
if (!state) {
ret = us5182d_set_power_state(data, false);
if (ret < 0)
goto err;
}
if (!data->falling_en && !data->rising_en && !data->default_continuous)
data->power_mode = US5182D_ONESHOT;
mutex_unlock(&data->lock);
return 0;
err_poweroff:
if (state)
us5182d_set_power_state(data, false);
err:
mutex_unlock(&data->lock);
return ret;
}
static const struct iio_info us5182d_info = {
.driver_module = THIS_MODULE,
.read_raw = us5182d_read_raw,
.write_raw = us5182d_write_raw,
.attrs = &us5182d_attr_group,
.read_event_value = &us5182d_read_thresh,
.write_event_value = &us5182d_write_thresh,
.read_event_config = &us5182d_read_event_config,
.write_event_config = &us5182d_write_event_config,
};
static int us5182d_reset(struct iio_dev *indio_dev)
@ -368,6 +742,10 @@ static int us5182d_init(struct iio_dev *indio_dev)
return ret;
data->opmode = 0;
data->power_mode = US5182D_CONTINUOUS;
data->px_low_th = US5182D_REG_PXL_TH_DEFAULT;
data->px_high_th = US5182D_REG_PXH_TH_DEFAULT;
for (i = 0; i < ARRAY_SIZE(us5182d_regvals); i++) {
ret = i2c_smbus_write_byte_data(data->client,
us5182d_regvals[i].reg,
@ -376,7 +754,17 @@ static int us5182d_init(struct iio_dev *indio_dev)
return ret;
}
return 0;
data->als_enabled = true;
data->px_enabled = true;
if (!data->default_continuous) {
ret = us5182d_shutdown_en(data, US5182D_CFG0_SHUTDOWN_EN);
if (ret < 0)
return ret;
data->power_mode = US5182D_ONESHOT;
}
return ret;
}
static void us5182d_get_platform_data(struct iio_dev *indio_dev)
@ -399,6 +787,8 @@ static void us5182d_get_platform_data(struct iio_dev *indio_dev)
"upisemi,lower-dark-gain",
&data->lower_dark_gain))
data->lower_dark_gain = US5182D_REG_AUTO_LDARK_GAIN_DEFAULT;
data->default_continuous = device_property_read_bool(&data->client->dev,
"upisemi,continuous");
}
static int us5182d_dark_gain_config(struct iio_dev *indio_dev)
@ -426,6 +816,33 @@ static int us5182d_dark_gain_config(struct iio_dev *indio_dev)
US5182D_REG_DARK_AUTO_EN_DEFAULT);
}
static irqreturn_t us5182d_irq_thread_handler(int irq, void *private)
{
struct iio_dev *indio_dev = private;
struct us5182d_data *data = iio_priv(indio_dev);
enum iio_event_direction dir;
int ret;
u64 ev;
ret = i2c_smbus_read_byte_data(data->client, US5182D_REG_CFG0);
if (ret < 0) {
dev_err(&data->client->dev, "i2c transfer error in irq\n");
return IRQ_HANDLED;
}
dir = ret & US5182D_CFG0_PROX ? IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING;
ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 1, IIO_EV_TYPE_THRESH, dir);
iio_push_event(indio_dev, ev, iio_get_time_ns());
ret = i2c_smbus_write_byte_data(data->client, US5182D_REG_CFG0,
ret & ~US5182D_CFG0_PX_IRQ);
if (ret < 0)
dev_err(&data->client->dev, "i2c transfer error in irq\n");
return IRQ_HANDLED;
}
static int us5182d_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@ -457,6 +874,16 @@ static int us5182d_probe(struct i2c_client *client,
return (ret < 0) ? ret : -ENODEV;
}
if (client->irq > 0) {
ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
us5182d_irq_thread_handler,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
"us5182d-irq", indio_dev);
if (ret < 0)
return ret;
} else
dev_warn(&client->dev, "no valid irq found\n");
us5182d_get_platform_data(indio_dev);
ret = us5182d_init(indio_dev);
if (ret < 0)
@ -464,18 +891,73 @@ static int us5182d_probe(struct i2c_client *client,
ret = us5182d_dark_gain_config(indio_dev);
if (ret < 0)
return ret;
goto out_err;
if (data->default_continuous) {
pm_runtime_set_active(&client->dev);
if (ret < 0)
goto out_err;
}
pm_runtime_enable(&client->dev);
pm_runtime_set_autosuspend_delay(&client->dev,
US5182D_SLEEP_MS);
pm_runtime_use_autosuspend(&client->dev);
ret = iio_device_register(indio_dev);
if (ret < 0)
goto out_err;
return 0;
out_err:
us5182d_shutdown_en(data, US5182D_CFG0_SHUTDOWN_EN);
return ret;
return iio_device_register(indio_dev);
}
static int us5182d_remove(struct i2c_client *client)
{
struct us5182d_data *data = iio_priv(i2c_get_clientdata(client));
iio_device_unregister(i2c_get_clientdata(client));
return i2c_smbus_write_byte_data(client, US5182D_REG_CFG0,
US5182D_CFG0_SHUTDOWN_EN);
pm_runtime_disable(&client->dev);
pm_runtime_set_suspended(&client->dev);
return us5182d_shutdown_en(data, US5182D_CFG0_SHUTDOWN_EN);
}
#if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM)
static int us5182d_suspend(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct us5182d_data *data = iio_priv(indio_dev);
if (data->power_mode == US5182D_CONTINUOUS)
return us5182d_shutdown_en(data, US5182D_CFG0_SHUTDOWN_EN);
return 0;
}
static int us5182d_resume(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct us5182d_data *data = iio_priv(indio_dev);
if (data->power_mode == US5182D_CONTINUOUS)
return us5182d_shutdown_en(data,
~US5182D_CFG0_SHUTDOWN_EN & 0xff);
return 0;
}
#endif
static const struct dev_pm_ops us5182d_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(us5182d_suspend, us5182d_resume)
SET_RUNTIME_PM_OPS(us5182d_suspend, us5182d_resume, NULL)
};
static const struct acpi_device_id us5182d_acpi_match[] = {
{ "USD5182", 0},
{}
@ -493,6 +975,7 @@ MODULE_DEVICE_TABLE(i2c, us5182d_id);
static struct i2c_driver us5182d_driver = {
.driver = {
.name = US5182D_DRV_NAME,
.pm = &us5182d_pm_ops,
.acpi_match_table = ACPI_PTR(us5182d_acpi_match),
},
.probe = us5182d_probe,

View File

@ -928,27 +928,24 @@ static int bmc150_magn_probe(struct i2c_client *client,
goto err_free_irq;
}
ret = iio_device_register(indio_dev);
if (ret < 0) {
dev_err(&client->dev, "unable to register iio device\n");
goto err_buffer_cleanup;
}
ret = pm_runtime_set_active(&client->dev);
if (ret)
goto err_iio_unregister;
goto err_buffer_cleanup;
pm_runtime_enable(&client->dev);
pm_runtime_set_autosuspend_delay(&client->dev,
BMC150_MAGN_AUTO_SUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(&client->dev);
dev_dbg(&indio_dev->dev, "Registered device %s\n", name);
ret = iio_device_register(indio_dev);
if (ret < 0) {
dev_err(&client->dev, "unable to register iio device\n");
goto err_buffer_cleanup;
}
dev_dbg(&indio_dev->dev, "Registered device %s\n", name);
return 0;
err_iio_unregister:
iio_device_unregister(indio_dev);
err_buffer_cleanup:
iio_triggered_buffer_cleanup(indio_dev);
err_free_irq:
@ -967,11 +964,12 @@ static int bmc150_magn_remove(struct i2c_client *client)
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct bmc150_magn_data *data = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
pm_runtime_disable(&client->dev);
pm_runtime_set_suspended(&client->dev);
pm_runtime_put_noidle(&client->dev);
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
if (client->irq > 0)

View File

@ -13,7 +13,7 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* TODO: runtime pm, interrupt mode, and signal strength reporting
* TODO: interrupt mode, and signal strength reporting
*/
#include <linux/err.h>
@ -21,6 +21,7 @@
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
@ -35,8 +36,11 @@
#define LIDAR_REG_STATUS_INVALID BIT(3)
#define LIDAR_REG_STATUS_READY BIT(0)
#define LIDAR_REG_DATA_HBYTE 0x0f
#define LIDAR_REG_DATA_LBYTE 0x10
#define LIDAR_REG_DATA_HBYTE 0x0f
#define LIDAR_REG_DATA_LBYTE 0x10
#define LIDAR_REG_DATA_WORD_READ BIT(7)
#define LIDAR_REG_PWR_CONTROL 0x65
#define LIDAR_DRV_NAME "lidar"
@ -44,6 +48,9 @@ struct lidar_data {
struct iio_dev *indio_dev;
struct i2c_client *client;
int (*xfer)(struct lidar_data *data, u8 reg, u8 *val, int len);
int i2c_enabled;
u16 buffer[8]; /* 2 byte distance + 8 byte timestamp */
};
@ -62,7 +69,28 @@ static const struct iio_chan_spec lidar_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(1),
};
static int lidar_read_byte(struct lidar_data *data, int reg)
static int lidar_i2c_xfer(struct lidar_data *data, u8 reg, u8 *val, int len)
{
struct i2c_client *client = data->client;
struct i2c_msg msg[2];
int ret;
msg[0].addr = client->addr;
msg[0].flags = client->flags | I2C_M_STOP;
msg[0].len = 1;
msg[0].buf = (char *) &reg;
msg[1].addr = client->addr;
msg[1].flags = client->flags | I2C_M_RD;
msg[1].len = len;
msg[1].buf = (char *) val;
ret = i2c_transfer(client->adapter, msg, 2);
return (ret == 2) ? 0 : ret;
}
static int lidar_smbus_xfer(struct lidar_data *data, u8 reg, u8 *val, int len)
{
struct i2c_client *client = data->client;
int ret;
@ -72,17 +100,35 @@ static int lidar_read_byte(struct lidar_data *data, int reg)
* so in turn i2c_smbus_read_byte_data cannot be used
*/
ret = i2c_smbus_write_byte(client, reg);
if (ret < 0) {
dev_err(&client->dev, "cannot write addr value");
return ret;
while (len--) {
ret = i2c_smbus_write_byte(client, reg++);
if (ret < 0) {
dev_err(&client->dev, "cannot write addr value");
return ret;
}
ret = i2c_smbus_read_byte(client);
if (ret < 0) {
dev_err(&client->dev, "cannot read data value");
return ret;
}
*(val++) = ret;
}
ret = i2c_smbus_read_byte(client);
if (ret < 0)
dev_err(&client->dev, "cannot read data value");
return 0;
}
return ret;
static int lidar_read_byte(struct lidar_data *data, u8 reg)
{
int ret;
u8 val;
ret = data->xfer(data, reg, &val, 1);
if (ret < 0)
return ret;
return val;
}
static inline int lidar_write_control(struct lidar_data *data, int val)
@ -90,24 +136,22 @@ static inline int lidar_write_control(struct lidar_data *data, int val)
return i2c_smbus_write_byte_data(data->client, LIDAR_REG_CONTROL, val);
}
static inline int lidar_write_power(struct lidar_data *data, int val)
{
return i2c_smbus_write_byte_data(data->client,
LIDAR_REG_PWR_CONTROL, val);
}
static int lidar_read_measurement(struct lidar_data *data, u16 *reg)
{
int ret;
int val;
int ret = data->xfer(data, LIDAR_REG_DATA_HBYTE |
(data->i2c_enabled ? LIDAR_REG_DATA_WORD_READ : 0),
(u8 *) reg, 2);
ret = lidar_read_byte(data, LIDAR_REG_DATA_HBYTE);
if (ret < 0)
return ret;
val = ret << 8;
if (!ret)
*reg = be16_to_cpu(*reg);
ret = lidar_read_byte(data, LIDAR_REG_DATA_LBYTE);
if (ret < 0)
return ret;
val |= ret;
*reg = val;
return 0;
return ret;
}
static int lidar_get_measurement(struct lidar_data *data, u16 *reg)
@ -116,6 +160,8 @@ static int lidar_get_measurement(struct lidar_data *data, u16 *reg)
int tries = 10;
int ret;
pm_runtime_get_sync(&client->dev);
/* start sample */
ret = lidar_write_control(data, LIDAR_REG_CONTROL_ACQUIRE);
if (ret < 0) {
@ -144,6 +190,8 @@ static int lidar_get_measurement(struct lidar_data *data, u16 *reg)
}
ret = -EIO;
}
pm_runtime_mark_last_busy(&client->dev);
pm_runtime_put_autosuspend(&client->dev);
return ret;
}
@ -221,6 +269,16 @@ static int lidar_probe(struct i2c_client *client,
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
data->xfer = lidar_i2c_xfer;
data->i2c_enabled = 1;
} else if (i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE))
data->xfer = lidar_smbus_xfer;
else
return -ENOTSUPP;
indio_dev->info = &lidar_info;
indio_dev->name = LIDAR_DRV_NAME;
@ -228,7 +286,6 @@ static int lidar_probe(struct i2c_client *client,
indio_dev->num_channels = ARRAY_SIZE(lidar_channels);
indio_dev->modes = INDIO_DIRECT_MODE;
data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
data->client = client;
@ -243,6 +300,17 @@ static int lidar_probe(struct i2c_client *client,
if (ret)
goto error_unreg_buffer;
pm_runtime_set_autosuspend_delay(&client->dev, 1000);
pm_runtime_use_autosuspend(&client->dev);
ret = pm_runtime_set_active(&client->dev);
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;
error_unreg_buffer:
@ -258,6 +326,9 @@ static int lidar_remove(struct i2c_client *client)
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
pm_runtime_disable(&client->dev);
pm_runtime_set_suspended(&client->dev);
return 0;
}
@ -273,10 +344,38 @@ static const struct of_device_id lidar_dt_ids[] = {
};
MODULE_DEVICE_TABLE(of, lidar_dt_ids);
#ifdef CONFIG_PM
static int lidar_pm_runtime_suspend(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct lidar_data *data = iio_priv(indio_dev);
return lidar_write_power(data, 0x0f);
}
static int lidar_pm_runtime_resume(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct lidar_data *data = iio_priv(indio_dev);
int ret = lidar_write_power(data, 0);
/* regulator and FPGA needs settling time */
usleep_range(15000, 20000);
return ret;
}
#endif
static const struct dev_pm_ops lidar_pm_ops = {
SET_RUNTIME_PM_OPS(lidar_pm_runtime_suspend,
lidar_pm_runtime_resume, NULL)
};
static struct i2c_driver lidar_driver = {
.driver = {
.name = LIDAR_DRV_NAME,
.of_match_table = of_match_ptr(lidar_dt_ids),
.pm = &lidar_pm_ops,
},
.probe = lidar_probe,
.remove = lidar_remove,

View File

@ -5,6 +5,16 @@
menu "Triggers - standalone"
config IIO_HRTIMER_TRIGGER
tristate "High resolution timer trigger"
depends on IIO_SW_TRIGGER
help
Provides a frequency based IIO trigger using high resolution
timers as interrupt source.
To compile this driver as a module, choose M here: the
module will be called iio-trig-hrtimer.
config IIO_INTERRUPT_TRIGGER
tristate "Generic interrupt trigger"
help

View File

@ -3,5 +3,7 @@
#
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_IIO_HRTIMER_TRIGGER) += iio-trig-hrtimer.o
obj-$(CONFIG_IIO_INTERRUPT_TRIGGER) += iio-trig-interrupt.o
obj-$(CONFIG_IIO_SYSFS_TRIGGER) += iio-trig-sysfs.o

View File

@ -0,0 +1,193 @@
/**
* The industrial I/O periodic hrtimer trigger driver
*
* Copyright (C) Intuitive Aerial AB
* Written by Marten Svanfeldt, marten@intuitiveaerial.com
* Copyright (C) 2012, Analog Device Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
* Copyright (C) 2015, Intel Corporation
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/hrtimer.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger.h>
#include <linux/iio/sw_trigger.h>
/* default sampling frequency - 100Hz */
#define HRTIMER_DEFAULT_SAMPLING_FREQUENCY 100
struct iio_hrtimer_info {
struct iio_sw_trigger swt;
struct hrtimer timer;
unsigned long sampling_frequency;
ktime_t period;
};
static struct config_item_type iio_hrtimer_type = {
.ct_owner = THIS_MODULE,
};
static
ssize_t iio_hrtimer_show_sampling_frequency(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct iio_trigger *trig = to_iio_trigger(dev);
struct iio_hrtimer_info *info = iio_trigger_get_drvdata(trig);
return snprintf(buf, PAGE_SIZE, "%lu\n", info->sampling_frequency);
}
static
ssize_t iio_hrtimer_store_sampling_frequency(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
struct iio_trigger *trig = to_iio_trigger(dev);
struct iio_hrtimer_info *info = iio_trigger_get_drvdata(trig);
unsigned long val;
int ret;
ret = kstrtoul(buf, 10, &val);
if (ret)
return ret;
if (!val || val > NSEC_PER_SEC)
return -EINVAL;
info->sampling_frequency = val;
info->period = ktime_set(0, NSEC_PER_SEC / val);
return len;
}
static DEVICE_ATTR(sampling_frequency, S_IRUGO | S_IWUSR,
iio_hrtimer_show_sampling_frequency,
iio_hrtimer_store_sampling_frequency);
static struct attribute *iio_hrtimer_attrs[] = {
&dev_attr_sampling_frequency.attr,
NULL
};
static const struct attribute_group iio_hrtimer_attr_group = {
.attrs = iio_hrtimer_attrs,
};
static const struct attribute_group *iio_hrtimer_attr_groups[] = {
&iio_hrtimer_attr_group,
NULL
};
static enum hrtimer_restart iio_hrtimer_trig_handler(struct hrtimer *timer)
{
struct iio_hrtimer_info *info;
info = container_of(timer, struct iio_hrtimer_info, timer);
hrtimer_forward_now(timer, info->period);
iio_trigger_poll(info->swt.trigger);
return HRTIMER_RESTART;
}
static int iio_trig_hrtimer_set_state(struct iio_trigger *trig, bool state)
{
struct iio_hrtimer_info *trig_info;
trig_info = iio_trigger_get_drvdata(trig);
if (state)
hrtimer_start(&trig_info->timer, trig_info->period,
HRTIMER_MODE_REL);
else
hrtimer_cancel(&trig_info->timer);
return 0;
}
static const struct iio_trigger_ops iio_hrtimer_trigger_ops = {
.owner = THIS_MODULE,
.set_trigger_state = iio_trig_hrtimer_set_state,
};
static struct iio_sw_trigger *iio_trig_hrtimer_probe(const char *name)
{
struct iio_hrtimer_info *trig_info;
int ret;
trig_info = kzalloc(sizeof(*trig_info), GFP_KERNEL);
if (!trig_info)
return ERR_PTR(-ENOMEM);
trig_info->swt.trigger = iio_trigger_alloc("%s", name);
if (!trig_info->swt.trigger) {
ret = -ENOMEM;
goto err_free_trig_info;
}
iio_trigger_set_drvdata(trig_info->swt.trigger, trig_info);
trig_info->swt.trigger->ops = &iio_hrtimer_trigger_ops;
trig_info->swt.trigger->dev.groups = iio_hrtimer_attr_groups;
hrtimer_init(&trig_info->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
trig_info->timer.function = iio_hrtimer_trig_handler;
trig_info->sampling_frequency = HRTIMER_DEFAULT_SAMPLING_FREQUENCY;
trig_info->period = ktime_set(0, NSEC_PER_SEC /
trig_info->sampling_frequency);
ret = iio_trigger_register(trig_info->swt.trigger);
if (ret)
goto err_free_trigger;
iio_swt_group_init_type_name(&trig_info->swt, name, &iio_hrtimer_type);
return &trig_info->swt;
err_free_trigger:
iio_trigger_free(trig_info->swt.trigger);
err_free_trig_info:
kfree(trig_info);
return ERR_PTR(ret);
}
static int iio_trig_hrtimer_remove(struct iio_sw_trigger *swt)
{
struct iio_hrtimer_info *trig_info;
trig_info = iio_trigger_get_drvdata(swt->trigger);
iio_trigger_unregister(swt->trigger);
/* cancel the timer after unreg to make sure no one rearms it */
hrtimer_cancel(&trig_info->timer);
iio_trigger_free(swt->trigger);
kfree(trig_info);
return 0;
}
static const struct iio_sw_trigger_ops iio_trig_hrtimer_ops = {
.probe = iio_trig_hrtimer_probe,
.remove = iio_trig_hrtimer_remove,
};
static struct iio_sw_trigger_type iio_trig_hrtimer = {
.name = "hrtimer",
.owner = THIS_MODULE,
.ops = &iio_trig_hrtimer_ops,
};
module_iio_sw_trigger_driver(iio_trig_hrtimer);
MODULE_AUTHOR("Marten Svanfeldt <marten@intuitiveaerial.com>");
MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
MODULE_DESCRIPTION("Periodic hrtimer trigger for the IIO subsystem");
MODULE_LICENSE("GPL v2");

View File

@ -25,5 +25,13 @@ ion/
exposes existing cma regions and doesn't reserve unecessarily memory when
booting a system which doesn't use ion.
sync framework:
- remove CONFIG_SW_SYNC_USER, it is used only for testing/debugging and
should not be upstreamed.
- port CONFIG_SW_SYNC_USER tests interfaces to use debugfs somehow
- port libsync tests to kselftest
- clean up and ABI check for security issues
- move it to drivers/base/dma-buf
Please send patches to Greg Kroah-Hartman <greg@kroah.com> and Cc:
Arve Hjønnevåg <arve@android.com> and Riley Andrews <riandrews@android.com>

View File

@ -831,14 +831,14 @@ static struct miscdevice ashmem_misc = {
static int __init ashmem_init(void)
{
int ret;
int ret = -ENOMEM;
ashmem_area_cachep = kmem_cache_create("ashmem_area_cache",
sizeof(struct ashmem_area),
0, 0, NULL);
if (unlikely(!ashmem_area_cachep)) {
pr_err("failed to create slab cache\n");
return -ENOMEM;
goto out;
}
ashmem_range_cachep = kmem_cache_create("ashmem_range_cache",
@ -846,13 +846,13 @@ static int __init ashmem_init(void)
0, 0, NULL);
if (unlikely(!ashmem_range_cachep)) {
pr_err("failed to create slab cache\n");
return -ENOMEM;
goto out_free1;
}
ret = misc_register(&ashmem_misc);
if (unlikely(ret)) {
pr_err("failed to register misc device!\n");
return ret;
goto out_free2;
}
register_shrinker(&ashmem_shrinker);
@ -860,5 +860,12 @@ static int __init ashmem_init(void)
pr_info("initialized\n");
return 0;
out_free2:
kmem_cache_destroy(ashmem_range_cachep);
out_free1:
kmem_cache_destroy(ashmem_area_cachep);
out:
return ret;
}
device_initcall(ashmem_init);

View File

@ -33,3 +33,10 @@ config ION_TEGRA
help
Choose this option if you wish to use ion on an nVidia Tegra.
config ION_HISI
tristate "Ion for Hisilicon"
depends on ARCH_HISI && ION
help
Choose this option if you wish to use ion on Hisilicon Platform.
source "drivers/staging/android/ion/hisilicon/Kconfig"

View File

@ -7,4 +7,5 @@ endif
obj-$(CONFIG_ION_DUMMY) += ion_dummy_driver.o
obj-$(CONFIG_ION_TEGRA) += tegra/
obj-$(CONFIG_ION_HISI) += hisilicon/

View File

@ -137,7 +137,7 @@ long compat_ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
data32 = compat_ptr(arg);
data = compat_alloc_user_space(sizeof(*data));
if (data == NULL)
if (!data)
return -EFAULT;
err = compat_get_ion_allocation_data(data32, data);
@ -156,7 +156,7 @@ long compat_ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
data32 = compat_ptr(arg);
data = compat_alloc_user_space(sizeof(*data));
if (data == NULL)
if (!data)
return -EFAULT;
err = compat_get_ion_handle_data(data32, data);
@ -173,7 +173,7 @@ long compat_ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
data32 = compat_ptr(arg);
data = compat_alloc_user_space(sizeof(*data));
if (data == NULL)
if (!data)
return -EFAULT;
err = compat_get_ion_custom_data(data32, data);

View File

@ -0,0 +1,5 @@
config HI6220_ION
bool "Hi6220 ION Driver"
depends on ARCH_HISI && ION
help
Build the Hisilicon Hi6220 ion driver.

View File

@ -0,0 +1 @@
obj-$(CONFIG_HI6220_ION) += hi6220_ion.o

View File

@ -0,0 +1,223 @@
/*
* Hisilicon Hi6220 ION Driver
*
* Copyright (c) 2015 Hisilicon Limited.
*
* Author: Chen Feng <puck.chen@hisilicon.com>
*
* 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.
*/
#define pr_fmt(fmt) "Ion: " fmt
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/mm.h>
#include "../ion_priv.h"
#include "../ion.h"
struct hi6220_ion_type_table {
const char *name;
enum ion_heap_type type;
};
static struct hi6220_ion_type_table ion_type_table[] = {
{"ion_system", ION_HEAP_TYPE_SYSTEM},
{"ion_system_contig", ION_HEAP_TYPE_SYSTEM_CONTIG},
{"ion_carveout", ION_HEAP_TYPE_CARVEOUT},
{"ion_chunk", ION_HEAP_TYPE_CHUNK},
{"ion_dma", ION_HEAP_TYPE_DMA},
{"ion_custom", ION_HEAP_TYPE_CUSTOM},
};
static struct ion_device *idev;
static int num_heaps;
static struct ion_heap **heaps;
static struct ion_platform_heap **heaps_data;
static int get_type_by_name(const char *name, enum ion_heap_type *type)
{
int i;
for (i = 0; i < ARRAY_SIZE(ion_type_table); i++) {
if (strncmp(name, ion_type_table[i].name, strlen(name)))
continue;
*type = ion_type_table[i].type;
return 0;
}
return -EINVAL;
}
static int hi6220_set_platform_data(struct platform_device *pdev)
{
unsigned int base;
unsigned int size;
unsigned int id;
const char *heap_name;
const char *type_name;
enum ion_heap_type type;
int ret;
struct device_node *np;
struct ion_platform_heap *p_data;
const struct device_node *dt_node = pdev->dev.of_node;
int index = 0;
for_each_child_of_node(dt_node, np)
num_heaps++;
heaps_data = devm_kzalloc(&pdev->dev,
sizeof(struct ion_platform_heap *) *
num_heaps,
GFP_KERNEL);
if (!heaps_data)
return -ENOMEM;
for_each_child_of_node(dt_node, np) {
ret = of_property_read_string(np, "heap-name", &heap_name);
if (ret < 0) {
pr_err("check the name of node %s\n", np->name);
continue;
}
ret = of_property_read_u32(np, "heap-id", &id);
if (ret < 0) {
pr_err("check the id %s\n", np->name);
continue;
}
ret = of_property_read_u32(np, "heap-base", &base);
if (ret < 0) {
pr_err("check the base of node %s\n", np->name);
continue;
}
ret = of_property_read_u32(np, "heap-size", &size);
if (ret < 0) {
pr_err("check the size of node %s\n", np->name);
continue;
}
ret = of_property_read_string(np, "heap-type", &type_name);
if (ret < 0) {
pr_err("check the type of node %s\n", np->name);
continue;
}
ret = get_type_by_name(type_name, &type);
if (ret < 0) {
pr_err("type name error %s!\n", type_name);
continue;
}
pr_info("heap index %d : name %s base 0x%x size 0x%x id %d type %d\n",
index, heap_name, base, size, id, type);
p_data = devm_kzalloc(&pdev->dev,
sizeof(struct ion_platform_heap),
GFP_KERNEL);
if (!p_data)
return -ENOMEM;
p_data->name = heap_name;
p_data->base = base;
p_data->size = size;
p_data->id = id;
p_data->type = type;
heaps_data[index] = p_data;
index++;
}
return 0;
}
static int hi6220_ion_probe(struct platform_device *pdev)
{
int i;
int err;
static struct ion_platform_heap *p_heap;
idev = ion_device_create(NULL);
err = hi6220_set_platform_data(pdev);
if (err) {
pr_err("ion set platform data error!\n");
goto err_free_idev;
}
heaps = devm_kzalloc(&pdev->dev,
sizeof(struct ion_heap *) * num_heaps,
GFP_KERNEL);
if (!heaps) {
err = -ENOMEM;
goto err_free_idev;
}
/*
* create the heaps as specified in the dts file
*/
for (i = 0; i < num_heaps; i++) {
p_heap = heaps_data[i];
heaps[i] = ion_heap_create(p_heap);
if (IS_ERR_OR_NULL(heaps[i])) {
err = PTR_ERR(heaps[i]);
goto err_free_heaps;
}
ion_device_add_heap(idev, heaps[i]);
pr_info("%s: adding heap %s of type %d with %lx@%lx\n",
__func__, p_heap->name, p_heap->type,
p_heap->base, (unsigned long)p_heap->size);
}
return err;
err_free_heaps:
for (i = 0; i < num_heaps; ++i) {
ion_heap_destroy(heaps[i]);
heaps[i] = NULL;
}
err_free_idev:
ion_device_destroy(idev);
return err;
}
static int hi6220_ion_remove(struct platform_device *pdev)
{
int i;
for (i = 0; i < num_heaps; i++) {
ion_heap_destroy(heaps[i]);
heaps[i] = NULL;
}
ion_device_destroy(idev);
return 0;
}
static const struct of_device_id hi6220_ion_match_table[] = {
{.compatible = "hisilicon,hi6220-ion"},
{},
};
static struct platform_driver hi6220_ion_driver = {
.probe = hi6220_ion_probe,
.remove = hi6220_ion_remove,
.driver = {
.name = "ion-hi6220",
.of_match_table = hi6220_ion_match_table,
},
};
static int __init hi6220_ion_init(void)
{
int ret;
ret = platform_driver_register(&hi6220_ion_driver);
return ret;
}
subsys_initcall(hi6220_ion_init);

View File

@ -43,7 +43,7 @@
#include <linux/profile.h>
#include <linux/notifier.h>
static uint32_t lowmem_debug_level = 1;
static u32 lowmem_debug_level = 1;
static short lowmem_adj[6] = {
0,
1,
@ -105,8 +105,8 @@ static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
}
lowmem_print(3, "lowmem_scan %lu, %x, ofree %d %d, ma %hd\n",
sc->nr_to_scan, sc->gfp_mask, other_free,
other_file, min_score_adj);
sc->nr_to_scan, sc->gfp_mask, other_free,
other_file, min_score_adj);
if (min_score_adj == OOM_SCORE_ADJ_MAX + 1) {
lowmem_print(5, "lowmem_scan %lu, %x, return 0\n",

View File

@ -43,7 +43,7 @@ struct sync_timeline *sync_timeline_create(const struct sync_timeline_ops *ops,
return NULL;
obj = kzalloc(size, GFP_KERNEL);
if (obj == NULL)
if (!obj)
return NULL;
kref_init(&obj->kref);
@ -130,7 +130,7 @@ struct sync_pt *sync_pt_create(struct sync_timeline *obj, int size)
return NULL;
pt = kzalloc(size, GFP_KERNEL);
if (pt == NULL)
if (!pt)
return NULL;
spin_lock_irqsave(&obj->child_list_lock, flags);
@ -155,7 +155,7 @@ static struct sync_fence *sync_fence_alloc(int size, const char *name)
struct sync_fence *fence;
fence = kzalloc(size, GFP_KERNEL);
if (fence == NULL)
if (!fence)
return NULL;
fence->file = anon_inode_getfile("sync_fence", &sync_fence_fops,
@ -188,34 +188,39 @@ static void fence_check_cb_func(struct fence *f, struct fence_cb *cb)
}
/* TODO: implement a create which takes more that one sync_pt */
struct sync_fence *sync_fence_create(const char *name, struct sync_pt *pt)
struct sync_fence *sync_fence_create_dma(const char *name, struct fence *pt)
{
struct sync_fence *fence;
fence = sync_fence_alloc(offsetof(struct sync_fence, cbs[1]), name);
if (fence == NULL)
if (!fence)
return NULL;
fence->num_fences = 1;
atomic_set(&fence->status, 1);
fence->cbs[0].sync_pt = &pt->base;
fence->cbs[0].sync_pt = pt;
fence->cbs[0].fence = fence;
if (fence_add_callback(&pt->base, &fence->cbs[0].cb,
fence_check_cb_func))
if (fence_add_callback(pt, &fence->cbs[0].cb, fence_check_cb_func))
atomic_dec(&fence->status);
sync_fence_debug_add(fence);
return fence;
}
EXPORT_SYMBOL(sync_fence_create_dma);
struct sync_fence *sync_fence_create(const char *name, struct sync_pt *pt)
{
return sync_fence_create_dma(name, &pt->base);
}
EXPORT_SYMBOL(sync_fence_create);
struct sync_fence *sync_fence_fdget(int fd)
{
struct file *file = fget(fd);
if (file == NULL)
if (!file)
return NULL;
if (file->f_op != &sync_fence_fops)
@ -262,7 +267,7 @@ struct sync_fence *sync_fence_merge(const char *name,
unsigned long size = offsetof(struct sync_fence, cbs[num_fences]);
fence = sync_fence_alloc(size, name);
if (fence == NULL)
if (!fence)
return NULL;
atomic_set(&fence->status, num_fences);
@ -313,7 +318,7 @@ struct sync_fence *sync_fence_merge(const char *name,
EXPORT_SYMBOL(sync_fence_merge);
int sync_fence_wake_up_wq(wait_queue_t *curr, unsigned mode,
int wake_flags, void *key)
int wake_flags, void *key)
{
struct sync_fence_waiter *wait;
@ -353,7 +358,7 @@ int sync_fence_wait_async(struct sync_fence *fence,
EXPORT_SYMBOL(sync_fence_wait_async);
int sync_fence_cancel_async(struct sync_fence *fence,
struct sync_fence_waiter *waiter)
struct sync_fence_waiter *waiter)
{
unsigned long flags;
int ret = 0;
@ -519,12 +524,10 @@ static const struct fence_ops android_fence_ops = {
static void sync_fence_free(struct kref *kref)
{
struct sync_fence *fence = container_of(kref, struct sync_fence, kref);
int i, status = atomic_read(&fence->status);
int i;
for (i = 0; i < fence->num_fences; ++i) {
if (status)
fence_remove_callback(fence->cbs[i].sync_pt,
&fence->cbs[i].cb);
fence_remove_callback(fence->cbs[i].sync_pt, &fence->cbs[i].cb);
fence_put(fence->cbs[i].sync_pt);
}
@ -583,14 +586,14 @@ static long sync_fence_ioctl_merge(struct sync_fence *fence, unsigned long arg)
}
fence2 = sync_fence_fdget(data.fd2);
if (fence2 == NULL) {
if (!fence2) {
err = -ENOENT;
goto err_put_fd;
}
data.name[sizeof(data.name) - 1] = '\0';
fence3 = sync_fence_merge(data.name, fence, fence2);
if (fence3 == NULL) {
if (!fence3) {
err = -ENOMEM;
goto err_put_fence2;
}
@ -666,7 +669,7 @@ static long sync_fence_ioctl_fence_info(struct sync_fence *fence,
size = 4096;
data = kzalloc(size, GFP_KERNEL);
if (data == NULL)
if (!data)
return -ENOMEM;
strlcpy(data->name, fence->name, sizeof(data->name));

View File

@ -254,6 +254,16 @@ void sync_pt_free(struct sync_pt *pt);
*/
struct sync_fence *sync_fence_create(const char *name, struct sync_pt *pt);
/**
* sync_fence_create_dma() - creates a sync fence from dma-fence
* @name: name of fence to create
* @pt: dma-fence to add to the fence
*
* Creates a fence containg @pt. Once this is called, the fence takes
* ownership of @pt.
*/
struct sync_fence *sync_fence_create_dma(const char *name, struct fence *pt);
/*
* API for sync_fence consumers
*/

View File

@ -82,36 +82,42 @@ static const char *sync_status_str(int status)
return "error";
}
static void sync_print_pt(struct seq_file *s, struct sync_pt *pt, bool fence)
static void sync_print_pt(struct seq_file *s, struct fence *pt, bool fence)
{
int status = 1;
struct sync_timeline *parent = sync_pt_parent(pt);
if (fence_is_signaled_locked(&pt->base))
status = pt->base.status;
if (fence_is_signaled_locked(pt))
status = pt->status;
seq_printf(s, " %s%spt %s",
fence ? parent->name : "",
fence && pt->ops->get_timeline_name ?
pt->ops->get_timeline_name(pt) : "",
fence ? "_" : "",
sync_status_str(status));
if (status <= 0) {
struct timespec64 ts64 =
ktime_to_timespec64(pt->base.timestamp);
ktime_to_timespec64(pt->timestamp);
seq_printf(s, "@%lld.%09ld", (s64)ts64.tv_sec, ts64.tv_nsec);
}
if (parent->ops->timeline_value_str &&
parent->ops->pt_value_str) {
if ((!fence || pt->ops->timeline_value_str) &&
pt->ops->fence_value_str) {
char value[64];
bool success;
parent->ops->pt_value_str(pt, value, sizeof(value));
seq_printf(s, ": %s", value);
if (fence) {
parent->ops->timeline_value_str(parent, value,
sizeof(value));
seq_printf(s, " / %s", value);
pt->ops->fence_value_str(pt, value, sizeof(value));
success = strlen(value);
if (success)
seq_printf(s, ": %s", value);
if (success && fence) {
pt->ops->timeline_value_str(pt, value, sizeof(value));
if (strlen(value))
seq_printf(s, " / %s", value);
}
}
@ -138,7 +144,7 @@ static void sync_print_obj(struct seq_file *s, struct sync_timeline *obj)
list_for_each(pos, &obj->child_list_head) {
struct sync_pt *pt =
container_of(pos, struct sync_pt, child_list);
sync_print_pt(s, pt, false);
sync_print_pt(s, &pt->base, false);
}
spin_unlock_irqrestore(&obj->child_list_lock, flags);
}
@ -153,11 +159,7 @@ static void sync_print_fence(struct seq_file *s, struct sync_fence *fence)
sync_status_str(atomic_read(&fence->status)));
for (i = 0; i < fence->num_fences; ++i) {
struct sync_pt *pt =
container_of(fence->cbs[i].sync_pt,
struct sync_pt, base);
sync_print_pt(s, pt, true);
sync_print_pt(s, fence->cbs[i].sync_pt, true);
}
spin_lock_irqsave(&fence->wq.lock, flags);

View File

@ -25,7 +25,6 @@
#include "timed_output.h"
#include "timed_gpio.h"
struct timed_gpio_data {
struct timed_output_dev dev;
struct hrtimer timer;
@ -76,8 +75,8 @@ static void gpio_enable(struct timed_output_dev *dev, int value)
value = data->max_timeout;
hrtimer_start(&data->timer,
ktime_set(value / 1000, (value % 1000) * 1000000),
HRTIMER_MODE_REL);
ktime_set(value / 1000, (value % 1000) * 1000000),
HRTIMER_MODE_REL);
}
spin_unlock_irqrestore(&data->lock, flags);
@ -94,8 +93,8 @@ static int timed_gpio_probe(struct platform_device *pdev)
return -EBUSY;
gpio_data = devm_kzalloc(&pdev->dev,
sizeof(struct timed_gpio_data) * pdata->num_gpios,
GFP_KERNEL);
sizeof(*gpio_data) * pdata->num_gpios,
GFP_KERNEL);
if (!gpio_data)
return -ENOMEM;
@ -104,7 +103,7 @@ static int timed_gpio_probe(struct platform_device *pdev)
gpio_dat = &gpio_data[i];
hrtimer_init(&gpio_dat->timer, CLOCK_MONOTONIC,
HRTIMER_MODE_REL);
HRTIMER_MODE_REL);
gpio_dat->timer.function = gpio_timer_func;
spin_lock_init(&gpio_dat->lock);

View File

@ -737,15 +737,23 @@ config COMEDI_ADL_PCI9118
called adl_pci9118.
config COMEDI_ADV_PCI1710
tristate "Advantech PCI-171x, PCI-1720 and PCI-1731 support"
tristate "Advantech PCI-171x and PCI-1731 support"
select COMEDI_8254
---help---
Enable support for Advantech PCI-1710, PCI-1710HG, PCI-1711,
PCI-1713, PCI-1720 and PCI-1731
PCI-1713 and PCI-1731
To compile this driver as a module, choose M here: the module will be
called adv_pci1710.
config COMEDI_ADV_PCI1720
tristate "Advantech PCI-1720 support"
---help---
Enable support for Advantech PCI-1720 Analog Output board.
To compile this driver as a module, choose M here: the module will be
called adv_pci1720.
config COMEDI_ADV_PCI1723
tristate "Advantech PCI-1723 support"
---help---
@ -764,6 +772,14 @@ config COMEDI_ADV_PCI1724
To compile this driver as a module, choose M here: the module will be
called adv_pci1724.
config COMEDI_ADV_PCI1760
tristate "Advantech PCI-1760 support"
---help---
Enable support for Advantech PCI-1760 board.
To compile this driver as a module, choose M here: the module will be
called adv_pci1760.
config COMEDI_ADV_PCI_DIO
tristate "Advantech PCI DIO card support"
select COMEDI_8254
@ -771,8 +787,8 @@ config COMEDI_ADV_PCI_DIO
---help---
Enable support for Advantech PCI DIO cards
PCI-1730, PCI-1733, PCI-1734, PCI-1735U, PCI-1736UP, PCI-1739U,
PCI-1750, PCI-1751, PCI-1752, PCI-1753/E, PCI-1754, PCI-1756,
PCI-1760 and PCI-1762
PCI-1750, PCI-1751, PCI-1752, PCI-1753/E, PCI-1754, PCI-1756 and
PCI-1762
To compile this driver as a module, choose M here: the module will be
called adv_pci_dio.

View File

@ -1,20 +1,20 @@
/*
include/comedi.h (installed as /usr/include/comedi.h)
header file for comedi
COMEDI - Linux Control and Measurement Device Interface
Copyright (C) 1998-2001 David A. Schleef <ds@schleef.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser 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/comedi.h (installed as /usr/include/comedi.h)
* header file for comedi
*
* COMEDI - Linux Control and Measurement Device Interface
* Copyright (C) 1998-2001 David A. Schleef <ds@schleef.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser 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.
*/
#ifndef _COMEDI_H
#define _COMEDI_H
@ -28,9 +28,9 @@
#define COMEDI_MAJOR 98
/*
maximum number of minor devices. This can be increased, although
kernel structures are currently statically allocated, thus you
don't want this to be much more than you actually use.
* maximum number of minor devices. This can be increased, although
* kernel structures are currently statically allocated, thus you
* don't want this to be much more than you actually use.
*/
#define COMEDI_NDEVICES 16
@ -63,21 +63,21 @@
/* packs and unpacks a channel/range number */
#define CR_PACK(chan, rng, aref) \
((((aref)&0x3)<<24) | (((rng)&0xff)<<16) | (chan))
((((aref) & 0x3) << 24) | (((rng) & 0xff) << 16) | (chan))
#define CR_PACK_FLAGS(chan, range, aref, flags) \
(CR_PACK(chan, range, aref) | ((flags) & CR_FLAGS_MASK))
#define CR_CHAN(a) ((a)&0xffff)
#define CR_RANGE(a) (((a)>>16)&0xff)
#define CR_AREF(a) (((a)>>24)&0x03)
#define CR_CHAN(a) ((a) & 0xffff)
#define CR_RANGE(a) (((a) >> 16) & 0xff)
#define CR_AREF(a) (((a) >> 24) & 0x03)
#define CR_FLAGS_MASK 0xfc000000
#define CR_ALT_FILTER (1<<26)
#define CR_ALT_FILTER (1 << 26)
#define CR_DITHER CR_ALT_FILTER
#define CR_DEGLITCH CR_ALT_FILTER
#define CR_ALT_SOURCE (1<<27)
#define CR_EDGE (1<<30)
#define CR_INVERT (1<<31)
#define CR_ALT_SOURCE (1 << 27)
#define CR_EDGE (1 << 30)
#define CR_INVERT (1 << 31)
#define AREF_GROUND 0x00 /* analog ref = analog ground */
#define AREF_COMMON 0x01 /* analog ref = analog common */
@ -114,11 +114,11 @@
#define INSN_READ (0 | INSN_MASK_READ)
#define INSN_WRITE (1 | INSN_MASK_WRITE)
#define INSN_BITS (2 | INSN_MASK_READ|INSN_MASK_WRITE)
#define INSN_CONFIG (3 | INSN_MASK_READ|INSN_MASK_WRITE)
#define INSN_GTOD (4 | INSN_MASK_READ|INSN_MASK_SPECIAL)
#define INSN_WAIT (5 | INSN_MASK_WRITE|INSN_MASK_SPECIAL)
#define INSN_INTTRIG (6 | INSN_MASK_WRITE|INSN_MASK_SPECIAL)
#define INSN_BITS (2 | INSN_MASK_READ | INSN_MASK_WRITE)
#define INSN_CONFIG (3 | INSN_MASK_READ | INSN_MASK_WRITE)
#define INSN_GTOD (4 | INSN_MASK_READ | INSN_MASK_SPECIAL)
#define INSN_WAIT (5 | INSN_MASK_WRITE | INSN_MASK_SPECIAL)
#define INSN_INTTRIG (6 | INSN_MASK_WRITE | INSN_MASK_SPECIAL)
/* trigger flags */
/* These flags are used in comedi_trig structures */
@ -279,7 +279,8 @@ enum configuration_ids {
INSN_CONFIG_SET_OTHER_SRC = 2005, /* Set other source */
/* INSN_CONFIG_GET_OTHER_SRC = 2006,*//* Get other source */
/* Get size in bytes of subdevice's on-board fifos used during
* streaming input/output */
* streaming input/output
*/
INSN_CONFIG_GET_HARDWARE_BUFFER_SIZE = 2006,
INSN_CONFIG_SET_COUNTER_MODE = 4097,
/* INSN_CONFIG_8254_SET_MODE is deprecated */
@ -292,7 +293,8 @@ enum configuration_ids {
INSN_CONFIG_PWM_GET_PERIOD = 5001, /* gets frequency */
INSN_CONFIG_GET_PWM_STATUS = 5002, /* is it running? */
/* sets H bridge: duty cycle and sign bit for a relay at the
* same time */
* same time
*/
INSN_CONFIG_PWM_SET_H_BRIDGE = 5003,
/* gets H bridge data: duty cycle and the sign bit */
INSN_CONFIG_PWM_GET_H_BRIDGE = 5004
@ -502,13 +504,13 @@ struct comedi_bufinfo {
/* range stuff */
#define __RANGE(a, b) ((((a)&0xffff)<<16)|((b)&0xffff))
#define __RANGE(a, b) ((((a) & 0xffff) << 16) | ((b) & 0xffff))
#define RANGE_OFFSET(a) (((a)>>16)&0xffff)
#define RANGE_LENGTH(b) ((b)&0xffff)
#define RANGE_OFFSET(a) (((a) >> 16) & 0xffff)
#define RANGE_LENGTH(b) ((b) & 0xffff)
#define RF_UNIT(flags) ((flags)&0xff)
#define RF_EXTERNAL (1<<8)
#define RF_UNIT(flags) ((flags) & 0xff)
#define RF_EXTERNAL (1 << 8)
#define UNIT_volt 0
#define UNIT_mA 1
@ -521,23 +523,22 @@ struct comedi_bufinfo {
/**********************************************************/
/*
8254 specific configuration.
It supports two config commands:
0 ID: INSN_CONFIG_SET_COUNTER_MODE
1 8254 Mode
I8254_MODE0, I8254_MODE1, ..., I8254_MODE5
OR'ed with:
I8254_BCD, I8254_BINARY
0 ID: INSN_CONFIG_8254_READ_STATUS
1 <-- Status byte returned here.
B7 = Output
B6 = NULL Count
B5 - B0 Current mode.
*/
* 8254 specific configuration.
*
* It supports two config commands:
*
* 0 ID: INSN_CONFIG_SET_COUNTER_MODE
* 1 8254 Mode
* I8254_MODE0, I8254_MODE1, ..., I8254_MODE5
* OR'ed with:
* I8254_BCD, I8254_BINARY
*
* 0 ID: INSN_CONFIG_8254_READ_STATUS
* 1 <-- Status byte returned here.
* B7 = Output
* B6 = NULL Count
* B5 - B0 Current mode.
*/
enum i8254_mode {
I8254_MODE0 = (0 << 1), /* Interrupt on terminal count */
@ -545,18 +546,20 @@ enum i8254_mode {
I8254_MODE2 = (2 << 1), /* Rate generator */
I8254_MODE3 = (3 << 1), /* Square wave mode */
I8254_MODE4 = (4 << 1), /* Software triggered strobe */
I8254_MODE5 = (5 << 1), /* Hardware triggered strobe
* (retriggerable) */
I8254_BCD = 1, /* use binary-coded decimal instead of binary
* (pretty useless) */
/* Hardware triggered strobe (retriggerable) */
I8254_MODE5 = (5 << 1),
/* Use binary-coded decimal instead of binary (pretty useless) */
I8254_BCD = 1,
I8254_BINARY = 0
};
#define NI_USUAL_PFI_SELECT(x) (((x) < 10) ? (0x1 + (x)) : (0xb + (x)))
#define NI_USUAL_RTSI_SELECT(x) (((x) < 7) ? (0xb + (x)) : 0x1b)
/* mode bits for NI general-purpose counters, set with
* INSN_CONFIG_SET_COUNTER_MODE */
/*
* mode bits for NI general-purpose counters, set with
* INSN_CONFIG_SET_COUNTER_MODE
*/
#define NI_GPCT_COUNTING_MODE_SHIFT 16
#define NI_GPCT_INDEX_PHASE_BITSHIFT 20
#define NI_GPCT_COUNTING_DIRECTION_SHIFT 24
@ -624,8 +627,10 @@ enum ni_gpct_mode_bits {
NI_GPCT_INVERT_OUTPUT_BIT = 0x20000000
};
/* Bits for setting a clock source with
* INSN_CONFIG_SET_CLOCK_SRC when using NI general-purpose counters. */
/*
* Bits for setting a clock source with
* INSN_CONFIG_SET_CLOCK_SRC when using NI general-purpose counters.
*/
enum ni_gpct_clock_source_bits {
NI_GPCT_CLOCK_SRC_SELECT_MASK = 0x3f,
NI_GPCT_TIMEBASE_1_CLOCK_SRC_BITS = 0x0,
@ -656,9 +661,11 @@ enum ni_gpct_clock_source_bits {
/* no pfi on NI 660x */
#define NI_GPCT_PFI_CLOCK_SRC_BITS(x) (0x20 + (x))
/* Possibilities for setting a gate source with
INSN_CONFIG_SET_GATE_SRC when using NI general-purpose counters.
May be bitwise-or'd with CR_EDGE or CR_INVERT. */
/*
* Possibilities for setting a gate source with
* INSN_CONFIG_SET_GATE_SRC when using NI general-purpose counters.
* May be bitwise-or'd with CR_EDGE or CR_INVERT.
*/
enum ni_gpct_gate_select {
/* m-series gates */
NI_GPCT_TIMESTAMP_MUX_GATE_SELECT = 0x0,
@ -675,9 +682,11 @@ enum ni_gpct_gate_select {
/* more gates for 660x "second gate" */
NI_GPCT_UP_DOWN_PIN_i_GATE_SELECT = 0x201,
NI_GPCT_SELECTED_GATE_GATE_SELECT = 0x21e,
/* m-series "second gate" sources are unknown,
/*
* m-series "second gate" sources are unknown,
* we should add them here with an offset of 0x300 when
* known. */
* known.
*/
NI_GPCT_DISABLED_GATE_SELECT = 0x8000,
};
@ -686,8 +695,10 @@ enum ni_gpct_gate_select {
#define NI_GPCT_PFI_GATE_SELECT(x) NI_USUAL_PFI_SELECT(x)
#define NI_GPCT_UP_DOWN_PIN_GATE_SELECT(x) (0x202 + (x))
/* Possibilities for setting a source with
INSN_CONFIG_SET_OTHER_SRC when using NI general-purpose counters. */
/*
* Possibilities for setting a source with
* INSN_CONFIG_SET_OTHER_SRC when using NI general-purpose counters.
*/
enum ni_gpct_other_index {
NI_GPCT_SOURCE_ENCODER_A,
NI_GPCT_SOURCE_ENCODER_B,
@ -702,18 +713,24 @@ enum ni_gpct_other_select {
#define NI_GPCT_PFI_OTHER_SELECT(x) NI_USUAL_PFI_SELECT(x)
/* start sources for ni general-purpose counters for use with
INSN_CONFIG_ARM */
/*
* start sources for ni general-purpose counters for use with
* INSN_CONFIG_ARM
*/
enum ni_gpct_arm_source {
NI_GPCT_ARM_IMMEDIATE = 0x0,
NI_GPCT_ARM_PAIRED_IMMEDIATE = 0x1, /* Start both the counter
* and the adjacent paired
* counter simultaneously */
/* NI doesn't document bits for selecting hardware arm triggers.
/*
* Start both the counter and the adjacent pared
* counter simultaneously
*/
NI_GPCT_ARM_PAIRED_IMMEDIATE = 0x1,
/*
* NI doesn't document bits for selecting hardware arm triggers.
* If the NI_GPCT_ARM_UNKNOWN bit is set, we will pass the least
* significant bits (3 bits for 660x or 5 bits for m-series)
* through to the hardware. This will at least allow someone to
* figure out what the bits do later. */
* figure out what the bits do later.
*/
NI_GPCT_ARM_UNKNOWN = 0x1000,
};
@ -728,8 +745,10 @@ enum ni_gpct_filter_select {
NI_GPCT_FILTER_2x_TIMEBASE_3 = 0x6
};
/* PFI digital filtering options for ni m-series for use with
* INSN_CONFIG_FILTER. */
/*
* PFI digital filtering options for ni m-series for use with
* INSN_CONFIG_FILTER.
*/
enum ni_pfi_filter_select {
NI_PFI_FILTER_OFF = 0x0,
NI_PFI_FILTER_125ns = 0x1,
@ -740,9 +759,11 @@ enum ni_pfi_filter_select {
/* master clock sources for ni mio boards and INSN_CONFIG_SET_CLOCK_SRC */
enum ni_mio_clock_source {
NI_MIO_INTERNAL_CLOCK = 0,
NI_MIO_RTSI_CLOCK = 1, /* doesn't work for m-series, use
NI_MIO_PLL_RTSI_CLOCK() */
/* the NI_MIO_PLL_* sources are m-series only */
/*
* Doesn't work for m-series, use NI_MIO_PLL_RTSI_CLOCK()
* the NI_MIO_PLL_* sources are m-series only
*/
NI_MIO_RTSI_CLOCK = 1,
NI_MIO_PLL_PXI_STAR_TRIGGER_CLOCK = 2,
NI_MIO_PLL_PXI10_CLOCK = 3,
NI_MIO_PLL_RTSI0_CLOCK = 4
@ -750,9 +771,11 @@ enum ni_mio_clock_source {
#define NI_MIO_PLL_RTSI_CLOCK(x) (NI_MIO_PLL_RTSI0_CLOCK + (x))
/* Signals which can be routed to an NI RTSI pin with INSN_CONFIG_SET_ROUTING.
The numbers assigned are not arbitrary, they correspond to the bits required
to program the board. */
/*
* Signals which can be routed to an NI RTSI pin with INSN_CONFIG_SET_ROUTING.
* The numbers assigned are not arbitrary, they correspond to the bits required
* to program the board.
*/
enum ni_rtsi_routing {
NI_RTSI_OUTPUT_ADR_START1 = 0,
NI_RTSI_OUTPUT_ADR_START2 = 1,
@ -763,17 +786,19 @@ enum ni_rtsi_routing {
NI_RTSI_OUTPUT_G_GATE0 = 6,
NI_RTSI_OUTPUT_RGOUT0 = 7,
NI_RTSI_OUTPUT_RTSI_BRD_0 = 8,
NI_RTSI_OUTPUT_RTSI_OSC = 12 /* pre-m-series always have RTSI
* clock on line 7 */
/* Pre-m-series always have RTSI clock on line 7 */
NI_RTSI_OUTPUT_RTSI_OSC = 12
};
#define NI_RTSI_OUTPUT_RTSI_BRD(x) (NI_RTSI_OUTPUT_RTSI_BRD_0 + (x))
/* Signals which can be routed to an NI PFI pin on an m-series board with
/*
* Signals which can be routed to an NI PFI pin on an m-series board with
* INSN_CONFIG_SET_ROUTING. These numbers are also returned by
* INSN_CONFIG_GET_ROUTING on pre-m-series boards, even though their routing
* cannot be changed. The numbers assigned are not arbitrary, they correspond
* to the bits required to program the board. */
* to the bits required to program the board.
*/
enum ni_pfi_routing {
NI_PFI_OUTPUT_PFI_DEFAULT = 0,
NI_PFI_OUTPUT_AI_START1 = 1,
@ -803,20 +828,24 @@ enum ni_pfi_routing {
#define NI_PFI_OUTPUT_RTSI(x) (NI_PFI_OUTPUT_RTSI0 + (x))
/* Signals which can be routed to output on a NI PFI pin on a 660x board
with INSN_CONFIG_SET_ROUTING. The numbers assigned are
not arbitrary, they correspond to the bits required
to program the board. Lines 0 to 7 can only be set to
NI_660X_PFI_OUTPUT_DIO. Lines 32 to 39 can only be set to
NI_660X_PFI_OUTPUT_COUNTER. */
/*
* Signals which can be routed to output on a NI PFI pin on a 660x board
* with INSN_CONFIG_SET_ROUTING. The numbers assigned are
* not arbitrary, they correspond to the bits required
* to program the board. Lines 0 to 7 can only be set to
* NI_660X_PFI_OUTPUT_DIO. Lines 32 to 39 can only be set to
* NI_660X_PFI_OUTPUT_COUNTER.
*/
enum ni_660x_pfi_routing {
NI_660X_PFI_OUTPUT_COUNTER = 1, /* counter */
NI_660X_PFI_OUTPUT_DIO = 2, /* static digital output */
};
/* NI External Trigger lines. These values are not arbitrary, but are related
/*
* NI External Trigger lines. These values are not arbitrary, but are related
* to the bits required to program the board (offset by 1 for historical
* reasons). */
* reasons).
*/
#define NI_EXT_PFI(x) (NI_USUAL_PFI_SELECT(x) - 1)
#define NI_EXT_RTSI(x) (NI_USUAL_RTSI_SELECT(x) - 1)
@ -827,9 +856,11 @@ enum comedi_counter_status_flags {
COMEDI_COUNTER_TERMINAL_COUNT = 0x4,
};
/* Clock sources for CDIO subdevice on NI m-series boards. Used as the
/*
* Clock sources for CDIO subdevice on NI m-series boards. Used as the
* scan_begin_arg for a comedi_command. These sources may also be bitwise-or'd
* with CR_INVERT to change polarity. */
* with CR_INVERT to change polarity.
*/
enum ni_m_series_cdio_scan_begin_src {
NI_CDIO_SCAN_BEGIN_SRC_GROUND = 0,
NI_CDIO_SCAN_BEGIN_SRC_AI_START = 18,
@ -846,38 +877,50 @@ enum ni_m_series_cdio_scan_begin_src {
#define NI_CDIO_SCAN_BEGIN_SRC_PFI(x) NI_USUAL_PFI_SELECT(x)
#define NI_CDIO_SCAN_BEGIN_SRC_RTSI(x) NI_USUAL_RTSI_SELECT(x)
/* scan_begin_src for scan_begin_arg==TRIG_EXT with analog output command on NI
/*
* scan_begin_src for scan_begin_arg==TRIG_EXT with analog output command on NI
* boards. These scan begin sources can also be bitwise-or'd with CR_INVERT to
* change polarity. */
* change polarity.
*/
#define NI_AO_SCAN_BEGIN_SRC_PFI(x) NI_USUAL_PFI_SELECT(x)
#define NI_AO_SCAN_BEGIN_SRC_RTSI(x) NI_USUAL_RTSI_SELECT(x)
/* Bits for setting a clock source with
* INSN_CONFIG_SET_CLOCK_SRC when using NI frequency output subdevice. */
/*
* Bits for setting a clock source with
* INSN_CONFIG_SET_CLOCK_SRC when using NI frequency output subdevice.
*/
enum ni_freq_out_clock_source_bits {
NI_FREQ_OUT_TIMEBASE_1_DIV_2_CLOCK_SRC, /* 10 MHz */
NI_FREQ_OUT_TIMEBASE_2_CLOCK_SRC /* 100 KHz */
};
/* Values for setting a clock source with INSN_CONFIG_SET_CLOCK_SRC for
* 8254 counter subdevices on Amplicon DIO boards (amplc_dio200 driver). */
/*
* Values for setting a clock source with INSN_CONFIG_SET_CLOCK_SRC for
* 8254 counter subdevices on Amplicon DIO boards (amplc_dio200 driver).
*/
enum amplc_dio_clock_source {
AMPLC_DIO_CLK_CLKN, /* per channel external clock
input/output pin (pin is only an
input when clock source set to this
value, otherwise it is an output) */
/*
* Per channel external clock
* input/output pin (pin is only an
* input when clock source set to this value,
* otherwise it is an output)
*/
AMPLC_DIO_CLK_CLKN,
AMPLC_DIO_CLK_10MHZ, /* 10 MHz internal clock */
AMPLC_DIO_CLK_1MHZ, /* 1 MHz internal clock */
AMPLC_DIO_CLK_100KHZ, /* 100 kHz internal clock */
AMPLC_DIO_CLK_10KHZ, /* 10 kHz internal clock */
AMPLC_DIO_CLK_1KHZ, /* 1 kHz internal clock */
AMPLC_DIO_CLK_OUTNM1, /* output of preceding counter channel
(for channel 0, preceding counter
channel is channel 2 on preceding
counter subdevice, for first counter
subdevice, preceding counter
subdevice is the last counter
subdevice) */
/*
* Output of preceding counter channel
* (for channel 0, preceding counter
* channel is channel 2 on preceding
* counter subdevice, for first counter
* subdevice, preceding counter
* subdevice is the last counter
* subdevice)
*/
AMPLC_DIO_CLK_OUTNM1,
AMPLC_DIO_CLK_EXT, /* per chip external input pin */
/* the following are "enhanced" clock sources for PCIe models */
AMPLC_DIO_CLK_VCC, /* clock input HIGH */
@ -886,35 +929,39 @@ enum amplc_dio_clock_source {
AMPLC_DIO_CLK_20MHZ /* 20 MHz internal clock */
};
/* Values for setting a clock source with INSN_CONFIG_SET_CLOCK_SRC for
* timer subdevice on some Amplicon DIO PCIe boards (amplc_dio200 driver). */
/*
* Values for setting a clock source with INSN_CONFIG_SET_CLOCK_SRC for
* timer subdevice on some Amplicon DIO PCIe boards (amplc_dio200 driver).
*/
enum amplc_dio_ts_clock_src {
AMPLC_DIO_TS_CLK_1GHZ, /* 1 ns period with 20 ns granularity */
AMPLC_DIO_TS_CLK_1MHZ, /* 1 us period */
AMPLC_DIO_TS_CLK_1KHZ /* 1 ms period */
};
/* Values for setting a gate source with INSN_CONFIG_SET_GATE_SRC for
* 8254 counter subdevices on Amplicon DIO boards (amplc_dio200 driver). */
/*
* Values for setting a gate source with INSN_CONFIG_SET_GATE_SRC for
* 8254 counter subdevices on Amplicon DIO boards (amplc_dio200 driver).
*/
enum amplc_dio_gate_source {
AMPLC_DIO_GAT_VCC, /* internal high logic level */
AMPLC_DIO_GAT_GND, /* internal low logic level */
AMPLC_DIO_GAT_GATN, /* per channel external gate input */
AMPLC_DIO_GAT_NOUTNM2, /* negated output of counter channel
minus 2 (for channels 0 or 1,
channel minus 2 is channel 1 or 2 on
the preceding counter subdevice, for
the first counter subdevice the
preceding counter subdevice is the
last counter subdevice) */
/*
* negated output of counter channel minus 2
* (for channels 0 or 1, channel minus 2 is channel 1 or 2 on
* the preceding counter subdevice, for the first counter subdevice
* the preceding counter subdevice is the last counter subdevice)
*/
AMPLC_DIO_GAT_NOUTNM2,
AMPLC_DIO_GAT_RESERVED4,
AMPLC_DIO_GAT_RESERVED5,
AMPLC_DIO_GAT_RESERVED6,
AMPLC_DIO_GAT_RESERVED7,
/* the following are "enhanced" gate sources for PCIe models */
AMPLC_DIO_GAT_NGATN = 6, /* negated per channel gate input */
AMPLC_DIO_GAT_OUTNM2, /* non-negated output of counter
channel minus 2 */
/* non-negated output of counter channel minus 2 */
AMPLC_DIO_GAT_OUTNM2,
AMPLC_DIO_GAT_PAT_PRESENT, /* "pattern present" signal */
AMPLC_DIO_GAT_PAT_OCCURRED, /* "pattern occurred" latched */
AMPLC_DIO_GAT_PAT_GONE, /* "pattern gone away" latched */

View File

@ -2303,11 +2303,13 @@ static ssize_t comedi_write(struct file *file, const char __user *buf,
{
struct comedi_subdevice *s;
struct comedi_async *async;
int n, m, count = 0, retval = 0;
unsigned int n, m;
ssize_t count = 0;
int retval = 0;
DECLARE_WAITQUEUE(wait, current);
struct comedi_file *cfp = file->private_data;
struct comedi_device *dev = cfp->dev;
bool on_wait_queue = false;
bool become_nonbusy = false;
bool attach_locked;
unsigned int old_detach_count;
@ -2329,74 +2331,33 @@ static ssize_t comedi_write(struct file *file, const char __user *buf,
}
async = s->async;
if (!s->busy || !nbytes)
goto out;
if (s->busy != file) {
retval = -EACCES;
goto out;
}
if (!(async->cmd.flags & CMDF_WRITE)) {
if (s->busy != file || !(async->cmd.flags & CMDF_WRITE)) {
retval = -EINVAL;
goto out;
}
add_wait_queue(&async->wait_head, &wait);
on_wait_queue = true;
while (nbytes > 0 && !retval) {
while (count == 0 && !retval) {
unsigned runflags;
unsigned int wp, n1, n2;
set_current_state(TASK_INTERRUPTIBLE);
runflags = comedi_get_subdevice_runflags(s);
if (!comedi_is_runflags_running(runflags)) {
if (count == 0) {
struct comedi_subdevice *new_s;
if (comedi_is_runflags_in_error(runflags))
retval = -EPIPE;
else
retval = 0;
/*
* To avoid deadlock, cannot acquire dev->mutex
* while dev->attach_lock is held. Need to
* remove task from the async wait queue before
* releasing dev->attach_lock, as it might not
* be valid afterwards.
*/
remove_wait_queue(&async->wait_head, &wait);
on_wait_queue = false;
up_read(&dev->attach_lock);
attach_locked = false;
mutex_lock(&dev->mutex);
/*
* Become non-busy unless things have changed
* behind our back. Checking dev->detach_count
* is unchanged ought to be sufficient (unless
* there have been 2**32 detaches in the
* meantime!), but check the subdevice pointer
* as well just in case.
*/
new_s = comedi_file_write_subdevice(file);
if (dev->attached &&
old_detach_count == dev->detach_count &&
s == new_s && new_s->async == async)
do_become_nonbusy(dev, s);
mutex_unlock(&dev->mutex);
}
if (comedi_is_runflags_in_error(runflags))
retval = -EPIPE;
if (retval || nbytes)
become_nonbusy = true;
break;
}
if (nbytes == 0)
break;
n = nbytes;
m = n;
if (async->buf_write_ptr + m > async->prealloc_bufsz)
m = async->prealloc_bufsz - async->buf_write_ptr;
/* Allocate all free buffer space. */
comedi_buf_write_alloc(s, async->prealloc_bufsz);
if (m > comedi_buf_write_n_allocated(s))
m = comedi_buf_write_n_allocated(s);
if (m < n)
n = m;
m = comedi_buf_write_n_allocated(s);
n = min_t(size_t, m, nbytes);
if (n == 0) {
if (file->f_flags & O_NONBLOCK) {
@ -2408,21 +2369,22 @@ static ssize_t comedi_write(struct file *file, const char __user *buf,
retval = -ERESTARTSYS;
break;
}
if (!s->busy)
break;
if (s->busy != file) {
retval = -EACCES;
break;
}
if (!(async->cmd.flags & CMDF_WRITE)) {
if (s->busy != file ||
!(async->cmd.flags & CMDF_WRITE)) {
retval = -EINVAL;
break;
}
continue;
}
m = copy_from_user(async->prealloc_buf + async->buf_write_ptr,
buf, n);
wp = async->buf_write_ptr;
n1 = min(n, async->prealloc_bufsz - wp);
n2 = n - n1;
m = copy_from_user(async->prealloc_buf + wp, buf, n1);
if (m)
m += n2;
else if (n2)
m = copy_from_user(async->prealloc_buf, buf + n1, n2);
if (m) {
n -= m;
retval = -EFAULT;
@ -2433,12 +2395,38 @@ static ssize_t comedi_write(struct file *file, const char __user *buf,
nbytes -= n;
buf += n;
break; /* makes device work like a pipe */
}
remove_wait_queue(&async->wait_head, &wait);
set_current_state(TASK_RUNNING);
if (become_nonbusy && count == 0) {
struct comedi_subdevice *new_s;
/*
* To avoid deadlock, cannot acquire dev->mutex
* while dev->attach_lock is held.
*/
up_read(&dev->attach_lock);
attach_locked = false;
mutex_lock(&dev->mutex);
/*
* Check device hasn't become detached behind our back.
* Checking dev->detach_count is unchanged ought to be
* sufficient (unless there have been 2**32 detaches in the
* meantime!), but check the subdevice pointer as well just in
* case.
*
* Also check the subdevice is still in a suitable state to
* become non-busy in case it changed behind our back.
*/
new_s = comedi_file_write_subdevice(file);
if (dev->attached && old_detach_count == dev->detach_count &&
s == new_s && new_s->async == async && s->busy == file &&
(async->cmd.flags & CMDF_WRITE) &&
!comedi_is_subdevice_running(s))
do_become_nonbusy(dev, s);
mutex_unlock(&dev->mutex);
}
out:
if (on_wait_queue)
remove_wait_queue(&async->wait_head, &wait);
set_current_state(TASK_RUNNING);
if (attach_locked)
up_read(&dev->attach_lock);

Some files were not shown because too many files have changed in this diff Show More