mirror of
https://github.com/hardkernel/linux.git
synced 2026-05-04 19:25:37 +09:00
Merge tag 'iio-for-4.16a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next
Jonathan writes:
First set of new device support, cleanups and features for IIO in the 4.16 cycle
New device support
* IDT Z0PT2201 ambient light and UVB sensor
- new driver and DT bindings.
* MAX30102 (pulse oximetery sensor)
- support for MAX30105 sensor (smoke detector) Just goes to show
how two supposedly totally different applications can use very similar
devices.
* UVIS25 UV sensor
- new driver and DT bindings.
Major new features
* at91-sama5d2-adc
- DMA support including bindings + a fix for an issue with acking the
interrupt to prevent false overrun reports.
* ina2xx
- allow control of shunt voltage PGA and bus voltage range to give better
accuracy in some cases.
* stm32-adc
- support differential channels (precursor patch reworked how channel names
were created to enable this).
Cleanups / minor fixes / features
* core
- mark a deliberate switch fallthrough.
- macro to populate struct iio_map array elements.
* docs
- typo fix.
* MAINTAINERS
- add some missing entries for IIO ABI files.
* ad7152
- tidy up unlocking paths.
* ad7746
- tidy up unlocking paths.
* ak8975
- add an ACPI id found on a prototype board.
* aspeed-adc
- deassert reset in probe to ensure device is usable.
* bfin-trigger
- platform_get_irq return value fixing.
* bmc150
- OF device ID table for i2c (spi to be done).
* cros_ec
- unused variable cleanup.
* da208
- ACPI binding seen on Linx 820 tablet.
* ina2xx
- shift down raw value to drop status flags from value (likely to have
been hidden in the noise).
- tidy up a special case that wasn't needed.
* inv_mpu6050
- i2c_unregister_device knows about null values so don't check it twice.
* kxsd9
- fix missing MODULE_LICENSE and MODULE_DESCRIPTION.
* max30102
- missing new lines in dev_err.
- inconsistent punctuation in error messages.
- fix LED mode mask number of bits.
- check return value of power mode functions to handle errors.
- introduce an intensity channel macro to reduce duplication.
- fix minor issue where device wasn't necessarily enabled during
a get temperature.
- use indicies for LED channels.
- move the mode seetting to buffer_postenable - precursor to new device
support.
- prepare to allow copying of varying numbers of measurement.
* meson-saradc
- drop irrelevant clock and update bindings.
* mma8452
- a couple of renames for readability reasons.
* qcom_vadc
- fix missing MODULE_LICENSE and MODULE_DESCRIPTION.
* st_accel
- drop an unused variable.
* sx9500
- add an ACPI id found on a prototype board.
This commit is contained in:
@@ -1290,7 +1290,7 @@ KernelVersion: 3.4
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Unit-less light intensity. Modifiers both and ir indicate
|
||||
that measurements contains visible and infrared light
|
||||
that measurements contain visible and infrared light
|
||||
components or just infrared light, respectively. Modifier uv indicates
|
||||
that measurements contain ultraviolet light components.
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ Required properties:
|
||||
- "clkin" for the reference clock (typically XTAL)
|
||||
- "core" for the SAR ADC core clock
|
||||
optional clocks:
|
||||
- "sana" for the analog clock
|
||||
- "adc_clk" for the ADC (sampling) clock
|
||||
- "adc_sel" for the ADC (sampling) clock mux
|
||||
- vref-supply: the regulator supply for the ADC reference voltage
|
||||
|
||||
@@ -8,6 +8,7 @@ Required properties:
|
||||
- reg: memory window mapping address and length
|
||||
- clocks: Input clock used to derive the sample clock. Expected to be the
|
||||
SoC's APB clock.
|
||||
- resets: Reset controller phandle
|
||||
- #io-channel-cells: Must be set to <1> to indicate channels are selected
|
||||
by index.
|
||||
|
||||
@@ -15,6 +16,7 @@ Example:
|
||||
adc@1e6e9000 {
|
||||
compatible = "aspeed,ast2400-adc";
|
||||
reg = <0x1e6e9000 0xb0>;
|
||||
clocks = <&clk_apb>;
|
||||
clocks = <&syscon ASPEED_CLK_APB>;
|
||||
resets = <&syscon ASPEED_RESET_ADC>;
|
||||
#io-channel-cells = <1>;
|
||||
};
|
||||
|
||||
@@ -17,6 +17,11 @@ Required properties:
|
||||
This property uses the IRQ edge types values: IRQ_TYPE_EDGE_RISING ,
|
||||
IRQ_TYPE_EDGE_FALLING or IRQ_TYPE_EDGE_BOTH
|
||||
|
||||
Optional properties:
|
||||
- dmas: Phandle to dma channel for the ADC.
|
||||
- dma-names: Must be "rx" when dmas property is being used.
|
||||
See ../../dma/dma.txt for details.
|
||||
|
||||
Example:
|
||||
|
||||
adc: adc@fc030000 {
|
||||
@@ -31,4 +36,6 @@ adc: adc@fc030000 {
|
||||
vddana-supply = <&vdd_3v3_lp_reg>;
|
||||
vref-supply = <&vdd_3v3_lp_reg>;
|
||||
atmel,trigger-edge-type = <IRQ_TYPE_EDGE_BOTH>;
|
||||
dmas = <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) | AT91_XDMAC_DT_PERID(25))>;
|
||||
dma-names = "rx";
|
||||
}
|
||||
|
||||
@@ -62,6 +62,15 @@ Required properties:
|
||||
- st,adc-channels: List of single-ended channels muxed for this ADC.
|
||||
It can have up to 16 channels on stm32f4 or 20 channels on stm32h7, numbered
|
||||
from 0 to 15 or 19 (resp. for in0..in15 or in0..in19).
|
||||
- st,adc-diff-channels: List of differential channels muxed for this ADC.
|
||||
Depending on part used, some channels can be configured as differential
|
||||
instead of single-ended (e.g. stm32h7). List here positive and negative
|
||||
inputs pairs as <vinp vinn>, <vinp vinn>,... vinp and vinn are numbered
|
||||
from 0 to 19 on stm32h7)
|
||||
Note: At least one of "st,adc-channels" or "st,adc-diff-channels" is required.
|
||||
Both properties can be used together. Some channels can be used as
|
||||
single-ended and some other ones as differential (mixed). But channels
|
||||
can't be configured both as single-ended and differential (invalid).
|
||||
- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers" in
|
||||
Documentation/devicetree/bindings/iio/iio-bindings.txt
|
||||
|
||||
@@ -111,3 +120,18 @@ Example:
|
||||
...
|
||||
other adc child nodes follow...
|
||||
};
|
||||
|
||||
Example to setup:
|
||||
- channel 1 as single-ended
|
||||
- channels 2 & 3 as differential (with resp. 6 & 7 negative inputs)
|
||||
|
||||
adc: adc@40022000 {
|
||||
compatible = "st,stm32h7-adc-core";
|
||||
...
|
||||
adc1: adc@0 {
|
||||
compatible = "st,stm32h7-adc";
|
||||
...
|
||||
st,adc-channels = <1>;
|
||||
st,adc-diff-channels = <2 6>, <3 7>;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
Maxim MAX30102 heart rate and pulse oximeter sensor
|
||||
Maxim MAX30105 optical particle-sensing module
|
||||
|
||||
* https://datasheets.maximintegrated.com/en/ds/MAX30102.pdf
|
||||
* https://datasheets.maximintegrated.com/en/ds/MAX30105.pdf
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "maxim,max30102"
|
||||
- compatible: must be "maxim,max30102" or "maxim,max30105"
|
||||
- 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
|
||||
@@ -12,8 +14,10 @@ Required properties:
|
||||
interrupt client node bindings.
|
||||
|
||||
Optional properties:
|
||||
- maxim,red-led-current-microamp: configuration for RED LED current
|
||||
- maxim,red-led-current-microamp: configuration for red LED current
|
||||
- maxim,ir-led-current-microamp: configuration for IR LED current
|
||||
- maxim,green-led-current-microamp: configuration for green LED current
|
||||
(max30105 only)
|
||||
|
||||
Note that each step is approximately 200 microamps, ranging from 0 uA to
|
||||
50800 uA.
|
||||
|
||||
23
Documentation/devicetree/bindings/iio/light/uvis25.txt
Normal file
23
Documentation/devicetree/bindings/iio/light/uvis25.txt
Normal file
@@ -0,0 +1,23 @@
|
||||
* ST UVIS25 uv sensor
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "st,uvis25"
|
||||
- reg: i2c address of the sensor / spi cs line
|
||||
|
||||
Optional properties:
|
||||
- interrupt-parent: should be the phandle for the interrupt controller
|
||||
- interrupts: interrupt mapping for IRQ. It should be configured with
|
||||
flags IRQ_TYPE_LEVEL_HIGH, IRQ_TYPE_EDGE_RISING, IRQ_TYPE_LEVEL_LOW or
|
||||
IRQ_TYPE_EDGE_FALLING.
|
||||
|
||||
Refer to interrupt-controller/interrupts.txt for generic interrupt
|
||||
client node bindings.
|
||||
|
||||
Example:
|
||||
|
||||
uvis25@47 {
|
||||
compatible = "st,uvis25";
|
||||
reg = <0x47>;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <0 IRQ_TYPE_EDGE_RISING>;
|
||||
};
|
||||
@@ -262,6 +262,7 @@ ACCES 104-QUAD-8 IIO DRIVER
|
||||
M: William Breathitt Gray <vilhelm.gray@gmail.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/ABI/testing/sysfs-bus-iio-counter-104-quad-8
|
||||
F: drivers/iio/counter/104-quad-8.c
|
||||
|
||||
ACCES PCI-IDIO-16 GPIO DRIVER
|
||||
@@ -845,6 +846,8 @@ M: Michael Hennerich <Michael.Hennerich@analog.com>
|
||||
W: http://wiki.analog.com/
|
||||
W: http://ez.analog.com/community/linux-device-drivers
|
||||
S: Supported
|
||||
F: Documentation/ABI/testing/sysfs-bus-iio-frequency-ad9523
|
||||
F: Documentation/ABI/testing/sysfs-bus-iio-frequency-adf4350
|
||||
F: drivers/iio/*/ad*
|
||||
F: drivers/iio/adc/ltc2497*
|
||||
X: drivers/iio/*/adjd*
|
||||
@@ -4126,6 +4129,7 @@ DEVANTECH SRF ULTRASONIC RANGER IIO DRIVER
|
||||
M: Andreas Klinger <ak@it-klinger.de>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/ABI/testing/sysfs-bus-iio-distance-srf08
|
||||
F: drivers/iio/proximity/srf*.c
|
||||
|
||||
DEVICE COREDUMP (DEV_COREDUMP)
|
||||
@@ -6829,6 +6833,8 @@ R: Peter Meerwald-Stadler <pmeerw@pmeerw.net>
|
||||
L: linux-iio@vger.kernel.org
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git
|
||||
S: Maintained
|
||||
F: Documentation/ABI/testing/configfs-iio*
|
||||
F: Documentation/ABI/testing/sysfs-bus-iio*
|
||||
F: Documentation/devicetree/bindings/iio/
|
||||
F: drivers/iio/
|
||||
F: drivers/staging/iio/
|
||||
|
||||
@@ -81,9 +81,21 @@ static const struct i2c_device_id bmc150_accel_id[] = {
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, bmc150_accel_id);
|
||||
|
||||
static const struct of_device_id bmc150_accel_of_match[] = {
|
||||
{ .compatible = "bosch,bmc150_accel" },
|
||||
{ .compatible = "bosch,bmi055_accel" },
|
||||
{ .compatible = "bosch,bma255" },
|
||||
{ .compatible = "bosch,bma250e" },
|
||||
{ .compatible = "bosch,bma222e" },
|
||||
{ .compatible = "bosch,bma280" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bmc150_accel_of_match);
|
||||
|
||||
static struct i2c_driver bmc150_accel_driver = {
|
||||
.driver = {
|
||||
.name = "bmc150_accel_i2c",
|
||||
.of_match_table = bmc150_accel_of_match,
|
||||
.acpi_match_table = ACPI_PTR(bmc150_accel_acpi_match),
|
||||
.pm = &bmc150_accel_pm_ops,
|
||||
},
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/byteorder/generic.h>
|
||||
@@ -25,7 +26,7 @@
|
||||
#define DA280_MODE_ENABLE 0x1e
|
||||
#define DA280_MODE_DISABLE 0x9e
|
||||
|
||||
enum { da226, da280 };
|
||||
enum da280_chipset { da226, da280 };
|
||||
|
||||
/*
|
||||
* a value of + or -4096 corresponds to + or - 1G
|
||||
@@ -91,12 +92,24 @@ static const struct iio_info da280_info = {
|
||||
.read_raw = da280_read_raw,
|
||||
};
|
||||
|
||||
static enum da280_chipset da280_match_acpi_device(struct device *dev)
|
||||
{
|
||||
const struct acpi_device_id *id;
|
||||
|
||||
id = acpi_match_device(dev->driver->acpi_match_table, dev);
|
||||
if (!id)
|
||||
return -EINVAL;
|
||||
|
||||
return (enum da280_chipset) id->driver_data;
|
||||
}
|
||||
|
||||
static int da280_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct iio_dev *indio_dev;
|
||||
struct da280_data *data;
|
||||
enum da280_chipset chip;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, DA280_REG_CHIP_ID);
|
||||
if (ret != DA280_CHIP_ID)
|
||||
@@ -114,7 +127,14 @@ static int da280_probe(struct i2c_client *client,
|
||||
indio_dev->info = &da280_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = da280_channels;
|
||||
if (id->driver_data == da226) {
|
||||
|
||||
if (ACPI_HANDLE(&client->dev)) {
|
||||
chip = da280_match_acpi_device(&client->dev);
|
||||
} else {
|
||||
chip = id->driver_data;
|
||||
}
|
||||
|
||||
if (chip == da226) {
|
||||
indio_dev->name = "da226";
|
||||
indio_dev->num_channels = 2;
|
||||
} else {
|
||||
@@ -158,6 +178,12 @@ static int da280_resume(struct device *dev)
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(da280_pm_ops, da280_suspend, da280_resume);
|
||||
|
||||
static const struct acpi_device_id da280_acpi_match[] = {
|
||||
{"MIRAACC", da280},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, da280_acpi_match);
|
||||
|
||||
static const struct i2c_device_id da280_i2c_id[] = {
|
||||
{ "da226", da226 },
|
||||
{ "da280", da280 },
|
||||
@@ -168,6 +194,7 @@ MODULE_DEVICE_TABLE(i2c, da280_i2c_id);
|
||||
static struct i2c_driver da280_driver = {
|
||||
.driver = {
|
||||
.name = "da280",
|
||||
.acpi_match_table = ACPI_PTR(da280_acpi_match),
|
||||
.pm = &da280_pm_ops,
|
||||
},
|
||||
.probe = da280_probe,
|
||||
|
||||
@@ -63,3 +63,6 @@ static struct i2c_driver kxsd9_i2c_driver = {
|
||||
.id_table = kxsd9_i2c_id,
|
||||
};
|
||||
module_i2c_driver(kxsd9_i2c_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("KXSD9 accelerometer I2C interface");
|
||||
|
||||
@@ -135,7 +135,7 @@ struct mma8452_event_regs {
|
||||
u8 ev_count;
|
||||
};
|
||||
|
||||
static const struct mma8452_event_regs ev_regs_accel_falling = {
|
||||
static const struct mma8452_event_regs ff_mt_ev_regs = {
|
||||
.ev_cfg = MMA8452_FF_MT_CFG,
|
||||
.ev_cfg_ele = MMA8452_FF_MT_CFG_ELE,
|
||||
.ev_cfg_chan_shift = MMA8452_FF_MT_CHAN_SHIFT,
|
||||
@@ -145,7 +145,7 @@ static const struct mma8452_event_regs ev_regs_accel_falling = {
|
||||
.ev_count = MMA8452_FF_MT_COUNT
|
||||
};
|
||||
|
||||
static const struct mma8452_event_regs ev_regs_accel_rising = {
|
||||
static const struct mma8452_event_regs trans_ev_regs = {
|
||||
.ev_cfg = MMA8452_TRANSIENT_CFG,
|
||||
.ev_cfg_ele = MMA8452_TRANSIENT_CFG_ELE,
|
||||
.ev_cfg_chan_shift = MMA8452_TRANSIENT_CHAN_SHIFT,
|
||||
@@ -284,7 +284,7 @@ static const int mma8452_samp_freq[8][2] = {
|
||||
};
|
||||
|
||||
/* Datasheet table: step time "Relationship with the ODR" (sample frequency) */
|
||||
static const unsigned int mma8452_transient_time_step_us[4][8] = {
|
||||
static const unsigned int mma8452_time_step_us[4][8] = {
|
||||
{ 1250, 2500, 5000, 10000, 20000, 20000, 20000, 20000 }, /* normal */
|
||||
{ 1250, 2500, 5000, 10000, 20000, 80000, 80000, 80000 }, /* l p l n */
|
||||
{ 1250, 2500, 2500, 2500, 2500, 2500, 2500, 2500 }, /* high res*/
|
||||
@@ -777,12 +777,12 @@ static int mma8452_get_event_regs(struct mma8452_data *data,
|
||||
& MMA8452_INT_TRANS) &&
|
||||
(data->chip_info->enabled_events
|
||||
& MMA8452_INT_TRANS))
|
||||
*ev_reg = &ev_regs_accel_rising;
|
||||
*ev_reg = &trans_ev_regs;
|
||||
else
|
||||
*ev_reg = &ev_regs_accel_falling;
|
||||
*ev_reg = &ff_mt_ev_regs;
|
||||
return 0;
|
||||
case IIO_EV_DIR_FALLING:
|
||||
*ev_reg = &ev_regs_accel_falling;
|
||||
*ev_reg = &ff_mt_ev_regs;
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@@ -826,7 +826,7 @@ static int mma8452_read_event_value(struct iio_dev *indio_dev,
|
||||
if (power_mode < 0)
|
||||
return power_mode;
|
||||
|
||||
us = ret * mma8452_transient_time_step_us[power_mode][
|
||||
us = ret * mma8452_time_step_us[power_mode][
|
||||
mma8452_get_odr_index(data)];
|
||||
*val = us / USEC_PER_SEC;
|
||||
*val2 = us % USEC_PER_SEC;
|
||||
@@ -883,7 +883,7 @@ static int mma8452_write_event_value(struct iio_dev *indio_dev,
|
||||
return ret;
|
||||
|
||||
steps = (val * USEC_PER_SEC + val2) /
|
||||
mma8452_transient_time_step_us[ret][
|
||||
mma8452_time_step_us[ret][
|
||||
mma8452_get_odr_index(data)];
|
||||
|
||||
if (steps < 0 || steps > 0xff)
|
||||
|
||||
@@ -920,8 +920,6 @@ static const struct iio_trigger_ops st_accel_trigger_ops = {
|
||||
int st_accel_common_probe(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *adata = iio_priv(indio_dev);
|
||||
struct st_sensors_platform_data *pdata =
|
||||
(struct st_sensors_platform_data *)adata->dev->platform_data;
|
||||
int irq = adata->get_irq_data_ready(indio_dev);
|
||||
int err;
|
||||
|
||||
@@ -948,9 +946,6 @@ int st_accel_common_probe(struct iio_dev *indio_dev)
|
||||
&adata->sensor_settings->fs.fs_avl[0];
|
||||
adata->odr = adata->sensor_settings->odr.odr_avl[0].hz;
|
||||
|
||||
if (!pdata)
|
||||
pdata = (struct st_sensors_platform_data *)&default_accel_pdata;
|
||||
|
||||
err = st_sensors_init_sensor(indio_dev, adata->dev->platform_data);
|
||||
if (err < 0)
|
||||
goto st_accel_power_off;
|
||||
|
||||
@@ -158,6 +158,7 @@ config AT91_SAMA5D2_ADC
|
||||
tristate "Atmel AT91 SAMA5D2 ADC"
|
||||
depends on ARCH_AT91 || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
depends on HAS_DMA
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for Atmel SAMA5D2 ADC which is
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
@@ -53,11 +54,12 @@ struct aspeed_adc_model_data {
|
||||
};
|
||||
|
||||
struct aspeed_adc_data {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
spinlock_t clk_lock;
|
||||
struct clk_hw *clk_prescaler;
|
||||
struct clk_hw *clk_scaler;
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
spinlock_t clk_lock;
|
||||
struct clk_hw *clk_prescaler;
|
||||
struct clk_hw *clk_scaler;
|
||||
struct reset_control *rst;
|
||||
};
|
||||
|
||||
#define ASPEED_CHAN(_idx, _data_reg_addr) { \
|
||||
@@ -217,6 +219,15 @@ static int aspeed_adc_probe(struct platform_device *pdev)
|
||||
goto scaler_error;
|
||||
}
|
||||
|
||||
data->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL);
|
||||
if (IS_ERR(data->rst)) {
|
||||
dev_err(&pdev->dev,
|
||||
"invalid or missing reset controller device tree entry");
|
||||
ret = PTR_ERR(data->rst);
|
||||
goto reset_error;
|
||||
}
|
||||
reset_control_deassert(data->rst);
|
||||
|
||||
model_data = of_device_get_match_data(&pdev->dev);
|
||||
|
||||
if (model_data->wait_init_sequence) {
|
||||
@@ -263,9 +274,10 @@ iio_register_error:
|
||||
writel(ASPEED_OPERATION_MODE_POWER_DOWN,
|
||||
data->base + ASPEED_REG_ENGINE_CONTROL);
|
||||
clk_disable_unprepare(data->clk_scaler->clk);
|
||||
reset_error:
|
||||
reset_control_assert(data->rst);
|
||||
clk_enable_error:
|
||||
clk_hw_unregister_divider(data->clk_scaler);
|
||||
|
||||
scaler_error:
|
||||
clk_hw_unregister_divider(data->clk_prescaler);
|
||||
return ret;
|
||||
@@ -280,6 +292,7 @@ static int aspeed_adc_remove(struct platform_device *pdev)
|
||||
writel(ASPEED_OPERATION_MODE_POWER_DOWN,
|
||||
data->base + ASPEED_REG_ENGINE_CONTROL);
|
||||
clk_disable_unprepare(data->clk_scaler->clk);
|
||||
reset_control_assert(data->rst);
|
||||
clk_hw_unregister_divider(data->clk_scaler);
|
||||
clk_hw_unregister_divider(data->clk_prescaler);
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
@@ -100,6 +102,8 @@
|
||||
#define AT91_SAMA5D2_LCDR 0x20
|
||||
/* Interrupt Enable Register */
|
||||
#define AT91_SAMA5D2_IER 0x24
|
||||
/* Interrupt Enable Register - general overrun error */
|
||||
#define AT91_SAMA5D2_IER_GOVRE BIT(25)
|
||||
/* Interrupt Disable Register */
|
||||
#define AT91_SAMA5D2_IDR 0x28
|
||||
/* Interrupt Mask Register */
|
||||
@@ -167,13 +171,19 @@
|
||||
|
||||
/*
|
||||
* Maximum number of bytes to hold conversion from all channels
|
||||
* plus the timestamp
|
||||
* without the timestamp.
|
||||
*/
|
||||
#define AT91_BUFFER_MAX_BYTES ((AT91_SAMA5D2_SINGLE_CHAN_CNT + \
|
||||
AT91_SAMA5D2_DIFF_CHAN_CNT) * 2 + 8)
|
||||
#define AT91_BUFFER_MAX_CONVERSION_BYTES ((AT91_SAMA5D2_SINGLE_CHAN_CNT + \
|
||||
AT91_SAMA5D2_DIFF_CHAN_CNT) * 2)
|
||||
|
||||
/* This total must also include the timestamp */
|
||||
#define AT91_BUFFER_MAX_BYTES (AT91_BUFFER_MAX_CONVERSION_BYTES + 8)
|
||||
|
||||
#define AT91_BUFFER_MAX_HWORDS (AT91_BUFFER_MAX_BYTES / 2)
|
||||
|
||||
#define AT91_HWFIFO_MAX_SIZE_STR "128"
|
||||
#define AT91_HWFIFO_MAX_SIZE 128
|
||||
|
||||
#define AT91_SAMA5D2_CHAN_SINGLE(num, addr) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
@@ -228,6 +238,28 @@ struct at91_adc_trigger {
|
||||
bool hw_trig;
|
||||
};
|
||||
|
||||
/**
|
||||
* at91_adc_dma - at91-sama5d2 dma information struct
|
||||
* @dma_chan: the dma channel acquired
|
||||
* @rx_buf: dma coherent allocated area
|
||||
* @rx_dma_buf: dma handler for the buffer
|
||||
* @phys_addr: physical address of the ADC base register
|
||||
* @buf_idx: index inside the dma buffer where reading was last done
|
||||
* @rx_buf_sz: size of buffer used by DMA operation
|
||||
* @watermark: number of conversions to copy before DMA triggers irq
|
||||
* @dma_ts: hold the start timestamp of dma operation
|
||||
*/
|
||||
struct at91_adc_dma {
|
||||
struct dma_chan *dma_chan;
|
||||
u8 *rx_buf;
|
||||
dma_addr_t rx_dma_buf;
|
||||
phys_addr_t phys_addr;
|
||||
int buf_idx;
|
||||
int rx_buf_sz;
|
||||
int watermark;
|
||||
s64 dma_ts;
|
||||
};
|
||||
|
||||
struct at91_adc_state {
|
||||
void __iomem *base;
|
||||
int irq;
|
||||
@@ -242,6 +274,7 @@ struct at91_adc_state {
|
||||
u32 conversion_value;
|
||||
struct at91_adc_soc_info soc_info;
|
||||
wait_queue_head_t wq_data_available;
|
||||
struct at91_adc_dma dma_st;
|
||||
u16 buffer[AT91_BUFFER_MAX_HWORDS];
|
||||
/*
|
||||
* lock to prevent concurrent 'single conversion' requests through
|
||||
@@ -322,11 +355,17 @@ static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state)
|
||||
if (state) {
|
||||
at91_adc_writel(st, AT91_SAMA5D2_CHER,
|
||||
BIT(chan->channel));
|
||||
at91_adc_writel(st, AT91_SAMA5D2_IER,
|
||||
BIT(chan->channel));
|
||||
/* enable irq only if not using DMA */
|
||||
if (!st->dma_st.dma_chan) {
|
||||
at91_adc_writel(st, AT91_SAMA5D2_IER,
|
||||
BIT(chan->channel));
|
||||
}
|
||||
} else {
|
||||
at91_adc_writel(st, AT91_SAMA5D2_IDR,
|
||||
BIT(chan->channel));
|
||||
/* disable irq only if not using DMA */
|
||||
if (!st->dma_st.dma_chan) {
|
||||
at91_adc_writel(st, AT91_SAMA5D2_IDR,
|
||||
BIT(chan->channel));
|
||||
}
|
||||
at91_adc_writel(st, AT91_SAMA5D2_CHDR,
|
||||
BIT(chan->channel));
|
||||
}
|
||||
@@ -340,6 +379,10 @@ static int at91_adc_reenable_trigger(struct iio_trigger *trig)
|
||||
struct iio_dev *indio = iio_trigger_get_drvdata(trig);
|
||||
struct at91_adc_state *st = iio_priv(indio);
|
||||
|
||||
/* if we are using DMA, we must not reenable irq after each trigger */
|
||||
if (st->dma_st.dma_chan)
|
||||
return 0;
|
||||
|
||||
enable_irq(st->irq);
|
||||
|
||||
/* Needed to ACK the DRDY interruption */
|
||||
@@ -350,6 +393,153 @@ static int at91_adc_reenable_trigger(struct iio_trigger *trig)
|
||||
static const struct iio_trigger_ops at91_adc_trigger_ops = {
|
||||
.set_trigger_state = &at91_adc_configure_trigger,
|
||||
.try_reenable = &at91_adc_reenable_trigger,
|
||||
.validate_device = iio_trigger_validate_own_device,
|
||||
};
|
||||
|
||||
static int at91_adc_dma_size_done(struct at91_adc_state *st)
|
||||
{
|
||||
struct dma_tx_state state;
|
||||
enum dma_status status;
|
||||
int i, size;
|
||||
|
||||
status = dmaengine_tx_status(st->dma_st.dma_chan,
|
||||
st->dma_st.dma_chan->cookie,
|
||||
&state);
|
||||
if (status != DMA_IN_PROGRESS)
|
||||
return 0;
|
||||
|
||||
/* Transferred length is size in bytes from end of buffer */
|
||||
i = st->dma_st.rx_buf_sz - state.residue;
|
||||
|
||||
/* Return available bytes */
|
||||
if (i >= st->dma_st.buf_idx)
|
||||
size = i - st->dma_st.buf_idx;
|
||||
else
|
||||
size = st->dma_st.rx_buf_sz + i - st->dma_st.buf_idx;
|
||||
return size;
|
||||
}
|
||||
|
||||
static void at91_dma_buffer_done(void *data)
|
||||
{
|
||||
struct iio_dev *indio_dev = data;
|
||||
|
||||
iio_trigger_poll_chained(indio_dev->trig);
|
||||
}
|
||||
|
||||
static int at91_adc_dma_start(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct at91_adc_state *st = iio_priv(indio_dev);
|
||||
struct dma_async_tx_descriptor *desc;
|
||||
dma_cookie_t cookie;
|
||||
int ret;
|
||||
u8 bit;
|
||||
|
||||
if (!st->dma_st.dma_chan)
|
||||
return 0;
|
||||
|
||||
/* we start a new DMA, so set buffer index to start */
|
||||
st->dma_st.buf_idx = 0;
|
||||
|
||||
/*
|
||||
* compute buffer size w.r.t. watermark and enabled channels.
|
||||
* scan_bytes is aligned so we need an exact size for DMA
|
||||
*/
|
||||
st->dma_st.rx_buf_sz = 0;
|
||||
|
||||
for_each_set_bit(bit, indio_dev->active_scan_mask,
|
||||
indio_dev->num_channels) {
|
||||
struct iio_chan_spec const *chan = indio_dev->channels + bit;
|
||||
|
||||
st->dma_st.rx_buf_sz += chan->scan_type.storagebits / 8;
|
||||
}
|
||||
st->dma_st.rx_buf_sz *= st->dma_st.watermark;
|
||||
|
||||
/* Prepare a DMA cyclic transaction */
|
||||
desc = dmaengine_prep_dma_cyclic(st->dma_st.dma_chan,
|
||||
st->dma_st.rx_dma_buf,
|
||||
st->dma_st.rx_buf_sz,
|
||||
st->dma_st.rx_buf_sz / 2,
|
||||
DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);
|
||||
|
||||
if (!desc) {
|
||||
dev_err(&indio_dev->dev, "cannot prepare DMA cyclic\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
desc->callback = at91_dma_buffer_done;
|
||||
desc->callback_param = indio_dev;
|
||||
|
||||
cookie = dmaengine_submit(desc);
|
||||
ret = dma_submit_error(cookie);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "cannot submit DMA cyclic\n");
|
||||
dmaengine_terminate_async(st->dma_st.dma_chan);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* enable general overrun error signaling */
|
||||
at91_adc_writel(st, AT91_SAMA5D2_IER, AT91_SAMA5D2_IER_GOVRE);
|
||||
/* Issue pending DMA requests */
|
||||
dma_async_issue_pending(st->dma_st.dma_chan);
|
||||
|
||||
/* consider current time as DMA start time for timestamps */
|
||||
st->dma_st.dma_ts = iio_get_time_ns(indio_dev);
|
||||
|
||||
dev_dbg(&indio_dev->dev, "DMA cyclic started\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at91_adc_buffer_postenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = at91_adc_dma_start(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "buffer postenable failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return iio_triggered_buffer_postenable(indio_dev);
|
||||
}
|
||||
|
||||
static int at91_adc_buffer_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct at91_adc_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
u8 bit;
|
||||
|
||||
ret = iio_triggered_buffer_predisable(indio_dev);
|
||||
if (ret < 0)
|
||||
dev_err(&indio_dev->dev, "buffer predisable failed\n");
|
||||
|
||||
if (!st->dma_st.dma_chan)
|
||||
return ret;
|
||||
|
||||
/* if we are using DMA we must clear registers and end DMA */
|
||||
dmaengine_terminate_sync(st->dma_st.dma_chan);
|
||||
|
||||
/*
|
||||
* For each enabled channel we must read the last converted value
|
||||
* to clear EOC status and not get a possible interrupt later.
|
||||
* This value is being read by DMA from LCDR anyway
|
||||
*/
|
||||
for_each_set_bit(bit, indio_dev->active_scan_mask,
|
||||
indio_dev->num_channels) {
|
||||
struct iio_chan_spec const *chan = indio_dev->channels + bit;
|
||||
|
||||
if (st->dma_st.dma_chan)
|
||||
at91_adc_readl(st, chan->address);
|
||||
}
|
||||
|
||||
/* read overflow register to clear possible overflow status */
|
||||
at91_adc_readl(st, AT91_SAMA5D2_OVER);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops at91_buffer_setup_ops = {
|
||||
.postenable = &at91_adc_buffer_postenable,
|
||||
.predisable = &at91_adc_buffer_predisable,
|
||||
};
|
||||
|
||||
static struct iio_trigger *at91_adc_allocate_trigger(struct iio_dev *indio,
|
||||
@@ -388,24 +578,77 @@ static int at91_adc_trigger_init(struct iio_dev *indio)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
|
||||
static void at91_adc_trigger_handler_nodma(struct iio_dev *indio_dev,
|
||||
struct iio_poll_func *pf)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio = pf->indio_dev;
|
||||
struct at91_adc_state *st = iio_priv(indio);
|
||||
struct at91_adc_state *st = iio_priv(indio_dev);
|
||||
int i = 0;
|
||||
u8 bit;
|
||||
|
||||
for_each_set_bit(bit, indio->active_scan_mask, indio->num_channels) {
|
||||
struct iio_chan_spec const *chan = indio->channels + bit;
|
||||
for_each_set_bit(bit, indio_dev->active_scan_mask,
|
||||
indio_dev->num_channels) {
|
||||
struct iio_chan_spec const *chan = indio_dev->channels + bit;
|
||||
|
||||
st->buffer[i] = at91_adc_readl(st, chan->address);
|
||||
i++;
|
||||
}
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, st->buffer,
|
||||
pf->timestamp);
|
||||
}
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio, st->buffer, pf->timestamp);
|
||||
static void at91_adc_trigger_handler_dma(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct at91_adc_state *st = iio_priv(indio_dev);
|
||||
int transferred_len = at91_adc_dma_size_done(st);
|
||||
s64 ns = iio_get_time_ns(indio_dev);
|
||||
s64 interval;
|
||||
int sample_index = 0, sample_count, sample_size;
|
||||
|
||||
iio_trigger_notify_done(indio->trig);
|
||||
u32 status = at91_adc_readl(st, AT91_SAMA5D2_ISR);
|
||||
/* if we reached this point, we cannot sample faster */
|
||||
if (status & AT91_SAMA5D2_IER_GOVRE)
|
||||
pr_info_ratelimited("%s: conversion overrun detected\n",
|
||||
indio_dev->name);
|
||||
|
||||
sample_size = div_s64(st->dma_st.rx_buf_sz, st->dma_st.watermark);
|
||||
|
||||
sample_count = div_s64(transferred_len, sample_size);
|
||||
|
||||
/*
|
||||
* interval between samples is total time since last transfer handling
|
||||
* divided by the number of samples (total size divided by sample size)
|
||||
*/
|
||||
interval = div_s64((ns - st->dma_st.dma_ts), sample_count);
|
||||
|
||||
while (transferred_len >= sample_size) {
|
||||
iio_push_to_buffers_with_timestamp(indio_dev,
|
||||
(st->dma_st.rx_buf + st->dma_st.buf_idx),
|
||||
(st->dma_st.dma_ts + interval * sample_index));
|
||||
/* adjust remaining length */
|
||||
transferred_len -= sample_size;
|
||||
/* adjust buffer index */
|
||||
st->dma_st.buf_idx += sample_size;
|
||||
/* in case of reaching end of buffer, reset index */
|
||||
if (st->dma_st.buf_idx >= st->dma_st.rx_buf_sz)
|
||||
st->dma_st.buf_idx = 0;
|
||||
sample_index++;
|
||||
}
|
||||
/* adjust saved time for next transfer handling */
|
||||
st->dma_st.dma_ts = iio_get_time_ns(indio_dev);
|
||||
}
|
||||
|
||||
static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct at91_adc_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (st->dma_st.dma_chan)
|
||||
at91_adc_trigger_handler_dma(indio_dev);
|
||||
else
|
||||
at91_adc_trigger_handler_nodma(indio_dev, pf);
|
||||
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
@@ -414,7 +657,7 @@ static int at91_adc_buffer_init(struct iio_dev *indio)
|
||||
{
|
||||
return devm_iio_triggered_buffer_setup(&indio->dev, indio,
|
||||
&iio_pollfunc_store_time,
|
||||
&at91_adc_trigger_handler, NULL);
|
||||
&at91_adc_trigger_handler, &at91_buffer_setup_ops);
|
||||
}
|
||||
|
||||
static unsigned at91_adc_startup_time(unsigned startup_time_min,
|
||||
@@ -485,10 +728,13 @@ static irqreturn_t at91_adc_interrupt(int irq, void *private)
|
||||
if (!(status & imr))
|
||||
return IRQ_NONE;
|
||||
|
||||
if (iio_buffer_enabled(indio)) {
|
||||
if (iio_buffer_enabled(indio) && !st->dma_st.dma_chan) {
|
||||
disable_irq_nosync(irq);
|
||||
iio_trigger_poll(indio->trig);
|
||||
} else {
|
||||
} else if (iio_buffer_enabled(indio) && st->dma_st.dma_chan) {
|
||||
disable_irq_nosync(irq);
|
||||
WARN(true, "Unexpected irq occurred\n");
|
||||
} else if (!iio_buffer_enabled(indio)) {
|
||||
st->conversion_value = at91_adc_readl(st, st->chan->address);
|
||||
st->conversion_done = true;
|
||||
wake_up_interruptible(&st->wq_data_available);
|
||||
@@ -510,7 +756,6 @@ static int at91_adc_read_raw(struct iio_dev *indio_dev,
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
|
||||
st->chan = chan;
|
||||
@@ -541,6 +786,9 @@ static int at91_adc_read_raw(struct iio_dev *indio_dev,
|
||||
at91_adc_writel(st, AT91_SAMA5D2_IDR, BIT(chan->channel));
|
||||
at91_adc_writel(st, AT91_SAMA5D2_CHDR, BIT(chan->channel));
|
||||
|
||||
/* Needed to ACK the DRDY interruption */
|
||||
at91_adc_readl(st, AT91_SAMA5D2_LCDR);
|
||||
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
@@ -580,9 +828,123 @@ static int at91_adc_write_raw(struct iio_dev *indio_dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void at91_adc_dma_init(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct at91_adc_state *st = iio_priv(indio_dev);
|
||||
struct dma_slave_config config = {0};
|
||||
/*
|
||||
* We make the buffer double the size of the fifo,
|
||||
* such that DMA uses one half of the buffer (full fifo size)
|
||||
* and the software uses the other half to read/write.
|
||||
*/
|
||||
unsigned int pages = DIV_ROUND_UP(AT91_HWFIFO_MAX_SIZE *
|
||||
AT91_BUFFER_MAX_CONVERSION_BYTES * 2,
|
||||
PAGE_SIZE);
|
||||
|
||||
if (st->dma_st.dma_chan)
|
||||
return;
|
||||
|
||||
st->dma_st.dma_chan = dma_request_slave_channel(&pdev->dev, "rx");
|
||||
|
||||
if (!st->dma_st.dma_chan) {
|
||||
dev_info(&pdev->dev, "can't get DMA channel\n");
|
||||
goto dma_exit;
|
||||
}
|
||||
|
||||
st->dma_st.rx_buf = dma_alloc_coherent(st->dma_st.dma_chan->device->dev,
|
||||
pages * PAGE_SIZE,
|
||||
&st->dma_st.rx_dma_buf,
|
||||
GFP_KERNEL);
|
||||
if (!st->dma_st.rx_buf) {
|
||||
dev_info(&pdev->dev, "can't allocate coherent DMA area\n");
|
||||
goto dma_chan_disable;
|
||||
}
|
||||
|
||||
/* Configure DMA channel to read data register */
|
||||
config.direction = DMA_DEV_TO_MEM;
|
||||
config.src_addr = (phys_addr_t)(st->dma_st.phys_addr
|
||||
+ AT91_SAMA5D2_LCDR);
|
||||
config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
|
||||
config.src_maxburst = 1;
|
||||
config.dst_maxburst = 1;
|
||||
|
||||
if (dmaengine_slave_config(st->dma_st.dma_chan, &config)) {
|
||||
dev_info(&pdev->dev, "can't configure DMA slave\n");
|
||||
goto dma_free_area;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "using %s for rx DMA transfers\n",
|
||||
dma_chan_name(st->dma_st.dma_chan));
|
||||
|
||||
return;
|
||||
|
||||
dma_free_area:
|
||||
dma_free_coherent(st->dma_st.dma_chan->device->dev, pages * PAGE_SIZE,
|
||||
st->dma_st.rx_buf, st->dma_st.rx_dma_buf);
|
||||
dma_chan_disable:
|
||||
dma_release_channel(st->dma_st.dma_chan);
|
||||
st->dma_st.dma_chan = 0;
|
||||
dma_exit:
|
||||
dev_info(&pdev->dev, "continuing without DMA support\n");
|
||||
}
|
||||
|
||||
static void at91_adc_dma_disable(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct at91_adc_state *st = iio_priv(indio_dev);
|
||||
unsigned int pages = DIV_ROUND_UP(AT91_HWFIFO_MAX_SIZE *
|
||||
AT91_BUFFER_MAX_CONVERSION_BYTES * 2,
|
||||
PAGE_SIZE);
|
||||
|
||||
/* if we are not using DMA, just return */
|
||||
if (!st->dma_st.dma_chan)
|
||||
return;
|
||||
|
||||
/* wait for all transactions to be terminated first*/
|
||||
dmaengine_terminate_sync(st->dma_st.dma_chan);
|
||||
|
||||
dma_free_coherent(st->dma_st.dma_chan->device->dev, pages * PAGE_SIZE,
|
||||
st->dma_st.rx_buf, st->dma_st.rx_dma_buf);
|
||||
dma_release_channel(st->dma_st.dma_chan);
|
||||
st->dma_st.dma_chan = 0;
|
||||
|
||||
dev_info(&pdev->dev, "continuing without DMA support\n");
|
||||
}
|
||||
|
||||
static int at91_adc_set_watermark(struct iio_dev *indio_dev, unsigned int val)
|
||||
{
|
||||
struct at91_adc_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (val > AT91_HWFIFO_MAX_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
if (!st->selected_trig->hw_trig) {
|
||||
dev_dbg(&indio_dev->dev, "we need hw trigger for DMA\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev_dbg(&indio_dev->dev, "new watermark is %u\n", val);
|
||||
st->dma_st.watermark = val;
|
||||
|
||||
/*
|
||||
* The logic here is: if we have watermark 1, it means we do
|
||||
* each conversion with it's own IRQ, thus we don't need DMA.
|
||||
* If the watermark is higher, we do DMA to do all the transfers in bulk
|
||||
*/
|
||||
|
||||
if (val == 1)
|
||||
at91_adc_dma_disable(to_platform_device(&indio_dev->dev));
|
||||
else if (val > 1)
|
||||
at91_adc_dma_init(to_platform_device(&indio_dev->dev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_info at91_adc_info = {
|
||||
.read_raw = &at91_adc_read_raw,
|
||||
.write_raw = &at91_adc_write_raw,
|
||||
.hwfifo_set_watermark = &at91_adc_set_watermark,
|
||||
};
|
||||
|
||||
static void at91_adc_hw_init(struct at91_adc_state *st)
|
||||
@@ -599,6 +961,42 @@ static void at91_adc_hw_init(struct at91_adc_state *st)
|
||||
at91_adc_setup_samp_freq(st, st->soc_info.min_sample_rate);
|
||||
}
|
||||
|
||||
static ssize_t at91_adc_get_fifo_state(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev =
|
||||
platform_get_drvdata(to_platform_device(dev));
|
||||
struct at91_adc_state *st = iio_priv(indio_dev);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", !!st->dma_st.dma_chan);
|
||||
}
|
||||
|
||||
static ssize_t at91_adc_get_watermark(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev =
|
||||
platform_get_drvdata(to_platform_device(dev));
|
||||
struct at91_adc_state *st = iio_priv(indio_dev);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", st->dma_st.watermark);
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(hwfifo_enabled, 0444,
|
||||
at91_adc_get_fifo_state, NULL, 0);
|
||||
static IIO_DEVICE_ATTR(hwfifo_watermark, 0444,
|
||||
at91_adc_get_watermark, NULL, 0);
|
||||
|
||||
static IIO_CONST_ATTR(hwfifo_watermark_min, "2");
|
||||
static IIO_CONST_ATTR(hwfifo_watermark_max, AT91_HWFIFO_MAX_SIZE_STR);
|
||||
|
||||
static const struct attribute *at91_adc_fifo_attributes[] = {
|
||||
&iio_const_attr_hwfifo_watermark_min.dev_attr.attr,
|
||||
&iio_const_attr_hwfifo_watermark_max.dev_attr.attr,
|
||||
&iio_dev_attr_hwfifo_watermark.dev_attr.attr,
|
||||
&iio_dev_attr_hwfifo_enabled.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static int at91_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
@@ -674,6 +1072,9 @@ static int at91_adc_probe(struct platform_device *pdev)
|
||||
if (!res)
|
||||
return -EINVAL;
|
||||
|
||||
/* if we plan to use DMA, we need the physical address of the regs */
|
||||
st->dma_st.phys_addr = res->start;
|
||||
|
||||
st->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(st->base))
|
||||
return PTR_ERR(st->base);
|
||||
@@ -737,11 +1138,22 @@ static int at91_adc_probe(struct platform_device *pdev)
|
||||
dev_err(&pdev->dev, "couldn't setup the triggers.\n");
|
||||
goto per_clk_disable_unprepare;
|
||||
}
|
||||
/*
|
||||
* Initially the iio buffer has a length of 2 and
|
||||
* a watermark of 1
|
||||
*/
|
||||
st->dma_st.watermark = 1;
|
||||
|
||||
iio_buffer_set_attrs(indio_dev->buffer,
|
||||
at91_adc_fifo_attributes);
|
||||
}
|
||||
|
||||
if (dma_coerce_mask_and_coherent(&indio_dev->dev, DMA_BIT_MASK(32)))
|
||||
dev_info(&pdev->dev, "cannot set DMA mask to 32-bit\n");
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
goto per_clk_disable_unprepare;
|
||||
goto dma_disable;
|
||||
|
||||
if (st->selected_trig->hw_trig)
|
||||
dev_info(&pdev->dev, "setting up trigger as %s\n",
|
||||
@@ -752,6 +1164,8 @@ static int at91_adc_probe(struct platform_device *pdev)
|
||||
|
||||
return 0;
|
||||
|
||||
dma_disable:
|
||||
at91_adc_dma_disable(pdev);
|
||||
per_clk_disable_unprepare:
|
||||
clk_disable_unprepare(st->per_clk);
|
||||
vref_disable:
|
||||
@@ -768,6 +1182,8 @@ static int at91_adc_remove(struct platform_device *pdev)
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
at91_adc_dma_disable(pdev);
|
||||
|
||||
clk_disable_unprepare(st->per_clk);
|
||||
|
||||
regulator_disable(st->vref);
|
||||
|
||||
@@ -92,22 +92,14 @@ static const struct iio_chan_spec axp288_adc_channels[] = {
|
||||
},
|
||||
};
|
||||
|
||||
#define AXP288_ADC_MAP(_adc_channel_label, _consumer_dev_name, \
|
||||
_consumer_channel) \
|
||||
{ \
|
||||
.adc_channel_label = _adc_channel_label, \
|
||||
.consumer_dev_name = _consumer_dev_name, \
|
||||
.consumer_channel = _consumer_channel, \
|
||||
}
|
||||
|
||||
/* for consumer drivers */
|
||||
static struct iio_map axp288_adc_default_maps[] = {
|
||||
AXP288_ADC_MAP("TS_PIN", "axp288-batt", "axp288-batt-temp"),
|
||||
AXP288_ADC_MAP("PMIC_TEMP", "axp288-pmic", "axp288-pmic-temp"),
|
||||
AXP288_ADC_MAP("GPADC", "axp288-gpadc", "axp288-system-temp"),
|
||||
AXP288_ADC_MAP("BATT_CHG_I", "axp288-chrg", "axp288-chrg-curr"),
|
||||
AXP288_ADC_MAP("BATT_DISCHRG_I", "axp288-chrg", "axp288-chrg-d-curr"),
|
||||
AXP288_ADC_MAP("BATT_V", "axp288-batt", "axp288-batt-volt"),
|
||||
IIO_MAP("TS_PIN", "axp288-batt", "axp288-batt-temp"),
|
||||
IIO_MAP("PMIC_TEMP", "axp288-pmic", "axp288-pmic-temp"),
|
||||
IIO_MAP("GPADC", "axp288-gpadc", "axp288-system-temp"),
|
||||
IIO_MAP("BATT_CHG_I", "axp288-chrg", "axp288-chrg-curr"),
|
||||
IIO_MAP("BATT_DISCHRG_I", "axp288-chrg", "axp288-chrg-d-curr"),
|
||||
IIO_MAP("BATT_V", "axp288-batt", "axp288-batt-volt"),
|
||||
{},
|
||||
};
|
||||
|
||||
|
||||
@@ -44,13 +44,14 @@
|
||||
|
||||
#define INA226_MASK_ENABLE 0x06
|
||||
#define INA226_CVRF BIT(3)
|
||||
#define INA219_CNVR BIT(1)
|
||||
|
||||
#define INA2XX_MAX_REGISTERS 8
|
||||
|
||||
/* settings - depend on use case */
|
||||
#define INA219_CONFIG_DEFAULT 0x399F /* PGA=8 */
|
||||
#define INA219_CONFIG_DEFAULT 0x399F /* PGA=1/8, BRNG=32V */
|
||||
#define INA219_DEFAULT_IT 532
|
||||
#define INA219_DEFAULT_BRNG 1 /* 32V */
|
||||
#define INA219_DEFAULT_PGA 125 /* 1000/8 */
|
||||
#define INA226_CONFIG_DEFAULT 0x4327
|
||||
#define INA226_DEFAULT_AVG 4
|
||||
#define INA226_DEFAULT_IT 1110
|
||||
@@ -63,6 +64,14 @@
|
||||
*/
|
||||
#define INA2XX_MODE_MASK GENMASK(3, 0)
|
||||
|
||||
/* Gain for VShunt: 1/8 (default), 1/4, 1/2, 1 */
|
||||
#define INA219_PGA_MASK GENMASK(12, 11)
|
||||
#define INA219_SHIFT_PGA(val) ((val) << 11)
|
||||
|
||||
/* VBus range: 32V (default), 16V */
|
||||
#define INA219_BRNG_MASK BIT(13)
|
||||
#define INA219_SHIFT_BRNG(val) ((val) << 13)
|
||||
|
||||
/* Averaging for VBus/VShunt/Power */
|
||||
#define INA226_AVG_MASK GENMASK(11, 9)
|
||||
#define INA226_SHIFT_AVG(val) ((val) << 9)
|
||||
@@ -79,6 +88,11 @@
|
||||
#define INA226_ITS_MASK GENMASK(5, 3)
|
||||
#define INA226_SHIFT_ITS(val) ((val) << 3)
|
||||
|
||||
/* INA219 Bus voltage register, low bits are flags */
|
||||
#define INA219_OVF BIT(0)
|
||||
#define INA219_CNVR BIT(1)
|
||||
#define INA219_BUS_VOLTAGE_SHIFT 3
|
||||
|
||||
/* Cosmetic macro giving the sampling period for a full P=UxI cycle */
|
||||
#define SAMPLING_PERIOD(c) ((c->int_time_vbus + c->int_time_vshunt) \
|
||||
* c->avg)
|
||||
@@ -111,8 +125,8 @@ enum ina2xx_ids { ina219, ina226 };
|
||||
struct ina2xx_config {
|
||||
u16 config_default;
|
||||
int calibration_factor;
|
||||
int shunt_div;
|
||||
int bus_voltage_shift;
|
||||
int shunt_voltage_lsb; /* nV */
|
||||
int bus_voltage_shift; /* position of lsb */
|
||||
int bus_voltage_lsb; /* uV */
|
||||
int power_lsb; /* uW */
|
||||
enum ina2xx_ids chip_id;
|
||||
@@ -127,6 +141,8 @@ struct ina2xx_chip_info {
|
||||
int avg;
|
||||
int int_time_vbus; /* Bus voltage integration time uS */
|
||||
int int_time_vshunt; /* Shunt voltage integration time uS */
|
||||
int range_vbus; /* Bus voltage maximum in V */
|
||||
int pga_gain_vshunt; /* Shunt voltage PGA gain */
|
||||
bool allow_async_readout;
|
||||
};
|
||||
|
||||
@@ -134,8 +150,8 @@ static const struct ina2xx_config ina2xx_config[] = {
|
||||
[ina219] = {
|
||||
.config_default = INA219_CONFIG_DEFAULT,
|
||||
.calibration_factor = 40960000,
|
||||
.shunt_div = 100,
|
||||
.bus_voltage_shift = 3,
|
||||
.shunt_voltage_lsb = 10000,
|
||||
.bus_voltage_shift = INA219_BUS_VOLTAGE_SHIFT,
|
||||
.bus_voltage_lsb = 4000,
|
||||
.power_lsb = 20000,
|
||||
.chip_id = ina219,
|
||||
@@ -143,7 +159,7 @@ static const struct ina2xx_config ina2xx_config[] = {
|
||||
[ina226] = {
|
||||
.config_default = INA226_CONFIG_DEFAULT,
|
||||
.calibration_factor = 5120000,
|
||||
.shunt_div = 400,
|
||||
.shunt_voltage_lsb = 2500,
|
||||
.bus_voltage_shift = 0,
|
||||
.bus_voltage_lsb = 1250,
|
||||
.power_lsb = 25000,
|
||||
@@ -170,6 +186,9 @@ static int ina2xx_read_raw(struct iio_dev *indio_dev,
|
||||
else
|
||||
*val = regval;
|
||||
|
||||
if (chan->address == INA2XX_BUS_VOLTAGE)
|
||||
*val >>= chip->config->bus_voltage_shift;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
@@ -197,15 +216,15 @@ static int ina2xx_read_raw(struct iio_dev *indio_dev,
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->address) {
|
||||
case INA2XX_SHUNT_VOLTAGE:
|
||||
/* processed (mV) = raw/shunt_div */
|
||||
*val2 = chip->config->shunt_div;
|
||||
*val = 1;
|
||||
/* processed (mV) = raw * lsb(nV) / 1000000 */
|
||||
*val = chip->config->shunt_voltage_lsb;
|
||||
*val2 = 1000000;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
|
||||
case INA2XX_BUS_VOLTAGE:
|
||||
/* processed (mV) = raw*lsb (uV) / (1000 << shift) */
|
||||
/* processed (mV) = raw * lsb (uV) / 1000 */
|
||||
*val = chip->config->bus_voltage_lsb;
|
||||
*val2 = 1000 << chip->config->bus_voltage_shift;
|
||||
*val2 = 1000;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
|
||||
case INA2XX_POWER:
|
||||
@@ -219,6 +238,18 @@ static int ina2xx_read_raw(struct iio_dev *indio_dev,
|
||||
*val = 1;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
case IIO_CHAN_INFO_HARDWAREGAIN:
|
||||
switch (chan->address) {
|
||||
case INA2XX_SHUNT_VOLTAGE:
|
||||
*val = chip->pga_gain_vshunt;
|
||||
*val2 = 1000;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
|
||||
case INA2XX_BUS_VOLTAGE:
|
||||
*val = chip->range_vbus == 32 ? 1 : 2;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
@@ -353,6 +384,74 @@ static int ina219_set_int_time_vshunt(struct ina2xx_chip_info *chip,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const int ina219_vbus_range_tab[] = { 1, 2 };
|
||||
static int ina219_set_vbus_range_denom(struct ina2xx_chip_info *chip,
|
||||
unsigned int range,
|
||||
unsigned int *config)
|
||||
{
|
||||
if (range == 1)
|
||||
chip->range_vbus = 32;
|
||||
else if (range == 2)
|
||||
chip->range_vbus = 16;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
*config &= ~INA219_BRNG_MASK;
|
||||
*config |= INA219_SHIFT_BRNG(range == 1 ? 1 : 0) & INA219_BRNG_MASK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const int ina219_vshunt_gain_tab[] = { 125, 250, 500, 1000 };
|
||||
static const int ina219_vshunt_gain_frac[] = {
|
||||
125, 1000, 250, 1000, 500, 1000, 1000, 1000 };
|
||||
|
||||
static int ina219_set_vshunt_pga_gain(struct ina2xx_chip_info *chip,
|
||||
unsigned int gain,
|
||||
unsigned int *config)
|
||||
{
|
||||
int bits;
|
||||
|
||||
if (gain < 125 || gain > 1000)
|
||||
return -EINVAL;
|
||||
|
||||
bits = find_closest(gain, ina219_vshunt_gain_tab,
|
||||
ARRAY_SIZE(ina219_vshunt_gain_tab));
|
||||
|
||||
chip->pga_gain_vshunt = ina219_vshunt_gain_tab[bits];
|
||||
bits = 3 - bits;
|
||||
|
||||
*config &= ~INA219_PGA_MASK;
|
||||
*config |= INA219_SHIFT_PGA(bits) & INA219_PGA_MASK;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ina2xx_read_avail(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
const int **vals, int *type, int *length,
|
||||
long mask)
|
||||
{
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_HARDWAREGAIN:
|
||||
switch (chan->address) {
|
||||
case INA2XX_SHUNT_VOLTAGE:
|
||||
*type = IIO_VAL_FRACTIONAL;
|
||||
*length = sizeof(ina219_vshunt_gain_frac) / sizeof(int);
|
||||
*vals = ina219_vshunt_gain_frac;
|
||||
return IIO_AVAIL_LIST;
|
||||
|
||||
case INA2XX_BUS_VOLTAGE:
|
||||
*type = IIO_VAL_INT;
|
||||
*length = sizeof(ina219_vbus_range_tab) / sizeof(int);
|
||||
*vals = ina219_vbus_range_tab;
|
||||
return IIO_AVAIL_LIST;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ina2xx_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
@@ -395,6 +494,14 @@ static int ina2xx_write_raw(struct iio_dev *indio_dev,
|
||||
}
|
||||
break;
|
||||
|
||||
case IIO_CHAN_INFO_HARDWAREGAIN:
|
||||
if (chan->address == INA2XX_SHUNT_VOLTAGE)
|
||||
ret = ina219_set_vshunt_pga_gain(chip, val * 1000 +
|
||||
val2 / 1000, &tmp);
|
||||
else
|
||||
ret = ina219_set_vbus_range_denom(chip, val, &tmp);
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
@@ -532,19 +639,23 @@ static ssize_t ina2xx_shunt_resistor_store(struct device *dev,
|
||||
* Sampling Freq is a consequence of the integration times of
|
||||
* the Voltage channels.
|
||||
*/
|
||||
#define INA219_CHAN_VOLTAGE(_index, _address) { \
|
||||
#define INA219_CHAN_VOLTAGE(_index, _address, _shift) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.address = (_address), \
|
||||
.indexed = 1, \
|
||||
.channel = (_index), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_INT_TIME), \
|
||||
BIT(IIO_CHAN_INFO_INT_TIME) | \
|
||||
BIT(IIO_CHAN_INFO_HARDWAREGAIN), \
|
||||
.info_mask_separate_available = \
|
||||
BIT(IIO_CHAN_INFO_HARDWAREGAIN), \
|
||||
.info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
.scan_index = (_index), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = 16, \
|
||||
.shift = _shift, \
|
||||
.realbits = 16 - _shift, \
|
||||
.storagebits = 16, \
|
||||
.endianness = IIO_LE, \
|
||||
} \
|
||||
@@ -579,8 +690,8 @@ static const struct iio_chan_spec ina226_channels[] = {
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec ina219_channels[] = {
|
||||
INA219_CHAN_VOLTAGE(0, INA2XX_SHUNT_VOLTAGE),
|
||||
INA219_CHAN_VOLTAGE(1, INA2XX_BUS_VOLTAGE),
|
||||
INA219_CHAN_VOLTAGE(0, INA2XX_SHUNT_VOLTAGE, 0),
|
||||
INA219_CHAN_VOLTAGE(1, INA2XX_BUS_VOLTAGE, INA219_BUS_VOLTAGE_SHIFT),
|
||||
INA219_CHAN(IIO_POWER, 2, INA2XX_POWER),
|
||||
INA219_CHAN(IIO_CURRENT, 3, INA2XX_CURRENT),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4),
|
||||
@@ -746,7 +857,6 @@ static IIO_CONST_ATTR_NAMED(ina226_integration_time_available,
|
||||
integration_time_available,
|
||||
"0.000140 0.000204 0.000332 0.000588 0.001100 0.002116 0.004156 0.008244");
|
||||
|
||||
|
||||
static IIO_DEVICE_ATTR(in_allow_async_readout, S_IRUGO | S_IWUSR,
|
||||
ina2xx_allow_async_readout_show,
|
||||
ina2xx_allow_async_readout_store, 0);
|
||||
@@ -780,6 +890,7 @@ static const struct attribute_group ina226_attribute_group = {
|
||||
static const struct iio_info ina219_info = {
|
||||
.attrs = &ina219_attribute_group,
|
||||
.read_raw = ina2xx_read_raw,
|
||||
.read_avail = ina2xx_read_avail,
|
||||
.write_raw = ina2xx_write_raw,
|
||||
.debugfs_reg_access = ina2xx_debug_reg,
|
||||
};
|
||||
@@ -860,6 +971,8 @@ static int ina2xx_probe(struct i2c_client *client,
|
||||
chip->avg = 1;
|
||||
ina219_set_int_time_vbus(chip, INA219_DEFAULT_IT, &val);
|
||||
ina219_set_int_time_vshunt(chip, INA219_DEFAULT_IT, &val);
|
||||
ina219_set_vbus_range_denom(chip, INA219_DEFAULT_BRNG, &val);
|
||||
ina219_set_vshunt_pga_gain(chip, INA219_DEFAULT_PGA, &val);
|
||||
}
|
||||
|
||||
ret = ina2xx_init(chip, val);
|
||||
|
||||
@@ -231,7 +231,6 @@ struct meson_sar_adc_priv {
|
||||
const struct meson_sar_adc_data *data;
|
||||
struct clk *clkin;
|
||||
struct clk *core_clk;
|
||||
struct clk *sana_clk;
|
||||
struct clk *adc_sel_clk;
|
||||
struct clk *adc_clk;
|
||||
struct clk_gate clk_gate;
|
||||
@@ -708,12 +707,6 @@ static int meson_sar_adc_hw_enable(struct iio_dev *indio_dev)
|
||||
goto err_core_clk;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(priv->sana_clk);
|
||||
if (ret) {
|
||||
dev_err(indio_dev->dev.parent, "failed to enable sana clk\n");
|
||||
goto err_sana_clk;
|
||||
}
|
||||
|
||||
regval = FIELD_PREP(MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, 1);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
|
||||
MESON_SAR_ADC_REG0_FIFO_CNT_IRQ_MASK, regval);
|
||||
@@ -741,8 +734,6 @@ err_adc_clk:
|
||||
MESON_SAR_ADC_REG3_ADC_EN, 0);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11,
|
||||
MESON_SAR_ADC_REG11_BANDGAP_EN, 0);
|
||||
clk_disable_unprepare(priv->sana_clk);
|
||||
err_sana_clk:
|
||||
clk_disable_unprepare(priv->core_clk);
|
||||
err_core_clk:
|
||||
regulator_disable(priv->vref);
|
||||
@@ -768,7 +759,6 @@ static int meson_sar_adc_hw_disable(struct iio_dev *indio_dev)
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG11,
|
||||
MESON_SAR_ADC_REG11_BANDGAP_EN, 0);
|
||||
|
||||
clk_disable_unprepare(priv->sana_clk);
|
||||
clk_disable_unprepare(priv->core_clk);
|
||||
|
||||
regulator_disable(priv->vref);
|
||||
@@ -961,16 +951,6 @@ static int meson_sar_adc_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(priv->core_clk);
|
||||
}
|
||||
|
||||
priv->sana_clk = devm_clk_get(&pdev->dev, "sana");
|
||||
if (IS_ERR(priv->sana_clk)) {
|
||||
if (PTR_ERR(priv->sana_clk) == -ENOENT) {
|
||||
priv->sana_clk = NULL;
|
||||
} else {
|
||||
dev_err(&pdev->dev, "failed to get sana clk\n");
|
||||
return PTR_ERR(priv->sana_clk);
|
||||
}
|
||||
}
|
||||
|
||||
priv->adc_clk = devm_clk_get(&pdev->dev, "adc_clk");
|
||||
if (IS_ERR(priv->adc_clk)) {
|
||||
if (PTR_ERR(priv->adc_clk) == -ENOENT) {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <linux/math64.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "qcom-vadc-common.h"
|
||||
|
||||
@@ -229,3 +230,6 @@ int qcom_vadc_decimation_from_dt(u32 value)
|
||||
return __ffs64(value / VADC_DECIMATION_MIN);
|
||||
}
|
||||
EXPORT_SYMBOL(qcom_vadc_decimation_from_dt);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("Qualcomm ADC common functionality");
|
||||
|
||||
@@ -92,6 +92,7 @@
|
||||
#define STM32H7_ADC_SQR3 0x38
|
||||
#define STM32H7_ADC_SQR4 0x3C
|
||||
#define STM32H7_ADC_DR 0x40
|
||||
#define STM32H7_ADC_DIFSEL 0xC0
|
||||
#define STM32H7_ADC_CALFACT 0xC4
|
||||
#define STM32H7_ADC_CALFACT2 0xC8
|
||||
|
||||
@@ -153,6 +154,8 @@ enum stm32h7_adc_dmngt {
|
||||
/* BOOST bit must be set on STM32H7 when ADC clock is above 20MHz */
|
||||
#define STM32H7_BOOST_CLKRATE 20000000UL
|
||||
|
||||
#define STM32_ADC_CH_MAX 20 /* max number of channels */
|
||||
#define STM32_ADC_CH_SZ 10 /* max channel name size */
|
||||
#define STM32_ADC_MAX_SQ 16 /* SQ1..SQ16 */
|
||||
#define STM32_ADC_MAX_SMP 7 /* SMPx range is [0..7] */
|
||||
#define STM32_ADC_TIMEOUT_US 100000
|
||||
@@ -297,9 +300,11 @@ struct stm32_adc_cfg {
|
||||
* @rx_buf: dma rx buffer cpu address
|
||||
* @rx_dma_buf: dma rx buffer bus address
|
||||
* @rx_buf_sz: dma rx buffer size
|
||||
* @difsel bitmask to set single-ended/differential channel
|
||||
* @pcsel bitmask to preselect channels on some devices
|
||||
* @smpr_val: sampling time settings (e.g. smpr1 / smpr2)
|
||||
* @cal: optional calibration data on some devices
|
||||
* @chan_name: channel name array
|
||||
*/
|
||||
struct stm32_adc {
|
||||
struct stm32_adc_common *common;
|
||||
@@ -318,72 +323,37 @@ struct stm32_adc {
|
||||
u8 *rx_buf;
|
||||
dma_addr_t rx_dma_buf;
|
||||
unsigned int rx_buf_sz;
|
||||
u32 difsel;
|
||||
u32 pcsel;
|
||||
u32 smpr_val[2];
|
||||
struct stm32_adc_calib cal;
|
||||
char chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct stm32_adc_chan_spec - specification of stm32 adc channel
|
||||
* @type: IIO channel type
|
||||
* @channel: channel number (single ended)
|
||||
* @name: channel name (single ended)
|
||||
*/
|
||||
struct stm32_adc_chan_spec {
|
||||
enum iio_chan_type type;
|
||||
int channel;
|
||||
const char *name;
|
||||
struct stm32_adc_diff_channel {
|
||||
u32 vinp;
|
||||
u32 vinn;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct stm32_adc_info - stm32 ADC, per instance config data
|
||||
* @channels: Reference to stm32 channels spec
|
||||
* @max_channels: Number of channels
|
||||
* @resolutions: available resolutions
|
||||
* @num_res: number of available resolutions
|
||||
*/
|
||||
struct stm32_adc_info {
|
||||
const struct stm32_adc_chan_spec *channels;
|
||||
int max_channels;
|
||||
const unsigned int *resolutions;
|
||||
const unsigned int num_res;
|
||||
};
|
||||
|
||||
/*
|
||||
* Input definitions common for all instances:
|
||||
* stm32f4 can have up to 16 channels
|
||||
* stm32h7 can have up to 20 channels
|
||||
*/
|
||||
static const struct stm32_adc_chan_spec stm32_adc_channels[] = {
|
||||
{ IIO_VOLTAGE, 0, "in0" },
|
||||
{ IIO_VOLTAGE, 1, "in1" },
|
||||
{ IIO_VOLTAGE, 2, "in2" },
|
||||
{ IIO_VOLTAGE, 3, "in3" },
|
||||
{ IIO_VOLTAGE, 4, "in4" },
|
||||
{ IIO_VOLTAGE, 5, "in5" },
|
||||
{ IIO_VOLTAGE, 6, "in6" },
|
||||
{ IIO_VOLTAGE, 7, "in7" },
|
||||
{ IIO_VOLTAGE, 8, "in8" },
|
||||
{ IIO_VOLTAGE, 9, "in9" },
|
||||
{ IIO_VOLTAGE, 10, "in10" },
|
||||
{ IIO_VOLTAGE, 11, "in11" },
|
||||
{ IIO_VOLTAGE, 12, "in12" },
|
||||
{ IIO_VOLTAGE, 13, "in13" },
|
||||
{ IIO_VOLTAGE, 14, "in14" },
|
||||
{ IIO_VOLTAGE, 15, "in15" },
|
||||
{ IIO_VOLTAGE, 16, "in16" },
|
||||
{ IIO_VOLTAGE, 17, "in17" },
|
||||
{ IIO_VOLTAGE, 18, "in18" },
|
||||
{ IIO_VOLTAGE, 19, "in19" },
|
||||
};
|
||||
|
||||
static const unsigned int stm32f4_adc_resolutions[] = {
|
||||
/* sorted values so the index matches RES[1:0] in STM32F4_ADC_CR1 */
|
||||
12, 10, 8, 6,
|
||||
};
|
||||
|
||||
/* stm32f4 can have up to 16 channels */
|
||||
static const struct stm32_adc_info stm32f4_adc_info = {
|
||||
.channels = stm32_adc_channels,
|
||||
.max_channels = 16,
|
||||
.resolutions = stm32f4_adc_resolutions,
|
||||
.num_res = ARRAY_SIZE(stm32f4_adc_resolutions),
|
||||
@@ -394,9 +364,9 @@ static const unsigned int stm32h7_adc_resolutions[] = {
|
||||
16, 14, 12, 10, 8,
|
||||
};
|
||||
|
||||
/* stm32h7 can have up to 20 channels */
|
||||
static const struct stm32_adc_info stm32h7_adc_info = {
|
||||
.channels = stm32_adc_channels,
|
||||
.max_channels = 20,
|
||||
.max_channels = STM32_ADC_CH_MAX,
|
||||
.resolutions = stm32h7_adc_resolutions,
|
||||
.num_res = ARRAY_SIZE(stm32h7_adc_resolutions),
|
||||
};
|
||||
@@ -983,15 +953,19 @@ pwr_dwn:
|
||||
* stm32h7_adc_prepare() - Leave power down mode to enable ADC.
|
||||
* @adc: stm32 adc instance
|
||||
* Leave power down mode.
|
||||
* Configure channels as single ended or differential before enabling ADC.
|
||||
* Enable ADC.
|
||||
* Restore calibration data.
|
||||
* Pre-select channels that may be used in PCSEL (required by input MUX / IO).
|
||||
* Pre-select channels that may be used in PCSEL (required by input MUX / IO):
|
||||
* - Only one input is selected for single ended (e.g. 'vinp')
|
||||
* - Two inputs are selected for differential channels (e.g. 'vinp' & 'vinn')
|
||||
*/
|
||||
static int stm32h7_adc_prepare(struct stm32_adc *adc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
stm32h7_adc_exit_pwr_down(adc);
|
||||
stm32_adc_writel(adc, STM32H7_ADC_DIFSEL, adc->difsel);
|
||||
|
||||
ret = stm32h7_adc_enable(adc);
|
||||
if (ret)
|
||||
@@ -1263,10 +1237,23 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,
|
||||
return ret;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = adc->common->vref_mv;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
if (chan->differential) {
|
||||
*val = adc->common->vref_mv * 2;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
} else {
|
||||
*val = adc->common->vref_mv;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
}
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
if (chan->differential)
|
||||
/* ADC_full_scale / 2 */
|
||||
*val = -((1 << chan->scan_type.realbits) / 2);
|
||||
else
|
||||
*val = 0;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -1628,29 +1615,40 @@ static void stm32_adc_smpr_init(struct stm32_adc *adc, int channel, u32 smp_ns)
|
||||
}
|
||||
|
||||
static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec *chan,
|
||||
const struct stm32_adc_chan_spec *channel,
|
||||
int scan_index, u32 smp)
|
||||
struct iio_chan_spec *chan, u32 vinp,
|
||||
u32 vinn, int scan_index, bool differential)
|
||||
{
|
||||
struct stm32_adc *adc = iio_priv(indio_dev);
|
||||
char *name = adc->chan_name[vinp];
|
||||
|
||||
chan->type = channel->type;
|
||||
chan->channel = channel->channel;
|
||||
chan->datasheet_name = channel->name;
|
||||
chan->type = IIO_VOLTAGE;
|
||||
chan->channel = vinp;
|
||||
if (differential) {
|
||||
chan->differential = 1;
|
||||
chan->channel2 = vinn;
|
||||
snprintf(name, STM32_ADC_CH_SZ, "in%d-in%d", vinp, vinn);
|
||||
} else {
|
||||
snprintf(name, STM32_ADC_CH_SZ, "in%d", vinp);
|
||||
}
|
||||
chan->datasheet_name = name;
|
||||
chan->scan_index = scan_index;
|
||||
chan->indexed = 1;
|
||||
chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
|
||||
chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
|
||||
chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET);
|
||||
chan->scan_type.sign = 'u';
|
||||
chan->scan_type.realbits = adc->cfg->adc_info->resolutions[adc->res];
|
||||
chan->scan_type.storagebits = 16;
|
||||
chan->ext_info = stm32_adc_ext_info;
|
||||
|
||||
/* Prepare sampling time settings */
|
||||
stm32_adc_smpr_init(adc, chan->channel, smp);
|
||||
|
||||
/* pre-build selected channels mask */
|
||||
adc->pcsel |= BIT(chan->channel);
|
||||
if (differential) {
|
||||
/* pre-build diff channels mask */
|
||||
adc->difsel |= BIT(chan->channel);
|
||||
/* Also add negative input to pre-selected channels */
|
||||
adc->pcsel |= BIT(chan->channel2);
|
||||
}
|
||||
}
|
||||
|
||||
static int stm32_adc_chan_of_init(struct iio_dev *indio_dev)
|
||||
@@ -1658,17 +1656,40 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev)
|
||||
struct device_node *node = indio_dev->dev.of_node;
|
||||
struct stm32_adc *adc = iio_priv(indio_dev);
|
||||
const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
|
||||
struct stm32_adc_diff_channel diff[STM32_ADC_CH_MAX];
|
||||
struct property *prop;
|
||||
const __be32 *cur;
|
||||
struct iio_chan_spec *channels;
|
||||
int scan_index = 0, num_channels, ret;
|
||||
int scan_index = 0, num_channels = 0, num_diff = 0, ret, i;
|
||||
u32 val, smp = 0;
|
||||
|
||||
num_channels = of_property_count_u32_elems(node, "st,adc-channels");
|
||||
if (num_channels < 0 ||
|
||||
num_channels > adc_info->max_channels) {
|
||||
ret = of_property_count_u32_elems(node, "st,adc-channels");
|
||||
if (ret > adc_info->max_channels) {
|
||||
dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
|
||||
return num_channels < 0 ? num_channels : -EINVAL;
|
||||
return -EINVAL;
|
||||
} else if (ret > 0) {
|
||||
num_channels += ret;
|
||||
}
|
||||
|
||||
ret = of_property_count_elems_of_size(node, "st,adc-diff-channels",
|
||||
sizeof(*diff));
|
||||
if (ret > adc_info->max_channels) {
|
||||
dev_err(&indio_dev->dev, "Bad st,adc-diff-channels?\n");
|
||||
return -EINVAL;
|
||||
} else if (ret > 0) {
|
||||
int size = ret * sizeof(*diff) / sizeof(u32);
|
||||
|
||||
num_diff = ret;
|
||||
num_channels += ret;
|
||||
ret = of_property_read_u32_array(node, "st,adc-diff-channels",
|
||||
(u32 *)diff, size);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!num_channels) {
|
||||
dev_err(&indio_dev->dev, "No channels configured\n");
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
/* Optional sample time is provided either for each, or all channels */
|
||||
@@ -1689,6 +1710,33 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Channel can't be configured both as single-ended & diff */
|
||||
for (i = 0; i < num_diff; i++) {
|
||||
if (val == diff[i].vinp) {
|
||||
dev_err(&indio_dev->dev,
|
||||
"channel %d miss-configured\n", val);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
stm32_adc_chan_init_one(indio_dev, &channels[scan_index], val,
|
||||
0, scan_index, false);
|
||||
scan_index++;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_diff; i++) {
|
||||
if (diff[i].vinp >= adc_info->max_channels ||
|
||||
diff[i].vinn >= adc_info->max_channels) {
|
||||
dev_err(&indio_dev->dev, "Invalid channel in%d-in%d\n",
|
||||
diff[i].vinp, diff[i].vinn);
|
||||
return -EINVAL;
|
||||
}
|
||||
stm32_adc_chan_init_one(indio_dev, &channels[scan_index],
|
||||
diff[i].vinp, diff[i].vinn, scan_index,
|
||||
true);
|
||||
scan_index++;
|
||||
}
|
||||
|
||||
for (i = 0; i < scan_index; i++) {
|
||||
/*
|
||||
* Using of_property_read_u32_index(), smp value will only be
|
||||
* modified if valid u32 value can be decoded. This allows to
|
||||
@@ -1696,12 +1744,9 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev)
|
||||
* value per channel.
|
||||
*/
|
||||
of_property_read_u32_index(node, "st,min-sample-time-nsecs",
|
||||
scan_index, &smp);
|
||||
|
||||
stm32_adc_chan_init_one(indio_dev, &channels[scan_index],
|
||||
&adc_info->channels[val],
|
||||
scan_index, smp);
|
||||
scan_index++;
|
||||
i, &smp);
|
||||
/* Prepare sampling time settings */
|
||||
stm32_adc_smpr_init(adc, channels[i].channel, smp);
|
||||
}
|
||||
|
||||
indio_dev->num_channels = scan_index;
|
||||
|
||||
@@ -191,7 +191,6 @@ static int cros_ec_sensors_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
|
||||
struct cros_ec_device *ec_device;
|
||||
struct iio_dev *indio_dev;
|
||||
struct cros_ec_sensors_state *state;
|
||||
struct iio_chan_spec *channel;
|
||||
@@ -201,7 +200,6 @@ static int cros_ec_sensors_probe(struct platform_device *pdev)
|
||||
dev_warn(&pdev->dev, "No CROS EC device found.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
ec_device = ec_dev->ec_dev;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*state));
|
||||
if (!indio_dev)
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
*
|
||||
* Copyright (C) 2017 Matt Ranostay <matt@ranostay.consulting>
|
||||
*
|
||||
* Support for MAX30105 optical particle sensor
|
||||
* Copyright (C) 2017 Peter Meerwald-Stadler <pmeerw@pmeerw.net>
|
||||
*
|
||||
* 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
|
||||
@@ -13,6 +16,7 @@
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* 7-bit I2C chip address: 0x57
|
||||
* TODO: proximity power saving feature
|
||||
*/
|
||||
|
||||
@@ -32,6 +36,18 @@
|
||||
|
||||
#define MAX30102_REGMAP_NAME "max30102_regmap"
|
||||
#define MAX30102_DRV_NAME "max30102"
|
||||
#define MAX30102_PART_NUMBER 0x15
|
||||
|
||||
enum max30102_chip_id {
|
||||
max30102,
|
||||
max30105,
|
||||
};
|
||||
|
||||
enum max3012_led_idx {
|
||||
MAX30102_LED_RED,
|
||||
MAX30102_LED_IR,
|
||||
MAX30105_LED_GREEN,
|
||||
};
|
||||
|
||||
#define MAX30102_REG_INT_STATUS 0x00
|
||||
#define MAX30102_REG_INT_STATUS_PWR_RDY BIT(0)
|
||||
@@ -52,7 +68,7 @@
|
||||
#define MAX30102_REG_FIFO_OVR_CTR 0x05
|
||||
#define MAX30102_REG_FIFO_RD_PTR 0x06
|
||||
#define MAX30102_REG_FIFO_DATA 0x07
|
||||
#define MAX30102_REG_FIFO_DATA_ENTRY_LEN 6
|
||||
#define MAX30102_REG_FIFO_DATA_BYTES 3
|
||||
|
||||
#define MAX30102_REG_FIFO_CONFIG 0x08
|
||||
#define MAX30102_REG_FIFO_CONFIG_AVG_4SAMPLES BIT(1)
|
||||
@@ -60,11 +76,18 @@
|
||||
#define MAX30102_REG_FIFO_CONFIG_AFULL BIT(0)
|
||||
|
||||
#define MAX30102_REG_MODE_CONFIG 0x09
|
||||
#define MAX30102_REG_MODE_CONFIG_MODE_SPO2_EN BIT(0)
|
||||
#define MAX30102_REG_MODE_CONFIG_MODE_HR_EN BIT(1)
|
||||
#define MAX30102_REG_MODE_CONFIG_MODE_MASK 0x03
|
||||
#define MAX30102_REG_MODE_CONFIG_MODE_NONE 0x00
|
||||
#define MAX30102_REG_MODE_CONFIG_MODE_HR 0x02 /* red LED */
|
||||
#define MAX30102_REG_MODE_CONFIG_MODE_HR_SPO2 0x03 /* red + IR LED */
|
||||
#define MAX30102_REG_MODE_CONFIG_MODE_MULTI 0x07 /* multi-LED mode */
|
||||
#define MAX30102_REG_MODE_CONFIG_MODE_MASK GENMASK(2, 0)
|
||||
#define MAX30102_REG_MODE_CONFIG_PWR BIT(7)
|
||||
|
||||
#define MAX30102_REG_MODE_CONTROL_SLOT21 0x11 /* multi-LED control */
|
||||
#define MAX30102_REG_MODE_CONTROL_SLOT43 0x12
|
||||
#define MAX30102_REG_MODE_CONTROL_SLOT_MASK (GENMASK(6, 4) | GENMASK(2, 0))
|
||||
#define MAX30102_REG_MODE_CONTROL_SLOT_SHIFT 4
|
||||
|
||||
#define MAX30102_REG_SPO2_CONFIG 0x0a
|
||||
#define MAX30102_REG_SPO2_CONFIG_PULSE_411_US 0x03
|
||||
#define MAX30102_REG_SPO2_CONFIG_SR_400HZ 0x03
|
||||
@@ -75,6 +98,7 @@
|
||||
|
||||
#define MAX30102_REG_RED_LED_CONFIG 0x0c
|
||||
#define MAX30102_REG_IR_LED_CONFIG 0x0d
|
||||
#define MAX30105_REG_GREEN_LED_CONFIG 0x0e
|
||||
|
||||
#define MAX30102_REG_TEMP_CONFIG 0x21
|
||||
#define MAX30102_REG_TEMP_CONFIG_TEMP_EN BIT(0)
|
||||
@@ -82,14 +106,18 @@
|
||||
#define MAX30102_REG_TEMP_INTEGER 0x1f
|
||||
#define MAX30102_REG_TEMP_FRACTION 0x20
|
||||
|
||||
#define MAX30102_REG_REV_ID 0xfe
|
||||
#define MAX30102_REG_PART_ID 0xff
|
||||
|
||||
struct max30102_data {
|
||||
struct i2c_client *client;
|
||||
struct iio_dev *indio_dev;
|
||||
struct mutex lock;
|
||||
struct regmap *regmap;
|
||||
enum max30102_chip_id chip_id;
|
||||
|
||||
u8 buffer[8];
|
||||
__be32 processed_buffer[2]; /* 2 x 18-bit (padded to 32-bits) */
|
||||
u8 buffer[12];
|
||||
__be32 processed_buffer[3]; /* 3 x 18-bit (padded to 32-bits) */
|
||||
};
|
||||
|
||||
static const struct regmap_config max30102_regmap_config = {
|
||||
@@ -99,37 +127,35 @@ static const struct regmap_config max30102_regmap_config = {
|
||||
.val_bits = 8,
|
||||
};
|
||||
|
||||
static const unsigned long max30102_scan_masks[] = {0x3, 0};
|
||||
static const unsigned long max30102_scan_masks[] = {
|
||||
BIT(MAX30102_LED_RED) | BIT(MAX30102_LED_IR),
|
||||
0
|
||||
};
|
||||
|
||||
static const unsigned long max30105_scan_masks[] = {
|
||||
BIT(MAX30102_LED_RED) | BIT(MAX30102_LED_IR),
|
||||
BIT(MAX30102_LED_RED) | BIT(MAX30102_LED_IR) |
|
||||
BIT(MAX30105_LED_GREEN),
|
||||
0
|
||||
};
|
||||
|
||||
#define MAX30102_INTENSITY_CHANNEL(_si, _mod) { \
|
||||
.type = IIO_INTENSITY, \
|
||||
.channel2 = _mod, \
|
||||
.modified = 1, \
|
||||
.scan_index = _si, \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.shift = 8, \
|
||||
.realbits = 18, \
|
||||
.storagebits = 32, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec max30102_channels[] = {
|
||||
{
|
||||
.type = IIO_INTENSITY,
|
||||
.channel2 = IIO_MOD_LIGHT_RED,
|
||||
.modified = 1,
|
||||
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.shift = 8,
|
||||
.realbits = 18,
|
||||
.storagebits = 32,
|
||||
.endianness = IIO_BE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.type = IIO_INTENSITY,
|
||||
.channel2 = IIO_MOD_LIGHT_IR,
|
||||
.modified = 1,
|
||||
|
||||
.scan_index = 1,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.shift = 8,
|
||||
.realbits = 18,
|
||||
.storagebits = 32,
|
||||
.endianness = IIO_BE,
|
||||
},
|
||||
},
|
||||
MAX30102_INTENSITY_CHANNEL(MAX30102_LED_RED, IIO_MOD_LIGHT_RED),
|
||||
MAX30102_INTENSITY_CHANNEL(MAX30102_LED_IR, IIO_MOD_LIGHT_IR),
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate =
|
||||
@@ -138,25 +164,81 @@ static const struct iio_chan_spec max30102_channels[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static int max30102_set_powermode(struct max30102_data *data, bool state)
|
||||
static const struct iio_chan_spec max30105_channels[] = {
|
||||
MAX30102_INTENSITY_CHANNEL(MAX30102_LED_RED, IIO_MOD_LIGHT_RED),
|
||||
MAX30102_INTENSITY_CHANNEL(MAX30102_LED_IR, IIO_MOD_LIGHT_IR),
|
||||
MAX30102_INTENSITY_CHANNEL(MAX30105_LED_GREEN, IIO_MOD_LIGHT_GREEN),
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
.scan_index = -1,
|
||||
},
|
||||
};
|
||||
|
||||
static int max30102_set_power(struct max30102_data *data, bool en)
|
||||
{
|
||||
return regmap_update_bits(data->regmap, MAX30102_REG_MODE_CONFIG,
|
||||
MAX30102_REG_MODE_CONFIG_PWR,
|
||||
state ? 0 : MAX30102_REG_MODE_CONFIG_PWR);
|
||||
en ? 0 : MAX30102_REG_MODE_CONFIG_PWR);
|
||||
}
|
||||
|
||||
static int max30102_set_powermode(struct max30102_data *data, u8 mode, bool en)
|
||||
{
|
||||
u8 reg = mode;
|
||||
|
||||
if (!en)
|
||||
reg |= MAX30102_REG_MODE_CONFIG_PWR;
|
||||
|
||||
return regmap_update_bits(data->regmap, MAX30102_REG_MODE_CONFIG,
|
||||
MAX30102_REG_MODE_CONFIG_PWR |
|
||||
MAX30102_REG_MODE_CONFIG_MODE_MASK, reg);
|
||||
}
|
||||
|
||||
#define MAX30102_MODE_CONTROL_LED_SLOTS(slot2, slot1) \
|
||||
((slot2 << MAX30102_REG_MODE_CONTROL_SLOT_SHIFT) | slot1)
|
||||
|
||||
static int max30102_buffer_postenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct max30102_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
u8 reg;
|
||||
|
||||
return max30102_set_powermode(data, true);
|
||||
switch (*indio_dev->active_scan_mask) {
|
||||
case BIT(MAX30102_LED_RED) | BIT(MAX30102_LED_IR):
|
||||
reg = MAX30102_REG_MODE_CONFIG_MODE_HR_SPO2;
|
||||
break;
|
||||
case BIT(MAX30102_LED_RED) | BIT(MAX30102_LED_IR) |
|
||||
BIT(MAX30105_LED_GREEN):
|
||||
ret = regmap_update_bits(data->regmap,
|
||||
MAX30102_REG_MODE_CONTROL_SLOT21,
|
||||
MAX30102_REG_MODE_CONTROL_SLOT_MASK,
|
||||
MAX30102_MODE_CONTROL_LED_SLOTS(2, 1));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_update_bits(data->regmap,
|
||||
MAX30102_REG_MODE_CONTROL_SLOT43,
|
||||
MAX30102_REG_MODE_CONTROL_SLOT_MASK,
|
||||
MAX30102_MODE_CONTROL_LED_SLOTS(0, 3));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reg = MAX30102_REG_MODE_CONFIG_MODE_MULTI;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return max30102_set_powermode(data, reg, true);
|
||||
}
|
||||
|
||||
static int max30102_buffer_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct max30102_data *data = iio_priv(indio_dev);
|
||||
|
||||
return max30102_set_powermode(data, false);
|
||||
return max30102_set_powermode(data, MAX30102_REG_MODE_CONFIG_MODE_NONE,
|
||||
false);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops max30102_buffer_setup_ops = {
|
||||
@@ -180,32 +262,51 @@ static inline int max30102_fifo_count(struct max30102_data *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max30102_read_measurement(struct max30102_data *data)
|
||||
#define MAX30102_COPY_DATA(i) \
|
||||
memcpy(&data->processed_buffer[(i)], \
|
||||
&buffer[(i) * MAX30102_REG_FIFO_DATA_BYTES], \
|
||||
MAX30102_REG_FIFO_DATA_BYTES)
|
||||
|
||||
static int max30102_read_measurement(struct max30102_data *data,
|
||||
unsigned int measurements)
|
||||
{
|
||||
int ret;
|
||||
u8 *buffer = (u8 *) &data->buffer;
|
||||
|
||||
ret = i2c_smbus_read_i2c_block_data(data->client,
|
||||
MAX30102_REG_FIFO_DATA,
|
||||
MAX30102_REG_FIFO_DATA_ENTRY_LEN,
|
||||
measurements *
|
||||
MAX30102_REG_FIFO_DATA_BYTES,
|
||||
buffer);
|
||||
|
||||
memcpy(&data->processed_buffer[0], &buffer[0], 3);
|
||||
memcpy(&data->processed_buffer[1], &buffer[3], 3);
|
||||
switch (measurements) {
|
||||
case 3:
|
||||
MAX30102_COPY_DATA(2);
|
||||
case 2: /* fall-through */
|
||||
MAX30102_COPY_DATA(1);
|
||||
case 1: /* fall-through */
|
||||
MAX30102_COPY_DATA(0);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return (ret == MAX30102_REG_FIFO_DATA_ENTRY_LEN) ? 0 : -EINVAL;
|
||||
return (ret == measurements * MAX30102_REG_FIFO_DATA_BYTES) ?
|
||||
0 : -EINVAL;
|
||||
}
|
||||
|
||||
static irqreturn_t max30102_interrupt_handler(int irq, void *private)
|
||||
{
|
||||
struct iio_dev *indio_dev = private;
|
||||
struct max30102_data *data = iio_priv(indio_dev);
|
||||
unsigned int measurements = bitmap_weight(indio_dev->active_scan_mask,
|
||||
indio_dev->masklength);
|
||||
int ret, cnt = 0;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
|
||||
while (cnt || (cnt = max30102_fifo_count(data)) > 0) {
|
||||
ret = max30102_read_measurement(data);
|
||||
ret = max30102_read_measurement(data, measurements);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
@@ -251,6 +352,29 @@ static int max30102_led_init(struct max30102_data *data)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (data->chip_id == max30105) {
|
||||
ret = of_property_read_u32(np,
|
||||
"maxim,green-led-current-microamp", &val);
|
||||
if (ret) {
|
||||
dev_info(dev, "no green-led-current-microamp set\n");
|
||||
|
||||
/* Default to 7 mA green LED */
|
||||
val = 7000;
|
||||
}
|
||||
|
||||
ret = max30102_get_current_idx(val, ®);
|
||||
if (ret) {
|
||||
dev_err(dev, "invalid green LED current setting %d\n",
|
||||
val);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_write(data->regmap, MAX30105_REG_GREEN_LED_CONFIG,
|
||||
reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "maxim,ir-led-current-microamp", &val);
|
||||
if (ret) {
|
||||
dev_info(dev, "no ir-led-current-microamp set\n");
|
||||
@@ -261,7 +385,7 @@ static int max30102_led_init(struct max30102_data *data)
|
||||
|
||||
ret = max30102_get_current_idx(val, ®);
|
||||
if (ret) {
|
||||
dev_err(dev, "invalid IR LED current setting %d", val);
|
||||
dev_err(dev, "invalid IR LED current setting %d\n", val);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -277,7 +401,7 @@ static int max30102_chip_init(struct max30102_data *data)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* enable 18-bit HR + SPO2 readings at 400Hz */
|
||||
/* configure 18-bit HR + SpO2 readings at 400Hz */
|
||||
ret = regmap_write(data->regmap, MAX30102_REG_SPO2_CONFIG,
|
||||
(MAX30102_REG_SPO2_CONFIG_ADC_4096_STEPS
|
||||
<< MAX30102_REG_SPO2_CONFIG_ADC_MASK_SHIFT) |
|
||||
@@ -287,14 +411,6 @@ static int max30102_chip_init(struct max30102_data *data)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* enable SPO2 mode */
|
||||
ret = regmap_update_bits(data->regmap, MAX30102_REG_MODE_CONFIG,
|
||||
MAX30102_REG_MODE_CONFIG_MODE_MASK,
|
||||
MAX30102_REG_MODE_CONFIG_MODE_HR_EN |
|
||||
MAX30102_REG_MODE_CONFIG_MODE_SPO2_EN);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* average 4 samples + generate FIFO interrupt */
|
||||
ret = regmap_write(data->regmap, MAX30102_REG_FIFO_CONFIG,
|
||||
(MAX30102_REG_FIFO_CONFIG_AVG_4SAMPLES
|
||||
@@ -329,20 +445,31 @@ static int max30102_read_temp(struct max30102_data *data, int *val)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max30102_get_temp(struct max30102_data *data, int *val)
|
||||
static int max30102_get_temp(struct max30102_data *data, int *val, bool en)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (en) {
|
||||
ret = max30102_set_power(data, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* start acquisition */
|
||||
ret = regmap_update_bits(data->regmap, MAX30102_REG_TEMP_CONFIG,
|
||||
MAX30102_REG_TEMP_CONFIG_TEMP_EN,
|
||||
MAX30102_REG_TEMP_CONFIG_TEMP_EN);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto out;
|
||||
|
||||
msleep(35);
|
||||
ret = max30102_read_temp(data, val);
|
||||
|
||||
return max30102_read_temp(data, val);
|
||||
out:
|
||||
if (en)
|
||||
max30102_set_power(data, false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max30102_read_raw(struct iio_dev *indio_dev,
|
||||
@@ -355,20 +482,19 @@ static int max30102_read_raw(struct iio_dev *indio_dev,
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
/*
|
||||
* Temperature reading can only be acquired while engine
|
||||
* is running
|
||||
* Temperature reading can only be acquired when not in
|
||||
* shutdown; leave shutdown briefly when buffer not running
|
||||
*/
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
if (!iio_buffer_enabled(indio_dev))
|
||||
ret = -EBUSY;
|
||||
else {
|
||||
ret = max30102_get_temp(data, val);
|
||||
if (!ret)
|
||||
ret = IIO_VAL_INT;
|
||||
}
|
||||
|
||||
ret = max30102_get_temp(data, val, true);
|
||||
else
|
||||
ret = max30102_get_temp(data, val, false);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 1; /* 0.0625 */
|
||||
@@ -391,6 +517,7 @@ static int max30102_probe(struct i2c_client *client,
|
||||
struct iio_buffer *buffer;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
unsigned int reg;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
@@ -403,10 +530,7 @@ static int max30102_probe(struct i2c_client *client,
|
||||
iio_device_attach_buffer(indio_dev, buffer);
|
||||
|
||||
indio_dev->name = MAX30102_DRV_NAME;
|
||||
indio_dev->channels = max30102_channels;
|
||||
indio_dev->info = &max30102_info;
|
||||
indio_dev->num_channels = ARRAY_SIZE(max30102_channels);
|
||||
indio_dev->available_scan_masks = max30102_scan_masks;
|
||||
indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE);
|
||||
indio_dev->setup_ops = &max30102_buffer_setup_ops;
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
@@ -414,16 +538,50 @@ static int max30102_probe(struct i2c_client *client,
|
||||
data = iio_priv(indio_dev);
|
||||
data->indio_dev = indio_dev;
|
||||
data->client = client;
|
||||
data->chip_id = id->driver_data;
|
||||
|
||||
mutex_init(&data->lock);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
switch (data->chip_id) {
|
||||
case max30105:
|
||||
indio_dev->channels = max30105_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(max30105_channels);
|
||||
indio_dev->available_scan_masks = max30105_scan_masks;
|
||||
break;
|
||||
case max30102:
|
||||
indio_dev->channels = max30102_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(max30102_channels);
|
||||
indio_dev->available_scan_masks = max30102_scan_masks;
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
data->regmap = devm_regmap_init_i2c(client, &max30102_regmap_config);
|
||||
if (IS_ERR(data->regmap)) {
|
||||
dev_err(&client->dev, "regmap initialization failed.\n");
|
||||
dev_err(&client->dev, "regmap initialization failed\n");
|
||||
return PTR_ERR(data->regmap);
|
||||
}
|
||||
max30102_set_powermode(data, false);
|
||||
|
||||
/* check part ID */
|
||||
ret = regmap_read(data->regmap, MAX30102_REG_PART_ID, ®);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (reg != MAX30102_PART_NUMBER)
|
||||
return -ENODEV;
|
||||
|
||||
/* show revision ID */
|
||||
ret = regmap_read(data->regmap, MAX30102_REG_REV_ID, ®);
|
||||
if (ret)
|
||||
return ret;
|
||||
dev_dbg(&client->dev, "max3010x revision %02x\n", reg);
|
||||
|
||||
/* clear mode setting, chip shutdown */
|
||||
ret = max30102_set_powermode(data, MAX30102_REG_MODE_CONFIG_MODE_NONE,
|
||||
false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = max30102_chip_init(data);
|
||||
if (ret)
|
||||
@@ -452,19 +610,21 @@ static int max30102_remove(struct i2c_client *client)
|
||||
struct max30102_data *data = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
max30102_set_powermode(data, false);
|
||||
max30102_set_power(data, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id max30102_id[] = {
|
||||
{ "max30102", 0 },
|
||||
{ "max30102", max30102 },
|
||||
{ "max30105", max30105 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, max30102_id);
|
||||
|
||||
static const struct of_device_id max30102_dt_ids[] = {
|
||||
{ .compatible = "maxim,max30102" },
|
||||
{ .compatible = "maxim,max30105" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, max30102_dt_ids);
|
||||
@@ -481,5 +641,5 @@ static struct i2c_driver max30102_driver = {
|
||||
module_i2c_driver(max30102_driver);
|
||||
|
||||
MODULE_AUTHOR("Matt Ranostay <matt@ranostay.consulting>");
|
||||
MODULE_DESCRIPTION("MAX30102 heart rate and pulse oximeter sensor");
|
||||
MODULE_DESCRIPTION("MAX30102 heart rate/pulse oximeter and MAX30105 particle sensor driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -196,8 +196,7 @@ void inv_mpu_acpi_delete_mux_client(struct i2c_client *client)
|
||||
{
|
||||
struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(&client->dev));
|
||||
|
||||
if (st->mux_client)
|
||||
i2c_unregister_device(st->mux_client);
|
||||
i2c_unregister_device(st->mux_client);
|
||||
}
|
||||
#else
|
||||
|
||||
|
||||
@@ -588,6 +588,7 @@ static ssize_t __iio_format_value(char *buf, size_t len, unsigned int type,
|
||||
return snprintf(buf, len, "%d", vals[0]);
|
||||
case IIO_VAL_INT_PLUS_MICRO_DB:
|
||||
scale_db = true;
|
||||
/* fall through */
|
||||
case IIO_VAL_INT_PLUS_MICRO:
|
||||
if (vals[1] < 0)
|
||||
return snprintf(buf, len, "-%d.%06u%s", abs(vals[0]),
|
||||
|
||||
@@ -334,6 +334,30 @@ config STK3310
|
||||
Choosing M will build the driver as a module. If so, the module
|
||||
will be called stk3310.
|
||||
|
||||
config ST_UVIS25
|
||||
tristate "STMicroelectronics UVIS25 sensor driver"
|
||||
depends on (I2C || SPI)
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select ST_UVIS25_I2C if (I2C)
|
||||
select ST_UVIS25_SPI if (SPI_MASTER)
|
||||
help
|
||||
Say yes here to build support for STMicroelectronics UVIS25
|
||||
uv sensor
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called st_uvis25.
|
||||
|
||||
config ST_UVIS25_I2C
|
||||
tristate
|
||||
depends on ST_UVIS25
|
||||
select REGMAP_I2C
|
||||
|
||||
config ST_UVIS25_SPI
|
||||
tristate
|
||||
depends on ST_UVIS25
|
||||
select REGMAP_SPI
|
||||
|
||||
config TCS3414
|
||||
tristate "TAOS TCS3414 digital color sensor"
|
||||
depends on I2C
|
||||
@@ -425,4 +449,14 @@ config VL6180
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called vl6180.
|
||||
|
||||
config ZOPT2201
|
||||
tristate "ZOPT2201 ALS and UV B sensor"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here if you want to build a driver for the IDT
|
||||
ZOPT2201 ambient light and UV B sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called zopt2201.
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -33,6 +33,9 @@ obj-$(CONFIG_RPR0521) += rpr0521.o
|
||||
obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o
|
||||
obj-$(CONFIG_SI1145) += si1145.o
|
||||
obj-$(CONFIG_STK3310) += stk3310.o
|
||||
obj-$(CONFIG_ST_UVIS25) += st_uvis25_core.o
|
||||
obj-$(CONFIG_ST_UVIS25_I2C) += st_uvis25_i2c.o
|
||||
obj-$(CONFIG_ST_UVIS25_SPI) += st_uvis25_spi.o
|
||||
obj-$(CONFIG_TCS3414) += tcs3414.o
|
||||
obj-$(CONFIG_TCS3472) += tcs3472.o
|
||||
obj-$(CONFIG_TSL2583) += tsl2583.o
|
||||
@@ -41,3 +44,4 @@ obj-$(CONFIG_US5182D) += us5182d.o
|
||||
obj-$(CONFIG_VCNL4000) += vcnl4000.o
|
||||
obj-$(CONFIG_VEML6070) += veml6070.o
|
||||
obj-$(CONFIG_VL6180) += vl6180.o
|
||||
obj-$(CONFIG_ZOPT2201) += zopt2201.o
|
||||
|
||||
@@ -181,7 +181,6 @@ static int cros_ec_light_prox_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
|
||||
struct cros_ec_device *ec_device;
|
||||
struct iio_dev *indio_dev;
|
||||
struct cros_ec_light_prox_state *state;
|
||||
struct iio_chan_spec *channel;
|
||||
@@ -191,7 +190,6 @@ static int cros_ec_light_prox_probe(struct platform_device *pdev)
|
||||
dev_warn(dev, "No CROS EC device found.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
ec_device = ec_dev->ec_dev;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*state));
|
||||
if (!indio_dev)
|
||||
|
||||
37
drivers/iio/light/st_uvis25.h
Normal file
37
drivers/iio/light/st_uvis25.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* STMicroelectronics uvis25 sensor driver
|
||||
*
|
||||
* Copyright 2017 STMicroelectronics Inc.
|
||||
*
|
||||
* Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#ifndef ST_UVIS25_H
|
||||
#define ST_UVIS25_H
|
||||
|
||||
#define ST_UVIS25_DEV_NAME "uvis25"
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
/**
|
||||
* struct st_uvis25_hw - ST UVIS25 sensor instance
|
||||
* @regmap: Register map of the device.
|
||||
* @trig: The trigger in use by the driver.
|
||||
* @enabled: Status of the sensor (false->off, true->on).
|
||||
* @irq: Device interrupt line (I2C or SPI).
|
||||
*/
|
||||
struct st_uvis25_hw {
|
||||
struct regmap *regmap;
|
||||
|
||||
struct iio_trigger *trig;
|
||||
bool enabled;
|
||||
int irq;
|
||||
};
|
||||
|
||||
extern const struct dev_pm_ops st_uvis25_pm_ops;
|
||||
|
||||
int st_uvis25_probe(struct device *dev, int irq, struct regmap *regmap);
|
||||
|
||||
#endif /* ST_UVIS25_H */
|
||||
359
drivers/iio/light/st_uvis25_core.c
Normal file
359
drivers/iio/light/st_uvis25_core.c
Normal file
@@ -0,0 +1,359 @@
|
||||
/*
|
||||
* STMicroelectronics uvis25 sensor driver
|
||||
*
|
||||
* Copyright 2017 STMicroelectronics Inc.
|
||||
*
|
||||
* Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqreturn.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "st_uvis25.h"
|
||||
|
||||
#define ST_UVIS25_REG_WHOAMI_ADDR 0x0f
|
||||
#define ST_UVIS25_REG_WHOAMI_VAL 0xca
|
||||
#define ST_UVIS25_REG_CTRL1_ADDR 0x20
|
||||
#define ST_UVIS25_REG_ODR_MASK BIT(0)
|
||||
#define ST_UVIS25_REG_BDU_MASK BIT(1)
|
||||
#define ST_UVIS25_REG_CTRL2_ADDR 0x21
|
||||
#define ST_UVIS25_REG_BOOT_MASK BIT(7)
|
||||
#define ST_UVIS25_REG_CTRL3_ADDR 0x22
|
||||
#define ST_UVIS25_REG_HL_MASK BIT(7)
|
||||
#define ST_UVIS25_REG_STATUS_ADDR 0x27
|
||||
#define ST_UVIS25_REG_UV_DA_MASK BIT(0)
|
||||
#define ST_UVIS25_REG_OUT_ADDR 0x28
|
||||
|
||||
static const struct iio_chan_spec st_uvis25_channels[] = {
|
||||
{
|
||||
.type = IIO_UVINDEX,
|
||||
.address = ST_UVIS25_REG_OUT_ADDR,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 8,
|
||||
.storagebits = 8,
|
||||
},
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
static int st_uvis25_check_whoami(struct st_uvis25_hw *hw)
|
||||
{
|
||||
int err, data;
|
||||
|
||||
err = regmap_read(hw->regmap, ST_UVIS25_REG_WHOAMI_ADDR, &data);
|
||||
if (err < 0) {
|
||||
dev_err(regmap_get_device(hw->regmap),
|
||||
"failed to read whoami register\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
if (data != ST_UVIS25_REG_WHOAMI_VAL) {
|
||||
dev_err(regmap_get_device(hw->regmap),
|
||||
"wrong whoami {%02x vs %02x}\n",
|
||||
data, ST_UVIS25_REG_WHOAMI_VAL);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_uvis25_set_enable(struct st_uvis25_hw *hw, bool enable)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = regmap_update_bits(hw->regmap, ST_UVIS25_REG_CTRL1_ADDR,
|
||||
ST_UVIS25_REG_ODR_MASK, enable);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
hw->enabled = enable;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_uvis25_read_oneshot(struct st_uvis25_hw *hw, u8 addr, int *val)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = st_uvis25_set_enable(hw, true);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
msleep(1500);
|
||||
|
||||
/*
|
||||
* in order to avoid possible race conditions with interrupt
|
||||
* generation, disable the sensor first and then poll output
|
||||
* register. That sequence guarantees the interrupt will be reset
|
||||
* when irq line is unmasked
|
||||
*/
|
||||
err = st_uvis25_set_enable(hw, false);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = regmap_read(hw->regmap, addr, val);
|
||||
|
||||
return err < 0 ? err : IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int st_uvis25_read_raw(struct iio_dev *iio_dev,
|
||||
struct iio_chan_spec const *ch,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = iio_device_claim_direct_mode(iio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_PROCESSED: {
|
||||
struct st_uvis25_hw *hw = iio_priv(iio_dev);
|
||||
|
||||
/*
|
||||
* mask irq line during oneshot read since the sensor
|
||||
* does not export the capability to disable data-ready line
|
||||
* in the register map and it is enabled by default.
|
||||
* If the line is unmasked during read_raw() it will be set
|
||||
* active and never reset since the trigger is disabled
|
||||
*/
|
||||
if (hw->irq > 0)
|
||||
disable_irq(hw->irq);
|
||||
ret = st_uvis25_read_oneshot(hw, ch->address, val);
|
||||
if (hw->irq > 0)
|
||||
enable_irq(hw->irq);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
iio_device_release_direct_mode(iio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t st_uvis25_trigger_handler_thread(int irq, void *private)
|
||||
{
|
||||
struct st_uvis25_hw *hw = private;
|
||||
int err, status;
|
||||
|
||||
err = regmap_read(hw->regmap, ST_UVIS25_REG_STATUS_ADDR, &status);
|
||||
if (err < 0)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
if (!(status & ST_UVIS25_REG_UV_DA_MASK))
|
||||
return IRQ_NONE;
|
||||
|
||||
iio_trigger_poll_chained(hw->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int st_uvis25_allocate_trigger(struct iio_dev *iio_dev)
|
||||
{
|
||||
struct st_uvis25_hw *hw = iio_priv(iio_dev);
|
||||
struct device *dev = regmap_get_device(hw->regmap);
|
||||
bool irq_active_low = false;
|
||||
unsigned long irq_type;
|
||||
int err;
|
||||
|
||||
irq_type = irqd_get_trigger_type(irq_get_irq_data(hw->irq));
|
||||
|
||||
switch (irq_type) {
|
||||
case IRQF_TRIGGER_HIGH:
|
||||
case IRQF_TRIGGER_RISING:
|
||||
break;
|
||||
case IRQF_TRIGGER_LOW:
|
||||
case IRQF_TRIGGER_FALLING:
|
||||
irq_active_low = true;
|
||||
break;
|
||||
default:
|
||||
dev_info(dev, "mode %lx unsupported\n", irq_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = regmap_update_bits(hw->regmap, ST_UVIS25_REG_CTRL3_ADDR,
|
||||
ST_UVIS25_REG_HL_MASK, irq_active_low);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = devm_request_threaded_irq(dev, hw->irq, NULL,
|
||||
st_uvis25_trigger_handler_thread,
|
||||
irq_type | IRQF_ONESHOT,
|
||||
iio_dev->name, hw);
|
||||
if (err) {
|
||||
dev_err(dev, "failed to request trigger irq %d\n",
|
||||
hw->irq);
|
||||
return err;
|
||||
}
|
||||
|
||||
hw->trig = devm_iio_trigger_alloc(dev, "%s-trigger",
|
||||
iio_dev->name);
|
||||
if (!hw->trig)
|
||||
return -ENOMEM;
|
||||
|
||||
iio_trigger_set_drvdata(hw->trig, iio_dev);
|
||||
hw->trig->dev.parent = dev;
|
||||
|
||||
return devm_iio_trigger_register(dev, hw->trig);
|
||||
}
|
||||
|
||||
static int st_uvis25_buffer_preenable(struct iio_dev *iio_dev)
|
||||
{
|
||||
return st_uvis25_set_enable(iio_priv(iio_dev), true);
|
||||
}
|
||||
|
||||
static int st_uvis25_buffer_postdisable(struct iio_dev *iio_dev)
|
||||
{
|
||||
return st_uvis25_set_enable(iio_priv(iio_dev), false);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops st_uvis25_buffer_ops = {
|
||||
.preenable = st_uvis25_buffer_preenable,
|
||||
.postenable = iio_triggered_buffer_postenable,
|
||||
.predisable = iio_triggered_buffer_predisable,
|
||||
.postdisable = st_uvis25_buffer_postdisable,
|
||||
};
|
||||
|
||||
static irqreturn_t st_uvis25_buffer_handler_thread(int irq, void *p)
|
||||
{
|
||||
u8 buffer[ALIGN(sizeof(u8), sizeof(s64)) + sizeof(s64)];
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *iio_dev = pf->indio_dev;
|
||||
struct st_uvis25_hw *hw = iio_priv(iio_dev);
|
||||
int err;
|
||||
|
||||
err = regmap_read(hw->regmap, ST_UVIS25_REG_OUT_ADDR, (int *)buffer);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(iio_dev, buffer,
|
||||
iio_get_time_ns(iio_dev));
|
||||
|
||||
out:
|
||||
iio_trigger_notify_done(hw->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int st_uvis25_allocate_buffer(struct iio_dev *iio_dev)
|
||||
{
|
||||
struct st_uvis25_hw *hw = iio_priv(iio_dev);
|
||||
|
||||
return devm_iio_triggered_buffer_setup(regmap_get_device(hw->regmap),
|
||||
iio_dev, NULL,
|
||||
st_uvis25_buffer_handler_thread,
|
||||
&st_uvis25_buffer_ops);
|
||||
}
|
||||
|
||||
static const struct iio_info st_uvis25_info = {
|
||||
.read_raw = st_uvis25_read_raw,
|
||||
};
|
||||
|
||||
static int st_uvis25_init_sensor(struct st_uvis25_hw *hw)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = regmap_update_bits(hw->regmap, ST_UVIS25_REG_CTRL2_ADDR,
|
||||
ST_UVIS25_REG_BOOT_MASK, 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
msleep(2000);
|
||||
|
||||
return regmap_update_bits(hw->regmap, ST_UVIS25_REG_CTRL1_ADDR,
|
||||
ST_UVIS25_REG_BDU_MASK, 1);
|
||||
}
|
||||
|
||||
int st_uvis25_probe(struct device *dev, int irq, struct regmap *regmap)
|
||||
{
|
||||
struct st_uvis25_hw *hw;
|
||||
struct iio_dev *iio_dev;
|
||||
int err;
|
||||
|
||||
iio_dev = devm_iio_device_alloc(dev, sizeof(*hw));
|
||||
if (!iio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(dev, (void *)iio_dev);
|
||||
|
||||
hw = iio_priv(iio_dev);
|
||||
hw->irq = irq;
|
||||
hw->regmap = regmap;
|
||||
|
||||
err = st_uvis25_check_whoami(hw);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
iio_dev->modes = INDIO_DIRECT_MODE;
|
||||
iio_dev->dev.parent = dev;
|
||||
iio_dev->channels = st_uvis25_channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(st_uvis25_channels);
|
||||
iio_dev->name = ST_UVIS25_DEV_NAME;
|
||||
iio_dev->info = &st_uvis25_info;
|
||||
|
||||
err = st_uvis25_init_sensor(hw);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (hw->irq > 0) {
|
||||
err = st_uvis25_allocate_buffer(iio_dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = st_uvis25_allocate_trigger(iio_dev);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return devm_iio_device_register(dev, iio_dev);
|
||||
}
|
||||
EXPORT_SYMBOL(st_uvis25_probe);
|
||||
|
||||
static int __maybe_unused st_uvis25_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *iio_dev = dev_get_drvdata(dev);
|
||||
struct st_uvis25_hw *hw = iio_priv(iio_dev);
|
||||
|
||||
return regmap_update_bits(hw->regmap, ST_UVIS25_REG_CTRL1_ADDR,
|
||||
ST_UVIS25_REG_ODR_MASK, 0);
|
||||
}
|
||||
|
||||
static int __maybe_unused st_uvis25_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *iio_dev = dev_get_drvdata(dev);
|
||||
struct st_uvis25_hw *hw = iio_priv(iio_dev);
|
||||
|
||||
if (hw->enabled)
|
||||
return regmap_update_bits(hw->regmap, ST_UVIS25_REG_CTRL1_ADDR,
|
||||
ST_UVIS25_REG_ODR_MASK, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct dev_pm_ops st_uvis25_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(st_uvis25_suspend, st_uvis25_resume)
|
||||
};
|
||||
EXPORT_SYMBOL(st_uvis25_pm_ops);
|
||||
|
||||
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics uvis25 sensor driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
69
drivers/iio/light/st_uvis25_i2c.c
Normal file
69
drivers/iio/light/st_uvis25_i2c.c
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* STMicroelectronics uvis25 i2c driver
|
||||
*
|
||||
* Copyright 2017 STMicroelectronics Inc.
|
||||
*
|
||||
* Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "st_uvis25.h"
|
||||
|
||||
#define UVIS25_I2C_AUTO_INCREMENT BIT(7)
|
||||
|
||||
const struct regmap_config st_uvis25_i2c_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.write_flag_mask = UVIS25_I2C_AUTO_INCREMENT,
|
||||
.read_flag_mask = UVIS25_I2C_AUTO_INCREMENT,
|
||||
};
|
||||
|
||||
static int st_uvis25_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = devm_regmap_init_i2c(client, &st_uvis25_i2c_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&client->dev, "Failed to register i2c regmap %d\n",
|
||||
(int)PTR_ERR(regmap));
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
return st_uvis25_probe(&client->dev, client->irq, regmap);
|
||||
}
|
||||
|
||||
static const struct of_device_id st_uvis25_i2c_of_match[] = {
|
||||
{ .compatible = "st,uvis25", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_uvis25_i2c_of_match);
|
||||
|
||||
static const struct i2c_device_id st_uvis25_i2c_id_table[] = {
|
||||
{ ST_UVIS25_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, st_uvis25_i2c_id_table);
|
||||
|
||||
static struct i2c_driver st_uvis25_driver = {
|
||||
.driver = {
|
||||
.name = "st_uvis25_i2c",
|
||||
.pm = &st_uvis25_pm_ops,
|
||||
.of_match_table = of_match_ptr(st_uvis25_i2c_of_match),
|
||||
},
|
||||
.probe = st_uvis25_i2c_probe,
|
||||
.id_table = st_uvis25_i2c_id_table,
|
||||
};
|
||||
module_i2c_driver(st_uvis25_driver);
|
||||
|
||||
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics uvis25 i2c driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
68
drivers/iio/light/st_uvis25_spi.c
Normal file
68
drivers/iio/light/st_uvis25_spi.c
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* STMicroelectronics uvis25 spi driver
|
||||
*
|
||||
* Copyright 2017 STMicroelectronics Inc.
|
||||
*
|
||||
* Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "st_uvis25.h"
|
||||
|
||||
#define UVIS25_SENSORS_SPI_READ BIT(7)
|
||||
#define UVIS25_SPI_AUTO_INCREMENT BIT(6)
|
||||
|
||||
const struct regmap_config st_uvis25_spi_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.read_flag_mask = UVIS25_SENSORS_SPI_READ | UVIS25_SPI_AUTO_INCREMENT,
|
||||
.write_flag_mask = UVIS25_SPI_AUTO_INCREMENT,
|
||||
};
|
||||
|
||||
static int st_uvis25_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = devm_regmap_init_spi(spi, &st_uvis25_spi_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&spi->dev, "Failed to register spi regmap %d\n",
|
||||
(int)PTR_ERR(regmap));
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
return st_uvis25_probe(&spi->dev, spi->irq, regmap);
|
||||
}
|
||||
|
||||
static const struct of_device_id st_uvis25_spi_of_match[] = {
|
||||
{ .compatible = "st,uvis25", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_uvis25_spi_of_match);
|
||||
|
||||
static const struct spi_device_id st_uvis25_spi_id_table[] = {
|
||||
{ ST_UVIS25_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, st_uvis25_spi_id_table);
|
||||
|
||||
static struct spi_driver st_uvis25_driver = {
|
||||
.driver = {
|
||||
.name = "st_uvis25_spi",
|
||||
.pm = &st_uvis25_pm_ops,
|
||||
.of_match_table = of_match_ptr(st_uvis25_spi_of_match),
|
||||
},
|
||||
.probe = st_uvis25_spi_probe,
|
||||
.id_table = st_uvis25_spi_id_table,
|
||||
};
|
||||
module_spi_driver(st_uvis25_driver);
|
||||
|
||||
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics uvis25 spi driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
568
drivers/iio/light/zopt2201.c
Normal file
568
drivers/iio/light/zopt2201.c
Normal file
@@ -0,0 +1,568 @@
|
||||
/*
|
||||
* zopt2201.c - Support for IDT ZOPT2201 ambient light and UV B sensor
|
||||
*
|
||||
* Copyright 2017 Peter Meerwald-Stadler <pmeerw@pmeerw.net>
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* Datasheet: https://www.idt.com/document/dst/zopt2201-datasheet
|
||||
* 7-bit I2C slave addresses 0x53 (default) or 0x52 (programmed)
|
||||
*
|
||||
* TODO: interrupt support, ALS/UVB raw mode
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define ZOPT2201_DRV_NAME "zopt2201"
|
||||
|
||||
/* Registers */
|
||||
#define ZOPT2201_MAIN_CTRL 0x00
|
||||
#define ZOPT2201_LS_MEAS_RATE 0x04
|
||||
#define ZOPT2201_LS_GAIN 0x05
|
||||
#define ZOPT2201_PART_ID 0x06
|
||||
#define ZOPT2201_MAIN_STATUS 0x07
|
||||
#define ZOPT2201_ALS_DATA 0x0d /* LSB first, 13 to 20 bits */
|
||||
#define ZOPT2201_UVB_DATA 0x10 /* LSB first, 13 to 20 bits */
|
||||
#define ZOPT2201_UV_COMP_DATA 0x13 /* LSB first, 13 to 20 bits */
|
||||
#define ZOPT2201_COMP_DATA 0x16 /* LSB first, 13 to 20 bits */
|
||||
#define ZOPT2201_INT_CFG 0x19
|
||||
#define ZOPT2201_INT_PST 0x1a
|
||||
|
||||
#define ZOPT2201_MAIN_CTRL_LS_MODE BIT(3) /* 0 .. ALS, 1 .. UV B */
|
||||
#define ZOPT2201_MAIN_CTRL_LS_EN BIT(1)
|
||||
|
||||
/* Values for ZOPT2201_LS_MEAS_RATE resolution / bit width */
|
||||
#define ZOPT2201_MEAS_RES_20BIT 0 /* takes 400 ms */
|
||||
#define ZOPT2201_MEAS_RES_19BIT 1 /* takes 200 ms */
|
||||
#define ZOPT2201_MEAS_RES_18BIT 2 /* takes 100 ms, default */
|
||||
#define ZOPT2201_MEAS_RES_17BIT 3 /* takes 50 ms */
|
||||
#define ZOPT2201_MEAS_RES_16BIT 4 /* takes 25 ms */
|
||||
#define ZOPT2201_MEAS_RES_13BIT 5 /* takes 3.125 ms */
|
||||
#define ZOPT2201_MEAS_RES_SHIFT 4
|
||||
|
||||
/* Values for ZOPT2201_LS_MEAS_RATE measurement rate */
|
||||
#define ZOPT2201_MEAS_FREQ_25MS 0
|
||||
#define ZOPT2201_MEAS_FREQ_50MS 1
|
||||
#define ZOPT2201_MEAS_FREQ_100MS 2 /* default */
|
||||
#define ZOPT2201_MEAS_FREQ_200MS 3
|
||||
#define ZOPT2201_MEAS_FREQ_500MS 4
|
||||
#define ZOPT2201_MEAS_FREQ_1000MS 5
|
||||
#define ZOPT2201_MEAS_FREQ_2000MS 6
|
||||
|
||||
/* Values for ZOPT2201_LS_GAIN */
|
||||
#define ZOPT2201_LS_GAIN_1 0
|
||||
#define ZOPT2201_LS_GAIN_3 1
|
||||
#define ZOPT2201_LS_GAIN_6 2
|
||||
#define ZOPT2201_LS_GAIN_9 3
|
||||
#define ZOPT2201_LS_GAIN_18 4
|
||||
|
||||
/* Values for ZOPT2201_MAIN_STATUS */
|
||||
#define ZOPT2201_MAIN_STATUS_POWERON BIT(5)
|
||||
#define ZOPT2201_MAIN_STATUS_INT BIT(4)
|
||||
#define ZOPT2201_MAIN_STATUS_DRDY BIT(3)
|
||||
|
||||
#define ZOPT2201_PART_NUMBER 0xb2
|
||||
|
||||
struct zopt2201_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock;
|
||||
u8 gain;
|
||||
u8 res;
|
||||
u8 rate;
|
||||
};
|
||||
|
||||
static const struct {
|
||||
unsigned int gain; /* gain factor */
|
||||
unsigned int scale; /* micro lux per count */
|
||||
} zopt2201_gain_als[] = {
|
||||
{ 1, 19200000 },
|
||||
{ 3, 6400000 },
|
||||
{ 6, 3200000 },
|
||||
{ 9, 2133333 },
|
||||
{ 18, 1066666 },
|
||||
};
|
||||
|
||||
static const struct {
|
||||
unsigned int gain; /* gain factor */
|
||||
unsigned int scale; /* micro W/m2 per count */
|
||||
} zopt2201_gain_uvb[] = {
|
||||
{ 1, 460800 },
|
||||
{ 3, 153600 },
|
||||
{ 6, 76800 },
|
||||
{ 9, 51200 },
|
||||
{ 18, 25600 },
|
||||
};
|
||||
|
||||
static const struct {
|
||||
unsigned int bits; /* sensor resolution in bits */
|
||||
unsigned long us; /* measurement time in micro seconds */
|
||||
} zopt2201_resolution[] = {
|
||||
{ 20, 400000 },
|
||||
{ 19, 200000 },
|
||||
{ 18, 100000 },
|
||||
{ 17, 50000 },
|
||||
{ 16, 25000 },
|
||||
{ 13, 3125 },
|
||||
};
|
||||
|
||||
static const struct {
|
||||
unsigned int scale, uscale; /* scale factor as integer + micro */
|
||||
u8 gain; /* gain register value */
|
||||
u8 res; /* resolution register value */
|
||||
} zopt2201_scale_als[] = {
|
||||
{ 19, 200000, 0, 5 },
|
||||
{ 6, 400000, 1, 5 },
|
||||
{ 3, 200000, 2, 5 },
|
||||
{ 2, 400000, 0, 4 },
|
||||
{ 2, 133333, 3, 5 },
|
||||
{ 1, 200000, 0, 3 },
|
||||
{ 1, 66666, 4, 5 },
|
||||
{ 0, 800000, 1, 4 },
|
||||
{ 0, 600000, 0, 2 },
|
||||
{ 0, 400000, 2, 4 },
|
||||
{ 0, 300000, 0, 1 },
|
||||
{ 0, 266666, 3, 4 },
|
||||
{ 0, 200000, 2, 3 },
|
||||
{ 0, 150000, 0, 0 },
|
||||
{ 0, 133333, 4, 4 },
|
||||
{ 0, 100000, 2, 2 },
|
||||
{ 0, 66666, 4, 3 },
|
||||
{ 0, 50000, 2, 1 },
|
||||
{ 0, 33333, 4, 2 },
|
||||
{ 0, 25000, 2, 0 },
|
||||
{ 0, 16666, 4, 1 },
|
||||
{ 0, 8333, 4, 0 },
|
||||
};
|
||||
|
||||
static const struct {
|
||||
unsigned int scale, uscale; /* scale factor as integer + micro */
|
||||
u8 gain; /* gain register value */
|
||||
u8 res; /* resolution register value */
|
||||
} zopt2201_scale_uvb[] = {
|
||||
{ 0, 460800, 0, 5 },
|
||||
{ 0, 153600, 1, 5 },
|
||||
{ 0, 76800, 2, 5 },
|
||||
{ 0, 57600, 0, 4 },
|
||||
{ 0, 51200, 3, 5 },
|
||||
{ 0, 28800, 0, 3 },
|
||||
{ 0, 25600, 4, 5 },
|
||||
{ 0, 19200, 1, 4 },
|
||||
{ 0, 14400, 0, 2 },
|
||||
{ 0, 9600, 2, 4 },
|
||||
{ 0, 7200, 0, 1 },
|
||||
{ 0, 6400, 3, 4 },
|
||||
{ 0, 4800, 2, 3 },
|
||||
{ 0, 3600, 0, 0 },
|
||||
{ 0, 3200, 4, 4 },
|
||||
{ 0, 2400, 2, 2 },
|
||||
{ 0, 1600, 4, 3 },
|
||||
{ 0, 1200, 2, 1 },
|
||||
{ 0, 800, 4, 2 },
|
||||
{ 0, 600, 2, 0 },
|
||||
{ 0, 400, 4, 1 },
|
||||
{ 0, 200, 4, 0 },
|
||||
};
|
||||
|
||||
static int zopt2201_enable_mode(struct zopt2201_data *data, bool uvb_mode)
|
||||
{
|
||||
u8 out = ZOPT2201_MAIN_CTRL_LS_EN;
|
||||
|
||||
if (uvb_mode)
|
||||
out |= ZOPT2201_MAIN_CTRL_LS_MODE;
|
||||
|
||||
return i2c_smbus_write_byte_data(data->client, ZOPT2201_MAIN_CTRL, out);
|
||||
}
|
||||
|
||||
static int zopt2201_read(struct zopt2201_data *data, u8 reg)
|
||||
{
|
||||
struct i2c_client *client = data->client;
|
||||
int tries = 10;
|
||||
u8 buf[3];
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
ret = zopt2201_enable_mode(data, reg == ZOPT2201_UVB_DATA);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
while (tries--) {
|
||||
unsigned long t = zopt2201_resolution[data->res].us;
|
||||
|
||||
if (t <= 20000)
|
||||
usleep_range(t, t + 1000);
|
||||
else
|
||||
msleep(t / 1000);
|
||||
ret = i2c_smbus_read_byte_data(client, ZOPT2201_MAIN_STATUS);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
if (ret & ZOPT2201_MAIN_STATUS_DRDY)
|
||||
break;
|
||||
}
|
||||
|
||||
if (tries < 0) {
|
||||
ret = -ETIMEDOUT;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_read_i2c_block_data(client, reg, sizeof(buf), buf);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, ZOPT2201_MAIN_CTRL, 0x00);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
return (buf[2] << 16) | (buf[1] << 8) | buf[0];
|
||||
|
||||
fail:
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec zopt2201_channels[] = {
|
||||
{
|
||||
.type = IIO_LIGHT,
|
||||
.address = ZOPT2201_ALS_DATA,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
|
||||
},
|
||||
{
|
||||
.type = IIO_INTENSITY,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_LIGHT_UV,
|
||||
.address = ZOPT2201_UVB_DATA,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
|
||||
},
|
||||
{
|
||||
.type = IIO_UVINDEX,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||
},
|
||||
};
|
||||
|
||||
static int zopt2201_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct zopt2201_data *data = iio_priv(indio_dev);
|
||||
u64 tmp;
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = zopt2201_read(data, chan->address);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
ret = zopt2201_read(data, ZOPT2201_UVB_DATA);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret * 18 *
|
||||
(1 << (20 - zopt2201_resolution[data->res].bits)) /
|
||||
zopt2201_gain_uvb[data->gain].gain;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->address) {
|
||||
case ZOPT2201_ALS_DATA:
|
||||
*val = zopt2201_gain_als[data->gain].scale;
|
||||
break;
|
||||
case ZOPT2201_UVB_DATA:
|
||||
*val = zopt2201_gain_uvb[data->gain].scale;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*val2 = 1000000;
|
||||
*val2 *= (1 << (zopt2201_resolution[data->res].bits - 13));
|
||||
tmp = div_s64(*val * 1000000ULL, *val2);
|
||||
*val = div_s64_rem(tmp, 1000000, val2);
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
*val = 0;
|
||||
*val2 = zopt2201_resolution[data->res].us;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int zopt2201_set_resolution(struct zopt2201_data *data, u8 res)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client, ZOPT2201_LS_MEAS_RATE,
|
||||
(res << ZOPT2201_MEAS_RES_SHIFT) |
|
||||
data->rate);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data->res = res;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zopt2201_write_resolution(struct zopt2201_data *data,
|
||||
int val, int val2)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
if (val != 0)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(zopt2201_resolution); i++)
|
||||
if (val2 == zopt2201_resolution[i].us) {
|
||||
mutex_lock(&data->lock);
|
||||
ret = zopt2201_set_resolution(data, i);
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int zopt2201_set_gain(struct zopt2201_data *data, u8 gain)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client, ZOPT2201_LS_GAIN, gain);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data->gain = gain;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int zopt2201_write_scale_als_by_idx(struct zopt2201_data *data, int idx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
ret = zopt2201_set_resolution(data, zopt2201_scale_als[idx].res);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
ret = zopt2201_set_gain(data, zopt2201_scale_als[idx].gain);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int zopt2201_write_scale_als(struct zopt2201_data *data,
|
||||
int val, int val2)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(zopt2201_scale_als); i++)
|
||||
if (val == zopt2201_scale_als[i].scale &&
|
||||
val2 == zopt2201_scale_als[i].uscale) {
|
||||
return zopt2201_write_scale_als_by_idx(data, i);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int zopt2201_write_scale_uvb_by_idx(struct zopt2201_data *data, int idx)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
ret = zopt2201_set_resolution(data, zopt2201_scale_als[idx].res);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
ret = zopt2201_set_gain(data, zopt2201_scale_als[idx].gain);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int zopt2201_write_scale_uvb(struct zopt2201_data *data,
|
||||
int val, int val2)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(zopt2201_scale_uvb); i++)
|
||||
if (val == zopt2201_scale_uvb[i].scale &&
|
||||
val2 == zopt2201_scale_uvb[i].uscale)
|
||||
return zopt2201_write_scale_uvb_by_idx(data, i);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int zopt2201_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct zopt2201_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
return zopt2201_write_resolution(data, val, val2);
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->address) {
|
||||
case ZOPT2201_ALS_DATA:
|
||||
return zopt2201_write_scale_als(data, val, val2);
|
||||
case ZOPT2201_UVB_DATA:
|
||||
return zopt2201_write_scale_uvb(data, val, val2);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static ssize_t zopt2201_show_int_time_available(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
size_t len = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(zopt2201_resolution); i++)
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06lu ",
|
||||
zopt2201_resolution[i].us);
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static IIO_DEV_ATTR_INT_TIME_AVAIL(zopt2201_show_int_time_available);
|
||||
|
||||
static ssize_t zopt2201_show_als_scale_avail(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
ssize_t len = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(zopt2201_scale_als); i++)
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06u ",
|
||||
zopt2201_scale_als[i].scale,
|
||||
zopt2201_scale_als[i].uscale);
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t zopt2201_show_uvb_scale_avail(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
ssize_t len = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(zopt2201_scale_uvb); i++)
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06u ",
|
||||
zopt2201_scale_uvb[i].scale,
|
||||
zopt2201_scale_uvb[i].uscale);
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(in_illuminance_scale_available, 0444,
|
||||
zopt2201_show_als_scale_avail, NULL, 0);
|
||||
static IIO_DEVICE_ATTR(in_intensity_uv_scale_available, 0444,
|
||||
zopt2201_show_uvb_scale_avail, NULL, 0);
|
||||
|
||||
static struct attribute *zopt2201_attributes[] = {
|
||||
&iio_dev_attr_integration_time_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_illuminance_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_intensity_uv_scale_available.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group zopt2201_attribute_group = {
|
||||
.attrs = zopt2201_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info zopt2201_info = {
|
||||
.read_raw = zopt2201_read_raw,
|
||||
.write_raw = zopt2201_write_raw,
|
||||
.attrs = &zopt2201_attribute_group,
|
||||
};
|
||||
|
||||
static int zopt2201_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct zopt2201_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_READ_I2C_BLOCK))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, ZOPT2201_PART_ID);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != ZOPT2201_PART_NUMBER)
|
||||
return -ENODEV;
|
||||
|
||||
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;
|
||||
mutex_init(&data->lock);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &zopt2201_info;
|
||||
indio_dev->channels = zopt2201_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(zopt2201_channels);
|
||||
indio_dev->name = ZOPT2201_DRV_NAME;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
data->rate = ZOPT2201_MEAS_FREQ_100MS;
|
||||
ret = zopt2201_set_resolution(data, ZOPT2201_MEAS_RES_18BIT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = zopt2201_set_gain(data, ZOPT2201_LS_GAIN_3);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id zopt2201_id[] = {
|
||||
{ "zopt2201", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, zopt2201_id);
|
||||
|
||||
static struct i2c_driver zopt2201_driver = {
|
||||
.driver = {
|
||||
.name = ZOPT2201_DRV_NAME,
|
||||
},
|
||||
.probe = zopt2201_probe,
|
||||
.id_table = zopt2201_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(zopt2201_driver);
|
||||
|
||||
MODULE_AUTHOR("Peter Meerwald-Stadler <pmeerw@pmeerw.net>");
|
||||
MODULE_DESCRIPTION("IDT ZOPT2201 ambient light and UV B sensor driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -788,6 +788,7 @@ static const struct acpi_device_id ak_acpi_match[] = {
|
||||
{"AK8975", AK8975},
|
||||
{"AK8963", AK8963},
|
||||
{"INVN6500", AK8963},
|
||||
{"AK009911", AK09911},
|
||||
{"AK09911", AK09911},
|
||||
{"AK09912", AK09912},
|
||||
{ },
|
||||
|
||||
@@ -1022,6 +1022,7 @@ static const struct dev_pm_ops sx9500_pm_ops = {
|
||||
|
||||
static const struct acpi_device_id sx9500_acpi_match[] = {
|
||||
{"SSX9500", 0},
|
||||
{"SASX9500", 0},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, sx9500_acpi_match);
|
||||
|
||||
@@ -118,22 +118,23 @@ static inline ssize_t ad7152_start_calib(struct device *dev,
|
||||
|
||||
mutex_lock(&chip->state_lock);
|
||||
ret = i2c_smbus_write_byte_data(chip->client, AD7152_REG_CFG, regval);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&chip->state_lock);
|
||||
return ret;
|
||||
}
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
do {
|
||||
mdelay(20);
|
||||
ret = i2c_smbus_read_byte_data(chip->client, AD7152_REG_CFG);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&chip->state_lock);
|
||||
return ret;
|
||||
}
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
} while ((ret == regval) && timeout--);
|
||||
|
||||
mutex_unlock(&chip->state_lock);
|
||||
return len;
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&chip->state_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t ad7152_start_offset_calib(struct device *dev,
|
||||
|
||||
@@ -302,23 +302,24 @@ static inline ssize_t ad7746_start_calib(struct device *dev,
|
||||
mutex_lock(&chip->lock);
|
||||
regval |= chip->config;
|
||||
ret = i2c_smbus_write_byte_data(chip->client, AD7746_REG_CFG, regval);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&chip->lock);
|
||||
return ret;
|
||||
}
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
do {
|
||||
msleep(20);
|
||||
ret = i2c_smbus_read_byte_data(chip->client, AD7746_REG_CFG);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&chip->lock);
|
||||
return ret;
|
||||
}
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
} while ((ret == regval) && timeout--);
|
||||
|
||||
mutex_unlock(&chip->lock);
|
||||
|
||||
return len;
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&chip->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t ad7746_start_offset_calib(struct device *dev,
|
||||
|
||||
@@ -187,9 +187,9 @@ static int iio_bfin_tmr_trigger_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
st->irq = platform_get_irq(pdev, 0);
|
||||
if (!st->irq) {
|
||||
if (st->irq < 0) {
|
||||
dev_err(&pdev->dev, "No IRQs specified");
|
||||
return -ENODEV;
|
||||
return st->irq;
|
||||
}
|
||||
|
||||
ret = iio_bfin_tmr_get_number(st->irq);
|
||||
|
||||
@@ -28,4 +28,11 @@ struct iio_map {
|
||||
void *consumer_data;
|
||||
};
|
||||
|
||||
#define IIO_MAP(_provider_channel, _consumer_dev_name, _consumer_channel) \
|
||||
{ \
|
||||
.adc_channel_label = _provider_channel, \
|
||||
.consumer_dev_name = _consumer_dev_name, \
|
||||
.consumer_channel = _consumer_channel, \
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user