Staging/IIO patches for 4.9-rc1
Here is the big staging and IIO driver pull request for 4.9-rc1. There are a lot of patches in here, the majority due to the drivers/staging/greybus/ subsystem being merged in with full development history that went back a few years, in order to preserve the work that those developers did over time. This was done the same way that btrfs was merged into the tree, so all should be ok there. Lots and lots of tiny cleanups happened in the tree as well, due to the Outreachy application process and lots of other developers showing up for the first time to clean code up. Along with those changes, we deleted a wireless driver, and added a raspberrypi driver (currently marked broken), and lots of new iio drivers. Overall the tree still shrunk with more lines removed than added, about 10 thousand lines removed in total. Full details are in the very long shortlog below. All of this has been in the linux-next tree with no issues. There will be some merge problems with other subsystem trees, but those are all minor problems and shouldn't be hard to work out when they happen (MAINTAINERS and some lustre build problems with the IB tree.) Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iFYEABECABYFAlf0qWIPHGdyZWdAa3JvYWguY29tAAoJEDFH1A3bLfsp9GkAoLMa Dl/S8W02azDtKP893es5GXh3AJ4k8J9JlHgTS0RlzVJMvzkSZ2x7Vg== =j18s -----END PGP SIGNATURE----- Merge tag 'staging-4.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging Pull staging and IIO updates from Greg KH: "Here is the big staging and IIO driver pull request for 4.9-rc1. There are a lot of patches in here, the majority due to the drivers/staging/greybus/ subsystem being merged in with full development history that went back a few years, in order to preserve the work that those developers did over time. Lots and lots of tiny cleanups happened in the tree as well, due to the Outreachy application process and lots of other developers showing up for the first time to clean code up. Along with those changes, we deleted a wireless driver, and added a raspberrypi driver (currently marked broken), and lots of new iio drivers. Overall the tree still shrunk with more lines removed than added, about 10 thousand lines removed in total. Full details are in the very long shortlog below. All of this has been in the linux-next tree with no issues. There will be some merge problems with other subsystem trees, but those are all minor problems and shouldn't be hard to work out when they happen (MAINTAINERS and some lustre build problems with the IB tree)" And furter from me asking for clarification about greybus: "Right now there is a phone from Motorola shipping with this code (a slightly older version, but the same tree), so even though Ara is not alive in the same form, the functionality is happening. We are working with the developers of that phone to merge the newer stuff in with their fork so they can use the upstream version in future versions of their phone product line. Toshiba has at least one chip shipping in their catalog that needs/uses this protocol over a Unipro link, and rumor has it that there might be more in the future. There are also other users of the greybus protocols, there is a talk next week at ELC that shows how it is being used across a network connection to control a device, and previous ELC talks have showed the protocol stack being used over USB to drive embedded Linux boards. I've also talked to some people who are starting to work to add a host controller driver to control arduinos as the greybus PHY protocols are very useful to control a serial/i2c/spio/whatever device across a random physical link, as it is a way to have a self-describing device be attached to a host without needing manual configuration. So yes, people are using it, and there is still the chance that it will show up in a phone/laptop/tablet/whatever from Google in the future as well, the tech isn't dead, even if the original large phone project happens to be" * tag 'staging-4.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (3703 commits) Staging: fbtft: Fix bug in fbtft-core staging: rtl8188eu: fix double unlock error in rtw_resume_process() staging:r8188eu: remove GEN_MLME_EXT_HANDLER macro staging:r8188eu: remove GEN_DRV_CMD_HANDLER macro staging:r8188eu: remove GEN_EVT_CODE macro staging:r8188eu: remove GEN_CMD_CODE macro staging:r8188eu: remove pkt_newalloc member of the recv_buf structure staging:r8188eu: remove rtw_handle_dualmac declaration staging:r8188eu: remove (RGTRY|BSSID)_(OFT|SZ) macros staging:r8188eu: change rtl8188e_process_phy_info function argument type Staging: fsl-mc: Remove blank lines Staging: fsl-mc: Fix unaligned * in block comments Staging: comedi: Align the * in block comments Staging : ks7010 : Fix block comments warninig Staging: vt6655: Remove explicit NULL comparison using Coccinelle staging: rtl8188eu: core: rtw_xmit: Use macros instead of constants staging: rtl8188eu: core: rtw_xmit: Move constant of the right side staging: dgnc: Fix lines longer than 80 characters Staging: dgnc: constify attribute_group structures Staging: most: hdm-dim2: constify attribute_group structures ...
This commit is contained in:
commit
41844e3620
932 changed files with 82078 additions and 92099 deletions
1
.mailmap
1
.mailmap
|
@ -160,6 +160,7 @@ Valdis Kletnieks <Valdis.Kletnieks@vt.edu>
|
|||
Viresh Kumar <vireshk@kernel.org> <viresh.kumar@st.com>
|
||||
Viresh Kumar <vireshk@kernel.org> <viresh.linux@gmail.com>
|
||||
Viresh Kumar <vireshk@kernel.org> <viresh.kumar2@arm.com>
|
||||
Vlad Dogaru <ddvlad@gmail.com> <vlad.dogaru@intel.com>
|
||||
Vladimir Davydov <vdavydov.dev@gmail.com> <vdavydov@virtuozzo.com>
|
||||
Vladimir Davydov <vdavydov.dev@gmail.com> <vdavydov@parallels.com>
|
||||
Takashi YOSHII <takashi.yoshii.zj@renesas.com>
|
||||
|
|
|
@ -38,6 +38,7 @@ dallas,ds4510 CPU Supervisor with Nonvolatile Memory and Programmable I/O
|
|||
dallas,ds75 Digital Thermometer and Thermostat
|
||||
dlg,da9053 DA9053: flexible system level PMIC with multicore support
|
||||
dlg,da9063 DA9063: system PMIC for quad-core application processors
|
||||
domintech,dmard09 DMARD09: 3-axis Accelerometer
|
||||
epson,rx8010 I2C-BUS INTERFACE REAL TIME CLOCK MODULE
|
||||
epson,rx8025 High-Stability. I2C-Bus INTERFACE REAL TIME CLOCK MODULE
|
||||
epson,rx8581 I2C-BUS INTERFACE REAL TIME CLOCK MODULE
|
||||
|
@ -56,6 +57,7 @@ maxim,ds1050 5 Bit Programmable, Pulse-Width Modulator
|
|||
maxim,max1237 Low-Power, 4-/12-Channel, 2-Wire Serial, 12-Bit ADCs
|
||||
maxim,max6625 9-Bit/12-Bit Temperature Sensors with I²C-Compatible Serial Interface
|
||||
mc,rv3029c2 Real Time Clock Module with I2C-Bus
|
||||
mcube,mc3230 mCube 3-axis 8-bit digital accelerometer
|
||||
microchip,mcp4531-502 Microchip 7-bit Single I2C Digital Potentiometer (5k)
|
||||
microchip,mcp4531-103 Microchip 7-bit Single I2C Digital Potentiometer (10k)
|
||||
microchip,mcp4531-503 Microchip 7-bit Single I2C Digital Potentiometer (50k)
|
||||
|
|
19
Documentation/devicetree/bindings/iio/accel/dmard06.txt
Normal file
19
Documentation/devicetree/bindings/iio/accel/dmard06.txt
Normal file
|
@ -0,0 +1,19 @@
|
|||
Device tree bindings for Domintech DMARD05, DMARD06, DMARD07 accelerometers
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "domintech,dmard05"
|
||||
or "domintech,dmard06"
|
||||
or "domintech,dmard07"
|
||||
- reg : I2C address of the chip. Should be 0x1c
|
||||
|
||||
Example:
|
||||
&i2c1 {
|
||||
/* ... */
|
||||
|
||||
accelerometer@1c {
|
||||
compatible = "domintech,dmard06";
|
||||
reg = <0x1c>;
|
||||
};
|
||||
|
||||
/* ... */
|
||||
};
|
22
Documentation/devicetree/bindings/iio/accel/kionix,kxsd9.txt
Normal file
22
Documentation/devicetree/bindings/iio/accel/kionix,kxsd9.txt
Normal file
|
@ -0,0 +1,22 @@
|
|||
Kionix KXSD9 Accelerometer device tree bindings
|
||||
|
||||
Required properties:
|
||||
- compatible: should be set to "kionix,kxsd9"
|
||||
- reg: i2c slave address
|
||||
|
||||
Optional properties:
|
||||
- vdd-supply: The input supply for VDD
|
||||
- iovdd-supply: The input supply for IOVDD
|
||||
- interrupts: The movement detection interrupt
|
||||
- mount-matrix: See mount-matrix.txt
|
||||
|
||||
Example:
|
||||
|
||||
kxsd9@18 {
|
||||
compatible = "kionix,kxsd9";
|
||||
reg = <0x18>;
|
||||
interrupt-parent = <&foo>;
|
||||
interrupts = <57 IRQ_TYPE_EDGE_FALLING>;
|
||||
iovdd-supply = <&bar>;
|
||||
vdd-supply = <&baz>;
|
||||
};
|
29
Documentation/devicetree/bindings/iio/adc/mt6577_auxadc.txt
Normal file
29
Documentation/devicetree/bindings/iio/adc/mt6577_auxadc.txt
Normal file
|
@ -0,0 +1,29 @@
|
|||
* Mediatek AUXADC - Analog to Digital Converter on Mediatek mobile soc (mt65xx/mt81xx/mt27xx)
|
||||
===============
|
||||
|
||||
The Auxiliary Analog/Digital Converter (AUXADC) is an ADC found
|
||||
in some Mediatek SoCs which among other things measures the temperatures
|
||||
in the SoC. It can be used directly with register accesses, but it is also
|
||||
used by thermal controller which reads the temperatures from the AUXADC
|
||||
directly via its own bus interface. See
|
||||
Documentation/devicetree/bindings/thermal/mediatek-thermal.txt
|
||||
for the Thermal Controller which holds a phandle to the AUXADC.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be one of:
|
||||
- "mediatek,mt2701-auxadc": For MT2701 family of SoCs
|
||||
- "mediatek,mt8173-auxadc": For MT8173 family of SoCs
|
||||
- reg: Address range of the AUXADC unit.
|
||||
- clocks: Should contain a clock specifier for each entry in clock-names
|
||||
- clock-names: Should contain "main".
|
||||
- #io-channel-cells: Should be 1, see ../iio-bindings.txt
|
||||
|
||||
Example:
|
||||
|
||||
auxadc: adc@11001000 {
|
||||
compatible = "mediatek,mt2701-auxadc";
|
||||
reg = <0 0x11001000 0 0x1000>;
|
||||
clocks = <&pericfg CLK_PERI_AUXADC>;
|
||||
clock-names = "main";
|
||||
#io-channel-cells = <1>;
|
||||
};
|
37
Documentation/devicetree/bindings/iio/adc/ti-adc12138.txt
Normal file
37
Documentation/devicetree/bindings/iio/adc/ti-adc12138.txt
Normal file
|
@ -0,0 +1,37 @@
|
|||
* Texas Instruments' ADC12130/ADC12132/ADC12138
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be one of
|
||||
* "ti,adc12130"
|
||||
* "ti,adc12132"
|
||||
* "ti,adc12138"
|
||||
- reg: SPI chip select number for the device
|
||||
- interrupts: Should contain interrupt for EOC (end of conversion)
|
||||
- clocks: phandle to conversion clock input
|
||||
- spi-max-frequency: Definision as per
|
||||
Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
- vref-p-supply: The regulator supply for positive analog voltage reference
|
||||
|
||||
Optional properties:
|
||||
- vref-n-supply: The regulator supply for negative analog voltage reference
|
||||
(Note that this must not go below GND or exceed vref-p)
|
||||
If not specified, this is assumed to be analog ground.
|
||||
- ti,acquisition-time: The number of conversion clock periods for the S/H's
|
||||
acquisition time. Should be one of 6, 10, 18, 34. If not specified,
|
||||
default value of 10 is used.
|
||||
For high source impedances, this value can be increased to 18 or 34.
|
||||
For less ADC accuracy and/or slower CCLK frequencies this value may be
|
||||
decreased to 6. See section 6.0 INPUT SOURCE RESISTANCE in the
|
||||
datasheet for details.
|
||||
|
||||
Example:
|
||||
adc@0 {
|
||||
compatible = "ti,adc12138";
|
||||
reg = <0>;
|
||||
interrupts = <28 IRQ_TYPE_EDGE_RISING>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
clocks = <&cclk>;
|
||||
vref-p-supply = <&ldo4_reg>;
|
||||
spi-max-frequency = <5000000>;
|
||||
ti,acquisition-time = <6>;
|
||||
};
|
16
Documentation/devicetree/bindings/iio/adc/ti-adc161s626.txt
Normal file
16
Documentation/devicetree/bindings/iio/adc/ti-adc161s626.txt
Normal file
|
@ -0,0 +1,16 @@
|
|||
* Texas Instruments ADC141S626 and ADC161S626 chips
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "ti,adc141s626" or "ti,adc161s626"
|
||||
- reg: spi chip select number for the device
|
||||
|
||||
Recommended properties:
|
||||
- spi-max-frequency: Definition as per
|
||||
Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
|
||||
Example:
|
||||
adc@0 {
|
||||
compatible = "ti,adc161s626";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <4300000>;
|
||||
};
|
|
@ -0,0 +1,22 @@
|
|||
* Atlas Scientific ORP-SM OEM sensor
|
||||
|
||||
https://www.atlas-scientific.com/_files/_datasheets/_oem/ORP_oem_datasheet.pdf
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: must be "atlas,orp-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@66 {
|
||||
compatible = "atlas,orp-sm";
|
||||
reg = <0x66>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <16 2>;
|
||||
};
|
|
@ -0,0 +1,29 @@
|
|||
* Asahi Kasei AK8974 magnetometer sensor
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : should be "asahi-kasei,ak8974"
|
||||
- reg : the I2C address of the magnetometer
|
||||
|
||||
Optional properties:
|
||||
|
||||
- avdd-supply: regulator supply for the analog voltage
|
||||
(see regulator/regulator.txt)
|
||||
- dvdd-supply: regulator supply for the digital voltage
|
||||
(see regulator/regulator.txt)
|
||||
- interrupts: data ready (DRDY) and interrupt (INT1) lines
|
||||
from the chip, the DRDY interrupt must be placed first.
|
||||
The interrupts can be triggered on rising or falling
|
||||
edges alike.
|
||||
- mount-matrix: an optional 3x3 mounting rotation matrix
|
||||
|
||||
Example:
|
||||
|
||||
ak8974@0f {
|
||||
compatible = "asahi-kasei,ak8974";
|
||||
reg = <0x0f>;
|
||||
avdd-supply = <&foo_reg>;
|
||||
dvdd-supply = <&bar_reg>;
|
||||
interrupts = <0 IRQ_TYPE_EDGE_RISING>,
|
||||
<1 IRQ_TYPE_EDGE_RISING>;
|
||||
};
|
31
Documentation/devicetree/bindings/iio/pressure/zpa2326.txt
Normal file
31
Documentation/devicetree/bindings/iio/pressure/zpa2326.txt
Normal file
|
@ -0,0 +1,31 @@
|
|||
Murata ZPA2326 pressure sensor
|
||||
|
||||
Pressure sensor from Murata with SPI and I2C bus interfaces.
|
||||
|
||||
Required properties:
|
||||
- compatible: "murata,zpa2326"
|
||||
- reg: the I2C address or SPI chip select the device will respond to
|
||||
|
||||
Recommended properties for SPI bus usage:
|
||||
- spi-max-frequency: maximum SPI bus frequency as documented in
|
||||
Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
|
||||
Optional properties:
|
||||
- vref-supply: an optional regulator that needs to be on to provide VREF
|
||||
power to the sensor
|
||||
- vdd-supply: an optional regulator that needs to be on to provide VDD
|
||||
power to the sensor
|
||||
- interrupt-parent: phandle to the parent interrupt controller as documented in
|
||||
Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
|
||||
- interrupts: interrupt mapping for IRQ as documented in
|
||||
Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
|
||||
|
||||
Example:
|
||||
|
||||
zpa2326@5c {
|
||||
compatible = "murata,zpa2326";
|
||||
reg = <0x5c>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <12>;
|
||||
vdd-supply = <&ldo_1v8_gnss>;
|
||||
};
|
24
Documentation/devicetree/bindings/iio/proximity/sx9500.txt
Normal file
24
Documentation/devicetree/bindings/iio/proximity/sx9500.txt
Normal file
|
@ -0,0 +1,24 @@
|
|||
Semtech's SX9500 capacitive proximity button device driver
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "semtech,sx9500"
|
||||
- reg: i2c address where to find the device
|
||||
- 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.
|
||||
|
||||
Optional properties:
|
||||
- reset-gpios: Reference to the GPIO connected to the device's active
|
||||
low reset pin.
|
||||
|
||||
Example:
|
||||
|
||||
sx9500@28 {
|
||||
compatible = "semtech,sx9500";
|
||||
reg = <0x28>;
|
||||
interrupt-parent = <&gpio2>;
|
||||
interrupts = <16 IRQ_TYPE_LEVEL_LOW>;
|
||||
reset-gpios = <&gpio2 10 GPIO_ACTIVE_LOW>;
|
||||
};
|
|
@ -0,0 +1,21 @@
|
|||
Maxim thermocouple support
|
||||
|
||||
* https://datasheets.maximintegrated.com/en/ds/MAX6675.pdf
|
||||
* https://datasheets.maximintegrated.com/en/ds/MAX31855.pdf
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: must be "maxim,max31855" or "maxim,max6675"
|
||||
- reg: SPI chip select number for the device
|
||||
- spi-max-frequency: must be 4300000
|
||||
- spi-cpha: must be defined for max6675 to enable SPI mode 1
|
||||
|
||||
Refer to spi/spi-bus.txt for generic SPI slave bindings.
|
||||
|
||||
Example:
|
||||
|
||||
max31855@0 {
|
||||
compatible = "maxim,max31855";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <4300000>;
|
||||
};
|
|
@ -1,21 +0,0 @@
|
|||
MediaTek AUXADC
|
||||
===============
|
||||
|
||||
The Auxiliary Analog/Digital Converter (AUXADC) is an ADC found
|
||||
in some Mediatek SoCs which among other things measures the temperatures
|
||||
in the SoC. It can be used directly with register accesses, but it is also
|
||||
used by thermal controller which reads the temperatures from the AUXADC
|
||||
directly via its own bus interface. See
|
||||
Documentation/devicetree/bindings/thermal/mediatek-thermal.txt
|
||||
for the Thermal Controller which holds a phandle to the AUXADC.
|
||||
|
||||
Required properties:
|
||||
- compatible: Must be "mediatek,mt8173-auxadc"
|
||||
- reg: Address range of the AUXADC unit
|
||||
|
||||
Example:
|
||||
|
||||
auxadc: auxadc@11001000 {
|
||||
compatible = "mediatek,mt8173-auxadc";
|
||||
reg = <0 0x11001000 0 0x1000>;
|
||||
};
|
|
@ -76,6 +76,7 @@ digilent Diglent, Inc.
|
|||
dlg Dialog Semiconductor
|
||||
dlink D-Link Corporation
|
||||
dmo Data Modul AG
|
||||
domintech Domintech Co., Ltd.
|
||||
dptechnics DPTechnics
|
||||
dragino Dragino Technology Co., Limited
|
||||
ea Embedded Artists AB
|
||||
|
|
|
@ -266,8 +266,12 @@ IIO
|
|||
devm_iio_device_unregister()
|
||||
devm_iio_kfifo_allocate()
|
||||
devm_iio_kfifo_free()
|
||||
devm_iio_triggered_buffer_setup()
|
||||
devm_iio_triggered_buffer_cleanup()
|
||||
devm_iio_trigger_alloc()
|
||||
devm_iio_trigger_free()
|
||||
devm_iio_trigger_register()
|
||||
devm_iio_trigger_unregister()
|
||||
devm_iio_channel_get()
|
||||
devm_iio_channel_release()
|
||||
devm_iio_channel_get_all()
|
||||
|
|
96
MAINTAINERS
96
MAINTAINERS
|
@ -819,11 +819,11 @@ L: alsa-devel@alsa-project.org (moderated for non-subscribers)
|
|||
S: Maintained
|
||||
F: sound/aoa/
|
||||
|
||||
APEX EMBEDDED SYSTEMS STX104 DAC DRIVER
|
||||
APEX EMBEDDED SYSTEMS STX104 IIO DRIVER
|
||||
M: William Breathitt Gray <vilhelm.gray@gmail.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/iio/dac/stx104.c
|
||||
F: drivers/iio/adc/stx104.c
|
||||
|
||||
APM DRIVER
|
||||
M: Jiri Kosina <jikos@kernel.org>
|
||||
|
@ -1993,6 +1993,13 @@ S: Maintained
|
|||
F: drivers/media/i2c/as3645a.c
|
||||
F: include/media/i2c/as3645a.h
|
||||
|
||||
ASAHI KASEI AK8974 DRIVER
|
||||
M: Linus Walleij <linus.walleij@linaro.org>
|
||||
L: linux-iio@vger.kernel.org
|
||||
W: http://www.akm.com/
|
||||
S: Supported
|
||||
F: drivers/iio/magnetometer/ak8974.c
|
||||
|
||||
ASC7621 HARDWARE MONITOR DRIVER
|
||||
M: George Joseph <george.joseph@fairview5.com>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
|
@ -5318,6 +5325,77 @@ L: netdev@vger.kernel.org
|
|||
S: Maintained
|
||||
F: drivers/net/ethernet/aeroflex/
|
||||
|
||||
GREYBUS SUBSYSTEM
|
||||
M: Johan Hovold <johan@kernel.org>
|
||||
M: Alex Elder <elder@kernel.org>
|
||||
M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
S: Maintained
|
||||
F: drivers/staging/greybus/
|
||||
|
||||
GREYBUS AUDIO PROTOCOLS DRIVERS
|
||||
M: Vaibhav Agarwal <vaibhav.sr@gmail.com>
|
||||
M: Mark Greer <mgreer@animalcreek.com>
|
||||
S: Maintained
|
||||
F: drivers/staging/greybus/audio_apbridgea.c
|
||||
F: drivers/staging/greybus/audio_apbridgea.h
|
||||
F: drivers/staging/greybus/audio_codec.c
|
||||
F: drivers/staging/greybus/audio_codec.h
|
||||
F: drivers/staging/greybus/audio_gb.c
|
||||
F: drivers/staging/greybus/audio_manager.c
|
||||
F: drivers/staging/greybus/audio_manager.h
|
||||
F: drivers/staging/greybus/audio_manager_module.c
|
||||
F: drivers/staging/greybus/audio_manager_private.h
|
||||
F: drivers/staging/greybus/audio_manager_sysfs.c
|
||||
F: drivers/staging/greybus/audio_module.c
|
||||
F: drivers/staging/greybus/audio_topology.c
|
||||
|
||||
GREYBUS PROTOCOLS DRIVERS
|
||||
M: Rui Miguel Silva <rmfrfs@gmail.com>
|
||||
S: Maintained
|
||||
F: drivers/staging/greybus/sdio.c
|
||||
F: drivers/staging/greybus/light.c
|
||||
F: drivers/staging/greybus/gpio.c
|
||||
F: drivers/staging/greybus/power_supply.c
|
||||
F: drivers/staging/greybus/spi.c
|
||||
F: drivers/staging/greybus/spilib.c
|
||||
|
||||
GREYBUS PROTOCOLS DRIVERS
|
||||
M: Bryan O'Donoghue <pure.logic@nexus-software.ie>
|
||||
S: Maintained
|
||||
F: drivers/staging/greybus/loopback.c
|
||||
F: drivers/staging/greybus/timesync.c
|
||||
F: drivers/staging/greybus/timesync_platform.c
|
||||
|
||||
GREYBUS PROTOCOLS DRIVERS
|
||||
M: Viresh Kumar <vireshk@kernel.org>
|
||||
S: Maintained
|
||||
F: drivers/staging/greybus/authentication.c
|
||||
F: drivers/staging/greybus/bootrom.c
|
||||
F: drivers/staging/greybus/firmware.h
|
||||
F: drivers/staging/greybus/fw-core.c
|
||||
F: drivers/staging/greybus/fw-download.c
|
||||
F: drivers/staging/greybus/fw-managament.c
|
||||
F: drivers/staging/greybus/greybus_authentication.h
|
||||
F: drivers/staging/greybus/greybus_firmware.h
|
||||
F: drivers/staging/greybus/hid.c
|
||||
F: drivers/staging/greybus/i2c.c
|
||||
F: drivers/staging/greybus/spi.c
|
||||
F: drivers/staging/greybus/spilib.c
|
||||
F: drivers/staging/greybus/spilib.h
|
||||
|
||||
GREYBUS PROTOCOLS DRIVERS
|
||||
M: David Lin <dtwlin@gmail.com>
|
||||
S: Maintained
|
||||
F: drivers/staging/greybus/uart.c
|
||||
F: drivers/staging/greybus/log.c
|
||||
|
||||
GREYBUS PLATFORM DRIVERS
|
||||
M: Vaibhav Hiremath <hvaibhav.linux@gmail.com>
|
||||
S: Maintained
|
||||
F: drivers/staging/greybus/arche-platform.c
|
||||
F: drivers/staging/greybus/arche-apb-ctrl.c
|
||||
F: drivers/staging/greybus/arche_platform.h
|
||||
|
||||
GSPCA FINEPIX SUBDRIVER
|
||||
M: Frank Zago <frank@zago.net>
|
||||
L: linux-media@vger.kernel.org
|
||||
|
@ -7548,6 +7626,12 @@ L: linux-iio@vger.kernel.org
|
|||
S: Maintained
|
||||
F: drivers/iio/potentiometer/mcp4531.c
|
||||
|
||||
MEASUREMENT COMPUTING CIO-DAC IIO DRIVER
|
||||
M: William Breathitt Gray <vilhelm.gray@gmail.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/iio/dac/cio-dac.c
|
||||
|
||||
MEDIA DRIVERS FOR RENESAS - FCP
|
||||
M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
|
||||
L: linux-media@vger.kernel.org
|
||||
|
@ -11263,6 +11347,7 @@ F: drivers/staging/media/lirc/
|
|||
STAGING - LUSTRE PARALLEL FILESYSTEM
|
||||
M: Oleg Drokin <oleg.drokin@intel.com>
|
||||
M: Andreas Dilger <andreas.dilger@intel.com>
|
||||
M: James Simmons <jsimmons@infradead.org>
|
||||
L: lustre-devel@lists.lustre.org (moderated for non-subscribers)
|
||||
W: http://wiki.lustre.org/
|
||||
S: Maintained
|
||||
|
@ -11289,13 +11374,6 @@ M: Florian Schilhabel <florian.c.schilhabel@googlemail.com>.
|
|||
S: Odd Fixes
|
||||
F: drivers/staging/rtl8712/
|
||||
|
||||
STAGING - REALTEK RTL8723U WIRELESS DRIVER
|
||||
M: Larry Finger <Larry.Finger@lwfinger.net>
|
||||
M: Jes Sorensen <Jes.Sorensen@redhat.com>
|
||||
L: linux-wireless@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/staging/rtl8723au/
|
||||
|
||||
STAGING - SILICON MOTION SM750 FRAME BUFFER DRIVER
|
||||
M: Sudip Mukherjee <sudipm.mukherjee@gmail.com>
|
||||
M: Teddy Wang <teddy.wang@siliconmotion.com>
|
||||
|
|
|
@ -59,7 +59,6 @@ static struct dentry *binder_debugfs_dir_entry_proc;
|
|||
static struct binder_node *binder_context_mgr_node;
|
||||
static kuid_t binder_context_mgr_uid = INVALID_UID;
|
||||
static int binder_last_id;
|
||||
static struct workqueue_struct *binder_deferred_workqueue;
|
||||
|
||||
#define BINDER_DEBUG_ENTRY(name) \
|
||||
static int binder_##name##_open(struct inode *inode, struct file *file) \
|
||||
|
@ -3227,7 +3226,7 @@ binder_defer_work(struct binder_proc *proc, enum binder_deferred_state defer)
|
|||
if (hlist_unhashed(&proc->deferred_work_node)) {
|
||||
hlist_add_head(&proc->deferred_work_node,
|
||||
&binder_deferred_list);
|
||||
queue_work(binder_deferred_workqueue, &binder_deferred_work);
|
||||
schedule_work(&binder_deferred_work);
|
||||
}
|
||||
mutex_unlock(&binder_deferred_lock);
|
||||
}
|
||||
|
@ -3679,10 +3678,6 @@ static int __init binder_init(void)
|
|||
{
|
||||
int ret;
|
||||
|
||||
binder_deferred_workqueue = create_singlethread_workqueue("binder");
|
||||
if (!binder_deferred_workqueue)
|
||||
return -ENOMEM;
|
||||
|
||||
binder_debugfs_dir_entry_root = debugfs_create_dir("binder", NULL);
|
||||
if (binder_debugfs_dir_entry_root)
|
||||
binder_debugfs_dir_entry_proc = debugfs_create_dir("proc",
|
||||
|
|
|
@ -17,4 +17,17 @@ config SYNC_FILE
|
|||
Files fds, to the DRM driver for example. More details at
|
||||
Documentation/sync_file.txt.
|
||||
|
||||
config SW_SYNC
|
||||
bool "Sync File Validation Framework"
|
||||
default n
|
||||
depends on SYNC_FILE
|
||||
depends on DEBUG_FS
|
||||
---help---
|
||||
A sync object driver that uses a 32bit counter to coordinate
|
||||
synchronization. Useful when there is no hardware primitive backing
|
||||
the synchronization.
|
||||
|
||||
WARNING: improper use of this can result in deadlocking kernel
|
||||
drivers from userspace. Intended for test and debug only.
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
obj-y := dma-buf.o fence.o reservation.o seqno-fence.o fence-array.o
|
||||
obj-$(CONFIG_SYNC_FILE) += sync_file.o
|
||||
obj-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* drivers/dma-buf/sw_sync.c
|
||||
* Sync File validation framework
|
||||
*
|
||||
* Copyright (C) 2012 Google, Inc.
|
||||
*
|
||||
|
@ -23,8 +23,38 @@
|
|||
#include "sync_debug.h"
|
||||
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "trace/sync.h"
|
||||
#include "sync_trace.h"
|
||||
|
||||
/*
|
||||
* SW SYNC validation framework
|
||||
*
|
||||
* A sync object driver that uses a 32bit counter to coordinate
|
||||
* synchronization. Useful when there is no hardware primitive backing
|
||||
* the synchronization.
|
||||
*
|
||||
* To start the framework just open:
|
||||
*
|
||||
* <debugfs>/sync/sw_sync
|
||||
*
|
||||
* That will create a sync timeline, all fences created under this timeline
|
||||
* file descriptor will belong to the this timeline.
|
||||
*
|
||||
* The 'sw_sync' file can be opened many times as to create different
|
||||
* timelines.
|
||||
*
|
||||
* Fences can be created with SW_SYNC_IOC_CREATE_FENCE ioctl with struct
|
||||
* sw_sync_ioctl_create_fence as parameter.
|
||||
*
|
||||
* To increment the timeline counter, SW_SYNC_IOC_INC ioctl should be used
|
||||
* with the increment as u32. This will update the last signaled value
|
||||
* from the timeline and signal any fence that has a seqno smaller or equal
|
||||
* to it.
|
||||
*
|
||||
* struct sw_sync_ioctl_create_fence
|
||||
* @value: the seqno to initialise the fence with
|
||||
* @name: the name of the new sync point
|
||||
* @fence: return the fd of the new sync_file with the created fence
|
||||
*/
|
||||
struct sw_sync_create_fence_data {
|
||||
__u32 value;
|
||||
char name[32];
|
||||
|
@ -35,6 +65,7 @@ struct sw_sync_create_fence_data {
|
|||
|
||||
#define SW_SYNC_IOC_CREATE_FENCE _IOWR(SW_SYNC_IOC_MAGIC, 0,\
|
||||
struct sw_sync_create_fence_data)
|
||||
|
||||
#define SW_SYNC_IOC_INC _IOW(SW_SYNC_IOC_MAGIC, 1, __u32)
|
||||
|
||||
static const struct fence_ops timeline_fence_ops;
|
||||
|
@ -176,7 +207,7 @@ static void timeline_fence_release(struct fence *fence)
|
|||
|
||||
spin_lock_irqsave(fence->lock, flags);
|
||||
list_del(&pt->child_list);
|
||||
if (WARN_ON_ONCE(!list_empty(&pt->active_list)))
|
||||
if (!list_empty(&pt->active_list))
|
||||
list_del(&pt->active_list);
|
||||
spin_unlock_irqrestore(fence->lock, flags);
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* drivers/base/sync.c
|
||||
* Sync File validation framework and debug information
|
||||
*
|
||||
* Copyright (C) 2012 Google, Inc.
|
||||
*
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* include/linux/sync.h
|
||||
* Sync File validation framework and debug infomation
|
||||
*
|
||||
* Copyright (C) 2012 Google, Inc.
|
||||
*
|
|
@ -1,11 +1,11 @@
|
|||
#undef TRACE_SYSTEM
|
||||
#define TRACE_INCLUDE_PATH ../../drivers/staging/android/trace
|
||||
#define TRACE_SYSTEM sync
|
||||
#define TRACE_INCLUDE_PATH ../../drivers/dma-buf
|
||||
#define TRACE_SYSTEM sync_trace
|
||||
|
||||
#if !defined(_TRACE_SYNC_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define _TRACE_SYNC_H
|
||||
|
||||
#include "../sync_debug.h"
|
||||
#include "sync_debug.h"
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
TRACE_EVENT(sync_timeline,
|
|
@ -52,6 +52,27 @@ config BMC150_ACCEL_SPI
|
|||
tristate
|
||||
select REGMAP_SPI
|
||||
|
||||
config DMARD06
|
||||
tristate "Domintech DMARD06 Digital Accelerometer Driver"
|
||||
depends on OF || COMPILE_TEST
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to build support for the Domintech low-g tri-axial
|
||||
digital accelerometers: DMARD05, DMARD06, DMARD07.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called dmard06.
|
||||
|
||||
config DMARD09
|
||||
tristate "Domintech DMARD09 3-axis Accelerometer Driver"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to get support for the Domintech DMARD09 3-axis
|
||||
accelerometer.
|
||||
|
||||
Choosing M will build the driver as a module. If so, the module
|
||||
will be called dmard09.
|
||||
|
||||
config HID_SENSOR_ACCEL_3D
|
||||
depends on HID_SENSOR_HUB
|
||||
select IIO_BUFFER
|
||||
|
@ -98,14 +119,35 @@ config IIO_ST_ACCEL_SPI_3AXIS
|
|||
|
||||
config KXSD9
|
||||
tristate "Kionix KXSD9 Accelerometer Driver"
|
||||
depends on SPI
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for the Kionix KXSD9 accelerometer.
|
||||
Currently this only supports the device via an SPI interface.
|
||||
It can be accessed using an (optional) SPI or I2C interface.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called kxsd9.
|
||||
|
||||
config KXSD9_SPI
|
||||
tristate "Kionix KXSD9 SPI transport"
|
||||
depends on KXSD9
|
||||
depends on SPI
|
||||
default KXSD9
|
||||
select REGMAP_SPI
|
||||
help
|
||||
Say yes here to enable the Kionix KXSD9 accelerometer
|
||||
SPI transport channel.
|
||||
|
||||
config KXSD9_I2C
|
||||
tristate "Kionix KXSD9 I2C transport"
|
||||
depends on KXSD9
|
||||
depends on I2C
|
||||
default KXSD9
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say yes here to enable the Kionix KXSD9 accelerometer
|
||||
I2C transport channel.
|
||||
|
||||
config KXCJK1013
|
||||
tristate "Kionix 3-Axis Accelerometer Driver"
|
||||
depends on I2C
|
||||
|
@ -119,6 +161,16 @@ config KXCJK1013
|
|||
To compile this driver as a module, choose M here: the module will
|
||||
be called kxcjk-1013.
|
||||
|
||||
config MC3230
|
||||
tristate "mCube MC3230 Digital Accelerometer Driver"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to build support for the mCube MC3230 low-g tri-axial
|
||||
digital accelerometer.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called mc3230.
|
||||
|
||||
config MMA7455
|
||||
tristate
|
||||
select IIO_BUFFER
|
||||
|
|
|
@ -8,9 +8,14 @@ obj-$(CONFIG_BMA220) += bma220_spi.o
|
|||
obj-$(CONFIG_BMC150_ACCEL) += bmc150-accel-core.o
|
||||
obj-$(CONFIG_BMC150_ACCEL_I2C) += bmc150-accel-i2c.o
|
||||
obj-$(CONFIG_BMC150_ACCEL_SPI) += bmc150-accel-spi.o
|
||||
obj-$(CONFIG_DMARD06) += dmard06.o
|
||||
obj-$(CONFIG_DMARD09) += dmard09.o
|
||||
obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
|
||||
obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o
|
||||
obj-$(CONFIG_KXSD9) += kxsd9.o
|
||||
obj-$(CONFIG_KXSD9_SPI) += kxsd9-spi.o
|
||||
obj-$(CONFIG_KXSD9_I2C) += kxsd9-i2c.o
|
||||
obj-$(CONFIG_MC3230) += mc3230.o
|
||||
|
||||
obj-$(CONFIG_MMA7455) += mma7455_core.o
|
||||
obj-$(CONFIG_MMA7455_I2C) += mma7455_i2c.o
|
||||
|
|
|
@ -469,13 +469,14 @@ static int bma180_read_raw(struct iio_dev *indio_dev,
|
|||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
if (iio_buffer_enabled(indio_dev)) {
|
||||
mutex_unlock(&data->mutex);
|
||||
return -EBUSY;
|
||||
}
|
||||
ret = bma180_get_data_reg(data, chan->scan_index);
|
||||
mutex_unlock(&data->mutex);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = sign_extend32(ret >> chan->scan_type.shift,
|
||||
|
|
241
drivers/iio/accel/dmard06.c
Normal file
241
drivers/iio/accel/dmard06.c
Normal file
|
@ -0,0 +1,241 @@
|
|||
/*
|
||||
* IIO driver for Domintech DMARD06 accelerometer
|
||||
*
|
||||
* Copyright (C) 2016 Aleksei Mamlin <mamlinav@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#define DMARD06_DRV_NAME "dmard06"
|
||||
|
||||
/* Device data registers */
|
||||
#define DMARD06_CHIP_ID_REG 0x0f
|
||||
#define DMARD06_TOUT_REG 0x40
|
||||
#define DMARD06_XOUT_REG 0x41
|
||||
#define DMARD06_YOUT_REG 0x42
|
||||
#define DMARD06_ZOUT_REG 0x43
|
||||
#define DMARD06_CTRL1_REG 0x44
|
||||
|
||||
/* Device ID value */
|
||||
#define DMARD05_CHIP_ID 0x05
|
||||
#define DMARD06_CHIP_ID 0x06
|
||||
#define DMARD07_CHIP_ID 0x07
|
||||
|
||||
/* Device values */
|
||||
#define DMARD05_AXIS_SCALE_VAL 15625
|
||||
#define DMARD06_AXIS_SCALE_VAL 31250
|
||||
#define DMARD06_TEMP_CENTER_VAL 25
|
||||
#define DMARD06_SIGN_BIT 7
|
||||
|
||||
/* Device power modes */
|
||||
#define DMARD06_MODE_NORMAL 0x27
|
||||
#define DMARD06_MODE_POWERDOWN 0x00
|
||||
|
||||
/* Device channels */
|
||||
#define DMARD06_ACCEL_CHANNEL(_axis, _reg) { \
|
||||
.type = IIO_ACCEL, \
|
||||
.address = _reg, \
|
||||
.channel2 = IIO_MOD_##_axis, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.modified = 1, \
|
||||
}
|
||||
|
||||
#define DMARD06_TEMP_CHANNEL(_reg) { \
|
||||
.type = IIO_TEMP, \
|
||||
.address = _reg, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_OFFSET), \
|
||||
}
|
||||
|
||||
struct dmard06_data {
|
||||
struct i2c_client *client;
|
||||
u8 chip_id;
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec dmard06_channels[] = {
|
||||
DMARD06_ACCEL_CHANNEL(X, DMARD06_XOUT_REG),
|
||||
DMARD06_ACCEL_CHANNEL(Y, DMARD06_YOUT_REG),
|
||||
DMARD06_ACCEL_CHANNEL(Z, DMARD06_ZOUT_REG),
|
||||
DMARD06_TEMP_CHANNEL(DMARD06_TOUT_REG),
|
||||
};
|
||||
|
||||
static int dmard06_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct dmard06_data *dmard06 = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = i2c_smbus_read_byte_data(dmard06->client,
|
||||
chan->address);
|
||||
if (ret < 0) {
|
||||
dev_err(&dmard06->client->dev,
|
||||
"Error reading data: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*val = sign_extend32(ret, DMARD06_SIGN_BIT);
|
||||
|
||||
if (dmard06->chip_id == DMARD06_CHIP_ID)
|
||||
*val = *val >> 1;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_ACCEL:
|
||||
return IIO_VAL_INT;
|
||||
case IIO_TEMP:
|
||||
if (dmard06->chip_id != DMARD06_CHIP_ID)
|
||||
*val = *val / 2;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
switch (chan->type) {
|
||||
case IIO_TEMP:
|
||||
*val = DMARD06_TEMP_CENTER_VAL;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_ACCEL:
|
||||
*val = 0;
|
||||
if (dmard06->chip_id == DMARD06_CHIP_ID)
|
||||
*val2 = DMARD06_AXIS_SCALE_VAL;
|
||||
else
|
||||
*val2 = DMARD05_AXIS_SCALE_VAL;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info dmard06_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = dmard06_read_raw,
|
||||
};
|
||||
|
||||
static int dmard06_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct iio_dev *indio_dev;
|
||||
struct dmard06_data *dmard06;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
||||
dev_err(&client->dev, "I2C check functionality failed\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*dmard06));
|
||||
if (!indio_dev) {
|
||||
dev_err(&client->dev, "Failed to allocate iio device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dmard06 = iio_priv(indio_dev);
|
||||
dmard06->client = client;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(dmard06->client, DMARD06_CHIP_ID_REG);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Error reading chip id: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ret != DMARD05_CHIP_ID && ret != DMARD06_CHIP_ID &&
|
||||
ret != DMARD07_CHIP_ID) {
|
||||
dev_err(&client->dev, "Invalid chip id: %02d\n", ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dmard06->chip_id = ret;
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = DMARD06_DRV_NAME;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = dmard06_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(dmard06_channels);
|
||||
indio_dev->info = &dmard06_info;
|
||||
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int dmard06_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct dmard06_data *dmard06 = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(dmard06->client, DMARD06_CTRL1_REG,
|
||||
DMARD06_MODE_POWERDOWN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dmard06_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct dmard06_data *dmard06 = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(dmard06->client, DMARD06_CTRL1_REG,
|
||||
DMARD06_MODE_NORMAL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(dmard06_pm_ops, dmard06_suspend, dmard06_resume);
|
||||
#define DMARD06_PM_OPS (&dmard06_pm_ops)
|
||||
#else
|
||||
#define DMARD06_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id dmard06_id[] = {
|
||||
{ "dmard05", 0 },
|
||||
{ "dmard06", 0 },
|
||||
{ "dmard07", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, dmard06_id);
|
||||
|
||||
static const struct of_device_id dmard06_of_match[] = {
|
||||
{ .compatible = "domintech,dmard05" },
|
||||
{ .compatible = "domintech,dmard06" },
|
||||
{ .compatible = "domintech,dmard07" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dmard06_of_match);
|
||||
|
||||
static struct i2c_driver dmard06_driver = {
|
||||
.probe = dmard06_probe,
|
||||
.id_table = dmard06_id,
|
||||
.driver = {
|
||||
.name = DMARD06_DRV_NAME,
|
||||
.of_match_table = of_match_ptr(dmard06_of_match),
|
||||
.pm = DMARD06_PM_OPS,
|
||||
},
|
||||
};
|
||||
module_i2c_driver(dmard06_driver);
|
||||
|
||||
MODULE_AUTHOR("Aleksei Mamlin <mamlinav@gmail.com>");
|
||||
MODULE_DESCRIPTION("Domintech DMARD06 accelerometer driver");
|
||||
MODULE_LICENSE("GPL v2");
|
157
drivers/iio/accel/dmard09.c
Normal file
157
drivers/iio/accel/dmard09.c
Normal file
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* IIO driver for the 3-axis accelerometer Domintech DMARD09.
|
||||
*
|
||||
* Copyright (c) 2016, Jelle van der Waa <jelle@vdwaa.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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 <asm/unaligned.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#define DMARD09_DRV_NAME "dmard09"
|
||||
|
||||
#define DMARD09_REG_CHIPID 0x18
|
||||
#define DMARD09_REG_STAT 0x0A
|
||||
#define DMARD09_REG_X 0x0C
|
||||
#define DMARD09_REG_Y 0x0E
|
||||
#define DMARD09_REG_Z 0x10
|
||||
#define DMARD09_CHIPID 0x95
|
||||
|
||||
#define DMARD09_BUF_LEN 8
|
||||
#define DMARD09_AXIS_X 0
|
||||
#define DMARD09_AXIS_Y 1
|
||||
#define DMARD09_AXIS_Z 2
|
||||
#define DMARD09_AXIS_X_OFFSET ((DMARD09_AXIS_X + 1) * 2)
|
||||
#define DMARD09_AXIS_Y_OFFSET ((DMARD09_AXIS_Y + 1 )* 2)
|
||||
#define DMARD09_AXIS_Z_OFFSET ((DMARD09_AXIS_Z + 1) * 2)
|
||||
|
||||
struct dmard09_data {
|
||||
struct i2c_client *client;
|
||||
};
|
||||
|
||||
#define DMARD09_CHANNEL(_axis, offset) { \
|
||||
.type = IIO_ACCEL, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.modified = 1, \
|
||||
.address = offset, \
|
||||
.channel2 = IIO_MOD_##_axis, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec dmard09_channels[] = {
|
||||
DMARD09_CHANNEL(X, DMARD09_AXIS_X_OFFSET),
|
||||
DMARD09_CHANNEL(Y, DMARD09_AXIS_Y_OFFSET),
|
||||
DMARD09_CHANNEL(Z, DMARD09_AXIS_Z_OFFSET),
|
||||
};
|
||||
|
||||
static int dmard09_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct dmard09_data *data = iio_priv(indio_dev);
|
||||
u8 buf[DMARD09_BUF_LEN];
|
||||
int ret;
|
||||
s16 accel;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
/*
|
||||
* Read from the DMAR09_REG_STAT register, since the chip
|
||||
* caches reads from the individual X, Y, Z registers.
|
||||
*/
|
||||
ret = i2c_smbus_read_i2c_block_data(data->client,
|
||||
DMARD09_REG_STAT,
|
||||
DMARD09_BUF_LEN, buf);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error reading reg %d\n",
|
||||
DMARD09_REG_STAT);
|
||||
return ret;
|
||||
}
|
||||
|
||||
accel = get_unaligned_le16(&buf[chan->address]);
|
||||
|
||||
/* Remove lower 3 bits and sign extend */
|
||||
accel <<= 4;
|
||||
accel >>= 7;
|
||||
|
||||
*val = accel;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info dmard09_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = dmard09_read_raw,
|
||||
};
|
||||
|
||||
static int dmard09_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct iio_dev *indio_dev;
|
||||
struct dmard09_data *data;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev) {
|
||||
dev_err(&client->dev, "iio allocation failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->client = client;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(data->client, DMARD09_REG_CHIPID);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Error reading chip id %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ret != DMARD09_CHIPID) {
|
||||
dev_err(&client->dev, "Invalid chip id %d\n", ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = DMARD09_DRV_NAME;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = dmard09_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(dmard09_channels);
|
||||
indio_dev->info = &dmard09_info;
|
||||
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id dmard09_id[] = {
|
||||
{ "dmard09", 0},
|
||||
{ },
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, dmard09_id);
|
||||
|
||||
static struct i2c_driver dmard09_driver = {
|
||||
.driver = {
|
||||
.name = DMARD09_DRV_NAME
|
||||
},
|
||||
.probe = dmard09_probe,
|
||||
.id_table = dmard09_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(dmard09_driver);
|
||||
|
||||
MODULE_AUTHOR("Jelle van der Waa <jelle@vdwaa.nl>");
|
||||
MODULE_DESCRIPTION("DMARD09 3-axis accelerometer driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -1392,6 +1392,7 @@ static const struct acpi_device_id kx_acpi_match[] = {
|
|||
{"KXCJ1013", KXCJK1013},
|
||||
{"KXCJ1008", KXCJ91008},
|
||||
{"KXCJ9000", KXCJ91008},
|
||||
{"KIOX000A", KXCJ91008},
|
||||
{"KXTJ1009", KXTJ21009},
|
||||
{"SMO8500", KXCJ91008},
|
||||
{ },
|
||||
|
|
64
drivers/iio/accel/kxsd9-i2c.c
Normal file
64
drivers/iio/accel/kxsd9-i2c.c
Normal file
|
@ -0,0 +1,64 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "kxsd9.h"
|
||||
|
||||
static int kxsd9_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
static const struct regmap_config config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = 0x0e,
|
||||
};
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = devm_regmap_init_i2c(i2c, &config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&i2c->dev, "Failed to register i2c regmap %d\n",
|
||||
(int)PTR_ERR(regmap));
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
return kxsd9_common_probe(&i2c->dev,
|
||||
regmap,
|
||||
i2c->name);
|
||||
}
|
||||
|
||||
static int kxsd9_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
return kxsd9_common_remove(&client->dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id kxsd9_of_match[] = {
|
||||
{ .compatible = "kionix,kxsd9", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, kxsd9_of_match);
|
||||
#else
|
||||
#define kxsd9_of_match NULL
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id kxsd9_i2c_id[] = {
|
||||
{"kxsd9", 0},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, kxsd9_i2c_id);
|
||||
|
||||
static struct i2c_driver kxsd9_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "kxsd9",
|
||||
.of_match_table = of_match_ptr(kxsd9_of_match),
|
||||
.pm = &kxsd9_dev_pm_ops,
|
||||
},
|
||||
.probe = kxsd9_i2c_probe,
|
||||
.remove = kxsd9_i2c_remove,
|
||||
.id_table = kxsd9_i2c_id,
|
||||
};
|
||||
module_i2c_driver(kxsd9_i2c_driver);
|
56
drivers/iio/accel/kxsd9-spi.c
Normal file
56
drivers/iio/accel/kxsd9-spi.c
Normal file
|
@ -0,0 +1,56 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "kxsd9.h"
|
||||
|
||||
static int kxsd9_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
static const struct regmap_config config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = 0x0e,
|
||||
};
|
||||
struct regmap *regmap;
|
||||
|
||||
spi->mode = SPI_MODE_0;
|
||||
regmap = devm_regmap_init_spi(spi, &config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&spi->dev, "%s: regmap allocation failed: %ld\n",
|
||||
__func__, PTR_ERR(regmap));
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
return kxsd9_common_probe(&spi->dev,
|
||||
regmap,
|
||||
spi_get_device_id(spi)->name);
|
||||
}
|
||||
|
||||
static int kxsd9_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
return kxsd9_common_remove(&spi->dev);
|
||||
}
|
||||
|
||||
static const struct spi_device_id kxsd9_spi_id[] = {
|
||||
{"kxsd9", 0},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, kxsd9_spi_id);
|
||||
|
||||
static struct spi_driver kxsd9_spi_driver = {
|
||||
.driver = {
|
||||
.name = "kxsd9",
|
||||
.pm = &kxsd9_dev_pm_ops,
|
||||
},
|
||||
.probe = kxsd9_spi_probe,
|
||||
.remove = kxsd9_spi_remove,
|
||||
.id_table = kxsd9_spi_id,
|
||||
};
|
||||
module_spi_driver(kxsd9_spi_driver);
|
||||
|
||||
MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
|
||||
MODULE_DESCRIPTION("Kionix KXSD9 SPI driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -12,19 +12,25 @@
|
|||
* I have a suitable wire made up.
|
||||
*
|
||||
* TODO: Support the motion detector
|
||||
* Uses register address incrementing so could have a
|
||||
* heavily optimized ring buffer access function.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
|
||||
#include "kxsd9.h"
|
||||
|
||||
#define KXSD9_REG_X 0x00
|
||||
#define KXSD9_REG_Y 0x02
|
||||
|
@ -33,28 +39,45 @@
|
|||
#define KXSD9_REG_RESET 0x0a
|
||||
#define KXSD9_REG_CTRL_C 0x0c
|
||||
|
||||
#define KXSD9_FS_MASK 0x03
|
||||
#define KXSD9_CTRL_C_FS_MASK 0x03
|
||||
#define KXSD9_CTRL_C_FS_8G 0x00
|
||||
#define KXSD9_CTRL_C_FS_6G 0x01
|
||||
#define KXSD9_CTRL_C_FS_4G 0x02
|
||||
#define KXSD9_CTRL_C_FS_2G 0x03
|
||||
#define KXSD9_CTRL_C_MOT_LAT BIT(3)
|
||||
#define KXSD9_CTRL_C_MOT_LEV BIT(4)
|
||||
#define KXSD9_CTRL_C_LP_MASK 0xe0
|
||||
#define KXSD9_CTRL_C_LP_NONE 0x00
|
||||
#define KXSD9_CTRL_C_LP_2000HZC BIT(5)
|
||||
#define KXSD9_CTRL_C_LP_2000HZB BIT(6)
|
||||
#define KXSD9_CTRL_C_LP_2000HZA (BIT(5)|BIT(6))
|
||||
#define KXSD9_CTRL_C_LP_1000HZ BIT(7)
|
||||
#define KXSD9_CTRL_C_LP_500HZ (BIT(7)|BIT(5))
|
||||
#define KXSD9_CTRL_C_LP_100HZ (BIT(7)|BIT(6))
|
||||
#define KXSD9_CTRL_C_LP_50HZ (BIT(7)|BIT(6)|BIT(5))
|
||||
|
||||
#define KXSD9_REG_CTRL_B 0x0d
|
||||
|
||||
#define KXSD9_CTRL_B_CLK_HLD BIT(7)
|
||||
#define KXSD9_CTRL_B_ENABLE BIT(6)
|
||||
#define KXSD9_CTRL_B_ST BIT(5) /* Self-test */
|
||||
|
||||
#define KXSD9_REG_CTRL_A 0x0e
|
||||
|
||||
#define KXSD9_READ(a) (0x80 | (a))
|
||||
#define KXSD9_WRITE(a) (a)
|
||||
|
||||
#define KXSD9_STATE_RX_SIZE 2
|
||||
#define KXSD9_STATE_TX_SIZE 2
|
||||
/**
|
||||
* struct kxsd9_state - device related storage
|
||||
* @buf_lock: protect the rx and tx buffers.
|
||||
* @us: spi device
|
||||
* @rx: single rx buffer storage
|
||||
* @tx: single tx buffer storage
|
||||
**/
|
||||
* @dev: pointer to the parent device
|
||||
* @map: regmap to the device
|
||||
* @orientation: mounting matrix, flipped axis etc
|
||||
* @regs: regulators for this device, VDD and IOVDD
|
||||
* @scale: the current scaling setting
|
||||
*/
|
||||
struct kxsd9_state {
|
||||
struct mutex buf_lock;
|
||||
struct spi_device *us;
|
||||
u8 rx[KXSD9_STATE_RX_SIZE] ____cacheline_aligned;
|
||||
u8 tx[KXSD9_STATE_TX_SIZE];
|
||||
struct device *dev;
|
||||
struct regmap *map;
|
||||
struct iio_mount_matrix orientation;
|
||||
struct regulator_bulk_data regs[2];
|
||||
u8 scale;
|
||||
};
|
||||
|
||||
#define KXSD9_SCALE_2G "0.011978"
|
||||
|
@ -65,6 +88,14 @@ struct kxsd9_state {
|
|||
/* reverse order */
|
||||
static const int kxsd9_micro_scales[4] = { 47853, 35934, 23927, 11978 };
|
||||
|
||||
#define KXSD9_ZERO_G_OFFSET -2048
|
||||
|
||||
/*
|
||||
* Regulator names
|
||||
*/
|
||||
static const char kxsd9_reg_vdd[] = "vdd";
|
||||
static const char kxsd9_reg_iovdd[] = "iovdd";
|
||||
|
||||
static int kxsd9_write_scale(struct iio_dev *indio_dev, int micro)
|
||||
{
|
||||
int ret, i;
|
||||
|
@ -79,42 +110,17 @@ static int kxsd9_write_scale(struct iio_dev *indio_dev, int micro)
|
|||
if (!foundit)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
ret = spi_w8r8(st->us, KXSD9_READ(KXSD9_REG_CTRL_C));
|
||||
ret = regmap_update_bits(st->map,
|
||||
KXSD9_REG_CTRL_C,
|
||||
KXSD9_CTRL_C_FS_MASK,
|
||||
i);
|
||||
if (ret < 0)
|
||||
goto error_ret;
|
||||
st->tx[0] = KXSD9_WRITE(KXSD9_REG_CTRL_C);
|
||||
st->tx[1] = (ret & ~KXSD9_FS_MASK) | i;
|
||||
|
||||
ret = spi_write(st->us, st->tx, 2);
|
||||
/* Cached scale when the sensor is powered down */
|
||||
st->scale = i;
|
||||
|
||||
error_ret:
|
||||
mutex_unlock(&st->buf_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int kxsd9_read(struct iio_dev *indio_dev, u8 address)
|
||||
{
|
||||
int ret;
|
||||
struct kxsd9_state *st = iio_priv(indio_dev);
|
||||
struct spi_transfer xfers[] = {
|
||||
{
|
||||
.bits_per_word = 8,
|
||||
.len = 1,
|
||||
.delay_usecs = 200,
|
||||
.tx_buf = st->tx,
|
||||
}, {
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.rx_buf = st->rx,
|
||||
},
|
||||
};
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
st->tx[0] = KXSD9_READ(address);
|
||||
ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers));
|
||||
if (!ret)
|
||||
ret = (((u16)(st->rx[0])) << 8) | (st->rx[1] & 0xF0);
|
||||
mutex_unlock(&st->buf_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -136,6 +142,9 @@ static int kxsd9_write_raw(struct iio_dev *indio_dev,
|
|||
long mask)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
struct kxsd9_state *st = iio_priv(indio_dev);
|
||||
|
||||
pm_runtime_get_sync(st->dev);
|
||||
|
||||
if (mask == IIO_CHAN_INFO_SCALE) {
|
||||
/* Check no integer component */
|
||||
|
@ -144,6 +153,9 @@ static int kxsd9_write_raw(struct iio_dev *indio_dev,
|
|||
ret = kxsd9_write_scale(indio_dev, val2);
|
||||
}
|
||||
|
||||
pm_runtime_mark_last_busy(st->dev);
|
||||
pm_runtime_put_autosuspend(st->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -153,46 +165,154 @@ static int kxsd9_read_raw(struct iio_dev *indio_dev,
|
|||
{
|
||||
int ret = -EINVAL;
|
||||
struct kxsd9_state *st = iio_priv(indio_dev);
|
||||
unsigned int regval;
|
||||
__be16 raw_val;
|
||||
u16 nval;
|
||||
|
||||
pm_runtime_get_sync(st->dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = kxsd9_read(indio_dev, chan->address);
|
||||
if (ret < 0)
|
||||
ret = regmap_bulk_read(st->map, chan->address, &raw_val,
|
||||
sizeof(raw_val));
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
*val = ret;
|
||||
nval = be16_to_cpu(raw_val);
|
||||
/* Only 12 bits are valid */
|
||||
nval >>= 4;
|
||||
*val = nval;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
/* This has a bias of -2048 */
|
||||
*val = KXSD9_ZERO_G_OFFSET;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = spi_w8r8(st->us, KXSD9_READ(KXSD9_REG_CTRL_C));
|
||||
ret = regmap_read(st->map,
|
||||
KXSD9_REG_CTRL_C,
|
||||
®val);
|
||||
if (ret < 0)
|
||||
goto error_ret;
|
||||
*val = 0;
|
||||
*val2 = kxsd9_micro_scales[ret & KXSD9_FS_MASK];
|
||||
*val2 = kxsd9_micro_scales[regval & KXSD9_CTRL_C_FS_MASK];
|
||||
ret = IIO_VAL_INT_PLUS_MICRO;
|
||||
break;
|
||||
}
|
||||
|
||||
error_ret:
|
||||
pm_runtime_mark_last_busy(st->dev);
|
||||
pm_runtime_put_autosuspend(st->dev);
|
||||
|
||||
return ret;
|
||||
};
|
||||
#define KXSD9_ACCEL_CHAN(axis) \
|
||||
|
||||
static irqreturn_t kxsd9_trigger_handler(int irq, void *p)
|
||||
{
|
||||
const struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct kxsd9_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
/* 4 * 16bit values AND timestamp */
|
||||
__be16 hw_values[8];
|
||||
|
||||
ret = regmap_bulk_read(st->map,
|
||||
KXSD9_REG_X,
|
||||
&hw_values,
|
||||
8);
|
||||
if (ret) {
|
||||
dev_err(st->dev,
|
||||
"error reading data\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev,
|
||||
hw_values,
|
||||
iio_get_time_ns(indio_dev));
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int kxsd9_buffer_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct kxsd9_state *st = iio_priv(indio_dev);
|
||||
|
||||
pm_runtime_get_sync(st->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kxsd9_buffer_postdisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct kxsd9_state *st = iio_priv(indio_dev);
|
||||
|
||||
pm_runtime_mark_last_busy(st->dev);
|
||||
pm_runtime_put_autosuspend(st->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops kxsd9_buffer_setup_ops = {
|
||||
.preenable = kxsd9_buffer_preenable,
|
||||
.postenable = iio_triggered_buffer_postenable,
|
||||
.predisable = iio_triggered_buffer_predisable,
|
||||
.postdisable = kxsd9_buffer_postdisable,
|
||||
};
|
||||
|
||||
static const struct iio_mount_matrix *
|
||||
kxsd9_get_mount_matrix(const struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct kxsd9_state *st = iio_priv(indio_dev);
|
||||
|
||||
return &st->orientation;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec_ext_info kxsd9_ext_info[] = {
|
||||
IIO_MOUNT_MATRIX(IIO_SHARED_BY_TYPE, kxsd9_get_mount_matrix),
|
||||
{ },
|
||||
};
|
||||
|
||||
#define KXSD9_ACCEL_CHAN(axis, index) \
|
||||
{ \
|
||||
.type = IIO_ACCEL, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_##axis, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_OFFSET), \
|
||||
.ext_info = kxsd9_ext_info, \
|
||||
.address = KXSD9_REG_##axis, \
|
||||
.scan_index = index, \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = 12, \
|
||||
.storagebits = 16, \
|
||||
.shift = 4, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec kxsd9_channels[] = {
|
||||
KXSD9_ACCEL_CHAN(X), KXSD9_ACCEL_CHAN(Y), KXSD9_ACCEL_CHAN(Z),
|
||||
KXSD9_ACCEL_CHAN(X, 0),
|
||||
KXSD9_ACCEL_CHAN(Y, 1),
|
||||
KXSD9_ACCEL_CHAN(Z, 2),
|
||||
{
|
||||
.type = IIO_VOLTAGE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.indexed = 1,
|
||||
.address = KXSD9_REG_AUX,
|
||||
}
|
||||
.scan_index = 3,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 12,
|
||||
.storagebits = 16,
|
||||
.shift = 4,
|
||||
.endianness = IIO_BE,
|
||||
},
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4),
|
||||
};
|
||||
|
||||
static const struct attribute_group kxsd9_attribute_group = {
|
||||
|
@ -203,17 +323,69 @@ static int kxsd9_power_up(struct kxsd9_state *st)
|
|||
{
|
||||
int ret;
|
||||
|
||||
st->tx[0] = 0x0d;
|
||||
st->tx[1] = 0x40;
|
||||
ret = spi_write(st->us, st->tx, 2);
|
||||
/* Enable the regulators */
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(st->regs), st->regs);
|
||||
if (ret) {
|
||||
dev_err(st->dev, "Cannot enable regulators\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Power up */
|
||||
ret = regmap_write(st->map,
|
||||
KXSD9_REG_CTRL_B,
|
||||
KXSD9_CTRL_B_ENABLE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
st->tx[0] = 0x0c;
|
||||
st->tx[1] = 0x9b;
|
||||
return spi_write(st->us, st->tx, 2);
|
||||
/*
|
||||
* Set 1000Hz LPF, 2g fullscale, motion wakeup threshold 1g,
|
||||
* latched wakeup
|
||||
*/
|
||||
ret = regmap_write(st->map,
|
||||
KXSD9_REG_CTRL_C,
|
||||
KXSD9_CTRL_C_LP_1000HZ |
|
||||
KXSD9_CTRL_C_MOT_LEV |
|
||||
KXSD9_CTRL_C_MOT_LAT |
|
||||
st->scale);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Power-up time depends on the LPF setting, but typ 15.9 ms, let's
|
||||
* set 20 ms to allow for some slack.
|
||||
*/
|
||||
msleep(20);
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
static int kxsd9_power_down(struct kxsd9_state *st)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Set into low power mode - since there may be more users of the
|
||||
* regulators this is the first step of the power saving: it will
|
||||
* make sure we conserve power even if there are others users on the
|
||||
* regulators.
|
||||
*/
|
||||
ret = regmap_update_bits(st->map,
|
||||
KXSD9_REG_CTRL_B,
|
||||
KXSD9_CTRL_B_ENABLE,
|
||||
0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Disable the regulators */
|
||||
ret = regulator_bulk_disable(ARRAY_SIZE(st->regs), st->regs);
|
||||
if (ret) {
|
||||
dev_err(st->dev, "Cannot disable regulators\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_info kxsd9_info = {
|
||||
.read_raw = &kxsd9_read_raw,
|
||||
.write_raw = &kxsd9_write_raw,
|
||||
|
@ -221,57 +393,136 @@ static const struct iio_info kxsd9_info = {
|
|||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int kxsd9_probe(struct spi_device *spi)
|
||||
/* Four channels apart from timestamp, scan mask = 0x0f */
|
||||
static const unsigned long kxsd9_scan_masks[] = { 0xf, 0 };
|
||||
|
||||
int kxsd9_common_probe(struct device *dev,
|
||||
struct regmap *map,
|
||||
const char *name)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct kxsd9_state *st;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
st->dev = dev;
|
||||
st->map = map;
|
||||
|
||||
st->us = spi;
|
||||
mutex_init(&st->buf_lock);
|
||||
indio_dev->channels = kxsd9_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(kxsd9_channels);
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = name;
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->info = &kxsd9_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->available_scan_masks = kxsd9_scan_masks;
|
||||
|
||||
/* Read the mounting matrix, if present */
|
||||
ret = of_iio_read_mount_matrix(dev,
|
||||
"mount-matrix",
|
||||
&st->orientation);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Fetch and turn on regulators */
|
||||
st->regs[0].supply = kxsd9_reg_vdd;
|
||||
st->regs[1].supply = kxsd9_reg_iovdd;
|
||||
ret = devm_regulator_bulk_get(dev,
|
||||
ARRAY_SIZE(st->regs),
|
||||
st->regs);
|
||||
if (ret) {
|
||||
dev_err(dev, "Cannot get regulators\n");
|
||||
return ret;
|
||||
}
|
||||
/* Default scaling */
|
||||
st->scale = KXSD9_CTRL_C_FS_2G;
|
||||
|
||||
spi->mode = SPI_MODE_0;
|
||||
spi_setup(spi);
|
||||
kxsd9_power_up(st);
|
||||
|
||||
return iio_device_register(indio_dev);
|
||||
}
|
||||
ret = iio_triggered_buffer_setup(indio_dev,
|
||||
iio_pollfunc_store_time,
|
||||
kxsd9_trigger_handler,
|
||||
&kxsd9_buffer_setup_ops);
|
||||
if (ret) {
|
||||
dev_err(dev, "triggered buffer setup failed\n");
|
||||
goto err_power_down;
|
||||
}
|
||||
|
||||
static int kxsd9_remove(struct spi_device *spi)
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto err_cleanup_buffer;
|
||||
|
||||
dev_set_drvdata(dev, indio_dev);
|
||||
|
||||
/* Enable runtime PM */
|
||||
pm_runtime_get_noresume(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
/*
|
||||
* Set autosuspend to two orders of magnitude larger than the
|
||||
* start-up time. 20ms start-up time means 2000ms autosuspend,
|
||||
* i.e. 2 seconds.
|
||||
*/
|
||||
pm_runtime_set_autosuspend_delay(dev, 2000);
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
pm_runtime_put(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_cleanup_buffer:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
err_power_down:
|
||||
kxsd9_power_down(st);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(kxsd9_common_probe);
|
||||
|
||||
int kxsd9_common_remove(struct device *dev)
|
||||
{
|
||||
iio_device_unregister(spi_get_drvdata(spi));
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct kxsd9_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
iio_device_unregister(indio_dev);
|
||||
pm_runtime_get_sync(dev);
|
||||
pm_runtime_put_noidle(dev);
|
||||
pm_runtime_disable(dev);
|
||||
kxsd9_power_down(st);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(kxsd9_common_remove);
|
||||
|
||||
static const struct spi_device_id kxsd9_id[] = {
|
||||
{"kxsd9", 0},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, kxsd9_id);
|
||||
#ifdef CONFIG_PM
|
||||
static int kxsd9_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct kxsd9_state *st = iio_priv(indio_dev);
|
||||
|
||||
static struct spi_driver kxsd9_driver = {
|
||||
.driver = {
|
||||
.name = "kxsd9",
|
||||
},
|
||||
.probe = kxsd9_probe,
|
||||
.remove = kxsd9_remove,
|
||||
.id_table = kxsd9_id,
|
||||
return kxsd9_power_down(st);
|
||||
}
|
||||
|
||||
static int kxsd9_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct kxsd9_state *st = iio_priv(indio_dev);
|
||||
|
||||
return kxsd9_power_up(st);
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
const struct dev_pm_ops kxsd9_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
SET_RUNTIME_PM_OPS(kxsd9_runtime_suspend,
|
||||
kxsd9_runtime_resume, NULL)
|
||||
};
|
||||
module_spi_driver(kxsd9_driver);
|
||||
EXPORT_SYMBOL(kxsd9_dev_pm_ops);
|
||||
|
||||
MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
|
||||
MODULE_DESCRIPTION("Kionix KXSD9 SPI driver");
|
||||
MODULE_DESCRIPTION("Kionix KXSD9 driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
12
drivers/iio/accel/kxsd9.h
Normal file
12
drivers/iio/accel/kxsd9.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#define KXSD9_STATE_RX_SIZE 2
|
||||
#define KXSD9_STATE_TX_SIZE 2
|
||||
|
||||
int kxsd9_common_probe(struct device *dev,
|
||||
struct regmap *map,
|
||||
const char *name);
|
||||
int kxsd9_common_remove(struct device *dev);
|
||||
|
||||
extern const struct dev_pm_ops kxsd9_dev_pm_ops;
|
211
drivers/iio/accel/mc3230.c
Normal file
211
drivers/iio/accel/mc3230.c
Normal file
|
@ -0,0 +1,211 @@
|
|||
/**
|
||||
* mCube MC3230 3-Axis Accelerometer
|
||||
*
|
||||
* Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms 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.
|
||||
*
|
||||
* IIO driver for mCube MC3230; 7-bit I2C address: 0x4c.
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define MC3230_REG_XOUT 0x00
|
||||
#define MC3230_REG_YOUT 0x01
|
||||
#define MC3230_REG_ZOUT 0x02
|
||||
|
||||
#define MC3230_REG_MODE 0x07
|
||||
#define MC3230_MODE_OPCON_MASK 0x03
|
||||
#define MC3230_MODE_OPCON_WAKE 0x01
|
||||
#define MC3230_MODE_OPCON_STANDBY 0x03
|
||||
|
||||
#define MC3230_REG_CHIP_ID 0x18
|
||||
#define MC3230_CHIP_ID 0x01
|
||||
|
||||
#define MC3230_REG_PRODUCT_CODE 0x3b
|
||||
#define MC3230_PRODUCT_CODE 0x19
|
||||
|
||||
/*
|
||||
* The accelerometer has one measurement range:
|
||||
*
|
||||
* -1.5g - +1.5g (8-bit, signed)
|
||||
*
|
||||
* scale = (1.5 + 1.5) * 9.81 / (2^8 - 1) = 0.115411765
|
||||
*/
|
||||
|
||||
static const int mc3230_nscale = 115411765;
|
||||
|
||||
#define MC3230_CHANNEL(reg, axis) { \
|
||||
.type = IIO_ACCEL, \
|
||||
.address = reg, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_##axis, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec mc3230_channels[] = {
|
||||
MC3230_CHANNEL(MC3230_REG_XOUT, X),
|
||||
MC3230_CHANNEL(MC3230_REG_YOUT, Y),
|
||||
MC3230_CHANNEL(MC3230_REG_ZOUT, Z),
|
||||
};
|
||||
|
||||
struct mc3230_data {
|
||||
struct i2c_client *client;
|
||||
};
|
||||
|
||||
static int mc3230_set_opcon(struct mc3230_data *data, int opcon)
|
||||
{
|
||||
int ret;
|
||||
struct i2c_client *client = data->client;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, MC3230_REG_MODE);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "failed to read mode reg: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret &= ~MC3230_MODE_OPCON_MASK;
|
||||
ret |= opcon;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, MC3230_REG_MODE, ret);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "failed to write mode reg: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mc3230_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct mc3230_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = i2c_smbus_read_byte_data(data->client, chan->address);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = sign_extend32(ret, 7);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
*val2 = mc3230_nscale;
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info mc3230_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = mc3230_read_raw,
|
||||
};
|
||||
|
||||
static int mc3230_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct iio_dev *indio_dev;
|
||||
struct mc3230_data *data;
|
||||
|
||||
/* First check chip-id and product-id */
|
||||
ret = i2c_smbus_read_byte_data(client, MC3230_REG_CHIP_ID);
|
||||
if (ret != MC3230_CHIP_ID)
|
||||
return (ret < 0) ? ret : -ENODEV;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, MC3230_REG_PRODUCT_CODE);
|
||||
if (ret != MC3230_PRODUCT_CODE)
|
||||
return (ret < 0) ? ret : -ENODEV;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev) {
|
||||
dev_err(&client->dev, "iio allocation failed!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->client = client;
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &mc3230_info;
|
||||
indio_dev->name = "mc3230";
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = mc3230_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(mc3230_channels);
|
||||
|
||||
ret = mc3230_set_opcon(data, MC3230_MODE_OPCON_WAKE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "device_register failed\n");
|
||||
mc3230_set_opcon(data, MC3230_MODE_OPCON_STANDBY);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mc3230_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
return mc3230_set_opcon(iio_priv(indio_dev), MC3230_MODE_OPCON_STANDBY);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mc3230_suspend(struct device *dev)
|
||||
{
|
||||
struct mc3230_data *data;
|
||||
|
||||
data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
|
||||
|
||||
return mc3230_set_opcon(data, MC3230_MODE_OPCON_STANDBY);
|
||||
}
|
||||
|
||||
static int mc3230_resume(struct device *dev)
|
||||
{
|
||||
struct mc3230_data *data;
|
||||
|
||||
data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
|
||||
|
||||
return mc3230_set_opcon(data, MC3230_MODE_OPCON_WAKE);
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(mc3230_pm_ops, mc3230_suspend, mc3230_resume);
|
||||
|
||||
static const struct i2c_device_id mc3230_i2c_id[] = {
|
||||
{"mc3230", 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mc3230_i2c_id);
|
||||
|
||||
static struct i2c_driver mc3230_driver = {
|
||||
.driver = {
|
||||
.name = "mc3230",
|
||||
.pm = &mc3230_pm_ops,
|
||||
},
|
||||
.probe = mc3230_probe,
|
||||
.remove = mc3230_remove,
|
||||
.id_table = mc3230_i2c_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(mc3230_driver);
|
||||
|
||||
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
|
||||
MODULE_DESCRIPTION("mCube MC3230 3-Axis Accelerometer driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -251,6 +251,7 @@ static const struct i2c_device_id mma7660_i2c_id[] = {
|
|||
{"mma7660", 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mma7660_i2c_id);
|
||||
|
||||
static const struct acpi_device_id mma7660_acpi_id[] = {
|
||||
{"MMA7660", 0},
|
||||
|
|
|
@ -154,7 +154,7 @@ static int mxc6255_probe(struct i2c_client *client,
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (chip_id != MXC6255_CHIP_ID) {
|
||||
if ((chip_id & 0x1f) != MXC6255_CHIP_ID) {
|
||||
dev_err(&client->dev, "Invalid chip id %x\n", chip_id);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
@ -171,12 +171,14 @@ static int mxc6255_probe(struct i2c_client *client,
|
|||
}
|
||||
|
||||
static const struct acpi_device_id mxc6255_acpi_match[] = {
|
||||
{"MXC6225", 0},
|
||||
{"MXC6255", 0},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, mxc6255_acpi_match);
|
||||
|
||||
static const struct i2c_device_id mxc6255_id[] = {
|
||||
{"mxc6225", 0},
|
||||
{"mxc6255", 0},
|
||||
{ }
|
||||
};
|
||||
|
|
|
@ -74,7 +74,7 @@ static int ssp_accel_write_raw(struct iio_dev *indio_dev,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static struct iio_info ssp_accel_iio_info = {
|
||||
static const struct iio_info ssp_accel_iio_info = {
|
||||
.read_raw = &ssp_accel_read_raw,
|
||||
.write_raw = &ssp_accel_write_raw,
|
||||
};
|
||||
|
|
|
@ -264,6 +264,15 @@ config LPC18XX_ADC
|
|||
To compile this driver as a module, choose M here: the module will be
|
||||
called lpc18xx_adc.
|
||||
|
||||
config LTC2485
|
||||
tristate "Linear Technology LTC2485 ADC driver"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to build support for Linear Technology LTC2485 ADC.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called ltc2485.
|
||||
|
||||
config MAX1027
|
||||
tristate "Maxim max1027 ADC driver"
|
||||
depends on SPI
|
||||
|
@ -317,6 +326,19 @@ config MCP3422
|
|||
This driver can also be built as a module. If so, the module will be
|
||||
called mcp3422.
|
||||
|
||||
config MEDIATEK_MT6577_AUXADC
|
||||
tristate "MediaTek AUXADC driver"
|
||||
depends on ARCH_MEDIATEK || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Say yes here to enable support for MediaTek mt65xx AUXADC.
|
||||
|
||||
The driver supports immediate mode operation to read from one of sixteen
|
||||
channels (external or internal).
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called mt6577_auxadc.
|
||||
|
||||
config MEN_Z188_ADC
|
||||
tristate "MEN 16z188 ADC IP Core support"
|
||||
depends on MCB
|
||||
|
@ -397,6 +419,21 @@ config ROCKCHIP_SARADC
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called rockchip_saradc.
|
||||
|
||||
config STX104
|
||||
tristate "Apex Embedded Systems STX104 driver"
|
||||
depends on X86 && ISA_BUS_API
|
||||
select GPIOLIB
|
||||
help
|
||||
Say yes here to build support for the Apex Embedded Systems STX104
|
||||
integrated analog PC/104 card.
|
||||
|
||||
This driver supports the 16 channels of single-ended (8 channels of
|
||||
differential) analog inputs, 2 channels of analog output, 4 digital
|
||||
inputs, and 4 digital outputs provided by the STX104.
|
||||
|
||||
The base port addresses for the devices may be configured via the base
|
||||
array module parameter.
|
||||
|
||||
config TI_ADC081C
|
||||
tristate "Texas Instruments ADC081C/ADC101C/ADC121C family"
|
||||
depends on I2C
|
||||
|
@ -417,6 +454,18 @@ config TI_ADC0832
|
|||
This driver can also be built as a module. If so, the module will be
|
||||
called ti-adc0832.
|
||||
|
||||
config TI_ADC12138
|
||||
tristate "Texas Instruments ADC12130/ADC12132/ADC12138"
|
||||
depends on SPI
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
If you say yes here you get support for Texas Instruments ADC12130,
|
||||
ADC12132 and ADC12138 chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called ti-adc12138.
|
||||
|
||||
config TI_ADC128S052
|
||||
tristate "Texas Instruments ADC128S052/ADC122S021/ADC124S021"
|
||||
depends on SPI
|
||||
|
@ -427,6 +476,18 @@ config TI_ADC128S052
|
|||
This driver can also be built as a module. If so, the module will be
|
||||
called ti-adc128s052.
|
||||
|
||||
config TI_ADC161S626
|
||||
tristate "Texas Instruments ADC161S626 1-channel differential ADC"
|
||||
depends on SPI
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
If you say yes here you get support for Texas Instruments ADC141S626,
|
||||
and ADC161S626 chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called ti-adc161s626.
|
||||
|
||||
config TI_ADS1015
|
||||
tristate "Texas Instruments ADS1015 ADC"
|
||||
depends on I2C && !SENSORS_ADS1015
|
||||
|
|
|
@ -27,10 +27,12 @@ obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
|
|||
obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
|
||||
obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
|
||||
obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o
|
||||
obj-$(CONFIG_LTC2485) += ltc2485.o
|
||||
obj-$(CONFIG_MAX1027) += max1027.o
|
||||
obj-$(CONFIG_MAX1363) += max1363.o
|
||||
obj-$(CONFIG_MCP320X) += mcp320x.o
|
||||
obj-$(CONFIG_MCP3422) += mcp3422.o
|
||||
obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o
|
||||
obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
|
||||
obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o
|
||||
obj-$(CONFIG_NAU7802) += nau7802.o
|
||||
|
@ -38,9 +40,12 @@ 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_STX104) += stx104.o
|
||||
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
|
||||
obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
|
||||
obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
|
||||
obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
|
||||
obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o
|
||||
obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o
|
||||
obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o
|
||||
obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
|
||||
|
|
|
@ -481,7 +481,7 @@ static int ad7266_probe(struct spi_device *spi)
|
|||
if (!st->fixed_addr)
|
||||
gpio_free_array(st->gpios, ARRAY_SIZE(st->gpios));
|
||||
error_disable_reg:
|
||||
if (!IS_ERR_OR_NULL(st->reg))
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return ret;
|
||||
|
@ -496,7 +496,7 @@ static int ad7266_remove(struct spi_device *spi)
|
|||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
if (!st->fixed_addr)
|
||||
gpio_free_array(st->gpios, ARRAY_SIZE(st->gpios));
|
||||
if (!IS_ERR_OR_NULL(st->reg))
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -239,16 +239,16 @@ static int ad7298_read_raw(struct iio_dev *indio_dev,
|
|||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
|
||||
ret = -EBUSY;
|
||||
} else {
|
||||
if (chan->address == AD7298_CH_TEMP)
|
||||
ret = ad7298_scan_temp(st, val);
|
||||
else
|
||||
ret = ad7298_scan_direct(st, chan->address);
|
||||
}
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (chan->address == AD7298_CH_TEMP)
|
||||
ret = ad7298_scan_temp(st, val);
|
||||
else
|
||||
ret = ad7298_scan_direct(st, chan->address);
|
||||
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
|
|
@ -519,11 +519,9 @@ static int ad7793_write_raw(struct iio_dev *indio_dev,
|
|||
int ret, i;
|
||||
unsigned int tmp;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (iio_buffer_enabled(indio_dev)) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return -EBUSY;
|
||||
}
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
|
@ -548,7 +546,7 @@ static int ad7793_write_raw(struct iio_dev *indio_dev,
|
|||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -113,6 +113,7 @@
|
|||
#define AT91_ADC_TSMR_TSAV (3 << 4) /* Averages samples */
|
||||
#define AT91_ADC_TSMR_TSAV_(x) ((x) << 4)
|
||||
#define AT91_ADC_TSMR_SCTIM (0x0f << 16) /* Switch closure time */
|
||||
#define AT91_ADC_TSMR_SCTIM_(x) ((x) << 16)
|
||||
#define AT91_ADC_TSMR_PENDBC (0x0f << 28) /* Pen Debounce time */
|
||||
#define AT91_ADC_TSMR_PENDBC_(x) ((x) << 28)
|
||||
#define AT91_ADC_TSMR_NOTSDMA (1 << 22) /* No Touchscreen DMA */
|
||||
|
@ -150,6 +151,7 @@
|
|||
#define MAX_RLPOS_BITS 10
|
||||
#define TOUCH_SAMPLE_PERIOD_US_RL 10000 /* 10ms, the SoC can't keep up with 2ms */
|
||||
#define TOUCH_SHTIM 0xa
|
||||
#define TOUCH_SCTIM_US 10 /* 10us for the Touchscreen Switches Closure Time */
|
||||
|
||||
/**
|
||||
* struct at91_adc_reg_desc - Various informations relative to registers
|
||||
|
@ -1001,7 +1003,9 @@ static void atmel_ts_close(struct input_dev *dev)
|
|||
|
||||
static int at91_ts_hw_init(struct at91_adc_state *st, u32 adc_clk_khz)
|
||||
{
|
||||
struct iio_dev *idev = iio_priv_to_dev(st);
|
||||
u32 reg = 0;
|
||||
u32 tssctim = 0;
|
||||
int i = 0;
|
||||
|
||||
/* a Pen Detect Debounce Time is necessary for the ADC Touch to avoid
|
||||
|
@ -1034,11 +1038,20 @@ static int at91_ts_hw_init(struct at91_adc_state *st, u32 adc_clk_khz)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Touchscreen Switches Closure time needed for allowing the value to
|
||||
* stabilize.
|
||||
* Switch Closure Time = (TSSCTIM * 4) ADCClock periods
|
||||
*/
|
||||
tssctim = DIV_ROUND_UP(TOUCH_SCTIM_US * adc_clk_khz / 1000, 4);
|
||||
dev_dbg(&idev->dev, "adc_clk at: %d KHz, tssctim at: %d\n",
|
||||
adc_clk_khz, tssctim);
|
||||
|
||||
if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_4WIRE)
|
||||
reg = AT91_ADC_TSMR_TSMODE_4WIRE_PRESS;
|
||||
else
|
||||
reg = AT91_ADC_TSMR_TSMODE_5WIRE;
|
||||
|
||||
reg |= AT91_ADC_TSMR_SCTIM_(tssctim) & AT91_ADC_TSMR_SCTIM;
|
||||
reg |= AT91_ADC_TSMR_TSAV_(st->caps->ts_filter_average)
|
||||
& AT91_ADC_TSMR_TSAV;
|
||||
reg |= AT91_ADC_TSMR_PENDBC_(st->ts_pendbc) & AT91_ADC_TSMR_PENDBC;
|
||||
|
|
|
@ -114,7 +114,6 @@ struct ina2xx_chip_info {
|
|||
struct mutex state_lock;
|
||||
unsigned int shunt_resistor;
|
||||
int avg;
|
||||
s64 prev_ns; /* track buffer capture time, check for underruns */
|
||||
int int_time_vbus; /* Bus voltage integration time uS */
|
||||
int int_time_vshunt; /* Shunt voltage integration time uS */
|
||||
bool allow_async_readout;
|
||||
|
@ -509,8 +508,6 @@ static int ina2xx_work_buffer(struct iio_dev *indio_dev)
|
|||
iio_push_to_buffers_with_timestamp(indio_dev,
|
||||
(unsigned int *)data, time_a);
|
||||
|
||||
chip->prev_ns = time_a;
|
||||
|
||||
return (unsigned long)(time_b - time_a) / 1000;
|
||||
};
|
||||
|
||||
|
@ -554,8 +551,6 @@ static int ina2xx_buffer_enable(struct iio_dev *indio_dev)
|
|||
dev_dbg(&indio_dev->dev, "Async readout mode: %d\n",
|
||||
chip->allow_async_readout);
|
||||
|
||||
chip->prev_ns = iio_get_time_ns(indio_dev);
|
||||
|
||||
chip->task = kthread_run(ina2xx_capture_thread, (void *)indio_dev,
|
||||
"%s:%d-%uus", indio_dev->name, indio_dev->id,
|
||||
sampling_us);
|
||||
|
|
148
drivers/iio/adc/ltc2485.c
Normal file
148
drivers/iio/adc/ltc2485.c
Normal file
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* ltc2485.c - Driver for Linear Technology LTC2485 ADC
|
||||
*
|
||||
* Copyright (C) 2016 Alison Schofield <amsfield22@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Datasheet: http://cds.linear.com/docs/en/datasheet/2485fd.pdf
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
/* Power-on configuration: rejects both 50/60Hz, operates at 1x speed */
|
||||
#define LTC2485_CONFIG_DEFAULT 0
|
||||
|
||||
struct ltc2485_data {
|
||||
struct i2c_client *client;
|
||||
ktime_t time_prev; /* last conversion */
|
||||
};
|
||||
|
||||
static void ltc2485_wait_conv(struct ltc2485_data *data)
|
||||
{
|
||||
const unsigned int conv_time = 147; /* conversion time ms */
|
||||
unsigned int time_elapsed;
|
||||
|
||||
/* delay if conversion time not passed since last read or write */
|
||||
time_elapsed = ktime_ms_delta(ktime_get(), data->time_prev);
|
||||
|
||||
if (time_elapsed < conv_time)
|
||||
msleep(conv_time - time_elapsed);
|
||||
}
|
||||
|
||||
static int ltc2485_read(struct ltc2485_data *data, int *val)
|
||||
{
|
||||
struct i2c_client *client = data->client;
|
||||
__be32 buf = 0;
|
||||
int ret;
|
||||
|
||||
ltc2485_wait_conv(data);
|
||||
|
||||
ret = i2c_master_recv(client, (char *)&buf, 4);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "i2c_master_recv failed\n");
|
||||
return ret;
|
||||
}
|
||||
data->time_prev = ktime_get();
|
||||
*val = sign_extend32(be32_to_cpu(buf) >> 6, 24);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ltc2485_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct ltc2485_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
if (mask == IIO_CHAN_INFO_RAW) {
|
||||
ret = ltc2485_read(data, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
|
||||
} else if (mask == IIO_CHAN_INFO_SCALE) {
|
||||
*val = 5000; /* on board vref millivolts */
|
||||
*val2 = 25; /* 25 (24 + sign) data bits */
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ltc2485_channel[] = {
|
||||
{
|
||||
.type = IIO_VOLTAGE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE)
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_info ltc2485_info = {
|
||||
.read_raw = ltc2485_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int ltc2485_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct ltc2485_data *data;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
|
||||
I2C_FUNC_SMBUS_WRITE_BYTE))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->info = <c2485_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = ltc2485_channel;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ltc2485_channel);
|
||||
|
||||
ret = i2c_smbus_write_byte(data->client, LTC2485_CONFIG_DEFAULT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data->time_prev = ktime_get();
|
||||
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ltc2485_id[] = {
|
||||
{ "ltc2485", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ltc2485_id);
|
||||
|
||||
static struct i2c_driver ltc2485_driver = {
|
||||
.driver = {
|
||||
.name = "ltc2485",
|
||||
},
|
||||
.probe = ltc2485_probe,
|
||||
.id_table = ltc2485_id,
|
||||
};
|
||||
module_i2c_driver(ltc2485_driver);
|
||||
|
||||
MODULE_AUTHOR("Alison Schofield <amsfield22@gmail.com>");
|
||||
MODULE_DESCRIPTION("Linear Technology LTC2485 ADC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -78,7 +78,7 @@ static int z188_iio_read_raw(struct iio_dev *iio_dev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static struct iio_info z188_adc_info = {
|
||||
static const struct iio_info z188_adc_info = {
|
||||
.read_raw = &z188_iio_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
|
291
drivers/iio/adc/mt6577_auxadc.c
Normal file
291
drivers/iio/adc/mt6577_auxadc.c
Normal file
|
@ -0,0 +1,291 @@
|
|||
/*
|
||||
* Copyright (c) 2016 MediaTek Inc.
|
||||
* Author: Zhiyong Tao <zhiyong.tao@mediatek.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/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
/* Register definitions */
|
||||
#define MT6577_AUXADC_CON0 0x00
|
||||
#define MT6577_AUXADC_CON1 0x04
|
||||
#define MT6577_AUXADC_CON2 0x10
|
||||
#define MT6577_AUXADC_STA BIT(0)
|
||||
|
||||
#define MT6577_AUXADC_DAT0 0x14
|
||||
#define MT6577_AUXADC_RDY0 BIT(12)
|
||||
|
||||
#define MT6577_AUXADC_MISC 0x94
|
||||
#define MT6577_AUXADC_PDN_EN BIT(14)
|
||||
|
||||
#define MT6577_AUXADC_DAT_MASK 0xfff
|
||||
#define MT6577_AUXADC_SLEEP_US 1000
|
||||
#define MT6577_AUXADC_TIMEOUT_US 10000
|
||||
#define MT6577_AUXADC_POWER_READY_MS 1
|
||||
#define MT6577_AUXADC_SAMPLE_READY_US 25
|
||||
|
||||
struct mt6577_auxadc_device {
|
||||
void __iomem *reg_base;
|
||||
struct clk *adc_clk;
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
#define MT6577_AUXADC_CHANNEL(idx) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (idx), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec mt6577_auxadc_iio_channels[] = {
|
||||
MT6577_AUXADC_CHANNEL(0),
|
||||
MT6577_AUXADC_CHANNEL(1),
|
||||
MT6577_AUXADC_CHANNEL(2),
|
||||
MT6577_AUXADC_CHANNEL(3),
|
||||
MT6577_AUXADC_CHANNEL(4),
|
||||
MT6577_AUXADC_CHANNEL(5),
|
||||
MT6577_AUXADC_CHANNEL(6),
|
||||
MT6577_AUXADC_CHANNEL(7),
|
||||
MT6577_AUXADC_CHANNEL(8),
|
||||
MT6577_AUXADC_CHANNEL(9),
|
||||
MT6577_AUXADC_CHANNEL(10),
|
||||
MT6577_AUXADC_CHANNEL(11),
|
||||
MT6577_AUXADC_CHANNEL(12),
|
||||
MT6577_AUXADC_CHANNEL(13),
|
||||
MT6577_AUXADC_CHANNEL(14),
|
||||
MT6577_AUXADC_CHANNEL(15),
|
||||
};
|
||||
|
||||
static inline void mt6577_auxadc_mod_reg(void __iomem *reg,
|
||||
u32 or_mask, u32 and_mask)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl(reg);
|
||||
val |= or_mask;
|
||||
val &= ~and_mask;
|
||||
writel(val, reg);
|
||||
}
|
||||
|
||||
static int mt6577_auxadc_read(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan)
|
||||
{
|
||||
u32 val;
|
||||
void __iomem *reg_channel;
|
||||
int ret;
|
||||
struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev);
|
||||
|
||||
reg_channel = adc_dev->reg_base + MT6577_AUXADC_DAT0 +
|
||||
chan->channel * 0x04;
|
||||
|
||||
mutex_lock(&adc_dev->lock);
|
||||
|
||||
mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_CON1,
|
||||
0, 1 << chan->channel);
|
||||
|
||||
/* read channel and make sure old ready bit == 0 */
|
||||
ret = readl_poll_timeout(reg_channel, val,
|
||||
((val & MT6577_AUXADC_RDY0) == 0),
|
||||
MT6577_AUXADC_SLEEP_US,
|
||||
MT6577_AUXADC_TIMEOUT_US);
|
||||
if (ret < 0) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"wait for channel[%d] ready bit clear time out\n",
|
||||
chan->channel);
|
||||
goto err_timeout;
|
||||
}
|
||||
|
||||
/* set bit to trigger sample */
|
||||
mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_CON1,
|
||||
1 << chan->channel, 0);
|
||||
|
||||
/* we must delay here for hardware sample channel data */
|
||||
udelay(MT6577_AUXADC_SAMPLE_READY_US);
|
||||
|
||||
/* check MTK_AUXADC_CON2 if auxadc is idle */
|
||||
ret = readl_poll_timeout(adc_dev->reg_base + MT6577_AUXADC_CON2, val,
|
||||
((val & MT6577_AUXADC_STA) == 0),
|
||||
MT6577_AUXADC_SLEEP_US,
|
||||
MT6577_AUXADC_TIMEOUT_US);
|
||||
if (ret < 0) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"wait for auxadc idle time out\n");
|
||||
goto err_timeout;
|
||||
}
|
||||
|
||||
/* read channel and make sure ready bit == 1 */
|
||||
ret = readl_poll_timeout(reg_channel, val,
|
||||
((val & MT6577_AUXADC_RDY0) != 0),
|
||||
MT6577_AUXADC_SLEEP_US,
|
||||
MT6577_AUXADC_TIMEOUT_US);
|
||||
if (ret < 0) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"wait for channel[%d] data ready time out\n",
|
||||
chan->channel);
|
||||
goto err_timeout;
|
||||
}
|
||||
|
||||
/* read data */
|
||||
val = readl(reg_channel) & MT6577_AUXADC_DAT_MASK;
|
||||
|
||||
mutex_unlock(&adc_dev->lock);
|
||||
|
||||
return val;
|
||||
|
||||
err_timeout:
|
||||
|
||||
mutex_unlock(&adc_dev->lock);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int mt6577_auxadc_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long info)
|
||||
{
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
*val = mt6577_auxadc_read(indio_dev, chan);
|
||||
if (*val < 0) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"failed to sample data on channel[%d]\n",
|
||||
chan->channel);
|
||||
return *val;
|
||||
}
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info mt6577_auxadc_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &mt6577_auxadc_read_raw,
|
||||
};
|
||||
|
||||
static int mt6577_auxadc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mt6577_auxadc_device *adc_dev;
|
||||
unsigned long adc_clk_rate;
|
||||
struct resource *res;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc_dev));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
adc_dev = iio_priv(indio_dev);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->name = dev_name(&pdev->dev);
|
||||
indio_dev->info = &mt6577_auxadc_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = mt6577_auxadc_iio_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(mt6577_auxadc_iio_channels);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
adc_dev->reg_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(adc_dev->reg_base)) {
|
||||
dev_err(&pdev->dev, "failed to get auxadc base address\n");
|
||||
return PTR_ERR(adc_dev->reg_base);
|
||||
}
|
||||
|
||||
adc_dev->adc_clk = devm_clk_get(&pdev->dev, "main");
|
||||
if (IS_ERR(adc_dev->adc_clk)) {
|
||||
dev_err(&pdev->dev, "failed to get auxadc clock\n");
|
||||
return PTR_ERR(adc_dev->adc_clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(adc_dev->adc_clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to enable auxadc clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
adc_clk_rate = clk_get_rate(adc_dev->adc_clk);
|
||||
if (!adc_clk_rate) {
|
||||
ret = -EINVAL;
|
||||
dev_err(&pdev->dev, "null clock rate\n");
|
||||
goto err_disable_clk;
|
||||
}
|
||||
|
||||
mutex_init(&adc_dev->lock);
|
||||
|
||||
mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC,
|
||||
MT6577_AUXADC_PDN_EN, 0);
|
||||
mdelay(MT6577_AUXADC_POWER_READY_MS);
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to register iio device\n");
|
||||
goto err_power_off;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_power_off:
|
||||
mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC,
|
||||
0, MT6577_AUXADC_PDN_EN);
|
||||
err_disable_clk:
|
||||
clk_disable_unprepare(adc_dev->adc_clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mt6577_auxadc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC,
|
||||
0, MT6577_AUXADC_PDN_EN);
|
||||
|
||||
clk_disable_unprepare(adc_dev->adc_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id mt6577_auxadc_of_match[] = {
|
||||
{ .compatible = "mediatek,mt2701-auxadc", },
|
||||
{ .compatible = "mediatek,mt8173-auxadc", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mt6577_auxadc_of_match);
|
||||
|
||||
static struct platform_driver mt6577_auxadc_driver = {
|
||||
.driver = {
|
||||
.name = "mt6577-auxadc",
|
||||
.of_match_table = mt6577_auxadc_of_match,
|
||||
},
|
||||
.probe = mt6577_auxadc_probe,
|
||||
.remove = mt6577_auxadc_remove,
|
||||
};
|
||||
module_platform_driver(mt6577_auxadc_driver);
|
||||
|
||||
MODULE_AUTHOR("Zhiyong Tao <zhiyong.tao@mediatek.com>");
|
||||
MODULE_DESCRIPTION("MTK AUXADC Device Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -197,7 +197,7 @@ static irqreturn_t nau7802_eoc_trigger(int irq, void *private)
|
|||
if (st->conversion_count < NAU7802_MIN_CONVERSIONS)
|
||||
st->conversion_count++;
|
||||
if (st->conversion_count >= NAU7802_MIN_CONVERSIONS)
|
||||
complete_all(&st->value_ok);
|
||||
complete(&st->value_ok);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* DAC driver for the Apex Embedded Systems STX104
|
||||
* IIO 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
|
||||
|
@ -20,19 +20,30 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/isa.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#define STX104_NUM_CHAN 2
|
||||
|
||||
#define STX104_CHAN(chan) { \
|
||||
#define STX104_OUT_CHAN(chan) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.channel = chan, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.indexed = 1, \
|
||||
.output = 1 \
|
||||
}
|
||||
#define STX104_IN_CHAN(chan, diff) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.channel = chan, \
|
||||
.channel2 = chan, \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_HARDWAREGAIN) | \
|
||||
BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.indexed = 1, \
|
||||
.differential = diff \
|
||||
}
|
||||
|
||||
#define STX104_NUM_OUT_CHAN 2
|
||||
|
||||
#define STX104_EXTENT 16
|
||||
|
||||
|
@ -47,8 +58,8 @@ MODULE_PARM_DESC(base, "Apex Embedded Systems STX104 base addresses");
|
|||
* @base: base port address of the IIO device
|
||||
*/
|
||||
struct stx104_iio {
|
||||
unsigned chan_out_states[STX104_NUM_CHAN];
|
||||
unsigned base;
|
||||
unsigned int chan_out_states[STX104_NUM_OUT_CHAN];
|
||||
unsigned int base;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -79,28 +90,95 @@ 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);
|
||||
unsigned int adc_config;
|
||||
int adbu;
|
||||
int gain;
|
||||
|
||||
if (mask != IIO_CHAN_INFO_RAW)
|
||||
return -EINVAL;
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_HARDWAREGAIN:
|
||||
/* get gain configuration */
|
||||
adc_config = inb(priv->base + 11);
|
||||
gain = adc_config & 0x3;
|
||||
|
||||
*val = priv->chan_out_states[chan->channel];
|
||||
*val = 1 << gain;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (chan->output) {
|
||||
*val = priv->chan_out_states[chan->channel];
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
return IIO_VAL_INT;
|
||||
/* select ADC channel */
|
||||
outb(chan->channel | (chan->channel << 4), priv->base + 2);
|
||||
|
||||
/* trigger ADC sample capture and wait for completion */
|
||||
outb(0, priv->base);
|
||||
while (inb(priv->base + 8) & BIT(7));
|
||||
|
||||
*val = inw(priv->base);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
/* get ADC bipolar/unipolar configuration */
|
||||
adc_config = inb(priv->base + 11);
|
||||
adbu = !(adc_config & BIT(2));
|
||||
|
||||
*val = -32768 * adbu;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
/* get ADC bipolar/unipolar and gain configuration */
|
||||
adc_config = inb(priv->base + 11);
|
||||
adbu = !(adc_config & BIT(2));
|
||||
gain = adc_config & 0x3;
|
||||
|
||||
*val = 5;
|
||||
*val2 = 15 - adbu + gain;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
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)
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_HARDWAREGAIN:
|
||||
/* Only four gain states (x1, x2, x4, x8) */
|
||||
switch (val) {
|
||||
case 1:
|
||||
outb(0, priv->base + 11);
|
||||
break;
|
||||
case 2:
|
||||
outb(1, priv->base + 11);
|
||||
break;
|
||||
case 4:
|
||||
outb(2, priv->base + 11);
|
||||
break;
|
||||
case 8:
|
||||
outb(3, priv->base + 11);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (chan->output) {
|
||||
/* DAC can only accept up to a 16-bit value */
|
||||
if ((unsigned int)val > 65535)
|
||||
return -EINVAL;
|
||||
|
||||
priv->chan_out_states[chan->channel] = val;
|
||||
outw(val, priv->base + 4 + 2 * chan->channel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
priv->chan_out_states[chan->channel] = val;
|
||||
outw(val, priv->base + 4 + chan_addr_offset);
|
||||
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info stx104_info = {
|
||||
|
@ -109,9 +187,22 @@ static const struct iio_info stx104_info = {
|
|||
.write_raw = stx104_write_raw
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec stx104_channels[STX104_NUM_CHAN] = {
|
||||
STX104_CHAN(0),
|
||||
STX104_CHAN(1)
|
||||
/* single-ended input channels configuration */
|
||||
static const struct iio_chan_spec stx104_channels_sing[] = {
|
||||
STX104_OUT_CHAN(0), STX104_OUT_CHAN(1),
|
||||
STX104_IN_CHAN(0, 0), STX104_IN_CHAN(1, 0), STX104_IN_CHAN(2, 0),
|
||||
STX104_IN_CHAN(3, 0), STX104_IN_CHAN(4, 0), STX104_IN_CHAN(5, 0),
|
||||
STX104_IN_CHAN(6, 0), STX104_IN_CHAN(7, 0), STX104_IN_CHAN(8, 0),
|
||||
STX104_IN_CHAN(9, 0), STX104_IN_CHAN(10, 0), STX104_IN_CHAN(11, 0),
|
||||
STX104_IN_CHAN(12, 0), STX104_IN_CHAN(13, 0), STX104_IN_CHAN(14, 0),
|
||||
STX104_IN_CHAN(15, 0)
|
||||
};
|
||||
/* differential input channels configuration */
|
||||
static const struct iio_chan_spec stx104_channels_diff[] = {
|
||||
STX104_OUT_CHAN(0), STX104_OUT_CHAN(1),
|
||||
STX104_IN_CHAN(0, 1), STX104_IN_CHAN(1, 1), STX104_IN_CHAN(2, 1),
|
||||
STX104_IN_CHAN(3, 1), STX104_IN_CHAN(4, 1), STX104_IN_CHAN(5, 1),
|
||||
STX104_IN_CHAN(6, 1), STX104_IN_CHAN(7, 1)
|
||||
};
|
||||
|
||||
static int stx104_gpio_get_direction(struct gpio_chip *chip,
|
||||
|
@ -204,13 +295,27 @@ static int stx104_probe(struct device *dev, unsigned int id)
|
|||
|
||||
indio_dev->info = &stx104_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = stx104_channels;
|
||||
indio_dev->num_channels = STX104_NUM_CHAN;
|
||||
|
||||
/* determine if differential inputs */
|
||||
if (inb(base[id] + 8) & BIT(5)) {
|
||||
indio_dev->num_channels = ARRAY_SIZE(stx104_channels_diff);
|
||||
indio_dev->channels = stx104_channels_diff;
|
||||
} else {
|
||||
indio_dev->num_channels = ARRAY_SIZE(stx104_channels_sing);
|
||||
indio_dev->channels = stx104_channels_sing;
|
||||
}
|
||||
|
||||
indio_dev->name = dev_name(dev);
|
||||
|
||||
priv = iio_priv(indio_dev);
|
||||
priv->base = base[id];
|
||||
|
||||
/* configure device for software trigger operation */
|
||||
outb(0, base[id] + 9);
|
||||
|
||||
/* initialize gain setting to x1 */
|
||||
outb(0, base[id] + 11);
|
||||
|
||||
/* initialize DAC output to 0V */
|
||||
outw(0, base[id] + 4);
|
||||
outw(0, base[id] + 6);
|
||||
|
@ -271,5 +376,5 @@ static struct isa_driver stx104_driver = {
|
|||
module_isa_driver(stx104_driver, num_stx104);
|
||||
|
||||
MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
|
||||
MODULE_DESCRIPTION("Apex Embedded Systems STX104 DAC driver");
|
||||
MODULE_DESCRIPTION("Apex Embedded Systems STX104 IIO driver");
|
||||
MODULE_LICENSE("GPL v2");
|
552
drivers/iio/adc/ti-adc12138.c
Normal file
552
drivers/iio/adc/ti-adc12138.c
Normal file
|
@ -0,0 +1,552 @@
|
|||
/*
|
||||
* ADC12130/ADC12132/ADC12138 12-bit plus sign 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/adc12138.pdf
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#define ADC12138_MODE_AUTO_CAL 0x08
|
||||
#define ADC12138_MODE_READ_STATUS 0x0c
|
||||
#define ADC12138_MODE_ACQUISITION_TIME_6 0x0e
|
||||
#define ADC12138_MODE_ACQUISITION_TIME_10 0x4e
|
||||
#define ADC12138_MODE_ACQUISITION_TIME_18 0x8e
|
||||
#define ADC12138_MODE_ACQUISITION_TIME_34 0xce
|
||||
|
||||
#define ADC12138_STATUS_CAL BIT(6)
|
||||
|
||||
enum {
|
||||
adc12130,
|
||||
adc12132,
|
||||
adc12138,
|
||||
};
|
||||
|
||||
struct adc12138 {
|
||||
struct spi_device *spi;
|
||||
unsigned int id;
|
||||
/* conversion clock */
|
||||
struct clk *cclk;
|
||||
/* positive analog voltage reference */
|
||||
struct regulator *vref_p;
|
||||
/* negative analog voltage reference */
|
||||
struct regulator *vref_n;
|
||||
struct mutex lock;
|
||||
struct completion complete;
|
||||
/* The number of cclk periods for the S/H's acquisition time */
|
||||
unsigned int acquisition_time;
|
||||
|
||||
u8 tx_buf[2] ____cacheline_aligned;
|
||||
u8 rx_buf[2];
|
||||
};
|
||||
|
||||
#define ADC12138_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) \
|
||||
| BIT(IIO_CHAN_INFO_OFFSET), \
|
||||
.scan_index = chan, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 13, \
|
||||
.storagebits = 16, \
|
||||
.shift = 3, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define ADC12138_VOLTAGE_CHANNEL_DIFF(chan1, chan2, si) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (chan1), \
|
||||
.channel2 = (chan2), \
|
||||
.differential = 1, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
|
||||
| BIT(IIO_CHAN_INFO_OFFSET), \
|
||||
.scan_index = si, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 13, \
|
||||
.storagebits = 16, \
|
||||
.shift = 3, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec adc12132_channels[] = {
|
||||
ADC12138_VOLTAGE_CHANNEL(0),
|
||||
ADC12138_VOLTAGE_CHANNEL(1),
|
||||
ADC12138_VOLTAGE_CHANNEL_DIFF(0, 1, 2),
|
||||
ADC12138_VOLTAGE_CHANNEL_DIFF(1, 0, 3),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec adc12138_channels[] = {
|
||||
ADC12138_VOLTAGE_CHANNEL(0),
|
||||
ADC12138_VOLTAGE_CHANNEL(1),
|
||||
ADC12138_VOLTAGE_CHANNEL(2),
|
||||
ADC12138_VOLTAGE_CHANNEL(3),
|
||||
ADC12138_VOLTAGE_CHANNEL(4),
|
||||
ADC12138_VOLTAGE_CHANNEL(5),
|
||||
ADC12138_VOLTAGE_CHANNEL(6),
|
||||
ADC12138_VOLTAGE_CHANNEL(7),
|
||||
ADC12138_VOLTAGE_CHANNEL_DIFF(0, 1, 8),
|
||||
ADC12138_VOLTAGE_CHANNEL_DIFF(1, 0, 9),
|
||||
ADC12138_VOLTAGE_CHANNEL_DIFF(2, 3, 10),
|
||||
ADC12138_VOLTAGE_CHANNEL_DIFF(3, 2, 11),
|
||||
ADC12138_VOLTAGE_CHANNEL_DIFF(4, 5, 12),
|
||||
ADC12138_VOLTAGE_CHANNEL_DIFF(5, 4, 13),
|
||||
ADC12138_VOLTAGE_CHANNEL_DIFF(6, 7, 14),
|
||||
ADC12138_VOLTAGE_CHANNEL_DIFF(7, 6, 15),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(16),
|
||||
};
|
||||
|
||||
static int adc12138_mode_programming(struct adc12138 *adc, u8 mode,
|
||||
void *rx_buf, int len)
|
||||
{
|
||||
struct spi_transfer xfer = {
|
||||
.tx_buf = adc->tx_buf,
|
||||
.rx_buf = adc->rx_buf,
|
||||
.len = len,
|
||||
};
|
||||
int ret;
|
||||
|
||||
/* Skip unused bits for ADC12130 and ADC12132 */
|
||||
if (adc->id != adc12138)
|
||||
mode = (mode & 0xc0) | ((mode & 0x0f) << 2);
|
||||
|
||||
adc->tx_buf[0] = mode;
|
||||
|
||||
ret = spi_sync_transfer(adc->spi, &xfer, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
memcpy(rx_buf, adc->rx_buf, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adc12138_read_status(struct adc12138 *adc)
|
||||
{
|
||||
u8 rx_buf[2];
|
||||
int ret;
|
||||
|
||||
ret = adc12138_mode_programming(adc, ADC12138_MODE_READ_STATUS,
|
||||
rx_buf, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return (rx_buf[0] << 1) | (rx_buf[1] >> 7);
|
||||
}
|
||||
|
||||
static int __adc12138_start_conv(struct adc12138 *adc,
|
||||
struct iio_chan_spec const *channel,
|
||||
void *data, int len)
|
||||
|
||||
{
|
||||
const u8 ch_to_mux[] = { 0, 4, 1, 5, 2, 6, 3, 7 };
|
||||
u8 mode = (ch_to_mux[channel->channel] << 4) |
|
||||
(channel->differential ? 0 : 0x80);
|
||||
|
||||
return adc12138_mode_programming(adc, mode, data, len);
|
||||
}
|
||||
|
||||
static int adc12138_start_conv(struct adc12138 *adc,
|
||||
struct iio_chan_spec const *channel)
|
||||
{
|
||||
u8 trash;
|
||||
|
||||
return __adc12138_start_conv(adc, channel, &trash, 1);
|
||||
}
|
||||
|
||||
static int adc12138_start_and_read_conv(struct adc12138 *adc,
|
||||
struct iio_chan_spec const *channel,
|
||||
__be16 *data)
|
||||
{
|
||||
return __adc12138_start_conv(adc, channel, data, 2);
|
||||
}
|
||||
|
||||
static int adc12138_read_conv_data(struct adc12138 *adc, __be16 *value)
|
||||
{
|
||||
/* Issue a read status instruction and read previous conversion data */
|
||||
return adc12138_mode_programming(adc, ADC12138_MODE_READ_STATUS,
|
||||
value, sizeof(*value));
|
||||
}
|
||||
|
||||
static int adc12138_wait_eoc(struct adc12138 *adc, unsigned long timeout)
|
||||
{
|
||||
if (!wait_for_completion_timeout(&adc->complete, timeout))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adc12138_adc_conversion(struct adc12138 *adc,
|
||||
struct iio_chan_spec const *channel,
|
||||
__be16 *value)
|
||||
{
|
||||
int ret;
|
||||
|
||||
reinit_completion(&adc->complete);
|
||||
|
||||
ret = adc12138_start_conv(adc, channel);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adc12138_wait_eoc(adc, msecs_to_jiffies(100));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return adc12138_read_conv_data(adc, value);
|
||||
}
|
||||
|
||||
static int adc12138_read_raw(struct iio_dev *iio,
|
||||
struct iio_chan_spec const *channel, int *value,
|
||||
int *shift, long mask)
|
||||
{
|
||||
struct adc12138 *adc = iio_priv(iio);
|
||||
int ret;
|
||||
__be16 data;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&adc->lock);
|
||||
ret = adc12138_adc_conversion(adc, channel, &data);
|
||||
mutex_unlock(&adc->lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*value = sign_extend32(be16_to_cpu(data) >> 3, 12);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = regulator_get_voltage(adc->vref_p);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*value = ret;
|
||||
|
||||
if (!IS_ERR(adc->vref_n)) {
|
||||
ret = regulator_get_voltage(adc->vref_n);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*value -= ret;
|
||||
}
|
||||
|
||||
/* convert regulator output voltage to mV */
|
||||
*value /= 1000;
|
||||
*shift = channel->scan_type.realbits - 1;
|
||||
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
if (!IS_ERR(adc->vref_n)) {
|
||||
*value = regulator_get_voltage(adc->vref_n);
|
||||
if (*value < 0)
|
||||
return *value;
|
||||
} else {
|
||||
*value = 0;
|
||||
}
|
||||
|
||||
/* convert regulator output voltage to mV */
|
||||
*value /= 1000;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info adc12138_info = {
|
||||
.read_raw = adc12138_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int adc12138_init(struct adc12138 *adc)
|
||||
{
|
||||
int ret;
|
||||
int status;
|
||||
u8 mode;
|
||||
u8 trash;
|
||||
|
||||
reinit_completion(&adc->complete);
|
||||
|
||||
ret = adc12138_mode_programming(adc, ADC12138_MODE_AUTO_CAL, &trash, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* data output at this time has no significance */
|
||||
status = adc12138_read_status(adc);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
adc12138_wait_eoc(adc, msecs_to_jiffies(100));
|
||||
|
||||
status = adc12138_read_status(adc);
|
||||
if (status & ADC12138_STATUS_CAL) {
|
||||
dev_warn(&adc->spi->dev,
|
||||
"Auto Cal sequence is still in progress: %#x\n",
|
||||
status);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
switch (adc->acquisition_time) {
|
||||
case 6:
|
||||
mode = ADC12138_MODE_ACQUISITION_TIME_6;
|
||||
break;
|
||||
case 10:
|
||||
mode = ADC12138_MODE_ACQUISITION_TIME_10;
|
||||
break;
|
||||
case 18:
|
||||
mode = ADC12138_MODE_ACQUISITION_TIME_18;
|
||||
break;
|
||||
case 34:
|
||||
mode = ADC12138_MODE_ACQUISITION_TIME_34;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return adc12138_mode_programming(adc, mode, &trash, 1);
|
||||
}
|
||||
|
||||
static irqreturn_t adc12138_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct adc12138 *adc = iio_priv(indio_dev);
|
||||
__be16 data[20] = { }; /* 16x 2 bytes ADC data + 8 bytes timestamp */
|
||||
__be16 trash;
|
||||
int ret;
|
||||
int scan_index;
|
||||
int i = 0;
|
||||
|
||||
mutex_lock(&adc->lock);
|
||||
|
||||
for_each_set_bit(scan_index, indio_dev->active_scan_mask,
|
||||
indio_dev->masklength) {
|
||||
const struct iio_chan_spec *scan_chan =
|
||||
&indio_dev->channels[scan_index];
|
||||
|
||||
reinit_completion(&adc->complete);
|
||||
|
||||
ret = adc12138_start_and_read_conv(adc, scan_chan,
|
||||
i ? &data[i - 1] : &trash);
|
||||
if (ret) {
|
||||
dev_warn(&adc->spi->dev,
|
||||
"failed to start conversion\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = adc12138_wait_eoc(adc, msecs_to_jiffies(100));
|
||||
if (ret) {
|
||||
dev_warn(&adc->spi->dev, "wait eoc timeout\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i) {
|
||||
ret = adc12138_read_conv_data(adc, &data[i - 1]);
|
||||
if (ret) {
|
||||
dev_warn(&adc->spi->dev,
|
||||
"failed to get conversion data\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data,
|
||||
iio_get_time_ns(indio_dev));
|
||||
out:
|
||||
mutex_unlock(&adc->lock);
|
||||
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t adc12138_eoc_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_dev *indio_dev = p;
|
||||
struct adc12138 *adc = iio_priv(indio_dev);
|
||||
|
||||
complete(&adc->complete);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int adc12138_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct adc12138 *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;
|
||||
adc->id = spi_get_device_id(spi)->driver_data;
|
||||
mutex_init(&adc->lock);
|
||||
init_completion(&adc->complete);
|
||||
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->info = &adc12138_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
switch (adc->id) {
|
||||
case adc12130:
|
||||
case adc12132:
|
||||
indio_dev->channels = adc12132_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(adc12132_channels);
|
||||
break;
|
||||
case adc12138:
|
||||
indio_dev->channels = adc12138_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(adc12138_channels);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(spi->dev.of_node, "ti,acquisition-time",
|
||||
&adc->acquisition_time);
|
||||
if (ret)
|
||||
adc->acquisition_time = 10;
|
||||
|
||||
adc->cclk = devm_clk_get(&spi->dev, NULL);
|
||||
if (IS_ERR(adc->cclk))
|
||||
return PTR_ERR(adc->cclk);
|
||||
|
||||
adc->vref_p = devm_regulator_get(&spi->dev, "vref-p");
|
||||
if (IS_ERR(adc->vref_p))
|
||||
return PTR_ERR(adc->vref_p);
|
||||
|
||||
adc->vref_n = devm_regulator_get_optional(&spi->dev, "vref-n");
|
||||
if (IS_ERR(adc->vref_n)) {
|
||||
/*
|
||||
* Assume vref_n is 0V if an optional regulator is not
|
||||
* specified, otherwise return the error code.
|
||||
*/
|
||||
ret = PTR_ERR(adc->vref_n);
|
||||
if (ret != -ENODEV)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&spi->dev, spi->irq, adc12138_eoc_handler,
|
||||
IRQF_TRIGGER_RISING, indio_dev->name, indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(adc->cclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regulator_enable(adc->vref_p);
|
||||
if (ret)
|
||||
goto err_clk_disable;
|
||||
|
||||
if (!IS_ERR(adc->vref_n)) {
|
||||
ret = regulator_enable(adc->vref_n);
|
||||
if (ret)
|
||||
goto err_vref_p_disable;
|
||||
}
|
||||
|
||||
ret = adc12138_init(adc);
|
||||
if (ret)
|
||||
goto err_vref_n_disable;
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
adc12138_trigger_handler, NULL);
|
||||
if (ret)
|
||||
goto err_vref_n_disable;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto err_buffer_cleanup;
|
||||
|
||||
return 0;
|
||||
err_buffer_cleanup:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
err_vref_n_disable:
|
||||
if (!IS_ERR(adc->vref_n))
|
||||
regulator_disable(adc->vref_n);
|
||||
err_vref_p_disable:
|
||||
regulator_disable(adc->vref_p);
|
||||
err_clk_disable:
|
||||
clk_disable_unprepare(adc->cclk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adc12138_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct adc12138 *adc = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
if (!IS_ERR(adc->vref_n))
|
||||
regulator_disable(adc->vref_n);
|
||||
regulator_disable(adc->vref_p);
|
||||
clk_disable_unprepare(adc->cclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
||||
static const struct of_device_id adc12138_dt_ids[] = {
|
||||
{ .compatible = "ti,adc12130", },
|
||||
{ .compatible = "ti,adc12132", },
|
||||
{ .compatible = "ti,adc12138", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, adc12138_dt_ids);
|
||||
|
||||
#endif
|
||||
|
||||
static const struct spi_device_id adc12138_id[] = {
|
||||
{ "adc12130", adc12130 },
|
||||
{ "adc12132", adc12132 },
|
||||
{ "adc12138", adc12138 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, adc12138_id);
|
||||
|
||||
static struct spi_driver adc12138_driver = {
|
||||
.driver = {
|
||||
.name = "adc12138",
|
||||
.of_match_table = of_match_ptr(adc12138_dt_ids),
|
||||
},
|
||||
.probe = adc12138_probe,
|
||||
.remove = adc12138_remove,
|
||||
.id_table = adc12138_id,
|
||||
};
|
||||
module_spi_driver(adc12138_driver);
|
||||
|
||||
MODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>");
|
||||
MODULE_DESCRIPTION("ADC12130/ADC12132/ADC12138 driver");
|
||||
MODULE_LICENSE("GPL v2");
|
248
drivers/iio/adc/ti-adc161s626.c
Normal file
248
drivers/iio/adc/ti-adc161s626.c
Normal file
|
@ -0,0 +1,248 @@
|
|||
/*
|
||||
* ti-adc161s626.c - Texas Instruments ADC161S626 1-channel differential ADC
|
||||
*
|
||||
* ADC Devices Supported:
|
||||
* adc141s626 - 14-bit ADC
|
||||
* adc161s626 - 16-bit ADC
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#define TI_ADC_DRV_NAME "ti-adc161s626"
|
||||
|
||||
enum {
|
||||
TI_ADC141S626,
|
||||
TI_ADC161S626,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec ti_adc141s626_channels[] = {
|
||||
{
|
||||
.type = IIO_VOLTAGE,
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 14,
|
||||
.storagebits = 16,
|
||||
},
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec ti_adc161s626_channels[] = {
|
||||
{
|
||||
.type = IIO_VOLTAGE,
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
},
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
struct ti_adc_data {
|
||||
struct iio_dev *indio_dev;
|
||||
struct spi_device *spi;
|
||||
u8 read_size;
|
||||
u8 shift;
|
||||
|
||||
u8 buffer[16] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static int ti_adc_read_measurement(struct ti_adc_data *data,
|
||||
struct iio_chan_spec const *chan, int *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (data->read_size) {
|
||||
case 2: {
|
||||
__be16 buf;
|
||||
|
||||
ret = spi_read(data->spi, (void *) &buf, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = be16_to_cpu(buf);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
__be32 buf;
|
||||
|
||||
ret = spi_read(data->spi, (void *) &buf, 3);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = be32_to_cpu(buf) >> 8;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*val = sign_extend32(*val >> data->shift, chan->scan_type.realbits - 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t ti_adc_trigger_handler(int irq, void *private)
|
||||
{
|
||||
struct iio_poll_func *pf = private;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct ti_adc_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = ti_adc_read_measurement(data, &indio_dev->channels[0],
|
||||
(int *) &data->buffer);
|
||||
if (!ret)
|
||||
iio_push_to_buffers_with_timestamp(indio_dev,
|
||||
data->buffer,
|
||||
iio_get_time_ns(indio_dev));
|
||||
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ti_adc_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct ti_adc_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
if (mask != IIO_CHAN_INFO_RAW)
|
||||
return -EINVAL;
|
||||
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ti_adc_read_measurement(data, chan, val);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
|
||||
if (!ret)
|
||||
return IIO_VAL_INT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_info ti_adc_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = ti_adc_read_raw,
|
||||
};
|
||||
|
||||
static int ti_adc_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct ti_adc_data *data;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
indio_dev->info = &ti_adc_info;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->dev.of_node = spi->dev.of_node;
|
||||
indio_dev->name = TI_ADC_DRV_NAME;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->spi = spi;
|
||||
|
||||
switch (spi_get_device_id(spi)->driver_data) {
|
||||
case TI_ADC141S626:
|
||||
indio_dev->channels = ti_adc141s626_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ti_adc141s626_channels);
|
||||
data->shift = 0;
|
||||
data->read_size = 2;
|
||||
break;
|
||||
case TI_ADC161S626:
|
||||
indio_dev->channels = ti_adc161s626_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ti_adc161s626_channels);
|
||||
data->shift = 6;
|
||||
data->read_size = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
ti_adc_trigger_handler, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_unreg_buffer;
|
||||
|
||||
return 0;
|
||||
|
||||
error_unreg_buffer:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ti_adc_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ti_adc_dt_ids[] = {
|
||||
{ .compatible = "ti,adc141s626", },
|
||||
{ .compatible = "ti,adc161s626", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ti_adc_dt_ids);
|
||||
|
||||
static const struct spi_device_id ti_adc_id[] = {
|
||||
{"adc141s626", TI_ADC141S626},
|
||||
{"adc161s626", TI_ADC161S626},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ti_adc_id);
|
||||
|
||||
static struct spi_driver ti_adc_driver = {
|
||||
.driver = {
|
||||
.name = TI_ADC_DRV_NAME,
|
||||
.of_match_table = of_match_ptr(ti_adc_dt_ids),
|
||||
},
|
||||
.probe = ti_adc_probe,
|
||||
.remove = ti_adc_remove,
|
||||
.id_table = ti_adc_id,
|
||||
};
|
||||
module_spi_driver(ti_adc_driver);
|
||||
|
||||
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
|
||||
MODULE_DESCRIPTION("Texas Instruments ADC1x1S 1-channel differential ADC");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -522,6 +522,7 @@ static int ads1015_get_channels_config_of(struct i2c_client *client)
|
|||
if (pga > 6) {
|
||||
dev_err(&client->dev, "invalid gain on %s\n",
|
||||
node->full_name);
|
||||
of_node_put(node);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
@ -532,6 +533,7 @@ static int ads1015_get_channels_config_of(struct i2c_client *client)
|
|||
dev_err(&client->dev,
|
||||
"invalid data_rate on %s\n",
|
||||
node->full_name);
|
||||
of_node_put(node);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -438,7 +438,7 @@ static int ads8688_probe(struct spi_device *spi)
|
|||
return 0;
|
||||
|
||||
error_out:
|
||||
if (!IS_ERR_OR_NULL(st->reg))
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return ret;
|
||||
|
@ -451,7 +451,7 @@ static int ads8688_remove(struct spi_device *spi)
|
|||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
if (!IS_ERR_OR_NULL(st->reg))
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -18,6 +18,7 @@ struct iio_cb_buffer {
|
|||
int (*cb)(const void *data, void *private);
|
||||
void *private;
|
||||
struct iio_channel *channels;
|
||||
struct iio_dev *indio_dev;
|
||||
};
|
||||
|
||||
static struct iio_cb_buffer *buffer_to_cb_buffer(struct iio_buffer *buffer)
|
||||
|
@ -52,7 +53,6 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
|
|||
{
|
||||
int ret;
|
||||
struct iio_cb_buffer *cb_buff;
|
||||
struct iio_dev *indio_dev;
|
||||
struct iio_channel *chan;
|
||||
|
||||
cb_buff = kzalloc(sizeof(*cb_buff), GFP_KERNEL);
|
||||
|
@ -72,17 +72,17 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
|
|||
goto error_free_cb_buff;
|
||||
}
|
||||
|
||||
indio_dev = cb_buff->channels[0].indio_dev;
|
||||
cb_buff->indio_dev = cb_buff->channels[0].indio_dev;
|
||||
cb_buff->buffer.scan_mask
|
||||
= kcalloc(BITS_TO_LONGS(indio_dev->masklength), sizeof(long),
|
||||
GFP_KERNEL);
|
||||
= kcalloc(BITS_TO_LONGS(cb_buff->indio_dev->masklength),
|
||||
sizeof(long), GFP_KERNEL);
|
||||
if (cb_buff->buffer.scan_mask == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_release_channels;
|
||||
}
|
||||
chan = &cb_buff->channels[0];
|
||||
while (chan->indio_dev) {
|
||||
if (chan->indio_dev != indio_dev) {
|
||||
if (chan->indio_dev != cb_buff->indio_dev) {
|
||||
ret = -EINVAL;
|
||||
goto error_free_scan_mask;
|
||||
}
|
||||
|
@ -105,17 +105,14 @@ EXPORT_SYMBOL_GPL(iio_channel_get_all_cb);
|
|||
|
||||
int iio_channel_start_all_cb(struct iio_cb_buffer *cb_buff)
|
||||
{
|
||||
return iio_update_buffers(cb_buff->channels[0].indio_dev,
|
||||
&cb_buff->buffer,
|
||||
return iio_update_buffers(cb_buff->indio_dev, &cb_buff->buffer,
|
||||
NULL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_channel_start_all_cb);
|
||||
|
||||
void iio_channel_stop_all_cb(struct iio_cb_buffer *cb_buff)
|
||||
{
|
||||
iio_update_buffers(cb_buff->channels[0].indio_dev,
|
||||
NULL,
|
||||
&cb_buff->buffer);
|
||||
iio_update_buffers(cb_buff->indio_dev, NULL, &cb_buff->buffer);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_channel_stop_all_cb);
|
||||
|
||||
|
@ -133,6 +130,13 @@ struct iio_channel
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(iio_channel_cb_get_channels);
|
||||
|
||||
struct iio_dev
|
||||
*iio_channel_cb_get_iio_dev(const struct iio_cb_buffer *cb_buffer)
|
||||
{
|
||||
return cb_buffer->indio_dev;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_channel_cb_get_iio_dev);
|
||||
|
||||
MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
|
||||
MODULE_DESCRIPTION("Industrial I/O callback buffer");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -98,6 +98,48 @@ void iio_triggered_buffer_cleanup(struct iio_dev *indio_dev)
|
|||
}
|
||||
EXPORT_SYMBOL(iio_triggered_buffer_cleanup);
|
||||
|
||||
static void devm_iio_triggered_buffer_clean(struct device *dev, void *res)
|
||||
{
|
||||
iio_triggered_buffer_cleanup(*(struct iio_dev **)res);
|
||||
}
|
||||
|
||||
int devm_iio_triggered_buffer_setup(struct device *dev,
|
||||
struct iio_dev *indio_dev,
|
||||
irqreturn_t (*h)(int irq, void *p),
|
||||
irqreturn_t (*thread)(int irq, void *p),
|
||||
const struct iio_buffer_setup_ops *ops)
|
||||
{
|
||||
struct iio_dev **ptr;
|
||||
int ret;
|
||||
|
||||
ptr = devres_alloc(devm_iio_triggered_buffer_clean, sizeof(*ptr),
|
||||
GFP_KERNEL);
|
||||
if (!ptr)
|
||||
return -ENOMEM;
|
||||
|
||||
*ptr = indio_dev;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, h, thread, ops);
|
||||
if (!ret)
|
||||
devres_add(dev, ptr);
|
||||
else
|
||||
devres_free(ptr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_iio_triggered_buffer_setup);
|
||||
|
||||
void devm_iio_triggered_buffer_cleanup(struct device *dev,
|
||||
struct iio_dev *indio_dev)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = devres_release(dev, devm_iio_triggered_buffer_clean,
|
||||
devm_iio_device_match, indio_dev);
|
||||
WARN_ON(rc);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_iio_triggered_buffer_cleanup);
|
||||
|
||||
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
MODULE_DESCRIPTION("IIO helper functions for setting up triggered buffers");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -16,6 +16,7 @@ config ATLAS_PH_SENSOR
|
|||
Atlas Scientific OEM SM sensors:
|
||||
* pH SM sensor
|
||||
* EC SM sensor
|
||||
* ORP SM sensor
|
||||
|
||||
To compile this driver as module, choose M here: the
|
||||
module will be called atlas-ph-sensor.
|
||||
|
|
|
@ -66,12 +66,17 @@
|
|||
#define ATLAS_REG_TDS_DATA 0x1c
|
||||
#define ATLAS_REG_PSS_DATA 0x20
|
||||
|
||||
#define ATLAS_REG_ORP_CALIB_STATUS 0x0d
|
||||
#define ATLAS_REG_ORP_DATA 0x0e
|
||||
|
||||
#define ATLAS_PH_INT_TIME_IN_US 450000
|
||||
#define ATLAS_EC_INT_TIME_IN_US 650000
|
||||
#define ATLAS_ORP_INT_TIME_IN_US 450000
|
||||
|
||||
enum {
|
||||
ATLAS_PH_SM,
|
||||
ATLAS_EC_SM,
|
||||
ATLAS_ORP_SM,
|
||||
};
|
||||
|
||||
struct atlas_data {
|
||||
|
@ -84,26 +89,10 @@ struct atlas_data {
|
|||
__be32 buffer[6]; /* 96-bit 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),
|
||||
regmap_reg_range(ATLAS_REG_EC_DATA, ATLAS_REG_PSS_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_PSS_DATA + 4,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec atlas_ph_channels[] = {
|
||||
|
@ -175,6 +164,23 @@ static const struct iio_chan_spec atlas_ec_channels[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec atlas_orp_channels[] = {
|
||||
{
|
||||
.type = IIO_VOLTAGE,
|
||||
.address = ATLAS_REG_ORP_DATA,
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 32,
|
||||
.storagebits = 32,
|
||||
.endianness = IIO_BE,
|
||||
},
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
static int atlas_check_ph_calibration(struct atlas_data *data)
|
||||
{
|
||||
struct device *dev = &data->client->dev;
|
||||
|
@ -240,6 +246,22 @@ static int atlas_check_ec_calibration(struct atlas_data *data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int atlas_check_orp_calibration(struct atlas_data *data)
|
||||
{
|
||||
struct device *dev = &data->client->dev;
|
||||
int ret;
|
||||
unsigned int val;
|
||||
|
||||
ret = regmap_read(data->regmap, ATLAS_REG_ORP_CALIB_STATUS, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!val)
|
||||
dev_warn(dev, "device has not been calibrated\n");
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
struct atlas_device {
|
||||
const struct iio_chan_spec *channels;
|
||||
int num_channels;
|
||||
|
@ -264,7 +286,13 @@ static struct atlas_device atlas_devices[] = {
|
|||
.calibration = &atlas_check_ec_calibration,
|
||||
.delay = ATLAS_EC_INT_TIME_IN_US,
|
||||
},
|
||||
|
||||
[ATLAS_ORP_SM] = {
|
||||
.channels = atlas_orp_channels,
|
||||
.num_channels = 2,
|
||||
.data_reg = ATLAS_REG_ORP_DATA,
|
||||
.calibration = &atlas_check_orp_calibration,
|
||||
.delay = ATLAS_ORP_INT_TIME_IN_US,
|
||||
},
|
||||
};
|
||||
|
||||
static int atlas_set_powermode(struct atlas_data *data, int on)
|
||||
|
@ -402,15 +430,14 @@ static int atlas_read_raw(struct iio_dev *indio_dev,
|
|||
case IIO_PH:
|
||||
case IIO_CONCENTRATION:
|
||||
case IIO_ELECTRICALCONDUCTIVITY:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
case IIO_VOLTAGE:
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
ret = -EBUSY;
|
||||
else
|
||||
ret = atlas_read_measurement(data,
|
||||
chan->address, ®);
|
||||
ret = atlas_read_measurement(data, chan->address, ®);
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
|
@ -440,6 +467,10 @@ static int atlas_read_raw(struct iio_dev *indio_dev,
|
|||
*val = 0; /* 0.000000001 */
|
||||
*val2 = 1000;
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
case IIO_VOLTAGE:
|
||||
*val = 1; /* 0.1 */
|
||||
*val2 = 10;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -475,6 +506,7 @@ static const struct iio_info atlas_info = {
|
|||
static const struct i2c_device_id atlas_id[] = {
|
||||
{ "atlas-ph-sm", ATLAS_PH_SM},
|
||||
{ "atlas-ec-sm", ATLAS_EC_SM},
|
||||
{ "atlas-orp-sm", ATLAS_ORP_SM},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, atlas_id);
|
||||
|
@ -482,6 +514,7 @@ MODULE_DEVICE_TABLE(i2c, atlas_id);
|
|||
static const struct of_device_id atlas_dt_ids[] = {
|
||||
{ .compatible = "atlas,ph-sm", .data = (void *)ATLAS_PH_SM, },
|
||||
{ .compatible = "atlas,ec-sm", .data = (void *)ATLAS_EC_SM, },
|
||||
{ .compatible = "atlas,orp-sm", .data = (void *)ATLAS_ORP_SM, },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, atlas_dt_ids);
|
||||
|
|
|
@ -19,25 +19,55 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define VZ89X_REG_MEASUREMENT 0x09
|
||||
#define VZ89X_REG_MEASUREMENT_SIZE 6
|
||||
#define VZ89X_REG_MEASUREMENT_RD_SIZE 6
|
||||
#define VZ89X_REG_MEASUREMENT_WR_SIZE 3
|
||||
|
||||
#define VZ89X_VOC_CO2_IDX 0
|
||||
#define VZ89X_VOC_SHORT_IDX 1
|
||||
#define VZ89X_VOC_TVOC_IDX 2
|
||||
#define VZ89X_VOC_RESISTANCE_IDX 3
|
||||
|
||||
#define VZ89TE_REG_MEASUREMENT 0x0c
|
||||
#define VZ89TE_REG_MEASUREMENT_RD_SIZE 7
|
||||
#define VZ89TE_REG_MEASUREMENT_WR_SIZE 6
|
||||
|
||||
#define VZ89TE_VOC_TVOC_IDX 0
|
||||
#define VZ89TE_VOC_CO2_IDX 1
|
||||
#define VZ89TE_VOC_RESISTANCE_IDX 2
|
||||
|
||||
enum {
|
||||
VZ89X,
|
||||
VZ89TE,
|
||||
};
|
||||
|
||||
struct vz89x_chip_data;
|
||||
|
||||
struct vz89x_data {
|
||||
struct i2c_client *client;
|
||||
const struct vz89x_chip_data *chip;
|
||||
struct mutex lock;
|
||||
int (*xfer)(struct vz89x_data *data, u8 cmd);
|
||||
|
||||
bool is_valid;
|
||||
unsigned long last_update;
|
||||
u8 buffer[VZ89X_REG_MEASUREMENT_SIZE];
|
||||
u8 buffer[VZ89TE_REG_MEASUREMENT_RD_SIZE];
|
||||
};
|
||||
|
||||
struct vz89x_chip_data {
|
||||
bool (*valid)(struct vz89x_data *data);
|
||||
const struct iio_chan_spec *channels;
|
||||
u8 num_channels;
|
||||
|
||||
u8 cmd;
|
||||
u8 read_size;
|
||||
u8 write_size;
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec vz89x_channels[] = {
|
||||
|
@ -70,6 +100,40 @@ static const struct iio_chan_spec vz89x_channels[] = {
|
|||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
.address = VZ89X_VOC_RESISTANCE_IDX,
|
||||
.scan_index = -1,
|
||||
.scan_type = {
|
||||
.endianness = IIO_LE,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec vz89te_channels[] = {
|
||||
{
|
||||
.type = IIO_CONCENTRATION,
|
||||
.channel2 = IIO_MOD_VOC,
|
||||
.modified = 1,
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_RAW),
|
||||
.address = VZ89TE_VOC_TVOC_IDX,
|
||||
},
|
||||
|
||||
{
|
||||
.type = IIO_CONCENTRATION,
|
||||
.channel2 = IIO_MOD_CO2,
|
||||
.modified = 1,
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_RAW),
|
||||
.address = VZ89TE_VOC_CO2_IDX,
|
||||
},
|
||||
{
|
||||
.type = IIO_RESISTANCE,
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
.address = VZ89TE_VOC_RESISTANCE_IDX,
|
||||
.scan_index = -1,
|
||||
.scan_type = {
|
||||
.endianness = IIO_BE,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -93,29 +157,45 @@ static const struct attribute_group vz89x_attrs_group = {
|
|||
* always zero, and by also confirming the VOC_short isn't zero.
|
||||
*/
|
||||
|
||||
static int vz89x_measurement_is_valid(struct vz89x_data *data)
|
||||
static bool vz89x_measurement_is_valid(struct vz89x_data *data)
|
||||
{
|
||||
if (data->buffer[VZ89X_VOC_SHORT_IDX] == 0)
|
||||
return 1;
|
||||
return true;
|
||||
|
||||
return !!(data->buffer[VZ89X_REG_MEASUREMENT_SIZE - 1] > 0);
|
||||
return !!(data->buffer[data->chip->read_size - 1] > 0);
|
||||
}
|
||||
|
||||
/* VZ89TE device has a modified CRC-8 two complement check */
|
||||
static bool vz89te_measurement_is_valid(struct vz89x_data *data)
|
||||
{
|
||||
u8 crc = 0;
|
||||
int i, sum = 0;
|
||||
|
||||
for (i = 0; i < (data->chip->read_size - 1); i++) {
|
||||
sum = crc + data->buffer[i];
|
||||
crc = sum;
|
||||
crc += sum / 256;
|
||||
}
|
||||
|
||||
return !((0xff - crc) == data->buffer[data->chip->read_size - 1]);
|
||||
}
|
||||
|
||||
static int vz89x_i2c_xfer(struct vz89x_data *data, u8 cmd)
|
||||
{
|
||||
const struct vz89x_chip_data *chip = data->chip;
|
||||
struct i2c_client *client = data->client;
|
||||
struct i2c_msg msg[2];
|
||||
int ret;
|
||||
u8 buf[3] = { cmd, 0, 0};
|
||||
u8 buf[6] = { cmd, 0, 0, 0, 0, 0xf3 };
|
||||
|
||||
msg[0].addr = client->addr;
|
||||
msg[0].flags = client->flags;
|
||||
msg[0].len = 3;
|
||||
msg[0].len = chip->write_size;
|
||||
msg[0].buf = (char *) &buf;
|
||||
|
||||
msg[1].addr = client->addr;
|
||||
msg[1].flags = client->flags | I2C_M_RD;
|
||||
msg[1].len = VZ89X_REG_MEASUREMENT_SIZE;
|
||||
msg[1].len = chip->read_size;
|
||||
msg[1].buf = (char *) &data->buffer;
|
||||
|
||||
ret = i2c_transfer(client->adapter, msg, 2);
|
||||
|
@ -133,7 +213,7 @@ static int vz89x_smbus_xfer(struct vz89x_data *data, u8 cmd)
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < VZ89X_REG_MEASUREMENT_SIZE; i++) {
|
||||
for (i = 0; i < data->chip->read_size; i++) {
|
||||
ret = i2c_smbus_read_byte(client);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
@ -145,30 +225,47 @@ static int vz89x_smbus_xfer(struct vz89x_data *data, u8 cmd)
|
|||
|
||||
static int vz89x_get_measurement(struct vz89x_data *data)
|
||||
{
|
||||
const struct vz89x_chip_data *chip = data->chip;
|
||||
int ret;
|
||||
|
||||
/* sensor can only be polled once a second max per datasheet */
|
||||
if (!time_after(jiffies, data->last_update + HZ))
|
||||
return 0;
|
||||
return data->is_valid ? 0 : -EAGAIN;
|
||||
|
||||
ret = data->xfer(data, VZ89X_REG_MEASUREMENT);
|
||||
data->is_valid = false;
|
||||
data->last_update = jiffies;
|
||||
|
||||
ret = data->xfer(data, chip->cmd);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = vz89x_measurement_is_valid(data);
|
||||
ret = chip->valid(data);
|
||||
if (ret)
|
||||
return -EAGAIN;
|
||||
|
||||
data->last_update = jiffies;
|
||||
data->is_valid = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vz89x_get_resistance_reading(struct vz89x_data *data)
|
||||
static int vz89x_get_resistance_reading(struct vz89x_data *data,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val)
|
||||
{
|
||||
u8 *buf = &data->buffer[VZ89X_VOC_RESISTANCE_IDX];
|
||||
u8 *tmp = (u8 *) &data->buffer[chan->address];
|
||||
|
||||
return buf[0] | (buf[1] << 8);
|
||||
switch (chan->scan_type.endianness) {
|
||||
case IIO_LE:
|
||||
*val = le32_to_cpup((__le32 *) tmp) & GENMASK(23, 0);
|
||||
break;
|
||||
case IIO_BE:
|
||||
*val = be32_to_cpup((__be32 *) tmp) >> 8;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vz89x_read_raw(struct iio_dev *indio_dev,
|
||||
|
@ -187,15 +284,15 @@ static int vz89x_read_raw(struct iio_dev *indio_dev,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (chan->address) {
|
||||
case VZ89X_VOC_CO2_IDX:
|
||||
case VZ89X_VOC_SHORT_IDX:
|
||||
case VZ89X_VOC_TVOC_IDX:
|
||||
switch (chan->type) {
|
||||
case IIO_CONCENTRATION:
|
||||
*val = data->buffer[chan->address];
|
||||
return IIO_VAL_INT;
|
||||
case VZ89X_VOC_RESISTANCE_IDX:
|
||||
*val = vz89x_get_resistance_reading(data);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_RESISTANCE:
|
||||
ret = vz89x_get_resistance_reading(data, chan, val);
|
||||
if (!ret)
|
||||
return IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -210,12 +307,12 @@ static int vz89x_read_raw(struct iio_dev *indio_dev,
|
|||
}
|
||||
break;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
switch (chan->address) {
|
||||
case VZ89X_VOC_CO2_IDX:
|
||||
switch (chan->channel2) {
|
||||
case IIO_MOD_CO2:
|
||||
*val = 44;
|
||||
*val2 = 250000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case VZ89X_VOC_TVOC_IDX:
|
||||
case IIO_MOD_VOC:
|
||||
*val = -13;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
|
@ -232,11 +329,43 @@ static const struct iio_info vz89x_info = {
|
|||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct vz89x_chip_data vz89x_chips[] = {
|
||||
{
|
||||
.valid = vz89x_measurement_is_valid,
|
||||
|
||||
.cmd = VZ89X_REG_MEASUREMENT,
|
||||
.read_size = VZ89X_REG_MEASUREMENT_RD_SIZE,
|
||||
.write_size = VZ89X_REG_MEASUREMENT_WR_SIZE,
|
||||
|
||||
.channels = vz89x_channels,
|
||||
.num_channels = ARRAY_SIZE(vz89x_channels),
|
||||
},
|
||||
{
|
||||
.valid = vz89te_measurement_is_valid,
|
||||
|
||||
.cmd = VZ89TE_REG_MEASUREMENT,
|
||||
.read_size = VZ89TE_REG_MEASUREMENT_RD_SIZE,
|
||||
.write_size = VZ89TE_REG_MEASUREMENT_WR_SIZE,
|
||||
|
||||
.channels = vz89te_channels,
|
||||
.num_channels = ARRAY_SIZE(vz89te_channels),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct of_device_id vz89x_dt_ids[] = {
|
||||
{ .compatible = "sgx,vz89x", .data = (void *) VZ89X },
|
||||
{ .compatible = "sgx,vz89te", .data = (void *) VZ89TE },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, vz89x_dt_ids);
|
||||
|
||||
static int vz89x_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct vz89x_data *data;
|
||||
const struct of_device_id *of_id;
|
||||
int chip_id;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
|
@ -251,8 +380,15 @@ static int vz89x_probe(struct i2c_client *client,
|
|||
else
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
of_id = of_match_device(vz89x_dt_ids, &client->dev);
|
||||
if (!of_id)
|
||||
chip_id = id->driver_data;
|
||||
else
|
||||
chip_id = (unsigned long)of_id->data;
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
data->chip = &vz89x_chips[chip_id];
|
||||
data->last_update = jiffies - HZ;
|
||||
mutex_init(&data->lock);
|
||||
|
||||
|
@ -261,24 +397,19 @@ static int vz89x_probe(struct i2c_client *client,
|
|||
indio_dev->name = dev_name(&client->dev);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
indio_dev->channels = vz89x_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(vz89x_channels);
|
||||
indio_dev->channels = data->chip->channels;
|
||||
indio_dev->num_channels = data->chip->num_channels;
|
||||
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id vz89x_id[] = {
|
||||
{ "vz89x", 0 },
|
||||
{ "vz89x", VZ89X },
|
||||
{ "vz89te", VZ89TE },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, vz89x_id);
|
||||
|
||||
static const struct of_device_id vz89x_dt_ids[] = {
|
||||
{ .compatible = "sgx,vz89x" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, vz89x_dt_ids);
|
||||
|
||||
static struct i2c_driver vz89x_driver = {
|
||||
.driver = {
|
||||
.name = "vz89x",
|
||||
|
|
|
@ -122,6 +122,14 @@ int hid_sensor_power_state(struct hid_sensor_common *st, bool state)
|
|||
#endif
|
||||
}
|
||||
|
||||
static void hid_sensor_set_power_work(struct work_struct *work)
|
||||
{
|
||||
struct hid_sensor_common *attrb = container_of(work,
|
||||
struct hid_sensor_common,
|
||||
work);
|
||||
_hid_sensor_power_state(attrb, true);
|
||||
}
|
||||
|
||||
static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
||||
bool state)
|
||||
{
|
||||
|
@ -130,6 +138,7 @@ static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
|||
|
||||
void hid_sensor_remove_trigger(struct hid_sensor_common *attrb)
|
||||
{
|
||||
cancel_work_sync(&attrb->work);
|
||||
iio_trigger_unregister(attrb->trigger);
|
||||
iio_trigger_free(attrb->trigger);
|
||||
}
|
||||
|
@ -170,6 +179,9 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
|
|||
goto error_unreg_trigger;
|
||||
|
||||
iio_device_set_drvdata(indio_dev, attrb);
|
||||
|
||||
INIT_WORK(&attrb->work, hid_sensor_set_power_work);
|
||||
|
||||
pm_suspend_ignore_children(&attrb->pdev->dev, true);
|
||||
pm_runtime_enable(&attrb->pdev->dev);
|
||||
/* Default to 3 seconds, but can be changed from sysfs */
|
||||
|
@ -187,8 +199,7 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
|
|||
}
|
||||
EXPORT_SYMBOL(hid_sensor_setup_trigger);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int hid_sensor_suspend(struct device *dev)
|
||||
static int __maybe_unused hid_sensor_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
|
@ -197,21 +208,27 @@ static int hid_sensor_suspend(struct device *dev)
|
|||
return _hid_sensor_power_state(attrb, false);
|
||||
}
|
||||
|
||||
static int hid_sensor_resume(struct device *dev)
|
||||
static int __maybe_unused hid_sensor_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev);
|
||||
|
||||
return _hid_sensor_power_state(attrb, true);
|
||||
schedule_work(&attrb->work);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
static int __maybe_unused hid_sensor_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev);
|
||||
return _hid_sensor_power_state(attrb, true);
|
||||
}
|
||||
|
||||
const struct dev_pm_ops hid_sensor_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(hid_sensor_suspend, hid_sensor_resume)
|
||||
SET_RUNTIME_PM_OPS(hid_sensor_suspend,
|
||||
hid_sensor_resume, NULL)
|
||||
hid_sensor_runtime_resume, NULL)
|
||||
};
|
||||
EXPORT_SYMBOL(hid_sensor_pm_ops);
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ irqreturn_t st_sensors_trigger_handler(int irq, void *p)
|
|||
* the hardware trigger) and the hw_timestamp may get updated.
|
||||
* By storing it in a local variable first, we are safe.
|
||||
*/
|
||||
if (sdata->hw_irq_trigger)
|
||||
if (iio_trigger_using_own(indio_dev))
|
||||
timestamp = sdata->hw_timestamp;
|
||||
else
|
||||
timestamp = iio_get_time_ns(indio_dev);
|
||||
|
|
|
@ -234,39 +234,35 @@ int st_sensors_power_enable(struct iio_dev *indio_dev)
|
|||
int err;
|
||||
|
||||
/* Regulators not mandatory, but if requested we should enable them. */
|
||||
pdata->vdd = devm_regulator_get_optional(indio_dev->dev.parent, "vdd");
|
||||
if (!IS_ERR(pdata->vdd)) {
|
||||
err = regulator_enable(pdata->vdd);
|
||||
if (err != 0) {
|
||||
dev_warn(&indio_dev->dev,
|
||||
"Failed to enable specified Vdd supply\n");
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
err = PTR_ERR(pdata->vdd);
|
||||
if (err != -ENODEV)
|
||||
return err;
|
||||
pdata->vdd = devm_regulator_get(indio_dev->dev.parent, "vdd");
|
||||
if (IS_ERR(pdata->vdd)) {
|
||||
dev_err(&indio_dev->dev, "unable to get Vdd supply\n");
|
||||
return PTR_ERR(pdata->vdd);
|
||||
}
|
||||
err = regulator_enable(pdata->vdd);
|
||||
if (err != 0) {
|
||||
dev_warn(&indio_dev->dev,
|
||||
"Failed to enable specified Vdd supply\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
pdata->vdd_io = devm_regulator_get_optional(indio_dev->dev.parent, "vddio");
|
||||
if (!IS_ERR(pdata->vdd_io)) {
|
||||
err = regulator_enable(pdata->vdd_io);
|
||||
if (err != 0) {
|
||||
dev_warn(&indio_dev->dev,
|
||||
"Failed to enable specified Vdd_IO supply\n");
|
||||
goto st_sensors_disable_vdd;
|
||||
}
|
||||
} else {
|
||||
pdata->vdd_io = devm_regulator_get(indio_dev->dev.parent, "vddio");
|
||||
if (IS_ERR(pdata->vdd_io)) {
|
||||
dev_err(&indio_dev->dev, "unable to get Vdd_IO supply\n");
|
||||
err = PTR_ERR(pdata->vdd_io);
|
||||
if (err != -ENODEV)
|
||||
goto st_sensors_disable_vdd;
|
||||
goto st_sensors_disable_vdd;
|
||||
}
|
||||
err = regulator_enable(pdata->vdd_io);
|
||||
if (err != 0) {
|
||||
dev_warn(&indio_dev->dev,
|
||||
"Failed to enable specified Vdd_IO supply\n");
|
||||
goto st_sensors_disable_vdd;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
st_sensors_disable_vdd:
|
||||
if (!IS_ERR_OR_NULL(pdata->vdd))
|
||||
regulator_disable(pdata->vdd);
|
||||
regulator_disable(pdata->vdd);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_power_enable);
|
||||
|
@ -275,11 +271,8 @@ void st_sensors_power_disable(struct iio_dev *indio_dev)
|
|||
{
|
||||
struct st_sensor_data *pdata = iio_priv(indio_dev);
|
||||
|
||||
if (!IS_ERR_OR_NULL(pdata->vdd))
|
||||
regulator_disable(pdata->vdd);
|
||||
|
||||
if (!IS_ERR_OR_NULL(pdata->vdd_io))
|
||||
regulator_disable(pdata->vdd_io);
|
||||
regulator_disable(pdata->vdd);
|
||||
regulator_disable(pdata->vdd_io);
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_power_disable);
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ static int st_sensors_new_samples_available(struct iio_dev *indio_dev,
|
|||
* @irq: irq number
|
||||
* @p: private handler data
|
||||
*/
|
||||
irqreturn_t st_sensors_irq_handler(int irq, void *p)
|
||||
static irqreturn_t st_sensors_irq_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_trigger *trig = p;
|
||||
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
|
||||
|
@ -82,7 +82,7 @@ irqreturn_t st_sensors_irq_handler(int irq, void *p)
|
|||
* @irq: irq number
|
||||
* @p: private handler data
|
||||
*/
|
||||
irqreturn_t st_sensors_irq_thread(int irq, void *p)
|
||||
static irqreturn_t st_sensors_irq_thread(int irq, void *p)
|
||||
{
|
||||
struct iio_trigger *trig = p;
|
||||
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
|
||||
|
|
|
@ -181,6 +181,25 @@ config AD7303
|
|||
To compile this driver as module choose M here: the module will be called
|
||||
ad7303.
|
||||
|
||||
config CIO_DAC
|
||||
tristate "Measurement Computing CIO-DAC IIO driver"
|
||||
depends on X86 && ISA_BUS_API
|
||||
help
|
||||
Say yes here to build support for the Measurement Computing CIO-DAC
|
||||
analog output device family (CIO-DAC16, CIO-DAC08, PC104-DAC06). The
|
||||
base port addresses for the devices may be configured via the base
|
||||
array module parameter.
|
||||
|
||||
config AD8801
|
||||
tristate "Analog Devices AD8801/AD8803 DAC driver"
|
||||
depends on SPI_MASTER
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD8801, AD8803 Digital to
|
||||
Analog Converters (DAC).
|
||||
|
||||
To compile this driver as a module choose M here: the module will be called
|
||||
ad8801.
|
||||
|
||||
config LPC18XX_DAC
|
||||
tristate "NXP LPC18xx DAC driver"
|
||||
depends on ARCH_LPC18XX || COMPILE_TEST
|
||||
|
@ -245,16 +264,6 @@ 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 X86 && ISA_BUS_API
|
||||
select GPIOLIB
|
||||
help
|
||||
Say yes here to build support for the 2-channel DAC and GPIO on the
|
||||
Apex Embedded Systems STX104 integrated analog PC/104 card. The base
|
||||
port addresses for the devices may be configured via the base array
|
||||
module parameter.
|
||||
|
||||
config VF610_DAC
|
||||
tristate "Vybrid vf610 DAC driver"
|
||||
depends on OF
|
||||
|
|
|
@ -20,11 +20,12 @@ obj-$(CONFIG_AD5764) += ad5764.o
|
|||
obj-$(CONFIG_AD5791) += ad5791.o
|
||||
obj-$(CONFIG_AD5686) += ad5686.o
|
||||
obj-$(CONFIG_AD7303) += ad7303.o
|
||||
obj-$(CONFIG_AD8801) += ad8801.o
|
||||
obj-$(CONFIG_CIO_DAC) += cio-dac.o
|
||||
obj-$(CONFIG_LPC18XX_DAC) += lpc18xx_dac.o
|
||||
obj-$(CONFIG_M62332) += m62332.o
|
||||
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
|
||||
|
|
|
@ -655,7 +655,7 @@ static struct ad5755_platform_data *ad5755_parse_dt(struct device *dev)
|
|||
|
||||
devnr = 0;
|
||||
for_each_child_of_node(np, pp) {
|
||||
if (devnr > AD5755_NUM_CHANNELS) {
|
||||
if (devnr >= AD5755_NUM_CHANNELS) {
|
||||
dev_err(dev,
|
||||
"There is to many channels defined in DT\n");
|
||||
goto error_out;
|
||||
|
|
239
drivers/iio/dac/ad8801.c
Normal file
239
drivers/iio/dac/ad8801.c
Normal file
|
@ -0,0 +1,239 @@
|
|||
/*
|
||||
* IIO DAC driver for Analog Devices AD8801 DAC
|
||||
*
|
||||
* Copyright (C) 2016 Gwenhael Goavec-Merou
|
||||
* 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/iio/iio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
#define AD8801_CFG_ADDR_OFFSET 8
|
||||
|
||||
enum ad8801_device_ids {
|
||||
ID_AD8801,
|
||||
ID_AD8803,
|
||||
};
|
||||
|
||||
struct ad8801_state {
|
||||
struct spi_device *spi;
|
||||
unsigned char dac_cache[8]; /* Value write on each channel */
|
||||
unsigned int vrefh_mv;
|
||||
unsigned int vrefl_mv;
|
||||
struct regulator *vrefh_reg;
|
||||
struct regulator *vrefl_reg;
|
||||
|
||||
__be16 data ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static int ad8801_spi_write(struct ad8801_state *state,
|
||||
u8 channel, unsigned char value)
|
||||
{
|
||||
state->data = cpu_to_be16((channel << AD8801_CFG_ADDR_OFFSET) | value);
|
||||
return spi_write(state->spi, &state->data, sizeof(state->data));
|
||||
}
|
||||
|
||||
static int ad8801_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2, long mask)
|
||||
{
|
||||
struct ad8801_state *state = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (val >= 256 || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
ret = ad8801_spi_write(state, chan->channel, val);
|
||||
if (ret == 0)
|
||||
state->dac_cache[chan->channel] = val;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad8801_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val, int *val2, long info)
|
||||
{
|
||||
struct ad8801_state *state = iio_priv(indio_dev);
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
*val = state->dac_cache[chan->channel];
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = state->vrefh_mv - state->vrefl_mv;
|
||||
*val2 = 8;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = state->vrefl_mv;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info ad8801_info = {
|
||||
.read_raw = ad8801_read_raw,
|
||||
.write_raw = ad8801_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
#define AD8801_CHANNEL(chan) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = chan, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_OFFSET), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ad8801_channels[] = {
|
||||
AD8801_CHANNEL(0),
|
||||
AD8801_CHANNEL(1),
|
||||
AD8801_CHANNEL(2),
|
||||
AD8801_CHANNEL(3),
|
||||
AD8801_CHANNEL(4),
|
||||
AD8801_CHANNEL(5),
|
||||
AD8801_CHANNEL(6),
|
||||
AD8801_CHANNEL(7),
|
||||
};
|
||||
|
||||
static int ad8801_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad8801_state *state;
|
||||
const struct spi_device_id *id;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*state));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
state = iio_priv(indio_dev);
|
||||
state->spi = spi;
|
||||
id = spi_get_device_id(spi);
|
||||
|
||||
state->vrefh_reg = devm_regulator_get(&spi->dev, "vrefh");
|
||||
if (IS_ERR(state->vrefh_reg)) {
|
||||
dev_err(&spi->dev, "Vrefh regulator not specified\n");
|
||||
return PTR_ERR(state->vrefh_reg);
|
||||
}
|
||||
|
||||
ret = regulator_enable(state->vrefh_reg);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to enable vrefh regulator: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_get_voltage(state->vrefh_reg);
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "Failed to read vrefh regulator: %d\n",
|
||||
ret);
|
||||
goto error_disable_vrefh_reg;
|
||||
}
|
||||
state->vrefh_mv = ret / 1000;
|
||||
|
||||
if (id->driver_data == ID_AD8803) {
|
||||
state->vrefl_reg = devm_regulator_get(&spi->dev, "vrefl");
|
||||
if (IS_ERR(state->vrefl_reg)) {
|
||||
dev_err(&spi->dev, "Vrefl regulator not specified\n");
|
||||
ret = PTR_ERR(state->vrefl_reg);
|
||||
goto error_disable_vrefh_reg;
|
||||
}
|
||||
|
||||
ret = regulator_enable(state->vrefl_reg);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to enable vrefl regulator: %d\n",
|
||||
ret);
|
||||
goto error_disable_vrefh_reg;
|
||||
}
|
||||
|
||||
ret = regulator_get_voltage(state->vrefl_reg);
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "Failed to read vrefl regulator: %d\n",
|
||||
ret);
|
||||
goto error_disable_vrefl_reg;
|
||||
}
|
||||
state->vrefl_mv = ret / 1000;
|
||||
} else {
|
||||
state->vrefl_mv = 0;
|
||||
state->vrefl_reg = NULL;
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->info = &ad8801_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = ad8801_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ad8801_channels);
|
||||
indio_dev->name = id->name;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to register iio device: %d\n",
|
||||
ret);
|
||||
goto error_disable_vrefl_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_disable_vrefl_reg:
|
||||
if (state->vrefl_reg)
|
||||
regulator_disable(state->vrefl_reg);
|
||||
error_disable_vrefh_reg:
|
||||
regulator_disable(state->vrefh_reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad8801_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad8801_state *state = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
if (state->vrefl_reg)
|
||||
regulator_disable(state->vrefl_reg);
|
||||
regulator_disable(state->vrefh_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad8801_ids[] = {
|
||||
{"ad8801", ID_AD8801},
|
||||
{"ad8803", ID_AD8803},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad8801_ids);
|
||||
|
||||
static struct spi_driver ad8801_driver = {
|
||||
.driver = {
|
||||
.name = "ad8801",
|
||||
},
|
||||
.probe = ad8801_probe,
|
||||
.remove = ad8801_remove,
|
||||
.id_table = ad8801_ids,
|
||||
};
|
||||
module_spi_driver(ad8801_driver);
|
||||
|
||||
MODULE_AUTHOR("Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD8801/AD8803 DAC");
|
||||
MODULE_LICENSE("GPL v2");
|
144
drivers/iio/dac/cio-dac.c
Normal file
144
drivers/iio/dac/cio-dac.c
Normal file
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* IIO driver for the Measurement Computing CIO-DAC
|
||||
* Copyright (C) 2016 William Breathitt Gray
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License, version 2, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* This driver supports the following Measurement Computing devices: CIO-DAC16,
|
||||
* CIO-DAC06, and PC104-DAC06.
|
||||
*/
|
||||
#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 CIO_DAC_NUM_CHAN 16
|
||||
|
||||
#define CIO_DAC_CHAN(chan) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.channel = chan, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.indexed = 1, \
|
||||
.output = 1 \
|
||||
}
|
||||
|
||||
#define CIO_DAC_EXTENT 32
|
||||
|
||||
static unsigned int base[max_num_isa_dev(CIO_DAC_EXTENT)];
|
||||
static unsigned int num_cio_dac;
|
||||
module_param_array(base, uint, &num_cio_dac, 0);
|
||||
MODULE_PARM_DESC(base, "Measurement Computing CIO-DAC base addresses");
|
||||
|
||||
/**
|
||||
* struct cio_dac_iio - IIO device private data structure
|
||||
* @chan_out_states: channels' output states
|
||||
* @base: base port address of the IIO device
|
||||
*/
|
||||
struct cio_dac_iio {
|
||||
int chan_out_states[CIO_DAC_NUM_CHAN];
|
||||
unsigned int base;
|
||||
};
|
||||
|
||||
static int cio_dac_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val, int *val2, long mask)
|
||||
{
|
||||
struct cio_dac_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 cio_dac_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2, long mask)
|
||||
{
|
||||
struct cio_dac_iio *const priv = iio_priv(indio_dev);
|
||||
const unsigned int chan_addr_offset = 2 * chan->channel;
|
||||
|
||||
if (mask != IIO_CHAN_INFO_RAW)
|
||||
return -EINVAL;
|
||||
|
||||
/* DAC can only accept up to a 16-bit value */
|
||||
if ((unsigned int)val > 65535)
|
||||
return -EINVAL;
|
||||
|
||||
priv->chan_out_states[chan->channel] = val;
|
||||
outw(val, priv->base + chan_addr_offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_info cio_dac_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = cio_dac_read_raw,
|
||||
.write_raw = cio_dac_write_raw
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec cio_dac_channels[CIO_DAC_NUM_CHAN] = {
|
||||
CIO_DAC_CHAN(0), CIO_DAC_CHAN(1), CIO_DAC_CHAN(2), CIO_DAC_CHAN(3),
|
||||
CIO_DAC_CHAN(4), CIO_DAC_CHAN(5), CIO_DAC_CHAN(6), CIO_DAC_CHAN(7),
|
||||
CIO_DAC_CHAN(8), CIO_DAC_CHAN(9), CIO_DAC_CHAN(10), CIO_DAC_CHAN(11),
|
||||
CIO_DAC_CHAN(12), CIO_DAC_CHAN(13), CIO_DAC_CHAN(14), CIO_DAC_CHAN(15)
|
||||
};
|
||||
|
||||
static int cio_dac_probe(struct device *dev, unsigned int id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct cio_dac_iio *priv;
|
||||
unsigned int i;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!devm_request_region(dev, base[id], CIO_DAC_EXTENT,
|
||||
dev_name(dev))) {
|
||||
dev_err(dev, "Unable to request port addresses (0x%X-0x%X)\n",
|
||||
base[id], base[id] + CIO_DAC_EXTENT);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
indio_dev->info = &cio_dac_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = cio_dac_channels;
|
||||
indio_dev->num_channels = CIO_DAC_NUM_CHAN;
|
||||
indio_dev->name = dev_name(dev);
|
||||
|
||||
priv = iio_priv(indio_dev);
|
||||
priv->base = base[id];
|
||||
|
||||
/* initialize DAC outputs to 0V */
|
||||
for (i = 0; i < 32; i += 2)
|
||||
outw(0, base[id] + i);
|
||||
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
|
||||
static struct isa_driver cio_dac_driver = {
|
||||
.probe = cio_dac_probe,
|
||||
.driver = {
|
||||
.name = "cio-dac"
|
||||
}
|
||||
};
|
||||
|
||||
module_isa_driver(cio_dac_driver, num_cio_dac);
|
||||
|
||||
MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
|
||||
MODULE_DESCRIPTION("Measurement Computing CIO-DAC IIO driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -74,7 +74,7 @@ static int ssp_gyro_write_raw(struct iio_dev *indio_dev,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static struct iio_info ssp_gyro_iio_info = {
|
||||
static const struct iio_info ssp_gyro_iio_info = {
|
||||
.read_raw = &ssp_gyro_read_raw,
|
||||
.write_raw = &ssp_gyro_write_raw,
|
||||
};
|
||||
|
|
|
@ -28,11 +28,11 @@ config HDC100X
|
|||
tristate "TI HDC100x relative humidity and temperature sensor"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to build support for the TI HDC100x series of
|
||||
relative humidity and temperature sensors.
|
||||
Say yes here to build support for the Texas Instruments
|
||||
HDC1000 and HDC1008 relative humidity and temperature sensors.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called hdc100x.
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called hdc100x.
|
||||
|
||||
config HTU21
|
||||
tristate "Measurement Specialties HTU21 humidity & temperature sensor"
|
||||
|
|
|
@ -1308,7 +1308,7 @@ static void devm_iio_device_release(struct device *dev, void *res)
|
|||
iio_device_free(*(struct iio_dev **)res);
|
||||
}
|
||||
|
||||
static int devm_iio_device_match(struct device *dev, void *res, void *data)
|
||||
int devm_iio_device_match(struct device *dev, void *res, void *data)
|
||||
{
|
||||
struct iio_dev **r = res;
|
||||
if (!r || !*r) {
|
||||
|
@ -1317,6 +1317,7 @@ static int devm_iio_device_match(struct device *dev, void *res, void *data)
|
|||
}
|
||||
return *r == data;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_iio_device_match);
|
||||
|
||||
/**
|
||||
* devm_iio_device_alloc - Resource-managed iio_device_alloc()
|
||||
|
|
|
@ -57,6 +57,11 @@ bool iio_event_enabled(const struct iio_event_interface *ev_int)
|
|||
*
|
||||
* Note: The caller must make sure that this function is not running
|
||||
* concurrently for the same indio_dev more than once.
|
||||
*
|
||||
* This function may be safely used as soon as a valid reference to iio_dev has
|
||||
* been obtained via iio_device_alloc(), but any events that are submitted
|
||||
* before iio_device_register() has successfully completed will be silently
|
||||
* discarded.
|
||||
**/
|
||||
int iio_push_event(struct iio_dev *indio_dev, u64 ev_code, s64 timestamp)
|
||||
{
|
||||
|
@ -64,6 +69,9 @@ int iio_push_event(struct iio_dev *indio_dev, u64 ev_code, s64 timestamp)
|
|||
struct iio_event_data ev;
|
||||
int copied;
|
||||
|
||||
if (!ev_int)
|
||||
return 0;
|
||||
|
||||
/* Does anyone care? */
|
||||
if (iio_event_enabled(ev_int)) {
|
||||
|
||||
|
|
|
@ -119,6 +119,22 @@ void iio_trigger_unregister(struct iio_trigger *trig_info)
|
|||
}
|
||||
EXPORT_SYMBOL(iio_trigger_unregister);
|
||||
|
||||
int iio_trigger_set_immutable(struct iio_dev *indio_dev, struct iio_trigger *trig)
|
||||
{
|
||||
if (!indio_dev || !trig)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
WARN_ON(indio_dev->trig_readonly);
|
||||
|
||||
indio_dev->trig = iio_trigger_get(trig);
|
||||
indio_dev->trig_readonly = true;
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(iio_trigger_set_immutable);
|
||||
|
||||
/* Search for trigger by name, assuming iio_trigger_list_lock held */
|
||||
static struct iio_trigger *__iio_trigger_find_by_name(const char *name)
|
||||
{
|
||||
|
@ -255,6 +271,14 @@ static int iio_trigger_attach_poll_func(struct iio_trigger *trig,
|
|||
goto out_free_irq;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if we just registered to our own trigger: we determine that
|
||||
* this is the case if the IIO device and the trigger device share the
|
||||
* same parent device.
|
||||
*/
|
||||
if (pf->indio_dev->dev.parent == trig->dev.parent)
|
||||
trig->attached_own_device = true;
|
||||
|
||||
return ret;
|
||||
|
||||
out_free_irq:
|
||||
|
@ -279,6 +303,8 @@ static int iio_trigger_detach_poll_func(struct iio_trigger *trig,
|
|||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
if (pf->indio_dev->dev.parent == trig->dev.parent)
|
||||
trig->attached_own_device = false;
|
||||
iio_trigger_put_irq(trig, pf->irq);
|
||||
free_irq(pf->irq, pf);
|
||||
module_put(pf->indio_dev->info->driver_module);
|
||||
|
@ -384,6 +410,10 @@ static ssize_t iio_trigger_write_current(struct device *dev,
|
|||
mutex_unlock(&indio_dev->mlock);
|
||||
return -EBUSY;
|
||||
}
|
||||
if (indio_dev->trig_readonly) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return -EPERM;
|
||||
}
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
trig = iio_trigger_find_by_name(buf, len);
|
||||
|
@ -622,6 +652,71 @@ void devm_iio_trigger_free(struct device *dev, struct iio_trigger *iio_trig)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(devm_iio_trigger_free);
|
||||
|
||||
static void devm_iio_trigger_unreg(struct device *dev, void *res)
|
||||
{
|
||||
iio_trigger_unregister(*(struct iio_trigger **)res);
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_iio_trigger_register - Resource-managed iio_trigger_register()
|
||||
* @dev: device this trigger was allocated for
|
||||
* @trig_info: trigger to register
|
||||
*
|
||||
* Managed iio_trigger_register(). The IIO trigger registered with this
|
||||
* function is automatically unregistered on driver detach. This function
|
||||
* calls iio_trigger_register() internally. Refer to that function for more
|
||||
* information.
|
||||
*
|
||||
* If an iio_trigger registered with this function needs to be unregistered
|
||||
* separately, devm_iio_trigger_unregister() must be used.
|
||||
*
|
||||
* RETURNS:
|
||||
* 0 on success, negative error number on failure.
|
||||
*/
|
||||
int devm_iio_trigger_register(struct device *dev, struct iio_trigger *trig_info)
|
||||
{
|
||||
struct iio_trigger **ptr;
|
||||
int ret;
|
||||
|
||||
ptr = devres_alloc(devm_iio_trigger_unreg, sizeof(*ptr), GFP_KERNEL);
|
||||
if (!ptr)
|
||||
return -ENOMEM;
|
||||
|
||||
*ptr = trig_info;
|
||||
ret = iio_trigger_register(trig_info);
|
||||
if (!ret)
|
||||
devres_add(dev, ptr);
|
||||
else
|
||||
devres_free(ptr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_iio_trigger_register);
|
||||
|
||||
/**
|
||||
* devm_iio_trigger_unregister - Resource-managed iio_trigger_unregister()
|
||||
* @dev: device this iio_trigger belongs to
|
||||
* @trig_info: the trigger associated with the device
|
||||
*
|
||||
* Unregister trigger registered with devm_iio_trigger_register().
|
||||
*/
|
||||
void devm_iio_trigger_unregister(struct device *dev,
|
||||
struct iio_trigger *trig_info)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = devres_release(dev, devm_iio_trigger_unreg, devm_iio_trigger_match,
|
||||
trig_info);
|
||||
WARN_ON(rc);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_iio_trigger_unregister);
|
||||
|
||||
bool iio_trigger_using_own(struct iio_dev *indio_dev)
|
||||
{
|
||||
return indio_dev->trig->attached_own_device;
|
||||
}
|
||||
EXPORT_SYMBOL(iio_trigger_using_own);
|
||||
|
||||
void iio_device_register_trigger_consumer(struct iio_dev *indio_dev)
|
||||
{
|
||||
indio_dev->groups[indio_dev->groupcounter++] =
|
||||
|
|
|
@ -267,6 +267,19 @@ config PA12203001
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called pa12203001.
|
||||
|
||||
config SI1145
|
||||
tristate "SI1132 and SI1141/2/3/5/6/7 combined ALS, UV index and proximity sensor"
|
||||
depends on I2C
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say Y here if you want to build a driver for the Silicon Labs SI1132 or
|
||||
SI1141/2/3/5/6/7 combined ambient light, UV index and proximity sensor
|
||||
chips.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called si1145.
|
||||
|
||||
config STK3310
|
||||
tristate "STK3310 ALS and proximity sensor"
|
||||
depends on I2C
|
||||
|
@ -334,11 +347,11 @@ config US5182D
|
|||
will be called us5182d.
|
||||
|
||||
config VCNL4000
|
||||
tristate "VCNL4000 combined ALS and proximity sensor"
|
||||
tristate "VCNL4000/4010/4020 combined ALS and proximity sensor"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here if you want to build a driver for the Vishay VCNL4000
|
||||
combined ambient light and proximity sensor.
|
||||
Say Y here if you want to build a driver for the Vishay VCNL4000,
|
||||
VCNL4010, VCNL4020 combined ambient light and proximity sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called vcnl4000.
|
||||
|
|
|
@ -26,6 +26,7 @@ obj-$(CONFIG_OPT3001) += opt3001.o
|
|||
obj-$(CONFIG_PA12203001) += pa12203001.o
|
||||
obj-$(CONFIG_RPR0521) += rpr0521.o
|
||||
obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o
|
||||
obj-$(CONFIG_SI1145) += si1145.o
|
||||
obj-$(CONFIG_STK3310) += stk3310.o
|
||||
obj-$(CONFIG_TCS3414) += tcs3414.o
|
||||
obj-$(CONFIG_TCS3472) += tcs3472.o
|
||||
|
|
1404
drivers/iio/light/si1145.c
Normal file
1404
drivers/iio/light/si1145.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -894,7 +894,7 @@ static int us5182d_probe(struct i2c_client *client,
|
|||
goto out_err;
|
||||
|
||||
if (data->default_continuous) {
|
||||
pm_runtime_set_active(&client->dev);
|
||||
ret = pm_runtime_set_active(&client->dev);
|
||||
if (ret < 0)
|
||||
goto out_err;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* vcnl4000.c - Support for Vishay VCNL4000 combined ambient light and
|
||||
* proximity sensor
|
||||
* vcnl4000.c - Support for Vishay VCNL4000/4010/4020 combined ambient
|
||||
* light and proximity sensor
|
||||
*
|
||||
* Copyright 2012 Peter Meerwald <pmeerw@pmeerw.net>
|
||||
*
|
||||
|
@ -13,6 +13,8 @@
|
|||
* TODO:
|
||||
* allow to adjust IR current
|
||||
* proximity threshold and event handling
|
||||
* periodic ALS/proximity measurement (VCNL4010/20)
|
||||
* interrupts (VCNL4010/20)
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
@ -24,6 +26,8 @@
|
|||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define VCNL4000_DRV_NAME "vcnl4000"
|
||||
#define VCNL4000_ID 0x01
|
||||
#define VCNL4010_ID 0x02 /* for VCNL4020, VCNL4010 */
|
||||
|
||||
#define VCNL4000_COMMAND 0x80 /* Command register */
|
||||
#define VCNL4000_PROD_REV 0x81 /* Product ID and Revision ID */
|
||||
|
@ -37,13 +41,14 @@
|
|||
#define VCNL4000_PS_MOD_ADJ 0x8a /* Proximity modulator timing adjustment */
|
||||
|
||||
/* Bit masks for COMMAND register */
|
||||
#define VCNL4000_AL_RDY 0x40 /* ALS data ready? */
|
||||
#define VCNL4000_PS_RDY 0x20 /* proximity data ready? */
|
||||
#define VCNL4000_AL_OD 0x10 /* start on-demand ALS measurement */
|
||||
#define VCNL4000_PS_OD 0x08 /* start on-demand proximity measurement */
|
||||
#define VCNL4000_AL_RDY BIT(6) /* ALS data ready? */
|
||||
#define VCNL4000_PS_RDY BIT(5) /* proximity data ready? */
|
||||
#define VCNL4000_AL_OD BIT(4) /* start on-demand ALS measurement */
|
||||
#define VCNL4000_PS_OD BIT(3) /* start on-demand proximity measurement */
|
||||
|
||||
struct vcnl4000_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
static const struct i2c_device_id vcnl4000_id[] = {
|
||||
|
@ -59,16 +64,18 @@ static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask,
|
|||
__be16 buf;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND,
|
||||
req_mask);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto fail;
|
||||
|
||||
/* wait for data to become ready */
|
||||
while (tries--) {
|
||||
ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto fail;
|
||||
if (ret & rdy_mask)
|
||||
break;
|
||||
msleep(20); /* measurement takes up to 100 ms */
|
||||
|
@ -77,17 +84,23 @@ static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask,
|
|||
if (tries < 0) {
|
||||
dev_err(&data->client->dev,
|
||||
"vcnl4000_measure() failed, data not ready\n");
|
||||
return -EIO;
|
||||
ret = -EIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_read_i2c_block_data(data->client,
|
||||
data_reg, sizeof(buf), (u8 *) &buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto fail;
|
||||
|
||||
mutex_unlock(&data->lock);
|
||||
*val = be16_to_cpu(buf);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec vcnl4000_channels[] = {
|
||||
|
@ -105,7 +118,7 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev,
|
|||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
int ret;
|
||||
struct vcnl4000_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
|
@ -117,32 +130,27 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev,
|
|||
VCNL4000_AL_RESULT_HI, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_PROXIMITY:
|
||||
ret = vcnl4000_measure(data,
|
||||
VCNL4000_PS_OD, VCNL4000_PS_RDY,
|
||||
VCNL4000_PS_RESULT_HI, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
break;
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (chan->type == IIO_LIGHT) {
|
||||
*val = 0;
|
||||
*val2 = 250000;
|
||||
ret = IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (chan->type != IIO_LIGHT)
|
||||
return -EINVAL;
|
||||
|
||||
return ret;
|
||||
*val = 0;
|
||||
*val2 = 250000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info vcnl4000_info = {
|
||||
|
@ -155,7 +163,7 @@ static int vcnl4000_probe(struct i2c_client *client,
|
|||
{
|
||||
struct vcnl4000_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
int ret, prod_id;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
|
@ -164,13 +172,19 @@ static int vcnl4000_probe(struct i2c_client *client,
|
|||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
mutex_init(&data->lock);
|
||||
|
||||
ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dev_info(&client->dev, "VCNL4000 Ambient light/proximity sensor, Prod %02x, Rev: %02x\n",
|
||||
ret >> 4, ret & 0xf);
|
||||
prod_id = ret >> 4;
|
||||
if (prod_id != VCNL4010_ID && prod_id != VCNL4000_ID)
|
||||
return -ENODEV;
|
||||
|
||||
dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n",
|
||||
(prod_id == VCNL4010_ID) ? "VCNL4010/4020" : "VCNL4000",
|
||||
ret & 0xf);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &vcnl4000_info;
|
||||
|
|
|
@ -5,8 +5,22 @@
|
|||
|
||||
menu "Magnetometer sensors"
|
||||
|
||||
config AK8974
|
||||
tristate "Asahi Kasei AK8974 3-Axis Magnetometer"
|
||||
depends on I2C
|
||||
depends on OF
|
||||
select REGMAP_I2C
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for Asahi Kasei AK8974 or
|
||||
AMI305 I2C-based 3-axis magnetometer chips.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called ak8974.
|
||||
|
||||
config AK8975
|
||||
tristate "Asahi Kasei AK 3-Axis Magnetometer"
|
||||
tristate "Asahi Kasei AK8975 3-Axis Magnetometer"
|
||||
depends on I2C
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
select IIO_BUFFER
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_AK8974) += ak8974.o
|
||||
obj-$(CONFIG_AK8975) += ak8975.o
|
||||
obj-$(CONFIG_BMC150_MAGN) += bmc150_magn.o
|
||||
obj-$(CONFIG_BMC150_MAGN_I2C) += bmc150_magn_i2c.o
|
||||
|
|
860
drivers/iio/magnetometer/ak8974.c
Normal file
860
drivers/iio/magnetometer/ak8974.c
Normal file
|
@ -0,0 +1,860 @@
|
|||
/*
|
||||
* Driver for the Asahi Kasei EMD Corporation AK8974
|
||||
* and Aichi Steel AMI305 magnetometer chips.
|
||||
* Based on a patch from Samu Onkalo and the AK8975 IIO driver.
|
||||
*
|
||||
* Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
* Copyright (c) 2010 NVIDIA Corporation.
|
||||
* Copyright (C) 2016 Linaro Ltd.
|
||||
*
|
||||
* Author: Samu Onkalo <samu.p.onkalo@nokia.com>
|
||||
* Author: Linus Walleij <linus.walleij@linaro.org>
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h> /* For irq_get_irq_data() */
|
||||
#include <linux/completion.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
/*
|
||||
* 16-bit registers are little-endian. LSB is at the address defined below
|
||||
* and MSB is at the next higher address.
|
||||
*/
|
||||
|
||||
/* These registers are common for AK8974 and AMI305 */
|
||||
#define AK8974_SELFTEST 0x0C
|
||||
#define AK8974_SELFTEST_IDLE 0x55
|
||||
#define AK8974_SELFTEST_OK 0xAA
|
||||
|
||||
#define AK8974_INFO 0x0D
|
||||
|
||||
#define AK8974_WHOAMI 0x0F
|
||||
#define AK8974_WHOAMI_VALUE_AMI305 0x47
|
||||
#define AK8974_WHOAMI_VALUE_AK8974 0x48
|
||||
|
||||
#define AK8974_DATA_X 0x10
|
||||
#define AK8974_DATA_Y 0x12
|
||||
#define AK8974_DATA_Z 0x14
|
||||
#define AK8974_INT_SRC 0x16
|
||||
#define AK8974_STATUS 0x18
|
||||
#define AK8974_INT_CLEAR 0x1A
|
||||
#define AK8974_CTRL1 0x1B
|
||||
#define AK8974_CTRL2 0x1C
|
||||
#define AK8974_CTRL3 0x1D
|
||||
#define AK8974_INT_CTRL 0x1E
|
||||
#define AK8974_INT_THRES 0x26 /* Absolute any axis value threshold */
|
||||
#define AK8974_PRESET 0x30
|
||||
|
||||
/* AK8974-specific offsets */
|
||||
#define AK8974_OFFSET_X 0x20
|
||||
#define AK8974_OFFSET_Y 0x22
|
||||
#define AK8974_OFFSET_Z 0x24
|
||||
/* AMI305-specific offsets */
|
||||
#define AMI305_OFFSET_X 0x6C
|
||||
#define AMI305_OFFSET_Y 0x72
|
||||
#define AMI305_OFFSET_Z 0x78
|
||||
|
||||
/* Different temperature registers */
|
||||
#define AK8974_TEMP 0x31
|
||||
#define AMI305_TEMP 0x60
|
||||
|
||||
#define AK8974_INT_X_HIGH BIT(7) /* Axis over +threshold */
|
||||
#define AK8974_INT_Y_HIGH BIT(6)
|
||||
#define AK8974_INT_Z_HIGH BIT(5)
|
||||
#define AK8974_INT_X_LOW BIT(4) /* Axis below -threshold */
|
||||
#define AK8974_INT_Y_LOW BIT(3)
|
||||
#define AK8974_INT_Z_LOW BIT(2)
|
||||
#define AK8974_INT_RANGE BIT(1) /* Range overflow (any axis) */
|
||||
|
||||
#define AK8974_STATUS_DRDY BIT(6) /* Data ready */
|
||||
#define AK8974_STATUS_OVERRUN BIT(5) /* Data overrun */
|
||||
#define AK8974_STATUS_INT BIT(4) /* Interrupt occurred */
|
||||
|
||||
#define AK8974_CTRL1_POWER BIT(7) /* 0 = standby; 1 = active */
|
||||
#define AK8974_CTRL1_RATE BIT(4) /* 0 = 10 Hz; 1 = 20 Hz */
|
||||
#define AK8974_CTRL1_FORCE_EN BIT(1) /* 0 = normal; 1 = force */
|
||||
#define AK8974_CTRL1_MODE2 BIT(0) /* 0 */
|
||||
|
||||
#define AK8974_CTRL2_INT_EN BIT(4) /* 1 = enable interrupts */
|
||||
#define AK8974_CTRL2_DRDY_EN BIT(3) /* 1 = enable data ready signal */
|
||||
#define AK8974_CTRL2_DRDY_POL BIT(2) /* 1 = data ready active high */
|
||||
#define AK8974_CTRL2_RESDEF (AK8974_CTRL2_DRDY_POL)
|
||||
|
||||
#define AK8974_CTRL3_RESET BIT(7) /* Software reset */
|
||||
#define AK8974_CTRL3_FORCE BIT(6) /* Start forced measurement */
|
||||
#define AK8974_CTRL3_SELFTEST BIT(4) /* Set selftest register */
|
||||
#define AK8974_CTRL3_RESDEF 0x00
|
||||
|
||||
#define AK8974_INT_CTRL_XEN BIT(7) /* Enable interrupt for this axis */
|
||||
#define AK8974_INT_CTRL_YEN BIT(6)
|
||||
#define AK8974_INT_CTRL_ZEN BIT(5)
|
||||
#define AK8974_INT_CTRL_XYZEN (BIT(7)|BIT(6)|BIT(5))
|
||||
#define AK8974_INT_CTRL_POL BIT(3) /* 0 = active low; 1 = active high */
|
||||
#define AK8974_INT_CTRL_PULSE BIT(1) /* 0 = latched; 1 = pulse (50 usec) */
|
||||
#define AK8974_INT_CTRL_RESDEF (AK8974_INT_CTRL_XYZEN | AK8974_INT_CTRL_POL)
|
||||
|
||||
/* The AMI305 has elaborate FW version and serial number registers */
|
||||
#define AMI305_VER 0xE8
|
||||
#define AMI305_SN 0xEA
|
||||
|
||||
#define AK8974_MAX_RANGE 2048
|
||||
|
||||
#define AK8974_POWERON_DELAY 50
|
||||
#define AK8974_ACTIVATE_DELAY 1
|
||||
#define AK8974_SELFTEST_DELAY 1
|
||||
/*
|
||||
* Set the autosuspend to two orders of magnitude larger than the poweron
|
||||
* delay to make sane reasonable power tradeoff savings (5 seconds in
|
||||
* this case).
|
||||
*/
|
||||
#define AK8974_AUTOSUSPEND_DELAY 5000
|
||||
|
||||
#define AK8974_MEASTIME 3
|
||||
|
||||
#define AK8974_PWR_ON 1
|
||||
#define AK8974_PWR_OFF 0
|
||||
|
||||
/**
|
||||
* struct ak8974 - state container for the AK8974 driver
|
||||
* @i2c: parent I2C client
|
||||
* @orientation: mounting matrix, flipped axis etc
|
||||
* @map: regmap to access the AK8974 registers over I2C
|
||||
* @regs: the avdd and dvdd power regulators
|
||||
* @name: the name of the part
|
||||
* @variant: the whoami ID value (for selecting code paths)
|
||||
* @lock: locks the magnetometer for exclusive use during a measurement
|
||||
* @drdy_irq: uses the DRDY IRQ line
|
||||
* @drdy_complete: completion for DRDY
|
||||
* @drdy_active_low: the DRDY IRQ is active low
|
||||
*/
|
||||
struct ak8974 {
|
||||
struct i2c_client *i2c;
|
||||
struct iio_mount_matrix orientation;
|
||||
struct regmap *map;
|
||||
struct regulator_bulk_data regs[2];
|
||||
const char *name;
|
||||
u8 variant;
|
||||
struct mutex lock;
|
||||
bool drdy_irq;
|
||||
struct completion drdy_complete;
|
||||
bool drdy_active_low;
|
||||
};
|
||||
|
||||
static const char ak8974_reg_avdd[] = "avdd";
|
||||
static const char ak8974_reg_dvdd[] = "dvdd";
|
||||
|
||||
static int ak8974_set_power(struct ak8974 *ak8974, bool mode)
|
||||
{
|
||||
int ret;
|
||||
u8 val;
|
||||
|
||||
val = mode ? AK8974_CTRL1_POWER : 0;
|
||||
val |= AK8974_CTRL1_FORCE_EN;
|
||||
ret = regmap_write(ak8974->map, AK8974_CTRL1, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (mode)
|
||||
msleep(AK8974_ACTIVATE_DELAY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ak8974_reset(struct ak8974 *ak8974)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Power on to get register access. Sets CTRL1 reg to reset state */
|
||||
ret = ak8974_set_power(ak8974, AK8974_PWR_ON);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = regmap_write(ak8974->map, AK8974_CTRL2, AK8974_CTRL2_RESDEF);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = regmap_write(ak8974->map, AK8974_CTRL3, AK8974_CTRL3_RESDEF);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = regmap_write(ak8974->map, AK8974_INT_CTRL,
|
||||
AK8974_INT_CTRL_RESDEF);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* After reset, power off is default state */
|
||||
return ak8974_set_power(ak8974, AK8974_PWR_OFF);
|
||||
}
|
||||
|
||||
static int ak8974_configure(struct ak8974 *ak8974)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_write(ak8974->map, AK8974_CTRL2, AK8974_CTRL2_DRDY_EN |
|
||||
AK8974_CTRL2_INT_EN);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = regmap_write(ak8974->map, AK8974_CTRL3, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = regmap_write(ak8974->map, AK8974_INT_CTRL, AK8974_INT_CTRL_POL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return regmap_write(ak8974->map, AK8974_PRESET, 0);
|
||||
}
|
||||
|
||||
static int ak8974_trigmeas(struct ak8974 *ak8974)
|
||||
{
|
||||
unsigned int clear;
|
||||
u8 mask;
|
||||
u8 val;
|
||||
int ret;
|
||||
|
||||
/* Clear any previous measurement overflow status */
|
||||
ret = regmap_read(ak8974->map, AK8974_INT_CLEAR, &clear);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* If we have a DRDY IRQ line, use it */
|
||||
if (ak8974->drdy_irq) {
|
||||
mask = AK8974_CTRL2_INT_EN |
|
||||
AK8974_CTRL2_DRDY_EN |
|
||||
AK8974_CTRL2_DRDY_POL;
|
||||
val = AK8974_CTRL2_DRDY_EN;
|
||||
|
||||
if (!ak8974->drdy_active_low)
|
||||
val |= AK8974_CTRL2_DRDY_POL;
|
||||
|
||||
init_completion(&ak8974->drdy_complete);
|
||||
ret = regmap_update_bits(ak8974->map, AK8974_CTRL2,
|
||||
mask, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Force a measurement */
|
||||
return regmap_update_bits(ak8974->map,
|
||||
AK8974_CTRL3,
|
||||
AK8974_CTRL3_FORCE,
|
||||
AK8974_CTRL3_FORCE);
|
||||
}
|
||||
|
||||
static int ak8974_await_drdy(struct ak8974 *ak8974)
|
||||
{
|
||||
int timeout = 2;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
if (ak8974->drdy_irq) {
|
||||
ret = wait_for_completion_timeout(&ak8974->drdy_complete,
|
||||
1 + msecs_to_jiffies(1000));
|
||||
if (!ret) {
|
||||
dev_err(&ak8974->i2c->dev,
|
||||
"timeout waiting for DRDY IRQ\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Default delay-based poll loop */
|
||||
do {
|
||||
msleep(AK8974_MEASTIME);
|
||||
ret = regmap_read(ak8974->map, AK8974_STATUS, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (val & AK8974_STATUS_DRDY)
|
||||
return 0;
|
||||
} while (--timeout);
|
||||
if (!timeout) {
|
||||
dev_err(&ak8974->i2c->dev,
|
||||
"timeout waiting for DRDY\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ak8974_getresult(struct ak8974 *ak8974, s16 *result)
|
||||
{
|
||||
unsigned int src;
|
||||
int ret;
|
||||
|
||||
ret = ak8974_await_drdy(ak8974);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = regmap_read(ak8974->map, AK8974_INT_SRC, &src);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Out of range overflow! Strong magnet close? */
|
||||
if (src & AK8974_INT_RANGE) {
|
||||
dev_err(&ak8974->i2c->dev,
|
||||
"range overflow in sensor\n");
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
ret = regmap_bulk_read(ak8974->map, AK8974_DATA_X, result, 6);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t ak8974_drdy_irq(int irq, void *d)
|
||||
{
|
||||
struct ak8974 *ak8974 = d;
|
||||
|
||||
if (!ak8974->drdy_irq)
|
||||
return IRQ_NONE;
|
||||
|
||||
/* TODO: timestamp here to get good measurement stamps */
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
static irqreturn_t ak8974_drdy_irq_thread(int irq, void *d)
|
||||
{
|
||||
struct ak8974 *ak8974 = d;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
/* Check if this was a DRDY from us */
|
||||
ret = regmap_read(ak8974->map, AK8974_STATUS, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(&ak8974->i2c->dev, "error reading DRDY status\n");
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
if (val & AK8974_STATUS_DRDY) {
|
||||
/* Yes this was our IRQ */
|
||||
complete(&ak8974->drdy_complete);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* We may be on a shared IRQ, let the next client check */
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static int ak8974_selftest(struct ak8974 *ak8974)
|
||||
{
|
||||
struct device *dev = &ak8974->i2c->dev;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(ak8974->map, AK8974_SELFTEST, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (val != AK8974_SELFTEST_IDLE) {
|
||||
dev_err(dev, "selftest not idle before test\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Trigger self-test */
|
||||
ret = regmap_update_bits(ak8974->map,
|
||||
AK8974_CTRL3,
|
||||
AK8974_CTRL3_SELFTEST,
|
||||
AK8974_CTRL3_SELFTEST);
|
||||
if (ret) {
|
||||
dev_err(dev, "could not write CTRL3\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
msleep(AK8974_SELFTEST_DELAY);
|
||||
|
||||
ret = regmap_read(ak8974->map, AK8974_SELFTEST, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (val != AK8974_SELFTEST_OK) {
|
||||
dev_err(dev, "selftest result NOT OK (%02x)\n", val);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ret = regmap_read(ak8974->map, AK8974_SELFTEST, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (val != AK8974_SELFTEST_IDLE) {
|
||||
dev_err(dev, "selftest not idle after test (%02x)\n", val);
|
||||
return -EIO;
|
||||
}
|
||||
dev_dbg(dev, "passed self-test\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ak8974_get_u16_val(struct ak8974 *ak8974, u8 reg, u16 *val)
|
||||
{
|
||||
int ret;
|
||||
u16 bulk;
|
||||
|
||||
ret = regmap_bulk_read(ak8974->map, reg, &bulk, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val = le16_to_cpu(bulk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ak8974_detect(struct ak8974 *ak8974)
|
||||
{
|
||||
unsigned int whoami;
|
||||
const char *name;
|
||||
int ret;
|
||||
unsigned int fw;
|
||||
u16 sn;
|
||||
|
||||
ret = regmap_read(ak8974->map, AK8974_WHOAMI, &whoami);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (whoami) {
|
||||
case AK8974_WHOAMI_VALUE_AMI305:
|
||||
name = "ami305";
|
||||
ret = regmap_read(ak8974->map, AMI305_VER, &fw);
|
||||
if (ret)
|
||||
return ret;
|
||||
fw &= 0x7f; /* only bits 0 thru 6 valid */
|
||||
ret = ak8974_get_u16_val(ak8974, AMI305_SN, &sn);
|
||||
if (ret)
|
||||
return ret;
|
||||
dev_info(&ak8974->i2c->dev,
|
||||
"detected %s, FW ver %02x, S/N: %04x\n",
|
||||
name, fw, sn);
|
||||
break;
|
||||
case AK8974_WHOAMI_VALUE_AK8974:
|
||||
name = "ak8974";
|
||||
dev_info(&ak8974->i2c->dev, "detected AK8974\n");
|
||||
break;
|
||||
default:
|
||||
dev_err(&ak8974->i2c->dev, "unsupported device (%02x) ",
|
||||
whoami);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ak8974->name = name;
|
||||
ak8974->variant = whoami;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ak8974_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct ak8974 *ak8974 = iio_priv(indio_dev);
|
||||
s16 hw_values[3];
|
||||
int ret = -EINVAL;
|
||||
|
||||
pm_runtime_get_sync(&ak8974->i2c->dev);
|
||||
mutex_lock(&ak8974->lock);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (chan->address > 2) {
|
||||
dev_err(&ak8974->i2c->dev, "faulty channel address\n");
|
||||
ret = -EIO;
|
||||
goto out_unlock;
|
||||
}
|
||||
ret = ak8974_trigmeas(ak8974);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
ret = ak8974_getresult(ak8974, hw_values);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
|
||||
/*
|
||||
* We read all axes and discard all but one, for optimized
|
||||
* reading, use the triggered buffer.
|
||||
*/
|
||||
*val = le16_to_cpu(hw_values[chan->address]);
|
||||
|
||||
ret = IIO_VAL_INT;
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&ak8974->lock);
|
||||
pm_runtime_mark_last_busy(&ak8974->i2c->dev);
|
||||
pm_runtime_put_autosuspend(&ak8974->i2c->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ak8974_fill_buffer(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ak8974 *ak8974 = iio_priv(indio_dev);
|
||||
int ret;
|
||||
s16 hw_values[8]; /* Three axes + 64bit padding */
|
||||
|
||||
pm_runtime_get_sync(&ak8974->i2c->dev);
|
||||
mutex_lock(&ak8974->lock);
|
||||
|
||||
ret = ak8974_trigmeas(ak8974);
|
||||
if (ret) {
|
||||
dev_err(&ak8974->i2c->dev, "error triggering measure\n");
|
||||
goto out_unlock;
|
||||
}
|
||||
ret = ak8974_getresult(ak8974, hw_values);
|
||||
if (ret) {
|
||||
dev_err(&ak8974->i2c->dev, "error getting measures\n");
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, hw_values,
|
||||
iio_get_time_ns(indio_dev));
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&ak8974->lock);
|
||||
pm_runtime_mark_last_busy(&ak8974->i2c->dev);
|
||||
pm_runtime_put_autosuspend(&ak8974->i2c->dev);
|
||||
}
|
||||
|
||||
static irqreturn_t ak8974_handle_trigger(int irq, void *p)
|
||||
{
|
||||
const struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
|
||||
ak8974_fill_buffer(indio_dev);
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct iio_mount_matrix *
|
||||
ak8974_get_mount_matrix(const struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct ak8974 *ak8974 = iio_priv(indio_dev);
|
||||
|
||||
return &ak8974->orientation;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec_ext_info ak8974_ext_info[] = {
|
||||
IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, ak8974_get_mount_matrix),
|
||||
{ },
|
||||
};
|
||||
|
||||
#define AK8974_AXIS_CHANNEL(axis, index) \
|
||||
{ \
|
||||
.type = IIO_MAGN, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_##axis, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.ext_info = ak8974_ext_info, \
|
||||
.address = index, \
|
||||
.scan_index = index, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 16, \
|
||||
.storagebits = 16, \
|
||||
.endianness = IIO_LE \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ak8974_channels[] = {
|
||||
AK8974_AXIS_CHANNEL(X, 0),
|
||||
AK8974_AXIS_CHANNEL(Y, 1),
|
||||
AK8974_AXIS_CHANNEL(Z, 2),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3),
|
||||
};
|
||||
|
||||
static const unsigned long ak8974_scan_masks[] = { 0x7, 0 };
|
||||
|
||||
static const struct iio_info ak8974_info = {
|
||||
.read_raw = &ak8974_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static bool ak8974_writeable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
struct i2c_client *i2c = to_i2c_client(dev);
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(i2c);
|
||||
struct ak8974 *ak8974 = iio_priv(indio_dev);
|
||||
|
||||
switch (reg) {
|
||||
case AK8974_CTRL1:
|
||||
case AK8974_CTRL2:
|
||||
case AK8974_CTRL3:
|
||||
case AK8974_INT_CTRL:
|
||||
case AK8974_INT_THRES:
|
||||
case AK8974_INT_THRES + 1:
|
||||
case AK8974_PRESET:
|
||||
case AK8974_PRESET + 1:
|
||||
return true;
|
||||
case AK8974_OFFSET_X:
|
||||
case AK8974_OFFSET_X + 1:
|
||||
case AK8974_OFFSET_Y:
|
||||
case AK8974_OFFSET_Y + 1:
|
||||
case AK8974_OFFSET_Z:
|
||||
case AK8974_OFFSET_Z + 1:
|
||||
if (ak8974->variant == AK8974_WHOAMI_VALUE_AK8974)
|
||||
return true;
|
||||
return false;
|
||||
case AMI305_OFFSET_X:
|
||||
case AMI305_OFFSET_X + 1:
|
||||
case AMI305_OFFSET_Y:
|
||||
case AMI305_OFFSET_Y + 1:
|
||||
case AMI305_OFFSET_Z:
|
||||
case AMI305_OFFSET_Z + 1:
|
||||
if (ak8974->variant == AK8974_WHOAMI_VALUE_AMI305)
|
||||
return true;
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct regmap_config ak8974_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = 0xff,
|
||||
.writeable_reg = ak8974_writeable_reg,
|
||||
};
|
||||
|
||||
static int ak8974_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct ak8974 *ak8974;
|
||||
unsigned long irq_trig;
|
||||
int irq = i2c->irq;
|
||||
int ret;
|
||||
|
||||
/* Register with IIO */
|
||||
indio_dev = devm_iio_device_alloc(&i2c->dev, sizeof(*ak8974));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ak8974 = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(i2c, indio_dev);
|
||||
ak8974->i2c = i2c;
|
||||
mutex_init(&ak8974->lock);
|
||||
|
||||
ret = of_iio_read_mount_matrix(&i2c->dev,
|
||||
"mount-matrix",
|
||||
&ak8974->orientation);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ak8974->regs[0].supply = ak8974_reg_avdd;
|
||||
ak8974->regs[1].supply = ak8974_reg_dvdd;
|
||||
|
||||
ret = devm_regulator_bulk_get(&i2c->dev,
|
||||
ARRAY_SIZE(ak8974->regs),
|
||||
ak8974->regs);
|
||||
if (ret < 0) {
|
||||
dev_err(&i2c->dev, "cannot get regulators\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(ak8974->regs), ak8974->regs);
|
||||
if (ret < 0) {
|
||||
dev_err(&i2c->dev, "cannot enable regulators\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Take runtime PM online */
|
||||
pm_runtime_get_noresume(&i2c->dev);
|
||||
pm_runtime_set_active(&i2c->dev);
|
||||
pm_runtime_enable(&i2c->dev);
|
||||
|
||||
ak8974->map = devm_regmap_init_i2c(i2c, &ak8974_regmap_config);
|
||||
if (IS_ERR(ak8974->map)) {
|
||||
dev_err(&i2c->dev, "failed to allocate register map\n");
|
||||
return PTR_ERR(ak8974->map);
|
||||
}
|
||||
|
||||
ret = ak8974_set_power(ak8974, AK8974_PWR_ON);
|
||||
if (ret) {
|
||||
dev_err(&i2c->dev, "could not power on\n");
|
||||
goto power_off;
|
||||
}
|
||||
|
||||
ret = ak8974_detect(ak8974);
|
||||
if (ret) {
|
||||
dev_err(&i2c->dev, "neither AK8974 nor AMI305 found\n");
|
||||
goto power_off;
|
||||
}
|
||||
|
||||
ret = ak8974_selftest(ak8974);
|
||||
if (ret)
|
||||
dev_err(&i2c->dev, "selftest failed (continuing anyway)\n");
|
||||
|
||||
ret = ak8974_reset(ak8974);
|
||||
if (ret) {
|
||||
dev_err(&i2c->dev, "AK8974 reset failed\n");
|
||||
goto power_off;
|
||||
}
|
||||
|
||||
pm_runtime_set_autosuspend_delay(&i2c->dev,
|
||||
AK8974_AUTOSUSPEND_DELAY);
|
||||
pm_runtime_use_autosuspend(&i2c->dev);
|
||||
pm_runtime_put(&i2c->dev);
|
||||
|
||||
indio_dev->dev.parent = &i2c->dev;
|
||||
indio_dev->channels = ak8974_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ak8974_channels);
|
||||
indio_dev->info = &ak8974_info;
|
||||
indio_dev->available_scan_masks = ak8974_scan_masks;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->name = ak8974->name;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
ak8974_handle_trigger,
|
||||
NULL);
|
||||
if (ret) {
|
||||
dev_err(&i2c->dev, "triggered buffer setup failed\n");
|
||||
goto disable_pm;
|
||||
}
|
||||
|
||||
/* If we have a valid DRDY IRQ, make use of it */
|
||||
if (irq > 0) {
|
||||
irq_trig = irqd_get_trigger_type(irq_get_irq_data(irq));
|
||||
if (irq_trig == IRQF_TRIGGER_RISING) {
|
||||
dev_info(&i2c->dev, "enable rising edge DRDY IRQ\n");
|
||||
} else if (irq_trig == IRQF_TRIGGER_FALLING) {
|
||||
ak8974->drdy_active_low = true;
|
||||
dev_info(&i2c->dev, "enable falling edge DRDY IRQ\n");
|
||||
} else {
|
||||
irq_trig = IRQF_TRIGGER_RISING;
|
||||
}
|
||||
irq_trig |= IRQF_ONESHOT;
|
||||
irq_trig |= IRQF_SHARED;
|
||||
|
||||
ret = devm_request_threaded_irq(&i2c->dev,
|
||||
irq,
|
||||
ak8974_drdy_irq,
|
||||
ak8974_drdy_irq_thread,
|
||||
irq_trig,
|
||||
ak8974->name,
|
||||
ak8974);
|
||||
if (ret) {
|
||||
dev_err(&i2c->dev, "unable to request DRDY IRQ "
|
||||
"- proceeding without IRQ\n");
|
||||
goto no_irq;
|
||||
}
|
||||
ak8974->drdy_irq = true;
|
||||
}
|
||||
|
||||
no_irq:
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&i2c->dev, "device register failed\n");
|
||||
goto cleanup_buffer;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
cleanup_buffer:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
disable_pm:
|
||||
pm_runtime_put_noidle(&i2c->dev);
|
||||
pm_runtime_disable(&i2c->dev);
|
||||
ak8974_set_power(ak8974, AK8974_PWR_OFF);
|
||||
power_off:
|
||||
regulator_bulk_disable(ARRAY_SIZE(ak8974->regs), ak8974->regs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __exit ak8974_remove(struct i2c_client *i2c)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(i2c);
|
||||
struct ak8974 *ak8974 = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
pm_runtime_get_sync(&i2c->dev);
|
||||
pm_runtime_put_noidle(&i2c->dev);
|
||||
pm_runtime_disable(&i2c->dev);
|
||||
ak8974_set_power(ak8974, AK8974_PWR_OFF);
|
||||
regulator_bulk_disable(ARRAY_SIZE(ak8974->regs), ak8974->regs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused ak8974_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct ak8974 *ak8974 =
|
||||
iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
|
||||
|
||||
ak8974_set_power(ak8974, AK8974_PWR_OFF);
|
||||
regulator_bulk_disable(ARRAY_SIZE(ak8974->regs), ak8974->regs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused ak8974_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct ak8974 *ak8974 =
|
||||
iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
|
||||
int ret;
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(ak8974->regs), ak8974->regs);
|
||||
if (ret)
|
||||
return ret;
|
||||
msleep(AK8974_POWERON_DELAY);
|
||||
ret = ak8974_set_power(ak8974, AK8974_PWR_ON);
|
||||
if (ret)
|
||||
goto out_regulator_disable;
|
||||
|
||||
ret = ak8974_configure(ak8974);
|
||||
if (ret)
|
||||
goto out_disable_power;
|
||||
|
||||
return 0;
|
||||
|
||||
out_disable_power:
|
||||
ak8974_set_power(ak8974, AK8974_PWR_OFF);
|
||||
out_regulator_disable:
|
||||
regulator_bulk_disable(ARRAY_SIZE(ak8974->regs), ak8974->regs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops ak8974_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
SET_RUNTIME_PM_OPS(ak8974_runtime_suspend,
|
||||
ak8974_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct i2c_device_id ak8974_id[] = {
|
||||
{"ami305", 0 },
|
||||
{"ak8974", 0 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ak8974_id);
|
||||
|
||||
static const struct of_device_id ak8974_of_match[] = {
|
||||
{ .compatible = "asahi-kasei,ak8974", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ak8974_of_match);
|
||||
|
||||
static struct i2c_driver ak8974_driver = {
|
||||
.driver = {
|
||||
.name = "ak8974",
|
||||
.pm = &ak8974_dev_pm_ops,
|
||||
.of_match_table = of_match_ptr(ak8974_of_match),
|
||||
},
|
||||
.probe = ak8974_probe,
|
||||
.remove = __exit_p(ak8974_remove),
|
||||
.id_table = ak8974_id,
|
||||
};
|
||||
module_i2c_driver(ak8974_driver);
|
||||
|
||||
MODULE_DESCRIPTION("AK8974 and AMI305 3-axis magnetometer driver");
|
||||
MODULE_AUTHOR("Samu Onkalo");
|
||||
MODULE_AUTHOR("Linus Walleij");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -154,34 +154,41 @@ static int mag3110_read_raw(struct iio_dev *indio_dev,
|
|||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
return -EBUSY;
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_MAGN: /* in 0.1 uT / LSB */
|
||||
ret = mag3110_read(data, buffer);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto release;
|
||||
*val = sign_extend32(
|
||||
be16_to_cpu(buffer[chan->scan_index]), 15);
|
||||
return IIO_VAL_INT;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_TEMP: /* in 1 C / LSB */
|
||||
mutex_lock(&data->lock);
|
||||
ret = mag3110_request(data);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
goto release;
|
||||
}
|
||||
ret = i2c_smbus_read_byte_data(data->client,
|
||||
MAG3110_DIE_TEMP);
|
||||
mutex_unlock(&data->lock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto release;
|
||||
*val = sign_extend32(ret, 7);
|
||||
return IIO_VAL_INT;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
}
|
||||
release:
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
return ret;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_MAGN:
|
||||
|
|
|
@ -185,4 +185,26 @@ config HP206C
|
|||
This driver can also be built as a module. If so, the module will
|
||||
be called hp206c.
|
||||
|
||||
config ZPA2326
|
||||
tristate "Murata ZPA2326 pressure sensor driver"
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select REGMAP
|
||||
select ZPA2326_I2C if I2C
|
||||
select ZPA2326_SPI if SPI_MASTER
|
||||
help
|
||||
Say Y here to build support for the Murata ZPA2326 pressure and
|
||||
temperature sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called zpa2326.
|
||||
|
||||
config ZPA2326_I2C
|
||||
tristate
|
||||
select REGMAP_I2C
|
||||
|
||||
config ZPA2326_SPI
|
||||
tristate
|
||||
select REGMAP_SPI
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -22,6 +22,9 @@ st_pressure-y := st_pressure_core.o
|
|||
st_pressure-$(CONFIG_IIO_BUFFER) += st_pressure_buffer.o
|
||||
obj-$(CONFIG_T5403) += t5403.o
|
||||
obj-$(CONFIG_HP206C) += hp206c.o
|
||||
obj-$(CONFIG_ZPA2326) += zpa2326.o
|
||||
obj-$(CONFIG_ZPA2326_I2C) += zpa2326_i2c.o
|
||||
obj-$(CONFIG_ZPA2326_SPI) += zpa2326_spi.o
|
||||
|
||||
obj-$(CONFIG_IIO_ST_PRESS_I2C) += st_pressure_i2c.o
|
||||
obj-$(CONFIG_IIO_ST_PRESS_SPI) += st_pressure_spi.o
|
||||
|
|
|
@ -416,8 +416,7 @@ static int ms5611_init(struct iio_dev *indio_dev)
|
|||
return 0;
|
||||
|
||||
err_regulator_disable:
|
||||
if (!IS_ERR_OR_NULL(st->vdd))
|
||||
regulator_disable(st->vdd);
|
||||
regulator_disable(st->vdd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -425,8 +424,7 @@ static void ms5611_fini(const struct iio_dev *indio_dev)
|
|||
{
|
||||
const struct ms5611_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (!IS_ERR_OR_NULL(st->vdd))
|
||||
regulator_disable(st->vdd);
|
||||
regulator_disable(st->vdd);
|
||||
}
|
||||
|
||||
int ms5611_probe(struct iio_dev *indio_dev, struct device *dev,
|
||||
|
|
1735
drivers/iio/pressure/zpa2326.c
Normal file
1735
drivers/iio/pressure/zpa2326.c
Normal file
File diff suppressed because it is too large
Load diff
89
drivers/iio/pressure/zpa2326.h
Normal file
89
drivers/iio/pressure/zpa2326.h
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Murata ZPA2326 pressure and temperature sensor IIO driver
|
||||
*
|
||||
* Copyright (c) 2016 Parrot S.A.
|
||||
*
|
||||
* Author: Gregor Boirie <gregor.boirie@parrot.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 _ZPA2326_H
|
||||
#define _ZPA2326_H
|
||||
|
||||
/* Register map. */
|
||||
#define ZPA2326_REF_P_XL_REG (0x8)
|
||||
#define ZPA2326_REF_P_L_REG (0x9)
|
||||
#define ZPA2326_REF_P_H_REG (0xa)
|
||||
#define ZPA2326_DEVICE_ID_REG (0xf)
|
||||
#define ZPA2326_DEVICE_ID (0xb9)
|
||||
#define ZPA2326_RES_CONF_REG (0x10)
|
||||
#define ZPA2326_CTRL_REG0_REG (0x20)
|
||||
#define ZPA2326_CTRL_REG0_ONE_SHOT BIT(0)
|
||||
#define ZPA2326_CTRL_REG0_ENABLE BIT(1)
|
||||
#define ZPA2326_CTRL_REG1_REG (0x21)
|
||||
#define ZPA2326_CTRL_REG1_MASK_DATA_READY BIT(2)
|
||||
#define ZPA2326_CTRL_REG2_REG (0x22)
|
||||
#define ZPA2326_CTRL_REG2_SWRESET BIT(2)
|
||||
#define ZPA2326_CTRL_REG3_REG (0x23)
|
||||
#define ZPA2326_CTRL_REG3_ODR_SHIFT (4)
|
||||
#define ZPA2326_CTRL_REG3_ENABLE_MEAS BIT(7)
|
||||
#define ZPA2326_INT_SOURCE_REG (0x24)
|
||||
#define ZPA2326_INT_SOURCE_DATA_READY BIT(2)
|
||||
#define ZPA2326_THS_P_LOW_REG (0x25)
|
||||
#define ZPA2326_THS_P_HIGH_REG (0x26)
|
||||
#define ZPA2326_STATUS_REG (0x27)
|
||||
#define ZPA2326_STATUS_P_DA BIT(1)
|
||||
#define ZPA2326_STATUS_FIFO_E BIT(2)
|
||||
#define ZPA2326_STATUS_P_OR BIT(5)
|
||||
#define ZPA2326_PRESS_OUT_XL_REG (0x28)
|
||||
#define ZPA2326_PRESS_OUT_L_REG (0x29)
|
||||
#define ZPA2326_PRESS_OUT_H_REG (0x2a)
|
||||
#define ZPA2326_TEMP_OUT_L_REG (0x2b)
|
||||
#define ZPA2326_TEMP_OUT_H_REG (0x2c)
|
||||
|
||||
struct device;
|
||||
struct regmap;
|
||||
|
||||
bool zpa2326_isreg_writeable(struct device *dev, unsigned int reg);
|
||||
bool zpa2326_isreg_readable(struct device *dev, unsigned int reg);
|
||||
bool zpa2326_isreg_precious(struct device *dev, unsigned int reg);
|
||||
|
||||
/**
|
||||
* zpa2326_probe() - Instantiate and register core ZPA2326 IIO device
|
||||
* @parent: Hardware sampling device the created IIO device will be a child of.
|
||||
* @name: Arbitrary name to identify the device.
|
||||
* @irq: Interrupt line, negative if none.
|
||||
* @hwid: Expected device hardware id.
|
||||
* @regmap: Registers map used to abstract underlying bus accesses.
|
||||
*
|
||||
* Return: Zero when successful, a negative error code otherwise.
|
||||
*/
|
||||
int zpa2326_probe(struct device *parent,
|
||||
const char *name,
|
||||
int irq,
|
||||
unsigned int hwid,
|
||||
struct regmap *regmap);
|
||||
|
||||
/**
|
||||
* zpa2326_remove() - Unregister and destroy core ZPA2326 IIO device.
|
||||
* @parent: Hardware sampling device the IIO device to remove is a child of.
|
||||
*/
|
||||
void zpa2326_remove(const struct device *parent);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
#include <linux/pm.h>
|
||||
extern const struct dev_pm_ops zpa2326_pm_ops;
|
||||
#define ZPA2326_PM_OPS (&zpa2326_pm_ops)
|
||||
#else
|
||||
#define ZPA2326_PM_OPS (NULL)
|
||||
#endif
|
||||
|
||||
#endif
|
99
drivers/iio/pressure/zpa2326_i2c.c
Normal file
99
drivers/iio/pressure/zpa2326_i2c.c
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Murata ZPA2326 I2C pressure and temperature sensor driver
|
||||
*
|
||||
* Copyright (c) 2016 Parrot S.A.
|
||||
*
|
||||
* Author: Gregor Boirie <gregor.boirie@parrot.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/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/of_device.h>
|
||||
#include "zpa2326.h"
|
||||
|
||||
/*
|
||||
* read_flag_mask:
|
||||
* - address bit 7 must be set to request a register read operation
|
||||
*/
|
||||
static const struct regmap_config zpa2326_regmap_i2c_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.writeable_reg = zpa2326_isreg_writeable,
|
||||
.readable_reg = zpa2326_isreg_readable,
|
||||
.precious_reg = zpa2326_isreg_precious,
|
||||
.max_register = ZPA2326_TEMP_OUT_H_REG,
|
||||
.read_flag_mask = BIT(7),
|
||||
.cache_type = REGCACHE_NONE,
|
||||
};
|
||||
|
||||
static unsigned int zpa2326_i2c_hwid(const struct i2c_client *client)
|
||||
{
|
||||
#define ZPA2326_SA0(_addr) (_addr & BIT(0))
|
||||
#define ZPA2326_DEVICE_ID_SA0_SHIFT (1)
|
||||
|
||||
/* Identification register bit 1 mirrors device address bit 0. */
|
||||
return (ZPA2326_DEVICE_ID |
|
||||
(ZPA2326_SA0(client->addr) << ZPA2326_DEVICE_ID_SA0_SHIFT));
|
||||
}
|
||||
|
||||
static int zpa2326_probe_i2c(struct i2c_client *client,
|
||||
const struct i2c_device_id *i2c_id)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = devm_regmap_init_i2c(client, &zpa2326_regmap_i2c_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&client->dev, "failed to init registers map");
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
return zpa2326_probe(&client->dev, i2c_id->name, client->irq,
|
||||
zpa2326_i2c_hwid(client), regmap);
|
||||
}
|
||||
|
||||
static int zpa2326_remove_i2c(struct i2c_client *client)
|
||||
{
|
||||
zpa2326_remove(&client->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id zpa2326_i2c_ids[] = {
|
||||
{ "zpa2326", 0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, zpa2326_i2c_ids);
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
static const struct of_device_id zpa2326_i2c_matches[] = {
|
||||
{ .compatible = "murata,zpa2326" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, zpa2326_i2c_matches);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver zpa2326_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "zpa2326-i2c",
|
||||
.of_match_table = of_match_ptr(zpa2326_i2c_matches),
|
||||
.pm = ZPA2326_PM_OPS,
|
||||
},
|
||||
.probe = zpa2326_probe_i2c,
|
||||
.remove = zpa2326_remove_i2c,
|
||||
.id_table = zpa2326_i2c_ids,
|
||||
};
|
||||
module_i2c_driver(zpa2326_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Gregor Boirie <gregor.boirie@parrot.com>");
|
||||
MODULE_DESCRIPTION("I2C driver for Murata ZPA2326 pressure sensor");
|
||||
MODULE_LICENSE("GPL v2");
|
103
drivers/iio/pressure/zpa2326_spi.c
Normal file
103
drivers/iio/pressure/zpa2326_spi.c
Normal file
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Murata ZPA2326 SPI pressure and temperature sensor driver
|
||||
*
|
||||
* Copyright (c) 2016 Parrot S.A.
|
||||
*
|
||||
* Author: Gregor Boirie <gregor.boirie@parrot.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/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/of_device.h>
|
||||
#include "zpa2326.h"
|
||||
|
||||
/*
|
||||
* read_flag_mask:
|
||||
* - address bit 7 must be set to request a register read operation
|
||||
* - address bit 6 must be set to request register address auto increment
|
||||
*/
|
||||
static const struct regmap_config zpa2326_regmap_spi_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.writeable_reg = zpa2326_isreg_writeable,
|
||||
.readable_reg = zpa2326_isreg_readable,
|
||||
.precious_reg = zpa2326_isreg_precious,
|
||||
.max_register = ZPA2326_TEMP_OUT_H_REG,
|
||||
.read_flag_mask = BIT(7) | BIT(6),
|
||||
.cache_type = REGCACHE_NONE,
|
||||
};
|
||||
|
||||
static int zpa2326_probe_spi(struct spi_device *spi)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
int err;
|
||||
|
||||
regmap = devm_regmap_init_spi(spi, &zpa2326_regmap_spi_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&spi->dev, "failed to init registers map");
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
/*
|
||||
* Enforce SPI slave settings to prevent from DT misconfiguration.
|
||||
*
|
||||
* Clock is idle high. Sampling happens on trailing edge, i.e., rising
|
||||
* edge. Maximum bus frequency is 1 MHz. Registers are 8 bits wide.
|
||||
*/
|
||||
spi->mode = SPI_MODE_3;
|
||||
spi->max_speed_hz = min(spi->max_speed_hz, 1000000U);
|
||||
spi->bits_per_word = 8;
|
||||
err = spi_setup(spi);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return zpa2326_probe(&spi->dev, spi_get_device_id(spi)->name,
|
||||
spi->irq, ZPA2326_DEVICE_ID, regmap);
|
||||
}
|
||||
|
||||
static int zpa2326_remove_spi(struct spi_device *spi)
|
||||
{
|
||||
zpa2326_remove(&spi->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id zpa2326_spi_ids[] = {
|
||||
{ "zpa2326", 0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, zpa2326_spi_ids);
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
static const struct of_device_id zpa2326_spi_matches[] = {
|
||||
{ .compatible = "murata,zpa2326" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, zpa2326_spi_matches);
|
||||
#endif
|
||||
|
||||
static struct spi_driver zpa2326_spi_driver = {
|
||||
.driver = {
|
||||
.name = "zpa2326-spi",
|
||||
.of_match_table = of_match_ptr(zpa2326_spi_matches),
|
||||
.pm = ZPA2326_PM_OPS,
|
||||
},
|
||||
.probe = zpa2326_probe_spi,
|
||||
.remove = zpa2326_remove_spi,
|
||||
.id_table = zpa2326_spi_ids,
|
||||
};
|
||||
module_spi_driver(zpa2326_spi_driver);
|
||||
|
||||
MODULE_AUTHOR("Gregor Boirie <gregor.boirie@parrot.com>");
|
||||
MODULE_DESCRIPTION("SPI driver for Murata ZPA2326 pressure sensor");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -516,7 +516,7 @@ static irqreturn_t sx9500_irq_thread_handler(int irq, void *private)
|
|||
sx9500_push_events(indio_dev);
|
||||
|
||||
if (val & SX9500_CONVDONE_IRQ)
|
||||
complete_all(&data->completion);
|
||||
complete(&data->completion);
|
||||
|
||||
out:
|
||||
mutex_unlock(&data->mutex);
|
||||
|
@ -1025,6 +1025,12 @@ static const struct acpi_device_id sx9500_acpi_match[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(acpi, sx9500_acpi_match);
|
||||
|
||||
static const struct of_device_id sx9500_of_match[] = {
|
||||
{ .compatible = "semtech,sx9500", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sx9500_of_match);
|
||||
|
||||
static const struct i2c_device_id sx9500_id[] = {
|
||||
{"sx9500", 0},
|
||||
{ },
|
||||
|
@ -1035,6 +1041,7 @@ static struct i2c_driver sx9500_driver = {
|
|||
.driver = {
|
||||
.name = SX9500_DRIVER_NAME,
|
||||
.acpi_match_table = ACPI_PTR(sx9500_acpi_match),
|
||||
.of_match_table = of_match_ptr(sx9500_of_match),
|
||||
.pm = &sx9500_pm_ops,
|
||||
},
|
||||
.probe = sx9500_probe,
|
||||
|
|
|
@ -3,6 +3,22 @@
|
|||
#
|
||||
menu "Temperature sensors"
|
||||
|
||||
config MAXIM_THERMOCOUPLE
|
||||
tristate "Maxim thermocouple sensors"
|
||||
depends on SPI
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
If you say yes here you get support for the Maxim series of
|
||||
thermocouple sensors connected via SPI.
|
||||
|
||||
Supported sensors:
|
||||
* MAX6675
|
||||
* MAX31855
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called maxim_thermocouple.
|
||||
|
||||
config MLX90614
|
||||
tristate "MLX90614 contact-less infrared sensor"
|
||||
depends on I2C
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
# Makefile for industrial I/O temperature drivers
|
||||
#
|
||||
|
||||
obj-$(CONFIG_MAXIM_THERMOCOUPLE) += maxim_thermocouple.o
|
||||
obj-$(CONFIG_MLX90614) += mlx90614.o
|
||||
obj-$(CONFIG_TMP006) += tmp006.o
|
||||
obj-$(CONFIG_TSYS01) += tsys01.o
|
||||
|
|
281
drivers/iio/temperature/maxim_thermocouple.c
Normal file
281
drivers/iio/temperature/maxim_thermocouple.c
Normal file
|
@ -0,0 +1,281 @@
|
|||
/*
|
||||
* maxim_thermocouple.c - Support for Maxim thermocouple chips
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
|
||||
#define MAXIM_THERMOCOUPLE_DRV_NAME "maxim_thermocouple"
|
||||
|
||||
enum {
|
||||
MAX6675,
|
||||
MAX31855,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec max6675_channels[] = {
|
||||
{ /* thermocouple temperature */
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 13,
|
||||
.storagebits = 16,
|
||||
.shift = 3,
|
||||
.endianness = IIO_BE,
|
||||
},
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec max31855_channels[] = {
|
||||
{ /* thermocouple temperature */
|
||||
.type = IIO_TEMP,
|
||||
.address = 2,
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 14,
|
||||
.storagebits = 16,
|
||||
.shift = 2,
|
||||
.endianness = IIO_BE,
|
||||
},
|
||||
},
|
||||
{ /* cold junction temperature */
|
||||
.type = IIO_TEMP,
|
||||
.address = 0,
|
||||
.channel2 = IIO_MOD_TEMP_AMBIENT,
|
||||
.modified = 1,
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
.scan_index = 1,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 12,
|
||||
.storagebits = 16,
|
||||
.shift = 4,
|
||||
.endianness = IIO_BE,
|
||||
},
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(2),
|
||||
};
|
||||
|
||||
static const unsigned long max31855_scan_masks[] = {0x3, 0};
|
||||
|
||||
struct maxim_thermocouple_chip {
|
||||
const struct iio_chan_spec *channels;
|
||||
const unsigned long *scan_masks;
|
||||
u8 num_channels;
|
||||
u8 read_size;
|
||||
|
||||
/* bit-check for valid input */
|
||||
u32 status_bit;
|
||||
};
|
||||
|
||||
static const struct maxim_thermocouple_chip maxim_thermocouple_chips[] = {
|
||||
[MAX6675] = {
|
||||
.channels = max6675_channels,
|
||||
.num_channels = ARRAY_SIZE(max6675_channels),
|
||||
.read_size = 2,
|
||||
.status_bit = BIT(2),
|
||||
},
|
||||
[MAX31855] = {
|
||||
.channels = max31855_channels,
|
||||
.num_channels = ARRAY_SIZE(max31855_channels),
|
||||
.read_size = 4,
|
||||
.scan_masks = max31855_scan_masks,
|
||||
.status_bit = BIT(16),
|
||||
},
|
||||
};
|
||||
|
||||
struct maxim_thermocouple_data {
|
||||
struct spi_device *spi;
|
||||
const struct maxim_thermocouple_chip *chip;
|
||||
|
||||
u8 buffer[16] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static int maxim_thermocouple_read(struct maxim_thermocouple_data *data,
|
||||
struct iio_chan_spec const *chan, int *val)
|
||||
{
|
||||
unsigned int storage_bytes = data->chip->read_size;
|
||||
unsigned int shift = chan->scan_type.shift + (chan->address * 8);
|
||||
unsigned int buf;
|
||||
int ret;
|
||||
|
||||
ret = spi_read(data->spi, (void *) &buf, storage_bytes);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (storage_bytes) {
|
||||
case 2:
|
||||
*val = be16_to_cpu(buf);
|
||||
break;
|
||||
case 4:
|
||||
*val = be32_to_cpu(buf);
|
||||
break;
|
||||
}
|
||||
|
||||
/* check to be sure this is a valid reading */
|
||||
if (*val & data->chip->status_bit)
|
||||
return -EINVAL;
|
||||
|
||||
*val = sign_extend32(*val >> shift, chan->scan_type.realbits - 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t maxim_thermocouple_trigger_handler(int irq, void *private)
|
||||
{
|
||||
struct iio_poll_func *pf = private;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct maxim_thermocouple_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = spi_read(data->spi, data->buffer, data->chip->read_size);
|
||||
if (!ret) {
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
|
||||
iio_get_time_ns(indio_dev));
|
||||
}
|
||||
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int maxim_thermocouple_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct maxim_thermocouple_data *data = iio_priv(indio_dev);
|
||||
int ret = -EINVAL;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = maxim_thermocouple_read(data, chan, val);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
|
||||
if (!ret)
|
||||
return IIO_VAL_INT;
|
||||
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->channel2) {
|
||||
case IIO_MOD_TEMP_AMBIENT:
|
||||
*val = 62;
|
||||
*val2 = 500000; /* 1000 * 0.0625 */
|
||||
ret = IIO_VAL_INT_PLUS_MICRO;
|
||||
break;
|
||||
default:
|
||||
*val = 250; /* 1000 * 0.25 */
|
||||
ret = IIO_VAL_INT;
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info maxim_thermocouple_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = maxim_thermocouple_read_raw,
|
||||
};
|
||||
|
||||
static int maxim_thermocouple_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
struct iio_dev *indio_dev;
|
||||
struct maxim_thermocouple_data *data;
|
||||
const struct maxim_thermocouple_chip *chip =
|
||||
&maxim_thermocouple_chips[id->driver_data];
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
indio_dev->info = &maxim_thermocouple_info;
|
||||
indio_dev->name = MAXIM_THERMOCOUPLE_DRV_NAME;
|
||||
indio_dev->channels = chip->channels;
|
||||
indio_dev->available_scan_masks = chip->scan_masks;
|
||||
indio_dev->num_channels = chip->num_channels;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->spi = spi;
|
||||
data->chip = chip;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
maxim_thermocouple_trigger_handler, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_unreg_buffer;
|
||||
|
||||
return 0;
|
||||
|
||||
error_unreg_buffer:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int maxim_thermocouple_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id maxim_thermocouple_id[] = {
|
||||
{"max6675", MAX6675},
|
||||
{"max31855", MAX31855},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, maxim_thermocouple_id);
|
||||
|
||||
static struct spi_driver maxim_thermocouple_driver = {
|
||||
.driver = {
|
||||
.name = MAXIM_THERMOCOUPLE_DRV_NAME,
|
||||
},
|
||||
.probe = maxim_thermocouple_probe,
|
||||
.remove = maxim_thermocouple_remove,
|
||||
.id_table = maxim_thermocouple_id,
|
||||
};
|
||||
module_spi_driver(maxim_thermocouple_driver);
|
||||
|
||||
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
|
||||
MODULE_DESCRIPTION("Maxim thermocouple sensors");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -40,8 +40,6 @@ source "drivers/staging/rtl8712/Kconfig"
|
|||
|
||||
source "drivers/staging/rtl8188eu/Kconfig"
|
||||
|
||||
source "drivers/staging/rtl8723au/Kconfig"
|
||||
|
||||
source "drivers/staging/rts5208/Kconfig"
|
||||
|
||||
source "drivers/staging/octeon/Kconfig"
|
||||
|
@ -104,4 +102,8 @@ source "drivers/staging/i4l/Kconfig"
|
|||
|
||||
source "drivers/staging/ks7010/Kconfig"
|
||||
|
||||
source "drivers/staging/greybus/Kconfig"
|
||||
|
||||
source "drivers/staging/vc04_services/Kconfig"
|
||||
|
||||
endif # STAGING
|
||||
|
|
|
@ -9,7 +9,6 @@ obj-$(CONFIG_RTL8192U) += rtl8192u/
|
|||
obj-$(CONFIG_RTL8192E) += rtl8192e/
|
||||
obj-$(CONFIG_R8712U) += rtl8712/
|
||||
obj-$(CONFIG_R8188EU) += rtl8188eu/
|
||||
obj-$(CONFIG_R8723AU) += rtl8723au/
|
||||
obj-$(CONFIG_RTS5208) += rts5208/
|
||||
obj-$(CONFIG_NETLOGIC_XLR_NET) += netlogic/
|
||||
obj-$(CONFIG_OCTEON_ETHERNET) += octeon/
|
||||
|
@ -41,3 +40,5 @@ obj-$(CONFIG_WILC1000) += wilc1000/
|
|||
obj-$(CONFIG_MOST) += most/
|
||||
obj-$(CONFIG_ISDN_I4L) += i4l/
|
||||
obj-$(CONFIG_KS7010) += ks7010/
|
||||
obj-$(CONFIG_GREYBUS) += greybus/
|
||||
obj-$(CONFIG_BCM2708_VCHIQ) += vc04_services/
|
||||
|
|
|
@ -24,19 +24,6 @@ config ANDROID_LOW_MEMORY_KILLER
|
|||
scripts (/init.rc), and it defines priority values with minimum free memory size
|
||||
for each priority.
|
||||
|
||||
config SW_SYNC
|
||||
bool "Software synchronization framework"
|
||||
default n
|
||||
depends on SYNC_FILE
|
||||
depends on DEBUG_FS
|
||||
---help---
|
||||
A sync object driver that uses a 32bit counter to coordinate
|
||||
synchronization. Useful when there is no hardware primitive backing
|
||||
the synchronization.
|
||||
|
||||
WARNING: improper use of this can result in deadlocking kernel
|
||||
drivers from userspace. Intended for test and debug only.
|
||||
|
||||
source "drivers/staging/android/ion/Kconfig"
|
||||
|
||||
endif # if ANDROID
|
||||
|
|
|
@ -4,4 +4,3 @@ obj-y += ion/
|
|||
|
||||
obj-$(CONFIG_ASHMEM) += ashmem.o
|
||||
obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER) += lowmemorykiller.o
|
||||
obj-$(CONFIG_SW_SYNC) += sw_sync.o sync_debug.o
|
||||
|
|
|
@ -36,7 +36,19 @@ config ION_TEGRA
|
|||
config ION_HISI
|
||||
tristate "Ion for Hisilicon"
|
||||
depends on ARCH_HISI && ION
|
||||
select ION_OF
|
||||
help
|
||||
Choose this option if you wish to use ion on Hisilicon Platform.
|
||||
|
||||
source "drivers/staging/android/ion/hisilicon/Kconfig"
|
||||
|
||||
config ION_OF
|
||||
bool "Devicetree support for Ion"
|
||||
depends on ION && OF_ADDRESS
|
||||
help
|
||||
Provides base support for defining Ion heaps in devicetree
|
||||
and setting them up. Also includes functions for platforms
|
||||
to parse the devicetree and expand for their own custom
|
||||
extensions
|
||||
|
||||
If using Ion and devicetree, you should say Y here
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
obj-$(CONFIG_ION) += ion.o ion_heap.o ion_page_pool.o ion_system_heap.o \
|
||||
obj-$(CONFIG_ION) += ion.o ion-ioctl.o ion_heap.o \
|
||||
ion_page_pool.o ion_system_heap.o \
|
||||
ion_carveout_heap.o ion_chunk_heap.o ion_cma_heap.o
|
||||
obj-$(CONFIG_ION_TEST) += ion_test.o
|
||||
ifdef CONFIG_COMPAT
|
||||
|
@ -8,4 +9,5 @@ endif
|
|||
obj-$(CONFIG_ION_DUMMY) += ion_dummy_driver.o
|
||||
obj-$(CONFIG_ION_TEGRA) += tegra/
|
||||
obj-$(CONFIG_ION_HISI) += hisilicon/
|
||||
obj-$(CONFIG_ION_OF) += ion_of.o
|
||||
|
||||
|
|
51
drivers/staging/android/ion/devicetree.txt
Normal file
51
drivers/staging/android/ion/devicetree.txt
Normal file
|
@ -0,0 +1,51 @@
|
|||
Ion Memory Manager
|
||||
|
||||
Ion is a memory manager that allows for sharing of buffers via dma-buf.
|
||||
Ion allows for different types of allocation via an abstraction called
|
||||
a 'heap'. A heap represents a specific type of memory. Each heap has
|
||||
a different type. There can be multiple instances of the same heap
|
||||
type.
|
||||
|
||||
Specific heap instances are tied to heap IDs. Heap IDs are not to be specified
|
||||
in the devicetree.
|
||||
|
||||
Required properties for Ion
|
||||
|
||||
- compatible: "linux,ion" PLUS a compatible property for the device
|
||||
|
||||
All child nodes of a linux,ion node are interpreted as heaps
|
||||
|
||||
required properties for heaps
|
||||
|
||||
- compatible: compatible string for a heap type PLUS a compatible property
|
||||
for the specific instance of the heap. Current heap types
|
||||
-- linux,ion-heap-system
|
||||
-- linux,ion-heap-system-contig
|
||||
-- linux,ion-heap-carveout
|
||||
-- linux,ion-heap-chunk
|
||||
-- linux,ion-heap-dma
|
||||
-- linux,ion-heap-custom
|
||||
|
||||
Optional properties
|
||||
- memory-region: A phandle to a memory region. Required for DMA heap type
|
||||
(see reserved-memory.txt for details on the reservation)
|
||||
|
||||
Example:
|
||||
|
||||
ion {
|
||||
compatbile = "hisilicon,ion", "linux,ion";
|
||||
|
||||
ion-system-heap {
|
||||
compatbile = "hisilicon,system-heap", "linux,ion-heap-system"
|
||||
};
|
||||
|
||||
ion-camera-region {
|
||||
compatible = "hisilicon,camera-heap", "linux,ion-heap-dma"
|
||||
memory-region = <&camera_region>;
|
||||
};
|
||||
|
||||
ion-fb-region {
|
||||
compatbile = "hisilicon,fb-heap", "linux,ion-heap-dma"
|
||||
memory-region = <&fb_region>;
|
||||
};
|
||||
}
|
|
@ -19,181 +19,74 @@
|
|||
#include <linux/mm.h>
|
||||
#include "../ion_priv.h"
|
||||
#include "../ion.h"
|
||||
#include "../ion_of.h"
|
||||
|
||||
struct hi6220_ion_type_table {
|
||||
const char *name;
|
||||
enum ion_heap_type type;
|
||||
struct hisi_ion_dev {
|
||||
struct ion_heap **heaps;
|
||||
struct ion_device *idev;
|
||||
struct ion_platform_data *data;
|
||||
};
|
||||
|
||||
static struct hi6220_ion_type_table ion_type_table[] = {
|
||||
{"ion_system", ION_HEAP_TYPE_SYSTEM},
|
||||
{"ion_system_contig", ION_HEAP_TYPE_SYSTEM_CONTIG},
|
||||
{"ion_carveout", ION_HEAP_TYPE_CARVEOUT},
|
||||
{"ion_chunk", ION_HEAP_TYPE_CHUNK},
|
||||
{"ion_dma", ION_HEAP_TYPE_DMA},
|
||||
{"ion_custom", ION_HEAP_TYPE_CUSTOM},
|
||||
static struct ion_of_heap hisi_heaps[] = {
|
||||
PLATFORM_HEAP("hisilicon,sys_user", 0,
|
||||
ION_HEAP_TYPE_SYSTEM, "sys_user"),
|
||||
PLATFORM_HEAP("hisilicon,sys_contig", 1,
|
||||
ION_HEAP_TYPE_SYSTEM_CONTIG, "sys_contig"),
|
||||
PLATFORM_HEAP("hisilicon,cma", ION_HEAP_TYPE_DMA, ION_HEAP_TYPE_DMA,
|
||||
"cma"),
|
||||
{}
|
||||
};
|
||||
|
||||
static struct ion_device *idev;
|
||||
static int num_heaps;
|
||||
static struct ion_heap **heaps;
|
||||
static struct ion_platform_heap **heaps_data;
|
||||
|
||||
static int get_type_by_name(const char *name, enum ion_heap_type *type)
|
||||
static int hi6220_ion_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct hisi_ion_dev *ipdev;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ion_type_table); i++) {
|
||||
if (strncmp(name, ion_type_table[i].name, strlen(name)))
|
||||
continue;
|
||||
|
||||
*type = ion_type_table[i].type;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int hi6220_set_platform_data(struct platform_device *pdev)
|
||||
{
|
||||
unsigned int base;
|
||||
unsigned int size;
|
||||
unsigned int id;
|
||||
const char *heap_name;
|
||||
const char *type_name;
|
||||
enum ion_heap_type type;
|
||||
int ret;
|
||||
struct device_node *np;
|
||||
struct ion_platform_heap *p_data;
|
||||
const struct device_node *dt_node = pdev->dev.of_node;
|
||||
int index = 0;
|
||||
|
||||
for_each_child_of_node(dt_node, np)
|
||||
num_heaps++;
|
||||
|
||||
heaps_data = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct ion_platform_heap *) *
|
||||
num_heaps,
|
||||
GFP_KERNEL);
|
||||
if (!heaps_data)
|
||||
ipdev = devm_kzalloc(&pdev->dev, sizeof(*ipdev), GFP_KERNEL);
|
||||
if (!ipdev)
|
||||
return -ENOMEM;
|
||||
|
||||
for_each_child_of_node(dt_node, np) {
|
||||
ret = of_property_read_string(np, "heap-name", &heap_name);
|
||||
if (ret < 0) {
|
||||
pr_err("check the name of node %s\n", np->name);
|
||||
continue;
|
||||
}
|
||||
platform_set_drvdata(pdev, ipdev);
|
||||
|
||||
ret = of_property_read_u32(np, "heap-id", &id);
|
||||
if (ret < 0) {
|
||||
pr_err("check the id %s\n", np->name);
|
||||
continue;
|
||||
}
|
||||
ipdev->idev = ion_device_create(NULL);
|
||||
if (IS_ERR(ipdev->idev))
|
||||
return PTR_ERR(ipdev->idev);
|
||||
|
||||
ret = of_property_read_u32(np, "heap-base", &base);
|
||||
if (ret < 0) {
|
||||
pr_err("check the base of node %s\n", np->name);
|
||||
continue;
|
||||
}
|
||||
ipdev->data = ion_parse_dt(pdev, hisi_heaps);
|
||||
if (IS_ERR(ipdev->data))
|
||||
return PTR_ERR(ipdev->data);
|
||||
|
||||
ret = of_property_read_u32(np, "heap-size", &size);
|
||||
if (ret < 0) {
|
||||
pr_err("check the size of node %s\n", np->name);
|
||||
continue;
|
||||
}
|
||||
ipdev->heaps = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct ion_heap) * ipdev->data->nr,
|
||||
GFP_KERNEL);
|
||||
if (!ipdev->heaps) {
|
||||
ion_destroy_platform_data(ipdev->data);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = of_property_read_string(np, "heap-type", &type_name);
|
||||
if (ret < 0) {
|
||||
pr_err("check the type of node %s\n", np->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = get_type_by_name(type_name, &type);
|
||||
if (ret < 0) {
|
||||
pr_err("type name error %s!\n", type_name);
|
||||
continue;
|
||||
}
|
||||
pr_info("heap index %d : name %s base 0x%x size 0x%x id %d type %d\n",
|
||||
index, heap_name, base, size, id, type);
|
||||
|
||||
p_data = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct ion_platform_heap),
|
||||
GFP_KERNEL);
|
||||
if (!p_data)
|
||||
for (i = 0; i < ipdev->data->nr; i++) {
|
||||
ipdev->heaps[i] = ion_heap_create(&ipdev->data->heaps[i]);
|
||||
if (!ipdev->heaps) {
|
||||
ion_destroy_platform_data(ipdev->data);
|
||||
return -ENOMEM;
|
||||
|
||||
p_data->name = heap_name;
|
||||
p_data->base = base;
|
||||
p_data->size = size;
|
||||
p_data->id = id;
|
||||
p_data->type = type;
|
||||
|
||||
heaps_data[index] = p_data;
|
||||
index++;
|
||||
}
|
||||
ion_device_add_heap(ipdev->idev, ipdev->heaps[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hi6220_ion_probe(struct platform_device *pdev)
|
||||
{
|
||||
int i;
|
||||
int err;
|
||||
static struct ion_platform_heap *p_heap;
|
||||
|
||||
idev = ion_device_create(NULL);
|
||||
err = hi6220_set_platform_data(pdev);
|
||||
if (err) {
|
||||
pr_err("ion set platform data error!\n");
|
||||
goto err_free_idev;
|
||||
}
|
||||
heaps = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct ion_heap *) * num_heaps,
|
||||
GFP_KERNEL);
|
||||
if (!heaps) {
|
||||
err = -ENOMEM;
|
||||
goto err_free_idev;
|
||||
}
|
||||
|
||||
/*
|
||||
* create the heaps as specified in the dts file
|
||||
*/
|
||||
for (i = 0; i < num_heaps; i++) {
|
||||
p_heap = heaps_data[i];
|
||||
heaps[i] = ion_heap_create(p_heap);
|
||||
if (IS_ERR_OR_NULL(heaps[i])) {
|
||||
err = PTR_ERR(heaps[i]);
|
||||
goto err_free_heaps;
|
||||
}
|
||||
|
||||
ion_device_add_heap(idev, heaps[i]);
|
||||
|
||||
pr_info("%s: adding heap %s of type %d with %lx@%lx\n",
|
||||
__func__, p_heap->name, p_heap->type,
|
||||
p_heap->base, (unsigned long)p_heap->size);
|
||||
}
|
||||
return err;
|
||||
|
||||
err_free_heaps:
|
||||
for (i = 0; i < num_heaps; ++i) {
|
||||
ion_heap_destroy(heaps[i]);
|
||||
heaps[i] = NULL;
|
||||
}
|
||||
err_free_idev:
|
||||
ion_device_destroy(idev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int hi6220_ion_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct hisi_ion_dev *ipdev;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_heaps; i++) {
|
||||
ion_heap_destroy(heaps[i]);
|
||||
heaps[i] = NULL;
|
||||
}
|
||||
ion_device_destroy(idev);
|
||||
ipdev = platform_get_drvdata(pdev);
|
||||
|
||||
for (i = 0; i < ipdev->data->nr; i++)
|
||||
ion_heap_destroy(ipdev->heaps[i]);
|
||||
|
||||
ion_destroy_platform_data(ipdev->data);
|
||||
ion_device_destroy(ipdev->idev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue