2
0
mirror of https://github.com/edk2-porting/linux-next.git synced 2024-11-17 15:14:35 +08:00

Staging driver patches for 4.6-rc1

Here is the big staging driver pull request for 4.6-rc1.
 
 Lots of little things here, over 1600 patches or so.  Notible is all of
 the good Lustre work happening, those developers have finally woken up
 and are cleaning up their code greatly.  The Outreachy intern
 application process is also happening, which brought in another 400 or
 so patches.  Full details are in the very long shortlog.
 
 All of these have been in linux-next with no reported issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iEYEABECAAYFAlbrc3EACgkQMUfUDdst+ylu5ACgpdTKjrZn2vHElW3GRhTpzJdQ
 wz8AoJDeur2DmBkQrKD5/u/WL4UTRNZJ
 =+Q6N
 -----END PGP SIGNATURE-----

Merge tag 'staging-4.6-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.6-rc1.

  Lots of little things here, over 1600 patches or so.  Notable is all
  of the good Lustre work happening, those developers have finally woken
  up and are cleaning up their code greatly.  The Outreachy intern
  application process is also happening, which brought in another 400 or
  so patches.  Full details are in the very long shortlog.

  All of these have been in linux-next with no reported issues"

* tag 'staging-4.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (1673 commits)
  staging: lustre: fix aligments in lnet selftest
  staging: lustre: report minimum of two buffers for LNet selftest load test
  staging: lustre: test for proper errno code in lstcon_rpc_trans_abort
  staging: lustre: filter remaining extra spacing for lnet selftest
  staging: lustre: remove extra spacing when setting variable for lnet selftest
  staging: lustre: remove extra spacing of variable declartions for lnet selftest
  staging: lustre: fix spacing issues checkpatch reported in lnet selftest
  staging: lustre: remove returns in void function for lnet selftest
  staging: lustre: fix bogus lst errors for lnet selftest
  staging: netlogic: Replacing pr_err with dev_err after the call to devm_kzalloc
  staging: mt29f_spinand: Replacing pr_info with dev_info after the call to devm_kzalloc
  staging: android: ion: fix up file mode
  staging: ion: debugfs invalid gfp mask
  staging: rts5208: Replace pci_enable_device with pcim_enable_device
  Staging: ieee80211: Place constant on right side of the test.
  staging: speakup: Replace del_timer with del_timer_sync
  staging: lowmemorykiller: fix 2 checks that checkpatch complained
  staging: mt29f_spinand: Drop void pointer cast
  staging: rdma: hfi1: file_ops: Replace ALIGN with PAGE_ALIGN
  staging: rdma: hfi1: driver: Replace IS_ALIGNED with PAGE_ALIGNED
  ...
This commit is contained in:
Linus Torvalds 2016-03-17 22:13:41 -07:00
commit 9dffdb38d8
895 changed files with 36123 additions and 45321 deletions

View File

@ -3054,6 +3054,7 @@ D: PLX USB338x driver
D: PCA9634 driver
D: Option GTM671WFS
D: Fintek F81216A
D: AD5761 iio driver
D: Various kernel hacks
S: Qtechnology A/S
S: Valby Langgade 142

View File

@ -496,8 +496,11 @@ Description:
1kohm_to_gnd: connected to ground via an 1kOhm resistor,
6kohm_to_gnd: connected to ground via a 6kOhm resistor,
20kohm_to_gnd: connected to ground via a 20kOhm resistor,
90kohm_to_gnd: connected to ground via a 90kOhm resistor,
100kohm_to_gnd: connected to ground via an 100kOhm resistor,
125kohm_to_gnd: connected to ground via an 125kOhm resistor,
500kohm_to_gnd: connected to ground via a 500kOhm resistor,
640kohm_to_gnd: connected to ground via a 640kOhm resistor,
three_state: left floating.
For a list of available output power down options read
outX_powerdown_mode_available. If Y is not present the
@ -1491,3 +1494,10 @@ Description:
This ABI is especially applicable for humidity sensors
to heatup the device and get rid of any condensation
in some humidity environment
What: /sys/bus/iio/devices/iio:deviceX/in_ph_raw
KernelVersion: 4.5
Contact: linux-iio@vger.kernel.org
Description:
Raw (unscaled no offset etc.) pH reading of a substance as a negative
base-10 logarithm of hydrodium ions in a litre of water.

View File

@ -0,0 +1,54 @@
What: /sys/bus/iio/devices/iio:deviceX/tia_resistanceY
/sys/bus/iio/devices/iio:deviceX/tia_capacitanceY
Date: December 2015
KernelVersion:
Contact: Andrew F. Davis <afd@ti.com>
Description:
Get and set the resistance and the capacitance settings for the
Transimpedance Amplifier. Y is 1 for Rf1 and Cf1, Y is 2 for
Rf2 and Cf2 values.
What: /sys/bus/iio/devices/iio:deviceX/tia_separate_en
Date: December 2015
KernelVersion:
Contact: Andrew F. Davis <afd@ti.com>
Description:
Enable or disable separate settings for the TransImpedance
Amplifier above, when disabled both values are set by the
first channel.
What: /sys/bus/iio/devices/iio:deviceX/in_intensity_ledY_raw
/sys/bus/iio/devices/iio:deviceX/in_intensity_ledY_ambient_raw
Date: December 2015
KernelVersion:
Contact: Andrew F. Davis <afd@ti.com>
Description:
Get measured values from the ADC for these stages. Y is the
specific LED number. The values are expressed in 24-bit twos
complement.
What: /sys/bus/iio/devices/iio:deviceX/in_intensity_ledY-ledY_ambient_raw
Date: December 2015
KernelVersion:
Contact: Andrew F. Davis <afd@ti.com>
Description:
Get differential values from the ADC for these stages. Y is the
specific LED number. The values are expressed in 24-bit twos
complement for the specified LEDs.
What: /sys/bus/iio/devices/iio:deviceX/out_current_ledY_offset
/sys/bus/iio/devices/iio:deviceX/out_current_ledY_ambient_offset
Date: December 2015
KernelVersion:
Contact: Andrew F. Davis <afd@ti.com>
Description:
Get and set the offset cancellation DAC setting for these
stages. The values are expressed in 5-bit sign-magnitude.
What: /sys/bus/iio/devices/iio:deviceX/out_current_ledY_raw
Date: December 2015
KernelVersion:
Contact: Andrew F. Davis <afd@ti.com>
Description:
Get and set the LED current for the specified LED. Y is the
specific LED number.

View File

@ -0,0 +1,15 @@
What: /sys/bus/iio/devices/iio:deviceX/meas_conf
What: /sys/bus/iio/devices/iio:deviceX/meas_conf_available
KernelVersion: 4.5
Contact: linux-iio@vger.kernel.org
Description:
Current configuration and available configurations
for the bias current.
normal - Normal measurement configurations (default)
positivebias - Positive bias configuration
negativebias - Negative bias configuration
disabled - Only available on HMC5983. Disables magnetic
sensor and enables temperature sensor.
Note: The effect of this configuration may vary
according to the device. For exact documentation
check the device's datasheet.

View File

@ -5,3 +5,12 @@ Description:
Specifies the hardware conversion mode used. The three
available modes are "normal", "high-speed" and "low-power",
where the last is the default mode.
What: /sys/bus/iio/devices/iio:deviceX/out_conversion_mode
KernelVersion: 4.6
Contact: linux-iio@vger.kernel.org
Description:
Specifies the hardware conversion mode used within DAC.
The two available modes are "high-power" and "low-power",
where "low-power" mode is the default mode.

View File

@ -0,0 +1,17 @@
Android Goldfish Audio
Android goldfish audio device generated by android emulator.
Required properties:
- compatible : should contain "google,goldfish-audio" to match emulator
- reg : <registers mapping>
- interrupts : <interrupt mapping>
Example:
goldfish_audio@9030000 {
compatible = "google,goldfish-audio";
reg = <0x9030000 0x100>;
interrupts = <0x4>;
};

View File

@ -1,8 +1,10 @@
Freescale MMA8452Q, MMA8453Q, MMA8652FC or MMA8653FC triaxial accelerometer
Freescale MMA8451Q, MMA8452Q, MMA8453Q, MMA8652FC or MMA8653FC
triaxial accelerometer
Required properties:
- compatible: should contain one of
* "fsl,mma8451"
* "fsl,mma8452"
* "fsl,mma8453"
* "fsl,mma8652"

View File

@ -0,0 +1,28 @@
* AT91 SAMA5D2 Analog to Digital Converter (ADC)
Required properties:
- compatible: Should be "atmel,sama5d2-adc".
- reg: Should contain ADC registers location and length.
- interrupts: Should contain the IRQ line for the ADC.
- clocks: phandle to device clock.
- clock-names: Must be "adc_clk".
- vref-supply: Supply used as reference for conversions.
- vddana-supply: Supply for the adc device.
- atmel,min-sample-rate-hz: Minimum sampling rate, it depends on SoC.
- atmel,max-sample-rate-hz: Maximum sampling rate, it depends on SoC.
- atmel,startup-time-ms: Startup time expressed in ms, it depends on SoC.
Example:
adc: adc@fc030000 {
compatible = "atmel,sama5d2-adc";
reg = <0xfc030000 0x100>;
interrupts = <40 IRQ_TYPE_LEVEL_HIGH 7>;
clocks = <&adc_clk>;
clock-names = "adc_clk";
atmel,min-sample-rate-hz = <200000>;
atmel,max-sample-rate-hz = <20000000>;
atmel,startup-time-ms = <4>;
vddana-supply = <&vdd_3v3_lp_reg>;
vref-supply = <&vdd_3v3_lp_reg>;
}

View File

@ -6,6 +6,7 @@ Required properties:
"microchip,mcp3422" or
"microchip,mcp3423" or
"microchip,mcp3424" or
"microchip,mcp3425" or
"microchip,mcp3426" or
"microchip,mcp3427" or
"microchip,mcp3428"

View File

@ -0,0 +1,19 @@
* Texas Instruments' ADC0831/ADC0832/ADC0832/ADC0838
Required properties:
- compatible: Should be one of
* "ti,adc0831"
* "ti,adc0832"
* "ti,adc0834"
* "ti,adc0838"
- reg: spi chip select number for the device
- vref-supply: The regulator supply for ADC reference voltage
- spi-max-frequency: Max SPI frequency to use (< 400000)
Example:
adc@0 {
compatible = "ti,adc0832";
reg = <0>;
vref-supply = <&vdd_supply>;
spi-max-frequency = <200000>;
};

View File

@ -0,0 +1,22 @@
* Atlas Scientific pH-SM OEM sensor
http://www.atlas-scientific.com/_files/_datasheets/_oem/pH_oem_datasheet.pdf
Required properties:
- compatible: must be "atlas,ph-sm"
- 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:
atlas@65 {
compatible = "atlas,ph-sm";
reg = <0x65>;
interrupt-parent = <&gpio1>;
interrupts = <16 2>;
};

View File

@ -0,0 +1,20 @@
Freescale vf610 Digital to Analog Converter bindings
The devicetree bindings are for the new DAC driver written for
vf610 SoCs from Freescale.
Required properties:
- compatible: Should contain "fsl,vf610-dac"
- reg: Offset and length of the register set for the device
- interrupts: Should contain the interrupt for the device
- clocks: The clock is needed by the DAC controller
- clock-names: Must contain "dac" matching entry in the clocks property.
Example:
dac0: dac@400cc000 {
compatible = "fsl,vf610-dac";
reg = <0x400cc000 0x1000>;
interrupts = <55 IRQ_TYPE_LEVEL_HIGH>;
clock-names = "dac";
clocks = <&clks VF610_CLK_DAC0>;
};

View File

@ -0,0 +1,34 @@
Texas Instruments AFE4403 Heart rate and Pulse Oximeter
Required properties:
- compatible : Should be "ti,afe4403".
- reg : SPI chip select address of device.
- tx-supply : Regulator supply to transmitting LEDs.
- interrupt-parent : Phandle to he parent interrupt controller.
- interrupts : The interrupt line the device ADC_RDY pin is
connected to. For details refer to,
../../interrupt-controller/interrupts.txt.
Optional properties:
- reset-gpios : GPIO used to reset the device.
For details refer to, ../../gpio/gpio.txt.
For other required and optional properties of SPI slave nodes
please refer to ../../spi/spi-bus.txt.
Example:
&spi0 {
heart_mon@0 {
compatible = "ti,afe4403";
reg = <0>;
spi-max-frequency = <10000000>;
tx-supply = <&vbat>;
interrupt-parent = <&gpio1>;
interrupts = <28 IRQ_TYPE_EDGE_RISING>;
reset-gpios = <&gpio1 16 GPIO_ACTIVE_LOW>;
};
};

View File

@ -0,0 +1,30 @@
Texas Instruments AFE4404 Heart rate and Pulse Oximeter
Required properties:
- compatible : Should be "ti,afe4404".
- reg : I2C address of the device.
- tx-supply : Regulator supply to transmitting LEDs.
- interrupt-parent : Phandle to he parent interrupt controller.
- interrupts : The interrupt line the device ADC_RDY pin is
connected to. For details refer to,
../interrupt-controller/interrupts.txt.
Optional properties:
- reset-gpios : GPIO used to reset the device.
For details refer to, ../gpio/gpio.txt.
Example:
&i2c2 {
heart_mon@58 {
compatible = "ti,afe4404";
reg = <0x58>;
tx-supply = <&vbat>;
interrupt-parent = <&gpio1>;
interrupts = <28 IRQ_TYPE_EDGE_RISING>;
reset-gpios = <&gpio1 16 GPIO_ACTIVE_LOW>;
};
};

View File

@ -11,11 +11,19 @@ Required properties:
Refer to interrupt-controller/interrupts.txt for generic
interrupt client node bindings.
Optional properties:
- maxim,led-current-microamp: configuration for LED current in microamperes
while the engine is running. First indexed value is the configuration for
the RED LED, and second value is for the IR LED.
Refer to the datasheet for the allowed current values.
Example:
max30100@057 {
compatible = "maxim,max30100";
reg = <57>;
maxim,led-current-microamp = <24000 50000>;
interrupt-parent = <&gpio1>;
interrupts = <16 2>;
};

View File

@ -0,0 +1,26 @@
* Texas Instruments OPT3001 Ambient Light Sensor
The driver supports interrupt-driven and interrupt-less operation, depending
on whether an interrupt property has been populated into the DT. Note that
the optional generation of IIO events on rising/falling light threshold changes
requires the use of interrupts. Without interrupts, only the simple reading
of the current light value is supported through the IIO API.
http://www.ti.com/product/opt3001
Required properties:
- compatible: should be "ti,opt3001"
- reg: the I2C address of the sensor
Optional properties:
- interrupt-parent: should be the phandle for the interrupt controller
- interrupts: interrupt mapping for GPIO IRQ (configure for falling edge)
Example:
opt3001@44 {
compatible = "ti,opt3001";
reg = <0x44>;
interrupt-parent = <&gpio1>;
interrupts = <28 IRQ_TYPE_EDGE_FALLING>;
};

View File

@ -28,6 +28,7 @@ arm ARM Ltd.
armadeus ARMadeus Systems SARL
artesyn Artesyn Embedded Technologies Inc.
asahi-kasei Asahi Kasei Corp.
atlas Atlas Scientific LLC
atmel Atmel Corporation
auo AU Optronics Corporation
avago Avago Technologies

View File

@ -16,8 +16,6 @@ README.FAQ
- general info for FAQ.
README.HiSax
- info on the HiSax driver which replaces the old teles.
README.act2000
- info on driver for IBM ACT-2000 card.
README.audio
- info for running audio over ISDN.
README.avmb1
@ -34,14 +32,8 @@ README.hfc-pci
- info on hfc-pci based cards.
README.hysdn
- info on driver for Hypercope active HYSDN cards
README.icn
- info on the ICN-ISDN-card and its driver.
README.mISDN
- info on the Modular ISDN subsystem (mISDN)
README.pcbit
- info on the PCBIT-D ISDN adapter and driver.
README.sc
- info on driver for Spellcaster cards.
README.syncppp
- info on running Sync PPP over ISDN.
README.x25

View File

@ -775,6 +775,12 @@ L: alsa-devel@alsa-project.org (moderated for non-subscribers)
S: Maintained
F: sound/aoa/
APEX EMBEDDED SYSTEMS STX104 DAC DRIVER
M: William Breathitt Gray <vilhelm.gray@gmail.com>
L: linux-iio@vger.kernel.org
S: Maintained
F: drivers/iio/dac/stx104.c
APM DRIVER
M: Jiri Kosina <jikos@kernel.org>
S: Odd fixes
@ -1962,6 +1968,12 @@ M: Nicolas Ferre <nicolas.ferre@atmel.com>
S: Supported
F: drivers/tty/serial/atmel_serial.c
ATMEL SAMA5D2 ADC DRIVER
M: Ludovic Desroches <ludovic.desroches@atmel.com>
L: linux-iio@vger.kernel.org
S: Supported
F: drivers/iio/adc/at91-sama5d2_adc.c
ATMEL Audio ALSA driver
M: Nicolas Ferre <nicolas.ferre@atmel.com>
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
@ -3548,13 +3560,6 @@ L: driverdev-devel@linuxdriverproject.org
S: Maintained
F: drivers/staging/dgnc/
DIGI EPCA PCI PRODUCTS
M: Lidza Louina <lidza.louina@gmail.com>
M: Daeseok Youn <daeseok.youn@gmail.com>
L: driverdev-devel@linuxdriverproject.org
S: Maintained
F: drivers/staging/dgap/
DIOLAN U2C-12 I2C DRIVER
M: Guenter Roeck <linux@roeck-us.net>
L: linux-i2c@vger.kernel.org
@ -8202,6 +8207,13 @@ S: Maintained
F: Documentation/mn10300/
F: arch/mn10300/
PARALLEL LCD/KEYPAD PANEL DRIVER
M: Willy Tarreau <willy@haproxy.com>
M: Ksenija Stanojevic <ksenija.stanojevic@gmail.com>
S: Odd Fixes
F: Documentation/misc-devices/lcd-panel-cgram.txt
F: drivers/misc/panel.c
PARALLEL PORT SUBSYSTEM
M: Sudip Mukherjee <sudipm.mukherjee@gmail.com>
M: Sudip Mukherjee <sudip@vectorindia.org>
@ -10455,19 +10467,6 @@ L: linux-tegra@vger.kernel.org
S: Maintained
F: drivers/staging/nvec/
STAGING - OLPC SECONDARY DISPLAY CONTROLLER (DCON)
M: Jens Frederich <jfrederich@gmail.com>
M: Daniel Drake <dsd@laptop.org>
M: Jon Nettleton <jon.nettleton@gmail.com>
W: http://wiki.laptop.org/go/DCON
S: Maintained
F: drivers/staging/olpc_dcon/
STAGING - PARALLEL LCD/KEYPAD PANEL DRIVER
M: Willy Tarreau <willy@meta-x.org>
S: Odd Fixes
F: drivers/staging/panel/
STAGING - REALTEK RTL8712U DRIVERS
M: Larry Finger <Larry.Finger@lwfinger.net>
M: Florian Schilhabel <florian.c.schilhabel@googlemail.com>.

View File

@ -2744,6 +2744,10 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
/*pr_info("binder_ioctl: %d:%d %x %lx\n",
proc->pid, current->pid, cmd, arg);*/
if (unlikely(current->mm != proc->vma_vm_mm)) {
pr_err("current mm mismatch proc mm\n");
return -EINVAL;
}
trace_binder_ioctl(cmd, arg);
ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
@ -2958,6 +2962,7 @@ static int binder_open(struct inode *nodp, struct file *filp)
return -ENOMEM;
get_task_struct(current);
proc->tsk = current;
proc->vma_vm_mm = current->mm;
INIT_LIST_HEAD(&proc->todo);
init_waitqueue_head(&proc->wait);
proc->default_priority = task_nice(current);

View File

@ -143,7 +143,7 @@ config MMA8452
select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for the following Freescale 3-axis
accelerometers: MMA8452Q, MMA8453Q, MMA8652FC, MMA8653FC.
accelerometers: MMA8451Q, MMA8452Q, MMA8453Q, MMA8652FC, MMA8653FC.
To compile this driver as a module, choose M here: the module
will be called mma8452.

View File

@ -1,6 +1,7 @@
/*
* mma8452.c - Support for following Freescale 3-axis accelerometers:
*
* MMA8451Q (14 bit)
* MMA8452Q (12 bit)
* MMA8453Q (10 bit)
* MMA8652FC (12 bit)
@ -15,7 +16,7 @@
*
* 7-bit I2C slave address 0x1c/0x1d (pin selectable)
*
* TODO: orientation / freefall events, autosleep
* TODO: orientation events, autosleep
*/
#include <linux/module.h>
@ -85,8 +86,9 @@
#define MMA8452_INT_FF_MT BIT(2)
#define MMA8452_INT_TRANS BIT(5)
#define MMA8452_DEVICE_ID 0x2a
#define MMA8453_DEVICE_ID 0x3a
#define MMA8451_DEVICE_ID 0x1a
#define MMA8452_DEVICE_ID 0x2a
#define MMA8453_DEVICE_ID 0x3a
#define MMA8652_DEVICE_ID 0x4a
#define MMA8653_DEVICE_ID 0x5a
@ -416,6 +418,51 @@ fail:
return ret;
}
/* returns >0 if in freefall mode, 0 if not or <0 if an error occured */
static int mma8452_freefall_mode_enabled(struct mma8452_data *data)
{
int val;
const struct mma_chip_info *chip = data->chip_info;
val = i2c_smbus_read_byte_data(data->client, chip->ev_cfg);
if (val < 0)
return val;
return !(val & MMA8452_FF_MT_CFG_OAE);
}
static int mma8452_set_freefall_mode(struct mma8452_data *data, bool state)
{
int val;
const struct mma_chip_info *chip = data->chip_info;
if ((state && mma8452_freefall_mode_enabled(data)) ||
(!state && !(mma8452_freefall_mode_enabled(data))))
return 0;
val = i2c_smbus_read_byte_data(data->client, chip->ev_cfg);
if (val < 0)
return val;
if (state) {
val |= BIT(idx_x + chip->ev_cfg_chan_shift);
val |= BIT(idx_y + chip->ev_cfg_chan_shift);
val |= BIT(idx_z + chip->ev_cfg_chan_shift);
val &= ~MMA8452_FF_MT_CFG_OAE;
} else {
val &= ~BIT(idx_x + chip->ev_cfg_chan_shift);
val &= ~BIT(idx_y + chip->ev_cfg_chan_shift);
val &= ~BIT(idx_z + chip->ev_cfg_chan_shift);
val |= MMA8452_FF_MT_CFG_OAE;
}
val = mma8452_change_config(data, chip->ev_cfg, val);
if (val)
return val;
return 0;
}
static int mma8452_set_hp_filter_frequency(struct mma8452_data *data,
int val, int val2)
{
@ -609,12 +656,22 @@ static int mma8452_read_event_config(struct iio_dev *indio_dev,
const struct mma_chip_info *chip = data->chip_info;
int ret;
ret = i2c_smbus_read_byte_data(data->client,
data->chip_info->ev_cfg);
if (ret < 0)
return ret;
switch (dir) {
case IIO_EV_DIR_FALLING:
return mma8452_freefall_mode_enabled(data);
case IIO_EV_DIR_RISING:
if (mma8452_freefall_mode_enabled(data))
return 0;
return !!(ret & BIT(chan->scan_index + chip->ev_cfg_chan_shift));
ret = i2c_smbus_read_byte_data(data->client,
data->chip_info->ev_cfg);
if (ret < 0)
return ret;
return !!(ret & BIT(chan->scan_index + chip->ev_cfg_chan_shift));
default:
return -EINVAL;
}
}
static int mma8452_write_event_config(struct iio_dev *indio_dev,
@ -627,19 +684,35 @@ static int mma8452_write_event_config(struct iio_dev *indio_dev,
const struct mma_chip_info *chip = data->chip_info;
int val;
val = i2c_smbus_read_byte_data(data->client, chip->ev_cfg);
if (val < 0)
return val;
switch (dir) {
case IIO_EV_DIR_FALLING:
return mma8452_set_freefall_mode(data, state);
case IIO_EV_DIR_RISING:
val = i2c_smbus_read_byte_data(data->client, chip->ev_cfg);
if (val < 0)
return val;
if (state)
val |= BIT(chan->scan_index + chip->ev_cfg_chan_shift);
else
val &= ~BIT(chan->scan_index + chip->ev_cfg_chan_shift);
if (state) {
if (mma8452_freefall_mode_enabled(data)) {
val &= ~BIT(idx_x + chip->ev_cfg_chan_shift);
val &= ~BIT(idx_y + chip->ev_cfg_chan_shift);
val &= ~BIT(idx_z + chip->ev_cfg_chan_shift);
val |= MMA8452_FF_MT_CFG_OAE;
}
val |= BIT(chan->scan_index + chip->ev_cfg_chan_shift);
} else {
if (mma8452_freefall_mode_enabled(data))
return 0;
val |= chip->ev_cfg_ele;
val |= MMA8452_FF_MT_CFG_OAE;
val &= ~BIT(chan->scan_index + chip->ev_cfg_chan_shift);
}
return mma8452_change_config(data, chip->ev_cfg, val);
val |= chip->ev_cfg_ele;
return mma8452_change_config(data, chip->ev_cfg, val);
default:
return -EINVAL;
}
}
static void mma8452_transient_interrupt(struct iio_dev *indio_dev)
@ -652,6 +725,16 @@ static void mma8452_transient_interrupt(struct iio_dev *indio_dev)
if (src < 0)
return;
if (mma8452_freefall_mode_enabled(data)) {
iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_ACCEL, 0,
IIO_MOD_X_AND_Y_AND_Z,
IIO_EV_TYPE_MAG,
IIO_EV_DIR_FALLING),
ts);
return;
}
if (src & data->chip_info->ev_src_xe)
iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X,
@ -745,6 +828,27 @@ static int mma8452_reg_access_dbg(struct iio_dev *indio_dev,
return 0;
}
static const struct iio_event_spec mma8452_freefall_event[] = {
{
.type = IIO_EV_TYPE_MAG,
.dir = IIO_EV_DIR_FALLING,
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_PERIOD) |
BIT(IIO_EV_INFO_HIGH_PASS_FILTER_3DB)
},
};
static const struct iio_event_spec mma8652_freefall_event[] = {
{
.type = IIO_EV_TYPE_MAG,
.dir = IIO_EV_DIR_FALLING,
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_PERIOD)
},
};
static const struct iio_event_spec mma8452_transient_event[] = {
{
.type = IIO_EV_TYPE_MAG,
@ -781,6 +885,24 @@ static struct attribute_group mma8452_event_attribute_group = {
.attrs = mma8452_event_attributes,
};
#define MMA8452_FREEFALL_CHANNEL(modifier) { \
.type = IIO_ACCEL, \
.modified = 1, \
.channel2 = modifier, \
.scan_index = -1, \
.event_spec = mma8452_freefall_event, \
.num_event_specs = ARRAY_SIZE(mma8452_freefall_event), \
}
#define MMA8652_FREEFALL_CHANNEL(modifier) { \
.type = IIO_ACCEL, \
.modified = 1, \
.channel2 = modifier, \
.scan_index = -1, \
.event_spec = mma8652_freefall_event, \
.num_event_specs = ARRAY_SIZE(mma8652_freefall_event), \
}
#define MMA8452_CHANNEL(axis, idx, bits) { \
.type = IIO_ACCEL, \
.modified = 1, \
@ -822,11 +944,20 @@ static struct attribute_group mma8452_event_attribute_group = {
.num_event_specs = ARRAY_SIZE(mma8452_motion_event), \
}
static const struct iio_chan_spec mma8451_channels[] = {
MMA8452_CHANNEL(X, idx_x, 14),
MMA8452_CHANNEL(Y, idx_y, 14),
MMA8452_CHANNEL(Z, idx_z, 14),
IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
MMA8452_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z),
};
static const struct iio_chan_spec mma8452_channels[] = {
MMA8452_CHANNEL(X, idx_x, 12),
MMA8452_CHANNEL(Y, idx_y, 12),
MMA8452_CHANNEL(Z, idx_z, 12),
IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
MMA8452_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z),
};
static const struct iio_chan_spec mma8453_channels[] = {
@ -834,6 +965,7 @@ static const struct iio_chan_spec mma8453_channels[] = {
MMA8452_CHANNEL(Y, idx_y, 10),
MMA8452_CHANNEL(Z, idx_z, 10),
IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
MMA8452_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z),
};
static const struct iio_chan_spec mma8652_channels[] = {
@ -841,6 +973,7 @@ static const struct iio_chan_spec mma8652_channels[] = {
MMA8652_CHANNEL(Y, idx_y, 12),
MMA8652_CHANNEL(Z, idx_z, 12),
IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
MMA8652_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z),
};
static const struct iio_chan_spec mma8653_channels[] = {
@ -848,9 +981,11 @@ static const struct iio_chan_spec mma8653_channels[] = {
MMA8652_CHANNEL(Y, idx_y, 10),
MMA8652_CHANNEL(Z, idx_z, 10),
IIO_CHAN_SOFT_TIMESTAMP(idx_ts),
MMA8652_FREEFALL_CHANNEL(IIO_MOD_X_AND_Y_AND_Z),
};
enum {
mma8451,
mma8452,
mma8453,
mma8652,
@ -858,17 +993,34 @@ enum {
};
static const struct mma_chip_info mma_chip_info_table[] = {
[mma8451] = {
.chip_id = MMA8451_DEVICE_ID,
.channels = mma8451_channels,
.num_channels = ARRAY_SIZE(mma8451_channels),
/*
* Hardware has fullscale of -2G, -4G, -8G corresponding to
* raw value -8192 for 14 bit, -2048 for 12 bit or -512 for 10
* bit.
* The userspace interface uses m/s^2 and we declare micro units
* So scale factor for 12 bit here is given by:
* g * N * 1000000 / 2048 for N = 2, 4, 8 and g=9.80665
*/
.mma_scales = { {0, 2394}, {0, 4788}, {0, 9577} },
.ev_cfg = MMA8452_TRANSIENT_CFG,
.ev_cfg_ele = MMA8452_TRANSIENT_CFG_ELE,
.ev_cfg_chan_shift = 1,
.ev_src = MMA8452_TRANSIENT_SRC,
.ev_src_xe = MMA8452_TRANSIENT_SRC_XTRANSE,
.ev_src_ye = MMA8452_TRANSIENT_SRC_YTRANSE,
.ev_src_ze = MMA8452_TRANSIENT_SRC_ZTRANSE,
.ev_ths = MMA8452_TRANSIENT_THS,
.ev_ths_mask = MMA8452_TRANSIENT_THS_MASK,
.ev_count = MMA8452_TRANSIENT_COUNT,
},
[mma8452] = {
.chip_id = MMA8452_DEVICE_ID,
.channels = mma8452_channels,
.num_channels = ARRAY_SIZE(mma8452_channels),
/*
* Hardware has fullscale of -2G, -4G, -8G corresponding to
* raw value -2048 for 12 bit or -512 for 10 bit.
* The userspace interface uses m/s^2 and we declare micro units
* So scale factor for 12 bit here is given by:
* g * N * 1000000 / 2048 for N = 2, 4, 8 and g=9.80665
*/
.mma_scales = { {0, 9577}, {0, 19154}, {0, 38307} },
.ev_cfg = MMA8452_TRANSIENT_CFG,
.ev_cfg_ele = MMA8452_TRANSIENT_CFG_ELE,
@ -1049,6 +1201,7 @@ static int mma8452_reset(struct i2c_client *client)
}
static const struct of_device_id mma8452_dt_ids[] = {
{ .compatible = "fsl,mma8451", .data = &mma_chip_info_table[mma8451] },
{ .compatible = "fsl,mma8452", .data = &mma_chip_info_table[mma8452] },
{ .compatible = "fsl,mma8453", .data = &mma_chip_info_table[mma8453] },
{ .compatible = "fsl,mma8652", .data = &mma_chip_info_table[mma8652] },
@ -1085,6 +1238,7 @@ static int mma8452_probe(struct i2c_client *client,
return ret;
switch (ret) {
case MMA8451_DEVICE_ID:
case MMA8452_DEVICE_ID:
case MMA8453_DEVICE_ID:
case MMA8652_DEVICE_ID:
@ -1190,6 +1344,10 @@ static int mma8452_probe(struct i2c_client *client,
if (ret < 0)
goto buffer_cleanup;
ret = mma8452_set_freefall_mode(data, false);
if (ret)
return ret;
return 0;
buffer_cleanup:

View File

@ -67,6 +67,8 @@
#define ST_ACCEL_1_DRDY_IRQ_ADDR 0x22
#define ST_ACCEL_1_DRDY_IRQ_INT1_MASK 0x10
#define ST_ACCEL_1_DRDY_IRQ_INT2_MASK 0x08
#define ST_ACCEL_1_IHL_IRQ_ADDR 0x25
#define ST_ACCEL_1_IHL_IRQ_MASK 0x02
#define ST_ACCEL_1_MULTIREAD_BIT true
/* CUSTOM VALUES FOR SENSOR 2 */
@ -92,6 +94,8 @@
#define ST_ACCEL_2_DRDY_IRQ_ADDR 0x22
#define ST_ACCEL_2_DRDY_IRQ_INT1_MASK 0x02
#define ST_ACCEL_2_DRDY_IRQ_INT2_MASK 0x10
#define ST_ACCEL_2_IHL_IRQ_ADDR 0x22
#define ST_ACCEL_2_IHL_IRQ_MASK 0x80
#define ST_ACCEL_2_MULTIREAD_BIT true
/* CUSTOM VALUES FOR SENSOR 3 */
@ -125,6 +129,8 @@
#define ST_ACCEL_3_DRDY_IRQ_ADDR 0x23
#define ST_ACCEL_3_DRDY_IRQ_INT1_MASK 0x80
#define ST_ACCEL_3_DRDY_IRQ_INT2_MASK 0x00
#define ST_ACCEL_3_IHL_IRQ_ADDR 0x23
#define ST_ACCEL_3_IHL_IRQ_MASK 0x40
#define ST_ACCEL_3_IG1_EN_ADDR 0x23
#define ST_ACCEL_3_IG1_EN_MASK 0x08
#define ST_ACCEL_3_MULTIREAD_BIT false
@ -169,6 +175,8 @@
#define ST_ACCEL_5_DRDY_IRQ_ADDR 0x22
#define ST_ACCEL_5_DRDY_IRQ_INT1_MASK 0x04
#define ST_ACCEL_5_DRDY_IRQ_INT2_MASK 0x20
#define ST_ACCEL_5_IHL_IRQ_ADDR 0x22
#define ST_ACCEL_5_IHL_IRQ_MASK 0x80
#define ST_ACCEL_5_IG1_EN_ADDR 0x21
#define ST_ACCEL_5_IG1_EN_MASK 0x08
#define ST_ACCEL_5_MULTIREAD_BIT false
@ -292,6 +300,8 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
.addr = ST_ACCEL_1_DRDY_IRQ_ADDR,
.mask_int1 = ST_ACCEL_1_DRDY_IRQ_INT1_MASK,
.mask_int2 = ST_ACCEL_1_DRDY_IRQ_INT2_MASK,
.addr_ihl = ST_ACCEL_1_IHL_IRQ_ADDR,
.mask_ihl = ST_ACCEL_1_IHL_IRQ_MASK,
},
.multi_read_bit = ST_ACCEL_1_MULTIREAD_BIT,
.bootime = 2,
@ -355,6 +365,8 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
.addr = ST_ACCEL_2_DRDY_IRQ_ADDR,
.mask_int1 = ST_ACCEL_2_DRDY_IRQ_INT1_MASK,
.mask_int2 = ST_ACCEL_2_DRDY_IRQ_INT2_MASK,
.addr_ihl = ST_ACCEL_2_IHL_IRQ_ADDR,
.mask_ihl = ST_ACCEL_2_IHL_IRQ_MASK,
},
.multi_read_bit = ST_ACCEL_2_MULTIREAD_BIT,
.bootime = 2,
@ -430,6 +442,8 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
.addr = ST_ACCEL_3_DRDY_IRQ_ADDR,
.mask_int1 = ST_ACCEL_3_DRDY_IRQ_INT1_MASK,
.mask_int2 = ST_ACCEL_3_DRDY_IRQ_INT2_MASK,
.addr_ihl = ST_ACCEL_3_IHL_IRQ_ADDR,
.mask_ihl = ST_ACCEL_3_IHL_IRQ_MASK,
.ig1 = {
.en_addr = ST_ACCEL_3_IG1_EN_ADDR,
.en_mask = ST_ACCEL_3_IG1_EN_MASK,
@ -537,6 +551,8 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
.addr = ST_ACCEL_5_DRDY_IRQ_ADDR,
.mask_int1 = ST_ACCEL_5_DRDY_IRQ_INT1_MASK,
.mask_int2 = ST_ACCEL_5_DRDY_IRQ_INT2_MASK,
.addr_ihl = ST_ACCEL_5_IHL_IRQ_ADDR,
.mask_ihl = ST_ACCEL_5_IHL_IRQ_MASK,
},
.multi_read_bit = ST_ACCEL_5_MULTIREAD_BIT,
.bootime = 2, /* guess */

View File

@ -131,6 +131,16 @@ config AT91_ADC
To compile this driver as a module, choose M here: the module will be
called at91_adc.
config AT91_SAMA5D2_ADC
tristate "Atmel AT91 SAMA5D2 ADC"
depends on ARCH_AT91 || COMPILE_TEST
help
Say yes here to build support for Atmel SAMA5D2 ADC which is
available on SAMA5D2 SoC family.
To compile this driver as a module, choose M here: the module will be
called at91-sama5d2_adc.
config AXP288_ADC
tristate "X-Powers AXP288 ADC driver"
depends on MFD_AXP20X
@ -267,11 +277,11 @@ config MCP320X
called mcp320x.
config MCP3422
tristate "Microchip Technology MCP3422/3/4/6/7/8 driver"
tristate "Microchip Technology MCP3421/2/3/4/5/6/7/8 driver"
depends on I2C
help
Say yes here to build support for Microchip Technology's
MCP3422, MCP3423, MCP3424, MCP3426, MCP3427 or MCP3428
Say yes here to build support for Microchip Technology's MCP3421
MCP3422, MCP3423, MCP3424, MCP3425, MCP3426, MCP3427 or MCP3428
analog to digital converters.
This driver can also be built as a module. If so, the module will be
@ -287,6 +297,20 @@ config MEN_Z188_ADC
This driver can also be built as a module. If so, the module will be
called men_z188_adc.
config MXS_LRADC
tristate "Freescale i.MX23/i.MX28 LRADC"
depends on (ARCH_MXS || COMPILE_TEST) && HAS_IOMEM
depends on INPUT
select STMP_DEVICE
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for i.MX23/i.MX28 LRADC convertor
built into these chips.
To compile this driver as a module, choose M here: the
module will be called mxs-lradc.
config NAU7802
tristate "Nuvoton NAU7802 ADC driver"
depends on I2C
@ -352,6 +376,16 @@ config TI_ADC081C
This driver can also be built as a module. If so, the module will be
called ti-adc081c.
config TI_ADC0832
tristate "Texas Instruments ADC0831/ADC0832/ADC0834/ADC0838"
depends on SPI
help
If you say yes here you get support for Texas Instruments ADC0831,
ADC0832, ADC0834, ADC0838 ADC chips.
This driver can also be built as a module. If so, the module will be
called ti-adc0832.
config TI_ADC128S052
tristate "Texas Instruments ADC128S052/ADC122S021/ADC124S021"
depends on SPI
@ -362,6 +396,19 @@ config TI_ADC128S052
This driver can also be built as a module. If so, the module will be
called ti-adc128s052.
config TI_ADS1015
tristate "Texas Instruments ADS1015 ADC"
depends on I2C && !SENSORS_ADS1015
select REGMAP_I2C
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
If you say yes here you get support for Texas Instruments ADS1015
ADC chip.
This driver can also be built as a module. If so, the module will be
called ti-ads1015.
config TI_ADS8688
tristate "Texas Instruments ADS8688"
depends on SPI && OF

View File

@ -14,6 +14,7 @@ obj-$(CONFIG_AD7793) += ad7793.o
obj-$(CONFIG_AD7887) += ad7887.o
obj-$(CONFIG_AD799X) += ad799x.o
obj-$(CONFIG_AT91_ADC) += at91_adc.o
obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o
obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
@ -28,13 +29,16 @@ obj-$(CONFIG_MAX1363) += max1363.o
obj-$(CONFIG_MCP320X) += mcp320x.o
obj-$(CONFIG_MCP3422) += mcp3422.o
obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
obj-$(CONFIG_MXS_LRADC) += mxs-lradc.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_ADC0832) += ti-adc0832.o
obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o
obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o
obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o

View File

@ -0,0 +1,508 @@
/*
* Atmel ADC driver for SAMA5D2 devices and compatible.
*
* Copyright (C) 2015 Atmel,
* 2015 Ludovic Desroches <ludovic.desroches@atmel.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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/bitops.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/regulator/consumer.h>
/* Control Register */
#define AT91_SAMA5D2_CR 0x00
/* Software Reset */
#define AT91_SAMA5D2_CR_SWRST BIT(0)
/* Start Conversion */
#define AT91_SAMA5D2_CR_START BIT(1)
/* Touchscreen Calibration */
#define AT91_SAMA5D2_CR_TSCALIB BIT(2)
/* Comparison Restart */
#define AT91_SAMA5D2_CR_CMPRST BIT(4)
/* Mode Register */
#define AT91_SAMA5D2_MR 0x04
/* Trigger Selection */
#define AT91_SAMA5D2_MR_TRGSEL(v) ((v) << 1)
/* ADTRG */
#define AT91_SAMA5D2_MR_TRGSEL_TRIG0 0
/* TIOA0 */
#define AT91_SAMA5D2_MR_TRGSEL_TRIG1 1
/* TIOA1 */
#define AT91_SAMA5D2_MR_TRGSEL_TRIG2 2
/* TIOA2 */
#define AT91_SAMA5D2_MR_TRGSEL_TRIG3 3
/* PWM event line 0 */
#define AT91_SAMA5D2_MR_TRGSEL_TRIG4 4
/* PWM event line 1 */
#define AT91_SAMA5D2_MR_TRGSEL_TRIG5 5
/* TIOA3 */
#define AT91_SAMA5D2_MR_TRGSEL_TRIG6 6
/* RTCOUT0 */
#define AT91_SAMA5D2_MR_TRGSEL_TRIG7 7
/* Sleep Mode */
#define AT91_SAMA5D2_MR_SLEEP BIT(5)
/* Fast Wake Up */
#define AT91_SAMA5D2_MR_FWUP BIT(6)
/* Prescaler Rate Selection */
#define AT91_SAMA5D2_MR_PRESCAL(v) ((v) << AT91_SAMA5D2_MR_PRESCAL_OFFSET)
#define AT91_SAMA5D2_MR_PRESCAL_OFFSET 8
#define AT91_SAMA5D2_MR_PRESCAL_MAX 0xff
/* Startup Time */
#define AT91_SAMA5D2_MR_STARTUP(v) ((v) << 16)
/* Analog Change */
#define AT91_SAMA5D2_MR_ANACH BIT(23)
/* Tracking Time */
#define AT91_SAMA5D2_MR_TRACKTIM(v) ((v) << 24)
#define AT91_SAMA5D2_MR_TRACKTIM_MAX 0xff
/* Transfer Time */
#define AT91_SAMA5D2_MR_TRANSFER(v) ((v) << 28)
#define AT91_SAMA5D2_MR_TRANSFER_MAX 0x3
/* Use Sequence Enable */
#define AT91_SAMA5D2_MR_USEQ BIT(31)
/* Channel Sequence Register 1 */
#define AT91_SAMA5D2_SEQR1 0x08
/* Channel Sequence Register 2 */
#define AT91_SAMA5D2_SEQR2 0x0c
/* Channel Enable Register */
#define AT91_SAMA5D2_CHER 0x10
/* Channel Disable Register */
#define AT91_SAMA5D2_CHDR 0x14
/* Channel Status Register */
#define AT91_SAMA5D2_CHSR 0x18
/* Last Converted Data Register */
#define AT91_SAMA5D2_LCDR 0x20
/* Interrupt Enable Register */
#define AT91_SAMA5D2_IER 0x24
/* Interrupt Disable Register */
#define AT91_SAMA5D2_IDR 0x28
/* Interrupt Mask Register */
#define AT91_SAMA5D2_IMR 0x2c
/* Interrupt Status Register */
#define AT91_SAMA5D2_ISR 0x30
/* Last Channel Trigger Mode Register */
#define AT91_SAMA5D2_LCTMR 0x34
/* Last Channel Compare Window Register */
#define AT91_SAMA5D2_LCCWR 0x38
/* Overrun Status Register */
#define AT91_SAMA5D2_OVER 0x3c
/* Extended Mode Register */
#define AT91_SAMA5D2_EMR 0x40
/* Compare Window Register */
#define AT91_SAMA5D2_CWR 0x44
/* Channel Gain Register */
#define AT91_SAMA5D2_CGR 0x48
/* Channel Offset Register */
#define AT91_SAMA5D2_COR 0x4c
/* Channel Data Register 0 */
#define AT91_SAMA5D2_CDR0 0x50
/* Analog Control Register */
#define AT91_SAMA5D2_ACR 0x94
/* Touchscreen Mode Register */
#define AT91_SAMA5D2_TSMR 0xb0
/* Touchscreen X Position Register */
#define AT91_SAMA5D2_XPOSR 0xb4
/* Touchscreen Y Position Register */
#define AT91_SAMA5D2_YPOSR 0xb8
/* Touchscreen Pressure Register */
#define AT91_SAMA5D2_PRESSR 0xbc
/* Trigger Register */
#define AT91_SAMA5D2_TRGR 0xc0
/* Correction Select Register */
#define AT91_SAMA5D2_COSR 0xd0
/* Correction Value Register */
#define AT91_SAMA5D2_CVR 0xd4
/* Channel Error Correction Register */
#define AT91_SAMA5D2_CECR 0xd8
/* Write Protection Mode Register */
#define AT91_SAMA5D2_WPMR 0xe4
/* Write Protection Status Register */
#define AT91_SAMA5D2_WPSR 0xe8
/* Version Register */
#define AT91_SAMA5D2_VERSION 0xfc
#define AT91_AT91_SAMA5D2_CHAN(num, addr) \
{ \
.type = IIO_VOLTAGE, \
.channel = num, \
.address = addr, \
.scan_type = { \
.sign = 'u', \
.realbits = 12, \
}, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\
.datasheet_name = "CH"#num, \
.indexed = 1, \
}
#define at91_adc_readl(st, reg) readl_relaxed(st->base + reg)
#define at91_adc_writel(st, reg, val) writel_relaxed(val, st->base + reg)
struct at91_adc_soc_info {
unsigned startup_time;
unsigned min_sample_rate;
unsigned max_sample_rate;
};
struct at91_adc_state {
void __iomem *base;
int irq;
struct clk *per_clk;
struct regulator *reg;
struct regulator *vref;
int vref_uv;
const struct iio_chan_spec *chan;
bool conversion_done;
u32 conversion_value;
struct at91_adc_soc_info soc_info;
wait_queue_head_t wq_data_available;
/*
* lock to prevent concurrent 'single conversion' requests through
* sysfs.
*/
struct mutex lock;
};
static const struct iio_chan_spec at91_adc_channels[] = {
AT91_AT91_SAMA5D2_CHAN(0, 0x50),
AT91_AT91_SAMA5D2_CHAN(1, 0x54),
AT91_AT91_SAMA5D2_CHAN(2, 0x58),
AT91_AT91_SAMA5D2_CHAN(3, 0x5c),
AT91_AT91_SAMA5D2_CHAN(4, 0x60),
AT91_AT91_SAMA5D2_CHAN(5, 0x64),
AT91_AT91_SAMA5D2_CHAN(6, 0x68),
AT91_AT91_SAMA5D2_CHAN(7, 0x6c),
AT91_AT91_SAMA5D2_CHAN(8, 0x70),
AT91_AT91_SAMA5D2_CHAN(9, 0x74),
AT91_AT91_SAMA5D2_CHAN(10, 0x78),
AT91_AT91_SAMA5D2_CHAN(11, 0x7c),
};
static unsigned at91_adc_startup_time(unsigned startup_time_min,
unsigned adc_clk_khz)
{
const unsigned startup_lookup[] = {
0, 8, 16, 24,
64, 80, 96, 112,
512, 576, 640, 704,
768, 832, 896, 960
};
unsigned ticks_min, i;
/*
* Since the adc frequency is checked before, there is no reason
* to not meet the startup time constraint.
*/
ticks_min = startup_time_min * adc_clk_khz / 1000;
for (i = 0; i < ARRAY_SIZE(startup_lookup); i++)
if (startup_lookup[i] > ticks_min)
break;
return i;
}
static void at91_adc_setup_samp_freq(struct at91_adc_state *st, unsigned freq)
{
struct iio_dev *indio_dev = iio_priv_to_dev(st);
unsigned f_per, prescal, startup;
f_per = clk_get_rate(st->per_clk);
prescal = (f_per / (2 * freq)) - 1;
startup = at91_adc_startup_time(st->soc_info.startup_time,
freq / 1000);
at91_adc_writel(st, AT91_SAMA5D2_MR,
AT91_SAMA5D2_MR_TRANSFER(2)
| AT91_SAMA5D2_MR_STARTUP(startup)
| AT91_SAMA5D2_MR_PRESCAL(prescal));
dev_dbg(&indio_dev->dev, "freq: %u, startup: %u, prescal: %u\n",
freq, startup, prescal);
}
static unsigned at91_adc_get_sample_freq(struct at91_adc_state *st)
{
unsigned f_adc, f_per = clk_get_rate(st->per_clk);
unsigned mr, prescal;
mr = at91_adc_readl(st, AT91_SAMA5D2_MR);
prescal = (mr >> AT91_SAMA5D2_MR_PRESCAL_OFFSET)
& AT91_SAMA5D2_MR_PRESCAL_MAX;
f_adc = f_per / (2 * (prescal + 1));
return f_adc;
}
static irqreturn_t at91_adc_interrupt(int irq, void *private)
{
struct iio_dev *indio = private;
struct at91_adc_state *st = iio_priv(indio);
u32 status = at91_adc_readl(st, AT91_SAMA5D2_ISR);
u32 imr = at91_adc_readl(st, AT91_SAMA5D2_IMR);
if (status & imr) {
st->conversion_value = at91_adc_readl(st, st->chan->address);
st->conversion_done = true;
wake_up_interruptible(&st->wq_data_available);
return IRQ_HANDLED;
}
return IRQ_NONE;
}
static int at91_adc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct at91_adc_state *st = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
mutex_lock(&st->lock);
st->chan = chan;
at91_adc_writel(st, AT91_SAMA5D2_CHER, BIT(chan->channel));
at91_adc_writel(st, AT91_SAMA5D2_IER, BIT(chan->channel));
at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_START);
ret = wait_event_interruptible_timeout(st->wq_data_available,
st->conversion_done,
msecs_to_jiffies(1000));
if (ret == 0)
ret = -ETIMEDOUT;
if (ret > 0) {
*val = st->conversion_value;
ret = IIO_VAL_INT;
st->conversion_done = false;
}
at91_adc_writel(st, AT91_SAMA5D2_IDR, BIT(chan->channel));
at91_adc_writel(st, AT91_SAMA5D2_CHDR, BIT(chan->channel));
mutex_unlock(&st->lock);
return ret;
case IIO_CHAN_INFO_SCALE:
*val = st->vref_uv / 1000;
*val2 = chan->scan_type.realbits;
return IIO_VAL_FRACTIONAL_LOG2;
case IIO_CHAN_INFO_SAMP_FREQ:
*val = at91_adc_get_sample_freq(st);
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static int at91_adc_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct at91_adc_state *st = iio_priv(indio_dev);
if (mask != IIO_CHAN_INFO_SAMP_FREQ)
return -EINVAL;
if (val < st->soc_info.min_sample_rate ||
val > st->soc_info.max_sample_rate)
return -EINVAL;
at91_adc_setup_samp_freq(st, val);
return 0;
}
static const struct iio_info at91_adc_info = {
.read_raw = &at91_adc_read_raw,
.write_raw = &at91_adc_write_raw,
.driver_module = THIS_MODULE,
};
static int at91_adc_probe(struct platform_device *pdev)
{
struct iio_dev *indio_dev;
struct at91_adc_state *st;
struct resource *res;
int ret;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
indio_dev->dev.parent = &pdev->dev;
indio_dev->name = dev_name(&pdev->dev);
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &at91_adc_info;
indio_dev->channels = at91_adc_channels;
indio_dev->num_channels = ARRAY_SIZE(at91_adc_channels);
st = iio_priv(indio_dev);
ret = of_property_read_u32(pdev->dev.of_node,
"atmel,min-sample-rate-hz",
&st->soc_info.min_sample_rate);
if (ret) {
dev_err(&pdev->dev,
"invalid or missing value for atmel,min-sample-rate-hz\n");
return ret;
}
ret = of_property_read_u32(pdev->dev.of_node,
"atmel,max-sample-rate-hz",
&st->soc_info.max_sample_rate);
if (ret) {
dev_err(&pdev->dev,
"invalid or missing value for atmel,max-sample-rate-hz\n");
return ret;
}
ret = of_property_read_u32(pdev->dev.of_node, "atmel,startup-time-ms",
&st->soc_info.startup_time);
if (ret) {
dev_err(&pdev->dev,
"invalid or missing value for atmel,startup-time-ms\n");
return ret;
}
init_waitqueue_head(&st->wq_data_available);
mutex_init(&st->lock);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -EINVAL;
st->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(st->base))
return PTR_ERR(st->base);
st->irq = platform_get_irq(pdev, 0);
if (st->irq <= 0) {
if (!st->irq)
st->irq = -ENXIO;
return st->irq;
}
st->per_clk = devm_clk_get(&pdev->dev, "adc_clk");
if (IS_ERR(st->per_clk))
return PTR_ERR(st->per_clk);
st->reg = devm_regulator_get(&pdev->dev, "vddana");
if (IS_ERR(st->reg))
return PTR_ERR(st->reg);
st->vref = devm_regulator_get(&pdev->dev, "vref");
if (IS_ERR(st->vref))
return PTR_ERR(st->vref);
ret = devm_request_irq(&pdev->dev, st->irq, at91_adc_interrupt, 0,
pdev->dev.driver->name, indio_dev);
if (ret)
return ret;
ret = regulator_enable(st->reg);
if (ret)
return ret;
ret = regulator_enable(st->vref);
if (ret)
goto reg_disable;
st->vref_uv = regulator_get_voltage(st->vref);
if (st->vref_uv <= 0) {
ret = -EINVAL;
goto vref_disable;
}
at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST);
at91_adc_writel(st, AT91_SAMA5D2_IDR, 0xffffffff);
at91_adc_setup_samp_freq(st, st->soc_info.min_sample_rate);
ret = clk_prepare_enable(st->per_clk);
if (ret)
goto vref_disable;
ret = iio_device_register(indio_dev);
if (ret < 0)
goto per_clk_disable_unprepare;
dev_info(&pdev->dev, "version: %x\n",
readl_relaxed(st->base + AT91_SAMA5D2_VERSION));
return 0;
per_clk_disable_unprepare:
clk_disable_unprepare(st->per_clk);
vref_disable:
regulator_disable(st->vref);
reg_disable:
regulator_disable(st->reg);
return ret;
}
static int at91_adc_remove(struct platform_device *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct at91_adc_state *st = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
clk_disable_unprepare(st->per_clk);
regulator_disable(st->vref);
regulator_disable(st->reg);
return 0;
}
static const struct of_device_id at91_adc_dt_match[] = {
{
.compatible = "atmel,sama5d2-adc",
}, {
/* sentinel */
}
};
MODULE_DEVICE_TABLE(of, at91_adc_dt_match);
static struct platform_driver at91_adc_driver = {
.probe = at91_adc_probe,
.remove = at91_adc_remove,
.driver = {
.name = "at91-sama5d2_adc",
.of_match_table = at91_adc_dt_match,
},
};
module_platform_driver(at91_adc_driver)
MODULE_AUTHOR("Ludovic Desroches <ludovic.desroches@atmel.com>");
MODULE_DESCRIPTION("Atmel AT91 SAMA5D2 ADC");
MODULE_LICENSE("GPL v2");

View File

@ -46,7 +46,7 @@ struct axp288_adc_info {
struct regmap *regmap;
};
static const struct iio_chan_spec const axp288_adc_channels[] = {
static const struct iio_chan_spec axp288_adc_channels[] = {
{
.indexed = 1,
.type = IIO_TEMP,

View File

@ -19,17 +19,18 @@
*
* Configurable 7-bit I2C slave address from 0x40 to 0x4F
*/
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/iio/kfifo_buf.h>
#include <linux/iio/sysfs.h>
#include <linux/i2c.h>
#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/platform_data/ina2xx.h>
#include <linux/util_macros.h>
#include <linux/platform_data/ina2xx.h>
/* INA2XX registers definition */
#define INA2XX_CONFIG 0x00
#define INA2XX_SHUNT_VOLTAGE 0x01 /* readonly */
@ -38,7 +39,7 @@
#define INA2XX_CURRENT 0x04 /* readonly */
#define INA2XX_CALIBRATION 0x05
#define INA226_ALERT_MASK 0x06
#define INA226_ALERT_MASK GENMASK(2, 1)
#define INA266_CVRF BIT(3)
#define INA2XX_MAX_REGISTERS 8
@ -113,7 +114,7 @@ struct ina2xx_chip_info {
struct mutex state_lock;
unsigned int shunt_resistor;
int avg;
s64 prev_ns; /* track buffer capture time, check for underruns*/
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;
@ -121,21 +122,21 @@ struct ina2xx_chip_info {
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,
},
.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,
},
.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,
@ -149,7 +150,7 @@ static int ina2xx_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = regmap_read(chip->regmap, chan->address, &regval);
if (ret < 0)
if (ret)
return ret;
if (is_signed_reg(chan->address))
@ -251,7 +252,7 @@ static int ina226_set_int_time_vbus(struct ina2xx_chip_info *chip,
return -EINVAL;
bits = find_closest(val_us, ina226_conv_time_tab,
ARRAY_SIZE(ina226_conv_time_tab));
ARRAY_SIZE(ina226_conv_time_tab));
chip->int_time_vbus = ina226_conv_time_tab[bits];
@ -270,7 +271,7 @@ static int ina226_set_int_time_vshunt(struct ina2xx_chip_info *chip,
return -EINVAL;
bits = find_closest(val_us, ina226_conv_time_tab,
ARRAY_SIZE(ina226_conv_time_tab));
ARRAY_SIZE(ina226_conv_time_tab));
chip->int_time_vshunt = ina226_conv_time_tab[bits];
@ -285,8 +286,8 @@ static int ina2xx_write_raw(struct iio_dev *indio_dev,
int val, int val2, long mask)
{
struct ina2xx_chip_info *chip = iio_priv(indio_dev);
int ret;
unsigned int config, tmp;
int ret;
if (iio_buffer_enabled(indio_dev))
return -EBUSY;
@ -294,8 +295,8 @@ static int ina2xx_write_raw(struct iio_dev *indio_dev,
mutex_lock(&chip->state_lock);
ret = regmap_read(chip->regmap, INA2XX_CONFIG, &config);
if (ret < 0)
goto _err;
if (ret)
goto err;
tmp = config;
@ -310,19 +311,19 @@ static int ina2xx_write_raw(struct iio_dev *indio_dev,
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:
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)
@ -355,6 +356,7 @@ static int set_shunt_resistor(struct ina2xx_chip_info *chip, unsigned int val)
return -EINVAL;
chip->shunt_resistor = val;
return 0;
}
@ -438,7 +440,6 @@ 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;
@ -462,8 +463,6 @@ static int ina2xx_work_buffer(struct iio_dev *indio_dev)
return ret;
alert &= INA266_CVRF;
trace_printk("Conversion ready: %d\n", !!alert);
} while (!alert);
/*
@ -488,19 +487,14 @@ static int ina2xx_work_buffer(struct iio_dev *indio_dev)
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;
return (unsigned long)(time_b - time_a) / 1000;
};
static int ina2xx_capture_thread(void *data)
{
struct iio_dev *indio_dev = (struct iio_dev *)data;
struct iio_dev *indio_dev = data;
struct ina2xx_chip_info *chip = iio_priv(indio_dev);
unsigned int sampling_us = SAMPLING_PERIOD(chip);
int buffer_us;
@ -530,12 +524,13 @@ 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);
dev_dbg(&indio_dev->dev, "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);
dev_dbg(&indio_dev->dev, "Expected work period: %u us\n", sampling_us);
dev_dbg(&indio_dev->dev, "Async readout mode: %d\n",
chip->allow_async_readout);
chip->prev_ns = iio_get_time_ns();
@ -575,8 +570,7 @@ static int ina2xx_debug_reg(struct iio_dev *indio_dev,
}
/* 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_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,
@ -598,21 +592,23 @@ static const struct attribute_group ina2xx_attribute_group = {
};
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,
.attrs = &ina2xx_attribute_group,
.read_raw = ina2xx_read_raw,
.write_raw = ina2xx_write_raw,
.debugfs_reg_access = ina2xx_debug_reg,
};
/* 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);
int ret;
if (ret < 0)
ret = regmap_write(chip->regmap, INA2XX_CONFIG, config);
if (ret)
return ret;
/*
* Set current LSB to 1mA, shunt is in uOhms
* (equation 13 in datasheet). We hardcode a Current_LSB
@ -621,7 +617,7 @@ static int ina2xx_init(struct ina2xx_chip_info *chip, unsigned int config)
* to the user for now.
*/
regval = DIV_ROUND_CLOSEST(chip->config->calibration_factor,
chip->shunt_resistor);
chip->shunt_resistor);
return regmap_write(chip->regmap, INA2XX_CALIBRATION, regval);
}
@ -632,8 +628,8 @@ static int ina2xx_probe(struct i2c_client *client,
struct ina2xx_chip_info *chip;
struct iio_dev *indio_dev;
struct iio_buffer *buffer;
int ret;
unsigned int val;
int ret;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
if (!indio_dev)
@ -641,8 +637,19 @@ static int ina2xx_probe(struct i2c_client *client,
chip = iio_priv(indio_dev);
/* This is only used for device removal purposes. */
i2c_set_clientdata(client, indio_dev);
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);
}
chip->config = &ina2xx_config[id->driver_data];
mutex_init(&chip->state_lock);
if (of_property_read_u32(client->dev.of_node,
"shunt-resistor", &val) < 0) {
struct ina2xx_platform_data *pdata =
@ -658,25 +665,6 @@ static int ina2xx_probe(struct i2c_client *client,
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;
@ -687,24 +675,28 @@ static int ina2xx_probe(struct i2c_client *client,
}
ret = ina2xx_init(chip, val);
if (ret < 0) {
dev_err(&client->dev, "error configuring the device: %d\n",
ret);
return -ENODEV;
if (ret) {
dev_err(&client->dev, "error configuring the device\n");
return ret;
}
indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
indio_dev->dev.parent = &client->dev;
indio_dev->channels = ina2xx_channels;
indio_dev->num_channels = ARRAY_SIZE(ina2xx_channels);
indio_dev->name = id->name;
indio_dev->info = &ina2xx_info;
indio_dev->setup_ops = &ina2xx_setup_ops;
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);
@ -717,7 +709,6 @@ static int ina2xx_remove(struct i2c_client *client)
INA2XX_MODE_MASK, 0);
}
static const struct i2c_device_id ina2xx_id[] = {
{"ina219", ina219},
{"ina220", ina219},
@ -726,7 +717,6 @@ static const struct i2c_device_id ina2xx_id[] = {
{"ina231", ina226},
{}
};
MODULE_DEVICE_TABLE(i2c, ina2xx_id);
static struct i2c_driver ina2xx_driver = {
@ -737,7 +727,6 @@ static struct i2c_driver ina2xx_driver = {
.remove = ina2xx_remove,
.id_table = ina2xx_id,
};
module_i2c_driver(ina2xx_driver);
MODULE_AUTHOR("Marc Titinger <marc.titinger@baylibre.com>");

View File

@ -187,26 +187,27 @@ out:
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
}
#define MCP320X_VOLTAGE_CHANNEL_DIFF(num) \
#define MCP320X_VOLTAGE_CHANNEL_DIFF(chan1, chan2) \
{ \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = (num * 2), \
.channel2 = (num * 2 + 1), \
.address = (num * 2), \
.channel = (chan1), \
.channel2 = (chan2), \
.address = (chan1), \
.differential = 1, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
}
static const struct iio_chan_spec mcp3201_channels[] = {
MCP320X_VOLTAGE_CHANNEL_DIFF(0),
MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1),
};
static const struct iio_chan_spec mcp3202_channels[] = {
MCP320X_VOLTAGE_CHANNEL(0),
MCP320X_VOLTAGE_CHANNEL(1),
MCP320X_VOLTAGE_CHANNEL_DIFF(0),
MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1),
MCP320X_VOLTAGE_CHANNEL_DIFF(1, 0),
};
static const struct iio_chan_spec mcp3204_channels[] = {
@ -214,8 +215,10 @@ static const struct iio_chan_spec mcp3204_channels[] = {
MCP320X_VOLTAGE_CHANNEL(1),
MCP320X_VOLTAGE_CHANNEL(2),
MCP320X_VOLTAGE_CHANNEL(3),
MCP320X_VOLTAGE_CHANNEL_DIFF(0),
MCP320X_VOLTAGE_CHANNEL_DIFF(1),
MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1),
MCP320X_VOLTAGE_CHANNEL_DIFF(1, 0),
MCP320X_VOLTAGE_CHANNEL_DIFF(2, 3),
MCP320X_VOLTAGE_CHANNEL_DIFF(3, 2),
};
static const struct iio_chan_spec mcp3208_channels[] = {
@ -227,10 +230,14 @@ static const struct iio_chan_spec mcp3208_channels[] = {
MCP320X_VOLTAGE_CHANNEL(5),
MCP320X_VOLTAGE_CHANNEL(6),
MCP320X_VOLTAGE_CHANNEL(7),
MCP320X_VOLTAGE_CHANNEL_DIFF(0),
MCP320X_VOLTAGE_CHANNEL_DIFF(1),
MCP320X_VOLTAGE_CHANNEL_DIFF(2),
MCP320X_VOLTAGE_CHANNEL_DIFF(3),
MCP320X_VOLTAGE_CHANNEL_DIFF(0, 1),
MCP320X_VOLTAGE_CHANNEL_DIFF(1, 0),
MCP320X_VOLTAGE_CHANNEL_DIFF(2, 3),
MCP320X_VOLTAGE_CHANNEL_DIFF(3, 2),
MCP320X_VOLTAGE_CHANNEL_DIFF(4, 5),
MCP320X_VOLTAGE_CHANNEL_DIFF(5, 4),
MCP320X_VOLTAGE_CHANNEL_DIFF(6, 7),
MCP320X_VOLTAGE_CHANNEL_DIFF(7, 6),
};
static const struct iio_info mcp320x_info = {

View File

@ -1,11 +1,12 @@
/*
* mcp3422.c - driver for the Microchip mcp3422/3/4/6/7/8 chip family
* mcp3422.c - driver for the Microchip mcp3421/2/3/4/5/6/7/8 chip family
*
* Copyright (C) 2013, Angelo Compagnucci
* Author: Angelo Compagnucci <angelo.compagnucci@gmail.com>
*
* Datasheet: http://ww1.microchip.com/downloads/en/devicedoc/22088b.pdf
* http://ww1.microchip.com/downloads/en/DeviceDoc/22226a.pdf
* http://ww1.microchip.com/downloads/en/DeviceDoc/22072b.pdf
*
* This driver exports the value of analog input voltage to sysfs, the
* voltage unit is nV.
@ -338,7 +339,7 @@ static int mcp3422_probe(struct i2c_client *client,
u8 config;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
return -ENODEV;
return -EOPNOTSUPP;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*adc));
if (!indio_dev)
@ -357,6 +358,7 @@ static int mcp3422_probe(struct i2c_client *client,
switch (adc->id) {
case 1:
case 5:
indio_dev->channels = mcp3421_channels;
indio_dev->num_channels = ARRAY_SIZE(mcp3421_channels);
break;
@ -395,6 +397,7 @@ static const struct i2c_device_id mcp3422_id[] = {
{ "mcp3422", 2 },
{ "mcp3423", 3 },
{ "mcp3424", 4 },
{ "mcp3425", 5 },
{ "mcp3426", 6 },
{ "mcp3427", 7 },
{ "mcp3428", 8 },
@ -421,5 +424,5 @@ static struct i2c_driver mcp3422_driver = {
module_i2c_driver(mcp3422_driver);
MODULE_AUTHOR("Angelo Compagnucci <angelo.compagnucci@gmail.com>");
MODULE_DESCRIPTION("Microchip mcp3422/3/4/6/7/8 driver");
MODULE_DESCRIPTION("Microchip mcp3421/2/3/4/5/6/7/8 driver");
MODULE_LICENSE("GPL v2");

View File

@ -443,7 +443,8 @@ static void mxs_lradc_setup_ts_channel(struct mxs_lradc *lradc, unsigned ch)
LRADC_CH_NUM_SAMPLES(lradc->over_sample_cnt - 1),
LRADC_CH(ch));
/* from the datasheet:
/*
* from the datasheet:
* "Software must clear this register in preparation for a
* multi-cycle accumulation.
*/
@ -504,7 +505,8 @@ static void mxs_lradc_setup_ts_pressure(struct mxs_lradc *lradc, unsigned ch1,
mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch1));
mxs_lradc_reg_wrt(lradc, reg, LRADC_CH(ch2));
/* from the datasheet:
/*
* from the datasheet:
* "Software must clear this register in preparation for a
* multi-cycle accumulation.
*/
@ -914,7 +916,8 @@ static int mxs_lradc_read_raw(struct iio_dev *iio_dev,
case IIO_CHAN_INFO_SCALE:
if (chan->type == IIO_TEMP) {
/* From the datasheet, we have to multiply by 1.012 and
/*
* From the datasheet, we have to multiply by 1.012 and
* divide by 4
*/
*val = 0;
@ -929,7 +932,8 @@ static int mxs_lradc_read_raw(struct iio_dev *iio_dev,
case IIO_CHAN_INFO_OFFSET:
if (chan->type == IIO_TEMP) {
/* The calculated value from the ADC is in Kelvin, we
/*
* The calculated value from the ADC is in Kelvin, we
* want Celsius for hwmon so the offset is -273.15
* The offset is applied before scaling so it is
* actually -213.15 * 4 / 1.012 = -1079.644268
@ -1750,6 +1754,7 @@ static int mxs_lradc_remove(struct platform_device *pdev)
iio_triggered_buffer_cleanup(iio);
clk_disable_unprepare(lradc->clk);
return 0;
}

View File

@ -534,7 +534,7 @@ static int palmas_gpadc_probe(struct platform_device *pdev)
}
ret = request_threaded_irq(adc->irq, NULL,
palmas_gpadc_irq,
IRQF_ONESHOT | IRQF_EARLY_RESUME, dev_name(adc->dev),
IRQF_ONESHOT, dev_name(adc->dev),
adc);
if (ret < 0) {
dev_err(adc->dev,
@ -549,7 +549,7 @@ static int palmas_gpadc_probe(struct platform_device *pdev)
adc->irq_auto_0 = platform_get_irq(pdev, 1);
ret = request_threaded_irq(adc->irq_auto_0, NULL,
palmas_gpadc_irq_auto,
IRQF_ONESHOT | IRQF_EARLY_RESUME,
IRQF_ONESHOT,
"palmas-adc-auto-0", adc);
if (ret < 0) {
dev_err(adc->dev, "request auto0 irq %d failed: %d\n",
@ -565,7 +565,7 @@ static int palmas_gpadc_probe(struct platform_device *pdev)
adc->irq_auto_1 = platform_get_irq(pdev, 2);
ret = request_threaded_irq(adc->irq_auto_1, NULL,
palmas_gpadc_irq_auto,
IRQF_ONESHOT | IRQF_EARLY_RESUME,
IRQF_ONESHOT,
"palmas-adc-auto-1", adc);
if (ret < 0) {
dev_err(adc->dev, "request auto1 irq %d failed: %d\n",

View File

@ -73,7 +73,7 @@ static int adc081c_probe(struct i2c_client *client,
int err;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
return -ENODEV;
return -EOPNOTSUPP;
iio = devm_iio_device_alloc(&client->dev, sizeof(*adc));
if (!iio)

View File

@ -0,0 +1,288 @@
/*
* ADC0831/ADC0832/ADC0834/ADC0838 8-bit ADC driver
*
* Copyright (c) 2016 Akinobu Mita <akinobu.mita@gmail.com>
*
* 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.
*
* Datasheet: http://www.ti.com/lit/ds/symlink/adc0832-n.pdf
*/
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/iio/iio.h>
#include <linux/regulator/consumer.h>
enum {
adc0831,
adc0832,
adc0834,
adc0838,
};
struct adc0832 {
struct spi_device *spi;
struct regulator *reg;
struct mutex lock;
u8 mux_bits;
u8 tx_buf[2] ____cacheline_aligned;
u8 rx_buf[2];
};
#define ADC0832_VOLTAGE_CHANNEL(chan) \
{ \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = chan, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
}
#define ADC0832_VOLTAGE_CHANNEL_DIFF(chan1, chan2) \
{ \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = (chan1), \
.channel2 = (chan2), \
.differential = 1, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
}
static const struct iio_chan_spec adc0831_channels[] = {
ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1),
};
static const struct iio_chan_spec adc0832_channels[] = {
ADC0832_VOLTAGE_CHANNEL(0),
ADC0832_VOLTAGE_CHANNEL(1),
ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1),
ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0),
};
static const struct iio_chan_spec adc0834_channels[] = {
ADC0832_VOLTAGE_CHANNEL(0),
ADC0832_VOLTAGE_CHANNEL(1),
ADC0832_VOLTAGE_CHANNEL(2),
ADC0832_VOLTAGE_CHANNEL(3),
ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1),
ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0),
ADC0832_VOLTAGE_CHANNEL_DIFF(2, 3),
ADC0832_VOLTAGE_CHANNEL_DIFF(3, 2),
};
static const struct iio_chan_spec adc0838_channels[] = {
ADC0832_VOLTAGE_CHANNEL(0),
ADC0832_VOLTAGE_CHANNEL(1),
ADC0832_VOLTAGE_CHANNEL(2),
ADC0832_VOLTAGE_CHANNEL(3),
ADC0832_VOLTAGE_CHANNEL(4),
ADC0832_VOLTAGE_CHANNEL(5),
ADC0832_VOLTAGE_CHANNEL(6),
ADC0832_VOLTAGE_CHANNEL(7),
ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1),
ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0),
ADC0832_VOLTAGE_CHANNEL_DIFF(2, 3),
ADC0832_VOLTAGE_CHANNEL_DIFF(3, 2),
ADC0832_VOLTAGE_CHANNEL_DIFF(4, 5),
ADC0832_VOLTAGE_CHANNEL_DIFF(5, 4),
ADC0832_VOLTAGE_CHANNEL_DIFF(6, 7),
ADC0832_VOLTAGE_CHANNEL_DIFF(7, 6),
};
static int adc0831_adc_conversion(struct adc0832 *adc)
{
struct spi_device *spi = adc->spi;
int ret;
ret = spi_read(spi, &adc->rx_buf, 2);
if (ret)
return ret;
/*
* Skip TRI-STATE and a leading zero
*/
return (adc->rx_buf[0] << 2 & 0xff) | (adc->rx_buf[1] >> 6);
}
static int adc0832_adc_conversion(struct adc0832 *adc, int channel,
bool differential)
{
struct spi_device *spi = adc->spi;
struct spi_transfer xfer = {
.tx_buf = adc->tx_buf,
.rx_buf = adc->rx_buf,
.len = 2,
};
int ret;
if (!adc->mux_bits)
return adc0831_adc_conversion(adc);
/* start bit */
adc->tx_buf[0] = 1 << (adc->mux_bits + 1);
/* single-ended or differential */
adc->tx_buf[0] |= differential ? 0 : (1 << adc->mux_bits);
/* odd / sign */
adc->tx_buf[0] |= (channel % 2) << (adc->mux_bits - 1);
/* select */
if (adc->mux_bits > 1)
adc->tx_buf[0] |= channel / 2;
/* align Data output BIT7 (MSB) to 8-bit boundary */
adc->tx_buf[0] <<= 1;
ret = spi_sync_transfer(spi, &xfer, 1);
if (ret)
return ret;
return adc->rx_buf[1];
}
static int adc0832_read_raw(struct iio_dev *iio,
struct iio_chan_spec const *channel, int *value,
int *shift, long mask)
{
struct adc0832 *adc = iio_priv(iio);
switch (mask) {
case IIO_CHAN_INFO_RAW:
mutex_lock(&adc->lock);
*value = adc0832_adc_conversion(adc, channel->channel,
channel->differential);
mutex_unlock(&adc->lock);
if (*value < 0)
return *value;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*value = regulator_get_voltage(adc->reg);
if (*value < 0)
return *value;
/* convert regulator output voltage to mV */
*value /= 1000;
*shift = 8;
return IIO_VAL_FRACTIONAL_LOG2;
}
return -EINVAL;
}
static const struct iio_info adc0832_info = {
.read_raw = adc0832_read_raw,
.driver_module = THIS_MODULE,
};
static int adc0832_probe(struct spi_device *spi)
{
struct iio_dev *indio_dev;
struct adc0832 *adc;
int ret;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
if (!indio_dev)
return -ENOMEM;
adc = iio_priv(indio_dev);
adc->spi = spi;
mutex_init(&adc->lock);
indio_dev->name = spi_get_device_id(spi)->name;
indio_dev->dev.parent = &spi->dev;
indio_dev->info = &adc0832_info;
indio_dev->modes = INDIO_DIRECT_MODE;
switch (spi_get_device_id(spi)->driver_data) {
case adc0831:
adc->mux_bits = 0;
indio_dev->channels = adc0831_channels;
indio_dev->num_channels = ARRAY_SIZE(adc0831_channels);
break;
case adc0832:
adc->mux_bits = 1;
indio_dev->channels = adc0832_channels;
indio_dev->num_channels = ARRAY_SIZE(adc0832_channels);
break;
case adc0834:
adc->mux_bits = 2;
indio_dev->channels = adc0834_channels;
indio_dev->num_channels = ARRAY_SIZE(adc0834_channels);
break;
case adc0838:
adc->mux_bits = 3;
indio_dev->channels = adc0838_channels;
indio_dev->num_channels = ARRAY_SIZE(adc0838_channels);
break;
default:
return -EINVAL;
}
adc->reg = devm_regulator_get(&spi->dev, "vref");
if (IS_ERR(adc->reg))
return PTR_ERR(adc->reg);
ret = regulator_enable(adc->reg);
if (ret)
return ret;
spi_set_drvdata(spi, indio_dev);
ret = iio_device_register(indio_dev);
if (ret)
regulator_disable(adc->reg);
return ret;
}
static int adc0832_remove(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct adc0832 *adc = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
regulator_disable(adc->reg);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id adc0832_dt_ids[] = {
{ .compatible = "ti,adc0831", },
{ .compatible = "ti,adc0832", },
{ .compatible = "ti,adc0834", },
{ .compatible = "ti,adc0838", },
{}
};
MODULE_DEVICE_TABLE(of, adc0832_dt_ids);
#endif
static const struct spi_device_id adc0832_id[] = {
{ "adc0831", adc0831 },
{ "adc0832", adc0832 },
{ "adc0834", adc0834 },
{ "adc0838", adc0838 },
{}
};
MODULE_DEVICE_TABLE(spi, adc0832_id);
static struct spi_driver adc0832_driver = {
.driver = {
.name = "adc0832",
.of_match_table = of_match_ptr(adc0832_dt_ids),
},
.probe = adc0832_probe,
.remove = adc0832_remove,
.id_table = adc0832_id,
};
module_spi_driver(adc0832_driver);
MODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>");
MODULE_DESCRIPTION("ADC0831/ADC0832/ADC0834/ADC0838 driver");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,612 @@
/*
* ADS1015 - Texas Instruments Analog-to-Digital Converter
*
* Copyright (c) 2016, 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 ADS1015 ADC 7-bit I2C slave address:
* * 0x48 - ADDR connected to Ground
* * 0x49 - ADDR connected to Vdd
* * 0x4A - ADDR connected to SDA
* * 0x4B - ADDR connected to SCL
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/pm_runtime.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/i2c/ads1015.h>
#include <linux/iio/iio.h>
#include <linux/iio/types.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
#define ADS1015_DRV_NAME "ads1015"
#define ADS1015_CONV_REG 0x00
#define ADS1015_CFG_REG 0x01
#define ADS1015_CFG_DR_SHIFT 5
#define ADS1015_CFG_MOD_SHIFT 8
#define ADS1015_CFG_PGA_SHIFT 9
#define ADS1015_CFG_MUX_SHIFT 12
#define ADS1015_CFG_DR_MASK GENMASK(7, 5)
#define ADS1015_CFG_MOD_MASK BIT(8)
#define ADS1015_CFG_PGA_MASK GENMASK(11, 9)
#define ADS1015_CFG_MUX_MASK GENMASK(14, 12)
/* device operating modes */
#define ADS1015_CONTINUOUS 0
#define ADS1015_SINGLESHOT 1
#define ADS1015_SLEEP_DELAY_MS 2000
#define ADS1015_DEFAULT_PGA 2
#define ADS1015_DEFAULT_DATA_RATE 4
#define ADS1015_DEFAULT_CHAN 0
enum ads1015_channels {
ADS1015_AIN0_AIN1 = 0,
ADS1015_AIN0_AIN3,
ADS1015_AIN1_AIN3,
ADS1015_AIN2_AIN3,
ADS1015_AIN0,
ADS1015_AIN1,
ADS1015_AIN2,
ADS1015_AIN3,
ADS1015_TIMESTAMP,
};
static const unsigned int ads1015_data_rate[] = {
128, 250, 490, 920, 1600, 2400, 3300, 3300
};
static const struct {
int scale;
int uscale;
} ads1015_scale[] = {
{3, 0},
{2, 0},
{1, 0},
{0, 500000},
{0, 250000},
{0, 125000},
{0, 125000},
{0, 125000},
};
#define ADS1015_V_CHAN(_chan, _addr) { \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.address = _addr, \
.channel = _chan, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.scan_index = _addr, \
.scan_type = { \
.sign = 's', \
.realbits = 12, \
.storagebits = 16, \
.shift = 4, \
.endianness = IIO_CPU, \
}, \
}
#define ADS1015_V_DIFF_CHAN(_chan, _chan2, _addr) { \
.type = IIO_VOLTAGE, \
.differential = 1, \
.indexed = 1, \
.address = _addr, \
.channel = _chan, \
.channel2 = _chan2, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.scan_index = _addr, \
.scan_type = { \
.sign = 's', \
.realbits = 12, \
.storagebits = 16, \
.shift = 4, \
.endianness = IIO_CPU, \
}, \
}
struct ads1015_data {
struct regmap *regmap;
/*
* Protects ADC ops, e.g: concurrent sysfs/buffered
* data reads, configuration updates
*/
struct mutex lock;
struct ads1015_channel_data channel_data[ADS1015_CHANNELS];
};
static bool ads1015_is_writeable_reg(struct device *dev, unsigned int reg)
{
return (reg == ADS1015_CFG_REG);
}
static const struct regmap_config ads1015_regmap_config = {
.reg_bits = 8,
.val_bits = 16,
.max_register = ADS1015_CFG_REG,
.writeable_reg = ads1015_is_writeable_reg,
};
static const struct iio_chan_spec ads1015_channels[] = {
ADS1015_V_DIFF_CHAN(0, 1, ADS1015_AIN0_AIN1),
ADS1015_V_DIFF_CHAN(0, 3, ADS1015_AIN0_AIN3),
ADS1015_V_DIFF_CHAN(1, 3, ADS1015_AIN1_AIN3),
ADS1015_V_DIFF_CHAN(2, 3, ADS1015_AIN2_AIN3),
ADS1015_V_CHAN(0, ADS1015_AIN0),
ADS1015_V_CHAN(1, ADS1015_AIN1),
ADS1015_V_CHAN(2, ADS1015_AIN2),
ADS1015_V_CHAN(3, ADS1015_AIN3),
IIO_CHAN_SOFT_TIMESTAMP(ADS1015_TIMESTAMP),
};
static int ads1015_set_power_state(struct ads1015_data *data, bool on)
{
int ret;
struct device *dev = regmap_get_device(data->regmap);
if (on) {
ret = pm_runtime_get_sync(dev);
if (ret < 0)
pm_runtime_put_noidle(dev);
} else {
pm_runtime_mark_last_busy(dev);
ret = pm_runtime_put_autosuspend(dev);
}
return ret;
}
static
int ads1015_get_adc_result(struct ads1015_data *data, int chan, int *val)
{
int ret, pga, dr, conv_time;
bool change;
if (chan < 0 || chan >= ADS1015_CHANNELS)
return -EINVAL;
pga = data->channel_data[chan].pga;
dr = data->channel_data[chan].data_rate;
ret = regmap_update_bits_check(data->regmap, ADS1015_CFG_REG,
ADS1015_CFG_MUX_MASK |
ADS1015_CFG_PGA_MASK,
chan << ADS1015_CFG_MUX_SHIFT |
pga << ADS1015_CFG_PGA_SHIFT,
&change);
if (ret < 0)
return ret;
if (change) {
conv_time = DIV_ROUND_UP(USEC_PER_SEC, ads1015_data_rate[dr]);
usleep_range(conv_time, conv_time + 1);
}
return regmap_read(data->regmap, ADS1015_CONV_REG, val);
}
static irqreturn_t ads1015_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct ads1015_data *data = iio_priv(indio_dev);
s16 buf[8]; /* 1x s16 ADC val + 3x s16 padding + 4x s16 timestamp */
int chan, ret, res;
memset(buf, 0, sizeof(buf));
mutex_lock(&data->lock);
chan = find_first_bit(indio_dev->active_scan_mask,
indio_dev->masklength);
ret = ads1015_get_adc_result(data, chan, &res);
if (ret < 0) {
mutex_unlock(&data->lock);
goto err;
}
buf[0] = res;
mutex_unlock(&data->lock);
iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns());
err:
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
static int ads1015_set_scale(struct ads1015_data *data, int chan,
int scale, int uscale)
{
int i, ret, rindex = -1;
for (i = 0; i < ARRAY_SIZE(ads1015_scale); i++)
if (ads1015_scale[i].scale == scale &&
ads1015_scale[i].uscale == uscale) {
rindex = i;
break;
}
if (rindex < 0)
return -EINVAL;
ret = regmap_update_bits(data->regmap, ADS1015_CFG_REG,
ADS1015_CFG_PGA_MASK,
rindex << ADS1015_CFG_PGA_SHIFT);
if (ret < 0)
return ret;
data->channel_data[chan].pga = rindex;
return 0;
}
static int ads1015_set_data_rate(struct ads1015_data *data, int chan, int rate)
{
int i, ret, rindex = -1;
for (i = 0; i < ARRAY_SIZE(ads1015_data_rate); i++)
if (ads1015_data_rate[i] == rate) {
rindex = i;
break;
}
if (rindex < 0)
return -EINVAL;
ret = regmap_update_bits(data->regmap, ADS1015_CFG_REG,
ADS1015_CFG_DR_MASK,
rindex << ADS1015_CFG_DR_SHIFT);
if (ret < 0)
return ret;
data->channel_data[chan].data_rate = rindex;
return 0;
}
static int ads1015_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
int ret, idx;
struct ads1015_data *data = iio_priv(indio_dev);
mutex_lock(&indio_dev->mlock);
mutex_lock(&data->lock);
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (iio_buffer_enabled(indio_dev)) {
ret = -EBUSY;
break;
}
ret = ads1015_set_power_state(data, true);
if (ret < 0)
break;
ret = ads1015_get_adc_result(data, chan->address, val);
if (ret < 0) {
ads1015_set_power_state(data, false);
break;
}
/* 12 bit res, D0 is bit 4 in conversion register */
*val = sign_extend32(*val >> 4, 11);
ret = ads1015_set_power_state(data, false);
if (ret < 0)
break;
ret = IIO_VAL_INT;
break;
case IIO_CHAN_INFO_SCALE:
idx = data->channel_data[chan->address].pga;
*val = ads1015_scale[idx].scale;
*val2 = ads1015_scale[idx].uscale;
ret = IIO_VAL_INT_PLUS_MICRO;
break;
case IIO_CHAN_INFO_SAMP_FREQ:
idx = data->channel_data[chan->address].data_rate;
*val = ads1015_data_rate[idx];
ret = IIO_VAL_INT;
break;
default:
ret = -EINVAL;
break;
}
mutex_unlock(&data->lock);
mutex_unlock(&indio_dev->mlock);
return ret;
}
static int ads1015_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val,
int val2, long mask)
{
struct ads1015_data *data = iio_priv(indio_dev);
int ret;
mutex_lock(&data->lock);
switch (mask) {
case IIO_CHAN_INFO_SCALE:
ret = ads1015_set_scale(data, chan->address, val, val2);
break;
case IIO_CHAN_INFO_SAMP_FREQ:
ret = ads1015_set_data_rate(data, chan->address, val);
break;
default:
ret = -EINVAL;
break;
}
mutex_unlock(&data->lock);
return ret;
}
static int ads1015_buffer_preenable(struct iio_dev *indio_dev)
{
return ads1015_set_power_state(iio_priv(indio_dev), true);
}
static int ads1015_buffer_postdisable(struct iio_dev *indio_dev)
{
return ads1015_set_power_state(iio_priv(indio_dev), false);
}
static const struct iio_buffer_setup_ops ads1015_buffer_setup_ops = {
.preenable = ads1015_buffer_preenable,
.postenable = iio_triggered_buffer_postenable,
.predisable = iio_triggered_buffer_predisable,
.postdisable = ads1015_buffer_postdisable,
.validate_scan_mask = &iio_validate_scan_mask_onehot,
};
static IIO_CONST_ATTR(scale_available, "3 2 1 0.5 0.25 0.125");
static IIO_CONST_ATTR(sampling_frequency_available,
"128 250 490 920 1600 2400 3300");
static struct attribute *ads1015_attributes[] = {
&iio_const_attr_scale_available.dev_attr.attr,
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
NULL,
};
static const struct attribute_group ads1015_attribute_group = {
.attrs = ads1015_attributes,
};
static const struct iio_info ads1015_info = {
.driver_module = THIS_MODULE,
.read_raw = ads1015_read_raw,
.write_raw = ads1015_write_raw,
.attrs = &ads1015_attribute_group,
};
#ifdef CONFIG_OF
static int ads1015_get_channels_config_of(struct i2c_client *client)
{
struct ads1015_data *data = i2c_get_clientdata(client);
struct device_node *node;
if (!client->dev.of_node ||
!of_get_next_child(client->dev.of_node, NULL))
return -EINVAL;
for_each_child_of_node(client->dev.of_node, node) {
u32 pval;
unsigned int channel;
unsigned int pga = ADS1015_DEFAULT_PGA;
unsigned int data_rate = ADS1015_DEFAULT_DATA_RATE;
if (of_property_read_u32(node, "reg", &pval)) {
dev_err(&client->dev, "invalid reg on %s\n",
node->full_name);
continue;
}
channel = pval;
if (channel >= ADS1015_CHANNELS) {
dev_err(&client->dev,
"invalid channel index %d on %s\n",
channel, node->full_name);
continue;
}
if (!of_property_read_u32(node, "ti,gain", &pval)) {
pga = pval;
if (pga > 6) {
dev_err(&client->dev, "invalid gain on %s\n",
node->full_name);
return -EINVAL;
}
}
if (!of_property_read_u32(node, "ti,datarate", &pval)) {
data_rate = pval;
if (data_rate > 7) {
dev_err(&client->dev,
"invalid data_rate on %s\n",
node->full_name);
return -EINVAL;
}
}
data->channel_data[channel].pga = pga;
data->channel_data[channel].data_rate = data_rate;
}
return 0;
}
#endif
static void ads1015_get_channels_config(struct i2c_client *client)
{
unsigned int k;
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct ads1015_data *data = iio_priv(indio_dev);
struct ads1015_platform_data *pdata = dev_get_platdata(&client->dev);
/* prefer platform data */
if (pdata) {
memcpy(data->channel_data, pdata->channel_data,
sizeof(data->channel_data));
return;
}
#ifdef CONFIG_OF
if (!ads1015_get_channels_config_of(client))
return;
#endif
/* fallback on default configuration */
for (k = 0; k < ADS1015_CHANNELS; ++k) {
data->channel_data[k].pga = ADS1015_DEFAULT_PGA;
data->channel_data[k].data_rate = ADS1015_DEFAULT_DATA_RATE;
}
}
static int ads1015_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct iio_dev *indio_dev;
struct ads1015_data *data;
int ret;
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);
mutex_init(&data->lock);
indio_dev->dev.parent = &client->dev;
indio_dev->info = &ads1015_info;
indio_dev->name = ADS1015_DRV_NAME;
indio_dev->channels = ads1015_channels;
indio_dev->num_channels = ARRAY_SIZE(ads1015_channels);
indio_dev->modes = INDIO_DIRECT_MODE;
/* we need to keep this ABI the same as used by hwmon ADS1015 driver */
ads1015_get_channels_config(client);
data->regmap = devm_regmap_init_i2c(client, &ads1015_regmap_config);
if (IS_ERR(data->regmap)) {
dev_err(&client->dev, "Failed to allocate register map\n");
return PTR_ERR(data->regmap);
}
ret = iio_triggered_buffer_setup(indio_dev, NULL,
ads1015_trigger_handler,
&ads1015_buffer_setup_ops);
if (ret < 0) {
dev_err(&client->dev, "iio triggered buffer setup failed\n");
return ret;
}
ret = pm_runtime_set_active(&client->dev);
if (ret)
goto err_buffer_cleanup;
pm_runtime_set_autosuspend_delay(&client->dev, ADS1015_SLEEP_DELAY_MS);
pm_runtime_use_autosuspend(&client->dev);
pm_runtime_enable(&client->dev);
ret = iio_device_register(indio_dev);
if (ret < 0) {
dev_err(&client->dev, "Failed to register IIO device\n");
goto err_buffer_cleanup;
}
return 0;
err_buffer_cleanup:
iio_triggered_buffer_cleanup(indio_dev);
return ret;
}
static int ads1015_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct ads1015_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_triggered_buffer_cleanup(indio_dev);
/* power down single shot mode */
return regmap_update_bits(data->regmap, ADS1015_CFG_REG,
ADS1015_CFG_MOD_MASK,
ADS1015_SINGLESHOT << ADS1015_CFG_MOD_SHIFT);
}
#ifdef CONFIG_PM
static int ads1015_runtime_suspend(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct ads1015_data *data = iio_priv(indio_dev);
return regmap_update_bits(data->regmap, ADS1015_CFG_REG,
ADS1015_CFG_MOD_MASK,
ADS1015_SINGLESHOT << ADS1015_CFG_MOD_SHIFT);
}
static int ads1015_runtime_resume(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct ads1015_data *data = iio_priv(indio_dev);
return regmap_update_bits(data->regmap, ADS1015_CFG_REG,
ADS1015_CFG_MOD_MASK,
ADS1015_CONTINUOUS << ADS1015_CFG_MOD_SHIFT);
}
#endif
static const struct dev_pm_ops ads1015_pm_ops = {
SET_RUNTIME_PM_OPS(ads1015_runtime_suspend,
ads1015_runtime_resume, NULL)
};
static const struct i2c_device_id ads1015_id[] = {
{"ads1015", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, ads1015_id);
static struct i2c_driver ads1015_driver = {
.driver = {
.name = ADS1015_DRV_NAME,
.pm = &ads1015_pm_ops,
},
.probe = ads1015_probe,
.remove = ads1015_remove,
.id_table = ads1015_id,
};
module_i2c_driver(ads1015_driver);
MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
MODULE_DESCRIPTION("Texas Instruments ADS1015 ADC driver");
MODULE_LICENSE("GPL v2");

View File

@ -93,12 +93,7 @@ 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().
*/
dmaengine_terminate_sync(dmaengine_buffer->chan);
iio_dma_buffer_block_list_abort(queue, &dmaengine_buffer->active);
}

View File

@ -4,6 +4,20 @@
menu "Chemical Sensors"
config ATLAS_PH_SENSOR
tristate "Atlas Scientific OEM pH-SM sensor"
depends on I2C
select REGMAP_I2C
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
select IRQ_WORK
help
Say Y here to build I2C interface support for the Atlas
Scientific OEM pH-SM sensor.
To compile this driver as module, choose M here: the
module will be called atlas-ph-sensor.
config IAQCORE
tristate "AMS iAQ-Core VOC sensors"
depends on I2C

View File

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

View File

@ -0,0 +1,509 @@
/*
* atlas-ph-sensor.c - Support for Atlas Scientific OEM pH-SM sensor
*
* Copyright (C) 2015 Matt Ranostay <mranostay@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/err.h>
#include <linux/irq.h>
#include <linux/irq_work.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/pm_runtime.h>
#define ATLAS_REGMAP_NAME "atlas_ph_regmap"
#define ATLAS_DRV_NAME "atlas_ph"
#define ATLAS_REG_DEV_TYPE 0x00
#define ATLAS_REG_DEV_VERSION 0x01
#define ATLAS_REG_INT_CONTROL 0x04
#define ATLAS_REG_INT_CONTROL_EN BIT(3)
#define ATLAS_REG_PWR_CONTROL 0x06
#define ATLAS_REG_CALIB_STATUS 0x0d
#define ATLAS_REG_CALIB_STATUS_MASK 0x07
#define ATLAS_REG_CALIB_STATUS_LOW BIT(0)
#define ATLAS_REG_CALIB_STATUS_MID BIT(1)
#define ATLAS_REG_CALIB_STATUS_HIGH BIT(2)
#define ATLAS_REG_TEMP_DATA 0x0e
#define ATLAS_REG_PH_DATA 0x16
#define ATLAS_PH_INT_TIME_IN_US 450000
struct atlas_data {
struct i2c_client *client;
struct iio_trigger *trig;
struct regmap *regmap;
struct irq_work work;
__be32 buffer[4]; /* 32-bit pH data + 32-bit pad + 64-bit timestamp */
};
static const struct regmap_range atlas_volatile_ranges[] = {
regmap_reg_range(ATLAS_REG_INT_CONTROL, ATLAS_REG_INT_CONTROL),
regmap_reg_range(ATLAS_REG_PH_DATA, ATLAS_REG_PH_DATA + 4),
};
static const struct regmap_access_table atlas_volatile_table = {
.yes_ranges = atlas_volatile_ranges,
.n_yes_ranges = ARRAY_SIZE(atlas_volatile_ranges),
};
static const struct regmap_config atlas_regmap_config = {
.name = ATLAS_REGMAP_NAME,
.reg_bits = 8,
.val_bits = 8,
.volatile_table = &atlas_volatile_table,
.max_register = ATLAS_REG_PH_DATA + 4,
.cache_type = REGCACHE_RBTREE,
};
static const struct iio_chan_spec atlas_channels[] = {
{
.type = IIO_PH,
.info_mask_separate =
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
.scan_index = 0,
.scan_type = {
.sign = 'u',
.realbits = 32,
.storagebits = 32,
.endianness = IIO_BE,
},
},
IIO_CHAN_SOFT_TIMESTAMP(1),
{
.type = IIO_TEMP,
.address = ATLAS_REG_TEMP_DATA,
.info_mask_separate =
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
.output = 1,
.scan_index = -1
},
};
static int atlas_set_powermode(struct atlas_data *data, int on)
{
return regmap_write(data->regmap, ATLAS_REG_PWR_CONTROL, on);
}
static int atlas_set_interrupt(struct atlas_data *data, bool state)
{
return regmap_update_bits(data->regmap, ATLAS_REG_INT_CONTROL,
ATLAS_REG_INT_CONTROL_EN,
state ? ATLAS_REG_INT_CONTROL_EN : 0);
}
static int atlas_buffer_postenable(struct iio_dev *indio_dev)
{
struct atlas_data *data = iio_priv(indio_dev);
int ret;
ret = iio_triggered_buffer_postenable(indio_dev);
if (ret)
return ret;
ret = pm_runtime_get_sync(&data->client->dev);
if (ret < 0) {
pm_runtime_put_noidle(&data->client->dev);
return ret;
}
return atlas_set_interrupt(data, true);
}
static int atlas_buffer_predisable(struct iio_dev *indio_dev)
{
struct atlas_data *data = iio_priv(indio_dev);
int ret;
ret = iio_triggered_buffer_predisable(indio_dev);
if (ret)
return ret;
ret = atlas_set_interrupt(data, false);
if (ret)
return ret;
pm_runtime_mark_last_busy(&data->client->dev);
return pm_runtime_put_autosuspend(&data->client->dev);
}
static const struct iio_trigger_ops atlas_interrupt_trigger_ops = {
.owner = THIS_MODULE,
};
static const struct iio_buffer_setup_ops atlas_buffer_setup_ops = {
.postenable = atlas_buffer_postenable,
.predisable = atlas_buffer_predisable,
};
static void atlas_work_handler(struct irq_work *work)
{
struct atlas_data *data = container_of(work, struct atlas_data, work);
iio_trigger_poll(data->trig);
}
static irqreturn_t atlas_trigger_handler(int irq, void *private)
{
struct iio_poll_func *pf = private;
struct iio_dev *indio_dev = pf->indio_dev;
struct atlas_data *data = iio_priv(indio_dev);
int ret;
ret = regmap_bulk_read(data->regmap, ATLAS_REG_PH_DATA,
(u8 *) &data->buffer, sizeof(data->buffer[0]));
if (!ret)
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
iio_get_time_ns());
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
static irqreturn_t atlas_interrupt_handler(int irq, void *private)
{
struct iio_dev *indio_dev = private;
struct atlas_data *data = iio_priv(indio_dev);
irq_work_queue(&data->work);
return IRQ_HANDLED;
}
static int atlas_read_ph_measurement(struct atlas_data *data, __be32 *val)
{
struct device *dev = &data->client->dev;
int suspended = pm_runtime_suspended(dev);
int ret;
ret = pm_runtime_get_sync(dev);
if (ret < 0) {
pm_runtime_put_noidle(dev);
return ret;
}
if (suspended)
usleep_range(ATLAS_PH_INT_TIME_IN_US,
ATLAS_PH_INT_TIME_IN_US + 100000);
ret = regmap_bulk_read(data->regmap, ATLAS_REG_PH_DATA,
(u8 *) val, sizeof(*val));
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return ret;
}
static int atlas_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct atlas_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW: {
int ret;
__be32 reg;
switch (chan->type) {
case IIO_TEMP:
ret = regmap_bulk_read(data->regmap, chan->address,
(u8 *) &reg, sizeof(reg));
break;
case IIO_PH:
mutex_lock(&indio_dev->mlock);
if (iio_buffer_enabled(indio_dev))
ret = -EBUSY;
else
ret = atlas_read_ph_measurement(data, &reg);
mutex_unlock(&indio_dev->mlock);
break;
default:
ret = -EINVAL;
}
if (!ret) {
*val = be32_to_cpu(reg);
ret = IIO_VAL_INT;
}
return ret;
}
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_TEMP:
*val = 1; /* 0.01 */
*val2 = 100;
break;
case IIO_PH:
*val = 1; /* 0.001 */
*val2 = 1000;
break;
default:
return -EINVAL;
}
return IIO_VAL_FRACTIONAL;
}
return -EINVAL;
}
static int atlas_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct atlas_data *data = iio_priv(indio_dev);
__be32 reg = cpu_to_be32(val);
if (val2 != 0 || val < 0 || val > 20000)
return -EINVAL;
if (mask != IIO_CHAN_INFO_RAW || chan->type != IIO_TEMP)
return -EINVAL;
return regmap_bulk_write(data->regmap, chan->address,
&reg, sizeof(reg));
}
static const struct iio_info atlas_info = {
.driver_module = THIS_MODULE,
.read_raw = atlas_read_raw,
.write_raw = atlas_write_raw,
};
static int atlas_check_calibration(struct atlas_data *data)
{
struct device *dev = &data->client->dev;
int ret;
unsigned int val;
ret = regmap_read(data->regmap, ATLAS_REG_CALIB_STATUS, &val);
if (ret)
return ret;
if (!(val & ATLAS_REG_CALIB_STATUS_MASK)) {
dev_warn(dev, "device has not been calibrated\n");
return 0;
}
if (!(val & ATLAS_REG_CALIB_STATUS_LOW))
dev_warn(dev, "device missing low point calibration\n");
if (!(val & ATLAS_REG_CALIB_STATUS_MID))
dev_warn(dev, "device missing mid point calibration\n");
if (!(val & ATLAS_REG_CALIB_STATUS_HIGH))
dev_warn(dev, "device missing high point calibration\n");
return 0;
};
static int atlas_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct atlas_data *data;
struct iio_trigger *trig;
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
indio_dev->info = &atlas_info;
indio_dev->name = ATLAS_DRV_NAME;
indio_dev->channels = atlas_channels;
indio_dev->num_channels = ARRAY_SIZE(atlas_channels);
indio_dev->modes = INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE;
indio_dev->dev.parent = &client->dev;
trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d",
indio_dev->name, indio_dev->id);
if (!trig)
return -ENOMEM;
data = iio_priv(indio_dev);
data->client = client;
data->trig = trig;
trig->dev.parent = indio_dev->dev.parent;
trig->ops = &atlas_interrupt_trigger_ops;
iio_trigger_set_drvdata(trig, indio_dev);
i2c_set_clientdata(client, indio_dev);
data->regmap = devm_regmap_init_i2c(client, &atlas_regmap_config);
if (IS_ERR(data->regmap)) {
dev_err(&client->dev, "regmap initialization failed\n");
return PTR_ERR(data->regmap);
}
ret = pm_runtime_set_active(&client->dev);
if (ret)
return ret;
if (client->irq <= 0) {
dev_err(&client->dev, "no valid irq defined\n");
return -EINVAL;
}
ret = atlas_check_calibration(data);
if (ret)
return ret;
ret = iio_trigger_register(trig);
if (ret) {
dev_err(&client->dev, "failed to register trigger\n");
return ret;
}
ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
&atlas_trigger_handler, &atlas_buffer_setup_ops);
if (ret) {
dev_err(&client->dev, "cannot setup iio trigger\n");
goto unregister_trigger;
}
init_irq_work(&data->work, atlas_work_handler);
/* interrupt pin toggles on new conversion */
ret = devm_request_threaded_irq(&client->dev, client->irq,
NULL, atlas_interrupt_handler,
IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"atlas_irq",
indio_dev);
if (ret) {
dev_err(&client->dev, "request irq (%d) failed\n", client->irq);
goto unregister_buffer;
}
ret = atlas_set_powermode(data, 1);
if (ret) {
dev_err(&client->dev, "cannot power device on");
goto unregister_buffer;
}
pm_runtime_enable(&client->dev);
pm_runtime_set_autosuspend_delay(&client->dev, 2500);
pm_runtime_use_autosuspend(&client->dev);
ret = iio_device_register(indio_dev);
if (ret) {
dev_err(&client->dev, "unable to register device\n");
goto unregister_pm;
}
return 0;
unregister_pm:
pm_runtime_disable(&client->dev);
atlas_set_powermode(data, 0);
unregister_buffer:
iio_triggered_buffer_cleanup(indio_dev);
unregister_trigger:
iio_trigger_unregister(data->trig);
return ret;
}
static int atlas_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct atlas_data *data = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
iio_trigger_unregister(data->trig);
pm_runtime_disable(&client->dev);
pm_runtime_set_suspended(&client->dev);
pm_runtime_put_noidle(&client->dev);
return atlas_set_powermode(data, 0);
}
#ifdef CONFIG_PM
static int atlas_runtime_suspend(struct device *dev)
{
struct atlas_data *data =
iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
return atlas_set_powermode(data, 0);
}
static int atlas_runtime_resume(struct device *dev)
{
struct atlas_data *data =
iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
return atlas_set_powermode(data, 1);
}
#endif
static const struct dev_pm_ops atlas_pm_ops = {
SET_RUNTIME_PM_OPS(atlas_runtime_suspend,
atlas_runtime_resume, NULL)
};
static const struct i2c_device_id atlas_id[] = {
{ "atlas-ph-sm", 0 },
{}
};
MODULE_DEVICE_TABLE(i2c, atlas_id);
static const struct of_device_id atlas_dt_ids[] = {
{ .compatible = "atlas,ph-sm" },
{ }
};
MODULE_DEVICE_TABLE(of, atlas_dt_ids);
static struct i2c_driver atlas_driver = {
.driver = {
.name = ATLAS_DRV_NAME,
.of_match_table = of_match_ptr(atlas_dt_ids),
.pm = &atlas_pm_ops,
},
.probe = atlas_probe,
.remove = atlas_remove,
.id_table = atlas_id,
};
module_i2c_driver(atlas_driver);
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
MODULE_DESCRIPTION("Atlas Scientific pH-SM sensor");
MODULE_LICENSE("GPL");

View File

@ -249,7 +249,7 @@ static int vz89x_probe(struct i2c_client *client,
I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE))
data->xfer = vz89x_smbus_xfer;
else
return -ENOTSUPP;
return -EOPNOTSUPP;
i2c_set_clientdata(client, indio_dev);
data->client = client;

View File

@ -18,13 +18,15 @@
#include <asm/unaligned.h>
#include <linux/iio/common/st_sensors.h>
#include "st_sensors_core.h"
static inline u32 st_sensors_get_unaligned_le24(const u8 *p)
{
return (s32)((p[0] | p[1] << 8 | p[2] << 16) << 8) >> 8;
}
static int st_sensors_write_data_with_mask(struct iio_dev *indio_dev,
u8 reg_addr, u8 mask, u8 data)
int st_sensors_write_data_with_mask(struct iio_dev *indio_dev,
u8 reg_addr, u8 mask, u8 data)
{
int err;
u8 new_data;

View File

@ -0,0 +1,8 @@
/*
* Local functions in the ST Sensors core
*/
#ifndef __ST_SENSORS_CORE_H
#define __ST_SENSORS_CORE_H
int st_sensors_write_data_with_mask(struct iio_dev *indio_dev,
u8 reg_addr, u8 mask, u8 data);
#endif

View File

@ -14,32 +14,65 @@
#include <linux/iio/iio.h>
#include <linux/iio/trigger.h>
#include <linux/interrupt.h>
#include <linux/iio/common/st_sensors.h>
#include "st_sensors_core.h"
int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
const struct iio_trigger_ops *trigger_ops)
{
int err;
int err, irq;
struct st_sensor_data *sdata = iio_priv(indio_dev);
unsigned long irq_trig;
sdata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name);
if (sdata->trig == NULL) {
err = -ENOMEM;
dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n");
goto iio_trigger_alloc_error;
return -ENOMEM;
}
err = request_threaded_irq(sdata->get_irq_data_ready(indio_dev),
irq = sdata->get_irq_data_ready(indio_dev);
irq_trig = irqd_get_trigger_type(irq_get_irq_data(irq));
/*
* If the IRQ is triggered on falling edge, we need to mark the
* interrupt as active low, if the hardware supports this.
*/
if (irq_trig == IRQF_TRIGGER_FALLING) {
if (!sdata->sensor_settings->drdy_irq.addr_ihl) {
dev_err(&indio_dev->dev,
"falling edge specified for IRQ but hardware "
"only support rising edge, will request "
"rising edge\n");
irq_trig = IRQF_TRIGGER_RISING;
} else {
/* Set up INT active low i.e. falling edge */
err = st_sensors_write_data_with_mask(indio_dev,
sdata->sensor_settings->drdy_irq.addr_ihl,
sdata->sensor_settings->drdy_irq.mask_ihl, 1);
if (err < 0)
goto iio_trigger_free;
dev_info(&indio_dev->dev,
"interrupts on the falling edge\n");
}
} else if (irq_trig == IRQF_TRIGGER_RISING) {
dev_info(&indio_dev->dev,
"interrupts on the rising edge\n");
} else {
dev_err(&indio_dev->dev,
"unsupported IRQ trigger specified (%lx), only "
"rising and falling edges supported, enforce "
"rising edge\n", irq_trig);
irq_trig = IRQF_TRIGGER_RISING;
}
err = request_threaded_irq(irq,
iio_trigger_generic_data_rdy_poll,
NULL,
IRQF_TRIGGER_RISING,
irq_trig,
sdata->trig->name,
sdata->trig);
if (err) {
dev_err(&indio_dev->dev, "failed to request trigger IRQ.\n");
goto request_irq_error;
goto iio_trigger_free;
}
iio_trigger_set_drvdata(sdata->trig, indio_dev);
@ -57,9 +90,8 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
iio_trigger_register_error:
free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig);
request_irq_error:
iio_trigger_free:
iio_trigger_free(sdata->trig);
iio_trigger_alloc_error:
return err;
}
EXPORT_SYMBOL(st_sensors_allocate_trigger);

View File

@ -10,8 +10,10 @@ config AD5064
depends on (SPI_MASTER && I2C!=m) || I2C
help
Say yes here to build support for Analog Devices AD5024, AD5025, AD5044,
AD5045, AD5064, AD5064-1, AD5065, AD5628, AD5629R, AD5648, AD5666, AD5668,
AD5669R Digital to Analog Converter.
AD5045, AD5064, AD5064-1, AD5065, AD5625, AD5625R, AD5627, AD5627R,
AD5628, AD5629R, AD5645R, AD5647R, AD5648, AD5665, AD5665R, AD5666,
AD5667, AD5667R, AD5668, AD5669R, LTC2606, LTC2607, LTC2609, LTC2616,
LTC2617, LTC2619, LTC2626, LTC2627, LTC2629 Digital to Analog Converter.
To compile this driver as a module, choose M here: the
module will be called ad5064.
@ -111,6 +113,16 @@ config AD5755
To compile this driver as a module, choose M here: the
module will be called ad5755.
config AD5761
tristate "Analog Devices AD5761/61R/21/21R DAC driver"
depends on SPI_MASTER
help
Say yes here to build support for Analog Devices AD5761, AD5761R, AD5721,
AD5721R Digital to Analog Converter.
To compile this driver as a module, choose M here: the
module will be called ad5761.
config AD5764
tristate "Analog Devices AD5764/64R/44/44R DAC driver"
depends on SPI_MASTER
@ -176,11 +188,11 @@ config MAX5821
10 bits DAC.
config MCP4725
tristate "MCP4725 DAC driver"
tristate "MCP4725/6 DAC driver"
depends on I2C
---help---
Say Y here if you want to build a driver for the Microchip
MCP 4725 12-bit digital-to-analog converter (DAC) with I2C
MCP 4725/6 12-bit digital-to-analog converter (DAC) with I2C
interface.
To compile this driver as a module, choose M here: the module
@ -196,4 +208,23 @@ config MCP4922
To compile this driver as a module, choose M here: the module
will be called mcp4922.
config STX104
tristate "Apex Embedded Systems STX104 DAC driver"
depends on ISA
help
Say yes here to build support for the 2-channel DAC on the Apex
Embedded Systems STX104 integrated analog PC/104 card. The base port
addresses for the devices may be configured via the "base" module
parameter array.
config VF610_DAC
tristate "Vybrid vf610 DAC driver"
depends on OF
depends on HAS_IOMEM
help
Say yes here to support Vybrid board digital-to-analog converter.
This driver can also be built as a module. If so, the module will
be called vf610_dac.
endmenu

View File

@ -12,6 +12,7 @@ obj-$(CONFIG_AD5504) += ad5504.o
obj-$(CONFIG_AD5446) += ad5446.o
obj-$(CONFIG_AD5449) += ad5449.o
obj-$(CONFIG_AD5755) += ad5755.o
obj-$(CONFIG_AD5761) += ad5761.o
obj-$(CONFIG_AD5764) += ad5764.o
obj-$(CONFIG_AD5791) += ad5791.o
obj-$(CONFIG_AD5686) += ad5686.o
@ -21,3 +22,5 @@ obj-$(CONFIG_MAX517) += max517.o
obj-$(CONFIG_MAX5821) += max5821.o
obj-$(CONFIG_MCP4725) += mcp4725.o
obj-$(CONFIG_MCP4922) += mcp4922.o
obj-$(CONFIG_STX104) += stx104.o
obj-$(CONFIG_VF610_DAC) += vf610_dac.o

View File

@ -1,6 +1,9 @@
/*
* AD5024, AD5025, AD5044, AD5045, AD5064, AD5064-1, AD5065, AD5628, AD5629R,
* AD5648, AD5666, AD5668, AD5669R Digital to analog converters driver
* AD5024, AD5025, AD5044, AD5045, AD5064, AD5064-1, AD5065, AD5625, AD5625R,
* AD5627, AD5627R, AD5628, AD5629R, AD5645R, AD5647R, AD5648, AD5665, AD5665R,
* AD5666, AD5667, AD5667R, AD5668, AD5669R, LTC2606, LTC2607, LTC2609, LTC2616,
* LTC2617, LTC2619, LTC2626, LTC2627, LTC2629 Digital to analog converters
* driver
*
* Copyright 2011 Analog Devices Inc.
*
@ -39,6 +42,9 @@
#define AD5064_CMD_RESET 0x7
#define AD5064_CMD_CONFIG 0x8
#define AD5064_CMD_RESET_V2 0x5
#define AD5064_CMD_CONFIG_V2 0x7
#define AD5064_CONFIG_DAISY_CHAIN_ENABLE BIT(1)
#define AD5064_CONFIG_INT_VREF_ENABLE BIT(0)
@ -47,13 +53,26 @@
#define AD5064_LDAC_PWRDN_100K 0x2
#define AD5064_LDAC_PWRDN_3STATE 0x3
/**
* enum ad5064_regmap_type - Register layout variant
* @AD5064_REGMAP_ADI: Old Analog Devices register map layout
* @AD5064_REGMAP_ADI2: New Analog Devices register map layout
* @AD5064_REGMAP_LTC: LTC register map layout
*/
enum ad5064_regmap_type {
AD5064_REGMAP_ADI,
AD5064_REGMAP_ADI2,
AD5064_REGMAP_LTC,
};
/**
* struct ad5064_chip_info - chip specific information
* @shared_vref: whether the vref supply is shared between channels
* @internal_vref: internal reference voltage. 0 if the chip has no internal
* vref.
* @internal_vref: internal reference voltage. 0 if the chip has no
internal vref.
* @channel: channel specification
* @num_channels: number of channels
* @regmap_type: register map layout variant
*/
struct ad5064_chip_info {
@ -61,6 +80,7 @@ struct ad5064_chip_info {
unsigned long internal_vref;
const struct iio_chan_spec *channels;
unsigned int num_channels;
enum ad5064_regmap_type regmap_type;
};
struct ad5064_state;
@ -111,18 +131,43 @@ enum ad5064_type {
ID_AD5064,
ID_AD5064_1,
ID_AD5065,
ID_AD5625,
ID_AD5625R_1V25,
ID_AD5625R_2V5,
ID_AD5627,
ID_AD5627R_1V25,
ID_AD5627R_2V5,
ID_AD5628_1,
ID_AD5628_2,
ID_AD5629_1,
ID_AD5629_2,
ID_AD5645R_1V25,
ID_AD5645R_2V5,
ID_AD5647R_1V25,
ID_AD5647R_2V5,
ID_AD5648_1,
ID_AD5648_2,
ID_AD5665,
ID_AD5665R_1V25,
ID_AD5665R_2V5,
ID_AD5666_1,
ID_AD5666_2,
ID_AD5667,
ID_AD5667R_1V25,
ID_AD5667R_2V5,
ID_AD5668_1,
ID_AD5668_2,
ID_AD5669_1,
ID_AD5669_2,
ID_LTC2606,
ID_LTC2607,
ID_LTC2609,
ID_LTC2616,
ID_LTC2617,
ID_LTC2619,
ID_LTC2626,
ID_LTC2627,
ID_LTC2629,
};
static int ad5064_write(struct ad5064_state *st, unsigned int cmd,
@ -136,15 +181,27 @@ static int ad5064_write(struct ad5064_state *st, unsigned int cmd,
static int ad5064_sync_powerdown_mode(struct ad5064_state *st,
const struct iio_chan_spec *chan)
{
unsigned int val;
unsigned int val, address;
unsigned int shift;
int ret;
val = (0x1 << chan->address);
if (st->chip_info->regmap_type == AD5064_REGMAP_LTC) {
val = 0;
address = chan->address;
} else {
if (st->chip_info->regmap_type == AD5064_REGMAP_ADI2)
shift = 4;
else
shift = 8;
if (st->pwr_down[chan->channel])
val |= st->pwr_down_mode[chan->channel] << 8;
val = (0x1 << chan->address);
address = 0;
ret = ad5064_write(st, AD5064_CMD_POWERDOWN_DAC, 0, val, 0);
if (st->pwr_down[chan->channel])
val |= st->pwr_down_mode[chan->channel] << shift;
}
ret = ad5064_write(st, AD5064_CMD_POWERDOWN_DAC, address, val, 0);
return ret;
}
@ -155,6 +212,10 @@ static const char * const ad5064_powerdown_modes[] = {
"three_state",
};
static const char * const ltc2617_powerdown_modes[] = {
"90kohm_to_gnd",
};
static int ad5064_get_powerdown_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
@ -185,6 +246,13 @@ static const struct iio_enum ad5064_powerdown_mode_enum = {
.set = ad5064_set_powerdown_mode,
};
static const struct iio_enum ltc2617_powerdown_mode_enum = {
.items = ltc2617_powerdown_modes,
.num_items = ARRAY_SIZE(ltc2617_powerdown_modes),
.get = ad5064_get_powerdown_mode,
.set = ad5064_set_powerdown_mode,
};
static ssize_t ad5064_read_dac_powerdown(struct iio_dev *indio_dev,
uintptr_t private, const struct iio_chan_spec *chan, char *buf)
{
@ -295,7 +363,19 @@ static const struct iio_chan_spec_ext_info ad5064_ext_info[] = {
{ },
};
#define AD5064_CHANNEL(chan, addr, bits, _shift) { \
static const struct iio_chan_spec_ext_info ltc2617_ext_info[] = {
{
.name = "powerdown",
.read = ad5064_read_dac_powerdown,
.write = ad5064_write_dac_powerdown,
.shared = IIO_SEPARATE,
},
IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ltc2617_powerdown_mode_enum),
IIO_ENUM_AVAILABLE("powerdown_mode", &ltc2617_powerdown_mode_enum),
{ },
};
#define AD5064_CHANNEL(chan, addr, bits, _shift, _ext_info) { \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.output = 1, \
@ -309,145 +389,340 @@ static const struct iio_chan_spec_ext_info ad5064_ext_info[] = {
.storagebits = 16, \
.shift = (_shift), \
}, \
.ext_info = ad5064_ext_info, \
.ext_info = (_ext_info), \
}
#define DECLARE_AD5064_CHANNELS(name, bits, shift) \
#define DECLARE_AD5064_CHANNELS(name, bits, shift, ext_info) \
const struct iio_chan_spec name[] = { \
AD5064_CHANNEL(0, 0, bits, shift), \
AD5064_CHANNEL(1, 1, bits, shift), \
AD5064_CHANNEL(2, 2, bits, shift), \
AD5064_CHANNEL(3, 3, bits, shift), \
AD5064_CHANNEL(4, 4, bits, shift), \
AD5064_CHANNEL(5, 5, bits, shift), \
AD5064_CHANNEL(6, 6, bits, shift), \
AD5064_CHANNEL(7, 7, bits, shift), \
AD5064_CHANNEL(0, 0, bits, shift, ext_info), \
AD5064_CHANNEL(1, 1, bits, shift, ext_info), \
AD5064_CHANNEL(2, 2, bits, shift, ext_info), \
AD5064_CHANNEL(3, 3, bits, shift, ext_info), \
AD5064_CHANNEL(4, 4, bits, shift, ext_info), \
AD5064_CHANNEL(5, 5, bits, shift, ext_info), \
AD5064_CHANNEL(6, 6, bits, shift, ext_info), \
AD5064_CHANNEL(7, 7, bits, shift, ext_info), \
}
#define DECLARE_AD5065_CHANNELS(name, bits, shift) \
#define DECLARE_AD5065_CHANNELS(name, bits, shift, ext_info) \
const struct iio_chan_spec name[] = { \
AD5064_CHANNEL(0, 0, bits, shift), \
AD5064_CHANNEL(1, 3, bits, shift), \
AD5064_CHANNEL(0, 0, bits, shift, ext_info), \
AD5064_CHANNEL(1, 3, bits, shift, ext_info), \
}
static DECLARE_AD5064_CHANNELS(ad5024_channels, 12, 8);
static DECLARE_AD5064_CHANNELS(ad5044_channels, 14, 6);
static DECLARE_AD5064_CHANNELS(ad5064_channels, 16, 4);
static DECLARE_AD5064_CHANNELS(ad5024_channels, 12, 8, ad5064_ext_info);
static DECLARE_AD5064_CHANNELS(ad5044_channels, 14, 6, ad5064_ext_info);
static DECLARE_AD5064_CHANNELS(ad5064_channels, 16, 4, ad5064_ext_info);
static DECLARE_AD5065_CHANNELS(ad5025_channels, 12, 8);
static DECLARE_AD5065_CHANNELS(ad5045_channels, 14, 6);
static DECLARE_AD5065_CHANNELS(ad5065_channels, 16, 4);
static DECLARE_AD5065_CHANNELS(ad5025_channels, 12, 8, ad5064_ext_info);
static DECLARE_AD5065_CHANNELS(ad5045_channels, 14, 6, ad5064_ext_info);
static DECLARE_AD5065_CHANNELS(ad5065_channels, 16, 4, ad5064_ext_info);
static DECLARE_AD5064_CHANNELS(ad5629_channels, 12, 4);
static DECLARE_AD5064_CHANNELS(ad5669_channels, 16, 0);
static DECLARE_AD5064_CHANNELS(ad5629_channels, 12, 4, ad5064_ext_info);
static DECLARE_AD5064_CHANNELS(ad5645_channels, 14, 2, ad5064_ext_info);
static DECLARE_AD5064_CHANNELS(ad5669_channels, 16, 0, ad5064_ext_info);
static DECLARE_AD5064_CHANNELS(ltc2607_channels, 16, 0, ltc2617_ext_info);
static DECLARE_AD5064_CHANNELS(ltc2617_channels, 14, 2, ltc2617_ext_info);
static DECLARE_AD5064_CHANNELS(ltc2627_channels, 12, 4, ltc2617_ext_info);
static const struct ad5064_chip_info ad5064_chip_info_tbl[] = {
[ID_AD5024] = {
.shared_vref = false,
.channels = ad5024_channels,
.num_channels = 4,
.regmap_type = AD5064_REGMAP_ADI,
},
[ID_AD5025] = {
.shared_vref = false,
.channels = ad5025_channels,
.num_channels = 2,
.regmap_type = AD5064_REGMAP_ADI,
},
[ID_AD5044] = {
.shared_vref = false,
.channels = ad5044_channels,
.num_channels = 4,
.regmap_type = AD5064_REGMAP_ADI,
},
[ID_AD5045] = {
.shared_vref = false,
.channels = ad5045_channels,
.num_channels = 2,
.regmap_type = AD5064_REGMAP_ADI,
},
[ID_AD5064] = {
.shared_vref = false,
.channels = ad5064_channels,
.num_channels = 4,
.regmap_type = AD5064_REGMAP_ADI,
},
[ID_AD5064_1] = {
.shared_vref = true,
.channels = ad5064_channels,
.num_channels = 4,
.regmap_type = AD5064_REGMAP_ADI,
},
[ID_AD5065] = {
.shared_vref = false,
.channels = ad5065_channels,
.num_channels = 2,
.regmap_type = AD5064_REGMAP_ADI,
},
[ID_AD5625] = {
.shared_vref = true,
.channels = ad5629_channels,
.num_channels = 4,
.regmap_type = AD5064_REGMAP_ADI2
},
[ID_AD5625R_1V25] = {
.shared_vref = true,
.internal_vref = 1250000,
.channels = ad5629_channels,
.num_channels = 4,
.regmap_type = AD5064_REGMAP_ADI2
},
[ID_AD5625R_2V5] = {
.shared_vref = true,
.internal_vref = 2500000,
.channels = ad5629_channels,
.num_channels = 4,
.regmap_type = AD5064_REGMAP_ADI2
},
[ID_AD5627] = {
.shared_vref = true,
.channels = ad5629_channels,
.num_channels = 2,
.regmap_type = AD5064_REGMAP_ADI2
},
[ID_AD5627R_1V25] = {
.shared_vref = true,
.internal_vref = 1250000,
.channels = ad5629_channels,
.num_channels = 2,
.regmap_type = AD5064_REGMAP_ADI2
},
[ID_AD5627R_2V5] = {
.shared_vref = true,
.internal_vref = 2500000,
.channels = ad5629_channels,
.num_channels = 2,
.regmap_type = AD5064_REGMAP_ADI2
},
[ID_AD5628_1] = {
.shared_vref = true,
.internal_vref = 2500000,
.channels = ad5024_channels,
.num_channels = 8,
.regmap_type = AD5064_REGMAP_ADI,
},
[ID_AD5628_2] = {
.shared_vref = true,
.internal_vref = 5000000,
.channels = ad5024_channels,
.num_channels = 8,
.regmap_type = AD5064_REGMAP_ADI,
},
[ID_AD5629_1] = {
.shared_vref = true,
.internal_vref = 2500000,
.channels = ad5629_channels,
.num_channels = 8,
.regmap_type = AD5064_REGMAP_ADI,
},
[ID_AD5629_2] = {
.shared_vref = true,
.internal_vref = 5000000,
.channels = ad5629_channels,
.num_channels = 8,
.regmap_type = AD5064_REGMAP_ADI,
},
[ID_AD5645R_1V25] = {
.shared_vref = true,
.internal_vref = 1250000,
.channels = ad5645_channels,
.num_channels = 4,
.regmap_type = AD5064_REGMAP_ADI2
},
[ID_AD5645R_2V5] = {
.shared_vref = true,
.internal_vref = 2500000,
.channels = ad5645_channels,
.num_channels = 4,
.regmap_type = AD5064_REGMAP_ADI2
},
[ID_AD5647R_1V25] = {
.shared_vref = true,
.internal_vref = 1250000,
.channels = ad5645_channels,
.num_channels = 2,
.regmap_type = AD5064_REGMAP_ADI2
},
[ID_AD5647R_2V5] = {
.shared_vref = true,
.internal_vref = 2500000,
.channels = ad5645_channels,
.num_channels = 2,
.regmap_type = AD5064_REGMAP_ADI2
},
[ID_AD5648_1] = {
.shared_vref = true,
.internal_vref = 2500000,
.channels = ad5044_channels,
.num_channels = 8,
.regmap_type = AD5064_REGMAP_ADI,
},
[ID_AD5648_2] = {
.shared_vref = true,
.internal_vref = 5000000,
.channels = ad5044_channels,
.num_channels = 8,
.regmap_type = AD5064_REGMAP_ADI,
},
[ID_AD5665] = {
.shared_vref = true,
.channels = ad5669_channels,
.num_channels = 4,
.regmap_type = AD5064_REGMAP_ADI2
},
[ID_AD5665R_1V25] = {
.shared_vref = true,
.internal_vref = 1250000,
.channels = ad5669_channels,
.num_channels = 4,
.regmap_type = AD5064_REGMAP_ADI2
},
[ID_AD5665R_2V5] = {
.shared_vref = true,
.internal_vref = 2500000,
.channels = ad5669_channels,
.num_channels = 4,
.regmap_type = AD5064_REGMAP_ADI2
},
[ID_AD5666_1] = {
.shared_vref = true,
.internal_vref = 2500000,
.channels = ad5064_channels,
.num_channels = 4,
.regmap_type = AD5064_REGMAP_ADI,
},
[ID_AD5666_2] = {
.shared_vref = true,
.internal_vref = 5000000,
.channels = ad5064_channels,
.num_channels = 4,
.regmap_type = AD5064_REGMAP_ADI,
},
[ID_AD5667] = {
.shared_vref = true,
.channels = ad5669_channels,
.num_channels = 2,
.regmap_type = AD5064_REGMAP_ADI2
},
[ID_AD5667R_1V25] = {
.shared_vref = true,
.internal_vref = 1250000,
.channels = ad5669_channels,
.num_channels = 2,
.regmap_type = AD5064_REGMAP_ADI2
},
[ID_AD5667R_2V5] = {
.shared_vref = true,
.internal_vref = 2500000,
.channels = ad5669_channels,
.num_channels = 2,
.regmap_type = AD5064_REGMAP_ADI2
},
[ID_AD5668_1] = {
.shared_vref = true,
.internal_vref = 2500000,
.channels = ad5064_channels,
.num_channels = 8,
.regmap_type = AD5064_REGMAP_ADI,
},
[ID_AD5668_2] = {
.shared_vref = true,
.internal_vref = 5000000,
.channels = ad5064_channels,
.num_channels = 8,
.regmap_type = AD5064_REGMAP_ADI,
},
[ID_AD5669_1] = {
.shared_vref = true,
.internal_vref = 2500000,
.channels = ad5669_channels,
.num_channels = 8,
.regmap_type = AD5064_REGMAP_ADI,
},
[ID_AD5669_2] = {
.shared_vref = true,
.internal_vref = 5000000,
.channels = ad5669_channels,
.num_channels = 8,
.regmap_type = AD5064_REGMAP_ADI,
},
[ID_LTC2606] = {
.shared_vref = true,
.internal_vref = 0,
.channels = ltc2607_channels,
.num_channels = 1,
.regmap_type = AD5064_REGMAP_LTC,
},
[ID_LTC2607] = {
.shared_vref = true,
.internal_vref = 0,
.channels = ltc2607_channels,
.num_channels = 2,
.regmap_type = AD5064_REGMAP_LTC,
},
[ID_LTC2609] = {
.shared_vref = false,
.internal_vref = 0,
.channels = ltc2607_channels,
.num_channels = 4,
.regmap_type = AD5064_REGMAP_LTC,
},
[ID_LTC2616] = {
.shared_vref = true,
.internal_vref = 0,
.channels = ltc2617_channels,
.num_channels = 1,
.regmap_type = AD5064_REGMAP_LTC,
},
[ID_LTC2617] = {
.shared_vref = true,
.internal_vref = 0,
.channels = ltc2617_channels,
.num_channels = 2,
.regmap_type = AD5064_REGMAP_LTC,
},
[ID_LTC2619] = {
.shared_vref = false,
.internal_vref = 0,
.channels = ltc2617_channels,
.num_channels = 4,
.regmap_type = AD5064_REGMAP_LTC,
},
[ID_LTC2626] = {
.shared_vref = true,
.internal_vref = 0,
.channels = ltc2627_channels,
.num_channels = 1,
.regmap_type = AD5064_REGMAP_LTC,
},
[ID_LTC2627] = {
.shared_vref = true,
.internal_vref = 0,
.channels = ltc2627_channels,
.num_channels = 2,
.regmap_type = AD5064_REGMAP_LTC,
},
[ID_LTC2629] = {
.shared_vref = false,
.internal_vref = 0,
.channels = ltc2627_channels,
.num_channels = 4,
.regmap_type = AD5064_REGMAP_LTC,
},
};
@ -469,6 +744,22 @@ static const char * const ad5064_vref_name(struct ad5064_state *st,
return st->chip_info->shared_vref ? "vref" : ad5064_vref_names[vref];
}
static int ad5064_set_config(struct ad5064_state *st, unsigned int val)
{
unsigned int cmd;
switch (st->chip_info->regmap_type) {
case AD5064_REGMAP_ADI2:
cmd = AD5064_CMD_CONFIG_V2;
break;
default:
cmd = AD5064_CMD_CONFIG;
break;
}
return ad5064_write(st, cmd, 0, val, 0);
}
static int ad5064_probe(struct device *dev, enum ad5064_type type,
const char *name, ad5064_write_func write)
{
@ -498,8 +789,7 @@ static int ad5064_probe(struct device *dev, enum ad5064_type type,
if (!st->chip_info->internal_vref)
return ret;
st->use_internal_vref = true;
ret = ad5064_write(st, AD5064_CMD_CONFIG, 0,
AD5064_CONFIG_INT_VREF_ENABLE, 0);
ret = ad5064_set_config(st, AD5064_CONFIG_INT_VREF_ENABLE);
if (ret) {
dev_err(dev, "Failed to enable internal vref: %d\n",
ret);
@ -628,9 +918,19 @@ static int ad5064_i2c_write(struct ad5064_state *st, unsigned int cmd,
unsigned int addr, unsigned int val)
{
struct i2c_client *i2c = to_i2c_client(st->dev);
unsigned int cmd_shift;
int ret;
st->data.i2c[0] = (cmd << 4) | addr;
switch (st->chip_info->regmap_type) {
case AD5064_REGMAP_ADI2:
cmd_shift = 3;
break;
default:
cmd_shift = 4;
break;
}
st->data.i2c[0] = (cmd << cmd_shift) | addr;
put_unaligned_be16(val, &st->data.i2c[1]);
ret = i2c_master_send(i2c, st->data.i2c, 3);
@ -653,12 +953,35 @@ static int ad5064_i2c_remove(struct i2c_client *i2c)
}
static const struct i2c_device_id ad5064_i2c_ids[] = {
{"ad5625", ID_AD5625 },
{"ad5625r-1v25", ID_AD5625R_1V25 },
{"ad5625r-2v5", ID_AD5625R_2V5 },
{"ad5627", ID_AD5627 },
{"ad5627r-1v25", ID_AD5627R_1V25 },
{"ad5627r-2v5", ID_AD5627R_2V5 },
{"ad5629-1", ID_AD5629_1},
{"ad5629-2", ID_AD5629_2},
{"ad5629-3", ID_AD5629_2}, /* similar enough to ad5629-2 */
{"ad5645r-1v25", ID_AD5645R_1V25 },
{"ad5645r-2v5", ID_AD5645R_2V5 },
{"ad5665", ID_AD5665 },
{"ad5665r-1v25", ID_AD5665R_1V25 },
{"ad5665r-2v5", ID_AD5665R_2V5 },
{"ad5667", ID_AD5667 },
{"ad5667r-1v25", ID_AD5667R_1V25 },
{"ad5667r-2v5", ID_AD5667R_2V5 },
{"ad5669-1", ID_AD5669_1},
{"ad5669-2", ID_AD5669_2},
{"ad5669-3", ID_AD5669_2}, /* similar enough to ad5669-2 */
{"ltc2606", ID_LTC2606},
{"ltc2607", ID_LTC2607},
{"ltc2609", ID_LTC2609},
{"ltc2616", ID_LTC2616},
{"ltc2617", ID_LTC2617},
{"ltc2619", ID_LTC2619},
{"ltc2626", ID_LTC2626},
{"ltc2627", ID_LTC2627},
{"ltc2629", ID_LTC2629},
{}
};
MODULE_DEVICE_TABLE(i2c, ad5064_i2c_ids);

430
drivers/iio/dac/ad5761.c Normal file
View File

@ -0,0 +1,430 @@
/*
* AD5721, AD5721R, AD5761, AD5761R, Voltage Output Digital to Analog Converter
*
* Copyright 2016 Qtechnology A/S
* 2016 Ricardo Ribalda <ricardo.ribalda@gmail.com>
*
* Licensed under the GPL-2.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/bitops.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/regulator/consumer.h>
#include <linux/platform_data/ad5761.h>
#define AD5761_ADDR(addr) ((addr & 0xf) << 16)
#define AD5761_ADDR_NOOP 0x0
#define AD5761_ADDR_DAC_WRITE 0x3
#define AD5761_ADDR_CTRL_WRITE_REG 0x4
#define AD5761_ADDR_SW_DATA_RESET 0x7
#define AD5761_ADDR_DAC_READ 0xb
#define AD5761_ADDR_CTRL_READ_REG 0xc
#define AD5761_ADDR_SW_FULL_RESET 0xf
#define AD5761_CTRL_USE_INTVREF BIT(5)
#define AD5761_CTRL_ETS BIT(6)
/**
* struct ad5761_chip_info - chip specific information
* @int_vref: Value of the internal reference voltage in mV - 0 if external
* reference voltage is used
* @channel: channel specification
*/
struct ad5761_chip_info {
unsigned long int_vref;
const struct iio_chan_spec channel;
};
struct ad5761_range_params {
int m;
int c;
};
enum ad5761_supported_device_ids {
ID_AD5721,
ID_AD5721R,
ID_AD5761,
ID_AD5761R,
};
/**
* struct ad5761_state - driver instance specific data
* @spi: spi_device
* @vref_reg: reference voltage regulator
* @use_intref: true when the internal voltage reference is used
* @vref: actual voltage reference in mVolts
* @range: output range mode used
* @data: cache aligned spi buffer
*/
struct ad5761_state {
struct spi_device *spi;
struct regulator *vref_reg;
bool use_intref;
int vref;
enum ad5761_voltage_range range;
/*
* DMA (thus cache coherency maintenance) requires the
* transfer buffers to live in their own cache lines.
*/
union {
__be32 d32;
u8 d8[4];
} data[3] ____cacheline_aligned;
};
static const struct ad5761_range_params ad5761_range_params[] = {
[AD5761_VOLTAGE_RANGE_M10V_10V] = {
.m = 80,
.c = 40,
},
[AD5761_VOLTAGE_RANGE_0V_10V] = {
.m = 40,
.c = 0,
},
[AD5761_VOLTAGE_RANGE_M5V_5V] = {
.m = 40,
.c = 20,
},
[AD5761_VOLTAGE_RANGE_0V_5V] = {
.m = 20,
.c = 0,
},
[AD5761_VOLTAGE_RANGE_M2V5_7V5] = {
.m = 40,
.c = 10,
},
[AD5761_VOLTAGE_RANGE_M3V_3V] = {
.m = 24,
.c = 12,
},
[AD5761_VOLTAGE_RANGE_0V_16V] = {
.m = 64,
.c = 0,
},
[AD5761_VOLTAGE_RANGE_0V_20V] = {
.m = 80,
.c = 0,
},
};
static int _ad5761_spi_write(struct ad5761_state *st, u8 addr, u16 val)
{
st->data[0].d32 = cpu_to_be32(AD5761_ADDR(addr) | val);
return spi_write(st->spi, &st->data[0].d8[1], 3);
}
static int ad5761_spi_write(struct iio_dev *indio_dev, u8 addr, u16 val)
{
struct ad5761_state *st = iio_priv(indio_dev);
int ret;
mutex_lock(&indio_dev->mlock);
ret = _ad5761_spi_write(st, addr, val);
mutex_unlock(&indio_dev->mlock);
return ret;
}
static int _ad5761_spi_read(struct ad5761_state *st, u8 addr, u16 *val)
{
int ret;
struct spi_transfer xfers[] = {
{
.tx_buf = &st->data[0].d8[1],
.bits_per_word = 8,
.len = 3,
.cs_change = true,
}, {
.tx_buf = &st->data[1].d8[1],
.rx_buf = &st->data[2].d8[1],
.bits_per_word = 8,
.len = 3,
},
};
st->data[0].d32 = cpu_to_be32(AD5761_ADDR(addr));
st->data[1].d32 = cpu_to_be32(AD5761_ADDR(AD5761_ADDR_NOOP));
ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers));
*val = be32_to_cpu(st->data[2].d32);
return ret;
}
static int ad5761_spi_read(struct iio_dev *indio_dev, u8 addr, u16 *val)
{
struct ad5761_state *st = iio_priv(indio_dev);
int ret;
mutex_lock(&indio_dev->mlock);
ret = _ad5761_spi_read(st, addr, val);
mutex_unlock(&indio_dev->mlock);
return ret;
}
static int ad5761_spi_set_range(struct ad5761_state *st,
enum ad5761_voltage_range range)
{
u16 aux;
int ret;
aux = (range & 0x7) | AD5761_CTRL_ETS;
if (st->use_intref)
aux |= AD5761_CTRL_USE_INTVREF;
ret = _ad5761_spi_write(st, AD5761_ADDR_SW_FULL_RESET, 0);
if (ret)
return ret;
ret = _ad5761_spi_write(st, AD5761_ADDR_CTRL_WRITE_REG, aux);
if (ret)
return ret;
st->range = range;
return 0;
}
static int ad5761_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val,
int *val2,
long mask)
{
struct ad5761_state *st;
int ret;
u16 aux;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = ad5761_spi_read(indio_dev, AD5761_ADDR_DAC_READ, &aux);
if (ret)
return ret;
*val = aux >> chan->scan_type.shift;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
st = iio_priv(indio_dev);
*val = st->vref * ad5761_range_params[st->range].m;
*val /= 10;
*val2 = chan->scan_type.realbits;
return IIO_VAL_FRACTIONAL_LOG2;
case IIO_CHAN_INFO_OFFSET:
st = iio_priv(indio_dev);
*val = -(1 << chan->scan_type.realbits);
*val *= ad5761_range_params[st->range].c;
*val /= ad5761_range_params[st->range].m;
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static int ad5761_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val,
int val2,
long mask)
{
u16 aux;
if (mask != IIO_CHAN_INFO_RAW)
return -EINVAL;
if (val2 || (val << chan->scan_type.shift) > 0xffff || val < 0)
return -EINVAL;
aux = val << chan->scan_type.shift;
return ad5761_spi_write(indio_dev, AD5761_ADDR_DAC_WRITE, aux);
}
static const struct iio_info ad5761_info = {
.read_raw = &ad5761_read_raw,
.write_raw = &ad5761_write_raw,
.driver_module = THIS_MODULE,
};
#define AD5761_CHAN(_bits) { \
.type = IIO_VOLTAGE, \
.output = 1, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_OFFSET), \
.scan_type = { \
.sign = 'u', \
.realbits = (_bits), \
.storagebits = 16, \
.shift = 16 - (_bits), \
}, \
}
static const struct ad5761_chip_info ad5761_chip_infos[] = {
[ID_AD5721] = {
.int_vref = 0,
.channel = AD5761_CHAN(12),
},
[ID_AD5721R] = {
.int_vref = 2500,
.channel = AD5761_CHAN(12),
},
[ID_AD5761] = {
.int_vref = 0,
.channel = AD5761_CHAN(16),
},
[ID_AD5761R] = {
.int_vref = 2500,
.channel = AD5761_CHAN(16),
},
};
static int ad5761_get_vref(struct ad5761_state *st,
const struct ad5761_chip_info *chip_info)
{
int ret;
st->vref_reg = devm_regulator_get_optional(&st->spi->dev, "vref");
if (PTR_ERR(st->vref_reg) == -ENODEV) {
/* Use Internal regulator */
if (!chip_info->int_vref) {
dev_err(&st->spi->dev,
"Voltage reference not found\n");
return -EIO;
}
st->use_intref = true;
st->vref = chip_info->int_vref;
return 0;
}
if (IS_ERR(st->vref_reg)) {
dev_err(&st->spi->dev,
"Error getting voltage reference regulator\n");
return PTR_ERR(st->vref_reg);
}
ret = regulator_enable(st->vref_reg);
if (ret) {
dev_err(&st->spi->dev,
"Failed to enable voltage reference\n");
return ret;
}
ret = regulator_get_voltage(st->vref_reg);
if (ret < 0) {
dev_err(&st->spi->dev,
"Failed to get voltage reference value\n");
goto disable_regulator_vref;
}
if (ret < 2000000 || ret > 3000000) {
dev_warn(&st->spi->dev,
"Invalid external voltage ref. value %d uV\n", ret);
ret = -EIO;
goto disable_regulator_vref;
}
st->vref = ret / 1000;
st->use_intref = false;
return 0;
disable_regulator_vref:
regulator_disable(st->vref_reg);
st->vref_reg = NULL;
return ret;
}
static int ad5761_probe(struct spi_device *spi)
{
struct iio_dev *iio_dev;
struct ad5761_state *st;
int ret;
const struct ad5761_chip_info *chip_info =
&ad5761_chip_infos[spi_get_device_id(spi)->driver_data];
enum ad5761_voltage_range voltage_range = AD5761_VOLTAGE_RANGE_0V_5V;
struct ad5761_platform_data *pdata = dev_get_platdata(&spi->dev);
iio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
if (!iio_dev)
return -ENOMEM;
st = iio_priv(iio_dev);
st->spi = spi;
spi_set_drvdata(spi, iio_dev);
ret = ad5761_get_vref(st, chip_info);
if (ret)
return ret;
if (pdata)
voltage_range = pdata->voltage_range;
ret = ad5761_spi_set_range(st, voltage_range);
if (ret)
goto disable_regulator_err;
iio_dev->dev.parent = &spi->dev;
iio_dev->info = &ad5761_info;
iio_dev->modes = INDIO_DIRECT_MODE;
iio_dev->channels = &chip_info->channel;
iio_dev->num_channels = 1;
iio_dev->name = spi_get_device_id(st->spi)->name;
ret = iio_device_register(iio_dev);
if (ret)
goto disable_regulator_err;
return 0;
disable_regulator_err:
if (!IS_ERR_OR_NULL(st->vref_reg))
regulator_disable(st->vref_reg);
return ret;
}
static int ad5761_remove(struct spi_device *spi)
{
struct iio_dev *iio_dev = spi_get_drvdata(spi);
struct ad5761_state *st = iio_priv(iio_dev);
iio_device_unregister(iio_dev);
if (!IS_ERR_OR_NULL(st->vref_reg))
regulator_disable(st->vref_reg);
return 0;
}
static const struct spi_device_id ad5761_id[] = {
{"ad5721", ID_AD5721},
{"ad5721r", ID_AD5721R},
{"ad5761", ID_AD5761},
{"ad5761r", ID_AD5761R},
{}
};
MODULE_DEVICE_TABLE(spi, ad5761_id);
static struct spi_driver ad5761_driver = {
.driver = {
.name = "ad5761",
},
.probe = ad5761_probe,
.remove = ad5761_remove,
.id_table = ad5761_id,
};
module_spi_driver(ad5761_driver);
MODULE_AUTHOR("Ricardo Ribalda <ricardo.ribalda@gmail.com>");
MODULE_DESCRIPTION("Analog Devices AD5721, AD5721R, AD5761, AD5761R driver");
MODULE_LICENSE("GPL v2");

View File

@ -1,5 +1,5 @@
/*
* mcp4725.c - Support for Microchip MCP4725
* mcp4725.c - Support for Microchip MCP4725/6
*
* Copyright (C) 2012 Peter Meerwald <pmeerw@pmeerw.net>
*
@ -134,6 +134,12 @@ static const char * const mcp4725_powerdown_modes[] = {
"500kohm_to_gnd"
};
static const char * const mcp4726_powerdown_modes[] = {
"1kohm_to_gnd",
"125kohm_to_gnd",
"640kohm_to_gnd"
};
static int mcp4725_get_powerdown_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
@ -182,11 +188,24 @@ static ssize_t mcp4725_write_powerdown(struct iio_dev *indio_dev,
return len;
}
static const struct iio_enum mcp4725_powerdown_mode_enum = {
.items = mcp4725_powerdown_modes,
.num_items = ARRAY_SIZE(mcp4725_powerdown_modes),
.get = mcp4725_get_powerdown_mode,
.set = mcp4725_set_powerdown_mode,
enum {
MCP4725,
MCP4726,
};
static const struct iio_enum mcp472x_powerdown_mode_enum[] = {
[MCP4725] = {
.items = mcp4725_powerdown_modes,
.num_items = ARRAY_SIZE(mcp4725_powerdown_modes),
.get = mcp4725_get_powerdown_mode,
.set = mcp4725_set_powerdown_mode,
},
[MCP4726] = {
.items = mcp4726_powerdown_modes,
.num_items = ARRAY_SIZE(mcp4726_powerdown_modes),
.get = mcp4725_get_powerdown_mode,
.set = mcp4725_set_powerdown_mode,
},
};
static const struct iio_chan_spec_ext_info mcp4725_ext_info[] = {
@ -196,19 +215,46 @@ static const struct iio_chan_spec_ext_info mcp4725_ext_info[] = {
.write = mcp4725_write_powerdown,
.shared = IIO_SEPARATE,
},
IIO_ENUM("powerdown_mode", IIO_SEPARATE, &mcp4725_powerdown_mode_enum),
IIO_ENUM_AVAILABLE("powerdown_mode", &mcp4725_powerdown_mode_enum),
IIO_ENUM("powerdown_mode", IIO_SEPARATE,
&mcp472x_powerdown_mode_enum[MCP4725]),
IIO_ENUM_AVAILABLE("powerdown_mode",
&mcp472x_powerdown_mode_enum[MCP4725]),
{ },
};
static const struct iio_chan_spec mcp4725_channel = {
.type = IIO_VOLTAGE,
.indexed = 1,
.output = 1,
.channel = 0,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
.ext_info = mcp4725_ext_info,
static const struct iio_chan_spec_ext_info mcp4726_ext_info[] = {
{
.name = "powerdown",
.read = mcp4725_read_powerdown,
.write = mcp4725_write_powerdown,
.shared = IIO_SEPARATE,
},
IIO_ENUM("powerdown_mode", IIO_SEPARATE,
&mcp472x_powerdown_mode_enum[MCP4726]),
IIO_ENUM_AVAILABLE("powerdown_mode",
&mcp472x_powerdown_mode_enum[MCP4726]),
{ },
};
static const struct iio_chan_spec mcp472x_channel[] = {
[MCP4725] = {
.type = IIO_VOLTAGE,
.indexed = 1,
.output = 1,
.channel = 0,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
.ext_info = mcp4725_ext_info,
},
[MCP4726] = {
.type = IIO_VOLTAGE,
.indexed = 1,
.output = 1,
.channel = 0,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
.ext_info = mcp4726_ext_info,
},
};
static int mcp4725_set_value(struct iio_dev *indio_dev, int val)
@ -302,7 +348,7 @@ static int mcp4725_probe(struct i2c_client *client,
indio_dev->dev.parent = &client->dev;
indio_dev->name = id->name;
indio_dev->info = &mcp4725_info;
indio_dev->channels = &mcp4725_channel;
indio_dev->channels = &mcp472x_channel[id->driver_data];
indio_dev->num_channels = 1;
indio_dev->modes = INDIO_DIRECT_MODE;
@ -316,7 +362,7 @@ static int mcp4725_probe(struct i2c_client *client,
}
pd = (inbuf[0] >> 1) & 0x3;
data->powerdown = pd > 0 ? true : false;
data->powerdown_mode = pd ? pd-1 : 2; /* 500kohm_to_gnd */
data->powerdown_mode = pd ? pd - 1 : 2; /* largest register to gnd */
data->dac_value = (inbuf[1] << 4) | (inbuf[2] >> 4);
return iio_device_register(indio_dev);
@ -329,7 +375,8 @@ static int mcp4725_remove(struct i2c_client *client)
}
static const struct i2c_device_id mcp4725_id[] = {
{ "mcp4725", 0 },
{ "mcp4725", MCP4725 },
{ "mcp4726", MCP4726 },
{ }
};
MODULE_DEVICE_TABLE(i2c, mcp4725_id);
@ -346,5 +393,5 @@ static struct i2c_driver mcp4725_driver = {
module_i2c_driver(mcp4725_driver);
MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
MODULE_DESCRIPTION("MCP4725 12-bit DAC");
MODULE_DESCRIPTION("MCP4725/6 12-bit DAC");
MODULE_LICENSE("GPL");

152
drivers/iio/dac/stx104.c Normal file
View File

@ -0,0 +1,152 @@
/*
* DAC driver for the Apex Embedded Systems STX104
* Copyright (C) 2016 William Breathitt Gray
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2, as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/iio/iio.h>
#include <linux/iio/types.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/isa.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#define STX104_NUM_CHAN 2
#define STX104_CHAN(chan) { \
.type = IIO_VOLTAGE, \
.channel = chan, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.indexed = 1, \
.output = 1 \
}
#define STX104_EXTENT 16
/**
* The highest base address possible for an ISA device is 0x3FF; this results in
* 1024 possible base addresses. Dividing the number of possible base addresses
* by the address extent taken by each device results in the maximum number of
* devices on a system.
*/
#define MAX_NUM_STX104 (1024 / STX104_EXTENT)
static unsigned base[MAX_NUM_STX104];
static unsigned num_stx104;
module_param_array(base, uint, &num_stx104, 0);
MODULE_PARM_DESC(base, "Apex Embedded Systems STX104 base addresses");
/**
* struct stx104_iio - IIO device private data structure
* @chan_out_states: channels' output states
* @base: base port address of the IIO device
*/
struct stx104_iio {
unsigned chan_out_states[STX104_NUM_CHAN];
unsigned base;
};
static int stx104_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val, int *val2, long mask)
{
struct stx104_iio *const priv = iio_priv(indio_dev);
if (mask != IIO_CHAN_INFO_RAW)
return -EINVAL;
*val = priv->chan_out_states[chan->channel];
return IIO_VAL_INT;
}
static int stx104_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val, int val2, long mask)
{
struct stx104_iio *const priv = iio_priv(indio_dev);
const unsigned chan_addr_offset = 2 * chan->channel;
if (mask != IIO_CHAN_INFO_RAW)
return -EINVAL;
priv->chan_out_states[chan->channel] = val;
outw(val, priv->base + 4 + chan_addr_offset);
return 0;
}
static const struct iio_info stx104_info = {
.driver_module = THIS_MODULE,
.read_raw = stx104_read_raw,
.write_raw = stx104_write_raw
};
static const struct iio_chan_spec stx104_channels[STX104_NUM_CHAN] = {
STX104_CHAN(0),
STX104_CHAN(1)
};
static int stx104_probe(struct device *dev, unsigned int id)
{
struct iio_dev *indio_dev;
struct stx104_iio *priv;
indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
if (!indio_dev)
return -ENOMEM;
if (!devm_request_region(dev, base[id], STX104_EXTENT,
dev_name(dev))) {
dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
base[id], base[id] + STX104_EXTENT);
return -EBUSY;
}
indio_dev->info = &stx104_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = stx104_channels;
indio_dev->num_channels = STX104_NUM_CHAN;
indio_dev->name = dev_name(dev);
priv = iio_priv(indio_dev);
priv->base = base[id];
/* initialize DAC output to 0V */
outw(0, base[id] + 4);
outw(0, base[id] + 6);
return devm_iio_device_register(dev, indio_dev);
}
static struct isa_driver stx104_driver = {
.probe = stx104_probe,
.driver = {
.name = "stx104"
}
};
static void __exit stx104_exit(void)
{
isa_unregister_driver(&stx104_driver);
}
static int __init stx104_init(void)
{
return isa_register_driver(&stx104_driver, num_stx104);
}
module_init(stx104_init);
module_exit(stx104_exit);
MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
MODULE_DESCRIPTION("Apex Embedded Systems STX104 DAC driver");
MODULE_LICENSE("GPL v2");

298
drivers/iio/dac/vf610_dac.c Normal file
View File

@ -0,0 +1,298 @@
/*
* Freescale Vybrid vf610 DAC driver
*
* Copyright 2016 Toradex AG
*
* 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/clk.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/slab.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#define VF610_DACx_STATCTRL 0x20
#define VF610_DAC_DACEN BIT(15)
#define VF610_DAC_DACRFS BIT(14)
#define VF610_DAC_LPEN BIT(11)
#define VF610_DAC_DAT0(x) ((x) & 0xFFF)
enum vf610_conversion_mode_sel {
VF610_DAC_CONV_HIGH_POWER,
VF610_DAC_CONV_LOW_POWER,
};
struct vf610_dac {
struct clk *clk;
struct device *dev;
enum vf610_conversion_mode_sel conv_mode;
void __iomem *regs;
};
static void vf610_dac_init(struct vf610_dac *info)
{
int val;
info->conv_mode = VF610_DAC_CONV_LOW_POWER;
val = VF610_DAC_DACEN | VF610_DAC_DACRFS |
VF610_DAC_LPEN;
writel(val, info->regs + VF610_DACx_STATCTRL);
}
static void vf610_dac_exit(struct vf610_dac *info)
{
int val;
val = readl(info->regs + VF610_DACx_STATCTRL);
val &= ~VF610_DAC_DACEN;
writel(val, info->regs + VF610_DACx_STATCTRL);
}
static int vf610_set_conversion_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
unsigned int mode)
{
struct vf610_dac *info = iio_priv(indio_dev);
int val;
mutex_lock(&indio_dev->mlock);
info->conv_mode = mode;
val = readl(info->regs + VF610_DACx_STATCTRL);
if (mode)
val |= VF610_DAC_LPEN;
else
val &= ~VF610_DAC_LPEN;
writel(val, info->regs + VF610_DACx_STATCTRL);
mutex_unlock(&indio_dev->mlock);
return 0;
}
static int vf610_get_conversion_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
struct vf610_dac *info = iio_priv(indio_dev);
return info->conv_mode;
}
static const char * const vf610_conv_modes[] = { "high-power", "low-power" };
static const struct iio_enum vf610_conversion_mode = {
.items = vf610_conv_modes,
.num_items = ARRAY_SIZE(vf610_conv_modes),
.get = vf610_get_conversion_mode,
.set = vf610_set_conversion_mode,
};
static const struct iio_chan_spec_ext_info vf610_ext_info[] = {
IIO_ENUM("conversion_mode", IIO_SHARED_BY_DIR,
&vf610_conversion_mode),
{},
};
#define VF610_DAC_CHAN(_chan_type) { \
.type = (_chan_type), \
.output = 1, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
.ext_info = vf610_ext_info, \
}
static const struct iio_chan_spec vf610_dac_iio_channels[] = {
VF610_DAC_CHAN(IIO_VOLTAGE),
};
static int vf610_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2,
long mask)
{
struct vf610_dac *info = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
*val = VF610_DAC_DAT0(readl(info->regs));
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
/*
* DACRFS is always 1 for valid reference and typical
* reference voltage as per Vybrid datasheet is 3.3V
* from section 9.1.2.1 of Vybrid datasheet
*/
*val = 3300 /* mV */;
*val2 = 12;
return IIO_VAL_FRACTIONAL_LOG2;
default:
return -EINVAL;
}
}
static int vf610_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2,
long mask)
{
struct vf610_dac *info = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
mutex_lock(&indio_dev->mlock);
writel(VF610_DAC_DAT0(val), info->regs);
mutex_unlock(&indio_dev->mlock);
return 0;
default:
return -EINVAL;
}
}
static const struct iio_info vf610_dac_iio_info = {
.driver_module = THIS_MODULE,
.read_raw = &vf610_read_raw,
.write_raw = &vf610_write_raw,
};
static const struct of_device_id vf610_dac_match[] = {
{ .compatible = "fsl,vf610-dac", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, vf610_dac_match);
static int vf610_dac_probe(struct platform_device *pdev)
{
struct iio_dev *indio_dev;
struct vf610_dac *info;
struct resource *mem;
int ret;
indio_dev = devm_iio_device_alloc(&pdev->dev,
sizeof(struct vf610_dac));
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))
return PTR_ERR(info->regs);
info->clk = devm_clk_get(&pdev->dev, "dac");
if (IS_ERR(info->clk)) {
dev_err(&pdev->dev, "Failed getting clock, err = %ld\n",
PTR_ERR(info->clk));
return PTR_ERR(info->clk);
}
platform_set_drvdata(pdev, indio_dev);
indio_dev->name = dev_name(&pdev->dev);
indio_dev->dev.parent = &pdev->dev;
indio_dev->dev.of_node = pdev->dev.of_node;
indio_dev->info = &vf610_dac_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = vf610_dac_iio_channels;
indio_dev->num_channels = ARRAY_SIZE(vf610_dac_iio_channels);
ret = clk_prepare_enable(info->clk);
if (ret) {
dev_err(&pdev->dev,
"Could not prepare or enable the clock\n");
return ret;
}
vf610_dac_init(info);
ret = iio_device_register(indio_dev);
if (ret) {
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);
return ret;
}
static int vf610_dac_remove(struct platform_device *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct vf610_dac *info = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
vf610_dac_exit(info);
clk_disable_unprepare(info->clk);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int vf610_dac_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct vf610_dac *info = iio_priv(indio_dev);
vf610_dac_exit(info);
clk_disable_unprepare(info->clk);
return 0;
}
static int vf610_dac_resume(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct vf610_dac *info = iio_priv(indio_dev);
int ret;
ret = clk_prepare_enable(info->clk);
if (ret)
return ret;
vf610_dac_init(info);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(vf610_dac_pm_ops, vf610_dac_suspend, vf610_dac_resume);
static struct platform_driver vf610_dac_driver = {
.probe = vf610_dac_probe,
.remove = vf610_dac_remove,
.driver = {
.name = "vf610-dac",
.of_match_table = vf610_dac_match,
.pm = &vf610_dac_pm_ops,
},
};
module_platform_driver(vf610_dac_driver);
MODULE_AUTHOR("Sanchayan Maity <sanchayan.maity@toradex.com>");
MODULE_DESCRIPTION("Freescale VF610 DAC driver");
MODULE_LICENSE("GPL v2");

View File

@ -185,6 +185,11 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
.drdy_irq = {
.addr = ST_GYRO_1_DRDY_IRQ_ADDR,
.mask_int2 = ST_GYRO_1_DRDY_IRQ_INT2_MASK,
/*
* The sensor has IHL (active low) and open
* drain settings, but only for INT1 and not
* for the DRDY line on INT2.
*/
},
.multi_read_bit = ST_GYRO_1_MULTIREAD_BIT,
.bootime = 2,
@ -248,6 +253,11 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
.drdy_irq = {
.addr = ST_GYRO_2_DRDY_IRQ_ADDR,
.mask_int2 = ST_GYRO_2_DRDY_IRQ_INT2_MASK,
/*
* The sensor has IHL (active low) and open
* drain settings, but only for INT1 and not
* for the DRDY line on INT2.
*/
},
.multi_read_bit = ST_GYRO_2_MULTIREAD_BIT,
.bootime = 2,
@ -307,6 +317,11 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
.drdy_irq = {
.addr = ST_GYRO_3_DRDY_IRQ_ADDR,
.mask_int2 = ST_GYRO_3_DRDY_IRQ_INT2_MASK,
/*
* The sensor has IHL (active low) and open
* drain settings, but only for INT1 and not
* for the DRDY line on INT2.
*/
},
.multi_read_bit = ST_GYRO_3_MULTIREAD_BIT,
.bootime = 2,

View File

@ -3,7 +3,35 @@
#
# When adding new entries keep the list in alphabetical order
menu "Health sensors"
menu "Health Sensors"
menu "Heart Rate Monitors"
config AFE4403
tristate "TI AFE4403 Heart Rate Monitor"
depends on SPI_MASTER
select REGMAP_SPI
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say yes to choose the Texas Instruments AFE4403
heart rate monitor and low-cost pulse oximeter.
To compile this driver as a module, choose M here: the
module will be called afe4403.
config AFE4404
tristate "TI AFE4404 heart rate and pulse oximeter sensor"
depends on I2C
select REGMAP_I2C
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say yes to choose the Texas Instruments AFE4404
heart rate monitor and low-cost pulse oximeter.
To compile this driver as a module, choose M here: the
module will be called afe4404.
config MAX30100
tristate "MAX30100 heart rate and pulse oximeter sensor"
@ -19,3 +47,5 @@ config MAX30100
module will be called max30100.
endmenu
endmenu

View File

@ -4,4 +4,6 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_AFE4403) += afe4403.o
obj-$(CONFIG_AFE4404) += afe4404.o
obj-$(CONFIG_MAX30100) += max30100.o

View File

@ -0,0 +1,708 @@
/*
* AFE4403 Heart Rate Monitors and Low-Cost Pulse Oximeters
*
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
* Andrew F. Davis <afd@ti.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.
*
* 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/device.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include <linux/sysfs.h>
#include <linux/regulator/consumer.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
#include "afe440x.h"
#define AFE4403_DRIVER_NAME "afe4403"
/* AFE4403 Registers */
#define AFE4403_TIAGAIN 0x20
#define AFE4403_TIA_AMB_GAIN 0x21
/* AFE4403 GAIN register fields */
#define AFE4403_TIAGAIN_RES_MASK GENMASK(2, 0)
#define AFE4403_TIAGAIN_RES_SHIFT 0
#define AFE4403_TIAGAIN_CAP_MASK GENMASK(7, 3)
#define AFE4403_TIAGAIN_CAP_SHIFT 3
/* AFE4403 LEDCNTRL register fields */
#define AFE440X_LEDCNTRL_LED1_MASK GENMASK(15, 8)
#define AFE440X_LEDCNTRL_LED1_SHIFT 8
#define AFE440X_LEDCNTRL_LED2_MASK GENMASK(7, 0)
#define AFE440X_LEDCNTRL_LED2_SHIFT 0
#define AFE440X_LEDCNTRL_LED_RANGE_MASK GENMASK(17, 16)
#define AFE440X_LEDCNTRL_LED_RANGE_SHIFT 16
/* AFE4403 CONTROL2 register fields */
#define AFE440X_CONTROL2_PWR_DWN_TX BIT(2)
#define AFE440X_CONTROL2_EN_SLOW_DIAG BIT(8)
#define AFE440X_CONTROL2_DIAG_OUT_TRI BIT(10)
#define AFE440X_CONTROL2_TX_BRDG_MOD BIT(11)
#define AFE440X_CONTROL2_TX_REF_MASK GENMASK(18, 17)
#define AFE440X_CONTROL2_TX_REF_SHIFT 17
/* AFE4404 NULL fields */
#define NULL_MASK 0
#define NULL_SHIFT 0
/* AFE4403 LEDCNTRL values */
#define AFE440X_LEDCNTRL_RANGE_TX_HALF 0x1
#define AFE440X_LEDCNTRL_RANGE_TX_FULL 0x2
#define AFE440X_LEDCNTRL_RANGE_TX_OFF 0x3
/* AFE4403 CONTROL2 values */
#define AFE440X_CONTROL2_TX_REF_025 0x0
#define AFE440X_CONTROL2_TX_REF_050 0x1
#define AFE440X_CONTROL2_TX_REF_100 0x2
#define AFE440X_CONTROL2_TX_REF_075 0x3
/* AFE4403 CONTROL3 values */
#define AFE440X_CONTROL3_CLK_DIV_2 0x0
#define AFE440X_CONTROL3_CLK_DIV_4 0x2
#define AFE440X_CONTROL3_CLK_DIV_6 0x3
#define AFE440X_CONTROL3_CLK_DIV_8 0x4
#define AFE440X_CONTROL3_CLK_DIV_12 0x5
#define AFE440X_CONTROL3_CLK_DIV_1 0x7
/* AFE4403 TIAGAIN_CAP values */
#define AFE4403_TIAGAIN_CAP_5_P 0x0
#define AFE4403_TIAGAIN_CAP_10_P 0x1
#define AFE4403_TIAGAIN_CAP_20_P 0x2
#define AFE4403_TIAGAIN_CAP_30_P 0x3
#define AFE4403_TIAGAIN_CAP_55_P 0x8
#define AFE4403_TIAGAIN_CAP_155_P 0x10
/* AFE4403 TIAGAIN_RES values */
#define AFE4403_TIAGAIN_RES_500_K 0x0
#define AFE4403_TIAGAIN_RES_250_K 0x1
#define AFE4403_TIAGAIN_RES_100_K 0x2
#define AFE4403_TIAGAIN_RES_50_K 0x3
#define AFE4403_TIAGAIN_RES_25_K 0x4
#define AFE4403_TIAGAIN_RES_10_K 0x5
#define AFE4403_TIAGAIN_RES_1_M 0x6
#define AFE4403_TIAGAIN_RES_NONE 0x7
/**
* struct afe4403_data
* @dev - Device structure
* @spi - SPI device handle
* @regmap - Register map of the device
* @regulator - Pointer to the regulator for the IC
* @trig - IIO trigger for this device
* @irq - ADC_RDY line interrupt number
*/
struct afe4403_data {
struct device *dev;
struct spi_device *spi;
struct regmap *regmap;
struct regulator *regulator;
struct iio_trigger *trig;
int irq;
};
enum afe4403_chan_id {
LED1,
ALED1,
LED2,
ALED2,
LED1_ALED1,
LED2_ALED2,
ILED1,
ILED2,
};
static const struct afe440x_reg_info afe4403_reg_info[] = {
[LED1] = AFE440X_REG_INFO(AFE440X_LED1VAL, 0, NULL),
[ALED1] = AFE440X_REG_INFO(AFE440X_ALED1VAL, 0, NULL),
[LED2] = AFE440X_REG_INFO(AFE440X_LED2VAL, 0, NULL),
[ALED2] = AFE440X_REG_INFO(AFE440X_ALED2VAL, 0, NULL),
[LED1_ALED1] = AFE440X_REG_INFO(AFE440X_LED1_ALED1VAL, 0, NULL),
[LED2_ALED2] = AFE440X_REG_INFO(AFE440X_LED2_ALED2VAL, 0, NULL),
[ILED1] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE440X_LEDCNTRL_LED1),
[ILED2] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE440X_LEDCNTRL_LED2),
};
static const struct iio_chan_spec afe4403_channels[] = {
/* ADC values */
AFE440X_INTENSITY_CHAN(LED1, "led1", 0),
AFE440X_INTENSITY_CHAN(ALED1, "led1_ambient", 0),
AFE440X_INTENSITY_CHAN(LED2, "led2", 0),
AFE440X_INTENSITY_CHAN(ALED2, "led2_ambient", 0),
AFE440X_INTENSITY_CHAN(LED1_ALED1, "led1-led1_ambient", 0),
AFE440X_INTENSITY_CHAN(LED2_ALED2, "led2-led2_ambient", 0),
/* LED current */
AFE440X_CURRENT_CHAN(ILED1, "led1"),
AFE440X_CURRENT_CHAN(ILED2, "led2"),
};
static const struct afe440x_val_table afe4403_res_table[] = {
{ 500000 }, { 250000 }, { 100000 }, { 50000 },
{ 25000 }, { 10000 }, { 1000000 }, { 0 },
};
AFE440X_TABLE_ATTR(tia_resistance_available, afe4403_res_table);
static const struct afe440x_val_table afe4403_cap_table[] = {
{ 0, 5000 }, { 0, 10000 }, { 0, 20000 }, { 0, 25000 },
{ 0, 30000 }, { 0, 35000 }, { 0, 45000 }, { 0, 50000 },
{ 0, 55000 }, { 0, 60000 }, { 0, 70000 }, { 0, 75000 },
{ 0, 80000 }, { 0, 85000 }, { 0, 95000 }, { 0, 100000 },
{ 0, 155000 }, { 0, 160000 }, { 0, 170000 }, { 0, 175000 },
{ 0, 180000 }, { 0, 185000 }, { 0, 195000 }, { 0, 200000 },
{ 0, 205000 }, { 0, 210000 }, { 0, 220000 }, { 0, 225000 },
{ 0, 230000 }, { 0, 235000 }, { 0, 245000 }, { 0, 250000 },
};
AFE440X_TABLE_ATTR(tia_capacitance_available, afe4403_cap_table);
static ssize_t afe440x_show_register(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct afe4403_data *afe = iio_priv(indio_dev);
struct afe440x_attr *afe440x_attr = to_afe440x_attr(attr);
unsigned int reg_val, type;
int vals[2];
int ret, val_len;
ret = regmap_read(afe->regmap, afe440x_attr->reg, &reg_val);
if (ret)
return ret;
reg_val &= afe440x_attr->mask;
reg_val >>= afe440x_attr->shift;
switch (afe440x_attr->type) {
case SIMPLE:
type = IIO_VAL_INT;
val_len = 1;
vals[0] = reg_val;
break;
case RESISTANCE:
case CAPACITANCE:
type = IIO_VAL_INT_PLUS_MICRO;
val_len = 2;
if (reg_val < afe440x_attr->table_size) {
vals[0] = afe440x_attr->val_table[reg_val].integer;
vals[1] = afe440x_attr->val_table[reg_val].fract;
break;
}
return -EINVAL;
default:
return -EINVAL;
}
return iio_format_value(buf, type, val_len, vals);
}
static ssize_t afe440x_store_register(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct afe4403_data *afe = iio_priv(indio_dev);
struct afe440x_attr *afe440x_attr = to_afe440x_attr(attr);
int val, integer, fract, ret;
ret = iio_str_to_fixpoint(buf, 100000, &integer, &fract);
if (ret)
return ret;
switch (afe440x_attr->type) {
case SIMPLE:
val = integer;
break;
case RESISTANCE:
case CAPACITANCE:
for (val = 0; val < afe440x_attr->table_size; val++)
if (afe440x_attr->val_table[val].integer == integer &&
afe440x_attr->val_table[val].fract == fract)
break;
if (val == afe440x_attr->table_size)
return -EINVAL;
break;
default:
return -EINVAL;
}
ret = regmap_update_bits(afe->regmap, afe440x_attr->reg,
afe440x_attr->mask,
(val << afe440x_attr->shift));
if (ret)
return ret;
return count;
}
static AFE440X_ATTR(tia_separate_en, AFE4403_TIAGAIN, AFE440X_TIAGAIN_ENSEPGAIN, SIMPLE, NULL, 0);
static AFE440X_ATTR(tia_resistance1, AFE4403_TIAGAIN, AFE4403_TIAGAIN_RES, RESISTANCE, afe4403_res_table, ARRAY_SIZE(afe4403_res_table));
static AFE440X_ATTR(tia_capacitance1, AFE4403_TIAGAIN, AFE4403_TIAGAIN_CAP, CAPACITANCE, afe4403_cap_table, ARRAY_SIZE(afe4403_cap_table));
static AFE440X_ATTR(tia_resistance2, AFE4403_TIA_AMB_GAIN, AFE4403_TIAGAIN_RES, RESISTANCE, afe4403_res_table, ARRAY_SIZE(afe4403_res_table));
static AFE440X_ATTR(tia_capacitance2, AFE4403_TIA_AMB_GAIN, AFE4403_TIAGAIN_RES, CAPACITANCE, afe4403_cap_table, ARRAY_SIZE(afe4403_cap_table));
static struct attribute *afe440x_attributes[] = {
&afe440x_attr_tia_separate_en.dev_attr.attr,
&afe440x_attr_tia_resistance1.dev_attr.attr,
&afe440x_attr_tia_capacitance1.dev_attr.attr,
&afe440x_attr_tia_resistance2.dev_attr.attr,
&afe440x_attr_tia_capacitance2.dev_attr.attr,
&dev_attr_tia_resistance_available.attr,
&dev_attr_tia_capacitance_available.attr,
NULL
};
static const struct attribute_group afe440x_attribute_group = {
.attrs = afe440x_attributes
};
static int afe4403_read(struct afe4403_data *afe, unsigned int reg, u32 *val)
{
u8 tx[4] = {AFE440X_CONTROL0, 0x0, 0x0, AFE440X_CONTROL0_READ};
u8 rx[3];
int ret;
/* Enable reading from the device */
ret = spi_write_then_read(afe->spi, tx, 4, NULL, 0);
if (ret)
return ret;
ret = spi_write_then_read(afe->spi, &reg, 1, rx, 3);
if (ret)
return ret;
*val = (rx[0] << 16) |
(rx[1] << 8) |
(rx[2]);
/* Disable reading from the device */
tx[3] = AFE440X_CONTROL0_WRITE;
ret = spi_write_then_read(afe->spi, tx, 4, NULL, 0);
if (ret)
return ret;
return 0;
}
static int afe4403_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct afe4403_data *afe = iio_priv(indio_dev);
const struct afe440x_reg_info reg_info = afe4403_reg_info[chan->address];
int ret;
switch (chan->type) {
case IIO_INTENSITY:
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = afe4403_read(afe, reg_info.reg, val);
if (ret)
return ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_OFFSET:
ret = regmap_read(afe->regmap, reg_info.offreg,
val);
if (ret)
return ret;
*val &= reg_info.mask;
*val >>= reg_info.shift;
return IIO_VAL_INT;
}
break;
case IIO_CURRENT:
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = regmap_read(afe->regmap, reg_info.reg, val);
if (ret)
return ret;
*val &= reg_info.mask;
*val >>= reg_info.shift;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 0;
*val2 = 800000;
return IIO_VAL_INT_PLUS_MICRO;
}
break;
default:
break;
}
return -EINVAL;
}
static int afe4403_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct afe4403_data *afe = iio_priv(indio_dev);
const struct afe440x_reg_info reg_info = afe4403_reg_info[chan->address];
switch (chan->type) {
case IIO_INTENSITY:
switch (mask) {
case IIO_CHAN_INFO_OFFSET:
return regmap_update_bits(afe->regmap,
reg_info.offreg,
reg_info.mask,
(val << reg_info.shift));
}
break;
case IIO_CURRENT:
switch (mask) {
case IIO_CHAN_INFO_RAW:
return regmap_update_bits(afe->regmap,
reg_info.reg,
reg_info.mask,
(val << reg_info.shift));
}
break;
default:
break;
}
return -EINVAL;
}
static const struct iio_info afe4403_iio_info = {
.attrs = &afe440x_attribute_group,
.read_raw = afe4403_read_raw,
.write_raw = afe4403_write_raw,
.driver_module = THIS_MODULE,
};
static irqreturn_t afe4403_trigger_handler(int irq, void *private)
{
struct iio_poll_func *pf = private;
struct iio_dev *indio_dev = pf->indio_dev;
struct afe4403_data *afe = iio_priv(indio_dev);
int ret, bit, i = 0;
s32 buffer[8];
u8 tx[4] = {AFE440X_CONTROL0, 0x0, 0x0, AFE440X_CONTROL0_READ};
u8 rx[3];
/* Enable reading from the device */
ret = spi_write_then_read(afe->spi, tx, 4, NULL, 0);
if (ret)
goto err;
for_each_set_bit(bit, indio_dev->active_scan_mask,
indio_dev->masklength) {
ret = spi_write_then_read(afe->spi,
&afe4403_reg_info[bit].reg, 1,
rx, 3);
if (ret)
goto err;
buffer[i++] = (rx[0] << 16) |
(rx[1] << 8) |
(rx[2]);
}
/* Disable reading from the device */
tx[3] = AFE440X_CONTROL0_WRITE;
ret = spi_write_then_read(afe->spi, tx, 4, NULL, 0);
if (ret)
goto err;
iio_push_to_buffers_with_timestamp(indio_dev, buffer, pf->timestamp);
err:
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
static const struct iio_trigger_ops afe4403_trigger_ops = {
.owner = THIS_MODULE,
};
#define AFE4403_TIMING_PAIRS \
{ AFE440X_LED2STC, 0x000050 }, \
{ AFE440X_LED2ENDC, 0x0003e7 }, \
{ AFE440X_LED1LEDSTC, 0x0007d0 }, \
{ AFE440X_LED1LEDENDC, 0x000bb7 }, \
{ AFE440X_ALED2STC, 0x000438 }, \
{ AFE440X_ALED2ENDC, 0x0007cf }, \
{ AFE440X_LED1STC, 0x000820 }, \
{ AFE440X_LED1ENDC, 0x000bb7 }, \
{ AFE440X_LED2LEDSTC, 0x000000 }, \
{ AFE440X_LED2LEDENDC, 0x0003e7 }, \
{ AFE440X_ALED1STC, 0x000c08 }, \
{ AFE440X_ALED1ENDC, 0x000f9f }, \
{ AFE440X_LED2CONVST, 0x0003ef }, \
{ AFE440X_LED2CONVEND, 0x0007cf }, \
{ AFE440X_ALED2CONVST, 0x0007d7 }, \
{ AFE440X_ALED2CONVEND, 0x000bb7 }, \
{ AFE440X_LED1CONVST, 0x000bbf }, \
{ AFE440X_LED1CONVEND, 0x009c3f }, \
{ AFE440X_ALED1CONVST, 0x000fa7 }, \
{ AFE440X_ALED1CONVEND, 0x001387 }, \
{ AFE440X_ADCRSTSTCT0, 0x0003e8 }, \
{ AFE440X_ADCRSTENDCT0, 0x0003eb }, \
{ AFE440X_ADCRSTSTCT1, 0x0007d0 }, \
{ AFE440X_ADCRSTENDCT1, 0x0007d3 }, \
{ AFE440X_ADCRSTSTCT2, 0x000bb8 }, \
{ AFE440X_ADCRSTENDCT2, 0x000bbb }, \
{ AFE440X_ADCRSTSTCT3, 0x000fa0 }, \
{ AFE440X_ADCRSTENDCT3, 0x000fa3 }, \
{ AFE440X_PRPCOUNT, 0x009c3f }, \
{ AFE440X_PDNCYCLESTC, 0x001518 }, \
{ AFE440X_PDNCYCLEENDC, 0x00991f }
static const struct reg_sequence afe4403_reg_sequences[] = {
AFE4403_TIMING_PAIRS,
{ AFE440X_CONTROL1, AFE440X_CONTROL1_TIMEREN | 0x000007},
{ AFE4403_TIA_AMB_GAIN, AFE4403_TIAGAIN_RES_1_M },
{ AFE440X_LEDCNTRL, (0x14 << AFE440X_LEDCNTRL_LED1_SHIFT) |
(0x14 << AFE440X_LEDCNTRL_LED2_SHIFT) },
{ AFE440X_CONTROL2, AFE440X_CONTROL2_TX_REF_050 <<
AFE440X_CONTROL2_TX_REF_SHIFT },
};
static const struct regmap_range afe4403_yes_ranges[] = {
regmap_reg_range(AFE440X_LED2VAL, AFE440X_LED1_ALED1VAL),
};
static const struct regmap_access_table afe4403_volatile_table = {
.yes_ranges = afe4403_yes_ranges,
.n_yes_ranges = ARRAY_SIZE(afe4403_yes_ranges),
};
static const struct regmap_config afe4403_regmap_config = {
.reg_bits = 8,
.val_bits = 24,
.max_register = AFE440X_PDNCYCLEENDC,
.cache_type = REGCACHE_RBTREE,
.volatile_table = &afe4403_volatile_table,
};
#ifdef CONFIG_OF
static const struct of_device_id afe4403_of_match[] = {
{ .compatible = "ti,afe4403", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, afe4403_of_match);
#endif
static int __maybe_unused afe4403_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct afe4403_data *afe = iio_priv(indio_dev);
int ret;
ret = regmap_update_bits(afe->regmap, AFE440X_CONTROL2,
AFE440X_CONTROL2_PDN_AFE,
AFE440X_CONTROL2_PDN_AFE);
if (ret)
return ret;
ret = regulator_disable(afe->regulator);
if (ret) {
dev_err(dev, "Unable to disable regulator\n");
return ret;
}
return 0;
}
static int __maybe_unused afe4403_resume(struct device *dev)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct afe4403_data *afe = iio_priv(indio_dev);
int ret;
ret = regulator_enable(afe->regulator);
if (ret) {
dev_err(dev, "Unable to enable regulator\n");
return ret;
}
ret = regmap_update_bits(afe->regmap, AFE440X_CONTROL2,
AFE440X_CONTROL2_PDN_AFE, 0);
if (ret)
return ret;
return 0;
}
static SIMPLE_DEV_PM_OPS(afe4403_pm_ops, afe4403_suspend, afe4403_resume);
static int afe4403_probe(struct spi_device *spi)
{
struct iio_dev *indio_dev;
struct afe4403_data *afe;
int ret;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*afe));
if (!indio_dev)
return -ENOMEM;
afe = iio_priv(indio_dev);
spi_set_drvdata(spi, indio_dev);
afe->dev = &spi->dev;
afe->spi = spi;
afe->irq = spi->irq;
afe->regmap = devm_regmap_init_spi(spi, &afe4403_regmap_config);
if (IS_ERR(afe->regmap)) {
dev_err(afe->dev, "Unable to allocate register map\n");
return PTR_ERR(afe->regmap);
}
afe->regulator = devm_regulator_get(afe->dev, "tx_sup");
if (IS_ERR(afe->regulator)) {
dev_err(afe->dev, "Unable to get regulator\n");
return PTR_ERR(afe->regulator);
}
ret = regulator_enable(afe->regulator);
if (ret) {
dev_err(afe->dev, "Unable to enable regulator\n");
return ret;
}
ret = regmap_write(afe->regmap, AFE440X_CONTROL0,
AFE440X_CONTROL0_SW_RESET);
if (ret) {
dev_err(afe->dev, "Unable to reset device\n");
goto err_disable_reg;
}
ret = regmap_multi_reg_write(afe->regmap, afe4403_reg_sequences,
ARRAY_SIZE(afe4403_reg_sequences));
if (ret) {
dev_err(afe->dev, "Unable to set register defaults\n");
goto err_disable_reg;
}
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->dev.parent = afe->dev;
indio_dev->channels = afe4403_channels;
indio_dev->num_channels = ARRAY_SIZE(afe4403_channels);
indio_dev->name = AFE4403_DRIVER_NAME;
indio_dev->info = &afe4403_iio_info;
if (afe->irq > 0) {
afe->trig = devm_iio_trigger_alloc(afe->dev,
"%s-dev%d",
indio_dev->name,
indio_dev->id);
if (!afe->trig) {
dev_err(afe->dev, "Unable to allocate IIO trigger\n");
ret = -ENOMEM;
goto err_disable_reg;
}
iio_trigger_set_drvdata(afe->trig, indio_dev);
afe->trig->ops = &afe4403_trigger_ops;
afe->trig->dev.parent = afe->dev;
ret = iio_trigger_register(afe->trig);
if (ret) {
dev_err(afe->dev, "Unable to register IIO trigger\n");
goto err_disable_reg;
}
ret = devm_request_threaded_irq(afe->dev, afe->irq,
iio_trigger_generic_data_rdy_poll,
NULL, IRQF_ONESHOT,
AFE4403_DRIVER_NAME,
afe->trig);
if (ret) {
dev_err(afe->dev, "Unable to request IRQ\n");
goto err_trig;
}
}
ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
afe4403_trigger_handler, NULL);
if (ret) {
dev_err(afe->dev, "Unable to setup buffer\n");
goto err_trig;
}
ret = iio_device_register(indio_dev);
if (ret) {
dev_err(afe->dev, "Unable to register IIO device\n");
goto err_buff;
}
return 0;
err_buff:
iio_triggered_buffer_cleanup(indio_dev);
err_trig:
if (afe->irq > 0)
iio_trigger_unregister(afe->trig);
err_disable_reg:
regulator_disable(afe->regulator);
return ret;
}
static int afe4403_remove(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct afe4403_data *afe = iio_priv(indio_dev);
int ret;
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
if (afe->irq > 0)
iio_trigger_unregister(afe->trig);
ret = regulator_disable(afe->regulator);
if (ret) {
dev_err(afe->dev, "Unable to disable regulator\n");
return ret;
}
return 0;
}
static const struct spi_device_id afe4403_ids[] = {
{ "afe4403", 0 },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(spi, afe4403_ids);
static struct spi_driver afe4403_spi_driver = {
.driver = {
.name = AFE4403_DRIVER_NAME,
.of_match_table = of_match_ptr(afe4403_of_match),
.pm = &afe4403_pm_ops,
},
.probe = afe4403_probe,
.remove = afe4403_remove,
.id_table = afe4403_ids,
};
module_spi_driver(afe4403_spi_driver);
MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
MODULE_DESCRIPTION("TI AFE4403 Heart Rate and Pulse Oximeter");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,679 @@
/*
* AFE4404 Heart Rate Monitors and Low-Cost Pulse Oximeters
*
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
* Andrew F. Davis <afd@ti.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.
*
* 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/device.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/sysfs.h>
#include <linux/regulator/consumer.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
#include "afe440x.h"
#define AFE4404_DRIVER_NAME "afe4404"
/* AFE4404 registers */
#define AFE4404_TIA_GAIN_SEP 0x20
#define AFE4404_TIA_GAIN 0x21
#define AFE4404_PROG_TG_STC 0x34
#define AFE4404_PROG_TG_ENDC 0x35
#define AFE4404_LED3LEDSTC 0x36
#define AFE4404_LED3LEDENDC 0x37
#define AFE4404_CLKDIV_PRF 0x39
#define AFE4404_OFFDAC 0x3a
#define AFE4404_DEC 0x3d
#define AFE4404_AVG_LED2_ALED2VAL 0x3f
#define AFE4404_AVG_LED1_ALED1VAL 0x40
/* AFE4404 GAIN register fields */
#define AFE4404_TIA_GAIN_RES_MASK GENMASK(2, 0)
#define AFE4404_TIA_GAIN_RES_SHIFT 0
#define AFE4404_TIA_GAIN_CAP_MASK GENMASK(5, 3)
#define AFE4404_TIA_GAIN_CAP_SHIFT 3
/* AFE4404 LEDCNTRL register fields */
#define AFE4404_LEDCNTRL_ILED1_MASK GENMASK(5, 0)
#define AFE4404_LEDCNTRL_ILED1_SHIFT 0
#define AFE4404_LEDCNTRL_ILED2_MASK GENMASK(11, 6)
#define AFE4404_LEDCNTRL_ILED2_SHIFT 6
#define AFE4404_LEDCNTRL_ILED3_MASK GENMASK(17, 12)
#define AFE4404_LEDCNTRL_ILED3_SHIFT 12
/* AFE4404 CONTROL2 register fields */
#define AFE440X_CONTROL2_ILED_2X_MASK BIT(17)
#define AFE440X_CONTROL2_ILED_2X_SHIFT 17
/* AFE4404 CONTROL3 register fields */
#define AFE440X_CONTROL3_OSC_ENABLE BIT(9)
/* AFE4404 OFFDAC register current fields */
#define AFE4404_OFFDAC_CURR_LED1_MASK GENMASK(9, 5)
#define AFE4404_OFFDAC_CURR_LED1_SHIFT 5
#define AFE4404_OFFDAC_CURR_LED2_MASK GENMASK(19, 15)
#define AFE4404_OFFDAC_CURR_LED2_SHIFT 15
#define AFE4404_OFFDAC_CURR_LED3_MASK GENMASK(4, 0)
#define AFE4404_OFFDAC_CURR_LED3_SHIFT 0
#define AFE4404_OFFDAC_CURR_ALED1_MASK GENMASK(14, 10)
#define AFE4404_OFFDAC_CURR_ALED1_SHIFT 10
#define AFE4404_OFFDAC_CURR_ALED2_MASK GENMASK(4, 0)
#define AFE4404_OFFDAC_CURR_ALED2_SHIFT 0
/* AFE4404 NULL fields */
#define NULL_MASK 0
#define NULL_SHIFT 0
/* AFE4404 TIA_GAIN_CAP values */
#define AFE4404_TIA_GAIN_CAP_5_P 0x0
#define AFE4404_TIA_GAIN_CAP_2_5_P 0x1
#define AFE4404_TIA_GAIN_CAP_10_P 0x2
#define AFE4404_TIA_GAIN_CAP_7_5_P 0x3
#define AFE4404_TIA_GAIN_CAP_20_P 0x4
#define AFE4404_TIA_GAIN_CAP_17_5_P 0x5
#define AFE4404_TIA_GAIN_CAP_25_P 0x6
#define AFE4404_TIA_GAIN_CAP_22_5_P 0x7
/* AFE4404 TIA_GAIN_RES values */
#define AFE4404_TIA_GAIN_RES_500_K 0x0
#define AFE4404_TIA_GAIN_RES_250_K 0x1
#define AFE4404_TIA_GAIN_RES_100_K 0x2
#define AFE4404_TIA_GAIN_RES_50_K 0x3
#define AFE4404_TIA_GAIN_RES_25_K 0x4
#define AFE4404_TIA_GAIN_RES_10_K 0x5
#define AFE4404_TIA_GAIN_RES_1_M 0x6
#define AFE4404_TIA_GAIN_RES_2_M 0x7
/**
* struct afe4404_data
* @dev - Device structure
* @regmap - Register map of the device
* @regulator - Pointer to the regulator for the IC
* @trig - IIO trigger for this device
* @irq - ADC_RDY line interrupt number
*/
struct afe4404_data {
struct device *dev;
struct regmap *regmap;
struct regulator *regulator;
struct iio_trigger *trig;
int irq;
};
enum afe4404_chan_id {
LED1,
ALED1,
LED2,
ALED2,
LED3,
LED1_ALED1,
LED2_ALED2,
ILED1,
ILED2,
ILED3,
};
static const struct afe440x_reg_info afe4404_reg_info[] = {
[LED1] = AFE440X_REG_INFO(AFE440X_LED1VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_LED1),
[ALED1] = AFE440X_REG_INFO(AFE440X_ALED1VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_ALED1),
[LED2] = AFE440X_REG_INFO(AFE440X_LED2VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_LED2),
[ALED2] = AFE440X_REG_INFO(AFE440X_ALED2VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_ALED2),
[LED3] = AFE440X_REG_INFO(AFE440X_ALED2VAL, 0, NULL),
[LED1_ALED1] = AFE440X_REG_INFO(AFE440X_LED1_ALED1VAL, 0, NULL),
[LED2_ALED2] = AFE440X_REG_INFO(AFE440X_LED2_ALED2VAL, 0, NULL),
[ILED1] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE4404_LEDCNTRL_ILED1),
[ILED2] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE4404_LEDCNTRL_ILED2),
[ILED3] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE4404_LEDCNTRL_ILED3),
};
static const struct iio_chan_spec afe4404_channels[] = {
/* ADC values */
AFE440X_INTENSITY_CHAN(LED1, "led1", BIT(IIO_CHAN_INFO_OFFSET)),
AFE440X_INTENSITY_CHAN(ALED1, "led1_ambient", BIT(IIO_CHAN_INFO_OFFSET)),
AFE440X_INTENSITY_CHAN(LED2, "led2", BIT(IIO_CHAN_INFO_OFFSET)),
AFE440X_INTENSITY_CHAN(ALED2, "led2_ambient", BIT(IIO_CHAN_INFO_OFFSET)),
AFE440X_INTENSITY_CHAN(LED3, "led3", BIT(IIO_CHAN_INFO_OFFSET)),
AFE440X_INTENSITY_CHAN(LED1_ALED1, "led1-led1_ambient", 0),
AFE440X_INTENSITY_CHAN(LED2_ALED2, "led2-led2_ambient", 0),
/* LED current */
AFE440X_CURRENT_CHAN(ILED1, "led1"),
AFE440X_CURRENT_CHAN(ILED2, "led2"),
AFE440X_CURRENT_CHAN(ILED3, "led3"),
};
static const struct afe440x_val_table afe4404_res_table[] = {
{ .integer = 500000, .fract = 0 },
{ .integer = 250000, .fract = 0 },
{ .integer = 100000, .fract = 0 },
{ .integer = 50000, .fract = 0 },
{ .integer = 25000, .fract = 0 },
{ .integer = 10000, .fract = 0 },
{ .integer = 1000000, .fract = 0 },
{ .integer = 2000000, .fract = 0 },
};
AFE440X_TABLE_ATTR(tia_resistance_available, afe4404_res_table);
static const struct afe440x_val_table afe4404_cap_table[] = {
{ .integer = 0, .fract = 5000 },
{ .integer = 0, .fract = 2500 },
{ .integer = 0, .fract = 10000 },
{ .integer = 0, .fract = 7500 },
{ .integer = 0, .fract = 20000 },
{ .integer = 0, .fract = 17500 },
{ .integer = 0, .fract = 25000 },
{ .integer = 0, .fract = 22500 },
};
AFE440X_TABLE_ATTR(tia_capacitance_available, afe4404_cap_table);
static ssize_t afe440x_show_register(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct afe4404_data *afe = iio_priv(indio_dev);
struct afe440x_attr *afe440x_attr = to_afe440x_attr(attr);
unsigned int reg_val, type;
int vals[2];
int ret, val_len;
ret = regmap_read(afe->regmap, afe440x_attr->reg, &reg_val);
if (ret)
return ret;
reg_val &= afe440x_attr->mask;
reg_val >>= afe440x_attr->shift;
switch (afe440x_attr->type) {
case SIMPLE:
type = IIO_VAL_INT;
val_len = 1;
vals[0] = reg_val;
break;
case RESISTANCE:
case CAPACITANCE:
type = IIO_VAL_INT_PLUS_MICRO;
val_len = 2;
if (reg_val < afe440x_attr->table_size) {
vals[0] = afe440x_attr->val_table[reg_val].integer;
vals[1] = afe440x_attr->val_table[reg_val].fract;
break;
}
return -EINVAL;
default:
return -EINVAL;
}
return iio_format_value(buf, type, val_len, vals);
}
static ssize_t afe440x_store_register(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct afe4404_data *afe = iio_priv(indio_dev);
struct afe440x_attr *afe440x_attr = to_afe440x_attr(attr);
int val, integer, fract, ret;
ret = iio_str_to_fixpoint(buf, 100000, &integer, &fract);
if (ret)
return ret;
switch (afe440x_attr->type) {
case SIMPLE:
val = integer;
break;
case RESISTANCE:
case CAPACITANCE:
for (val = 0; val < afe440x_attr->table_size; val++)
if (afe440x_attr->val_table[val].integer == integer &&
afe440x_attr->val_table[val].fract == fract)
break;
if (val == afe440x_attr->table_size)
return -EINVAL;
break;
default:
return -EINVAL;
}
ret = regmap_update_bits(afe->regmap, afe440x_attr->reg,
afe440x_attr->mask,
(val << afe440x_attr->shift));
if (ret)
return ret;
return count;
}
static AFE440X_ATTR(tia_separate_en, AFE4404_TIA_GAIN_SEP, AFE440X_TIAGAIN_ENSEPGAIN, SIMPLE, NULL, 0);
static AFE440X_ATTR(tia_resistance1, AFE4404_TIA_GAIN, AFE4404_TIA_GAIN_RES, RESISTANCE, afe4404_res_table, ARRAY_SIZE(afe4404_res_table));
static AFE440X_ATTR(tia_capacitance1, AFE4404_TIA_GAIN, AFE4404_TIA_GAIN_CAP, CAPACITANCE, afe4404_cap_table, ARRAY_SIZE(afe4404_cap_table));
static AFE440X_ATTR(tia_resistance2, AFE4404_TIA_GAIN_SEP, AFE4404_TIA_GAIN_RES, RESISTANCE, afe4404_res_table, ARRAY_SIZE(afe4404_res_table));
static AFE440X_ATTR(tia_capacitance2, AFE4404_TIA_GAIN_SEP, AFE4404_TIA_GAIN_CAP, CAPACITANCE, afe4404_cap_table, ARRAY_SIZE(afe4404_cap_table));
static struct attribute *afe440x_attributes[] = {
&afe440x_attr_tia_separate_en.dev_attr.attr,
&afe440x_attr_tia_resistance1.dev_attr.attr,
&afe440x_attr_tia_capacitance1.dev_attr.attr,
&afe440x_attr_tia_resistance2.dev_attr.attr,
&afe440x_attr_tia_capacitance2.dev_attr.attr,
&dev_attr_tia_resistance_available.attr,
&dev_attr_tia_capacitance_available.attr,
NULL
};
static const struct attribute_group afe440x_attribute_group = {
.attrs = afe440x_attributes
};
static int afe4404_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct afe4404_data *afe = iio_priv(indio_dev);
const struct afe440x_reg_info reg_info = afe4404_reg_info[chan->address];
int ret;
switch (chan->type) {
case IIO_INTENSITY:
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = regmap_read(afe->regmap, reg_info.reg, val);
if (ret)
return ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_OFFSET:
ret = regmap_read(afe->regmap, reg_info.offreg,
val);
if (ret)
return ret;
*val &= reg_info.mask;
*val >>= reg_info.shift;
return IIO_VAL_INT;
}
break;
case IIO_CURRENT:
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = regmap_read(afe->regmap, reg_info.reg, val);
if (ret)
return ret;
*val &= reg_info.mask;
*val >>= reg_info.shift;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 0;
*val2 = 800000;
return IIO_VAL_INT_PLUS_MICRO;
}
break;
default:
break;
}
return -EINVAL;
}
static int afe4404_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct afe4404_data *afe = iio_priv(indio_dev);
const struct afe440x_reg_info reg_info = afe4404_reg_info[chan->address];
switch (chan->type) {
case IIO_INTENSITY:
switch (mask) {
case IIO_CHAN_INFO_OFFSET:
return regmap_update_bits(afe->regmap,
reg_info.offreg,
reg_info.mask,
(val << reg_info.shift));
}
break;
case IIO_CURRENT:
switch (mask) {
case IIO_CHAN_INFO_RAW:
return regmap_update_bits(afe->regmap,
reg_info.reg,
reg_info.mask,
(val << reg_info.shift));
}
break;
default:
break;
}
return -EINVAL;
}
static const struct iio_info afe4404_iio_info = {
.attrs = &afe440x_attribute_group,
.read_raw = afe4404_read_raw,
.write_raw = afe4404_write_raw,
.driver_module = THIS_MODULE,
};
static irqreturn_t afe4404_trigger_handler(int irq, void *private)
{
struct iio_poll_func *pf = private;
struct iio_dev *indio_dev = pf->indio_dev;
struct afe4404_data *afe = iio_priv(indio_dev);
int ret, bit, i = 0;
s32 buffer[10];
for_each_set_bit(bit, indio_dev->active_scan_mask,
indio_dev->masklength) {
ret = regmap_read(afe->regmap, afe4404_reg_info[bit].reg,
&buffer[i++]);
if (ret)
goto err;
}
iio_push_to_buffers_with_timestamp(indio_dev, buffer, pf->timestamp);
err:
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
static const struct iio_trigger_ops afe4404_trigger_ops = {
.owner = THIS_MODULE,
};
/* Default timings from data-sheet */
#define AFE4404_TIMING_PAIRS \
{ AFE440X_PRPCOUNT, 39999 }, \
{ AFE440X_LED2LEDSTC, 0 }, \
{ AFE440X_LED2LEDENDC, 398 }, \
{ AFE440X_LED2STC, 80 }, \
{ AFE440X_LED2ENDC, 398 }, \
{ AFE440X_ADCRSTSTCT0, 5600 }, \
{ AFE440X_ADCRSTENDCT0, 5606 }, \
{ AFE440X_LED2CONVST, 5607 }, \
{ AFE440X_LED2CONVEND, 6066 }, \
{ AFE4404_LED3LEDSTC, 400 }, \
{ AFE4404_LED3LEDENDC, 798 }, \
{ AFE440X_ALED2STC, 480 }, \
{ AFE440X_ALED2ENDC, 798 }, \
{ AFE440X_ADCRSTSTCT1, 6068 }, \
{ AFE440X_ADCRSTENDCT1, 6074 }, \
{ AFE440X_ALED2CONVST, 6075 }, \
{ AFE440X_ALED2CONVEND, 6534 }, \
{ AFE440X_LED1LEDSTC, 800 }, \
{ AFE440X_LED1LEDENDC, 1198 }, \
{ AFE440X_LED1STC, 880 }, \
{ AFE440X_LED1ENDC, 1198 }, \
{ AFE440X_ADCRSTSTCT2, 6536 }, \
{ AFE440X_ADCRSTENDCT2, 6542 }, \
{ AFE440X_LED1CONVST, 6543 }, \
{ AFE440X_LED1CONVEND, 7003 }, \
{ AFE440X_ALED1STC, 1280 }, \
{ AFE440X_ALED1ENDC, 1598 }, \
{ AFE440X_ADCRSTSTCT3, 7005 }, \
{ AFE440X_ADCRSTENDCT3, 7011 }, \
{ AFE440X_ALED1CONVST, 7012 }, \
{ AFE440X_ALED1CONVEND, 7471 }, \
{ AFE440X_PDNCYCLESTC, 7671 }, \
{ AFE440X_PDNCYCLEENDC, 39199 }
static const struct reg_sequence afe4404_reg_sequences[] = {
AFE4404_TIMING_PAIRS,
{ AFE440X_CONTROL1, AFE440X_CONTROL1_TIMEREN },
{ AFE4404_TIA_GAIN, AFE4404_TIA_GAIN_RES_50_K },
{ AFE440X_LEDCNTRL, (0xf << AFE4404_LEDCNTRL_ILED1_SHIFT) |
(0x3 << AFE4404_LEDCNTRL_ILED2_SHIFT) |
(0x3 << AFE4404_LEDCNTRL_ILED3_SHIFT) },
{ AFE440X_CONTROL2, AFE440X_CONTROL3_OSC_ENABLE },
};
static const struct regmap_range afe4404_yes_ranges[] = {
regmap_reg_range(AFE440X_LED2VAL, AFE440X_LED1_ALED1VAL),
regmap_reg_range(AFE4404_AVG_LED2_ALED2VAL, AFE4404_AVG_LED1_ALED1VAL),
};
static const struct regmap_access_table afe4404_volatile_table = {
.yes_ranges = afe4404_yes_ranges,
.n_yes_ranges = ARRAY_SIZE(afe4404_yes_ranges),
};
static const struct regmap_config afe4404_regmap_config = {
.reg_bits = 8,
.val_bits = 24,
.max_register = AFE4404_AVG_LED1_ALED1VAL,
.cache_type = REGCACHE_RBTREE,
.volatile_table = &afe4404_volatile_table,
};
#ifdef CONFIG_OF
static const struct of_device_id afe4404_of_match[] = {
{ .compatible = "ti,afe4404", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, afe4404_of_match);
#endif
static int __maybe_unused afe4404_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct afe4404_data *afe = iio_priv(indio_dev);
int ret;
ret = regmap_update_bits(afe->regmap, AFE440X_CONTROL2,
AFE440X_CONTROL2_PDN_AFE,
AFE440X_CONTROL2_PDN_AFE);
if (ret)
return ret;
ret = regulator_disable(afe->regulator);
if (ret) {
dev_err(dev, "Unable to disable regulator\n");
return ret;
}
return 0;
}
static int __maybe_unused afe4404_resume(struct device *dev)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct afe4404_data *afe = iio_priv(indio_dev);
int ret;
ret = regulator_enable(afe->regulator);
if (ret) {
dev_err(dev, "Unable to enable regulator\n");
return ret;
}
ret = regmap_update_bits(afe->regmap, AFE440X_CONTROL2,
AFE440X_CONTROL2_PDN_AFE, 0);
if (ret)
return ret;
return 0;
}
static SIMPLE_DEV_PM_OPS(afe4404_pm_ops, afe4404_suspend, afe4404_resume);
static int afe4404_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct iio_dev *indio_dev;
struct afe4404_data *afe;
int ret;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*afe));
if (!indio_dev)
return -ENOMEM;
afe = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
afe->dev = &client->dev;
afe->irq = client->irq;
afe->regmap = devm_regmap_init_i2c(client, &afe4404_regmap_config);
if (IS_ERR(afe->regmap)) {
dev_err(afe->dev, "Unable to allocate register map\n");
return PTR_ERR(afe->regmap);
}
afe->regulator = devm_regulator_get(afe->dev, "tx_sup");
if (IS_ERR(afe->regulator)) {
dev_err(afe->dev, "Unable to get regulator\n");
return PTR_ERR(afe->regulator);
}
ret = regulator_enable(afe->regulator);
if (ret) {
dev_err(afe->dev, "Unable to enable regulator\n");
return ret;
}
ret = regmap_write(afe->regmap, AFE440X_CONTROL0,
AFE440X_CONTROL0_SW_RESET);
if (ret) {
dev_err(afe->dev, "Unable to reset device\n");
goto disable_reg;
}
ret = regmap_multi_reg_write(afe->regmap, afe4404_reg_sequences,
ARRAY_SIZE(afe4404_reg_sequences));
if (ret) {
dev_err(afe->dev, "Unable to set register defaults\n");
goto disable_reg;
}
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->dev.parent = afe->dev;
indio_dev->channels = afe4404_channels;
indio_dev->num_channels = ARRAY_SIZE(afe4404_channels);
indio_dev->name = AFE4404_DRIVER_NAME;
indio_dev->info = &afe4404_iio_info;
if (afe->irq > 0) {
afe->trig = devm_iio_trigger_alloc(afe->dev,
"%s-dev%d",
indio_dev->name,
indio_dev->id);
if (!afe->trig) {
dev_err(afe->dev, "Unable to allocate IIO trigger\n");
ret = -ENOMEM;
goto disable_reg;
}
iio_trigger_set_drvdata(afe->trig, indio_dev);
afe->trig->ops = &afe4404_trigger_ops;
afe->trig->dev.parent = afe->dev;
ret = iio_trigger_register(afe->trig);
if (ret) {
dev_err(afe->dev, "Unable to register IIO trigger\n");
goto disable_reg;
}
ret = devm_request_threaded_irq(afe->dev, afe->irq,
iio_trigger_generic_data_rdy_poll,
NULL, IRQF_ONESHOT,
AFE4404_DRIVER_NAME,
afe->trig);
if (ret) {
dev_err(afe->dev, "Unable to request IRQ\n");
goto disable_reg;
}
}
ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
afe4404_trigger_handler, NULL);
if (ret) {
dev_err(afe->dev, "Unable to setup buffer\n");
goto unregister_trigger;
}
ret = iio_device_register(indio_dev);
if (ret) {
dev_err(afe->dev, "Unable to register IIO device\n");
goto unregister_triggered_buffer;
}
return 0;
unregister_triggered_buffer:
iio_triggered_buffer_cleanup(indio_dev);
unregister_trigger:
if (afe->irq > 0)
iio_trigger_unregister(afe->trig);
disable_reg:
regulator_disable(afe->regulator);
return ret;
}
static int afe4404_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct afe4404_data *afe = iio_priv(indio_dev);
int ret;
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
if (afe->irq > 0)
iio_trigger_unregister(afe->trig);
ret = regulator_disable(afe->regulator);
if (ret) {
dev_err(afe->dev, "Unable to disable regulator\n");
return ret;
}
return 0;
}
static const struct i2c_device_id afe4404_ids[] = {
{ "afe4404", 0 },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, afe4404_ids);
static struct i2c_driver afe4404_i2c_driver = {
.driver = {
.name = AFE4404_DRIVER_NAME,
.of_match_table = of_match_ptr(afe4404_of_match),
.pm = &afe4404_pm_ops,
},
.probe = afe4404_probe,
.remove = afe4404_remove,
.id_table = afe4404_ids,
};
module_i2c_driver(afe4404_i2c_driver);
MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
MODULE_DESCRIPTION("TI AFE4404 Heart Rate and Pulse Oximeter");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,191 @@
/*
* AFE440X Heart Rate Monitors and Low-Cost Pulse Oximeters
*
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
* Andrew F. Davis <afd@ti.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.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*/
#ifndef _AFE440X_H
#define _AFE440X_H
/* AFE440X registers */
#define AFE440X_CONTROL0 0x00
#define AFE440X_LED2STC 0x01
#define AFE440X_LED2ENDC 0x02
#define AFE440X_LED1LEDSTC 0x03
#define AFE440X_LED1LEDENDC 0x04
#define AFE440X_ALED2STC 0x05
#define AFE440X_ALED2ENDC 0x06
#define AFE440X_LED1STC 0x07
#define AFE440X_LED1ENDC 0x08
#define AFE440X_LED2LEDSTC 0x09
#define AFE440X_LED2LEDENDC 0x0a
#define AFE440X_ALED1STC 0x0b
#define AFE440X_ALED1ENDC 0x0c
#define AFE440X_LED2CONVST 0x0d
#define AFE440X_LED2CONVEND 0x0e
#define AFE440X_ALED2CONVST 0x0f
#define AFE440X_ALED2CONVEND 0x10
#define AFE440X_LED1CONVST 0x11
#define AFE440X_LED1CONVEND 0x12
#define AFE440X_ALED1CONVST 0x13
#define AFE440X_ALED1CONVEND 0x14
#define AFE440X_ADCRSTSTCT0 0x15
#define AFE440X_ADCRSTENDCT0 0x16
#define AFE440X_ADCRSTSTCT1 0x17
#define AFE440X_ADCRSTENDCT1 0x18
#define AFE440X_ADCRSTSTCT2 0x19
#define AFE440X_ADCRSTENDCT2 0x1a
#define AFE440X_ADCRSTSTCT3 0x1b
#define AFE440X_ADCRSTENDCT3 0x1c
#define AFE440X_PRPCOUNT 0x1d
#define AFE440X_CONTROL1 0x1e
#define AFE440X_LEDCNTRL 0x22
#define AFE440X_CONTROL2 0x23
#define AFE440X_ALARM 0x29
#define AFE440X_LED2VAL 0x2a
#define AFE440X_ALED2VAL 0x2b
#define AFE440X_LED1VAL 0x2c
#define AFE440X_ALED1VAL 0x2d
#define AFE440X_LED2_ALED2VAL 0x2e
#define AFE440X_LED1_ALED1VAL 0x2f
#define AFE440X_CONTROL3 0x31
#define AFE440X_PDNCYCLESTC 0x32
#define AFE440X_PDNCYCLEENDC 0x33
/* CONTROL0 register fields */
#define AFE440X_CONTROL0_REG_READ BIT(0)
#define AFE440X_CONTROL0_TM_COUNT_RST BIT(1)
#define AFE440X_CONTROL0_SW_RESET BIT(3)
/* CONTROL1 register fields */
#define AFE440X_CONTROL1_TIMEREN BIT(8)
/* TIAGAIN register fields */
#define AFE440X_TIAGAIN_ENSEPGAIN_MASK BIT(15)
#define AFE440X_TIAGAIN_ENSEPGAIN_SHIFT 15
/* CONTROL2 register fields */
#define AFE440X_CONTROL2_PDN_AFE BIT(0)
#define AFE440X_CONTROL2_PDN_RX BIT(1)
#define AFE440X_CONTROL2_DYNAMIC4 BIT(3)
#define AFE440X_CONTROL2_DYNAMIC3 BIT(4)
#define AFE440X_CONTROL2_DYNAMIC2 BIT(14)
#define AFE440X_CONTROL2_DYNAMIC1 BIT(20)
/* CONTROL3 register fields */
#define AFE440X_CONTROL3_CLKDIV GENMASK(2, 0)
/* CONTROL0 values */
#define AFE440X_CONTROL0_WRITE 0x0
#define AFE440X_CONTROL0_READ 0x1
struct afe440x_reg_info {
unsigned int reg;
unsigned int offreg;
unsigned int shift;
unsigned int mask;
};
#define AFE440X_REG_INFO(_reg, _offreg, _sm) \
{ \
.reg = _reg, \
.offreg = _offreg, \
.shift = _sm ## _SHIFT, \
.mask = _sm ## _MASK, \
}
#define AFE440X_INTENSITY_CHAN(_index, _name, _mask) \
{ \
.type = IIO_INTENSITY, \
.channel = _index, \
.address = _index, \
.scan_index = _index, \
.scan_type = { \
.sign = 's', \
.realbits = 24, \
.storagebits = 32, \
.endianness = IIO_CPU, \
}, \
.extend_name = _name, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
_mask, \
}
#define AFE440X_CURRENT_CHAN(_index, _name) \
{ \
.type = IIO_CURRENT, \
.channel = _index, \
.address = _index, \
.scan_index = _index, \
.extend_name = _name, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE), \
.output = true, \
}
enum afe440x_reg_type {
SIMPLE,
RESISTANCE,
CAPACITANCE,
};
struct afe440x_val_table {
int integer;
int fract;
};
#define AFE440X_TABLE_ATTR(_name, _table) \
static ssize_t _name ## _show(struct device *dev, \
struct device_attribute *attr, char *buf) \
{ \
ssize_t len = 0; \
int i; \
\
for (i = 0; i < ARRAY_SIZE(_table); i++) \
len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06u ", \
_table[i].integer, \
_table[i].fract); \
\
buf[len - 1] = '\n'; \
\
return len; \
} \
static DEVICE_ATTR_RO(_name)
struct afe440x_attr {
struct device_attribute dev_attr;
unsigned int reg;
unsigned int shift;
unsigned int mask;
enum afe440x_reg_type type;
const struct afe440x_val_table *val_table;
unsigned int table_size;
};
#define to_afe440x_attr(_dev_attr) \
container_of(_dev_attr, struct afe440x_attr, dev_attr)
#define AFE440X_ATTR(_name, _reg, _field, _type, _table, _size) \
struct afe440x_attr afe440x_attr_##_name = { \
.dev_attr = __ATTR(_name, (S_IRUGO | S_IWUSR), \
afe440x_show_register, \
afe440x_store_register), \
.reg = _reg, \
.shift = _field ## _SHIFT, \
.mask = _field ## _MASK, \
.type = _type, \
.val_table = _table, \
.table_size = _size, \
}
#endif /* _AFE440X_H */

View File

@ -13,7 +13,7 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* TODO: allow LED current and pulse length controls via device tree properties
* TODO: enable pulse length controls via device tree properties
*/
#include <linux/module.h>
@ -24,6 +24,7 @@
#include <linux/irq.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
@ -65,6 +66,7 @@
#define MAX30100_REG_SPO2_CONFIG_1600US 0x3
#define MAX30100_REG_LED_CONFIG 0x09
#define MAX30100_REG_LED_CONFIG_LED_MASK 0x0f
#define MAX30100_REG_LED_CONFIG_RED_LED_SHIFT 4
#define MAX30100_REG_LED_CONFIG_24MA 0x07
@ -111,6 +113,12 @@ static const struct regmap_config max30100_regmap_config = {
.volatile_reg = max30100_is_volatile_reg,
};
static const unsigned int max30100_led_current_mapping[] = {
4400, 7600, 11000, 14200, 17400,
20800, 24000, 27100, 30600, 33800,
37000, 40200, 43600, 46800, 50000
};
static const unsigned long max30100_scan_masks[] = {0x3, 0};
static const struct iio_chan_spec max30100_channels[] = {
@ -243,15 +251,76 @@ static irqreturn_t max30100_interrupt_handler(int irq, void *private)
return IRQ_HANDLED;
}
static int max30100_get_current_idx(unsigned int val, int *reg)
{
int idx;
/* LED turned off */
if (val == 0) {
*reg = 0;
return 0;
}
for (idx = 0; idx < ARRAY_SIZE(max30100_led_current_mapping); idx++) {
if (max30100_led_current_mapping[idx] == val) {
*reg = idx + 1;
return 0;
}
}
return -EINVAL;
}
static int max30100_led_init(struct max30100_data *data)
{
struct device *dev = &data->client->dev;
struct device_node *np = dev->of_node;
unsigned int val[2];
int reg, ret;
ret = of_property_read_u32_array(np, "maxim,led-current-microamp",
(unsigned int *) &val, 2);
if (ret) {
/* Default to 24 mA RED LED, 50 mA IR LED */
reg = (MAX30100_REG_LED_CONFIG_24MA <<
MAX30100_REG_LED_CONFIG_RED_LED_SHIFT) |
MAX30100_REG_LED_CONFIG_50MA;
dev_warn(dev, "no led-current-microamp set");
return regmap_write(data->regmap, MAX30100_REG_LED_CONFIG, reg);
}
/* RED LED current */
ret = max30100_get_current_idx(val[0], &reg);
if (ret) {
dev_err(dev, "invalid RED current setting %d", val[0]);
return ret;
}
ret = regmap_update_bits(data->regmap, MAX30100_REG_LED_CONFIG,
MAX30100_REG_LED_CONFIG_LED_MASK <<
MAX30100_REG_LED_CONFIG_RED_LED_SHIFT,
reg << MAX30100_REG_LED_CONFIG_RED_LED_SHIFT);
if (ret)
return ret;
/* IR LED current */
ret = max30100_get_current_idx(val[1], &reg);
if (ret) {
dev_err(dev, "invalid IR current setting %d", val[1]);
return ret;
}
return regmap_update_bits(data->regmap, MAX30100_REG_LED_CONFIG,
MAX30100_REG_LED_CONFIG_LED_MASK, reg);
}
static int max30100_chip_init(struct max30100_data *data)
{
int ret;
/* RED IR LED = 24mA, IR LED = 50mA */
ret = regmap_write(data->regmap, MAX30100_REG_LED_CONFIG,
(MAX30100_REG_LED_CONFIG_24MA <<
MAX30100_REG_LED_CONFIG_RED_LED_SHIFT) |
MAX30100_REG_LED_CONFIG_50MA);
/* setup LED current settings */
ret = max30100_led_init(data);
if (ret)
return ret;

View File

@ -43,14 +43,16 @@ config SI7005
humidity and temperature sensor.
To compile this driver as a module, choose M here: the module
will be called si7005.
will be called si7005. This driver also
supports Hoperf TH02 Humidity and Temperature Sensor.
config SI7020
tristate "Si7013/20/21 Relative Humidity and Temperature Sensors"
depends on I2C
help
Say yes here to build support for the Silicon Labs Si7013/20/21
Relative Humidity and Temperature Sensors.
Relative Humidity and Temperature Sensors. This driver also
supports Hoperf TH06 Humidity and Temperature Sensor.
To compile this driver as a module, choose M here: the module
will be called si7020.

View File

@ -50,12 +50,32 @@
#define DHT11_EDGES_PER_READ (2 * DHT11_BITS_PER_READ + \
DHT11_EDGES_PREAMBLE + 1)
/* Data transmission timing (nano seconds) */
/*
* Data transmission timing:
* Data bits are encoded as pulse length (high time) on the data line.
* 0-bit: 22-30uS -- typically 26uS (AM2302)
* 1-bit: 68-75uS -- typically 70uS (AM2302)
* The acutal timings also depend on the properties of the cable, with
* longer cables typically making pulses shorter.
*
* Our decoding depends on the time resolution of the system:
* timeres > 34uS ... don't know what a 1-tick pulse is
* 34uS > timeres > 30uS ... no problem (30kHz and 32kHz clocks)
* 30uS > timeres > 23uS ... don't know what a 2-tick pulse is
* timeres < 23uS ... no problem
*
* Luckily clocks in the 33-44kHz range are quite uncommon, so we can
* support most systems if the threshold for decoding a pulse as 1-bit
* is chosen carefully. If somebody really wants to support clocks around
* 40kHz, where this driver is most unreliable, there are two options.
* a) select an implementation using busy loop polling on those systems
* b) use the checksum to do some probabilistic decoding
*/
#define DHT11_START_TRANSMISSION 18 /* ms */
#define DHT11_SENSOR_RESPONSE 80000
#define DHT11_START_BIT 50000
#define DHT11_DATA_BIT_LOW 27000
#define DHT11_DATA_BIT_HIGH 70000
#define DHT11_MIN_TIMERES 34000 /* ns */
#define DHT11_THRESHOLD 49000 /* ns */
#define DHT11_AMBIG_LOW 23000 /* ns */
#define DHT11_AMBIG_HIGH 30000 /* ns */
struct dht11 {
struct device *dev;
@ -76,43 +96,39 @@ struct dht11 {
struct {s64 ts; int value; } edges[DHT11_EDGES_PER_READ];
};
static unsigned char dht11_decode_byte(int *timing, int threshold)
static unsigned char dht11_decode_byte(char *bits)
{
unsigned char ret = 0;
int i;
for (i = 0; i < 8; ++i) {
ret <<= 1;
if (timing[i] >= threshold)
if (bits[i])
++ret;
}
return ret;
}
static int dht11_decode(struct dht11 *dht11, int offset, int timeres)
static int dht11_decode(struct dht11 *dht11, int offset)
{
int i, t, timing[DHT11_BITS_PER_READ], threshold;
int i, t;
char bits[DHT11_BITS_PER_READ];
unsigned char temp_int, temp_dec, hum_int, hum_dec, checksum;
threshold = DHT11_DATA_BIT_HIGH / timeres;
if (DHT11_DATA_BIT_LOW / timeres + 1 >= threshold)
pr_err("dht11: WARNING: decoding ambiguous\n");
/* scale down with timeres and check validity */
for (i = 0; i < DHT11_BITS_PER_READ; ++i) {
t = dht11->edges[offset + 2 * i + 2].ts -
dht11->edges[offset + 2 * i + 1].ts;
if (!dht11->edges[offset + 2 * i + 1].value)
return -EIO; /* lost synchronisation */
timing[i] = t / timeres;
bits[i] = t > DHT11_THRESHOLD;
}
hum_int = dht11_decode_byte(timing, threshold);
hum_dec = dht11_decode_byte(&timing[8], threshold);
temp_int = dht11_decode_byte(&timing[16], threshold);
temp_dec = dht11_decode_byte(&timing[24], threshold);
checksum = dht11_decode_byte(&timing[32], threshold);
hum_int = dht11_decode_byte(bits);
hum_dec = dht11_decode_byte(&bits[8]);
temp_int = dht11_decode_byte(&bits[16]);
temp_dec = dht11_decode_byte(&bits[24]);
checksum = dht11_decode_byte(&bits[32]);
if (((hum_int + hum_dec + temp_int + temp_dec) & 0xff) != checksum)
return -EIO;
@ -161,12 +177,12 @@ static int dht11_read_raw(struct iio_dev *iio_dev,
int *val, int *val2, long m)
{
struct dht11 *dht11 = iio_priv(iio_dev);
int ret, timeres;
int ret, timeres, offset;
mutex_lock(&dht11->lock);
if (dht11->timestamp + DHT11_DATA_VALID_TIME < ktime_get_boot_ns()) {
timeres = ktime_get_resolution_ns();
if (DHT11_DATA_BIT_HIGH < 2 * timeres) {
if (timeres > DHT11_MIN_TIMERES) {
dev_err(dht11->dev, "timeresolution %dns too low\n",
timeres);
/* In theory a better clock could become available
@ -176,6 +192,10 @@ static int dht11_read_raw(struct iio_dev *iio_dev,
ret = -EAGAIN;
goto err;
}
if (timeres > DHT11_AMBIG_LOW && timeres < DHT11_AMBIG_HIGH)
dev_warn(dht11->dev,
"timeresolution: %dns - decoding ambiguous\n",
timeres);
reinit_completion(&dht11->completion);
@ -208,11 +228,14 @@ static int dht11_read_raw(struct iio_dev *iio_dev,
if (ret < 0)
goto err;
ret = dht11_decode(dht11,
dht11->num_edges == DHT11_EDGES_PER_READ ?
DHT11_EDGES_PREAMBLE :
DHT11_EDGES_PREAMBLE - 2,
timeres);
offset = DHT11_EDGES_PREAMBLE +
dht11->num_edges - DHT11_EDGES_PER_READ;
for (; offset >= 0; --offset) {
ret = dht11_decode(dht11, offset);
if (!ret)
break;
}
if (ret)
goto err;
}

View File

@ -274,7 +274,7 @@ static int hdc100x_probe(struct i2c_client *client,
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE))
return -ENODEV;
return -EOPNOTSUPP;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)

View File

@ -192,7 +192,7 @@ static int htu21_probe(struct i2c_client *client,
I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
dev_err(&client->dev,
"Adapter does not support some i2c transaction\n");
return -ENODEV;
return -EOPNOTSUPP;
}
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*dev_data));

View File

@ -135,7 +135,7 @@ static int si7005_probe(struct i2c_client *client,
int ret;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
return -ENODEV;
return -EOPNOTSUPP;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
@ -170,6 +170,7 @@ static int si7005_probe(struct i2c_client *client,
static const struct i2c_device_id si7005_id[] = {
{ "si7005", 0 },
{ "th02", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, si7005_id);

View File

@ -121,7 +121,7 @@ static int si7020_probe(struct i2c_client *client,
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_WRITE_BYTE |
I2C_FUNC_SMBUS_READ_WORD_DATA))
return -ENODEV;
return -EOPNOTSUPP;
/* Reset device, loads default settings. */
ret = i2c_smbus_write_byte(client, SI7020CMD_RESET);
@ -149,6 +149,7 @@ static int si7020_probe(struct i2c_client *client,
static const struct i2c_device_id si7020_id[] = {
{ "si7020", 0 },
{ "th06", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, si7020_id);

View File

@ -3,15 +3,31 @@
#
config INV_MPU6050_IIO
tristate "Invensense MPU6050 devices"
depends on I2C && SYSFS
depends on I2C_MUX
tristate
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
config INV_MPU6050_I2C
tristate "Invensense MPU6050 devices (I2C)"
depends on I2C
select INV_MPU6050_IIO
select I2C_MUX
select REGMAP_I2C
help
This driver supports the Invensense MPU6050 devices.
This driver can also support MPU6500 in MPU6050 compatibility mode
and also in MPU6500 mode with some limitations.
It is a gyroscope/accelerometer combo device.
This driver can be built as a module. The module will be called
inv-mpu6050.
inv-mpu6050-i2c.
config INV_MPU6050_SPI
tristate "Invensense MPU6050 devices (SPI)"
depends on SPI_MASTER
select INV_MPU6050_IIO
select REGMAP_SPI
help
This driver supports the Invensense MPU6050 devices.
It is a gyroscope/accelerometer combo device.
This driver can be built as a module. The module will be called
inv-mpu6050-spi.

View File

@ -3,4 +3,10 @@
#
obj-$(CONFIG_INV_MPU6050_IIO) += inv-mpu6050.o
inv-mpu6050-objs := inv_mpu_core.o inv_mpu_ring.o inv_mpu_trigger.o inv_mpu_acpi.o
inv-mpu6050-objs := inv_mpu_core.o inv_mpu_ring.o inv_mpu_trigger.o
obj-$(CONFIG_INV_MPU6050_I2C) += inv-mpu6050-i2c.o
inv-mpu6050-i2c-objs := inv_mpu_i2c.o inv_mpu_acpi.o
obj-$(CONFIG_INV_MPU6050_SPI) += inv-mpu6050-spi.o
inv-mpu6050-spi-objs := inv_mpu_spi.o

View File

@ -66,11 +66,11 @@ static int asus_acpi_get_sensor_info(struct acpi_device *adev,
union acpi_object *elem;
int j;
elem = &(cpm->package.elements[i]);
elem = &cpm->package.elements[i];
for (j = 0; j < elem->package.count; ++j) {
union acpi_object *sub_elem;
sub_elem = &(elem->package.elements[j]);
sub_elem = &elem->package.elements[j];
if (sub_elem->type == ACPI_TYPE_STRING)
strlcpy(info->type, sub_elem->string.pointer,
sizeof(info->type));
@ -139,22 +139,23 @@ static int inv_mpu_process_acpi_config(struct i2c_client *client,
return 0;
}
int inv_mpu_acpi_create_mux_client(struct inv_mpu6050_state *st)
int inv_mpu_acpi_create_mux_client(struct i2c_client *client)
{
struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(&client->dev));
st->mux_client = NULL;
if (ACPI_HANDLE(&st->client->dev)) {
if (ACPI_HANDLE(&client->dev)) {
struct i2c_board_info info;
struct acpi_device *adev;
int ret = -1;
adev = ACPI_COMPANION(&st->client->dev);
adev = ACPI_COMPANION(&client->dev);
memset(&info, 0, sizeof(info));
dmi_check_system(inv_mpu_dev_list);
switch (matched_product_name) {
case INV_MPU_ASUS_T100TA:
ret = asus_acpi_get_sensor_info(adev, st->client,
ret = asus_acpi_get_sensor_info(adev, client,
&info);
break;
/* Add more matched product processing here */
@ -166,7 +167,7 @@ int inv_mpu_acpi_create_mux_client(struct inv_mpu6050_state *st)
/* No matching DMI, so create device on INV6XX type */
unsigned short primary, secondary;
ret = inv_mpu_process_acpi_config(st->client, &primary,
ret = inv_mpu_process_acpi_config(client, &primary,
&secondary);
if (!ret && secondary) {
char *name;
@ -185,14 +186,15 @@ int inv_mpu_acpi_create_mux_client(struct inv_mpu6050_state *st)
st->mux_client = i2c_new_device(st->mux_adapter, &info);
if (!st->mux_client)
return -ENODEV;
}
return 0;
}
void inv_mpu_acpi_delete_mux_client(struct inv_mpu6050_state *st)
void inv_mpu_acpi_delete_mux_client(struct i2c_client *client)
{
struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(&client->dev));
if (st->mux_client)
i2c_unregister_device(st->mux_client);
}
@ -200,12 +202,12 @@ void inv_mpu_acpi_delete_mux_client(struct inv_mpu6050_state *st)
#include "inv_mpu_iio.h"
int inv_mpu_acpi_create_mux_client(struct inv_mpu6050_state *st)
int inv_mpu_acpi_create_mux_client(struct i2c_client *client)
{
return 0;
}
void inv_mpu_acpi_delete_mux_client(struct inv_mpu6050_state *st)
void inv_mpu_acpi_delete_mux_client(struct i2c_client *client)
{
}
#endif

View File

@ -39,6 +39,26 @@ static const int gyro_scale_6050[] = {133090, 266181, 532362, 1064724};
*/
static const int accel_scale[] = {598, 1196, 2392, 4785};
static const struct inv_mpu6050_reg_map reg_set_6500 = {
.sample_rate_div = INV_MPU6050_REG_SAMPLE_RATE_DIV,
.lpf = INV_MPU6050_REG_CONFIG,
.user_ctrl = INV_MPU6050_REG_USER_CTRL,
.fifo_en = INV_MPU6050_REG_FIFO_EN,
.gyro_config = INV_MPU6050_REG_GYRO_CONFIG,
.accl_config = INV_MPU6050_REG_ACCEL_CONFIG,
.fifo_count_h = INV_MPU6050_REG_FIFO_COUNT_H,
.fifo_r_w = INV_MPU6050_REG_FIFO_R_W,
.raw_gyro = INV_MPU6050_REG_RAW_GYRO,
.raw_accl = INV_MPU6050_REG_RAW_ACCEL,
.temperature = INV_MPU6050_REG_TEMPERATURE,
.int_enable = INV_MPU6050_REG_INT_ENABLE,
.pwr_mgmt_1 = INV_MPU6050_REG_PWR_MGMT_1,
.pwr_mgmt_2 = INV_MPU6050_REG_PWR_MGMT_2,
.int_pin_cfg = INV_MPU6050_REG_INT_PIN_CFG,
.accl_offset = INV_MPU6500_REG_ACCEL_OFFSET,
.gyro_offset = INV_MPU6050_REG_GYRO_OFFSET,
};
static const struct inv_mpu6050_reg_map reg_set_6050 = {
.sample_rate_div = INV_MPU6050_REG_SAMPLE_RATE_DIV,
.lpf = INV_MPU6050_REG_CONFIG,
@ -55,6 +75,8 @@ static const struct inv_mpu6050_reg_map reg_set_6050 = {
.pwr_mgmt_1 = INV_MPU6050_REG_PWR_MGMT_1,
.pwr_mgmt_2 = INV_MPU6050_REG_PWR_MGMT_2,
.int_pin_cfg = INV_MPU6050_REG_INT_PIN_CFG,
.accl_offset = INV_MPU6050_REG_ACCEL_OFFSET,
.gyro_offset = INV_MPU6050_REG_GYRO_OFFSET,
};
static const struct inv_mpu6050_chip_config chip_config_6050 = {
@ -66,7 +88,13 @@ static const struct inv_mpu6050_chip_config chip_config_6050 = {
.accl_fs = INV_MPU6050_FS_02G,
};
static const struct inv_mpu6050_hw hw_info[INV_NUM_PARTS] = {
static const struct inv_mpu6050_hw hw_info[] = {
{
.num_reg = 117,
.name = "MPU6500",
.reg = &reg_set_6500,
.config = &chip_config_6050,
},
{
.num_reg = 117,
.name = "MPU6050",
@ -75,134 +103,53 @@ static const struct inv_mpu6050_hw hw_info[INV_NUM_PARTS] = {
},
};
int inv_mpu6050_write_reg(struct inv_mpu6050_state *st, int reg, u8 d)
{
return i2c_smbus_write_i2c_block_data(st->client, reg, 1, &d);
}
/*
* The i2c read/write needs to happen in unlocked mode. As the parent
* adapter is common. If we use locked versions, it will fail as
* the mux adapter will lock the parent i2c adapter, while calling
* select/deselect functions.
*/
static int inv_mpu6050_write_reg_unlocked(struct inv_mpu6050_state *st,
u8 reg, u8 d)
{
int ret;
u8 buf[2];
struct i2c_msg msg[1] = {
{
.addr = st->client->addr,
.flags = 0,
.len = sizeof(buf),
.buf = buf,
}
};
buf[0] = reg;
buf[1] = d;
ret = __i2c_transfer(st->client->adapter, msg, 1);
if (ret != 1)
return ret;
return 0;
}
static int inv_mpu6050_select_bypass(struct i2c_adapter *adap, void *mux_priv,
u32 chan_id)
{
struct iio_dev *indio_dev = mux_priv;
struct inv_mpu6050_state *st = iio_priv(indio_dev);
int ret = 0;
/* Use the same mutex which was used everywhere to protect power-op */
mutex_lock(&indio_dev->mlock);
if (!st->powerup_count) {
ret = inv_mpu6050_write_reg_unlocked(st, st->reg->pwr_mgmt_1,
0);
if (ret)
goto write_error;
msleep(INV_MPU6050_REG_UP_TIME);
}
if (!ret) {
st->powerup_count++;
ret = inv_mpu6050_write_reg_unlocked(st, st->reg->int_pin_cfg,
st->client->irq |
INV_MPU6050_BIT_BYPASS_EN);
}
write_error:
mutex_unlock(&indio_dev->mlock);
return ret;
}
static int inv_mpu6050_deselect_bypass(struct i2c_adapter *adap,
void *mux_priv, u32 chan_id)
{
struct iio_dev *indio_dev = mux_priv;
struct inv_mpu6050_state *st = iio_priv(indio_dev);
mutex_lock(&indio_dev->mlock);
/* It doesn't really mattter, if any of the calls fails */
inv_mpu6050_write_reg_unlocked(st, st->reg->int_pin_cfg,
st->client->irq);
st->powerup_count--;
if (!st->powerup_count)
inv_mpu6050_write_reg_unlocked(st, st->reg->pwr_mgmt_1,
INV_MPU6050_BIT_SLEEP);
mutex_unlock(&indio_dev->mlock);
return 0;
}
int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask)
{
u8 d, mgmt_1;
unsigned int d, mgmt_1;
int result;
/* switch clock needs to be careful. Only when gyro is on, can
clock source be switched to gyro. Otherwise, it must be set to
internal clock */
if (INV_MPU6050_BIT_PWR_GYRO_STBY == mask) {
result = i2c_smbus_read_i2c_block_data(st->client,
st->reg->pwr_mgmt_1, 1, &mgmt_1);
if (result != 1)
/*
* switch clock needs to be careful. Only when gyro is on, can
* clock source be switched to gyro. Otherwise, it must be set to
* internal clock
*/
if (mask == INV_MPU6050_BIT_PWR_GYRO_STBY) {
result = regmap_read(st->map, st->reg->pwr_mgmt_1, &mgmt_1);
if (result)
return result;
mgmt_1 &= ~INV_MPU6050_BIT_CLK_MASK;
}
if ((INV_MPU6050_BIT_PWR_GYRO_STBY == mask) && (!en)) {
/* turning off gyro requires switch to internal clock first.
Then turn off gyro engine */
if ((mask == INV_MPU6050_BIT_PWR_GYRO_STBY) && (!en)) {
/*
* turning off gyro requires switch to internal clock first.
* Then turn off gyro engine
*/
mgmt_1 |= INV_CLK_INTERNAL;
result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1, mgmt_1);
result = regmap_write(st->map, st->reg->pwr_mgmt_1, mgmt_1);
if (result)
return result;
}
result = i2c_smbus_read_i2c_block_data(st->client,
st->reg->pwr_mgmt_2, 1, &d);
if (result != 1)
result = regmap_read(st->map, st->reg->pwr_mgmt_2, &d);
if (result)
return result;
if (en)
d &= ~mask;
else
d |= mask;
result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_2, d);
result = regmap_write(st->map, st->reg->pwr_mgmt_2, d);
if (result)
return result;
if (en) {
/* Wait for output stabilize */
msleep(INV_MPU6050_TEMP_UP_TIME);
if (INV_MPU6050_BIT_PWR_GYRO_STBY == mask) {
if (mask == INV_MPU6050_BIT_PWR_GYRO_STBY) {
/* switch internal clock to PLL */
mgmt_1 |= INV_CLK_PLL;
result = inv_mpu6050_write_reg(st,
st->reg->pwr_mgmt_1, mgmt_1);
result = regmap_write(st->map,
st->reg->pwr_mgmt_1, mgmt_1);
if (result)
return result;
}
@ -218,25 +165,26 @@ int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on)
if (power_on) {
/* Already under indio-dev->mlock mutex */
if (!st->powerup_count)
result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1,
0);
result = regmap_write(st->map, st->reg->pwr_mgmt_1, 0);
if (!result)
st->powerup_count++;
} else {
st->powerup_count--;
if (!st->powerup_count)
result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1,
INV_MPU6050_BIT_SLEEP);
result = regmap_write(st->map, st->reg->pwr_mgmt_1,
INV_MPU6050_BIT_SLEEP);
}
if (result)
return result;
if (power_on)
msleep(INV_MPU6050_REG_UP_TIME);
usleep_range(INV_MPU6050_REG_UP_TIME_MIN,
INV_MPU6050_REG_UP_TIME_MAX);
return 0;
}
EXPORT_SYMBOL_GPL(inv_mpu6050_set_power_itg);
/**
* inv_mpu6050_init_config() - Initialize hardware, disable FIFO.
@ -257,59 +205,73 @@ static int inv_mpu6050_init_config(struct iio_dev *indio_dev)
if (result)
return result;
d = (INV_MPU6050_FSR_2000DPS << INV_MPU6050_GYRO_CONFIG_FSR_SHIFT);
result = inv_mpu6050_write_reg(st, st->reg->gyro_config, d);
result = regmap_write(st->map, st->reg->gyro_config, d);
if (result)
return result;
d = INV_MPU6050_FILTER_20HZ;
result = inv_mpu6050_write_reg(st, st->reg->lpf, d);
result = regmap_write(st->map, st->reg->lpf, d);
if (result)
return result;
d = INV_MPU6050_ONE_K_HZ / INV_MPU6050_INIT_FIFO_RATE - 1;
result = inv_mpu6050_write_reg(st, st->reg->sample_rate_div, d);
result = regmap_write(st->map, st->reg->sample_rate_div, d);
if (result)
return result;
d = (INV_MPU6050_FS_02G << INV_MPU6050_ACCL_CONFIG_FSR_SHIFT);
result = inv_mpu6050_write_reg(st, st->reg->accl_config, d);
result = regmap_write(st->map, st->reg->accl_config, d);
if (result)
return result;
memcpy(&st->chip_config, hw_info[st->chip_type].config,
sizeof(struct inv_mpu6050_chip_config));
sizeof(struct inv_mpu6050_chip_config));
result = inv_mpu6050_set_power_itg(st, false);
return result;
}
static int inv_mpu6050_sensor_set(struct inv_mpu6050_state *st, int reg,
int axis, int val)
{
int ind, result;
__be16 d = cpu_to_be16(val);
ind = (axis - IIO_MOD_X) * 2;
result = regmap_bulk_write(st->map, reg + ind, (u8 *)&d, 2);
if (result)
return -EINVAL;
return 0;
}
static int inv_mpu6050_sensor_show(struct inv_mpu6050_state *st, int reg,
int axis, int *val)
int axis, int *val)
{
int ind, result;
__be16 d;
ind = (axis - IIO_MOD_X) * 2;
result = i2c_smbus_read_i2c_block_data(st->client, reg + ind, 2,
(u8 *)&d);
if (result != 2)
result = regmap_bulk_read(st->map, reg + ind, (u8 *)&d, 2);
if (result)
return -EINVAL;
*val = (short)be16_to_cpup(&d);
return IIO_VAL_INT;
}
static int inv_mpu6050_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val,
int *val2,
long mask) {
static int
inv_mpu6050_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct inv_mpu6050_state *st = iio_priv(indio_dev);
int ret = 0;
switch (mask) {
case IIO_CHAN_INFO_RAW:
{
int ret, result;
int result;
ret = IIO_VAL_INT;
result = 0;
@ -323,16 +285,16 @@ static int inv_mpu6050_read_raw(struct iio_dev *indio_dev,
switch (chan->type) {
case IIO_ANGL_VEL:
if (!st->chip_config.gyro_fifo_enable ||
!st->chip_config.enable) {
!st->chip_config.enable) {
result = inv_mpu6050_switch_engine(st, true,
INV_MPU6050_BIT_PWR_GYRO_STBY);
if (result)
goto error_read_raw;
}
ret = inv_mpu6050_sensor_show(st, st->reg->raw_gyro,
chan->channel2, val);
ret = inv_mpu6050_sensor_show(st, st->reg->raw_gyro,
chan->channel2, val);
if (!st->chip_config.gyro_fifo_enable ||
!st->chip_config.enable) {
!st->chip_config.enable) {
result = inv_mpu6050_switch_engine(st, false,
INV_MPU6050_BIT_PWR_GYRO_STBY);
if (result)
@ -341,16 +303,16 @@ static int inv_mpu6050_read_raw(struct iio_dev *indio_dev,
break;
case IIO_ACCEL:
if (!st->chip_config.accl_fifo_enable ||
!st->chip_config.enable) {
!st->chip_config.enable) {
result = inv_mpu6050_switch_engine(st, true,
INV_MPU6050_BIT_PWR_ACCL_STBY);
if (result)
goto error_read_raw;
}
ret = inv_mpu6050_sensor_show(st, st->reg->raw_accl,
chan->channel2, val);
chan->channel2, val);
if (!st->chip_config.accl_fifo_enable ||
!st->chip_config.enable) {
!st->chip_config.enable) {
result = inv_mpu6050_switch_engine(st, false,
INV_MPU6050_BIT_PWR_ACCL_STBY);
if (result)
@ -360,8 +322,8 @@ static int inv_mpu6050_read_raw(struct iio_dev *indio_dev,
case IIO_TEMP:
/* wait for stablization */
msleep(INV_MPU6050_SENSOR_UP_TIME);
inv_mpu6050_sensor_show(st, st->reg->temperature,
IIO_MOD_X, val);
ret = inv_mpu6050_sensor_show(st, st->reg->temperature,
IIO_MOD_X, val);
break;
default:
ret = -EINVAL;
@ -405,6 +367,20 @@ error_read_raw:
default:
return -EINVAL;
}
case IIO_CHAN_INFO_CALIBBIAS:
switch (chan->type) {
case IIO_ANGL_VEL:
ret = inv_mpu6050_sensor_show(st, st->reg->gyro_offset,
chan->channel2, val);
return IIO_VAL_INT;
case IIO_ACCEL:
ret = inv_mpu6050_sensor_show(st, st->reg->accl_offset,
chan->channel2, val);
return IIO_VAL_INT;
default:
return -EINVAL;
}
default:
return -EINVAL;
}
@ -418,8 +394,7 @@ static int inv_mpu6050_write_gyro_scale(struct inv_mpu6050_state *st, int val)
for (i = 0; i < ARRAY_SIZE(gyro_scale_6050); ++i) {
if (gyro_scale_6050[i] == val) {
d = (i << INV_MPU6050_GYRO_CONFIG_FSR_SHIFT);
result = inv_mpu6050_write_reg(st,
st->reg->gyro_config, d);
result = regmap_write(st->map, st->reg->gyro_config, d);
if (result)
return result;
@ -448,6 +423,7 @@ static int inv_write_raw_get_fmt(struct iio_dev *indio_dev,
return -EINVAL;
}
static int inv_mpu6050_write_accel_scale(struct inv_mpu6050_state *st, int val)
{
int result, i;
@ -456,8 +432,7 @@ static int inv_mpu6050_write_accel_scale(struct inv_mpu6050_state *st, int val)
for (i = 0; i < ARRAY_SIZE(accel_scale); ++i) {
if (accel_scale[i] == val) {
d = (i << INV_MPU6050_ACCL_CONFIG_FSR_SHIFT);
result = inv_mpu6050_write_reg(st,
st->reg->accl_config, d);
result = regmap_write(st->map, st->reg->accl_config, d);
if (result)
return result;
@ -470,16 +445,17 @@ static int inv_mpu6050_write_accel_scale(struct inv_mpu6050_state *st, int val)
}
static int inv_mpu6050_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val,
int val2,
long mask) {
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct inv_mpu6050_state *st = iio_priv(indio_dev);
int result;
mutex_lock(&indio_dev->mlock);
/* we should only update scale when the chip is disabled, i.e.,
not running */
/*
* we should only update scale when the chip is disabled, i.e.
* not running
*/
if (st->chip_config.enable) {
result = -EBUSY;
goto error_write_raw;
@ -502,6 +478,21 @@ static int inv_mpu6050_write_raw(struct iio_dev *indio_dev,
break;
}
break;
case IIO_CHAN_INFO_CALIBBIAS:
switch (chan->type) {
case IIO_ANGL_VEL:
result = inv_mpu6050_sensor_set(st,
st->reg->gyro_offset,
chan->channel2, val);
break;
case IIO_ACCEL:
result = inv_mpu6050_sensor_set(st,
st->reg->accl_offset,
chan->channel2, val);
break;
default:
result = -EINVAL;
}
default:
result = -EINVAL;
break;
@ -537,7 +528,7 @@ static int inv_mpu6050_set_lpf(struct inv_mpu6050_state *st, int rate)
while ((h < hz[i]) && (i < ARRAY_SIZE(d) - 1))
i++;
data = d[i];
result = inv_mpu6050_write_reg(st, st->reg->lpf, data);
result = regmap_write(st->map, st->reg->lpf, data);
if (result)
return result;
st->chip_config.lpf = data;
@ -548,8 +539,9 @@ static int inv_mpu6050_set_lpf(struct inv_mpu6050_state *st, int rate)
/**
* inv_mpu6050_fifo_rate_store() - Set fifo rate.
*/
static ssize_t inv_mpu6050_fifo_rate_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
static ssize_t
inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
s32 fifo_rate;
u8 d;
@ -560,7 +552,7 @@ static ssize_t inv_mpu6050_fifo_rate_store(struct device *dev,
if (kstrtoint(buf, 10, &fifo_rate))
return -EINVAL;
if (fifo_rate < INV_MPU6050_MIN_FIFO_RATE ||
fifo_rate > INV_MPU6050_MAX_FIFO_RATE)
fifo_rate > INV_MPU6050_MAX_FIFO_RATE)
return -EINVAL;
if (fifo_rate == st->chip_config.fifo_rate)
return count;
@ -575,7 +567,7 @@ static ssize_t inv_mpu6050_fifo_rate_store(struct device *dev,
goto fifo_rate_fail;
d = INV_MPU6050_ONE_K_HZ / fifo_rate - 1;
result = inv_mpu6050_write_reg(st, st->reg->sample_rate_div, d);
result = regmap_write(st->map, st->reg->sample_rate_div, d);
if (result)
goto fifo_rate_fail;
st->chip_config.fifo_rate = fifo_rate;
@ -596,8 +588,9 @@ fifo_rate_fail:
/**
* inv_fifo_rate_show() - Get the current sampling rate.
*/
static ssize_t inv_fifo_rate_show(struct device *dev,
struct device_attribute *attr, char *buf)
static ssize_t
inv_fifo_rate_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct inv_mpu6050_state *st = iio_priv(dev_to_iio_dev(dev));
@ -608,16 +601,18 @@ static ssize_t inv_fifo_rate_show(struct device *dev,
* inv_attr_show() - calling this function will show current
* parameters.
*/
static ssize_t inv_attr_show(struct device *dev,
struct device_attribute *attr, char *buf)
static ssize_t inv_attr_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct inv_mpu6050_state *st = iio_priv(dev_to_iio_dev(dev));
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
s8 *m;
switch (this_attr->address) {
/* In MPU6050, the two matrix are the same because gyro and accel
are integrated in one chip */
/*
* In MPU6050, the two matrix are the same because gyro and accel
* are integrated in one chip
*/
case ATTR_GYRO_MATRIX:
case ATTR_ACCL_MATRIX:
m = st->plat_data.orientation;
@ -654,14 +649,15 @@ static int inv_mpu6050_validate_trigger(struct iio_dev *indio_dev,
.type = _type, \
.modified = 1, \
.channel2 = _channel2, \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_CALIBBIAS), \
.scan_index = _index, \
.scan_type = { \
.sign = 's', \
.realbits = 16, \
.storagebits = 16, \
.shift = 0 , \
.shift = 0, \
.endianness = IIO_BE, \
}, \
}
@ -674,7 +670,7 @@ static const struct iio_chan_spec inv_mpu_channels[] = {
*/
{
.type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
| BIT(IIO_CHAN_INFO_OFFSET)
| BIT(IIO_CHAN_INFO_SCALE),
.scan_index = -1,
@ -727,25 +723,25 @@ static const struct iio_info mpu_info = {
/**
* inv_check_and_setup_chip() - check and setup chip.
*/
static int inv_check_and_setup_chip(struct inv_mpu6050_state *st,
const struct i2c_device_id *id)
static int inv_check_and_setup_chip(struct inv_mpu6050_state *st)
{
int result;
st->chip_type = INV_MPU6050;
st->hw = &hw_info[st->chip_type];
st->reg = hw_info[st->chip_type].reg;
/* reset to make sure previous state are not there */
result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1,
INV_MPU6050_BIT_H_RESET);
result = regmap_write(st->map, st->reg->pwr_mgmt_1,
INV_MPU6050_BIT_H_RESET);
if (result)
return result;
msleep(INV_MPU6050_POWER_UP_TIME);
/* toggle power state. After reset, the sleep bit could be on
or off depending on the OTP settings. Toggling power would
make it in a definite state as well as making the hardware
state align with the software state */
/*
* toggle power state. After reset, the sleep bit could be on
* or off depending on the OTP settings. Toggling power would
* make it in a definite state as well as making the hardware
* state align with the software state
*/
result = inv_mpu6050_set_power_itg(st, false);
if (result)
return result;
@ -754,65 +750,59 @@ static int inv_check_and_setup_chip(struct inv_mpu6050_state *st,
return result;
result = inv_mpu6050_switch_engine(st, false,
INV_MPU6050_BIT_PWR_ACCL_STBY);
INV_MPU6050_BIT_PWR_ACCL_STBY);
if (result)
return result;
result = inv_mpu6050_switch_engine(st, false,
INV_MPU6050_BIT_PWR_GYRO_STBY);
INV_MPU6050_BIT_PWR_GYRO_STBY);
if (result)
return result;
return 0;
}
/**
* inv_mpu_probe() - probe function.
* @client: i2c client.
* @id: i2c device id.
*
* Returns 0 on success, a negative error code otherwise.
*/
static int inv_mpu_probe(struct i2c_client *client,
const struct i2c_device_id *id)
int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
int (*inv_mpu_bus_setup)(struct iio_dev *), int chip_type)
{
struct inv_mpu6050_state *st;
struct iio_dev *indio_dev;
struct inv_mpu6050_platform_data *pdata;
struct device *dev = regmap_get_device(regmap);
int result;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_I2C_BLOCK))
return -ENOSYS;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
st = iio_priv(indio_dev);
st->client = client;
st->chip_type = chip_type;
st->powerup_count = 0;
pdata = dev_get_platdata(&client->dev);
st->irq = irq;
st->map = regmap;
pdata = dev_get_platdata(dev);
if (pdata)
st->plat_data = *pdata;
/* power is turned on inside check chip type*/
result = inv_check_and_setup_chip(st, id);
result = inv_check_and_setup_chip(st);
if (result)
return result;
if (inv_mpu_bus_setup)
inv_mpu_bus_setup(indio_dev);
result = inv_mpu6050_init_config(indio_dev);
if (result) {
dev_err(&client->dev,
"Could not initialize device.\n");
dev_err(dev, "Could not initialize device.\n");
return result;
}
i2c_set_clientdata(client, indio_dev);
indio_dev->dev.parent = &client->dev;
/* id will be NULL when enumerated via ACPI */
if (id)
indio_dev->name = (char *)id->name;
dev_set_drvdata(dev, indio_dev);
indio_dev->dev.parent = dev;
/* name will be NULL when enumerated via ACPI */
if (name)
indio_dev->name = name;
else
indio_dev->name = (char *)dev_name(&client->dev);
indio_dev->name = dev_name(dev);
indio_dev->channels = inv_mpu_channels;
indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels);
@ -824,13 +814,12 @@ static int inv_mpu_probe(struct i2c_client *client,
inv_mpu6050_read_fifo,
NULL);
if (result) {
dev_err(&st->client->dev, "configure buffer fail %d\n",
result);
dev_err(dev, "configure buffer fail %d\n", result);
return result;
}
result = inv_mpu6050_probe_trigger(indio_dev);
if (result) {
dev_err(&st->client->dev, "trigger probe fail %d\n", result);
dev_err(dev, "trigger probe fail %d\n", result);
goto out_unreg_ring;
}
@ -838,102 +827,47 @@ static int inv_mpu_probe(struct i2c_client *client,
spin_lock_init(&st->time_stamp_lock);
result = iio_device_register(indio_dev);
if (result) {
dev_err(&st->client->dev, "IIO register fail %d\n", result);
dev_err(dev, "IIO register fail %d\n", result);
goto out_remove_trigger;
}
st->mux_adapter = i2c_add_mux_adapter(client->adapter,
&client->dev,
indio_dev,
0, 0, 0,
inv_mpu6050_select_bypass,
inv_mpu6050_deselect_bypass);
if (!st->mux_adapter) {
result = -ENODEV;
goto out_unreg_device;
}
result = inv_mpu_acpi_create_mux_client(st);
if (result)
goto out_del_mux;
return 0;
out_del_mux:
i2c_del_mux_adapter(st->mux_adapter);
out_unreg_device:
iio_device_unregister(indio_dev);
out_remove_trigger:
inv_mpu6050_remove_trigger(st);
out_unreg_ring:
iio_triggered_buffer_cleanup(indio_dev);
return result;
}
EXPORT_SYMBOL_GPL(inv_mpu_core_probe);
static int inv_mpu_remove(struct i2c_client *client)
int inv_mpu_core_remove(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct inv_mpu6050_state *st = iio_priv(indio_dev);
struct iio_dev *indio_dev = dev_get_drvdata(dev);
inv_mpu_acpi_delete_mux_client(st);
i2c_del_mux_adapter(st->mux_adapter);
iio_device_unregister(indio_dev);
inv_mpu6050_remove_trigger(st);
inv_mpu6050_remove_trigger(iio_priv(indio_dev));
iio_triggered_buffer_cleanup(indio_dev);
return 0;
}
EXPORT_SYMBOL_GPL(inv_mpu_core_remove);
#ifdef CONFIG_PM_SLEEP
static int inv_mpu_resume(struct device *dev)
{
return inv_mpu6050_set_power_itg(
iio_priv(i2c_get_clientdata(to_i2c_client(dev))), true);
return inv_mpu6050_set_power_itg(iio_priv(dev_get_drvdata(dev)), true);
}
static int inv_mpu_suspend(struct device *dev)
{
return inv_mpu6050_set_power_itg(
iio_priv(i2c_get_clientdata(to_i2c_client(dev))), false);
return inv_mpu6050_set_power_itg(iio_priv(dev_get_drvdata(dev)), false);
}
static SIMPLE_DEV_PM_OPS(inv_mpu_pmops, inv_mpu_suspend, inv_mpu_resume);
#define INV_MPU6050_PMOPS (&inv_mpu_pmops)
#else
#define INV_MPU6050_PMOPS NULL
#endif /* CONFIG_PM_SLEEP */
/*
* device id table is used to identify what device can be
* supported by this driver
*/
static const struct i2c_device_id inv_mpu_id[] = {
{"mpu6050", INV_MPU6050},
{"mpu6500", INV_MPU6500},
{}
};
MODULE_DEVICE_TABLE(i2c, inv_mpu_id);
static const struct acpi_device_id inv_acpi_match[] = {
{"INVN6500", 0},
{ },
};
MODULE_DEVICE_TABLE(acpi, inv_acpi_match);
static struct i2c_driver inv_mpu_driver = {
.probe = inv_mpu_probe,
.remove = inv_mpu_remove,
.id_table = inv_mpu_id,
.driver = {
.name = "inv-mpu6050",
.pm = INV_MPU6050_PMOPS,
.acpi_match_table = ACPI_PTR(inv_acpi_match),
},
};
module_i2c_driver(inv_mpu_driver);
SIMPLE_DEV_PM_OPS(inv_mpu_pmops, inv_mpu_suspend, inv_mpu_resume);
EXPORT_SYMBOL_GPL(inv_mpu_pmops);
MODULE_AUTHOR("Invensense Corporation");
MODULE_DESCRIPTION("Invensense device MPU6050 driver");

View File

@ -0,0 +1,208 @@
/*
* Copyright (C) 2012 Invensense, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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/acpi.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/i2c-mux.h>
#include <linux/iio/iio.h>
#include <linux/module.h>
#include "inv_mpu_iio.h"
static const struct regmap_config inv_mpu_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
};
/*
* The i2c read/write needs to happen in unlocked mode. As the parent
* adapter is common. If we use locked versions, it will fail as
* the mux adapter will lock the parent i2c adapter, while calling
* select/deselect functions.
*/
static int inv_mpu6050_write_reg_unlocked(struct i2c_client *client,
u8 reg, u8 d)
{
int ret;
u8 buf[2] = {reg, d};
struct i2c_msg msg[1] = {
{
.addr = client->addr,
.flags = 0,
.len = sizeof(buf),
.buf = buf,
}
};
ret = __i2c_transfer(client->adapter, msg, 1);
if (ret != 1)
return ret;
return 0;
}
static int inv_mpu6050_select_bypass(struct i2c_adapter *adap, void *mux_priv,
u32 chan_id)
{
struct i2c_client *client = mux_priv;
struct iio_dev *indio_dev = dev_get_drvdata(&client->dev);
struct inv_mpu6050_state *st = iio_priv(indio_dev);
int ret = 0;
/* Use the same mutex which was used everywhere to protect power-op */
mutex_lock(&indio_dev->mlock);
if (!st->powerup_count) {
ret = inv_mpu6050_write_reg_unlocked(client,
st->reg->pwr_mgmt_1, 0);
if (ret)
goto write_error;
usleep_range(INV_MPU6050_REG_UP_TIME_MIN,
INV_MPU6050_REG_UP_TIME_MAX);
}
if (!ret) {
st->powerup_count++;
ret = inv_mpu6050_write_reg_unlocked(client,
st->reg->int_pin_cfg,
INV_MPU6050_INT_PIN_CFG |
INV_MPU6050_BIT_BYPASS_EN);
}
write_error:
mutex_unlock(&indio_dev->mlock);
return ret;
}
static int inv_mpu6050_deselect_bypass(struct i2c_adapter *adap,
void *mux_priv, u32 chan_id)
{
struct i2c_client *client = mux_priv;
struct iio_dev *indio_dev = dev_get_drvdata(&client->dev);
struct inv_mpu6050_state *st = iio_priv(indio_dev);
mutex_lock(&indio_dev->mlock);
/* It doesn't really mattter, if any of the calls fails */
inv_mpu6050_write_reg_unlocked(client, st->reg->int_pin_cfg,
INV_MPU6050_INT_PIN_CFG);
st->powerup_count--;
if (!st->powerup_count)
inv_mpu6050_write_reg_unlocked(client, st->reg->pwr_mgmt_1,
INV_MPU6050_BIT_SLEEP);
mutex_unlock(&indio_dev->mlock);
return 0;
}
/**
* inv_mpu_probe() - probe function.
* @client: i2c client.
* @id: i2c device id.
*
* Returns 0 on success, a negative error code otherwise.
*/
static int inv_mpu_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct inv_mpu6050_state *st;
int result;
const char *name = id ? id->name : NULL;
struct regmap *regmap;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_I2C_BLOCK))
return -EOPNOTSUPP;
regmap = devm_regmap_init_i2c(client, &inv_mpu_regmap_config);
if (IS_ERR(regmap)) {
dev_err(&client->dev, "Failed to register i2c regmap %d\n",
(int)PTR_ERR(regmap));
return PTR_ERR(regmap);
}
result = inv_mpu_core_probe(regmap, client->irq, name,
NULL, id->driver_data);
if (result < 0)
return result;
st = iio_priv(dev_get_drvdata(&client->dev));
st->mux_adapter = i2c_add_mux_adapter(client->adapter,
&client->dev,
client,
0, 0, 0,
inv_mpu6050_select_bypass,
inv_mpu6050_deselect_bypass);
if (!st->mux_adapter) {
result = -ENODEV;
goto out_unreg_device;
}
result = inv_mpu_acpi_create_mux_client(client);
if (result)
goto out_del_mux;
return 0;
out_del_mux:
i2c_del_mux_adapter(st->mux_adapter);
out_unreg_device:
inv_mpu_core_remove(&client->dev);
return result;
}
static int inv_mpu_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct inv_mpu6050_state *st = iio_priv(indio_dev);
inv_mpu_acpi_delete_mux_client(client);
i2c_del_mux_adapter(st->mux_adapter);
return inv_mpu_core_remove(&client->dev);
}
/*
* device id table is used to identify what device can be
* supported by this driver
*/
static const struct i2c_device_id inv_mpu_id[] = {
{"mpu6050", INV_MPU6050},
{"mpu6500", INV_MPU6500},
{}
};
MODULE_DEVICE_TABLE(i2c, inv_mpu_id);
static const struct acpi_device_id inv_acpi_match[] = {
{"INVN6500", 0},
{ },
};
MODULE_DEVICE_TABLE(acpi, inv_acpi_match);
static struct i2c_driver inv_mpu_driver = {
.probe = inv_mpu_probe,
.remove = inv_mpu_remove,
.id_table = inv_mpu_id,
.driver = {
.acpi_match_table = ACPI_PTR(inv_acpi_match),
.name = "inv-mpu6050-i2c",
.pm = &inv_mpu_pmops,
},
};
module_i2c_driver(inv_mpu_driver);
MODULE_AUTHOR("Invensense Corporation");
MODULE_DESCRIPTION("Invensense device MPU6050 driver");
MODULE_LICENSE("GPL");

View File

@ -15,6 +15,7 @@
#include <linux/spinlock.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/regmap.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/kfifo_buf.h>
#include <linux/iio/trigger.h>
@ -38,6 +39,9 @@
* @int_enable: Interrupt enable register.
* @pwr_mgmt_1: Controls chip's power state and clock source.
* @pwr_mgmt_2: Controls power state of individual sensors.
* @int_pin_cfg; Controls interrupt pin configuration.
* @accl_offset: Controls the accelerometer calibration offset.
* @gyro_offset: Controls the gyroscope calibration offset.
*/
struct inv_mpu6050_reg_map {
u8 sample_rate_div;
@ -55,12 +59,15 @@ struct inv_mpu6050_reg_map {
u8 pwr_mgmt_1;
u8 pwr_mgmt_2;
u8 int_pin_cfg;
u8 accl_offset;
u8 gyro_offset;
};
/*device enum */
enum inv_devices {
INV_MPU6050,
INV_MPU6500,
INV_MPU6000,
INV_NUM_PARTS
};
@ -107,9 +114,10 @@ struct inv_mpu6050_hw {
* @hw: Other hardware-specific information.
* @chip_type: chip type.
* @time_stamp_lock: spin lock to time stamp.
* @client: i2c client handle.
* @plat_data: platform data.
* @timestamps: kfifo queue to store time stamp.
* @map regmap pointer.
* @irq interrupt number.
*/
struct inv_mpu6050_state {
#define TIMESTAMP_FIFO_SIZE 16
@ -119,15 +127,19 @@ struct inv_mpu6050_state {
const struct inv_mpu6050_hw *hw;
enum inv_devices chip_type;
spinlock_t time_stamp_lock;
struct i2c_client *client;
struct i2c_adapter *mux_adapter;
struct i2c_client *mux_client;
unsigned int powerup_count;
struct inv_mpu6050_platform_data plat_data;
DECLARE_KFIFO(timestamps, long long, TIMESTAMP_FIFO_SIZE);
struct regmap *map;
int irq;
};
/*register and associated bit definition*/
#define INV_MPU6050_REG_ACCEL_OFFSET 0x06
#define INV_MPU6050_REG_GYRO_OFFSET 0x13
#define INV_MPU6050_REG_SAMPLE_RATE_DIV 0x19
#define INV_MPU6050_REG_CONFIG 0x1A
#define INV_MPU6050_REG_GYRO_CONFIG 0x1B
@ -151,6 +163,7 @@ struct inv_mpu6050_state {
#define INV_MPU6050_BIT_I2C_MST_EN 0x20
#define INV_MPU6050_BIT_FIFO_EN 0x40
#define INV_MPU6050_BIT_DMP_EN 0x80
#define INV_MPU6050_BIT_I2C_IF_DIS 0x10
#define INV_MPU6050_REG_PWR_MGMT_1 0x6B
#define INV_MPU6050_BIT_H_RESET 0x80
@ -167,10 +180,18 @@ struct inv_mpu6050_state {
#define INV_MPU6050_BYTES_PER_3AXIS_SENSOR 6
#define INV_MPU6050_FIFO_COUNT_BYTE 2
#define INV_MPU6050_FIFO_THRESHOLD 500
/* mpu6500 registers */
#define INV_MPU6500_REG_ACCEL_OFFSET 0x77
/* delay time in milliseconds */
#define INV_MPU6050_POWER_UP_TIME 100
#define INV_MPU6050_TEMP_UP_TIME 100
#define INV_MPU6050_SENSOR_UP_TIME 30
#define INV_MPU6050_REG_UP_TIME 5
/* delay time in microseconds */
#define INV_MPU6050_REG_UP_TIME_MIN 5000
#define INV_MPU6050_REG_UP_TIME_MAX 10000
#define INV_MPU6050_TEMP_OFFSET 12421
#define INV_MPU6050_TEMP_SCALE 2941
@ -185,6 +206,7 @@ struct inv_mpu6050_state {
#define INV_MPU6050_REG_INT_PIN_CFG 0x37
#define INV_MPU6050_BIT_BYPASS_EN 0x2
#define INV_MPU6050_INT_PIN_CFG 0
/* init parameters */
#define INV_MPU6050_INIT_FIFO_RATE 50
@ -252,5 +274,10 @@ int inv_reset_fifo(struct iio_dev *indio_dev);
int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask);
int inv_mpu6050_write_reg(struct inv_mpu6050_state *st, int reg, u8 val);
int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on);
int inv_mpu_acpi_create_mux_client(struct inv_mpu6050_state *st);
void inv_mpu_acpi_delete_mux_client(struct inv_mpu6050_state *st);
int inv_mpu_acpi_create_mux_client(struct i2c_client *client);
void inv_mpu_acpi_delete_mux_client(struct i2c_client *client);
int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
int (*inv_mpu_bus_setup)(struct iio_dev *), int chip_type);
int inv_mpu_core_remove(struct device *dev);
int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on);
extern const struct dev_pm_ops inv_mpu_pmops;

View File

@ -13,7 +13,6 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/sysfs.h>
@ -41,23 +40,24 @@ int inv_reset_fifo(struct iio_dev *indio_dev)
struct inv_mpu6050_state *st = iio_priv(indio_dev);
/* disable interrupt */
result = inv_mpu6050_write_reg(st, st->reg->int_enable, 0);
result = regmap_write(st->map, st->reg->int_enable, 0);
if (result) {
dev_err(&st->client->dev, "int_enable failed %d\n", result);
dev_err(regmap_get_device(st->map), "int_enable failed %d\n",
result);
return result;
}
/* disable the sensor output to FIFO */
result = inv_mpu6050_write_reg(st, st->reg->fifo_en, 0);
result = regmap_write(st->map, st->reg->fifo_en, 0);
if (result)
goto reset_fifo_fail;
/* disable fifo reading */
result = inv_mpu6050_write_reg(st, st->reg->user_ctrl, 0);
result = regmap_write(st->map, st->reg->user_ctrl, 0);
if (result)
goto reset_fifo_fail;
/* reset FIFO*/
result = inv_mpu6050_write_reg(st, st->reg->user_ctrl,
INV_MPU6050_BIT_FIFO_RST);
result = regmap_write(st->map, st->reg->user_ctrl,
INV_MPU6050_BIT_FIFO_RST);
if (result)
goto reset_fifo_fail;
@ -67,14 +67,14 @@ int inv_reset_fifo(struct iio_dev *indio_dev)
/* enable interrupt */
if (st->chip_config.accl_fifo_enable ||
st->chip_config.gyro_fifo_enable) {
result = inv_mpu6050_write_reg(st, st->reg->int_enable,
INV_MPU6050_BIT_DATA_RDY_EN);
result = regmap_write(st->map, st->reg->int_enable,
INV_MPU6050_BIT_DATA_RDY_EN);
if (result)
return result;
}
/* enable FIFO reading and I2C master interface*/
result = inv_mpu6050_write_reg(st, st->reg->user_ctrl,
INV_MPU6050_BIT_FIFO_EN);
result = regmap_write(st->map, st->reg->user_ctrl,
INV_MPU6050_BIT_FIFO_EN);
if (result)
goto reset_fifo_fail;
/* enable sensor output to FIFO */
@ -83,16 +83,16 @@ int inv_reset_fifo(struct iio_dev *indio_dev)
d |= INV_MPU6050_BITS_GYRO_OUT;
if (st->chip_config.accl_fifo_enable)
d |= INV_MPU6050_BIT_ACCEL_OUT;
result = inv_mpu6050_write_reg(st, st->reg->fifo_en, d);
result = regmap_write(st->map, st->reg->fifo_en, d);
if (result)
goto reset_fifo_fail;
return 0;
reset_fifo_fail:
dev_err(&st->client->dev, "reset fifo failed %d\n", result);
result = inv_mpu6050_write_reg(st, st->reg->int_enable,
INV_MPU6050_BIT_DATA_RDY_EN);
dev_err(regmap_get_device(st->map), "reset fifo failed %d\n", result);
result = regmap_write(st->map, st->reg->int_enable,
INV_MPU6050_BIT_DATA_RDY_EN);
return result;
}
@ -109,7 +109,7 @@ irqreturn_t inv_mpu6050_irq_handler(int irq, void *p)
timestamp = iio_get_time_ns();
kfifo_in_spinlocked(&st->timestamps, &timestamp, 1,
&st->time_stamp_lock);
&st->time_stamp_lock);
return IRQ_WAKE_THREAD;
}
@ -143,10 +143,9 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
* read fifo_count register to know how many bytes inside FIFO
* right now
*/
result = i2c_smbus_read_i2c_block_data(st->client,
st->reg->fifo_count_h,
INV_MPU6050_FIFO_COUNT_BYTE, data);
if (result != INV_MPU6050_FIFO_COUNT_BYTE)
result = regmap_bulk_read(st->map, st->reg->fifo_count_h, data,
INV_MPU6050_FIFO_COUNT_BYTE);
if (result)
goto end_session;
fifo_count = be16_to_cpup((__be16 *)(&data[0]));
if (fifo_count < bytes_per_datum)
@ -158,22 +157,21 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
goto flush_fifo;
/* Timestamp mismatch. */
if (kfifo_len(&st->timestamps) >
fifo_count / bytes_per_datum + INV_MPU6050_TIME_STAMP_TOR)
goto flush_fifo;
fifo_count / bytes_per_datum + INV_MPU6050_TIME_STAMP_TOR)
goto flush_fifo;
while (fifo_count >= bytes_per_datum) {
result = i2c_smbus_read_i2c_block_data(st->client,
st->reg->fifo_r_w,
bytes_per_datum, data);
if (result != bytes_per_datum)
result = regmap_bulk_read(st->map, st->reg->fifo_r_w,
data, bytes_per_datum);
if (result)
goto flush_fifo;
result = kfifo_out(&st->timestamps, &timestamp, 1);
/* when there is no timestamp, put timestamp as 0 */
if (0 == result)
if (result == 0)
timestamp = 0;
result = iio_push_to_buffers_with_timestamp(indio_dev, data,
timestamp);
timestamp);
if (result)
goto flush_fifo;
fifo_count -= bytes_per_datum;

View File

@ -0,0 +1,98 @@
/*
* Copyright (C) 2015 Intel Corporation Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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/acpi.h>
#include <linux/spi/spi.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
#include "inv_mpu_iio.h"
static const struct regmap_config inv_mpu_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
};
static int inv_mpu_i2c_disable(struct iio_dev *indio_dev)
{
struct inv_mpu6050_state *st = iio_priv(indio_dev);
int ret = 0;
ret = inv_mpu6050_set_power_itg(st, true);
if (ret)
return ret;
ret = regmap_write(st->map, INV_MPU6050_REG_USER_CTRL,
INV_MPU6050_BIT_I2C_IF_DIS);
if (ret) {
inv_mpu6050_set_power_itg(st, false);
return ret;
}
return inv_mpu6050_set_power_itg(st, false);
}
static int inv_mpu_probe(struct spi_device *spi)
{
struct regmap *regmap;
const struct spi_device_id *id = spi_get_device_id(spi);
const char *name = id ? id->name : NULL;
regmap = devm_regmap_init_spi(spi, &inv_mpu_regmap_config);
if (IS_ERR(regmap)) {
dev_err(&spi->dev, "Failed to register spi regmap %d\n",
(int)PTR_ERR(regmap));
return PTR_ERR(regmap);
}
return inv_mpu_core_probe(regmap, spi->irq, name,
inv_mpu_i2c_disable, id->driver_data);
}
static int inv_mpu_remove(struct spi_device *spi)
{
return inv_mpu_core_remove(&spi->dev);
}
/*
* device id table is used to identify what device can be
* supported by this driver
*/
static const struct spi_device_id inv_mpu_id[] = {
{"mpu6000", INV_MPU6000},
{}
};
MODULE_DEVICE_TABLE(spi, inv_mpu_id);
static const struct acpi_device_id inv_acpi_match[] = {
{"INVN6000", 0},
{ },
};
MODULE_DEVICE_TABLE(acpi, inv_acpi_match);
static struct spi_driver inv_mpu_driver = {
.probe = inv_mpu_probe,
.remove = inv_mpu_remove,
.id_table = inv_mpu_id,
.driver = {
.acpi_match_table = ACPI_PTR(inv_acpi_match),
.name = "inv-mpu6000-spi",
.pm = &inv_mpu_pmops,
},
};
module_spi_driver(inv_mpu_driver);
MODULE_AUTHOR("Adriana Reus <adriana.reus@intel.com>");
MODULE_DESCRIPTION("Invensense device MPU6000 driver");
MODULE_LICENSE("GPL");

View File

@ -19,19 +19,19 @@ static void inv_scan_query(struct iio_dev *indio_dev)
st->chip_config.gyro_fifo_enable =
test_bit(INV_MPU6050_SCAN_GYRO_X,
indio_dev->active_scan_mask) ||
test_bit(INV_MPU6050_SCAN_GYRO_Y,
indio_dev->active_scan_mask) ||
test_bit(INV_MPU6050_SCAN_GYRO_Z,
indio_dev->active_scan_mask);
indio_dev->active_scan_mask) ||
test_bit(INV_MPU6050_SCAN_GYRO_Y,
indio_dev->active_scan_mask) ||
test_bit(INV_MPU6050_SCAN_GYRO_Z,
indio_dev->active_scan_mask);
st->chip_config.accl_fifo_enable =
test_bit(INV_MPU6050_SCAN_ACCL_X,
indio_dev->active_scan_mask) ||
test_bit(INV_MPU6050_SCAN_ACCL_Y,
indio_dev->active_scan_mask) ||
test_bit(INV_MPU6050_SCAN_ACCL_Z,
indio_dev->active_scan_mask);
indio_dev->active_scan_mask) ||
test_bit(INV_MPU6050_SCAN_ACCL_Y,
indio_dev->active_scan_mask) ||
test_bit(INV_MPU6050_SCAN_ACCL_Z,
indio_dev->active_scan_mask);
}
/**
@ -65,15 +65,15 @@ static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable)
if (result)
return result;
} else {
result = inv_mpu6050_write_reg(st, st->reg->fifo_en, 0);
result = regmap_write(st->map, st->reg->fifo_en, 0);
if (result)
return result;
result = inv_mpu6050_write_reg(st, st->reg->int_enable, 0);
result = regmap_write(st->map, st->reg->int_enable, 0);
if (result)
return result;
result = inv_mpu6050_write_reg(st, st->reg->user_ctrl, 0);
result = regmap_write(st->map, st->reg->user_ctrl, 0);
if (result)
return result;
@ -101,7 +101,7 @@ static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable)
* @state: Desired trigger state
*/
static int inv_mpu_data_rdy_trigger_set_state(struct iio_trigger *trig,
bool state)
bool state)
{
return inv_mpu6050_set_enable(iio_trigger_get_drvdata(trig), state);
}
@ -123,7 +123,7 @@ int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev)
if (!st->trig)
return -ENOMEM;
ret = devm_request_irq(&indio_dev->dev, st->client->irq,
ret = devm_request_irq(&indio_dev->dev, st->irq,
&iio_trigger_generic_data_rdy_poll,
IRQF_TRIGGER_RISING,
"inv_mpu",
@ -131,7 +131,7 @@ int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev)
if (ret)
return ret;
st->trig->dev.parent = &st->client->dev;
st->trig->dev.parent = regmap_get_device(st->map);
st->trig->ops = &inv_mpu_trigger_ops;
iio_trigger_set_drvdata(st->trig, indio_dev);

View File

@ -512,33 +512,41 @@ static ssize_t iio_buffer_show_enable(struct device *dev,
return sprintf(buf, "%d\n", iio_buffer_is_active(indio_dev->buffer));
}
static unsigned int iio_storage_bytes_for_si(struct iio_dev *indio_dev,
unsigned int scan_index)
{
const struct iio_chan_spec *ch;
unsigned int bytes;
ch = iio_find_channel_from_si(indio_dev, scan_index);
bytes = ch->scan_type.storagebits / 8;
if (ch->scan_type.repeat > 1)
bytes *= ch->scan_type.repeat;
return bytes;
}
static unsigned int iio_storage_bytes_for_timestamp(struct iio_dev *indio_dev)
{
return iio_storage_bytes_for_si(indio_dev,
indio_dev->scan_index_timestamp);
}
static int iio_compute_scan_bytes(struct iio_dev *indio_dev,
const unsigned long *mask, bool timestamp)
{
const struct iio_chan_spec *ch;
unsigned bytes = 0;
int length, i;
/* How much space will the demuxed element take? */
for_each_set_bit(i, mask,
indio_dev->masklength) {
ch = iio_find_channel_from_si(indio_dev, i);
if (ch->scan_type.repeat > 1)
length = ch->scan_type.storagebits / 8 *
ch->scan_type.repeat;
else
length = ch->scan_type.storagebits / 8;
length = iio_storage_bytes_for_si(indio_dev, i);
bytes = ALIGN(bytes, length);
bytes += length;
}
if (timestamp) {
ch = iio_find_channel_from_si(indio_dev,
indio_dev->scan_index_timestamp);
if (ch->scan_type.repeat > 1)
length = ch->scan_type.storagebits / 8 *
ch->scan_type.repeat;
else
length = ch->scan_type.storagebits / 8;
length = iio_storage_bytes_for_timestamp(indio_dev);
bytes = ALIGN(bytes, length);
bytes += length;
}
@ -1288,7 +1296,6 @@ static int iio_buffer_add_demux(struct iio_buffer *buffer,
static int iio_buffer_update_demux(struct iio_dev *indio_dev,
struct iio_buffer *buffer)
{
const struct iio_chan_spec *ch;
int ret, in_ind = -1, out_ind, length;
unsigned in_loc = 0, out_loc = 0;
struct iio_demux_table *p = NULL;
@ -1315,21 +1322,11 @@ static int iio_buffer_update_demux(struct iio_dev *indio_dev,
in_ind = find_next_bit(indio_dev->active_scan_mask,
indio_dev->masklength,
in_ind + 1);
ch = iio_find_channel_from_si(indio_dev, in_ind);
if (ch->scan_type.repeat > 1)
length = ch->scan_type.storagebits / 8 *
ch->scan_type.repeat;
else
length = ch->scan_type.storagebits / 8;
length = iio_storage_bytes_for_si(indio_dev, in_ind);
/* Make sure we are aligned */
in_loc = roundup(in_loc, length) + length;
}
ch = iio_find_channel_from_si(indio_dev, in_ind);
if (ch->scan_type.repeat > 1)
length = ch->scan_type.storagebits / 8 *
ch->scan_type.repeat;
else
length = ch->scan_type.storagebits / 8;
length = iio_storage_bytes_for_si(indio_dev, in_ind);
out_loc = roundup(out_loc, length);
in_loc = roundup(in_loc, length);
ret = iio_buffer_add_demux(buffer, &p, in_loc, out_loc, length);
@ -1340,13 +1337,7 @@ static int iio_buffer_update_demux(struct iio_dev *indio_dev,
}
/* Relies on scan_timestamp being last */
if (buffer->scan_timestamp) {
ch = iio_find_channel_from_si(indio_dev,
indio_dev->scan_index_timestamp);
if (ch->scan_type.repeat > 1)
length = ch->scan_type.storagebits / 8 *
ch->scan_type.repeat;
else
length = ch->scan_type.storagebits / 8;
length = iio_storage_bytes_for_timestamp(indio_dev);
out_loc = roundup(out_loc, length);
in_loc = roundup(in_loc, length);
ret = iio_buffer_add_demux(buffer, &p, in_loc, out_loc, length);

View File

@ -77,6 +77,7 @@ static const char * const iio_chan_type_name_spec[] = {
[IIO_VELOCITY] = "velocity",
[IIO_CONCENTRATION] = "concentration",
[IIO_RESISTANCE] = "resistance",
[IIO_PH] = "ph",
};
static const char * const iio_modifier_names[] = {

View File

@ -241,7 +241,7 @@ static int bh1750_probe(struct i2c_client *client,
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
I2C_FUNC_SMBUS_WRITE_BYTE))
return -ENODEV;
return -EOPNOTSUPP;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)

View File

@ -326,7 +326,7 @@ static int jsa1212_probe(struct i2c_client *client,
int ret;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
return -EOPNOTSUPP;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)

View File

@ -65,19 +65,25 @@
#define OPT3001_REG_EXPONENT(n) ((n) >> 12)
#define OPT3001_REG_MANTISSA(n) ((n) & 0xfff)
#define OPT3001_INT_TIME_LONG 800000
#define OPT3001_INT_TIME_SHORT 100000
/*
* Time to wait for conversion result to be ready. The device datasheet
* worst-case max value is 880ms. Add some slack to be on the safe side.
* sect. 6.5 states results are ready after total integration time plus 3ms.
* This results in worst-case max values of 113ms or 883ms, respectively.
* Add some slack to be on the safe side.
*/
#define OPT3001_RESULT_READY_TIMEOUT msecs_to_jiffies(1000)
#define OPT3001_RESULT_READY_SHORT 150
#define OPT3001_RESULT_READY_LONG 1000
struct opt3001 {
struct i2c_client *client;
struct device *dev;
struct mutex lock;
u16 ok_to_ignore_lock:1;
u16 result_ready:1;
bool ok_to_ignore_lock;
bool result_ready;
wait_queue_head_t result_ready_queue;
u16 result;
@ -89,6 +95,8 @@ struct opt3001 {
u8 high_thresh_exp;
u8 low_thresh_exp;
bool use_irq;
};
struct opt3001_scale {
@ -227,26 +235,30 @@ static int opt3001_get_lux(struct opt3001 *opt, int *val, int *val2)
u16 reg;
u8 exponent;
u16 value;
long timeout;
/*
* Enable the end-of-conversion interrupt mechanism. Note that doing
* so will overwrite the low-level limit value however we will restore
* this value later on.
*/
ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_LOW_LIMIT,
OPT3001_LOW_LIMIT_EOC_ENABLE);
if (ret < 0) {
dev_err(opt->dev, "failed to write register %02x\n",
OPT3001_LOW_LIMIT);
return ret;
if (opt->use_irq) {
/*
* Enable the end-of-conversion interrupt mechanism. Note that
* doing so will overwrite the low-level limit value however we
* will restore this value later on.
*/
ret = i2c_smbus_write_word_swapped(opt->client,
OPT3001_LOW_LIMIT,
OPT3001_LOW_LIMIT_EOC_ENABLE);
if (ret < 0) {
dev_err(opt->dev, "failed to write register %02x\n",
OPT3001_LOW_LIMIT);
return ret;
}
/* Allow IRQ to access the device despite lock being set */
opt->ok_to_ignore_lock = true;
}
/* Reset data-ready indicator flag (will be set in the IRQ routine) */
/* Reset data-ready indicator flag */
opt->result_ready = false;
/* Allow IRQ to access the device despite lock being set */
opt->ok_to_ignore_lock = true;
/* Configure for single-conversion mode and start a new conversion */
ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION);
if (ret < 0) {
@ -266,32 +278,69 @@ static int opt3001_get_lux(struct opt3001 *opt, int *val, int *val2)
goto err;
}
/* Wait for the IRQ to indicate the conversion is complete */
ret = wait_event_timeout(opt->result_ready_queue, opt->result_ready,
OPT3001_RESULT_READY_TIMEOUT);
if (opt->use_irq) {
/* Wait for the IRQ to indicate the conversion is complete */
ret = wait_event_timeout(opt->result_ready_queue,
opt->result_ready,
msecs_to_jiffies(OPT3001_RESULT_READY_LONG));
} else {
/* Sleep for result ready time */
timeout = (opt->int_time == OPT3001_INT_TIME_SHORT) ?
OPT3001_RESULT_READY_SHORT : OPT3001_RESULT_READY_LONG;
msleep(timeout);
/* Check result ready flag */
ret = i2c_smbus_read_word_swapped(opt->client,
OPT3001_CONFIGURATION);
if (ret < 0) {
dev_err(opt->dev, "failed to read register %02x\n",
OPT3001_CONFIGURATION);
goto err;
}
if (!(ret & OPT3001_CONFIGURATION_CRF)) {
ret = -ETIMEDOUT;
goto err;
}
/* Obtain value */
ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_RESULT);
if (ret < 0) {
dev_err(opt->dev, "failed to read register %02x\n",
OPT3001_RESULT);
goto err;
}
opt->result = ret;
opt->result_ready = true;
}
err:
/* Disallow IRQ to access the device while lock is active */
opt->ok_to_ignore_lock = false;
if (opt->use_irq)
/* Disallow IRQ to access the device while lock is active */
opt->ok_to_ignore_lock = false;
if (ret == 0)
return -ETIMEDOUT;
else if (ret < 0)
return ret;
/*
* Disable the end-of-conversion interrupt mechanism by restoring the
* low-level limit value (clearing OPT3001_LOW_LIMIT_EOC_ENABLE). Note
* that selectively clearing those enable bits would affect the actual
* limit value due to bit-overlap and therefore can't be done.
*/
value = (opt->low_thresh_exp << 12) | opt->low_thresh_mantissa;
ret = i2c_smbus_write_word_swapped(opt->client, OPT3001_LOW_LIMIT,
value);
if (ret < 0) {
dev_err(opt->dev, "failed to write register %02x\n",
OPT3001_LOW_LIMIT);
return ret;
if (opt->use_irq) {
/*
* Disable the end-of-conversion interrupt mechanism by
* restoring the low-level limit value (clearing
* OPT3001_LOW_LIMIT_EOC_ENABLE). Note that selectively clearing
* those enable bits would affect the actual limit value due to
* bit-overlap and therefore can't be done.
*/
value = (opt->low_thresh_exp << 12) | opt->low_thresh_mantissa;
ret = i2c_smbus_write_word_swapped(opt->client,
OPT3001_LOW_LIMIT,
value);
if (ret < 0) {
dev_err(opt->dev, "failed to write register %02x\n",
OPT3001_LOW_LIMIT);
return ret;
}
}
exponent = OPT3001_REG_EXPONENT(opt->result);
@ -325,13 +374,13 @@ static int opt3001_set_int_time(struct opt3001 *opt, int time)
reg = ret;
switch (time) {
case 100000:
case OPT3001_INT_TIME_SHORT:
reg &= ~OPT3001_CONFIGURATION_CT;
opt->int_time = 100000;
opt->int_time = OPT3001_INT_TIME_SHORT;
break;
case 800000:
case OPT3001_INT_TIME_LONG:
reg |= OPT3001_CONFIGURATION_CT;
opt->int_time = 800000;
opt->int_time = OPT3001_INT_TIME_LONG;
break;
default:
return -EINVAL;
@ -597,9 +646,9 @@ static int opt3001_configure(struct opt3001 *opt)
/* Reflect status of the device's integration time setting */
if (reg & OPT3001_CONFIGURATION_CT)
opt->int_time = 800000;
opt->int_time = OPT3001_INT_TIME_LONG;
else
opt->int_time = 100000;
opt->int_time = OPT3001_INT_TIME_SHORT;
/* Ensure device is in shutdown initially */
opt3001_set_mode(opt, &reg, OPT3001_CONFIGURATION_M_SHUTDOWN);
@ -733,12 +782,18 @@ static int opt3001_probe(struct i2c_client *client,
return ret;
}
ret = request_threaded_irq(irq, NULL, opt3001_irq,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"opt3001", iio);
if (ret) {
dev_err(dev, "failed to request IRQ #%d\n", irq);
return ret;
/* Make use of INT pin only if valid IRQ no. is given */
if (irq > 0) {
ret = request_threaded_irq(irq, NULL, opt3001_irq,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"opt3001", iio);
if (ret) {
dev_err(dev, "failed to request IRQ #%d\n", irq);
return ret;
}
opt->use_irq = true;
} else {
dev_dbg(opt->dev, "enabling interrupt-less operation\n");
}
return 0;
@ -751,7 +806,8 @@ static int opt3001_remove(struct i2c_client *client)
int ret;
u16 reg;
free_irq(client->irq, iio);
if (opt->use_irq)
free_irq(client->irq, iio);
ret = i2c_smbus_read_word_swapped(opt->client, OPT3001_CONFIGURATION);
if (ret < 0) {

View File

@ -105,4 +105,37 @@ config IIO_ST_MAGN_SPI_3AXIS
depends on IIO_ST_MAGN_3AXIS
depends on IIO_ST_SENSORS_SPI
config SENSORS_HMC5843
tristate
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
config SENSORS_HMC5843_I2C
tristate "Honeywell HMC5843/5883/5883L 3-Axis Magnetometer (I2C)"
depends on I2C
select SENSORS_HMC5843
select REGMAP_I2C
help
Say Y here to add support for the Honeywell HMC5843, HMC5883 and
HMC5883L 3-Axis Magnetometer (digital compass).
This driver can also be compiled as a set of modules.
If so, these modules will be created:
- hmc5843_core (core functions)
- hmc5843_i2c (support for HMC5843, HMC5883, HMC5883L and HMC5983)
config SENSORS_HMC5843_SPI
tristate "Honeywell HMC5983 3-Axis Magnetometer (SPI)"
depends on SPI_MASTER
select SENSORS_HMC5843
select REGMAP_SPI
help
Say Y here to add support for the Honeywell HMC5983 3-Axis Magnetometer
(digital compass).
This driver can also be compiled as a set of modules.
If so, these modules will be created:
- hmc5843_core (core functions)
- hmc5843_spi (support for HMC5983)
endmenu

View File

@ -15,3 +15,7 @@ st_magn-$(CONFIG_IIO_BUFFER) += st_magn_buffer.o
obj-$(CONFIG_IIO_ST_MAGN_I2C_3AXIS) += st_magn_i2c.o
obj-$(CONFIG_IIO_ST_MAGN_SPI_3AXIS) += st_magn_spi.o
obj-$(CONFIG_SENSORS_HMC5843) += hmc5843_core.o
obj-$(CONFIG_SENSORS_HMC5843_I2C) += hmc5843_i2c.o
obj-$(CONFIG_SENSORS_HMC5843_SPI) += hmc5843_spi.o

View File

@ -252,7 +252,7 @@ struct ak_def {
u8 data_regs[3];
};
static struct ak_def ak_def_array[AK_MAX_TYPE] = {
static const struct ak_def ak_def_array[AK_MAX_TYPE] = {
{
.type = AK8975,
.raw_to_gauss = ak8975_raw_to_gauss,
@ -360,7 +360,7 @@ static struct ak_def ak_def_array[AK_MAX_TYPE] = {
*/
struct ak8975_data {
struct i2c_client *client;
struct ak_def *def;
const struct ak_def *def;
struct attribute_group attrs;
struct mutex lock;
u8 asa[3];

View File

@ -7,8 +7,7 @@
* 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 HMC5843_CORE_H
#define HMC5843_CORE_H
@ -38,7 +37,7 @@ enum hmc5843_ids {
* @regmap: hardware access register maps
* @variant: describe chip variants
* @buffer: 3x 16-bit channels + padding + 64-bit timestamp
**/
*/
struct hmc5843_data {
struct device *dev;
struct mutex lock;

View File

@ -18,7 +18,6 @@
* 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>
@ -66,6 +65,33 @@
#define HMC5843_MEAS_CONF_NEGATIVE_BIAS 0x02
#define HMC5843_MEAS_CONF_MASK 0x03
/*
* API for setting the measurement configuration to
* Normal, Positive bias and Negative bias
*
* From the datasheet:
* 0 - Normal measurement configuration (default): In normal measurement
* configuration the device follows normal measurement flow. Pins BP
* and BN are left floating and high impedance.
*
* 1 - Positive bias configuration: In positive bias configuration, a
* positive current is forced across the resistive load on pins BP
* and BN.
*
* 2 - Negative bias configuration. In negative bias configuration, a
* negative current is forced across the resistive load on pins BP
* and BN.
*
* 3 - Only available on HMC5983. Magnetic sensor is disabled.
* Temperature sensor is enabled.
*/
static const char *const hmc5843_meas_conf_modes[] = {"normal", "positivebias",
"negativebias"};
static const char *const hmc5983_meas_conf_modes[] = {"normal", "positivebias",
"negativebias",
"disabled"};
/* Scaling factors: 10000000/Gain */
static const int hmc5843_regval_to_nanoscale[] = {
6173, 7692, 10309, 12821, 18868, 21739, 25641, 35714
@ -174,24 +200,6 @@ static int hmc5843_read_measurement(struct hmc5843_data *data,
return IIO_VAL_INT;
}
/*
* API for setting the measurement configuration to
* Normal, Positive bias and Negative bias
*
* From the datasheet:
* 0 - Normal measurement configuration (default): In normal measurement
* configuration the device follows normal measurement flow. Pins BP
* and BN are left floating and high impedance.
*
* 1 - Positive bias configuration: In positive bias configuration, a
* positive current is forced across the resistive load on pins BP
* and BN.
*
* 2 - Negative bias configuration. In negative bias configuration, a
* negative current is forced across the resistive load on pins BP
* and BN.
*
*/
static int hmc5843_set_meas_conf(struct hmc5843_data *data, u8 meas_conf)
{
int ret;
@ -205,48 +213,55 @@ static int hmc5843_set_meas_conf(struct hmc5843_data *data, u8 meas_conf)
}
static
ssize_t hmc5843_show_measurement_configuration(struct device *dev,
struct device_attribute *attr,
char *buf)
int hmc5843_show_measurement_configuration(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
struct hmc5843_data *data = iio_priv(dev_to_iio_dev(dev));
struct hmc5843_data *data = iio_priv(indio_dev);
unsigned int val;
int ret;
ret = regmap_read(data->regmap, HMC5843_CONFIG_REG_A, &val);
if (ret)
return ret;
val &= HMC5843_MEAS_CONF_MASK;
return sprintf(buf, "%d\n", val);
return val & HMC5843_MEAS_CONF_MASK;
}
static
ssize_t hmc5843_set_measurement_configuration(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
int hmc5843_set_measurement_configuration(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
unsigned int meas_conf)
{
struct hmc5843_data *data = iio_priv(dev_to_iio_dev(dev));
unsigned long meas_conf = 0;
int ret;
struct hmc5843_data *data = iio_priv(indio_dev);
ret = kstrtoul(buf, 10, &meas_conf);
if (ret)
return ret;
if (meas_conf >= HMC5843_MEAS_CONF_MASK)
return -EINVAL;
ret = hmc5843_set_meas_conf(data, meas_conf);
return (ret < 0) ? ret : count;
return hmc5843_set_meas_conf(data, meas_conf);
}
static IIO_DEVICE_ATTR(meas_conf,
S_IWUSR | S_IRUGO,
hmc5843_show_measurement_configuration,
hmc5843_set_measurement_configuration,
0);
static const struct iio_enum hmc5843_meas_conf_enum = {
.items = hmc5843_meas_conf_modes,
.num_items = ARRAY_SIZE(hmc5843_meas_conf_modes),
.get = hmc5843_show_measurement_configuration,
.set = hmc5843_set_measurement_configuration,
};
static const struct iio_chan_spec_ext_info hmc5843_ext_info[] = {
IIO_ENUM("meas_conf", true, &hmc5843_meas_conf_enum),
IIO_ENUM_AVAILABLE("meas_conf", &hmc5843_meas_conf_enum),
{ },
};
static const struct iio_enum hmc5983_meas_conf_enum = {
.items = hmc5983_meas_conf_modes,
.num_items = ARRAY_SIZE(hmc5983_meas_conf_modes),
.get = hmc5843_show_measurement_configuration,
.set = hmc5843_set_measurement_configuration,
};
static const struct iio_chan_spec_ext_info hmc5983_ext_info[] = {
IIO_ENUM("meas_conf", true, &hmc5983_meas_conf_enum),
IIO_ENUM_AVAILABLE("meas_conf", &hmc5983_meas_conf_enum),
{ },
};
static
ssize_t hmc5843_show_samp_freq_avail(struct device *dev,
@ -459,6 +474,25 @@ done:
.storagebits = 16, \
.endianness = IIO_BE, \
}, \
.ext_info = hmc5843_ext_info, \
}
#define HMC5983_CHANNEL(axis, idx) \
{ \
.type = IIO_MAGN, \
.modified = 1, \
.channel2 = IIO_MOD_##axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.scan_index = idx, \
.scan_type = { \
.sign = 's', \
.realbits = 16, \
.storagebits = 16, \
.endianness = IIO_BE, \
}, \
.ext_info = hmc5983_ext_info, \
}
static const struct iio_chan_spec hmc5843_channels[] = {
@ -476,8 +510,14 @@ static const struct iio_chan_spec hmc5883_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(3),
};
static const struct iio_chan_spec hmc5983_channels[] = {
HMC5983_CHANNEL(X, 0),
HMC5983_CHANNEL(Z, 1),
HMC5983_CHANNEL(Y, 2),
IIO_CHAN_SOFT_TIMESTAMP(3),
};
static struct attribute *hmc5843_attributes[] = {
&iio_dev_attr_meas_conf.dev_attr.attr,
&iio_dev_attr_scale_available.dev_attr.attr,
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
NULL
@ -516,7 +556,7 @@ static const struct hmc5843_chip_info hmc5843_chip_info_tbl[] = {
ARRAY_SIZE(hmc5883l_regval_to_nanoscale),
},
[HMC5983_ID] = {
.channels = hmc5883_channels,
.channels = hmc5983_channels,
.regval_to_samp_freq = hmc5983_regval_to_samp_freq,
.n_regval_to_samp_freq =
ARRAY_SIZE(hmc5983_regval_to_samp_freq),
@ -565,14 +605,14 @@ static const unsigned long hmc5843_scan_masks[] = {0x7, 0};
int hmc5843_common_suspend(struct device *dev)
{
return hmc5843_set_mode(iio_priv(dev_get_drvdata(dev)),
HMC5843_MODE_CONVERSION_CONTINUOUS);
HMC5843_MODE_SLEEP);
}
EXPORT_SYMBOL(hmc5843_common_suspend);
int hmc5843_common_resume(struct device *dev)
{
return hmc5843_set_mode(iio_priv(dev_get_drvdata(dev)),
HMC5843_MODE_SLEEP);
HMC5843_MODE_CONVERSION_CONTINUOUS);
}
EXPORT_SYMBOL(hmc5843_common_resume);

View File

@ -7,8 +7,7 @@
* 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/i2c.h>

View File

@ -6,8 +6,7 @@
* 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/spi/spi.h>

View File

@ -175,6 +175,8 @@
#define ST_MAGN_3_BDU_MASK 0x10
#define ST_MAGN_3_DRDY_IRQ_ADDR 0x62
#define ST_MAGN_3_DRDY_INT_MASK 0x01
#define ST_MAGN_3_IHL_IRQ_ADDR 0x63
#define ST_MAGN_3_IHL_IRQ_MASK 0x04
#define ST_MAGN_3_FS_AVL_15000_GAIN 1500
#define ST_MAGN_3_MULTIREAD_BIT false
#define ST_MAGN_3_OUT_X_L_ADDR 0x68
@ -480,6 +482,8 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = {
.drdy_irq = {
.addr = ST_MAGN_3_DRDY_IRQ_ADDR,
.mask_int1 = ST_MAGN_3_DRDY_INT_MASK,
.addr_ihl = ST_MAGN_3_IHL_IRQ_ADDR,
.mask_ihl = ST_MAGN_3_IHL_IRQ_MASK,
},
.multi_read_bit = ST_MAGN_3_MULTIREAD_BIT,
.bootime = 2,

View File

@ -17,4 +17,16 @@ config MCP4531
To compile this driver as a module, choose M here: the
module will be called mcp4531.
config TPL0102
tristate "Texas Instruments digital potentiometer driver"
depends on I2C
select REGMAP_I2C
help
Say yes here to build support for the Texas Instruments
TPL0102, TPL0402
digital potentiometer chips.
To compile this driver as a module, choose M here: the
module will be called tpl0102.
endmenu

View File

@ -4,3 +4,4 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_MCP4531) += mcp4531.o
obj-$(CONFIG_TPL0102) += tpl0102.o

View File

@ -159,7 +159,7 @@ static int mcp4531_probe(struct i2c_client *client,
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_WORD_DATA)) {
dev_err(dev, "SMBUS Word Data not supported\n");
return -EIO;
return -EOPNOTSUPP;
}
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));

View File

@ -0,0 +1,166 @@
/*
* tpl0102.c - Support for Texas Instruments digital potentiometers
*
* Copyright (C) 2016 Matt Ranostay <mranostay@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* TODO: enable/disable hi-z output control
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
struct tpl0102_cfg {
int wipers;
int max_pos;
int kohms;
};
enum tpl0102_type {
CAT5140_503,
CAT5140_104,
TPL0102_104,
TPL0401_103,
};
static const struct tpl0102_cfg tpl0102_cfg[] = {
/* on-semiconductor parts */
[CAT5140_503] = { .wipers = 1, .max_pos = 256, .kohms = 50, },
[CAT5140_104] = { .wipers = 1, .max_pos = 256, .kohms = 100, },
/* ti parts */
[TPL0102_104] = { .wipers = 2, .max_pos = 256, .kohms = 100 },
[TPL0401_103] = { .wipers = 1, .max_pos = 128, .kohms = 10, },
};
struct tpl0102_data {
struct regmap *regmap;
unsigned long devid;
};
static const struct regmap_config tpl0102_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
};
#define TPL0102_CHANNEL(ch) { \
.type = IIO_RESISTANCE, \
.indexed = 1, \
.output = 1, \
.channel = (ch), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
}
static const struct iio_chan_spec tpl0102_channels[] = {
TPL0102_CHANNEL(0),
TPL0102_CHANNEL(1),
};
static int tpl0102_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct tpl0102_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW: {
int ret = regmap_read(data->regmap, chan->channel, val);
return ret ? ret : IIO_VAL_INT;
}
case IIO_CHAN_INFO_SCALE:
*val = 1000 * tpl0102_cfg[data->devid].kohms;
*val2 = tpl0102_cfg[data->devid].max_pos;
return IIO_VAL_FRACTIONAL;
}
return -EINVAL;
}
static int tpl0102_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct tpl0102_data *data = iio_priv(indio_dev);
if (mask != IIO_CHAN_INFO_RAW)
return -EINVAL;
if (val >= tpl0102_cfg[data->devid].max_pos || val < 0)
return -EINVAL;
return regmap_write(data->regmap, chan->channel, val);
}
static const struct iio_info tpl0102_info = {
.read_raw = tpl0102_read_raw,
.write_raw = tpl0102_write_raw,
.driver_module = THIS_MODULE,
};
static int tpl0102_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct tpl0102_data *data;
struct iio_dev *indio_dev;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_WORD_DATA))
return -ENOTSUPP;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
data->devid = id->driver_data;
data->regmap = devm_regmap_init_i2c(client, &tpl0102_regmap_config);
if (IS_ERR(data->regmap)) {
dev_err(dev, "regmap initialization failed\n");
return PTR_ERR(data->regmap);
}
indio_dev->dev.parent = dev;
indio_dev->info = &tpl0102_info;
indio_dev->channels = tpl0102_channels;
indio_dev->num_channels = tpl0102_cfg[data->devid].wipers;
indio_dev->name = client->name;
return devm_iio_device_register(dev, indio_dev);
}
static const struct i2c_device_id tpl0102_id[] = {
{ "cat5140-503", CAT5140_503 },
{ "cat5140-104", CAT5140_104 },
{ "tpl0102-104", TPL0102_104 },
{ "tpl0401-103", TPL0401_103 },
{}
};
MODULE_DEVICE_TABLE(i2c, tpl0102_id);
static struct i2c_driver tpl0102_driver = {
.driver = {
.name = "tpl0102",
},
.probe = tpl0102_probe,
.id_table = tpl0102_id,
};
module_i2c_driver(tpl0102_driver);
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
MODULE_DESCRIPTION("TPL0102 digital potentiometer");
MODULE_LICENSE("GPL");

View File

@ -10,11 +10,11 @@ config BMP280
depends on I2C
select REGMAP_I2C
help
Say yes here to build support for Bosch Sensortec BMP280
pressure and temperature sensor.
Say yes here to build support for Bosch Sensortec BMP280
pressure and temperature sensor.
To compile this driver as a module, choose M here: the module
will be called bmp280.
To compile this driver as a module, choose M here: the module
will be called bmp280.
config HID_SENSOR_PRESS
depends on HID_SENSOR_HUB
@ -27,18 +27,33 @@ config HID_SENSOR_PRESS
Say yes here to build support for the HID SENSOR
Pressure driver
To compile this driver as a module, choose M here: the module
will be called hid-sensor-press.
To compile this driver as a module, choose M here: the module
will be called hid-sensor-press.
config MPL115
tristate
config MPL115_I2C
tristate "Freescale MPL115A2 pressure sensor driver"
depends on I2C
select MPL115
help
Say yes here to build support for the Freescale MPL115A2
pressure sensor connected via I2C.
To compile this driver as a module, choose M here: the module
will be called mpl115.
To compile this driver as a module, choose M here: the module
will be called mpl115_i2c.
config MPL115_SPI
tristate "Freescale MPL115A1 pressure sensor driver"
depends on SPI_MASTER
select MPL115
help
Say yes here to build support for the Freescale MPL115A1
pressure sensor connected via SPI.
To compile this driver as a module, choose M here: the module
will be called mpl115_spi.
config MPL3115
tristate "Freescale MPL3115A2 pressure sensor driver"
@ -49,11 +64,13 @@ config MPL3115
Say yes here to build support for the Freescale MPL3115A2
pressure sensor / altimeter.
To compile this driver as a module, choose M here: the module
will be called mpl3115.
To compile this driver as a module, choose M here: the module
will be called mpl3115.
config MS5611
tristate "Measurement Specialties MS5611 pressure sensor driver"
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say Y here to build support for the Measurement Specialties
MS5611, MS5607 pressure and temperature sensors.
@ -82,7 +99,7 @@ config MS5611_SPI
config MS5637
tristate "Measurement Specialties MS5637 pressure & temperature sensor"
depends on I2C
select IIO_MS_SENSORS_I2C
select IIO_MS_SENSORS_I2C
help
If you say yes here you get support for the Measurement Specialties
MS5637 pressure and temperature sensor.
@ -128,7 +145,7 @@ config T5403
Say yes here to build support for the EPCOS T5403 pressure sensor
connected via I2C.
To compile this driver as a module, choose M here: the module
will be called t5403.
To compile this driver as a module, choose M here: the module
will be called t5403.
endmenu

View File

@ -6,6 +6,8 @@
obj-$(CONFIG_BMP280) += bmp280.o
obj-$(CONFIG_HID_SENSOR_PRESS) += hid-sensor-press.o
obj-$(CONFIG_MPL115) += mpl115.o
obj-$(CONFIG_MPL115_I2C) += mpl115_i2c.o
obj-$(CONFIG_MPL115_SPI) += mpl115_spi.o
obj-$(CONFIG_MPL3115) += mpl3115.o
obj-$(CONFIG_MS5611) += ms5611_core.o
obj-$(CONFIG_MS5611_I2C) += ms5611_i2c.o

View File

@ -1,5 +1,5 @@
/*
* mpl115.c - Support for Freescale MPL115A2 pressure/temperature sensor
* mpl115.c - Support for Freescale MPL115A pressure/temperature sensor
*
* Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
*
@ -7,17 +7,16 @@
* the GNU General Public License. See the file COPYING in the main
* directory of this archive for more details.
*
* (7-bit I2C slave address 0x60)
*
* TODO: shutdown pin
*
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
#include <linux/delay.h>
#include "mpl115.h"
#define MPL115_PADC 0x00 /* pressure ADC output value, MSB first, 10 bit */
#define MPL115_TADC 0x02 /* temperature ADC output value, MSB first, 10 bit */
#define MPL115_A0 0x04 /* 12 bit integer, 3 bit fraction */
@ -27,16 +26,18 @@
#define MPL115_CONVERT 0x12 /* convert temperature and pressure */
struct mpl115_data {
struct i2c_client *client;
struct device *dev;
struct mutex lock;
s16 a0;
s16 b1, b2;
s16 c12;
const struct mpl115_ops *ops;
};
static int mpl115_request(struct mpl115_data *data)
{
int ret = i2c_smbus_write_byte_data(data->client, MPL115_CONVERT, 0);
int ret = data->ops->write(data->dev, MPL115_CONVERT, 0);
if (ret < 0)
return ret;
@ -57,12 +58,12 @@ static int mpl115_comp_pressure(struct mpl115_data *data, int *val, int *val2)
if (ret < 0)
goto done;
ret = i2c_smbus_read_word_swapped(data->client, MPL115_PADC);
ret = data->ops->read(data->dev, MPL115_PADC);
if (ret < 0)
goto done;
padc = ret >> 6;
ret = i2c_smbus_read_word_swapped(data->client, MPL115_TADC);
ret = data->ops->read(data->dev, MPL115_TADC);
if (ret < 0)
goto done;
tadc = ret >> 6;
@ -90,7 +91,7 @@ static int mpl115_read_temp(struct mpl115_data *data)
ret = mpl115_request(data);
if (ret < 0)
goto done;
ret = i2c_smbus_read_word_swapped(data->client, MPL115_TADC);
ret = data->ops->read(data->dev, MPL115_TADC);
done:
mutex_unlock(&data->lock);
return ret;
@ -145,66 +146,53 @@ static const struct iio_info mpl115_info = {
.driver_module = THIS_MODULE,
};
static int mpl115_probe(struct i2c_client *client,
const struct i2c_device_id *id)
int mpl115_probe(struct device *dev, const char *name,
const struct mpl115_ops *ops)
{
struct mpl115_data *data;
struct iio_dev *indio_dev;
int ret;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
return -ENODEV;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
data->client = client;
data->dev = dev;
data->ops = ops;
mutex_init(&data->lock);
i2c_set_clientdata(client, indio_dev);
indio_dev->info = &mpl115_info;
indio_dev->name = id->name;
indio_dev->dev.parent = &client->dev;
indio_dev->name = name;
indio_dev->dev.parent = dev;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = mpl115_channels;
indio_dev->num_channels = ARRAY_SIZE(mpl115_channels);
ret = i2c_smbus_read_word_swapped(data->client, MPL115_A0);
ret = data->ops->init(data->dev);
if (ret)
return ret;
ret = data->ops->read(data->dev, MPL115_A0);
if (ret < 0)
return ret;
data->a0 = ret;
ret = i2c_smbus_read_word_swapped(data->client, MPL115_B1);
ret = data->ops->read(data->dev, MPL115_B1);
if (ret < 0)
return ret;
data->b1 = ret;
ret = i2c_smbus_read_word_swapped(data->client, MPL115_B2);
ret = data->ops->read(data->dev, MPL115_B2);
if (ret < 0)
return ret;
data->b2 = ret;
ret = i2c_smbus_read_word_swapped(data->client, MPL115_C12);
ret = data->ops->read(data->dev, MPL115_C12);
if (ret < 0)
return ret;
data->c12 = ret;
return devm_iio_device_register(&client->dev, indio_dev);
return devm_iio_device_register(dev, indio_dev);
}
static const struct i2c_device_id mpl115_id[] = {
{ "mpl115", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, mpl115_id);
static struct i2c_driver mpl115_driver = {
.driver = {
.name = "mpl115",
},
.probe = mpl115_probe,
.id_table = mpl115_id,
};
module_i2c_driver(mpl115_driver);
EXPORT_SYMBOL_GPL(mpl115_probe);
MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
MODULE_DESCRIPTION("Freescale MPL115 pressure/temperature driver");

View File

@ -0,0 +1,24 @@
/*
* Freescale MPL115A pressure/temperature sensor
*
* Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
* Copyright (c) 2016 Akinobu Mita <akinobu.mita@gmail.com>
*
* 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.
*/
#ifndef _MPL115_H_
#define _MPL115_H_
struct mpl115_ops {
int (*init)(struct device *);
int (*read)(struct device *, u8);
int (*write)(struct device *, u8, u8);
};
int mpl115_probe(struct device *dev, const char *name,
const struct mpl115_ops *ops);
#endif

View File

@ -0,0 +1,67 @@
/*
* Freescale MPL115A2 pressure/temperature sensor
*
* Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
*
* 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.
*
* (7-bit I2C slave address 0x60)
*
* Datasheet: http://www.nxp.com/files/sensors/doc/data_sheet/MPL115A2.pdf
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include "mpl115.h"
static int mpl115_i2c_init(struct device *dev)
{
return 0;
}
static int mpl115_i2c_read(struct device *dev, u8 address)
{
return i2c_smbus_read_word_swapped(to_i2c_client(dev), address);
}
static int mpl115_i2c_write(struct device *dev, u8 address, u8 value)
{
return i2c_smbus_write_byte_data(to_i2c_client(dev), address, value);
}
static const struct mpl115_ops mpl115_i2c_ops = {
.init = mpl115_i2c_init,
.read = mpl115_i2c_read,
.write = mpl115_i2c_write,
};
static int mpl115_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
return -EOPNOTSUPP;
return mpl115_probe(&client->dev, id->name, &mpl115_i2c_ops);
}
static const struct i2c_device_id mpl115_i2c_id[] = {
{ "mpl115", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, mpl115_i2c_id);
static struct i2c_driver mpl115_i2c_driver = {
.driver = {
.name = "mpl115",
},
.probe = mpl115_i2c_probe,
.id_table = mpl115_i2c_id,
};
module_i2c_driver(mpl115_i2c_driver);
MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
MODULE_DESCRIPTION("Freescale MPL115A2 pressure/temperature driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,106 @@
/*
* Freescale MPL115A1 pressure/temperature sensor
*
* Copyright (c) 2016 Akinobu Mita <akinobu.mita@gmail.com>
*
* 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.
*
* Datasheet: http://www.nxp.com/files/sensors/doc/data_sheet/MPL115A1.pdf
*/
#include <linux/module.h>
#include <linux/spi/spi.h>
#include "mpl115.h"
#define MPL115_SPI_WRITE(address) ((address) << 1)
#define MPL115_SPI_READ(address) (0x80 | (address) << 1)
struct mpl115_spi_buf {
u8 tx[4];
u8 rx[4];
};
static int mpl115_spi_init(struct device *dev)
{
struct spi_device *spi = to_spi_device(dev);
struct mpl115_spi_buf *buf;
buf = devm_kzalloc(dev, sizeof(*buf), GFP_KERNEL);
if (!buf)
return -ENOMEM;
spi_set_drvdata(spi, buf);
return 0;
}
static int mpl115_spi_read(struct device *dev, u8 address)
{
struct spi_device *spi = to_spi_device(dev);
struct mpl115_spi_buf *buf = spi_get_drvdata(spi);
struct spi_transfer xfer = {
.tx_buf = buf->tx,
.rx_buf = buf->rx,
.len = 4,
};
int ret;
buf->tx[0] = MPL115_SPI_READ(address);
buf->tx[2] = MPL115_SPI_READ(address + 1);
ret = spi_sync_transfer(spi, &xfer, 1);
if (ret)
return ret;
return (buf->rx[1] << 8) | buf->rx[3];
}
static int mpl115_spi_write(struct device *dev, u8 address, u8 value)
{
struct spi_device *spi = to_spi_device(dev);
struct mpl115_spi_buf *buf = spi_get_drvdata(spi);
struct spi_transfer xfer = {
.tx_buf = buf->tx,
.len = 2,
};
buf->tx[0] = MPL115_SPI_WRITE(address);
buf->tx[1] = value;
return spi_sync_transfer(spi, &xfer, 1);
}
static const struct mpl115_ops mpl115_spi_ops = {
.init = mpl115_spi_init,
.read = mpl115_spi_read,
.write = mpl115_spi_write,
};
static int mpl115_spi_probe(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);
return mpl115_probe(&spi->dev, id->name, &mpl115_spi_ops);
}
static const struct spi_device_id mpl115_spi_ids[] = {
{ "mpl115", 0 },
{}
};
MODULE_DEVICE_TABLE(spi, mpl115_spi_ids);
static struct spi_driver mpl115_spi_driver = {
.driver = {
.name = "mpl115",
},
.probe = mpl115_spi_probe,
.id_table = mpl115_spi_ids,
};
module_spi_driver(mpl115_spi_driver);
MODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>");
MODULE_DESCRIPTION("Freescale MPL115A1 pressure/temperature driver");
MODULE_LICENSE("GPL");

View File

@ -51,6 +51,8 @@ struct ms5611_state {
struct ms5611_chip_info *chip_info;
};
int ms5611_probe(struct iio_dev *indio_dev, struct device *dev, int type);
int ms5611_probe(struct iio_dev *indio_dev, struct device *dev,
const char* name, int type);
int ms5611_remove(struct iio_dev *indio_dev);
#endif /* _MS5611_H */

View File

@ -16,7 +16,11 @@
#include <linux/module.h>
#include <linux/iio/iio.h>
#include <linux/delay.h>
#include <linux/regulator/consumer.h>
#include <linux/iio/buffer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
#include "ms5611.h"
static bool ms5611_prom_is_valid(u16 *prom, size_t len)
@ -133,17 +137,17 @@ static int ms5607_temp_and_pressure_compensate(struct ms5611_chip_info *chip_inf
t = 2000 + ((chip_info->prom[6] * dt) >> 23);
if (t < 2000) {
s64 off2, sens2, t2;
s64 off2, sens2, t2, tmp;
t2 = (dt * dt) >> 31;
off2 = (61 * (t - 2000) * (t - 2000)) >> 4;
sens2 = off2 << 1;
tmp = (t - 2000) * (t - 2000);
off2 = (61 * tmp) >> 4;
sens2 = tmp << 1;
if (t < -1500) {
s64 tmp = (t + 1500) * (t + 1500);
tmp = (t + 1500) * (t + 1500);
off2 += 15 * tmp;
sens2 += (8 * tmp);
sens2 += 8 * tmp;
}
t -= t2;
@ -173,6 +177,28 @@ static int ms5611_reset(struct iio_dev *indio_dev)
return 0;
}
static irqreturn_t ms5611_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct ms5611_state *st = iio_priv(indio_dev);
s32 buf[4]; /* s32 (pressure) + s32 (temp) + 2 * s32 (timestamp) */
int ret;
mutex_lock(&st->lock);
ret = ms5611_read_temp_and_pressure(indio_dev, &buf[1], &buf[0]);
mutex_unlock(&st->lock);
if (ret < 0)
goto err;
iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns());
err:
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
static int ms5611_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
@ -201,11 +227,25 @@ static int ms5611_read_raw(struct iio_dev *indio_dev,
default:
return -EINVAL;
}
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_TEMP:
*val = 10;
return IIO_VAL_INT;
case IIO_PRESSURE:
*val = 0;
*val2 = 1000;
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
}
return -EINVAL;
}
static const unsigned long ms5611_scan_masks[] = {0x3, 0};
static struct ms5611_chip_info chip_info_tbl[] = {
[MS5611] = {
.temp_and_pressure_compensate = ms5611_temp_and_pressure_compensate,
@ -218,12 +258,29 @@ static struct ms5611_chip_info chip_info_tbl[] = {
static const struct iio_chan_spec ms5611_channels[] = {
{
.type = IIO_PRESSURE,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
BIT(IIO_CHAN_INFO_SCALE),
.scan_index = 0,
.scan_type = {
.sign = 's',
.realbits = 32,
.storagebits = 32,
.endianness = IIO_CPU,
},
},
{
.type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
}
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
BIT(IIO_CHAN_INFO_SCALE),
.scan_index = 1,
.scan_type = {
.sign = 's',
.realbits = 32,
.storagebits = 32,
.endianness = IIO_CPU,
},
},
IIO_CHAN_SOFT_TIMESTAMP(2),
};
static const struct iio_info ms5611_info = {
@ -234,6 +291,18 @@ static const struct iio_info ms5611_info = {
static int ms5611_init(struct iio_dev *indio_dev)
{
int ret;
struct regulator *vdd = devm_regulator_get(indio_dev->dev.parent,
"vdd");
/* Enable attached regulator if any. */
if (!IS_ERR(vdd)) {
ret = regulator_enable(vdd);
if (ret) {
dev_err(indio_dev->dev.parent,
"failed to enable Vdd supply: %d\n", ret);
return ret;
}
}
ret = ms5611_reset(indio_dev);
if (ret < 0)
@ -242,7 +311,8 @@ static int ms5611_init(struct iio_dev *indio_dev)
return ms5611_read_prom(indio_dev);
}
int ms5611_probe(struct iio_dev *indio_dev, struct device *dev, int type)
int ms5611_probe(struct iio_dev *indio_dev, struct device *dev,
const char *name, int type)
{
int ret;
struct ms5611_state *st = iio_priv(indio_dev);
@ -250,20 +320,48 @@ int ms5611_probe(struct iio_dev *indio_dev, struct device *dev, int type)
mutex_init(&st->lock);
st->chip_info = &chip_info_tbl[type];
indio_dev->dev.parent = dev;
indio_dev->name = dev->driver->name;
indio_dev->name = name;
indio_dev->info = &ms5611_info;
indio_dev->channels = ms5611_channels;
indio_dev->num_channels = ARRAY_SIZE(ms5611_channels);
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->available_scan_masks = ms5611_scan_masks;
ret = ms5611_init(indio_dev);
if (ret < 0)
return ret;
return devm_iio_device_register(dev, indio_dev);
ret = iio_triggered_buffer_setup(indio_dev, NULL,
ms5611_trigger_handler, NULL);
if (ret < 0) {
dev_err(dev, "iio triggered buffer setup failed\n");
return ret;
}
ret = iio_device_register(indio_dev);
if (ret < 0) {
dev_err(dev, "unable to register iio device\n");
goto err_buffer_cleanup;
}
return 0;
err_buffer_cleanup:
iio_triggered_buffer_cleanup(indio_dev);
return ret;
}
EXPORT_SYMBOL(ms5611_probe);
int ms5611_remove(struct iio_dev *indio_dev)
{
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
return 0;
}
EXPORT_SYMBOL(ms5611_remove);
MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
MODULE_DESCRIPTION("MS5611 core driver");
MODULE_LICENSE("GPL v2");

View File

@ -92,19 +92,25 @@ static int ms5611_i2c_probe(struct i2c_client *client,
I2C_FUNC_SMBUS_WRITE_BYTE |
I2C_FUNC_SMBUS_READ_WORD_DATA |
I2C_FUNC_SMBUS_READ_I2C_BLOCK))
return -ENODEV;
return -EOPNOTSUPP;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
st = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
st->reset = ms5611_i2c_reset;
st->read_prom_word = ms5611_i2c_read_prom_word;
st->read_adc_temp_and_pressure = ms5611_i2c_read_adc_temp_and_pressure;
st->client = client;
return ms5611_probe(indio_dev, &client->dev, id->driver_data);
return ms5611_probe(indio_dev, &client->dev, id->name, id->driver_data);
}
static int ms5611_i2c_remove(struct i2c_client *client)
{
return ms5611_remove(i2c_get_clientdata(client));
}
static const struct i2c_device_id ms5611_id[] = {
@ -120,6 +126,7 @@ static struct i2c_driver ms5611_driver = {
},
.id_table = ms5611_id,
.probe = ms5611_i2c_probe,
.remove = ms5611_i2c_remove,
};
module_i2c_driver(ms5611_driver);

View File

@ -90,6 +90,8 @@ static int ms5611_spi_probe(struct spi_device *spi)
if (!indio_dev)
return -ENOMEM;
spi_set_drvdata(spi, indio_dev);
spi->mode = SPI_MODE_0;
spi->max_speed_hz = 20000000;
spi->bits_per_word = 8;
@ -103,8 +105,13 @@ static int ms5611_spi_probe(struct spi_device *spi)
st->read_adc_temp_and_pressure = ms5611_spi_read_adc_temp_and_pressure;
st->client = spi;
return ms5611_probe(indio_dev, &spi->dev,
spi_get_device_id(spi)->driver_data);
return ms5611_probe(indio_dev, &spi->dev, spi_get_device_id(spi)->name,
spi_get_device_id(spi)->driver_data);
}
static int ms5611_spi_remove(struct spi_device *spi)
{
return ms5611_remove(spi_get_drvdata(spi));
}
static const struct spi_device_id ms5611_id[] = {
@ -120,6 +127,7 @@ static struct spi_driver ms5611_driver = {
},
.id_table = ms5611_id,
.probe = ms5611_spi_probe,
.remove = ms5611_spi_remove,
};
module_spi_driver(ms5611_driver);

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