diff --git a/Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector b/Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector new file mode 100644 index 000000000000..2071f9bcfaa5 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector @@ -0,0 +1,36 @@ +What: /sys/bus/iio/devices/iio:deviceX/in_altvoltageY_invert +Date: October 2016 +KernelVersion: 4.9 +Contact: Peter Rosin +Description: + The DAC is used to find the peak level of an alternating + voltage input signal by a binary search using the output + of a comparator wired to an interrupt pin. Like so: + _ + | \ + input +------>-------|+ \ + | \ + .-------. | }---. + | | | / | + | dac|-->--|- / | + | | |_/ | + | | | + | | | + | irq|------<-------' + | | + '-------' + The boolean invert attribute (0/1) should be set when the + input signal is centered around the maximum value of the + dac instead of zero. The envelope detector will search + from below in this case and will also invert the result. + The edge/level of the interrupt is also switched to its + opposite value. + +What: /sys/bus/iio/devices/iio:deviceX/in_altvoltageY_compare_interval +Date: October 2016 +KernelVersion: 4.9 +Contact: Peter Rosin +Description: + Number of milliseconds to wait for the comparator in each + step of the binary search for the input peak level. Needs + to relate to the frequency of the input signal. diff --git a/Documentation/ABI/testing/sysfs-bus-iio-dac-dpot-dac b/Documentation/ABI/testing/sysfs-bus-iio-dac-dpot-dac new file mode 100644 index 000000000000..580e93f373f6 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-dac-dpot-dac @@ -0,0 +1,8 @@ +What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_raw_available +Date: October 2016 +KernelVersion: 4.9 +Contact: Peter Rosin +Description: + The range of available values represented as the minimum value, + the step and the maximum value, all enclosed in square brackets. + Example: [0 1 256] diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583 b/Documentation/ABI/testing/sysfs-bus-iio-light-tsl2583 similarity index 74% rename from drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583 rename to Documentation/ABI/testing/sysfs-bus-iio-light-tsl2583 index 660781df409f..a2e19964e87e 100644 --- a/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583 +++ b/Documentation/ABI/testing/sysfs-bus-iio-light-tsl2583 @@ -1,18 +1,18 @@ -What: /sys/bus/iio/devices/device[n]/lux_table -KernelVersion: 2.6.37 -Contact: linux-iio@vger.kernel.org -Description: - This property gets/sets the table of coefficients - used in calculating illuminance in lux. - -What: /sys/bus/iio/devices/device[n]/illuminance0_calibrate +What: /sys/bus/iio/devices/device[n]/in_illuminance_calibrate KernelVersion: 2.6.37 Contact: linux-iio@vger.kernel.org Description: This property causes an internal calibration of the als gain trim value which is later used in calculating illuminance in lux. -What: /sys/bus/iio/devices/device[n]/illuminance0_input_target +What: /sys/bus/iio/devices/device[n]/in_illuminance_lux_table +KernelVersion: 2.6.37 +Contact: linux-iio@vger.kernel.org +Description: + This property gets/sets the table of coefficients + used in calculating illuminance in lux. + +What: /sys/bus/iio/devices/device[n]/in_illuminance_input_target KernelVersion: 2.6.37 Contact: linux-iio@vger.kernel.org Description: diff --git a/Documentation/ABI/testing/sysfs-bus-iio-potentiometer-mcp4531 b/Documentation/ABI/testing/sysfs-bus-iio-potentiometer-mcp4531 new file mode 100644 index 000000000000..2a91fbe394fc --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-potentiometer-mcp4531 @@ -0,0 +1,8 @@ +What: /sys/bus/iio/devices/iio:deviceX/out_resistance_raw_available +Date: October 2016 +KernelVersion: 4.9 +Contact: Peter Rosin +Description: + The range of available values represented as the minimum value, + the step and the maximum value, all enclosed in square brackets. + Example: [0 1 256] diff --git a/Documentation/devicetree/bindings/iio/adc/envelope-detector.txt b/Documentation/devicetree/bindings/iio/adc/envelope-detector.txt new file mode 100644 index 000000000000..27544bdd4478 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/envelope-detector.txt @@ -0,0 +1,54 @@ +Bindings for ADC envelope detector using a DAC and a comparator + +The DAC is used to find the peak level of an alternating voltage input +signal by a binary search using the output of a comparator wired to +an interrupt pin. Like so: + _ + | \ + input +------>-------|+ \ + | \ + .-------. | }---. + | | | / | + | dac|-->--|- / | + | | |_/ | + | | | + | | | + | irq|------<-------' + | | + '-------' + +Required properties: +- compatible: Should be "axentia,tse850-envelope-detector" +- io-channels: Channel node of the dac to be used for comparator input. +- io-channel-names: Should be "dac". +- interrupt specification for one client interrupt, + see ../../interrupt-controller/interrupts.txt for details. +- interrupt-names: Should be "comp". + +Example: + + &i2c { + dpot: mcp4651-104@28 { + compatible = "microchip,mcp4651-104"; + reg = <0x28>; + #io-channel-cells = <1>; + }; + }; + + dac: dac { + compatible = "dpot-dac"; + vref-supply = <®_3v3>; + io-channels = <&dpot 0>; + io-channel-names = "dpot"; + #io-channel-cells = <1>; + }; + + envelope-detector { + compatible = "axentia,tse850-envelope-detector"; + io-channels = <&dac 0>; + io-channel-names = "dac"; + + interrupt-parent = <&gpio>; + interrupts = <3 IRQ_TYPE_EDGE_FALLING>; + interrupt-names = "comp"; + }; diff --git a/Documentation/devicetree/bindings/iio/dac/dpot-dac.txt b/Documentation/devicetree/bindings/iio/dac/dpot-dac.txt new file mode 100644 index 000000000000..fdf47a01bfef --- /dev/null +++ b/Documentation/devicetree/bindings/iio/dac/dpot-dac.txt @@ -0,0 +1,41 @@ +Bindings for DAC emulation using a digital potentiometer + +It is assumed that the dpot is used as a voltage divider between the +current dpot wiper setting and the maximum resistance of the dpot. The +divided voltage is provided by a vref regulator. + + .------. + .-----------. | | + | vref |--' .---. + | regulator |--. | | + '-----------' | | d | + | | p | + | | o | wiper + | | t |<---------+ + | | | + | '---' dac output voltage + | | + '------+------------+ + +Required properties: +- compatible: Should be "dpot-dac" +- vref-supply: The regulator supplying the voltage divider. +- io-channels: Channel node of the dpot to be used for the voltage division. +- io-channel-names: Should be "dpot". + +Example: + + &i2c { + dpot: mcp4651-503@28 { + compatible = "microchip,mcp4651-503"; + reg = <0x28>; + #io-channel-cells = <1>; + }; + }; + + dac { + compatible = "dpot-dac"; + vref-supply = <®_3v3>; + io-channels = <&dpot 0>; + io-channel-names = "dpot"; + }; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index 8d04aa99187c..8e4253e20138 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -39,6 +39,7 @@ auo AU Optronics Corporation auvidea Auvidea GmbH avago Avago Technologies avic Shanghai AVIC Optoelectronics Co., Ltd. +axentia Axentia Technologies AB axis Axis Communications AB boe BOE Technology Group Co., Ltd. bosch Bosch Sensortec GmbH diff --git a/MAINTAINERS b/MAINTAINERS index 1d27dd7ce36e..2de970d2f332 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6124,6 +6124,22 @@ L: linux-media@vger.kernel.org S: Maintained F: drivers/media/rc/iguanair.c +IIO DIGITAL POTENTIOMETER DAC +M: Peter Rosin +L: linux-iio@vger.kernel.org +S: Maintained +F: Documentation/ABI/testing/sysfs-bus-iio-dac-dpot-dac +F: Documentation/devicetree/bindings/iio/dac/dpot-dac.txt +F: drivers/iio/dac/dpot-dac.c + +IIO ENVELOPE DETECTOR +M: Peter Rosin +L: linux-iio@vger.kernel.org +S: Maintained +F: Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector +F: Documentation/devicetree/bindings/iio/adc/envelope-detector.txt +F: drivers/iio/adc/envelope-detector.c + IIO SUBSYSTEM AND DRIVERS M: Jonathan Cameron R: Hartmut Knaack @@ -7714,6 +7730,7 @@ MCP4531 MICROCHIP DIGITAL POTENTIOMETER DRIVER M: Peter Rosin L: linux-iio@vger.kernel.org S: Maintained +F: Documentation/ABI/testing/sysfs-bus-iio-potentiometer-mcp4531 F: drivers/iio/potentiometer/mcp4531.c MEASUREMENT COMPUTING CIO-DAC IIO DRIVER diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 57ebb997072c..6bbee0b0dfff 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -207,6 +207,16 @@ config DA9150_GPADC To compile this driver as a module, choose M here: the module will be called berlin2-adc. +config ENVELOPE_DETECTOR + tristate "Envelope detector using a DAC and a comparator" + depends on OF + help + Say yes here to build support for an envelope detector using a DAC + and a comparator. + + To compile this driver as a module, choose M here: the module will be + called envelope-detector. + config EXYNOS_ADC tristate "Exynos ADC driver support" depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || (OF && COMPILE_TEST) diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 96894b32300d..9391217648cb 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_BCM_IPROC_ADC) += bcm_iproc_adc.o obj-$(CONFIG_BERLIN2_ADC) += berlin2-adc.o obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o +obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o obj-$(CONFIG_HI8435) += hi8435.o diff --git a/drivers/iio/adc/ad7766.c b/drivers/iio/adc/ad7766.c index d906686c48eb..75cca42b6e70 100644 --- a/drivers/iio/adc/ad7766.c +++ b/drivers/iio/adc/ad7766.c @@ -239,8 +239,8 @@ static int ad7766_probe(struct spi_device *spi) ret = devm_regulator_bulk_get(&spi->dev, ARRAY_SIZE(ad7766->reg), ad7766->reg); - if (IS_ERR(ad7766->reg)) - return PTR_ERR(ad7766->reg); + if (ret) + return ret; ad7766->pd_gpio = devm_gpiod_get_optional(&spi->dev, "powerdown", GPIOD_OUT_HIGH); diff --git a/drivers/iio/adc/envelope-detector.c b/drivers/iio/adc/envelope-detector.c new file mode 100644 index 000000000000..fef15c0d7c9c --- /dev/null +++ b/drivers/iio/adc/envelope-detector.c @@ -0,0 +1,422 @@ +/* + * Driver for an envelope detector using a DAC and a comparator + * + * Copyright (C) 2016 Axentia Technologies AB + * + * Author: Peter Rosin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * The DAC is used to find the peak level of an alternating voltage input + * signal by a binary search using the output of a comparator wired to + * an interrupt pin. Like so: + * _ + * | \ + * input +------>-------|+ \ + * | \ + * .-------. | }---. + * | | | / | + * | dac|-->--|- / | + * | | |_/ | + * | | | + * | | | + * | irq|------<-------' + * | | + * '-------' + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct envelope { + spinlock_t comp_lock; /* protects comp */ + int comp; + + struct mutex read_lock; /* protects everything else */ + + int comp_irq; + u32 comp_irq_trigger; + u32 comp_irq_trigger_inv; + + struct iio_channel *dac; + struct delayed_work comp_timeout; + + unsigned int comp_interval; + bool invert; + u32 dac_max; + + int high; + int level; + int low; + + struct completion done; +}; + +/* + * The envelope_detector_comp_latch function works together with the compare + * interrupt service routine below (envelope_detector_comp_isr) as a latch + * (one-bit memory) for if the interrupt has triggered since last calling + * this function. + * The ..._comp_isr function disables the interrupt so that the cpu does not + * need to service a possible interrupt flood from the comparator when no-one + * cares anyway, and this ..._comp_latch function reenables them again if + * needed. + */ +static int envelope_detector_comp_latch(struct envelope *env) +{ + int comp; + + spin_lock_irq(&env->comp_lock); + comp = env->comp; + env->comp = 0; + spin_unlock_irq(&env->comp_lock); + + if (!comp) + return 0; + + /* + * The irq was disabled, and is reenabled just now. + * But there might have been a pending irq that + * happened while the irq was disabled that fires + * just as the irq is reenabled. That is not what + * is desired. + */ + enable_irq(env->comp_irq); + + /* So, synchronize this possibly pending irq... */ + synchronize_irq(env->comp_irq); + + /* ...and redo the whole dance. */ + spin_lock_irq(&env->comp_lock); + comp = env->comp; + env->comp = 0; + spin_unlock_irq(&env->comp_lock); + + if (comp) + enable_irq(env->comp_irq); + + return 1; +} + +static irqreturn_t envelope_detector_comp_isr(int irq, void *ctx) +{ + struct envelope *env = ctx; + + spin_lock(&env->comp_lock); + env->comp = 1; + disable_irq_nosync(env->comp_irq); + spin_unlock(&env->comp_lock); + + return IRQ_HANDLED; +} + +static void envelope_detector_setup_compare(struct envelope *env) +{ + int ret; + + /* + * Do a binary search for the peak input level, and stop + * when that level is "trapped" between two adjacent DAC + * values. + * When invert is active, use the midpoint floor so that + * env->level ends up as env->low when the termination + * criteria below is fulfilled, and use the midpoint + * ceiling when invert is not active so that env->level + * ends up as env->high in that case. + */ + env->level = (env->high + env->low + !env->invert) / 2; + + if (env->high == env->low + 1) { + complete(&env->done); + return; + } + + /* Set a "safe" DAC level (if there is such a thing)... */ + ret = iio_write_channel_raw(env->dac, env->invert ? 0 : env->dac_max); + if (ret < 0) + goto err; + + /* ...clear the comparison result... */ + envelope_detector_comp_latch(env); + + /* ...set the real DAC level... */ + ret = iio_write_channel_raw(env->dac, env->level); + if (ret < 0) + goto err; + + /* ...and wait for a bit to see if the latch catches anything. */ + schedule_delayed_work(&env->comp_timeout, + msecs_to_jiffies(env->comp_interval)); + return; + +err: + env->level = ret; + complete(&env->done); +} + +static void envelope_detector_timeout(struct work_struct *work) +{ + struct envelope *env = container_of(work, struct envelope, + comp_timeout.work); + + /* Adjust low/high depending on the latch content... */ + if (!envelope_detector_comp_latch(env) ^ !env->invert) + env->low = env->level; + else + env->high = env->level; + + /* ...and continue the search. */ + envelope_detector_setup_compare(env); +} + +static int envelope_detector_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct envelope *env = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + /* + * When invert is active, start with high=max+1 and low=0 + * since we will end up with the low value when the + * termination criteria is fulfilled (rounding down). And + * start with high=max and low=-1 when invert is not active + * since we will end up with the high value in that case. + * This ensures that the returned value in both cases are + * in the same range as the DAC and is a value that has not + * triggered the comparator. + */ + mutex_lock(&env->read_lock); + env->high = env->dac_max + env->invert; + env->low = -1 + env->invert; + envelope_detector_setup_compare(env); + wait_for_completion(&env->done); + if (env->level < 0) { + ret = env->level; + goto err_unlock; + } + *val = env->invert ? env->dac_max - env->level : env->level; + mutex_unlock(&env->read_lock); + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + return iio_read_channel_scale(env->dac, val, val2); + } + + return -EINVAL; + +err_unlock: + mutex_unlock(&env->read_lock); + return ret; +} + +static ssize_t envelope_show_invert(struct iio_dev *indio_dev, + uintptr_t private, + struct iio_chan_spec const *ch, char *buf) +{ + struct envelope *env = iio_priv(indio_dev); + + return sprintf(buf, "%u\n", env->invert); +} + +static ssize_t envelope_store_invert(struct iio_dev *indio_dev, + uintptr_t private, + struct iio_chan_spec const *ch, + const char *buf, size_t len) +{ + struct envelope *env = iio_priv(indio_dev); + unsigned long invert; + int ret; + u32 trigger; + + ret = kstrtoul(buf, 0, &invert); + if (ret < 0) + return ret; + if (invert > 1) + return -EINVAL; + + trigger = invert ? env->comp_irq_trigger_inv : env->comp_irq_trigger; + + mutex_lock(&env->read_lock); + if (invert != env->invert) + ret = irq_set_irq_type(env->comp_irq, trigger); + if (!ret) { + env->invert = invert; + ret = len; + } + mutex_unlock(&env->read_lock); + + return ret; +} + +static ssize_t envelope_show_comp_interval(struct iio_dev *indio_dev, + uintptr_t private, + struct iio_chan_spec const *ch, + char *buf) +{ + struct envelope *env = iio_priv(indio_dev); + + return sprintf(buf, "%u\n", env->comp_interval); +} + +static ssize_t envelope_store_comp_interval(struct iio_dev *indio_dev, + uintptr_t private, + struct iio_chan_spec const *ch, + const char *buf, size_t len) +{ + struct envelope *env = iio_priv(indio_dev); + unsigned long interval; + int ret; + + ret = kstrtoul(buf, 0, &interval); + if (ret < 0) + return ret; + if (interval > 1000) + return -EINVAL; + + mutex_lock(&env->read_lock); + env->comp_interval = interval; + mutex_unlock(&env->read_lock); + + return len; +} + +static const struct iio_chan_spec_ext_info envelope_detector_ext_info[] = { + { .name = "invert", + .read = envelope_show_invert, + .write = envelope_store_invert, }, + { .name = "compare_interval", + .read = envelope_show_comp_interval, + .write = envelope_store_comp_interval, }, + { /* sentinel */ } +}; + +static const struct iio_chan_spec envelope_detector_iio_channel = { + .type = IIO_ALTVOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) + | BIT(IIO_CHAN_INFO_SCALE), + .ext_info = envelope_detector_ext_info, + .indexed = 1, +}; + +static const struct iio_info envelope_detector_info = { + .read_raw = &envelope_detector_read_raw, + .driver_module = THIS_MODULE, +}; + +static int envelope_detector_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct envelope *env; + enum iio_chan_type type; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*env)); + if (!indio_dev) + return -ENOMEM; + + platform_set_drvdata(pdev, indio_dev); + env = iio_priv(indio_dev); + env->comp_interval = 50; /* some sensible default? */ + + spin_lock_init(&env->comp_lock); + mutex_init(&env->read_lock); + init_completion(&env->done); + INIT_DELAYED_WORK(&env->comp_timeout, envelope_detector_timeout); + + indio_dev->name = dev_name(dev); + indio_dev->dev.parent = dev; + indio_dev->dev.of_node = dev->of_node; + indio_dev->info = &envelope_detector_info; + indio_dev->channels = &envelope_detector_iio_channel; + indio_dev->num_channels = 1; + + env->dac = devm_iio_channel_get(dev, "dac"); + if (IS_ERR(env->dac)) { + if (PTR_ERR(env->dac) != -EPROBE_DEFER) + dev_err(dev, "failed to get dac input channel\n"); + return PTR_ERR(env->dac); + } + + env->comp_irq = platform_get_irq_byname(pdev, "comp"); + if (env->comp_irq < 0) { + if (env->comp_irq != -EPROBE_DEFER) + dev_err(dev, "failed to get compare interrupt\n"); + return env->comp_irq; + } + + ret = devm_request_irq(dev, env->comp_irq, envelope_detector_comp_isr, + 0, "envelope-detector", env); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to request interrupt\n"); + return ret; + } + env->comp_irq_trigger = irq_get_trigger_type(env->comp_irq); + if (env->comp_irq_trigger & IRQF_TRIGGER_RISING) + env->comp_irq_trigger_inv |= IRQF_TRIGGER_FALLING; + if (env->comp_irq_trigger & IRQF_TRIGGER_FALLING) + env->comp_irq_trigger_inv |= IRQF_TRIGGER_RISING; + if (env->comp_irq_trigger & IRQF_TRIGGER_HIGH) + env->comp_irq_trigger_inv |= IRQF_TRIGGER_LOW; + if (env->comp_irq_trigger & IRQF_TRIGGER_LOW) + env->comp_irq_trigger_inv |= IRQF_TRIGGER_HIGH; + + ret = iio_get_channel_type(env->dac, &type); + if (ret < 0) + return ret; + + if (type != IIO_VOLTAGE) { + dev_err(dev, "dac is of the wrong type\n"); + return -EINVAL; + } + + ret = iio_read_max_channel_raw(env->dac, &env->dac_max); + if (ret < 0) { + dev_err(dev, "dac does not indicate its raw maximum value\n"); + return ret; + } + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id envelope_detector_match[] = { + { .compatible = "axentia,tse850-envelope-detector", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, envelope_detector_match); + +static struct platform_driver envelope_detector_driver = { + .probe = envelope_detector_probe, + .driver = { + .name = "iio-envelope-detector", + .of_match_table = envelope_detector_match, + }, +}; +module_platform_driver(envelope_detector_driver); + +MODULE_DESCRIPTION("Envelope detector using a DAC and a comparator"); +MODULE_AUTHOR("Peter Rosin "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c index a3be7991355e..416cae5ebbd0 100644 --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c @@ -291,15 +291,15 @@ int cros_ec_sensors_read_lpc(struct iio_dev *indio_dev, return -EIO; /* Read status byte until EC is not busy. */ - status = cros_ec_sensors_read_until_not_busy(st); - if (status < 0) - return status; + ret = cros_ec_sensors_read_until_not_busy(st); + if (ret < 0) + return ret; /* * Store the current sample id so that we can compare to the * sample id after reading the data. */ - samp_id = status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK; + samp_id = ret & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK; /* Read all EC data, format it, and store it into data. */ ret = cros_ec_sensors_read_data_unsafe(indio_dev, scan_mask, diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 120b24478469..d3084028905b 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -200,6 +200,16 @@ config AD8801 To compile this driver as a module choose M here: the module will be called ad8801. +config DPOT_DAC + tristate "DAC emulation using a DPOT" + depends on OF + help + Say yes here to build support for DAC emulation using a digital + potentiometer. + + To compile this driver as a module, choose M here: the module will be + called dpot-dac. + config LPC18XX_DAC tristate "NXP LPC18xx DAC driver" depends on ARCH_LPC18XX || COMPILE_TEST diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 27642bbf75f2..f01bf4a99867 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_AD5686) += ad5686.o obj-$(CONFIG_AD7303) += ad7303.o obj-$(CONFIG_AD8801) += ad8801.o obj-$(CONFIG_CIO_DAC) += cio-dac.o +obj-$(CONFIG_DPOT_DAC) += dpot-dac.o obj-$(CONFIG_LPC18XX_DAC) += lpc18xx_dac.o obj-$(CONFIG_M62332) += m62332.o obj-$(CONFIG_MAX517) += max517.o diff --git a/drivers/iio/dac/dpot-dac.c b/drivers/iio/dac/dpot-dac.c new file mode 100644 index 000000000000..960a2b430480 --- /dev/null +++ b/drivers/iio/dac/dpot-dac.c @@ -0,0 +1,266 @@ +/* + * IIO DAC emulation driver using a digital potentiometer + * + * Copyright (C) 2016 Axentia Technologies AB + * + * Author: Peter Rosin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * It is assumed that the dpot is used as a voltage divider between the + * current dpot wiper setting and the maximum resistance of the dpot. The + * divided voltage is provided by a vref regulator. + * + * .------. + * .-----------. | | + * | vref |--' .---. + * | regulator |--. | | + * '-----------' | | d | + * | | p | + * | | o | wiper + * | | t |<---------+ + * | | | + * | '---' dac output voltage + * | | + * '------+------------+ + */ + +#include +#include +#include +#include +#include +#include +#include + +struct dpot_dac { + struct regulator *vref; + struct iio_channel *dpot; + u32 max_ohms; +}; + +static const struct iio_chan_spec dpot_dac_iio_channel = { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) + | BIT(IIO_CHAN_INFO_SCALE), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW), + .output = 1, + .indexed = 1, +}; + +static int dpot_dac_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct dpot_dac *dac = iio_priv(indio_dev); + int ret; + unsigned long long tmp; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + return iio_read_channel_raw(dac->dpot, val); + + case IIO_CHAN_INFO_SCALE: + ret = iio_read_channel_scale(dac->dpot, val, val2); + switch (ret) { + case IIO_VAL_FRACTIONAL_LOG2: + tmp = *val * 1000000000LL; + do_div(tmp, dac->max_ohms); + tmp *= regulator_get_voltage(dac->vref) / 1000; + do_div(tmp, 1000000000LL); + *val = tmp; + return ret; + case IIO_VAL_INT: + /* + * Convert integer scale to fractional scale by + * setting the denominator (val2) to one... + */ + *val2 = 1; + ret = IIO_VAL_FRACTIONAL; + /* ...and fall through. */ + case IIO_VAL_FRACTIONAL: + *val *= regulator_get_voltage(dac->vref) / 1000; + *val2 *= dac->max_ohms; + break; + } + + return ret; + } + + return -EINVAL; +} + +static int dpot_dac_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct dpot_dac *dac = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + *type = IIO_VAL_INT; + return iio_read_avail_channel_raw(dac->dpot, vals, length); + } + + return -EINVAL; +} + +static int dpot_dac_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct dpot_dac *dac = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + return iio_write_channel_raw(dac->dpot, val); + } + + return -EINVAL; +} + +static const struct iio_info dpot_dac_info = { + .read_raw = dpot_dac_read_raw, + .read_avail = dpot_dac_read_avail, + .write_raw = dpot_dac_write_raw, + .driver_module = THIS_MODULE, +}; + +static int dpot_dac_channel_max_ohms(struct iio_dev *indio_dev) +{ + struct device *dev = &indio_dev->dev; + struct dpot_dac *dac = iio_priv(indio_dev); + unsigned long long tmp; + int ret; + int val; + int val2; + int max; + + ret = iio_read_max_channel_raw(dac->dpot, &max); + if (ret < 0) { + dev_err(dev, "dpot does not indicate its raw maximum value\n"); + return ret; + } + + switch (iio_read_channel_scale(dac->dpot, &val, &val2)) { + case IIO_VAL_INT: + return max * val; + case IIO_VAL_FRACTIONAL: + tmp = (unsigned long long)max * val; + do_div(tmp, val2); + return tmp; + case IIO_VAL_FRACTIONAL_LOG2: + tmp = val * 1000000000LL * max >> val2; + do_div(tmp, 1000000000LL); + return tmp; + default: + dev_err(dev, "dpot has a scale that is too weird\n"); + } + + return -EINVAL; +} + +static int dpot_dac_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_dev *indio_dev; + struct dpot_dac *dac; + enum iio_chan_type type; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*dac)); + if (!indio_dev) + return -ENOMEM; + + platform_set_drvdata(pdev, indio_dev); + dac = iio_priv(indio_dev); + + indio_dev->name = dev_name(dev); + indio_dev->dev.parent = dev; + indio_dev->info = &dpot_dac_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = &dpot_dac_iio_channel; + indio_dev->num_channels = 1; + + dac->vref = devm_regulator_get(dev, "vref"); + if (IS_ERR(dac->vref)) { + if (PTR_ERR(dac->vref) != -EPROBE_DEFER) + dev_err(&pdev->dev, "failed to get vref regulator\n"); + return PTR_ERR(dac->vref); + } + + dac->dpot = devm_iio_channel_get(dev, "dpot"); + if (IS_ERR(dac->dpot)) { + if (PTR_ERR(dac->dpot) != -EPROBE_DEFER) + dev_err(dev, "failed to get dpot input channel\n"); + return PTR_ERR(dac->dpot); + } + + ret = iio_get_channel_type(dac->dpot, &type); + if (ret < 0) + return ret; + + if (type != IIO_RESISTANCE) { + dev_err(dev, "dpot is of the wrong type\n"); + return -EINVAL; + } + + ret = dpot_dac_channel_max_ohms(indio_dev); + if (ret < 0) + return ret; + dac->max_ohms = ret; + + ret = regulator_enable(dac->vref); + if (ret) { + dev_err(dev, "failed to enable the vref regulator\n"); + return ret; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(dev, "failed to register iio device\n"); + goto disable_reg; + } + + return 0; + +disable_reg: + regulator_disable(dac->vref); + return ret; +} + +static int dpot_dac_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct dpot_dac *dac = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + regulator_disable(dac->vref); + + return 0; +} + +static const struct of_device_id dpot_dac_match[] = { + { .compatible = "dpot-dac" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, dpot_dac_match); + +static struct platform_driver dpot_dac_driver = { + .probe = dpot_dac_probe, + .remove = dpot_dac_remove, + .driver = { + .name = "iio-dpot-dac", + .of_match_table = dpot_dac_match, + }, +}; +module_platform_driver(dpot_dac_driver); + +MODULE_DESCRIPTION("DAC emulation driver using a digital potentiometer"); +MODULE_AUTHOR("Peter Rosin "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig index 107b5efd4178..3126cf05e6b9 100644 --- a/drivers/iio/gyro/Kconfig +++ b/drivers/iio/gyro/Kconfig @@ -93,6 +93,7 @@ config MPU3050 config MPU3050_I2C tristate "Invensense MPU3050 devices on I2C" depends on !(INPUT_MPU3050=y || INPUT_MPU3050=m) + depends on I2C select MPU3050 select REGMAP_I2C select I2C_MUX diff --git a/drivers/iio/gyro/mpu3050-core.c b/drivers/iio/gyro/mpu3050-core.c index ed681c70a7b4..2be2a5d287e6 100644 --- a/drivers/iio/gyro/mpu3050-core.c +++ b/drivers/iio/gyro/mpu3050-core.c @@ -746,7 +746,6 @@ static const struct iio_info mpu3050_info = { .read_raw = mpu3050_read_raw, .write_raw = mpu3050_write_raw, .attrs = &mpu3050_attribute_group, - .driver_module = THIS_MODULE, }; /** diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c index aea034d8fe0f..2a42b3d583e8 100644 --- a/drivers/iio/gyro/st_gyro_core.c +++ b/drivers/iio/gyro/st_gyro_core.c @@ -39,79 +39,6 @@ #define ST_GYRO_FS_AVL_500DPS 500 #define ST_GYRO_FS_AVL_2000DPS 2000 -/* CUSTOM VALUES FOR SENSOR 1 */ -#define ST_GYRO_1_WAI_EXP 0xd3 -#define ST_GYRO_1_ODR_ADDR 0x20 -#define ST_GYRO_1_ODR_MASK 0xc0 -#define ST_GYRO_1_ODR_AVL_100HZ_VAL 0x00 -#define ST_GYRO_1_ODR_AVL_200HZ_VAL 0x01 -#define ST_GYRO_1_ODR_AVL_400HZ_VAL 0x02 -#define ST_GYRO_1_ODR_AVL_800HZ_VAL 0x03 -#define ST_GYRO_1_PW_ADDR 0x20 -#define ST_GYRO_1_PW_MASK 0x08 -#define ST_GYRO_1_FS_ADDR 0x23 -#define ST_GYRO_1_FS_MASK 0x30 -#define ST_GYRO_1_FS_AVL_250_VAL 0x00 -#define ST_GYRO_1_FS_AVL_500_VAL 0x01 -#define ST_GYRO_1_FS_AVL_2000_VAL 0x02 -#define ST_GYRO_1_FS_AVL_250_GAIN IIO_DEGREE_TO_RAD(8750) -#define ST_GYRO_1_FS_AVL_500_GAIN IIO_DEGREE_TO_RAD(17500) -#define ST_GYRO_1_FS_AVL_2000_GAIN IIO_DEGREE_TO_RAD(70000) -#define ST_GYRO_1_BDU_ADDR 0x23 -#define ST_GYRO_1_BDU_MASK 0x80 -#define ST_GYRO_1_DRDY_IRQ_ADDR 0x22 -#define ST_GYRO_1_DRDY_IRQ_INT2_MASK 0x08 -#define ST_GYRO_1_MULTIREAD_BIT true - -/* CUSTOM VALUES FOR SENSOR 2 */ -#define ST_GYRO_2_WAI_EXP 0xd4 -#define ST_GYRO_2_ODR_ADDR 0x20 -#define ST_GYRO_2_ODR_MASK 0xc0 -#define ST_GYRO_2_ODR_AVL_95HZ_VAL 0x00 -#define ST_GYRO_2_ODR_AVL_190HZ_VAL 0x01 -#define ST_GYRO_2_ODR_AVL_380HZ_VAL 0x02 -#define ST_GYRO_2_ODR_AVL_760HZ_VAL 0x03 -#define ST_GYRO_2_PW_ADDR 0x20 -#define ST_GYRO_2_PW_MASK 0x08 -#define ST_GYRO_2_FS_ADDR 0x23 -#define ST_GYRO_2_FS_MASK 0x30 -#define ST_GYRO_2_FS_AVL_250_VAL 0x00 -#define ST_GYRO_2_FS_AVL_500_VAL 0x01 -#define ST_GYRO_2_FS_AVL_2000_VAL 0x02 -#define ST_GYRO_2_FS_AVL_250_GAIN IIO_DEGREE_TO_RAD(8750) -#define ST_GYRO_2_FS_AVL_500_GAIN IIO_DEGREE_TO_RAD(17500) -#define ST_GYRO_2_FS_AVL_2000_GAIN IIO_DEGREE_TO_RAD(70000) -#define ST_GYRO_2_BDU_ADDR 0x23 -#define ST_GYRO_2_BDU_MASK 0x80 -#define ST_GYRO_2_DRDY_IRQ_ADDR 0x22 -#define ST_GYRO_2_DRDY_IRQ_INT2_MASK 0x08 -#define ST_GYRO_2_MULTIREAD_BIT true - -/* CUSTOM VALUES FOR SENSOR 3 */ -#define ST_GYRO_3_WAI_EXP 0xd7 -#define ST_GYRO_3_ODR_ADDR 0x20 -#define ST_GYRO_3_ODR_MASK 0xc0 -#define ST_GYRO_3_ODR_AVL_95HZ_VAL 0x00 -#define ST_GYRO_3_ODR_AVL_190HZ_VAL 0x01 -#define ST_GYRO_3_ODR_AVL_380HZ_VAL 0x02 -#define ST_GYRO_3_ODR_AVL_760HZ_VAL 0x03 -#define ST_GYRO_3_PW_ADDR 0x20 -#define ST_GYRO_3_PW_MASK 0x08 -#define ST_GYRO_3_FS_ADDR 0x23 -#define ST_GYRO_3_FS_MASK 0x30 -#define ST_GYRO_3_FS_AVL_250_VAL 0x00 -#define ST_GYRO_3_FS_AVL_500_VAL 0x01 -#define ST_GYRO_3_FS_AVL_2000_VAL 0x02 -#define ST_GYRO_3_FS_AVL_250_GAIN IIO_DEGREE_TO_RAD(8750) -#define ST_GYRO_3_FS_AVL_500_GAIN IIO_DEGREE_TO_RAD(17500) -#define ST_GYRO_3_FS_AVL_2000_GAIN IIO_DEGREE_TO_RAD(70000) -#define ST_GYRO_3_BDU_ADDR 0x23 -#define ST_GYRO_3_BDU_MASK 0x80 -#define ST_GYRO_3_DRDY_IRQ_ADDR 0x22 -#define ST_GYRO_3_DRDY_IRQ_INT2_MASK 0x08 -#define ST_GYRO_3_MULTIREAD_BIT true - - static const struct iio_chan_spec st_gyro_16bit_channels[] = { ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), @@ -130,7 +57,7 @@ static const struct iio_chan_spec st_gyro_16bit_channels[] = { static const struct st_sensor_settings st_gyro_sensors_settings[] = { { - .wai = ST_GYRO_1_WAI_EXP, + .wai = 0xd3, .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS, .sensors_supported = { [0] = L3G4200D_GYRO_DEV_NAME, @@ -138,18 +65,18 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = { }, .ch = (struct iio_chan_spec *)st_gyro_16bit_channels, .odr = { - .addr = ST_GYRO_1_ODR_ADDR, - .mask = ST_GYRO_1_ODR_MASK, + .addr = 0x20, + .mask = 0xc0, .odr_avl = { - { 100, ST_GYRO_1_ODR_AVL_100HZ_VAL, }, - { 200, ST_GYRO_1_ODR_AVL_200HZ_VAL, }, - { 400, ST_GYRO_1_ODR_AVL_400HZ_VAL, }, - { 800, ST_GYRO_1_ODR_AVL_800HZ_VAL, }, + { .hz = 100, .value = 0x00, }, + { .hz = 200, .value = 0x01, }, + { .hz = 400, .value = 0x02, }, + { .hz = 800, .value = 0x03, }, }, }, .pw = { - .addr = ST_GYRO_1_PW_ADDR, - .mask = ST_GYRO_1_PW_MASK, + .addr = 0x20, + .mask = 0x08, .value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE, .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, }, @@ -158,33 +85,33 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = { .mask = ST_SENSORS_DEFAULT_AXIS_MASK, }, .fs = { - .addr = ST_GYRO_1_FS_ADDR, - .mask = ST_GYRO_1_FS_MASK, + .addr = 0x23, + .mask = 0x30, .fs_avl = { [0] = { .num = ST_GYRO_FS_AVL_250DPS, - .value = ST_GYRO_1_FS_AVL_250_VAL, - .gain = ST_GYRO_1_FS_AVL_250_GAIN, + .value = 0x00, + .gain = IIO_DEGREE_TO_RAD(8750), }, [1] = { .num = ST_GYRO_FS_AVL_500DPS, - .value = ST_GYRO_1_FS_AVL_500_VAL, - .gain = ST_GYRO_1_FS_AVL_500_GAIN, + .value = 0x01, + .gain = IIO_DEGREE_TO_RAD(17500), }, [2] = { .num = ST_GYRO_FS_AVL_2000DPS, - .value = ST_GYRO_1_FS_AVL_2000_VAL, - .gain = ST_GYRO_1_FS_AVL_2000_GAIN, + .value = 0x02, + .gain = IIO_DEGREE_TO_RAD(70000), }, }, }, .bdu = { - .addr = ST_GYRO_1_BDU_ADDR, - .mask = ST_GYRO_1_BDU_MASK, + .addr = 0x23, + .mask = 0x80, }, .drdy_irq = { - .addr = ST_GYRO_1_DRDY_IRQ_ADDR, - .mask_int2 = ST_GYRO_1_DRDY_IRQ_INT2_MASK, + .addr = 0x22, + .mask_int2 = 0x08, /* * The sensor has IHL (active low) and open * drain settings, but only for INT1 and not @@ -192,11 +119,11 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = { */ .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, }, - .multi_read_bit = ST_GYRO_1_MULTIREAD_BIT, + .multi_read_bit = true, .bootime = 2, }, { - .wai = ST_GYRO_2_WAI_EXP, + .wai = 0xd4, .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS, .sensors_supported = { [0] = L3GD20_GYRO_DEV_NAME, @@ -208,18 +135,18 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = { }, .ch = (struct iio_chan_spec *)st_gyro_16bit_channels, .odr = { - .addr = ST_GYRO_2_ODR_ADDR, - .mask = ST_GYRO_2_ODR_MASK, + .addr = 0x20, + .mask = 0xc0, .odr_avl = { - { 95, ST_GYRO_2_ODR_AVL_95HZ_VAL, }, - { 190, ST_GYRO_2_ODR_AVL_190HZ_VAL, }, - { 380, ST_GYRO_2_ODR_AVL_380HZ_VAL, }, - { 760, ST_GYRO_2_ODR_AVL_760HZ_VAL, }, + { .hz = 95, .value = 0x00, }, + { .hz = 190, .value = 0x01, }, + { .hz = 380, .value = 0x02, }, + { .hz = 760, .value = 0x03, }, }, }, .pw = { - .addr = ST_GYRO_2_PW_ADDR, - .mask = ST_GYRO_2_PW_MASK, + .addr = 0x20, + .mask = 0x08, .value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE, .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, }, @@ -228,33 +155,33 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = { .mask = ST_SENSORS_DEFAULT_AXIS_MASK, }, .fs = { - .addr = ST_GYRO_2_FS_ADDR, - .mask = ST_GYRO_2_FS_MASK, + .addr = 0x23, + .mask = 0x30, .fs_avl = { [0] = { .num = ST_GYRO_FS_AVL_250DPS, - .value = ST_GYRO_2_FS_AVL_250_VAL, - .gain = ST_GYRO_2_FS_AVL_250_GAIN, + .value = 0x00, + .gain = IIO_DEGREE_TO_RAD(8750), }, [1] = { .num = ST_GYRO_FS_AVL_500DPS, - .value = ST_GYRO_2_FS_AVL_500_VAL, - .gain = ST_GYRO_2_FS_AVL_500_GAIN, + .value = 0x01, + .gain = IIO_DEGREE_TO_RAD(17500), }, [2] = { .num = ST_GYRO_FS_AVL_2000DPS, - .value = ST_GYRO_2_FS_AVL_2000_VAL, - .gain = ST_GYRO_2_FS_AVL_2000_GAIN, + .value = 0x02, + .gain = IIO_DEGREE_TO_RAD(70000), }, }, }, .bdu = { - .addr = ST_GYRO_2_BDU_ADDR, - .mask = ST_GYRO_2_BDU_MASK, + .addr = 0x23, + .mask = 0x80, }, .drdy_irq = { - .addr = ST_GYRO_2_DRDY_IRQ_ADDR, - .mask_int2 = ST_GYRO_2_DRDY_IRQ_INT2_MASK, + .addr = 0x22, + .mask_int2 = 0x08, /* * The sensor has IHL (active low) and open * drain settings, but only for INT1 and not @@ -262,29 +189,29 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = { */ .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, }, - .multi_read_bit = ST_GYRO_2_MULTIREAD_BIT, + .multi_read_bit = true, .bootime = 2, }, { - .wai = ST_GYRO_3_WAI_EXP, + .wai = 0xd7, .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS, .sensors_supported = { [0] = L3GD20_GYRO_DEV_NAME, }, .ch = (struct iio_chan_spec *)st_gyro_16bit_channels, .odr = { - .addr = ST_GYRO_3_ODR_ADDR, - .mask = ST_GYRO_3_ODR_MASK, + .addr = 0x20, + .mask = 0xc0, .odr_avl = { - { 95, ST_GYRO_3_ODR_AVL_95HZ_VAL, }, - { 190, ST_GYRO_3_ODR_AVL_190HZ_VAL, }, - { 380, ST_GYRO_3_ODR_AVL_380HZ_VAL, }, - { 760, ST_GYRO_3_ODR_AVL_760HZ_VAL, }, + { .hz = 95, .value = 0x00, }, + { .hz = 190, .value = 0x01, }, + { .hz = 380, .value = 0x02, }, + { .hz = 760, .value = 0x03, }, }, }, .pw = { - .addr = ST_GYRO_3_PW_ADDR, - .mask = ST_GYRO_3_PW_MASK, + .addr = 0x20, + .mask = 0x08, .value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE, .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, }, @@ -293,33 +220,33 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = { .mask = ST_SENSORS_DEFAULT_AXIS_MASK, }, .fs = { - .addr = ST_GYRO_3_FS_ADDR, - .mask = ST_GYRO_3_FS_MASK, + .addr = 0x23, + .mask = 0x30, .fs_avl = { [0] = { .num = ST_GYRO_FS_AVL_250DPS, - .value = ST_GYRO_3_FS_AVL_250_VAL, - .gain = ST_GYRO_3_FS_AVL_250_GAIN, + .value = 0x00, + .gain = IIO_DEGREE_TO_RAD(8750), }, [1] = { .num = ST_GYRO_FS_AVL_500DPS, - .value = ST_GYRO_3_FS_AVL_500_VAL, - .gain = ST_GYRO_3_FS_AVL_500_GAIN, + .value = 0x01, + .gain = IIO_DEGREE_TO_RAD(17500), }, [2] = { .num = ST_GYRO_FS_AVL_2000DPS, - .value = ST_GYRO_3_FS_AVL_2000_VAL, - .gain = ST_GYRO_3_FS_AVL_2000_GAIN, + .value = 0x02, + .gain = IIO_DEGREE_TO_RAD(70000), }, }, }, .bdu = { - .addr = ST_GYRO_3_BDU_ADDR, - .mask = ST_GYRO_3_BDU_MASK, + .addr = 0x23, + .mask = 0x80, }, .drdy_irq = { - .addr = ST_GYRO_3_DRDY_IRQ_ADDR, - .mask_int2 = ST_GYRO_3_DRDY_IRQ_INT2_MASK, + .addr = 0x22, + .mask_int2 = 0x08, /* * The sensor has IHL (active low) and open * drain settings, but only for INT1 and not @@ -327,7 +254,7 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = { */ .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, }, - .multi_read_bit = ST_GYRO_3_MULTIREAD_BIT, + .multi_read_bit = true, .bootime = 2, }, }; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c index 19580d1db597..2c3f8964a3ea 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -126,7 +126,7 @@ static int inv_mpu_probe(struct i2c_client *client, st = iio_priv(dev_get_drvdata(&client->dev)); st->muxc = i2c_mux_alloc(client->adapter, &client->dev, - 1, 0, I2C_MUX_LOCKED, + 1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE, inv_mpu6050_select_bypass, inv_mpu6050_deselect_bypass); if (!st->muxc) { diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 649725bc15c1..aaca42862389 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -577,9 +577,62 @@ int of_iio_read_mount_matrix(const struct device *dev, #endif EXPORT_SYMBOL(of_iio_read_mount_matrix); +static ssize_t __iio_format_value(char *buf, size_t len, unsigned int type, + int size, const int *vals) +{ + unsigned long long tmp; + int tmp0, tmp1; + bool scale_db = false; + + switch (type) { + case IIO_VAL_INT: + return snprintf(buf, len, "%d", vals[0]); + case IIO_VAL_INT_PLUS_MICRO_DB: + scale_db = true; + case IIO_VAL_INT_PLUS_MICRO: + if (vals[1] < 0) + return snprintf(buf, len, "-%d.%06u%s", abs(vals[0]), + -vals[1], scale_db ? " dB" : ""); + else + return snprintf(buf, len, "%d.%06u%s", vals[0], vals[1], + scale_db ? " dB" : ""); + case IIO_VAL_INT_PLUS_NANO: + if (vals[1] < 0) + return snprintf(buf, len, "-%d.%09u", abs(vals[0]), + -vals[1]); + else + return snprintf(buf, len, "%d.%09u", vals[0], vals[1]); + case IIO_VAL_FRACTIONAL: + tmp = div_s64((s64)vals[0] * 1000000000LL, vals[1]); + tmp1 = vals[1]; + tmp0 = (int)div_s64_rem(tmp, 1000000000, &tmp1); + return snprintf(buf, len, "%d.%09u", tmp0, abs(tmp1)); + case IIO_VAL_FRACTIONAL_LOG2: + tmp = (s64)vals[0] * 1000000000LL >> vals[1]; + tmp1 = do_div(tmp, 1000000000LL); + tmp0 = tmp; + return snprintf(buf, len, "%d.%09u", tmp0, tmp1); + case IIO_VAL_INT_MULTIPLE: + { + int i; + int l = 0; + + for (i = 0; i < size; ++i) { + l += snprintf(&buf[l], len - l, "%d ", vals[i]); + if (l >= len) + break; + } + return l; + } + default: + return 0; + } +} + /** * iio_format_value() - Formats a IIO value into its string representation * @buf: The buffer to which the formatted value gets written + * which is assumed to be big enough (i.e. PAGE_SIZE). * @type: One of the IIO_VAL_... constants. This decides how the val * and val2 parameters are formatted. * @size: Number of IIO value entries contained in vals @@ -592,50 +645,13 @@ EXPORT_SYMBOL(of_iio_read_mount_matrix); */ ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals) { - unsigned long long tmp; - bool scale_db = false; + ssize_t len; - switch (type) { - case IIO_VAL_INT: - return sprintf(buf, "%d\n", vals[0]); - case IIO_VAL_INT_PLUS_MICRO_DB: - scale_db = true; - case IIO_VAL_INT_PLUS_MICRO: - if (vals[1] < 0) - return sprintf(buf, "-%d.%06u%s\n", abs(vals[0]), - -vals[1], scale_db ? " dB" : ""); - else - return sprintf(buf, "%d.%06u%s\n", vals[0], vals[1], - scale_db ? " dB" : ""); - case IIO_VAL_INT_PLUS_NANO: - if (vals[1] < 0) - return sprintf(buf, "-%d.%09u\n", abs(vals[0]), - -vals[1]); - else - return sprintf(buf, "%d.%09u\n", vals[0], vals[1]); - case IIO_VAL_FRACTIONAL: - tmp = div_s64((s64)vals[0] * 1000000000LL, vals[1]); - vals[0] = (int)div_s64_rem(tmp, 1000000000, &vals[1]); - return sprintf(buf, "%d.%09u\n", vals[0], abs(vals[1])); - case IIO_VAL_FRACTIONAL_LOG2: - tmp = (s64)vals[0] * 1000000000LL >> vals[1]; - vals[1] = do_div(tmp, 1000000000LL); - vals[0] = tmp; - return sprintf(buf, "%d.%09u\n", vals[0], vals[1]); - case IIO_VAL_INT_MULTIPLE: - { - int i; - int len = 0; + len = __iio_format_value(buf, PAGE_SIZE, type, size, vals); + if (len >= PAGE_SIZE - 1) + return -EFBIG; - for (i = 0; i < size; ++i) - len += snprintf(&buf[len], PAGE_SIZE - len, "%d ", - vals[i]); - len += snprintf(&buf[len], PAGE_SIZE - len, "\n"); - return len; - } - default: - return 0; - } + return len + sprintf(buf + len, "\n"); } EXPORT_SYMBOL_GPL(iio_format_value); @@ -664,6 +680,119 @@ static ssize_t iio_read_channel_info(struct device *dev, return iio_format_value(buf, ret, val_len, vals); } +static ssize_t iio_format_avail_list(char *buf, const int *vals, + int type, int length) +{ + int i; + ssize_t len = 0; + + switch (type) { + case IIO_VAL_INT: + for (i = 0; i < length; i++) { + len += __iio_format_value(buf + len, PAGE_SIZE - len, + type, 1, &vals[i]); + if (len >= PAGE_SIZE) + return -EFBIG; + if (i < length - 1) + len += snprintf(buf + len, PAGE_SIZE - len, + " "); + else + len += snprintf(buf + len, PAGE_SIZE - len, + "\n"); + if (len >= PAGE_SIZE) + return -EFBIG; + } + break; + default: + for (i = 0; i < length / 2; i++) { + len += __iio_format_value(buf + len, PAGE_SIZE - len, + type, 2, &vals[i * 2]); + if (len >= PAGE_SIZE) + return -EFBIG; + if (i < length / 2 - 1) + len += snprintf(buf + len, PAGE_SIZE - len, + " "); + else + len += snprintf(buf + len, PAGE_SIZE - len, + "\n"); + if (len >= PAGE_SIZE) + return -EFBIG; + } + } + + return len; +} + +static ssize_t iio_format_avail_range(char *buf, const int *vals, int type) +{ + int i; + ssize_t len; + + len = snprintf(buf, PAGE_SIZE, "["); + switch (type) { + case IIO_VAL_INT: + for (i = 0; i < 3; i++) { + len += __iio_format_value(buf + len, PAGE_SIZE - len, + type, 1, &vals[i]); + if (len >= PAGE_SIZE) + return -EFBIG; + if (i < 2) + len += snprintf(buf + len, PAGE_SIZE - len, + " "); + else + len += snprintf(buf + len, PAGE_SIZE - len, + "]\n"); + if (len >= PAGE_SIZE) + return -EFBIG; + } + break; + default: + for (i = 0; i < 3; i++) { + len += __iio_format_value(buf + len, PAGE_SIZE - len, + type, 2, &vals[i * 2]); + if (len >= PAGE_SIZE) + return -EFBIG; + if (i < 2) + len += snprintf(buf + len, PAGE_SIZE - len, + " "); + else + len += snprintf(buf + len, PAGE_SIZE - len, + "]\n"); + if (len >= PAGE_SIZE) + return -EFBIG; + } + } + + return len; +} + +static ssize_t iio_read_channel_info_avail(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + const int *vals; + int ret; + int length; + int type; + + ret = indio_dev->info->read_avail(indio_dev, this_attr->c, + &vals, &type, &length, + this_attr->address); + + if (ret < 0) + return ret; + switch (ret) { + case IIO_AVAIL_LIST: + return iio_format_avail_list(buf, vals, type, length); + case IIO_AVAIL_RANGE: + return iio_format_avail_range(buf, vals, type); + default: + return -EINVAL; + } +} + /** * iio_str_to_fixpoint() - Parse a fixed-point number from a string * @str: The string to parse @@ -980,6 +1109,40 @@ static int iio_device_add_info_mask_type(struct iio_dev *indio_dev, return attrcount; } +static int iio_device_add_info_mask_type_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + enum iio_shared_by shared_by, + const long *infomask) +{ + int i, ret, attrcount = 0; + char *avail_postfix; + + for_each_set_bit(i, infomask, sizeof(infomask) * 8) { + avail_postfix = kasprintf(GFP_KERNEL, + "%s_available", + iio_chan_info_postfix[i]); + if (!avail_postfix) + return -ENOMEM; + + ret = __iio_add_chan_devattr(avail_postfix, + chan, + &iio_read_channel_info_avail, + NULL, + i, + shared_by, + &indio_dev->dev, + &indio_dev->channel_attr_list); + kfree(avail_postfix); + if ((ret == -EBUSY) && (shared_by != IIO_SEPARATE)) + continue; + else if (ret < 0) + return ret; + attrcount++; + } + + return attrcount; +} + static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev, struct iio_chan_spec const *chan) { @@ -995,6 +1158,14 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev, return ret; attrcount += ret; + ret = iio_device_add_info_mask_type_avail(indio_dev, chan, + IIO_SEPARATE, + &chan-> + info_mask_separate_available); + if (ret < 0) + return ret; + attrcount += ret; + ret = iio_device_add_info_mask_type(indio_dev, chan, IIO_SHARED_BY_TYPE, &chan->info_mask_shared_by_type); @@ -1002,6 +1173,14 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev, return ret; attrcount += ret; + ret = iio_device_add_info_mask_type_avail(indio_dev, chan, + IIO_SHARED_BY_TYPE, + &chan-> + info_mask_shared_by_type_available); + if (ret < 0) + return ret; + attrcount += ret; + ret = iio_device_add_info_mask_type(indio_dev, chan, IIO_SHARED_BY_DIR, &chan->info_mask_shared_by_dir); @@ -1009,6 +1188,13 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev, return ret; attrcount += ret; + ret = iio_device_add_info_mask_type_avail(indio_dev, chan, + IIO_SHARED_BY_DIR, + &chan->info_mask_shared_by_dir_available); + if (ret < 0) + return ret; + attrcount += ret; + ret = iio_device_add_info_mask_type(indio_dev, chan, IIO_SHARED_BY_ALL, &chan->info_mask_shared_by_all); @@ -1016,6 +1202,13 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev, return ret; attrcount += ret; + ret = iio_device_add_info_mask_type_avail(indio_dev, chan, + IIO_SHARED_BY_ALL, + &chan->info_mask_shared_by_all_available); + if (ret < 0) + return ret; + attrcount += ret; + if (chan->ext_info) { unsigned int i = 0; for (ext_info = chan->ext_info; ext_info->name; ext_info++) { diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 29df11572858..b0f4630a163f 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -716,6 +716,110 @@ int iio_read_channel_scale(struct iio_channel *chan, int *val, int *val2) } EXPORT_SYMBOL_GPL(iio_read_channel_scale); +static int iio_channel_read_avail(struct iio_channel *chan, + const int **vals, int *type, int *length, + enum iio_chan_info_enum info) +{ + if (!iio_channel_has_available(chan->channel, info)) + return -EINVAL; + + return chan->indio_dev->info->read_avail(chan->indio_dev, chan->channel, + vals, type, length, info); +} + +int iio_read_avail_channel_raw(struct iio_channel *chan, + const int **vals, int *length) +{ + int ret; + int type; + + mutex_lock(&chan->indio_dev->info_exist_lock); + if (!chan->indio_dev->info) { + ret = -ENODEV; + goto err_unlock; + } + + ret = iio_channel_read_avail(chan, + vals, &type, length, IIO_CHAN_INFO_RAW); +err_unlock: + mutex_unlock(&chan->indio_dev->info_exist_lock); + + if (ret >= 0 && type != IIO_VAL_INT) { + /* raw values are assumed to be IIO_VAL_INT */ + ret = -EINVAL; + goto err_unlock; + } + + return ret; +} +EXPORT_SYMBOL_GPL(iio_read_avail_channel_raw); + +static int iio_channel_read_max(struct iio_channel *chan, + int *val, int *val2, int *type, + enum iio_chan_info_enum info) +{ + int unused; + const int *vals; + int length; + int ret; + + if (!val2) + val2 = &unused; + + ret = iio_channel_read_avail(chan, &vals, type, &length, info); + switch (ret) { + case IIO_AVAIL_RANGE: + switch (*type) { + case IIO_VAL_INT: + *val = vals[2]; + break; + default: + *val = vals[4]; + *val2 = vals[5]; + } + return 0; + + case IIO_AVAIL_LIST: + if (length <= 0) + return -EINVAL; + switch (*type) { + case IIO_VAL_INT: + *val = vals[--length]; + while (length) { + if (vals[--length] > *val) + *val = vals[length]; + } + break; + default: + /* FIXME: learn about max for other iio values */ + return -EINVAL; + } + return 0; + + default: + return ret; + } +} + +int iio_read_max_channel_raw(struct iio_channel *chan, int *val) +{ + int ret; + int type; + + mutex_lock(&chan->indio_dev->info_exist_lock); + if (!chan->indio_dev->info) { + ret = -ENODEV; + goto err_unlock; + } + + ret = iio_channel_read_max(chan, val, NULL, &type, IIO_CHAN_INFO_RAW); +err_unlock: + mutex_unlock(&chan->indio_dev->info_exist_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(iio_read_max_channel_raw); + int iio_get_channel_type(struct iio_channel *chan, enum iio_chan_type *type) { int ret = 0; diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index d01172089828..298ea5081a96 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -338,6 +338,13 @@ config SENSORS_TSL2563 This driver can also be built as a module. If so, the module will be called tsl2563. +config TSL2583 + tristate "TAOS TSL2580, TSL2581 and TSL2583 light-to-digital converters" + depends on I2C + help + Provides support for the TAOS tsl2580, tsl2581 and tsl2583 devices. + Access ALS data via iio, sysfs. + config TSL4531 tristate "TAOS TSL4531 ambient light sensors" depends on I2C diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index 15f24c557f5f..4de520036e6e 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_SI1145) += si1145.o obj-$(CONFIG_STK3310) += stk3310.o obj-$(CONFIG_TCS3414) += tcs3414.o obj-$(CONFIG_TCS3472) += tcs3472.o +obj-$(CONFIG_TSL2583) += tsl2583.o obj-$(CONFIG_TSL4531) += tsl4531.o obj-$(CONFIG_US5182D) += us5182d.o obj-$(CONFIG_VCNL4000) += vcnl4000.o diff --git a/drivers/staging/iio/light/tsl2583.c b/drivers/iio/light/tsl2583.c similarity index 59% rename from drivers/staging/iio/light/tsl2583.c rename to drivers/iio/light/tsl2583.c index 7eab17f4557e..0b87f6adbb79 100644 --- a/drivers/staging/iio/light/tsl2583.c +++ b/drivers/iio/light/tsl2583.c @@ -1,8 +1,9 @@ /* * Device driver for monitoring ambient light intensity (lux) - * within the TAOS tsl258x family of devices (tsl2580, tsl2581). + * within the TAOS tsl258x family of devices (tsl2580, tsl2581, tsl2583). * * Copyright (c) 2011, TAOS Corporation. + * Copyright (c) 2016 Brian Masney * * 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 @@ -13,10 +14,6 @@ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include @@ -31,96 +28,87 @@ #include #include -/* Triton register offsets */ -#define TSL258X_REG_MAX 8 - /* Device Registers and Masks */ -#define TSL258X_CNTRL 0x00 -#define TSL258X_ALS_TIME 0X01 -#define TSL258X_INTERRUPT 0x02 -#define TSL258X_GAIN 0x07 -#define TSL258X_REVID 0x11 -#define TSL258X_CHIPID 0x12 -#define TSL258X_ALS_CHAN0LO 0x14 -#define TSL258X_ALS_CHAN0HI 0x15 -#define TSL258X_ALS_CHAN1LO 0x16 -#define TSL258X_ALS_CHAN1HI 0x17 -#define TSL258X_TMR_LO 0x18 -#define TSL258X_TMR_HI 0x19 +#define TSL2583_CNTRL 0x00 +#define TSL2583_ALS_TIME 0X01 +#define TSL2583_INTERRUPT 0x02 +#define TSL2583_GAIN 0x07 +#define TSL2583_REVID 0x11 +#define TSL2583_CHIPID 0x12 +#define TSL2583_ALS_CHAN0LO 0x14 +#define TSL2583_ALS_CHAN0HI 0x15 +#define TSL2583_ALS_CHAN1LO 0x16 +#define TSL2583_ALS_CHAN1HI 0x17 +#define TSL2583_TMR_LO 0x18 +#define TSL2583_TMR_HI 0x19 /* tsl2583 cmd reg masks */ -#define TSL258X_CMD_REG 0x80 -#define TSL258X_CMD_SPL_FN 0x60 -#define TSL258X_CMD_ALS_INT_CLR 0X01 +#define TSL2583_CMD_REG 0x80 +#define TSL2583_CMD_SPL_FN 0x60 +#define TSL2583_CMD_ALS_INT_CLR 0x01 /* tsl2583 cntrl reg masks */ -#define TSL258X_CNTL_ADC_ENBL 0x02 -#define TSL258X_CNTL_PWR_ON 0x01 +#define TSL2583_CNTL_ADC_ENBL 0x02 +#define TSL2583_CNTL_PWR_OFF 0x00 +#define TSL2583_CNTL_PWR_ON 0x01 /* tsl2583 status reg masks */ -#define TSL258X_STA_ADC_VALID 0x01 -#define TSL258X_STA_ADC_INTR 0x10 +#define TSL2583_STA_ADC_VALID 0x01 +#define TSL2583_STA_ADC_INTR 0x10 /* Lux calculation constants */ -#define TSL258X_LUX_CALC_OVER_FLOW 65535 +#define TSL2583_LUX_CALC_OVER_FLOW 65535 + +#define TSL2583_INTERRUPT_DISABLED 0x00 #define TSL2583_CHIP_ID 0x90 #define TSL2583_CHIP_ID_MASK 0xf0 -enum { - TSL258X_CHIP_UNKNOWN = 0, - TSL258X_CHIP_WORKING = 1, - TSL258X_CHIP_SUSPENDED = 2 -}; - /* Per-device data */ -struct taos_als_info { +struct tsl2583_als_info { u16 als_ch0; u16 als_ch1; u16 lux; }; -struct taos_settings { - int als_time; - int als_gain; - int als_gain_trim; - int als_cal_target; -}; - -struct tsl2583_chip { - struct mutex als_mutex; - struct i2c_client *client; - struct taos_als_info als_cur_info; - struct taos_settings taos_settings; - int als_time_scale; - int als_saturation; - int taos_chip_status; - u8 taos_config[8]; -}; - -/* - * Initial values for device - this values can/will be changed by driver. - * and applications as needed. - * These values are dynamic. - */ -static const u8 taos_config[8] = { - 0x00, 0xee, 0x00, 0x03, 0x00, 0xFF, 0xFF, 0x00 -}; /* cntrl atime intC Athl0 Athl1 Athh0 Athh1 gain */ - -struct taos_lux { +struct tsl2583_lux { unsigned int ratio; unsigned int ch0; unsigned int ch1; }; -/* This structure is intentionally large to accommodate updates via sysfs. */ -/* Sized to 11 = max 10 segments + 1 termination segment */ -/* Assumption is one and only one type of glass used */ -static struct taos_lux taos_device_lux[11] = { +static const struct tsl2583_lux tsl2583_default_lux[] = { { 9830, 8520, 15729 }, { 12452, 10807, 23344 }, { 14746, 6383, 11705 }, { 17695, 4063, 6554 }, + { 0, 0, 0 } /* Termination segment */ +}; + +#define TSL2583_MAX_LUX_TABLE_ENTRIES 11 + +struct tsl2583_settings { + int als_time; + int als_gain; + int als_gain_trim; + int als_cal_target; + + /* + * This structure is intentionally large to accommodate updates via + * sysfs. Sized to 11 = max 10 segments + 1 termination segment. + * Assumption is that one and only one type of glass used. + */ + struct tsl2583_lux als_device_lux[TSL2583_MAX_LUX_TABLE_ENTRIES]; +}; + +struct tsl2583_chip { + struct mutex als_mutex; + struct i2c_client *client; + struct tsl2583_als_info als_cur_info; + struct tsl2583_settings als_settings; + int als_time_scale; + int als_saturation; + bool suspended; }; struct gainadj { @@ -141,25 +129,29 @@ static const struct gainadj gainadj[] = { * Provides initial operational parameter defaults. * These defaults may be changed through the device's sysfs files. */ -static void taos_defaults(struct tsl2583_chip *chip) +static void tsl2583_defaults(struct tsl2583_chip *chip) { /* * The integration time must be a multiple of 50ms and within the * range [50, 600] ms. */ - chip->taos_settings.als_time = 100; + chip->als_settings.als_time = 100; /* * This is an index into the gainadj table. Assume clear glass as the * default. */ - chip->taos_settings.als_gain = 0; + chip->als_settings.als_gain = 0; /* Default gain trim to account for aperture effects */ - chip->taos_settings.als_gain_trim = 1000; + chip->als_settings.als_gain_trim = 1000; /* Known external ALS reading used for calibration */ - chip->taos_settings.als_cal_target = 130; + chip->als_settings.als_cal_target = 130; + + /* Default lux table. */ + memcpy(chip->als_settings.als_device_lux, tsl2583_default_lux, + sizeof(tsl2583_default_lux)); } /* @@ -169,70 +161,61 @@ static void taos_defaults(struct tsl2583_chip *chip) * Time scale factor array values are adjusted based on the integration time. * The raw values are multiplied by a scale factor, and device gain is obtained * using gain index. Limit checks are done next, then the ratio of a multiple - * of ch1 value, to the ch0 value, is calculated. The array taos_device_lux[] + * of ch1 value, to the ch0 value, is calculated. The array als_device_lux[] * declared above is then scanned to find the first ratio value that is just * above the ratio we just calculated. The ch0 and ch1 multiplier constants in * the array are then used along with the time scale factor array values, to * calculate the lux. */ -static int taos_get_lux(struct iio_dev *indio_dev) +static int tsl2583_get_lux(struct iio_dev *indio_dev) { u16 ch0, ch1; /* separated ch0/ch1 data from device */ u32 lux; /* raw lux calculated from device data */ u64 lux64; u32 ratio; u8 buf[5]; - struct taos_lux *p; + struct tsl2583_lux *p; struct tsl2583_chip *chip = iio_priv(indio_dev); int i, ret; - u32 ch0lux = 0; - u32 ch1lux = 0; - if (chip->taos_chip_status != TSL258X_CHIP_WORKING) { - /* device is not enabled */ - dev_err(&chip->client->dev, "taos_get_lux device is not enabled\n"); - ret = -EBUSY; - goto done; - } - - ret = i2c_smbus_read_byte_data(chip->client, TSL258X_CMD_REG); + ret = i2c_smbus_read_byte_data(chip->client, TSL2583_CMD_REG); if (ret < 0) { - dev_err(&chip->client->dev, "taos_get_lux failed to read CMD_REG\n"); + dev_err(&chip->client->dev, "%s: failed to read CMD_REG register\n", + __func__); goto done; } /* is data new & valid */ - if (!(ret & TSL258X_STA_ADC_INTR)) { - dev_err(&chip->client->dev, "taos_get_lux data not valid\n"); + if (!(ret & TSL2583_STA_ADC_INTR)) { + dev_err(&chip->client->dev, "%s: data not valid; returning last value\n", + __func__); ret = chip->als_cur_info.lux; /* return LAST VALUE */ goto done; } for (i = 0; i < 4; i++) { - int reg = TSL258X_CMD_REG | (TSL258X_ALS_CHAN0LO + i); + int reg = TSL2583_CMD_REG | (TSL2583_ALS_CHAN0LO + i); ret = i2c_smbus_read_byte_data(chip->client, reg); if (ret < 0) { - dev_err(&chip->client->dev, - "taos_get_lux failed to read register %x\n", - reg); + dev_err(&chip->client->dev, "%s: failed to read register %x\n", + __func__, reg); goto done; } buf[i] = ret; } /* - * clear status, really interrupt status (interrupts are off), but - * we use the bit anyway - don't forget 0x80 - this is a command + * Clear the pending interrupt status bit on the chip to allow the next + * integration cycle to start. This has to be done even though this + * driver currently does not support interrupts. */ ret = i2c_smbus_write_byte(chip->client, - (TSL258X_CMD_REG | TSL258X_CMD_SPL_FN | - TSL258X_CMD_ALS_INT_CLR)); - + (TSL2583_CMD_REG | TSL2583_CMD_SPL_FN | + TSL2583_CMD_ALS_INT_CLR)); if (ret < 0) { - dev_err(&chip->client->dev, - "taos_i2c_write_command failed in taos_get_lux, err = %d\n", - ret); + dev_err(&chip->client->dev, "%s: failed to clear the interrupt bit\n", + __func__); goto done; /* have no data, so return failure */ } @@ -247,36 +230,46 @@ static int taos_get_lux(struct iio_dev *indio_dev) goto return_max; if (!ch0) { - /* have no data, so return LAST VALUE */ + /* + * The sensor appears to be in total darkness so set the + * calculated lux to 0 and return early to avoid a division by + * zero below when calculating the ratio. + */ ret = 0; chip->als_cur_info.lux = 0; goto done; } + /* calculate ratio */ ratio = (ch1 << 15) / ch0; + /* convert to unscaled lux using the pointer to the table */ - for (p = (struct taos_lux *)taos_device_lux; + for (p = (struct tsl2583_lux *)chip->als_settings.als_device_lux; p->ratio != 0 && p->ratio < ratio; p++) ; if (p->ratio == 0) { lux = 0; } else { - ch0lux = ((ch0 * p->ch0) + - (gainadj[chip->taos_settings.als_gain].ch0 >> 1)) - / gainadj[chip->taos_settings.als_gain].ch0; - ch1lux = ((ch1 * p->ch1) + - (gainadj[chip->taos_settings.als_gain].ch1 >> 1)) - / gainadj[chip->taos_settings.als_gain].ch1; - lux = ch0lux - ch1lux; - } + u32 ch0lux, ch1lux; - /* note: lux is 31 bit max at this point */ - if (ch1lux > ch0lux) { - dev_dbg(&chip->client->dev, "No Data - Return last value\n"); - ret = 0; - chip->als_cur_info.lux = 0; - goto done; + ch0lux = ((ch0 * p->ch0) + + (gainadj[chip->als_settings.als_gain].ch0 >> 1)) + / gainadj[chip->als_settings.als_gain].ch0; + ch1lux = ((ch1 * p->ch1) + + (gainadj[chip->als_settings.als_gain].ch1 >> 1)) + / gainadj[chip->als_settings.als_gain].ch1; + + /* note: lux is 31 bit max at this point */ + if (ch1lux > ch0lux) { + dev_dbg(&chip->client->dev, "%s: No Data - Returning 0\n", + __func__); + ret = 0; + chip->als_cur_info.lux = 0; + goto done; + } + + lux = ch0lux - ch1lux; } /* adjust for active time scale */ @@ -286,8 +279,9 @@ static int taos_get_lux(struct iio_dev *indio_dev) lux = (lux + (chip->als_time_scale >> 1)) / chip->als_time_scale; - /* Adjust for active gain scale. - * The taos_device_lux tables above have a factor of 8192 built in, + /* + * Adjust for active gain scale. + * The tsl2583_default_lux tables above have a factor of 8192 built in, * so we need to shift right. * User-specified gain provides a multiplier. * Apply user-specified gain before shifting right to retain precision. @@ -295,13 +289,14 @@ static int taos_get_lux(struct iio_dev *indio_dev) * Then go back to 32 bits before division to avoid using div_u64(). */ lux64 = lux; - lux64 = lux64 * chip->taos_settings.als_gain_trim; + lux64 = lux64 * chip->als_settings.als_gain_trim; lux64 >>= 13; lux = lux64; lux = (lux + 500) / 1000; - if (lux > TSL258X_LUX_CALC_OVER_FLOW) { /* check for overflow */ + + if (lux > TSL2583_LUX_CALC_OVER_FLOW) { /* check for overflow */ return_max: - lux = TSL258X_LUX_CALC_OVER_FLOW; + lux = TSL2583_LUX_CALC_OVER_FLOW; } /* Update the structure with the latest VALID lux. */ @@ -317,7 +312,7 @@ done: * to derive actual lux). * Return updated gain_trim value. */ -static int taos_als_calibrate(struct iio_dev *indio_dev) +static int tsl2583_als_calibrate(struct iio_dev *indio_dev) { struct tsl2583_chip *chip = iio_priv(indio_dev); unsigned int gain_trim_val; @@ -325,127 +320,151 @@ static int taos_als_calibrate(struct iio_dev *indio_dev) int lux_val; ret = i2c_smbus_read_byte_data(chip->client, - TSL258X_CMD_REG | TSL258X_CNTRL); + TSL2583_CMD_REG | TSL2583_CNTRL); if (ret < 0) { dev_err(&chip->client->dev, - "%s failed to read from the CNTRL register\n", + "%s: failed to read from the CNTRL register\n", __func__); return ret; } - if ((ret & (TSL258X_CNTL_ADC_ENBL | TSL258X_CNTL_PWR_ON)) - != (TSL258X_CNTL_ADC_ENBL | TSL258X_CNTL_PWR_ON)) { + if ((ret & (TSL2583_CNTL_ADC_ENBL | TSL2583_CNTL_PWR_ON)) + != (TSL2583_CNTL_ADC_ENBL | TSL2583_CNTL_PWR_ON)) { dev_err(&chip->client->dev, - "taos_als_calibrate failed: device not powered on with ADC enabled\n"); + "%s: Device is not powered on and/or ADC is not enabled\n", + __func__); return -EINVAL; - } else if ((ret & TSL258X_STA_ADC_VALID) != TSL258X_STA_ADC_VALID) { + } else if ((ret & TSL2583_STA_ADC_VALID) != TSL2583_STA_ADC_VALID) { dev_err(&chip->client->dev, - "taos_als_calibrate failed: STATUS - ADC not valid.\n"); + "%s: The two ADC channels have not completed an integration cycle\n", + __func__); return -ENODATA; } - lux_val = taos_get_lux(indio_dev); + + lux_val = tsl2583_get_lux(indio_dev); if (lux_val < 0) { - dev_err(&chip->client->dev, "taos_als_calibrate failed to get lux\n"); + dev_err(&chip->client->dev, "%s: failed to get lux\n", + __func__); return lux_val; } - gain_trim_val = (unsigned int)(((chip->taos_settings.als_cal_target) - * chip->taos_settings.als_gain_trim) / lux_val); + gain_trim_val = (unsigned int)(((chip->als_settings.als_cal_target) + * chip->als_settings.als_gain_trim) / lux_val); if ((gain_trim_val < 250) || (gain_trim_val > 4000)) { dev_err(&chip->client->dev, - "taos_als_calibrate failed: trim_val of %d is out of range\n", - gain_trim_val); + "%s: trim_val of %d is not within the range [250, 4000]\n", + __func__, gain_trim_val); return -ENODATA; } - chip->taos_settings.als_gain_trim = (int)gain_trim_val; - return (int)gain_trim_val; + chip->als_settings.als_gain_trim = (int)gain_trim_val; + + return 0; +} + +static int tsl2583_set_als_time(struct tsl2583_chip *chip) +{ + int als_count, als_time, ret; + u8 val; + + /* determine als integration register */ + als_count = (chip->als_settings.als_time * 100 + 135) / 270; + if (!als_count) + als_count = 1; /* ensure at least one cycle */ + + /* convert back to time (encompasses overrides) */ + als_time = (als_count * 27 + 5) / 10; + + val = 256 - als_count; + ret = i2c_smbus_write_byte_data(chip->client, + TSL2583_CMD_REG | TSL2583_ALS_TIME, + val); + if (ret < 0) { + dev_err(&chip->client->dev, "%s: failed to set the als time to %d\n", + __func__, val); + return ret; + } + + /* set chip struct re scaling and saturation */ + chip->als_saturation = als_count * 922; /* 90% of full scale */ + chip->als_time_scale = (als_time + 25) / 50; + + return ret; +} + +static int tsl2583_set_als_gain(struct tsl2583_chip *chip) +{ + int ret; + + /* Set the gain based on als_settings struct */ + ret = i2c_smbus_write_byte_data(chip->client, + TSL2583_CMD_REG | TSL2583_GAIN, + chip->als_settings.als_gain); + if (ret < 0) + dev_err(&chip->client->dev, + "%s: failed to set the gain to %d\n", __func__, + chip->als_settings.als_gain); + + return ret; +} + +static int tsl2583_set_power_state(struct tsl2583_chip *chip, u8 state) +{ + int ret; + + ret = i2c_smbus_write_byte_data(chip->client, + TSL2583_CMD_REG | TSL2583_CNTRL, state); + if (ret < 0) + dev_err(&chip->client->dev, + "%s: failed to set the power state to %d\n", __func__, + state); + + return ret; } /* * Turn the device on. * Configuration must be set before calling this function. */ -static int taos_chip_on(struct iio_dev *indio_dev) +static int tsl2583_chip_init_and_power_on(struct iio_dev *indio_dev) { - int i; - int ret; - u8 *uP; - u8 utmp; - int als_count; - int als_time; struct tsl2583_chip *chip = iio_priv(indio_dev); - - /* and make sure we're not already on */ - if (chip->taos_chip_status == TSL258X_CHIP_WORKING) { - /* if forcing a register update - turn off, then on */ - dev_info(&chip->client->dev, "device is already enabled\n"); - return -EINVAL; - } - - /* determine als integration register */ - als_count = (chip->taos_settings.als_time * 100 + 135) / 270; - if (!als_count) - als_count = 1; /* ensure at least one cycle */ - - /* convert back to time (encompasses overrides) */ - als_time = (als_count * 27 + 5) / 10; - chip->taos_config[TSL258X_ALS_TIME] = 256 - als_count; - - /* Set the gain based on taos_settings struct */ - chip->taos_config[TSL258X_GAIN] = chip->taos_settings.als_gain; - - /* set chip struct re scaling and saturation */ - chip->als_saturation = als_count * 922; /* 90% of full scale */ - chip->als_time_scale = (als_time + 25) / 50; + int ret; /* Power on the device; ADC off. */ - chip->taos_config[TSL258X_CNTRL] = TSL258X_CNTL_PWR_ON; + ret = tsl2583_set_power_state(chip, TSL2583_CNTL_PWR_ON); + if (ret < 0) + return ret; - /* - * Use the following shadow copy for our delay before enabling ADC. - * Write all the registers. - */ - for (i = 0, uP = chip->taos_config; i < TSL258X_REG_MAX; i++) { - ret = i2c_smbus_write_byte_data(chip->client, - TSL258X_CMD_REG + i, - *uP++); - if (ret < 0) { - dev_err(&chip->client->dev, - "taos_chip_on failed on reg %d.\n", i); - return ret; - } - } - - usleep_range(3000, 3500); - /* - * NOW enable the ADC - * initialize the desired mode of operation - */ - utmp = TSL258X_CNTL_PWR_ON | TSL258X_CNTL_ADC_ENBL; ret = i2c_smbus_write_byte_data(chip->client, - TSL258X_CMD_REG | TSL258X_CNTRL, - utmp); + TSL2583_CMD_REG | TSL2583_INTERRUPT, + TSL2583_INTERRUPT_DISABLED); if (ret < 0) { - dev_err(&chip->client->dev, "taos_chip_on failed on 2nd CTRL reg.\n"); + dev_err(&chip->client->dev, + "%s: failed to disable interrupts\n", __func__); return ret; } - chip->taos_chip_status = TSL258X_CHIP_WORKING; + + ret = tsl2583_set_als_time(chip); + if (ret < 0) + return ret; + + ret = tsl2583_set_als_gain(chip); + if (ret < 0) + return ret; + + usleep_range(3000, 3500); + + ret = tsl2583_set_power_state(chip, TSL2583_CNTL_PWR_ON | + TSL2583_CNTL_ADC_ENBL); + if (ret < 0) + return ret; + + chip->suspended = false; return ret; } -static int taos_chip_off(struct iio_dev *indio_dev) -{ - struct tsl2583_chip *chip = iio_priv(indio_dev); - - /* turn device off */ - chip->taos_chip_status = TSL258X_CHIP_SUSPENDED; - return i2c_smbus_write_byte_data(chip->client, - TSL258X_CMD_REG | TSL258X_CNTRL, - 0x00); -} - /* Sysfs Interface Functions */ static ssize_t in_illuminance_input_target_show(struct device *dev, @@ -457,7 +476,7 @@ static ssize_t in_illuminance_input_target_show(struct device *dev, int ret; mutex_lock(&chip->als_mutex); - ret = sprintf(buf, "%d\n", chip->taos_settings.als_cal_target); + ret = sprintf(buf, "%d\n", chip->als_settings.als_cal_target); mutex_unlock(&chip->als_mutex); return ret; @@ -475,7 +494,7 @@ static ssize_t in_illuminance_input_target_store(struct device *dev, return -EINVAL; mutex_lock(&chip->als_mutex); - chip->taos_settings.als_cal_target = value; + chip->als_settings.als_cal_target = value; mutex_unlock(&chip->als_mutex); return len; @@ -487,31 +506,44 @@ static ssize_t in_illuminance_calibrate_store(struct device *dev, { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct tsl2583_chip *chip = iio_priv(indio_dev); - int value; + int value, ret; if (kstrtoint(buf, 0, &value) || value != 1) return -EINVAL; mutex_lock(&chip->als_mutex); - taos_als_calibrate(indio_dev); + + if (chip->suspended) { + ret = -EBUSY; + goto done; + } + + ret = tsl2583_als_calibrate(indio_dev); + if (ret < 0) + goto done; + + ret = len; +done: mutex_unlock(&chip->als_mutex); - return len; + return ret; } static ssize_t in_illuminance_lux_table_show(struct device *dev, struct device_attribute *attr, char *buf) { - int i; + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct tsl2583_chip *chip = iio_priv(indio_dev); + unsigned int i; int offset = 0; - for (i = 0; i < ARRAY_SIZE(taos_device_lux); i++) { + for (i = 0; i < ARRAY_SIZE(chip->als_settings.als_device_lux); i++) { offset += sprintf(buf + offset, "%u,%u,%u,", - taos_device_lux[i].ratio, - taos_device_lux[i].ch0, - taos_device_lux[i].ch1); - if (taos_device_lux[i].ratio == 0) { + chip->als_settings.als_device_lux[i].ratio, + chip->als_settings.als_device_lux[i].ch0, + chip->als_settings.als_device_lux[i].ch1); + if (chip->als_settings.als_device_lux[i].ratio == 0) { /* * We just printed the first "0" entry. * Now get rid of the extra "," and break. @@ -522,6 +554,7 @@ static ssize_t in_illuminance_lux_table_show(struct device *dev, } offset += sprintf(buf + offset, "\n"); + return offset; } @@ -531,31 +564,36 @@ static ssize_t in_illuminance_lux_table_store(struct device *dev, { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct tsl2583_chip *chip = iio_priv(indio_dev); - int value[ARRAY_SIZE(taos_device_lux) * 3 + 1]; - int n, ret = -EINVAL; + const unsigned int max_ints = TSL2583_MAX_LUX_TABLE_ENTRIES * 3; + int value[TSL2583_MAX_LUX_TABLE_ENTRIES * 3]; + int ret = -EINVAL; + unsigned int n; mutex_lock(&chip->als_mutex); get_options(buf, ARRAY_SIZE(value), value); - /* We now have an array of ints starting at value[1], and + /* + * We now have an array of ints starting at value[1], and * enumerated by value[0]. * We expect each group of three ints is one table entry, * and the last table entry is all 0. */ n = value[0]; - if ((n % 3) || n < 6 || n > ((ARRAY_SIZE(taos_device_lux) - 1) * 3)) { - dev_info(dev, "LUX TABLE INPUT ERROR 1 Value[0]=%d\n", n); + if ((n % 3) || n < 6 || n > max_ints) { + dev_err(dev, + "%s: The number of entries in the lux table must be a multiple of 3 and within the range [6, %d]\n", + __func__, max_ints); goto done; } - if ((value[(n - 2)] | value[(n - 1)] | value[n]) != 0) { - dev_info(dev, "LUX TABLE INPUT ERROR 2 Value[0]=%d\n", n); + if ((value[n - 2] | value[n - 1] | value[n]) != 0) { + dev_err(dev, "%s: The last 3 entries in the lux table must be zeros.\n", + __func__); goto done; } - /* Zero out the table */ - memset(taos_device_lux, 0, sizeof(taos_device_lux)); - memcpy(taos_device_lux, &value[1], (value[0] * 4)); + memcpy(chip->als_settings.als_device_lux, &value[1], + value[0] * sizeof(value[1])); ret = len; @@ -616,7 +654,7 @@ static int tsl2583_read_raw(struct iio_dev *indio_dev, mutex_lock(&chip->als_mutex); - if (chip->taos_chip_status != TSL258X_CHIP_WORKING) { + if (chip->suspended) { ret = -EBUSY; goto read_done; } @@ -624,7 +662,7 @@ static int tsl2583_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: if (chan->type == IIO_LIGHT) { - ret = taos_get_lux(indio_dev); + ret = tsl2583_get_lux(indio_dev); if (ret < 0) goto read_done; @@ -647,7 +685,7 @@ static int tsl2583_read_raw(struct iio_dev *indio_dev, break; case IIO_CHAN_INFO_PROCESSED: if (chan->type == IIO_LIGHT) { - ret = taos_get_lux(indio_dev); + ret = tsl2583_get_lux(indio_dev); if (ret < 0) goto read_done; @@ -657,20 +695,20 @@ static int tsl2583_read_raw(struct iio_dev *indio_dev, break; case IIO_CHAN_INFO_CALIBBIAS: if (chan->type == IIO_LIGHT) { - *val = chip->taos_settings.als_gain_trim; + *val = chip->als_settings.als_gain_trim; ret = IIO_VAL_INT; } break; case IIO_CHAN_INFO_CALIBSCALE: if (chan->type == IIO_LIGHT) { - *val = gainadj[chip->taos_settings.als_gain].mean; + *val = gainadj[chip->als_settings.als_gain].mean; ret = IIO_VAL_INT; } break; case IIO_CHAN_INFO_INT_TIME: if (chan->type == IIO_LIGHT) { *val = 0; - *val2 = chip->taos_settings.als_time; + *val2 = chip->als_settings.als_time; ret = IIO_VAL_INT_PLUS_MICRO; } break; @@ -693,7 +731,7 @@ static int tsl2583_write_raw(struct iio_dev *indio_dev, mutex_lock(&chip->als_mutex); - if (chip->taos_chip_status != TSL258X_CHIP_WORKING) { + if (chip->suspended) { ret = -EBUSY; goto write_done; } @@ -701,18 +739,18 @@ static int tsl2583_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_CALIBBIAS: if (chan->type == IIO_LIGHT) { - chip->taos_settings.als_gain_trim = val; + chip->als_settings.als_gain_trim = val; ret = 0; } break; case IIO_CHAN_INFO_CALIBSCALE: if (chan->type == IIO_LIGHT) { - int i; + unsigned int i; for (i = 0; i < ARRAY_SIZE(gainadj); i++) { if (gainadj[i].mean == val) { - chip->taos_settings.als_gain = i; - ret = 0; + chip->als_settings.als_gain = i; + ret = tsl2583_set_als_gain(chip); break; } } @@ -721,8 +759,8 @@ static int tsl2583_write_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_INT_TIME: if (chan->type == IIO_LIGHT && !val && val2 >= 50 && val2 <= 650 && !(val2 % 50)) { - chip->taos_settings.als_time = val2; - ret = 0; + chip->als_settings.als_time = val2; + ret = tsl2583_set_als_time(chip); } break; default: @@ -742,12 +780,8 @@ static const struct iio_info tsl2583_info = { .write_raw = tsl2583_write_raw, }; -/* - * Client probe function - When a valid device is found, the driver's device - * data structure is updated, and initialization completes successfully. - */ -static int taos_probe(struct i2c_client *clientp, - const struct i2c_device_id *idp) +static int tsl2583_probe(struct i2c_client *clientp, + const struct i2c_device_id *idp) { int ret; struct tsl2583_chip *chip; @@ -755,138 +789,125 @@ static int taos_probe(struct i2c_client *clientp, if (!i2c_check_functionality(clientp->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { - dev_err(&clientp->dev, "taos_probe() - i2c smbus byte data func unsupported\n"); + dev_err(&clientp->dev, "%s: i2c smbus byte data functionality is unsupported\n", + __func__); return -EOPNOTSUPP; } indio_dev = devm_iio_device_alloc(&clientp->dev, sizeof(*chip)); if (!indio_dev) return -ENOMEM; + chip = iio_priv(indio_dev); chip->client = clientp; i2c_set_clientdata(clientp, indio_dev); mutex_init(&chip->als_mutex); - chip->taos_chip_status = TSL258X_CHIP_UNKNOWN; - memcpy(chip->taos_config, taos_config, sizeof(chip->taos_config)); + chip->suspended = true; ret = i2c_smbus_read_byte_data(clientp, - TSL258X_CMD_REG | TSL258X_CHIPID); + TSL2583_CMD_REG | TSL2583_CHIPID); if (ret < 0) { dev_err(&clientp->dev, - "%s failed to read the chip ID register\n", __func__); + "%s: failed to read the chip ID register\n", __func__); return ret; } if ((ret & TSL2583_CHIP_ID_MASK) != TSL2583_CHIP_ID) { - dev_info(&clientp->dev, "%s received an unknown chip ID %x\n", - __func__, ret); + dev_err(&clientp->dev, "%s: received an unknown chip ID %x\n", + __func__, ret); return -EINVAL; } - ret = i2c_smbus_write_byte(clientp, (TSL258X_CMD_REG | TSL258X_CNTRL)); - if (ret < 0) { - dev_err(&clientp->dev, - "i2c_smbus_write_byte() to cmd reg failed in taos_probe(), err = %d\n", - ret); - return ret; - } - indio_dev->info = &tsl2583_info; indio_dev->channels = tsl2583_channels; indio_dev->num_channels = ARRAY_SIZE(tsl2583_channels); indio_dev->dev.parent = &clientp->dev; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->name = chip->client->name; + ret = devm_iio_device_register(indio_dev->dev.parent, indio_dev); if (ret) { - dev_err(&clientp->dev, "iio registration failed\n"); + dev_err(&clientp->dev, "%s: iio registration failed\n", + __func__); return ret; } /* Load up the V2 defaults (these are hard coded defaults for now) */ - taos_defaults(chip); + tsl2583_defaults(chip); /* Make sure the chip is on */ - ret = taos_chip_on(indio_dev); + ret = tsl2583_chip_init_and_power_on(indio_dev); if (ret < 0) return ret; dev_info(&clientp->dev, "Light sensor found.\n"); + return 0; } -#ifdef CONFIG_PM_SLEEP -static int taos_suspend(struct device *dev) +static int __maybe_unused tsl2583_suspend(struct device *dev) { struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); struct tsl2583_chip *chip = iio_priv(indio_dev); - int ret = 0; + int ret; mutex_lock(&chip->als_mutex); - if (chip->taos_chip_status == TSL258X_CHIP_WORKING) { - ret = taos_chip_off(indio_dev); - chip->taos_chip_status = TSL258X_CHIP_SUSPENDED; - } + ret = tsl2583_set_power_state(chip, TSL2583_CNTL_PWR_OFF); + chip->suspended = true; mutex_unlock(&chip->als_mutex); + return ret; } -static int taos_resume(struct device *dev) +static int __maybe_unused tsl2583_resume(struct device *dev) { struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); struct tsl2583_chip *chip = iio_priv(indio_dev); - int ret = 0; + int ret; mutex_lock(&chip->als_mutex); - if (chip->taos_chip_status == TSL258X_CHIP_SUSPENDED) - ret = taos_chip_on(indio_dev); + ret = tsl2583_chip_init_and_power_on(indio_dev); mutex_unlock(&chip->als_mutex); + return ret; } -static SIMPLE_DEV_PM_OPS(taos_pm_ops, taos_suspend, taos_resume); -#define TAOS_PM_OPS (&taos_pm_ops) -#else -#define TAOS_PM_OPS NULL -#endif +static SIMPLE_DEV_PM_OPS(tsl2583_pm_ops, tsl2583_suspend, tsl2583_resume); -static struct i2c_device_id taos_idtable[] = { +static struct i2c_device_id tsl2583_idtable[] = { { "tsl2580", 0 }, { "tsl2581", 1 }, { "tsl2583", 2 }, {} }; -MODULE_DEVICE_TABLE(i2c, taos_idtable); +MODULE_DEVICE_TABLE(i2c, tsl2583_idtable); -#ifdef CONFIG_OF -static const struct of_device_id taos2583_of_match[] = { +static const struct of_device_id tsl2583_of_match[] = { { .compatible = "amstaos,tsl2580", }, { .compatible = "amstaos,tsl2581", }, { .compatible = "amstaos,tsl2583", }, { }, }; -MODULE_DEVICE_TABLE(of, taos2583_of_match); -#else -#define taos2583_of_match NULL -#endif +MODULE_DEVICE_TABLE(of, tsl2583_of_match); /* Driver definition */ -static struct i2c_driver taos_driver = { +static struct i2c_driver tsl2583_driver = { .driver = { .name = "tsl2583", - .pm = TAOS_PM_OPS, - .of_match_table = taos2583_of_match, + .pm = &tsl2583_pm_ops, + .of_match_table = tsl2583_of_match, }, - .id_table = taos_idtable, - .probe = taos_probe, + .id_table = tsl2583_idtable, + .probe = tsl2583_probe, }; -module_i2c_driver(taos_driver); +module_i2c_driver(tsl2583_driver); -MODULE_AUTHOR("J. August Brenner"); +MODULE_AUTHOR("J. August Brenner "); +MODULE_AUTHOR("Brian Masney "); MODULE_DESCRIPTION("TAOS tsl2583 ambient light sensor driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c index 3e1f06b2224c..8e1b0861fbe4 100644 --- a/drivers/iio/magnetometer/st_magn_core.c +++ b/drivers/iio/magnetometer/st_magn_core.c @@ -46,139 +46,12 @@ #define ST_MAGN_FS_AVL_15000MG 15000 #define ST_MAGN_FS_AVL_16000MG 16000 -/* CUSTOM VALUES FOR SENSOR 0 */ -#define ST_MAGN_0_ODR_ADDR 0x00 -#define ST_MAGN_0_ODR_MASK 0x1c -#define ST_MAGN_0_ODR_AVL_1HZ_VAL 0x00 -#define ST_MAGN_0_ODR_AVL_2HZ_VAL 0x01 -#define ST_MAGN_0_ODR_AVL_3HZ_VAL 0x02 -#define ST_MAGN_0_ODR_AVL_8HZ_VAL 0x03 -#define ST_MAGN_0_ODR_AVL_15HZ_VAL 0x04 -#define ST_MAGN_0_ODR_AVL_30HZ_VAL 0x05 -#define ST_MAGN_0_ODR_AVL_75HZ_VAL 0x06 -#define ST_MAGN_0_ODR_AVL_220HZ_VAL 0x07 -#define ST_MAGN_0_PW_ADDR 0x02 -#define ST_MAGN_0_PW_MASK 0x03 -#define ST_MAGN_0_PW_ON 0x00 -#define ST_MAGN_0_PW_OFF 0x03 -#define ST_MAGN_0_FS_ADDR 0x01 -#define ST_MAGN_0_FS_MASK 0xe0 -#define ST_MAGN_0_FS_AVL_1300_VAL 0x01 -#define ST_MAGN_0_FS_AVL_1900_VAL 0x02 -#define ST_MAGN_0_FS_AVL_2500_VAL 0x03 -#define ST_MAGN_0_FS_AVL_4000_VAL 0x04 -#define ST_MAGN_0_FS_AVL_4700_VAL 0x05 -#define ST_MAGN_0_FS_AVL_5600_VAL 0x06 -#define ST_MAGN_0_FS_AVL_8100_VAL 0x07 -#define ST_MAGN_0_FS_AVL_1300_GAIN_XY 1100 -#define ST_MAGN_0_FS_AVL_1900_GAIN_XY 855 -#define ST_MAGN_0_FS_AVL_2500_GAIN_XY 670 -#define ST_MAGN_0_FS_AVL_4000_GAIN_XY 450 -#define ST_MAGN_0_FS_AVL_4700_GAIN_XY 400 -#define ST_MAGN_0_FS_AVL_5600_GAIN_XY 330 -#define ST_MAGN_0_FS_AVL_8100_GAIN_XY 230 -#define ST_MAGN_0_FS_AVL_1300_GAIN_Z 980 -#define ST_MAGN_0_FS_AVL_1900_GAIN_Z 760 -#define ST_MAGN_0_FS_AVL_2500_GAIN_Z 600 -#define ST_MAGN_0_FS_AVL_4000_GAIN_Z 400 -#define ST_MAGN_0_FS_AVL_4700_GAIN_Z 355 -#define ST_MAGN_0_FS_AVL_5600_GAIN_Z 295 -#define ST_MAGN_0_FS_AVL_8100_GAIN_Z 205 -#define ST_MAGN_0_MULTIREAD_BIT false - -/* CUSTOM VALUES FOR SENSOR 1 */ -#define ST_MAGN_1_WAI_EXP 0x3c -#define ST_MAGN_1_ODR_ADDR 0x00 -#define ST_MAGN_1_ODR_MASK 0x1c -#define ST_MAGN_1_ODR_AVL_1HZ_VAL 0x00 -#define ST_MAGN_1_ODR_AVL_2HZ_VAL 0x01 -#define ST_MAGN_1_ODR_AVL_3HZ_VAL 0x02 -#define ST_MAGN_1_ODR_AVL_8HZ_VAL 0x03 -#define ST_MAGN_1_ODR_AVL_15HZ_VAL 0x04 -#define ST_MAGN_1_ODR_AVL_30HZ_VAL 0x05 -#define ST_MAGN_1_ODR_AVL_75HZ_VAL 0x06 -#define ST_MAGN_1_ODR_AVL_220HZ_VAL 0x07 -#define ST_MAGN_1_PW_ADDR 0x02 -#define ST_MAGN_1_PW_MASK 0x03 -#define ST_MAGN_1_PW_ON 0x00 -#define ST_MAGN_1_PW_OFF 0x03 -#define ST_MAGN_1_FS_ADDR 0x01 -#define ST_MAGN_1_FS_MASK 0xe0 -#define ST_MAGN_1_FS_AVL_1300_VAL 0x01 -#define ST_MAGN_1_FS_AVL_1900_VAL 0x02 -#define ST_MAGN_1_FS_AVL_2500_VAL 0x03 -#define ST_MAGN_1_FS_AVL_4000_VAL 0x04 -#define ST_MAGN_1_FS_AVL_4700_VAL 0x05 -#define ST_MAGN_1_FS_AVL_5600_VAL 0x06 -#define ST_MAGN_1_FS_AVL_8100_VAL 0x07 -#define ST_MAGN_1_FS_AVL_1300_GAIN_XY 909 -#define ST_MAGN_1_FS_AVL_1900_GAIN_XY 1169 -#define ST_MAGN_1_FS_AVL_2500_GAIN_XY 1492 -#define ST_MAGN_1_FS_AVL_4000_GAIN_XY 2222 -#define ST_MAGN_1_FS_AVL_4700_GAIN_XY 2500 -#define ST_MAGN_1_FS_AVL_5600_GAIN_XY 3030 -#define ST_MAGN_1_FS_AVL_8100_GAIN_XY 4347 -#define ST_MAGN_1_FS_AVL_1300_GAIN_Z 1020 -#define ST_MAGN_1_FS_AVL_1900_GAIN_Z 1315 -#define ST_MAGN_1_FS_AVL_2500_GAIN_Z 1666 -#define ST_MAGN_1_FS_AVL_4000_GAIN_Z 2500 -#define ST_MAGN_1_FS_AVL_4700_GAIN_Z 2816 -#define ST_MAGN_1_FS_AVL_5600_GAIN_Z 3389 -#define ST_MAGN_1_FS_AVL_8100_GAIN_Z 4878 -#define ST_MAGN_1_MULTIREAD_BIT false - -/* CUSTOM VALUES FOR SENSOR 2 */ -#define ST_MAGN_2_WAI_EXP 0x3d -#define ST_MAGN_2_ODR_ADDR 0x20 -#define ST_MAGN_2_ODR_MASK 0x1c -#define ST_MAGN_2_ODR_AVL_1HZ_VAL 0x00 -#define ST_MAGN_2_ODR_AVL_2HZ_VAL 0x01 -#define ST_MAGN_2_ODR_AVL_3HZ_VAL 0x02 -#define ST_MAGN_2_ODR_AVL_5HZ_VAL 0x03 -#define ST_MAGN_2_ODR_AVL_10HZ_VAL 0x04 -#define ST_MAGN_2_ODR_AVL_20HZ_VAL 0x05 -#define ST_MAGN_2_ODR_AVL_40HZ_VAL 0x06 -#define ST_MAGN_2_ODR_AVL_80HZ_VAL 0x07 -#define ST_MAGN_2_PW_ADDR 0x22 -#define ST_MAGN_2_PW_MASK 0x03 -#define ST_MAGN_2_PW_ON 0x00 -#define ST_MAGN_2_PW_OFF 0x03 -#define ST_MAGN_2_FS_ADDR 0x21 -#define ST_MAGN_2_FS_MASK 0x60 -#define ST_MAGN_2_FS_AVL_4000_VAL 0x00 -#define ST_MAGN_2_FS_AVL_8000_VAL 0x01 -#define ST_MAGN_2_FS_AVL_12000_VAL 0x02 -#define ST_MAGN_2_FS_AVL_16000_VAL 0x03 -#define ST_MAGN_2_FS_AVL_4000_GAIN 146 -#define ST_MAGN_2_FS_AVL_8000_GAIN 292 -#define ST_MAGN_2_FS_AVL_12000_GAIN 438 -#define ST_MAGN_2_FS_AVL_16000_GAIN 584 -#define ST_MAGN_2_MULTIREAD_BIT false +/* Special L addresses for Sensor 2 */ #define ST_MAGN_2_OUT_X_L_ADDR 0x28 #define ST_MAGN_2_OUT_Y_L_ADDR 0x2a #define ST_MAGN_2_OUT_Z_L_ADDR 0x2c -/* CUSTOM VALUES FOR SENSOR 3 */ -#define ST_MAGN_3_WAI_ADDR 0x4f -#define ST_MAGN_3_WAI_EXP 0x40 -#define ST_MAGN_3_ODR_ADDR 0x60 -#define ST_MAGN_3_ODR_MASK 0x0c -#define ST_MAGN_3_ODR_AVL_10HZ_VAL 0x00 -#define ST_MAGN_3_ODR_AVL_20HZ_VAL 0x01 -#define ST_MAGN_3_ODR_AVL_50HZ_VAL 0x02 -#define ST_MAGN_3_ODR_AVL_100HZ_VAL 0x03 -#define ST_MAGN_3_PW_ADDR 0x60 -#define ST_MAGN_3_PW_MASK 0x03 -#define ST_MAGN_3_PW_ON 0x00 -#define ST_MAGN_3_PW_OFF 0x03 -#define ST_MAGN_3_BDU_ADDR 0x62 -#define ST_MAGN_3_BDU_MASK 0x10 -#define ST_MAGN_3_DRDY_IRQ_ADDR 0x62 -#define ST_MAGN_3_DRDY_INT_MASK 0x01 -#define ST_MAGN_3_IHL_IRQ_ADDR 0x63 -#define ST_MAGN_3_IHL_IRQ_MASK 0x04 -#define ST_MAGN_3_FS_AVL_15000_GAIN 1500 -#define ST_MAGN_3_MULTIREAD_BIT false +/* Special L addresses for sensor 3 */ #define ST_MAGN_3_OUT_X_L_ADDR 0x68 #define ST_MAGN_3_OUT_Y_L_ADDR 0x6a #define ST_MAGN_3_OUT_Z_L_ADDR 0x6c @@ -240,77 +113,78 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = { }, .ch = (struct iio_chan_spec *)st_magn_16bit_channels, .odr = { - .addr = ST_MAGN_0_ODR_ADDR, - .mask = ST_MAGN_0_ODR_MASK, + .addr = 0x00, + .mask = 0x1c, .odr_avl = { - { 1, ST_MAGN_0_ODR_AVL_1HZ_VAL, }, - { 2, ST_MAGN_0_ODR_AVL_2HZ_VAL, }, - { 3, ST_MAGN_0_ODR_AVL_3HZ_VAL, }, - { 8, ST_MAGN_0_ODR_AVL_8HZ_VAL, }, - { 15, ST_MAGN_0_ODR_AVL_15HZ_VAL, }, - { 30, ST_MAGN_0_ODR_AVL_30HZ_VAL, }, - { 75, ST_MAGN_0_ODR_AVL_75HZ_VAL, }, + { .hz = 1, .value = 0x00 }, + { .hz = 2, .value = 0x01 }, + { .hz = 3, .value = 0x02 }, + { .hz = 8, .value = 0x03 }, + { .hz = 15, .value = 0x04 }, + { .hz = 30, .value = 0x05 }, + { .hz = 75, .value = 0x06 }, + /* 220 Hz, 0x07 reportedly exist */ }, }, .pw = { - .addr = ST_MAGN_0_PW_ADDR, - .mask = ST_MAGN_0_PW_MASK, - .value_on = ST_MAGN_0_PW_ON, - .value_off = ST_MAGN_0_PW_OFF, + .addr = 0x02, + .mask = 0x03, + .value_on = 0x00, + .value_off = 0x03, }, .fs = { - .addr = ST_MAGN_0_FS_ADDR, - .mask = ST_MAGN_0_FS_MASK, + .addr = 0x01, + .mask = 0xe0, .fs_avl = { [0] = { .num = ST_MAGN_FS_AVL_1300MG, - .value = ST_MAGN_0_FS_AVL_1300_VAL, - .gain = ST_MAGN_0_FS_AVL_1300_GAIN_XY, - .gain2 = ST_MAGN_0_FS_AVL_1300_GAIN_Z, + .value = 0x01, + .gain = 1100, + .gain2 = 980, }, [1] = { .num = ST_MAGN_FS_AVL_1900MG, - .value = ST_MAGN_0_FS_AVL_1900_VAL, - .gain = ST_MAGN_0_FS_AVL_1900_GAIN_XY, - .gain2 = ST_MAGN_0_FS_AVL_1900_GAIN_Z, + .value = 0x02, + .gain = 855, + .gain2 = 760, }, [2] = { .num = ST_MAGN_FS_AVL_2500MG, - .value = ST_MAGN_0_FS_AVL_2500_VAL, - .gain = ST_MAGN_0_FS_AVL_2500_GAIN_XY, - .gain2 = ST_MAGN_0_FS_AVL_2500_GAIN_Z, + .value = 0x03, + .gain = 670, + .gain2 = 600, }, [3] = { .num = ST_MAGN_FS_AVL_4000MG, - .value = ST_MAGN_0_FS_AVL_4000_VAL, - .gain = ST_MAGN_0_FS_AVL_4000_GAIN_XY, - .gain2 = ST_MAGN_0_FS_AVL_4000_GAIN_Z, + .value = 0x04, + .gain = 450, + .gain2 = 400, }, [4] = { .num = ST_MAGN_FS_AVL_4700MG, - .value = ST_MAGN_0_FS_AVL_4700_VAL, - .gain = ST_MAGN_0_FS_AVL_4700_GAIN_XY, - .gain2 = ST_MAGN_0_FS_AVL_4700_GAIN_Z, + .value = 0x05, + .gain = 400, + .gain2 = 355, }, [5] = { .num = ST_MAGN_FS_AVL_5600MG, - .value = ST_MAGN_0_FS_AVL_5600_VAL, - .gain = ST_MAGN_0_FS_AVL_5600_GAIN_XY, - .gain2 = ST_MAGN_0_FS_AVL_5600_GAIN_Z, + .value = 0x06, + .gain = 330, + .gain2 = 295, }, [6] = { .num = ST_MAGN_FS_AVL_8100MG, - .value = ST_MAGN_0_FS_AVL_8100_VAL, - .gain = ST_MAGN_0_FS_AVL_8100_GAIN_XY, - .gain2 = ST_MAGN_0_FS_AVL_8100_GAIN_Z, + .value = 0x07, + .gain = 230, + .gain2 = 205, }, }, }, - .multi_read_bit = ST_MAGN_0_MULTIREAD_BIT, + .multi_read_bit = false, .bootime = 2, }, { - .wai = ST_MAGN_1_WAI_EXP, + .wai = 0x3c, .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS, .sensors_supported = { [0] = LSM303DLHC_MAGN_DEV_NAME, @@ -318,175 +192,175 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = { }, .ch = (struct iio_chan_spec *)st_magn_16bit_channels, .odr = { - .addr = ST_MAGN_1_ODR_ADDR, - .mask = ST_MAGN_1_ODR_MASK, + .addr = 0x00, + .mask = 0x1c, .odr_avl = { - { 1, ST_MAGN_1_ODR_AVL_1HZ_VAL, }, - { 2, ST_MAGN_1_ODR_AVL_2HZ_VAL, }, - { 3, ST_MAGN_1_ODR_AVL_3HZ_VAL, }, - { 8, ST_MAGN_1_ODR_AVL_8HZ_VAL, }, - { 15, ST_MAGN_1_ODR_AVL_15HZ_VAL, }, - { 30, ST_MAGN_1_ODR_AVL_30HZ_VAL, }, - { 75, ST_MAGN_1_ODR_AVL_75HZ_VAL, }, - { 220, ST_MAGN_1_ODR_AVL_220HZ_VAL, }, + { .hz = 1, .value = 0x00 }, + { .hz = 2, .value = 0x01 }, + { .hz = 3, .value = 0x02 }, + { .hz = 8, .value = 0x03 }, + { .hz = 15, .value = 0x04 }, + { .hz = 30, .value = 0x05 }, + { .hz = 75, .value = 0x06 }, + { .hz = 220, .value = 0x07 }, }, }, .pw = { - .addr = ST_MAGN_1_PW_ADDR, - .mask = ST_MAGN_1_PW_MASK, - .value_on = ST_MAGN_1_PW_ON, - .value_off = ST_MAGN_1_PW_OFF, + .addr = 0x02, + .mask = 0x03, + .value_on = 0x00, + .value_off = 0x03, }, .fs = { - .addr = ST_MAGN_1_FS_ADDR, - .mask = ST_MAGN_1_FS_MASK, + .addr = 0x01, + .mask = 0xe0, .fs_avl = { [0] = { .num = ST_MAGN_FS_AVL_1300MG, - .value = ST_MAGN_1_FS_AVL_1300_VAL, - .gain = ST_MAGN_1_FS_AVL_1300_GAIN_XY, - .gain2 = ST_MAGN_1_FS_AVL_1300_GAIN_Z, + .value = 0x01, + .gain = 909, + .gain2 = 1020, }, [1] = { .num = ST_MAGN_FS_AVL_1900MG, - .value = ST_MAGN_1_FS_AVL_1900_VAL, - .gain = ST_MAGN_1_FS_AVL_1900_GAIN_XY, - .gain2 = ST_MAGN_1_FS_AVL_1900_GAIN_Z, + .value = 0x02, + .gain = 1169, + .gain2 = 1315, }, [2] = { .num = ST_MAGN_FS_AVL_2500MG, - .value = ST_MAGN_1_FS_AVL_2500_VAL, - .gain = ST_MAGN_1_FS_AVL_2500_GAIN_XY, - .gain2 = ST_MAGN_1_FS_AVL_2500_GAIN_Z, + .value = 0x03, + .gain = 1492, + .gain2 = 1666, }, [3] = { .num = ST_MAGN_FS_AVL_4000MG, - .value = ST_MAGN_1_FS_AVL_4000_VAL, - .gain = ST_MAGN_1_FS_AVL_4000_GAIN_XY, - .gain2 = ST_MAGN_1_FS_AVL_4000_GAIN_Z, + .value = 0x04, + .gain = 2222, + .gain2 = 2500, }, [4] = { .num = ST_MAGN_FS_AVL_4700MG, - .value = ST_MAGN_1_FS_AVL_4700_VAL, - .gain = ST_MAGN_1_FS_AVL_4700_GAIN_XY, - .gain2 = ST_MAGN_1_FS_AVL_4700_GAIN_Z, + .value = 0x05, + .gain = 2500, + .gain2 = 2816, }, [5] = { .num = ST_MAGN_FS_AVL_5600MG, - .value = ST_MAGN_1_FS_AVL_5600_VAL, - .gain = ST_MAGN_1_FS_AVL_5600_GAIN_XY, - .gain2 = ST_MAGN_1_FS_AVL_5600_GAIN_Z, + .value = 0x06, + .gain = 3030, + .gain2 = 3389, }, [6] = { .num = ST_MAGN_FS_AVL_8100MG, - .value = ST_MAGN_1_FS_AVL_8100_VAL, - .gain = ST_MAGN_1_FS_AVL_8100_GAIN_XY, - .gain2 = ST_MAGN_1_FS_AVL_8100_GAIN_Z, + .value = 0x07, + .gain = 4347, + .gain2 = 4878, }, }, }, - .multi_read_bit = ST_MAGN_1_MULTIREAD_BIT, + .multi_read_bit = false, .bootime = 2, }, { - .wai = ST_MAGN_2_WAI_EXP, + .wai = 0x3d, .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS, .sensors_supported = { [0] = LIS3MDL_MAGN_DEV_NAME, }, .ch = (struct iio_chan_spec *)st_magn_2_16bit_channels, .odr = { - .addr = ST_MAGN_2_ODR_ADDR, - .mask = ST_MAGN_2_ODR_MASK, + .addr = 0x20, + .mask = 0x1c, .odr_avl = { - { 1, ST_MAGN_2_ODR_AVL_1HZ_VAL, }, - { 2, ST_MAGN_2_ODR_AVL_2HZ_VAL, }, - { 3, ST_MAGN_2_ODR_AVL_3HZ_VAL, }, - { 5, ST_MAGN_2_ODR_AVL_5HZ_VAL, }, - { 10, ST_MAGN_2_ODR_AVL_10HZ_VAL, }, - { 20, ST_MAGN_2_ODR_AVL_20HZ_VAL, }, - { 40, ST_MAGN_2_ODR_AVL_40HZ_VAL, }, - { 80, ST_MAGN_2_ODR_AVL_80HZ_VAL, }, + { .hz = 1, .value = 0x00 }, + { .hz = 2, .value = 0x01 }, + { .hz = 3, .value = 0x02 }, + { .hz = 5, .value = 0x03 }, + { .hz = 10, .value = 0x04 }, + { .hz = 20, .value = 0x05 }, + { .hz = 40, .value = 0x06 }, + { .hz = 80, .value = 0x07 }, }, }, .pw = { - .addr = ST_MAGN_2_PW_ADDR, - .mask = ST_MAGN_2_PW_MASK, - .value_on = ST_MAGN_2_PW_ON, - .value_off = ST_MAGN_2_PW_OFF, + .addr = 0x22, + .mask = 0x03, + .value_on = 0x00, + .value_off = 0x03, }, .fs = { - .addr = ST_MAGN_2_FS_ADDR, - .mask = ST_MAGN_2_FS_MASK, + .addr = 0x21, + .mask = 0x60, .fs_avl = { [0] = { .num = ST_MAGN_FS_AVL_4000MG, - .value = ST_MAGN_2_FS_AVL_4000_VAL, - .gain = ST_MAGN_2_FS_AVL_4000_GAIN, + .value = 0x00, + .gain = 146, }, [1] = { .num = ST_MAGN_FS_AVL_8000MG, - .value = ST_MAGN_2_FS_AVL_8000_VAL, - .gain = ST_MAGN_2_FS_AVL_8000_GAIN, + .value = 0x01, + .gain = 292, }, [2] = { .num = ST_MAGN_FS_AVL_12000MG, - .value = ST_MAGN_2_FS_AVL_12000_VAL, - .gain = ST_MAGN_2_FS_AVL_12000_GAIN, + .value = 0x02, + .gain = 438, }, [3] = { .num = ST_MAGN_FS_AVL_16000MG, - .value = ST_MAGN_2_FS_AVL_16000_VAL, - .gain = ST_MAGN_2_FS_AVL_16000_GAIN, + .value = 0x03, + .gain = 584, }, }, }, - .multi_read_bit = ST_MAGN_2_MULTIREAD_BIT, + .multi_read_bit = false, .bootime = 2, }, { - .wai = ST_MAGN_3_WAI_EXP, - .wai_addr = ST_MAGN_3_WAI_ADDR, + .wai = 0x40, + .wai_addr = 0x4f, .sensors_supported = { [0] = LSM303AGR_MAGN_DEV_NAME, }, .ch = (struct iio_chan_spec *)st_magn_3_16bit_channels, .odr = { - .addr = ST_MAGN_3_ODR_ADDR, - .mask = ST_MAGN_3_ODR_MASK, + .addr = 0x60, + .mask = 0x0c, .odr_avl = { - { 10, ST_MAGN_3_ODR_AVL_10HZ_VAL, }, - { 20, ST_MAGN_3_ODR_AVL_20HZ_VAL, }, - { 50, ST_MAGN_3_ODR_AVL_50HZ_VAL, }, - { 100, ST_MAGN_3_ODR_AVL_100HZ_VAL, }, + { .hz = 10, .value = 0x00 }, + { .hz = 20, .value = 0x01 }, + { .hz = 50, .value = 0x02 }, + { .hz = 100, .value = 0x03 }, }, }, .pw = { - .addr = ST_MAGN_3_PW_ADDR, - .mask = ST_MAGN_3_PW_MASK, - .value_on = ST_MAGN_3_PW_ON, - .value_off = ST_MAGN_3_PW_OFF, + .addr = 0x60, + .mask = 0x03, + .value_on = 0x00, + .value_off = 0x03, }, .fs = { .fs_avl = { [0] = { .num = ST_MAGN_FS_AVL_15000MG, - .gain = ST_MAGN_3_FS_AVL_15000_GAIN, + .gain = 1500, }, }, }, .bdu = { - .addr = ST_MAGN_3_BDU_ADDR, - .mask = ST_MAGN_3_BDU_MASK, + .addr = 0x62, + .mask = 0x10, }, .drdy_irq = { - .addr = ST_MAGN_3_DRDY_IRQ_ADDR, - .mask_int1 = ST_MAGN_3_DRDY_INT_MASK, - .addr_ihl = ST_MAGN_3_IHL_IRQ_ADDR, - .mask_ihl = ST_MAGN_3_IHL_IRQ_MASK, + .addr = 0x62, + .mask_int1 = 0x01, + .addr_ihl = 0x63, + .mask_ihl = 0x04, .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, }, - .multi_read_bit = ST_MAGN_3_MULTIREAD_BIT, + .multi_read_bit = false, .bootime = 2, }, }; diff --git a/drivers/iio/potentiometer/mcp4531.c b/drivers/iio/potentiometer/mcp4531.c index 13b6ae2fcf7b..0d1bcf89ae17 100644 --- a/drivers/iio/potentiometer/mcp4531.c +++ b/drivers/iio/potentiometer/mcp4531.c @@ -38,7 +38,7 @@ struct mcp4531_cfg { int wipers; - int max_pos; + int avail[3]; int kohms; }; @@ -78,38 +78,38 @@ enum mcp4531_type { }; static const struct mcp4531_cfg mcp4531_cfg[] = { - [MCP453x_502] = { .wipers = 1, .max_pos = 128, .kohms = 5, }, - [MCP453x_103] = { .wipers = 1, .max_pos = 128, .kohms = 10, }, - [MCP453x_503] = { .wipers = 1, .max_pos = 128, .kohms = 50, }, - [MCP453x_104] = { .wipers = 1, .max_pos = 128, .kohms = 100, }, - [MCP454x_502] = { .wipers = 1, .max_pos = 128, .kohms = 5, }, - [MCP454x_103] = { .wipers = 1, .max_pos = 128, .kohms = 10, }, - [MCP454x_503] = { .wipers = 1, .max_pos = 128, .kohms = 50, }, - [MCP454x_104] = { .wipers = 1, .max_pos = 128, .kohms = 100, }, - [MCP455x_502] = { .wipers = 1, .max_pos = 256, .kohms = 5, }, - [MCP455x_103] = { .wipers = 1, .max_pos = 256, .kohms = 10, }, - [MCP455x_503] = { .wipers = 1, .max_pos = 256, .kohms = 50, }, - [MCP455x_104] = { .wipers = 1, .max_pos = 256, .kohms = 100, }, - [MCP456x_502] = { .wipers = 1, .max_pos = 256, .kohms = 5, }, - [MCP456x_103] = { .wipers = 1, .max_pos = 256, .kohms = 10, }, - [MCP456x_503] = { .wipers = 1, .max_pos = 256, .kohms = 50, }, - [MCP456x_104] = { .wipers = 1, .max_pos = 256, .kohms = 100, }, - [MCP463x_502] = { .wipers = 2, .max_pos = 128, .kohms = 5, }, - [MCP463x_103] = { .wipers = 2, .max_pos = 128, .kohms = 10, }, - [MCP463x_503] = { .wipers = 2, .max_pos = 128, .kohms = 50, }, - [MCP463x_104] = { .wipers = 2, .max_pos = 128, .kohms = 100, }, - [MCP464x_502] = { .wipers = 2, .max_pos = 128, .kohms = 5, }, - [MCP464x_103] = { .wipers = 2, .max_pos = 128, .kohms = 10, }, - [MCP464x_503] = { .wipers = 2, .max_pos = 128, .kohms = 50, }, - [MCP464x_104] = { .wipers = 2, .max_pos = 128, .kohms = 100, }, - [MCP465x_502] = { .wipers = 2, .max_pos = 256, .kohms = 5, }, - [MCP465x_103] = { .wipers = 2, .max_pos = 256, .kohms = 10, }, - [MCP465x_503] = { .wipers = 2, .max_pos = 256, .kohms = 50, }, - [MCP465x_104] = { .wipers = 2, .max_pos = 256, .kohms = 100, }, - [MCP466x_502] = { .wipers = 2, .max_pos = 256, .kohms = 5, }, - [MCP466x_103] = { .wipers = 2, .max_pos = 256, .kohms = 10, }, - [MCP466x_503] = { .wipers = 2, .max_pos = 256, .kohms = 50, }, - [MCP466x_104] = { .wipers = 2, .max_pos = 256, .kohms = 100, }, + [MCP453x_502] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 5, }, + [MCP453x_103] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 10, }, + [MCP453x_503] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 50, }, + [MCP453x_104] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 100, }, + [MCP454x_502] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 5, }, + [MCP454x_103] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 10, }, + [MCP454x_503] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 50, }, + [MCP454x_104] = { .wipers = 1, .avail = { 0, 1, 128 }, .kohms = 100, }, + [MCP455x_502] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 5, }, + [MCP455x_103] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 10, }, + [MCP455x_503] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 50, }, + [MCP455x_104] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 100, }, + [MCP456x_502] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 5, }, + [MCP456x_103] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 10, }, + [MCP456x_503] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 50, }, + [MCP456x_104] = { .wipers = 1, .avail = { 0, 1, 256 }, .kohms = 100, }, + [MCP463x_502] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 5, }, + [MCP463x_103] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 10, }, + [MCP463x_503] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 50, }, + [MCP463x_104] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 100, }, + [MCP464x_502] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 5, }, + [MCP464x_103] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 10, }, + [MCP464x_503] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 50, }, + [MCP464x_104] = { .wipers = 2, .avail = { 0, 1, 128 }, .kohms = 100, }, + [MCP465x_502] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 5, }, + [MCP465x_103] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 10, }, + [MCP465x_503] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 50, }, + [MCP465x_104] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 100, }, + [MCP466x_502] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 5, }, + [MCP466x_103] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 10, }, + [MCP466x_503] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 50, }, + [MCP466x_104] = { .wipers = 2, .avail = { 0, 1, 256 }, .kohms = 100, }, }; #define MCP4531_WRITE (0 << 2) @@ -124,13 +124,14 @@ struct mcp4531_data { const struct mcp4531_cfg *cfg; }; -#define MCP4531_CHANNEL(ch) { \ - .type = IIO_RESISTANCE, \ - .indexed = 1, \ - .output = 1, \ - .channel = (ch), \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ +#define MCP4531_CHANNEL(ch) { \ + .type = IIO_RESISTANCE, \ + .indexed = 1, \ + .output = 1, \ + .channel = (ch), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_RAW), \ } static const struct iio_chan_spec mcp4531_channels[] = { @@ -156,13 +157,31 @@ static int mcp4531_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: *val = 1000 * data->cfg->kohms; - *val2 = data->cfg->max_pos; + *val2 = data->cfg->avail[2]; return IIO_VAL_FRACTIONAL; } return -EINVAL; } +static int mcp4531_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct mcp4531_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + *length = ARRAY_SIZE(data->cfg->avail); + *vals = data->cfg->avail; + *type = IIO_VAL_INT; + return IIO_AVAIL_RANGE; + } + + return -EINVAL; +} + static int mcp4531_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) @@ -172,7 +191,7 @@ static int mcp4531_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - if (val > data->cfg->max_pos || val < 0) + if (val > data->cfg->avail[2] || val < 0) return -EINVAL; break; default: @@ -186,6 +205,7 @@ static int mcp4531_write_raw(struct iio_dev *indio_dev, static const struct iio_info mcp4531_info = { .read_raw = mcp4531_read_raw, + .read_avail = mcp4531_read_avail, .write_raw = mcp4531_write_raw, .driver_module = THIS_MODULE, }; diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c index 55df9a75eb3a..e19e0787864c 100644 --- a/drivers/iio/pressure/st_pressure_core.c +++ b/drivers/iio/pressure/st_pressure_core.c @@ -112,115 +112,24 @@ #define ST_PRESS_1_OUT_XL_ADDR 0x28 #define ST_TEMP_1_OUT_L_ADDR 0x2b -/* - * CUSTOM VALUES FOR LPS331AP SENSOR - * See LPS331AP datasheet: - * http://www2.st.com/resource/en/datasheet/lps331ap.pdf - */ -#define ST_PRESS_LPS331AP_WAI_EXP 0xbb -#define ST_PRESS_LPS331AP_ODR_ADDR 0x20 -#define ST_PRESS_LPS331AP_ODR_MASK 0x70 -#define ST_PRESS_LPS331AP_ODR_AVL_1HZ_VAL 0x01 -#define ST_PRESS_LPS331AP_ODR_AVL_7HZ_VAL 0x05 -#define ST_PRESS_LPS331AP_ODR_AVL_13HZ_VAL 0x06 -#define ST_PRESS_LPS331AP_ODR_AVL_25HZ_VAL 0x07 -#define ST_PRESS_LPS331AP_PW_ADDR 0x20 -#define ST_PRESS_LPS331AP_PW_MASK 0x80 -#define ST_PRESS_LPS331AP_FS_ADDR 0x23 -#define ST_PRESS_LPS331AP_FS_MASK 0x30 -#define ST_PRESS_LPS331AP_BDU_ADDR 0x20 -#define ST_PRESS_LPS331AP_BDU_MASK 0x04 -#define ST_PRESS_LPS331AP_DRDY_IRQ_ADDR 0x22 -#define ST_PRESS_LPS331AP_DRDY_IRQ_INT1_MASK 0x04 -#define ST_PRESS_LPS331AP_DRDY_IRQ_INT2_MASK 0x20 -#define ST_PRESS_LPS331AP_IHL_IRQ_ADDR 0x22 -#define ST_PRESS_LPS331AP_IHL_IRQ_MASK 0x80 -#define ST_PRESS_LPS331AP_OD_IRQ_ADDR 0x22 -#define ST_PRESS_LPS331AP_OD_IRQ_MASK 0x40 -#define ST_PRESS_LPS331AP_MULTIREAD_BIT true - -/* - * CUSTOM VALUES FOR THE OBSOLETE LPS001WP SENSOR - */ - /* LPS001WP pressure resolution */ #define ST_PRESS_LPS001WP_LSB_PER_MBAR 16UL /* LPS001WP temperature resolution */ #define ST_PRESS_LPS001WP_LSB_PER_CELSIUS 64UL - -#define ST_PRESS_LPS001WP_WAI_EXP 0xba -#define ST_PRESS_LPS001WP_ODR_ADDR 0x20 -#define ST_PRESS_LPS001WP_ODR_MASK 0x30 -#define ST_PRESS_LPS001WP_ODR_AVL_1HZ_VAL 0x01 -#define ST_PRESS_LPS001WP_ODR_AVL_7HZ_VAL 0x02 -#define ST_PRESS_LPS001WP_ODR_AVL_13HZ_VAL 0x03 -#define ST_PRESS_LPS001WP_PW_ADDR 0x20 -#define ST_PRESS_LPS001WP_PW_MASK 0x40 +/* LPS001WP pressure gain */ #define ST_PRESS_LPS001WP_FS_AVL_PRESS_GAIN \ (100000000UL / ST_PRESS_LPS001WP_LSB_PER_MBAR) -#define ST_PRESS_LPS001WP_BDU_ADDR 0x20 -#define ST_PRESS_LPS001WP_BDU_MASK 0x04 -#define ST_PRESS_LPS001WP_MULTIREAD_BIT true +/* LPS001WP pressure and temp L addresses */ #define ST_PRESS_LPS001WP_OUT_L_ADDR 0x28 #define ST_TEMP_LPS001WP_OUT_L_ADDR 0x2a -/* - * CUSTOM VALUES FOR LPS25H SENSOR - * See LPS25H datasheet: - * http://www2.st.com/resource/en/datasheet/lps25h.pdf - */ -#define ST_PRESS_LPS25H_WAI_EXP 0xbd -#define ST_PRESS_LPS25H_ODR_ADDR 0x20 -#define ST_PRESS_LPS25H_ODR_MASK 0x70 -#define ST_PRESS_LPS25H_ODR_AVL_1HZ_VAL 0x01 -#define ST_PRESS_LPS25H_ODR_AVL_7HZ_VAL 0x02 -#define ST_PRESS_LPS25H_ODR_AVL_13HZ_VAL 0x03 -#define ST_PRESS_LPS25H_ODR_AVL_25HZ_VAL 0x04 -#define ST_PRESS_LPS25H_PW_ADDR 0x20 -#define ST_PRESS_LPS25H_PW_MASK 0x80 -#define ST_PRESS_LPS25H_BDU_ADDR 0x20 -#define ST_PRESS_LPS25H_BDU_MASK 0x04 -#define ST_PRESS_LPS25H_DRDY_IRQ_ADDR 0x23 -#define ST_PRESS_LPS25H_DRDY_IRQ_INT1_MASK 0x01 -#define ST_PRESS_LPS25H_DRDY_IRQ_INT2_MASK 0x10 -#define ST_PRESS_LPS25H_IHL_IRQ_ADDR 0x22 -#define ST_PRESS_LPS25H_IHL_IRQ_MASK 0x80 -#define ST_PRESS_LPS25H_OD_IRQ_ADDR 0x22 -#define ST_PRESS_LPS25H_OD_IRQ_MASK 0x40 -#define ST_PRESS_LPS25H_MULTIREAD_BIT true +/* LPS25H pressure and temp L addresses */ #define ST_PRESS_LPS25H_OUT_XL_ADDR 0x28 #define ST_TEMP_LPS25H_OUT_L_ADDR 0x2b -/* - * CUSTOM VALUES FOR LPS22HB SENSOR - * See LPS22HB datasheet: - * http://www2.st.com/resource/en/datasheet/lps22hb.pdf - */ - /* LPS22HB temperature sensitivity */ #define ST_PRESS_LPS22HB_LSB_PER_CELSIUS 100UL -#define ST_PRESS_LPS22HB_WAI_EXP 0xb1 -#define ST_PRESS_LPS22HB_ODR_ADDR 0x10 -#define ST_PRESS_LPS22HB_ODR_MASK 0x70 -#define ST_PRESS_LPS22HB_ODR_AVL_1HZ_VAL 0x01 -#define ST_PRESS_LPS22HB_ODR_AVL_10HZ_VAL 0x02 -#define ST_PRESS_LPS22HB_ODR_AVL_25HZ_VAL 0x03 -#define ST_PRESS_LPS22HB_ODR_AVL_50HZ_VAL 0x04 -#define ST_PRESS_LPS22HB_ODR_AVL_75HZ_VAL 0x05 -#define ST_PRESS_LPS22HB_PW_ADDR 0x10 -#define ST_PRESS_LPS22HB_PW_MASK 0x70 -#define ST_PRESS_LPS22HB_BDU_ADDR 0x10 -#define ST_PRESS_LPS22HB_BDU_MASK 0x02 -#define ST_PRESS_LPS22HB_DRDY_IRQ_ADDR 0x12 -#define ST_PRESS_LPS22HB_DRDY_IRQ_INT1_MASK 0x04 -#define ST_PRESS_LPS22HB_DRDY_IRQ_INT2_MASK 0x08 -#define ST_PRESS_LPS22HB_IHL_IRQ_ADDR 0x12 -#define ST_PRESS_LPS22HB_IHL_IRQ_MASK 0x80 -#define ST_PRESS_LPS22HB_OD_IRQ_ADDR 0x12 -#define ST_PRESS_LPS22HB_OD_IRQ_MASK 0x40 -#define ST_PRESS_LPS22HB_MULTIREAD_BIT true - static const struct iio_chan_spec st_press_1_channels[] = { { .type = IIO_PRESSURE, @@ -321,7 +230,12 @@ static const struct iio_chan_spec st_press_lps22hb_channels[] = { static const struct st_sensor_settings st_press_sensors_settings[] = { { - .wai = ST_PRESS_LPS331AP_WAI_EXP, + /* + * CUSTOM VALUES FOR LPS331AP SENSOR + * See LPS331AP datasheet: + * http://www2.st.com/resource/en/datasheet/lps331ap.pdf + */ + .wai = 0xbb, .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS, .sensors_supported = { [0] = LPS331AP_PRESS_DEV_NAME, @@ -329,24 +243,24 @@ static const struct st_sensor_settings st_press_sensors_settings[] = { .ch = (struct iio_chan_spec *)st_press_1_channels, .num_ch = ARRAY_SIZE(st_press_1_channels), .odr = { - .addr = ST_PRESS_LPS331AP_ODR_ADDR, - .mask = ST_PRESS_LPS331AP_ODR_MASK, + .addr = 0x20, + .mask = 0x70, .odr_avl = { - { 1, ST_PRESS_LPS331AP_ODR_AVL_1HZ_VAL, }, - { 7, ST_PRESS_LPS331AP_ODR_AVL_7HZ_VAL, }, - { 13, ST_PRESS_LPS331AP_ODR_AVL_13HZ_VAL, }, - { 25, ST_PRESS_LPS331AP_ODR_AVL_25HZ_VAL, }, + { .hz = 1, .value = 0x01 }, + { .hz = 7, .value = 0x05 }, + { .hz = 13, .value = 0x06 }, + { .hz = 25, .value = 0x07 }, }, }, .pw = { - .addr = ST_PRESS_LPS331AP_PW_ADDR, - .mask = ST_PRESS_LPS331AP_PW_MASK, + .addr = 0x20, + .mask = 0x80, .value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE, .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, }, .fs = { - .addr = ST_PRESS_LPS331AP_FS_ADDR, - .mask = ST_PRESS_LPS331AP_FS_MASK, + .addr = 0x23, + .mask = 0x30, .fs_avl = { /* * Pressure and temperature sensitivity values @@ -360,24 +274,27 @@ static const struct st_sensor_settings st_press_sensors_settings[] = { }, }, .bdu = { - .addr = ST_PRESS_LPS331AP_BDU_ADDR, - .mask = ST_PRESS_LPS331AP_BDU_MASK, + .addr = 0x20, + .mask = 0x04, }, .drdy_irq = { - .addr = ST_PRESS_LPS331AP_DRDY_IRQ_ADDR, - .mask_int1 = ST_PRESS_LPS331AP_DRDY_IRQ_INT1_MASK, - .mask_int2 = ST_PRESS_LPS331AP_DRDY_IRQ_INT2_MASK, - .addr_ihl = ST_PRESS_LPS331AP_IHL_IRQ_ADDR, - .mask_ihl = ST_PRESS_LPS331AP_IHL_IRQ_MASK, - .addr_od = ST_PRESS_LPS331AP_OD_IRQ_ADDR, - .mask_od = ST_PRESS_LPS331AP_OD_IRQ_MASK, + .addr = 0x22, + .mask_int1 = 0x04, + .mask_int2 = 0x20, + .addr_ihl = 0x22, + .mask_ihl = 0x80, + .addr_od = 0x22, + .mask_od = 0x40, .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, }, - .multi_read_bit = ST_PRESS_LPS331AP_MULTIREAD_BIT, + .multi_read_bit = true, .bootime = 2, }, { - .wai = ST_PRESS_LPS001WP_WAI_EXP, + /* + * CUSTOM VALUES FOR LPS001WP SENSOR + */ + .wai = 0xba, .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS, .sensors_supported = { [0] = LPS001WP_PRESS_DEV_NAME, @@ -385,17 +302,17 @@ static const struct st_sensor_settings st_press_sensors_settings[] = { .ch = (struct iio_chan_spec *)st_press_lps001wp_channels, .num_ch = ARRAY_SIZE(st_press_lps001wp_channels), .odr = { - .addr = ST_PRESS_LPS001WP_ODR_ADDR, - .mask = ST_PRESS_LPS001WP_ODR_MASK, + .addr = 0x20, + .mask = 0x30, .odr_avl = { - { 1, ST_PRESS_LPS001WP_ODR_AVL_1HZ_VAL, }, - { 7, ST_PRESS_LPS001WP_ODR_AVL_7HZ_VAL, }, - { 13, ST_PRESS_LPS001WP_ODR_AVL_13HZ_VAL, }, + { .hz = 1, .value = 0x01 }, + { .hz = 7, .value = 0x02 }, + { .hz = 13, .value = 0x03 }, }, }, .pw = { - .addr = ST_PRESS_LPS001WP_PW_ADDR, - .mask = ST_PRESS_LPS001WP_PW_MASK, + .addr = 0x20, + .mask = 0x40, .value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE, .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, }, @@ -413,17 +330,22 @@ static const struct st_sensor_settings st_press_sensors_settings[] = { }, }, .bdu = { - .addr = ST_PRESS_LPS001WP_BDU_ADDR, - .mask = ST_PRESS_LPS001WP_BDU_MASK, + .addr = 0x20, + .mask = 0x04, }, .drdy_irq = { .addr = 0, }, - .multi_read_bit = ST_PRESS_LPS001WP_MULTIREAD_BIT, + .multi_read_bit = true, .bootime = 2, }, { - .wai = ST_PRESS_LPS25H_WAI_EXP, + /* + * CUSTOM VALUES FOR LPS25H SENSOR + * See LPS25H datasheet: + * http://www2.st.com/resource/en/datasheet/lps25h.pdf + */ + .wai = 0xbd, .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS, .sensors_supported = { [0] = LPS25H_PRESS_DEV_NAME, @@ -431,18 +353,18 @@ static const struct st_sensor_settings st_press_sensors_settings[] = { .ch = (struct iio_chan_spec *)st_press_1_channels, .num_ch = ARRAY_SIZE(st_press_1_channels), .odr = { - .addr = ST_PRESS_LPS25H_ODR_ADDR, - .mask = ST_PRESS_LPS25H_ODR_MASK, + .addr = 0x20, + .mask = 0x70, .odr_avl = { - { 1, ST_PRESS_LPS25H_ODR_AVL_1HZ_VAL, }, - { 7, ST_PRESS_LPS25H_ODR_AVL_7HZ_VAL, }, - { 13, ST_PRESS_LPS25H_ODR_AVL_13HZ_VAL, }, - { 25, ST_PRESS_LPS25H_ODR_AVL_25HZ_VAL, }, + { .hz = 1, .value = 0x01 }, + { .hz = 7, .value = 0x02 }, + { .hz = 13, .value = 0x03 }, + { .hz = 25, .value = 0x04 }, }, }, .pw = { - .addr = ST_PRESS_LPS25H_PW_ADDR, - .mask = ST_PRESS_LPS25H_PW_MASK, + .addr = 0x20, + .mask = 0x80, .value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE, .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, }, @@ -460,24 +382,29 @@ static const struct st_sensor_settings st_press_sensors_settings[] = { }, }, .bdu = { - .addr = ST_PRESS_LPS25H_BDU_ADDR, - .mask = ST_PRESS_LPS25H_BDU_MASK, + .addr = 0x20, + .mask = 0x04, }, .drdy_irq = { - .addr = ST_PRESS_LPS25H_DRDY_IRQ_ADDR, - .mask_int1 = ST_PRESS_LPS25H_DRDY_IRQ_INT1_MASK, - .mask_int2 = ST_PRESS_LPS25H_DRDY_IRQ_INT2_MASK, - .addr_ihl = ST_PRESS_LPS25H_IHL_IRQ_ADDR, - .mask_ihl = ST_PRESS_LPS25H_IHL_IRQ_MASK, - .addr_od = ST_PRESS_LPS25H_OD_IRQ_ADDR, - .mask_od = ST_PRESS_LPS25H_OD_IRQ_MASK, + .addr = 0x23, + .mask_int1 = 0x01, + .mask_int2 = 0x10, + .addr_ihl = 0x22, + .mask_ihl = 0x80, + .addr_od = 0x22, + .mask_od = 0x40, .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, }, - .multi_read_bit = ST_PRESS_LPS25H_MULTIREAD_BIT, + .multi_read_bit = true, .bootime = 2, }, { - .wai = ST_PRESS_LPS22HB_WAI_EXP, + /* + * CUSTOM VALUES FOR LPS22HB SENSOR + * See LPS22HB datasheet: + * http://www2.st.com/resource/en/datasheet/lps22hb.pdf + */ + .wai = 0xb1, .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS, .sensors_supported = { [0] = LPS22HB_PRESS_DEV_NAME, @@ -485,19 +412,19 @@ static const struct st_sensor_settings st_press_sensors_settings[] = { .ch = (struct iio_chan_spec *)st_press_lps22hb_channels, .num_ch = ARRAY_SIZE(st_press_lps22hb_channels), .odr = { - .addr = ST_PRESS_LPS22HB_ODR_ADDR, - .mask = ST_PRESS_LPS22HB_ODR_MASK, + .addr = 0x10, + .mask = 0x70, .odr_avl = { - { 1, ST_PRESS_LPS22HB_ODR_AVL_1HZ_VAL, }, - { 10, ST_PRESS_LPS22HB_ODR_AVL_10HZ_VAL, }, - { 25, ST_PRESS_LPS22HB_ODR_AVL_25HZ_VAL, }, - { 50, ST_PRESS_LPS22HB_ODR_AVL_50HZ_VAL, }, - { 75, ST_PRESS_LPS22HB_ODR_AVL_75HZ_VAL, }, + { .hz = 1, .value = 0x01 }, + { .hz = 10, .value = 0x02 }, + { .hz = 25, .value = 0x03 }, + { .hz = 50, .value = 0x04 }, + { .hz = 75, .value = 0x05 }, }, }, .pw = { - .addr = ST_PRESS_LPS22HB_PW_ADDR, - .mask = ST_PRESS_LPS22HB_PW_MASK, + .addr = 0x10, + .mask = 0x70, .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, }, .fs = { @@ -514,20 +441,20 @@ static const struct st_sensor_settings st_press_sensors_settings[] = { }, }, .bdu = { - .addr = ST_PRESS_LPS22HB_BDU_ADDR, - .mask = ST_PRESS_LPS22HB_BDU_MASK, + .addr = 0x10, + .mask = 0x02, }, .drdy_irq = { - .addr = ST_PRESS_LPS22HB_DRDY_IRQ_ADDR, - .mask_int1 = ST_PRESS_LPS22HB_DRDY_IRQ_INT1_MASK, - .mask_int2 = ST_PRESS_LPS22HB_DRDY_IRQ_INT2_MASK, - .addr_ihl = ST_PRESS_LPS22HB_IHL_IRQ_ADDR, - .mask_ihl = ST_PRESS_LPS22HB_IHL_IRQ_MASK, - .addr_od = ST_PRESS_LPS22HB_OD_IRQ_ADDR, - .mask_od = ST_PRESS_LPS22HB_OD_IRQ_MASK, + .addr = 0x12, + .mask_int1 = 0x04, + .mask_int2 = 0x08, + .addr_ihl = 0x12, + .mask_ihl = 0x80, + .addr_od = 0x12, + .mask_od = 0x40, .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, }, - .multi_read_bit = ST_PRESS_LPS22HB_MULTIREAD_BIT, + .multi_read_bit = true, }, }; diff --git a/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583 b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583 deleted file mode 100644 index 470f7ad9c073..000000000000 --- a/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583 +++ /dev/null @@ -1,6 +0,0 @@ -What: /sys/bus/iio/devices/device[n]/in_illuminance0_calibrate -KernelVersion: 2.6.37 -Contact: linux-iio@vger.kernel.org -Description: - This property causes an internal calibration of the als gain trim - value which is later used in calculating illuminance in lux. diff --git a/drivers/staging/iio/frequency/ad9832.c b/drivers/staging/iio/frequency/ad9832.c index 639047fade30..a5b2f068168d 100644 --- a/drivers/staging/iio/frequency/ad9832.c +++ b/drivers/staging/iio/frequency/ad9832.c @@ -211,6 +211,13 @@ static int ad9832_probe(struct spi_device *spi) return -ENODEV; } + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + spi_set_drvdata(spi, indio_dev); + st = iio_priv(indio_dev); + st->avdd = devm_regulator_get(&spi->dev, "avdd"); if (IS_ERR(st->avdd)) return PTR_ERR(st->avdd); @@ -233,13 +240,6 @@ static int ad9832_probe(struct spi_device *spi) goto error_disable_avdd; } - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); - if (!indio_dev) { - ret = -ENOMEM; - goto error_disable_dvdd; - } - spi_set_drvdata(spi, indio_dev); - st = iio_priv(indio_dev); st->mclk = pdata->mclk; st->spi = spi; diff --git a/drivers/staging/iio/light/Kconfig b/drivers/staging/iio/light/Kconfig index dbf22d396ddf..4fbf6298c0f3 100644 --- a/drivers/staging/iio/light/Kconfig +++ b/drivers/staging/iio/light/Kconfig @@ -13,13 +13,6 @@ config SENSORS_ISL29028 Proximity value via iio. The ISL29028 provides the concurrent sensing of ambient light and proximity. -config TSL2583 - tristate "TAOS TSL2580, TSL2581 and TSL2583 light-to-digital converters" - depends on I2C - help - Provides support for the TAOS tsl2580, tsl2581 and tsl2583 devices. - Access ALS data via iio, sysfs. - config TSL2x7x tristate "TAOS TSL/TMD2x71 and TSL/TMD2x72 Family of light and proximity sensors" depends on I2C diff --git a/drivers/staging/iio/light/Makefile b/drivers/staging/iio/light/Makefile index 6480856e1682..f8693e9fdc94 100644 --- a/drivers/staging/iio/light/Makefile +++ b/drivers/staging/iio/light/Makefile @@ -3,5 +3,4 @@ # obj-$(CONFIG_SENSORS_ISL29028) += isl29028.o -obj-$(CONFIG_TSL2583) += tsl2583.o obj-$(CONFIG_TSL2x7x) += tsl2x7x_core.o diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h index 638157234357..47eeec3218b5 100644 --- a/include/linux/iio/consumer.h +++ b/include/linux/iio/consumer.h @@ -225,6 +225,34 @@ int iio_read_channel_processed(struct iio_channel *chan, int *val); */ int iio_write_channel_raw(struct iio_channel *chan, int val); +/** + * iio_read_max_channel_raw() - read maximum available raw value from a given + * channel, i.e. the maximum possible value. + * @chan: The channel being queried. + * @val: Value read back. + * + * Note raw reads from iio channels are in adc counts and hence + * scale will need to be applied if standard units are required. + */ +int iio_read_max_channel_raw(struct iio_channel *chan, int *val); + +/** + * iio_read_avail_channel_raw() - read available raw values from a given channel + * @chan: The channel being queried. + * @vals: Available values read back. + * @length: Number of entries in vals. + * + * Returns an error code, IIO_AVAIL_RANGE or IIO_AVAIL_LIST. + * + * For ranges, three vals are always returned; min, step and max. + * For lists, all the possible values are enumerated. + * + * Note raw available values from iio channels are in adc counts and + * hence scale will need to be applied if standard units are required. + */ +int iio_read_avail_channel_raw(struct iio_channel *chan, + const int **vals, int *length); + /** * iio_get_channel_type() - get the type of a channel * @channel: The channel being queried. diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 4591d8ea41bd..3f5ea2e9a39e 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -225,12 +225,22 @@ struct iio_event_spec { * endianness: little or big endian * @info_mask_separate: What information is to be exported that is specific to * this channel. + * @info_mask_separate_available: What availability information is to be + * exported that is specific to this channel. * @info_mask_shared_by_type: What information is to be exported that is shared * by all channels of the same type. + * @info_mask_shared_by_type_available: What availability information is to be + * exported that is shared by all channels of the same + * type. * @info_mask_shared_by_dir: What information is to be exported that is shared * by all channels of the same direction. + * @info_mask_shared_by_dir_available: What availability information is to be + * exported that is shared by all channels of the same + * direction. * @info_mask_shared_by_all: What information is to be exported that is shared * by all channels. + * @info_mask_shared_by_all_available: What availability information is to be + * exported that is shared by all channels. * @event_spec: Array of events which should be registered for this * channel. * @num_event_specs: Size of the event_spec array. @@ -269,9 +279,13 @@ struct iio_chan_spec { enum iio_endian endianness; } scan_type; long info_mask_separate; + long info_mask_separate_available; long info_mask_shared_by_type; + long info_mask_shared_by_type_available; long info_mask_shared_by_dir; + long info_mask_shared_by_dir_available; long info_mask_shared_by_all; + long info_mask_shared_by_all_available; const struct iio_event_spec *event_spec; unsigned int num_event_specs; const struct iio_chan_spec_ext_info *ext_info; @@ -301,6 +315,23 @@ static inline bool iio_channel_has_info(const struct iio_chan_spec *chan, (chan->info_mask_shared_by_all & BIT(type)); } +/** + * iio_channel_has_available() - Checks if a channel has an available attribute + * @chan: The channel to be queried + * @type: Type of the available attribute to be checked + * + * Returns true if the channel supports reporting available values for the + * given attribute type, false otherwise. + */ +static inline bool iio_channel_has_available(const struct iio_chan_spec *chan, + enum iio_chan_info_enum type) +{ + return (chan->info_mask_separate_available & BIT(type)) | + (chan->info_mask_shared_by_type_available & BIT(type)) | + (chan->info_mask_shared_by_dir_available & BIT(type)) | + (chan->info_mask_shared_by_all_available & BIT(type)); +} + #define IIO_CHAN_SOFT_TIMESTAMP(_si) { \ .type = IIO_TIMESTAMP, \ .channel = -1, \ @@ -349,6 +380,14 @@ struct iio_dev; * max_len specifies maximum number of elements * vals pointer can contain. val_len is used to return * length of valid elements in vals. + * @read_avail: function to return the available values from the device. + * mask specifies which value. Note 0 means the available + * values for the channel in question. Return value + * specifies if a IIO_AVAIL_LIST or a IIO_AVAIL_RANGE is + * returned in vals. The type of the vals are returned in + * type and the number of vals is returned in length. For + * ranges, there are always three vals returned; min, step + * and max. For lists, all possible values are enumerated. * @write_raw: function to write a value to the device. * Parameters are the same as for read_raw. * @write_raw_get_fmt: callback function to query the expected @@ -397,6 +436,13 @@ struct iio_info { int *val_len, long mask); + int (*read_avail)(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, + int *type, + int *length, + long mask); + int (*write_raw)(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h index 32b579525004..2aa7b6384d64 100644 --- a/include/linux/iio/types.h +++ b/include/linux/iio/types.h @@ -29,4 +29,9 @@ enum iio_event_info { #define IIO_VAL_FRACTIONAL 10 #define IIO_VAL_FRACTIONAL_LOG2 11 +enum iio_available_type { + IIO_AVAIL_LIST, + IIO_AVAIL_RANGE, +}; + #endif /* _IIO_TYPES_H_ */