提交 39272dde 编写于 作者: L Linus Torvalds

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
  ...
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.
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.
......@@ -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)
......
......@@ -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";
};
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>;
};
......@@ -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 {
......
* 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
......
* 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>;
};
};
...
};
* 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
......
* 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>;
};
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>;
};
......@@ -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.
......
......@@ -36,6 +36,7 @@ Accelerometers:
- st,lsm303dlm-accel
- st,lsm330-accel
- st,lsm303agr-accel
- st,lis2dh12-accel
Gyroscopes:
- st,l3g4200d-gyro
......
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";
};
};
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.
......@@ -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"
......
......@@ -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/
......
......@@ -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
......
......@@ -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
......
......@@ -1623,24 +1623,22 @@ int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq,
}
}
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;
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;
}
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);
......
......@@ -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);
......
/*
* 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
/*
* 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");
/*
* 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");
/*
* 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");
......@@ -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,
......
......@@ -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);
......
......@@ -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);
......
/*
* 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");
......@@ -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
......
......@@ -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 = {
......
......@@ -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);
......
......@@ -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);
......
......@@ -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
......
......@@ -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
......
......@@ -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 */
......
......@@ -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;
......
/*
* 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");
/*
* 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");
......@@ -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],
}, {
}
};
......
......@@ -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 },
......
此差异已折叠。
/*
* 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);
......
/*
* 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");
......@@ -803,7 +803,7 @@ static int xadc_preenable(struct iio_dev *indio_dev)
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,
......
......@@ -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
......
......@@ -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
此差异已折叠。
/*
* 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);
......@@ -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
......
......@@ -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
/*
* 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");
......@@ -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_get_measurement(struct vz89x_data *data)
static int vz89x_i2c_xfer(struct vz89x_data *data, u8 cmd)
{
struct i2c_client *client = data->client;
struct i2c_msg msg[2];
int ret;
int i;
u8 buf[3] = { cmd, 0, 0};
/* sensor can only be polled once a second max per datasheet */
if (!time_after(jiffies, data->last_update + HZ))
return 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);
ret = i2c_smbus_write_word_data(data->client,
VZ89X_REG_MEASUREMENT, 0);
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(data->client);
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;
/* sensor can only be polled once a second max per datasheet */
if (!time_after(jiffies, data->last_update + HZ))
return 0;
ret = data->xfer(data, VZ89X_REG_MEASUREMENT);
if (ret < 0)
return 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;
......
......@@ -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;
......
此差异已折叠。
#
# 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
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
obj-$(CONFIG_HI6220_ION) += hi6220_ion.o
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册