diff options
Diffstat (limited to 'drivers/iio')
40 files changed, 2367 insertions, 171 deletions
diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c index bfec313..a7e68c8 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -451,9 +451,9 @@ static const struct iio_chan_spec_ext_info bma180_ext_info[] = { .type = IIO_ACCEL, \ .modified = 1, \ .channel2 = IIO_MOD_##_axis, \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ .scan_index = AXIS_##_axis, \ .scan_type = { \ .sign = 's', \ diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 2209f28..c02c4fb 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -197,6 +197,16 @@ config TWL6030_GPADC This driver can also be built as a module. If so, the module will be called twl6030-gpadc. +config VF610_ADC + tristate "Freescale vf610 ADC driver" + depends on OF + help + Say yes here to support for Vybrid board analog-to-digital converter. + Since the IP is used for i.MX6SLX, the driver also support i.MX6SLX. + + This driver can also be built as a module. If so, the module will be + called vf610_adc. + config VIPERBOARD_ADC tristate "Viperboard ADC support" depends on MFD_VIPERBOARD && USB diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index ba9a10a..6d96b0f 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -21,4 +21,5 @@ obj-$(CONFIG_NAU7802) += nau7802.o obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o +obj-$(CONFIG_VF610_ADC) += vf610_adc.o obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c index 3602592..9cf3229 100644 --- a/drivers/iio/adc/max1363.c +++ b/drivers/iio/adc/max1363.c @@ -8,17 +8,11 @@ * based on linux/drivers/acron/char/pcf8583.c * Copyright (C) 2000 Russell King * + * Driver for max1363 and similar chips. + * * 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. - * - * max1363.c - * - * Partial support for max1363 and similar chips. - * - * Not currently implemented. - * - * - Control of internal reference. */ #include <linux/interrupt.h> @@ -1253,7 +1247,7 @@ static const struct max1363_chip_info max1363_chip_info_tbl[] = { }, [max11604] = { .bits = 8, - .int_vref_mv = 4098, + .int_vref_mv = 4096, .mode_list = max1238_mode_list, .num_modes = ARRAY_SIZE(max1238_mode_list), .default_mode = s0to11, @@ -1313,7 +1307,7 @@ static const struct max1363_chip_info max1363_chip_info_tbl[] = { }, [max11610] = { .bits = 10, - .int_vref_mv = 4098, + .int_vref_mv = 4096, .mode_list = max1238_mode_list, .num_modes = ARRAY_SIZE(max1238_mode_list), .default_mode = s0to11, @@ -1373,7 +1367,7 @@ static const struct max1363_chip_info max1363_chip_info_tbl[] = { }, [max11616] = { .bits = 12, - .int_vref_mv = 4098, + .int_vref_mv = 4096, .mode_list = max1238_mode_list, .num_modes = ARRAY_SIZE(max1238_mode_list), .default_mode = s0to11, diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c index 31e786e..a4db302 100644 --- a/drivers/iio/adc/ti_am335x_adc.c +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -13,7 +13,6 @@ * GNU General Public License for more details. */ -#include <linux/init.h> #include <linux/kernel.h> #include <linux/err.h> #include <linux/module.h> diff --git a/drivers/iio/adc/twl6030-gpadc.c b/drivers/iio/adc/twl6030-gpadc.c index 53a24eb..15282f1 100644 --- a/drivers/iio/adc/twl6030-gpadc.c +++ b/drivers/iio/adc/twl6030-gpadc.c @@ -28,7 +28,6 @@ * 02110-1301 USA * */ -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/module.h> diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c new file mode 100644 index 0000000..37f542e --- /dev/null +++ b/drivers/iio/adc/vf610_adc.c @@ -0,0 +1,711 @@ +/* + * Freescale Vybrid vf610 ADC driver + * + * Copyright 2013 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/completion.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/regulator/consumer.h> +#include <linux/of_platform.h> +#include <linux/err.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/driver.h> + +/* This will be the driver name the kernel reports */ +#define DRIVER_NAME "vf610-adc" + +/* Vybrid/IMX ADC registers */ +#define VF610_REG_ADC_HC0 0x00 +#define VF610_REG_ADC_HC1 0x04 +#define VF610_REG_ADC_HS 0x08 +#define VF610_REG_ADC_R0 0x0c +#define VF610_REG_ADC_R1 0x10 +#define VF610_REG_ADC_CFG 0x14 +#define VF610_REG_ADC_GC 0x18 +#define VF610_REG_ADC_GS 0x1c +#define VF610_REG_ADC_CV 0x20 +#define VF610_REG_ADC_OFS 0x24 +#define VF610_REG_ADC_CAL 0x28 +#define VF610_REG_ADC_PCTL 0x30 + +/* Configuration register field define */ +#define VF610_ADC_MODE_BIT8 0x00 +#define VF610_ADC_MODE_BIT10 0x04 +#define VF610_ADC_MODE_BIT12 0x08 +#define VF610_ADC_MODE_MASK 0x0c +#define VF610_ADC_BUSCLK2_SEL 0x01 +#define VF610_ADC_ALTCLK_SEL 0x02 +#define VF610_ADC_ADACK_SEL 0x03 +#define VF610_ADC_ADCCLK_MASK 0x03 +#define VF610_ADC_CLK_DIV2 0x20 +#define VF610_ADC_CLK_DIV4 0x40 +#define VF610_ADC_CLK_DIV8 0x60 +#define VF610_ADC_CLK_MASK 0x60 +#define VF610_ADC_ADLSMP_LONG 0x10 +#define VF610_ADC_ADSTS_MASK 0x300 +#define VF610_ADC_ADLPC_EN 0x80 +#define VF610_ADC_ADHSC_EN 0x400 +#define VF610_ADC_REFSEL_VALT 0x100 +#define VF610_ADC_REFSEL_VBG 0x1000 +#define VF610_ADC_ADTRG_HARD 0x2000 +#define VF610_ADC_AVGS_8 0x4000 +#define VF610_ADC_AVGS_16 0x8000 +#define VF610_ADC_AVGS_32 0xC000 +#define VF610_ADC_AVGS_MASK 0xC000 +#define VF610_ADC_OVWREN 0x10000 + +/* General control register field define */ +#define VF610_ADC_ADACKEN 0x1 +#define VF610_ADC_DMAEN 0x2 +#define VF610_ADC_ACREN 0x4 +#define VF610_ADC_ACFGT 0x8 +#define VF610_ADC_ACFE 0x10 +#define VF610_ADC_AVGEN 0x20 +#define VF610_ADC_ADCON 0x40 +#define VF610_ADC_CAL 0x80 + +/* Other field define */ +#define VF610_ADC_ADCHC(x) ((x) & 0xF) +#define VF610_ADC_AIEN (0x1 << 7) +#define VF610_ADC_CONV_DISABLE 0x1F +#define VF610_ADC_HS_COCO0 0x1 +#define VF610_ADC_CALF 0x2 +#define VF610_ADC_TIMEOUT msecs_to_jiffies(100) + +enum clk_sel { + VF610_ADCIOC_BUSCLK_SET, + VF610_ADCIOC_ALTCLK_SET, + VF610_ADCIOC_ADACK_SET, +}; + +enum vol_ref { + VF610_ADCIOC_VR_VREF_SET, + VF610_ADCIOC_VR_VALT_SET, + VF610_ADCIOC_VR_VBG_SET, +}; + +enum average_sel { + VF610_ADC_SAMPLE_1, + VF610_ADC_SAMPLE_4, + VF610_ADC_SAMPLE_8, + VF610_ADC_SAMPLE_16, + VF610_ADC_SAMPLE_32, +}; + +struct vf610_adc_feature { + enum clk_sel clk_sel; + enum vol_ref vol_ref; + + int clk_div; + int sample_rate; + int res_mode; + + bool lpm; + bool calibration; + bool ovwren; +}; + +struct vf610_adc { + struct device *dev; + void __iomem *regs; + struct clk *clk; + + u32 vref_uv; + u32 value; + struct regulator *vref; + struct vf610_adc_feature adc_feature; + + struct completion completion; +}; + +#define VF610_ADC_CHAN(_idx, _chan_type) { \ + .type = (_chan_type), \ + .indexed = 1, \ + .channel = (_idx), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ +} + +static const struct iio_chan_spec vf610_adc_iio_channels[] = { + VF610_ADC_CHAN(0, IIO_VOLTAGE), + VF610_ADC_CHAN(1, IIO_VOLTAGE), + VF610_ADC_CHAN(2, IIO_VOLTAGE), + VF610_ADC_CHAN(3, IIO_VOLTAGE), + VF610_ADC_CHAN(4, IIO_VOLTAGE), + VF610_ADC_CHAN(5, IIO_VOLTAGE), + VF610_ADC_CHAN(6, IIO_VOLTAGE), + VF610_ADC_CHAN(7, IIO_VOLTAGE), + VF610_ADC_CHAN(8, IIO_VOLTAGE), + VF610_ADC_CHAN(9, IIO_VOLTAGE), + VF610_ADC_CHAN(10, IIO_VOLTAGE), + VF610_ADC_CHAN(11, IIO_VOLTAGE), + VF610_ADC_CHAN(12, IIO_VOLTAGE), + VF610_ADC_CHAN(13, IIO_VOLTAGE), + VF610_ADC_CHAN(14, IIO_VOLTAGE), + VF610_ADC_CHAN(15, IIO_VOLTAGE), + /* sentinel */ +}; + +/* + * ADC sample frequency, unit is ADCK cycles. + * ADC clk source is ipg clock, which is the same as bus clock. + * + * ADC conversion time = SFCAdder + AverageNum x (BCT + LSTAdder) + * SFCAdder: fixed to 6 ADCK cycles + * AverageNum: 1, 4, 8, 16, 32 samples for hardware average. + * BCT (Base Conversion Time): fixed to 25 ADCK cycles for 12 bit mode + * LSTAdder(Long Sample Time): fixed to 3 ADCK cycles + * + * By default, enable 12 bit resolution mode, clock source + * set to ipg clock, So get below frequency group: + */ +static const u32 vf610_sample_freq_avail[5] = +{1941176, 559332, 286957, 145374, 73171}; + +static inline void vf610_adc_cfg_init(struct vf610_adc *info) +{ + /* set default Configuration for ADC controller */ + info->adc_feature.clk_sel = VF610_ADCIOC_BUSCLK_SET; + info->adc_feature.vol_ref = VF610_ADCIOC_VR_VREF_SET; + + info->adc_feature.calibration = true; + info->adc_feature.ovwren = true; + + info->adc_feature.clk_div = 1; + info->adc_feature.res_mode = 12; + info->adc_feature.sample_rate = 1; + info->adc_feature.lpm = true; +} + +static void vf610_adc_cfg_post_set(struct vf610_adc *info) +{ + struct vf610_adc_feature *adc_feature = &info->adc_feature; + int cfg_data = 0; + int gc_data = 0; + + switch (adc_feature->clk_sel) { + case VF610_ADCIOC_ALTCLK_SET: + cfg_data |= VF610_ADC_ALTCLK_SEL; + break; + case VF610_ADCIOC_ADACK_SET: + cfg_data |= VF610_ADC_ADACK_SEL; + break; + default: + break; + } + + /* low power set for calibration */ + cfg_data |= VF610_ADC_ADLPC_EN; + + /* enable high speed for calibration */ + cfg_data |= VF610_ADC_ADHSC_EN; + + /* voltage reference */ + switch (adc_feature->vol_ref) { + case VF610_ADCIOC_VR_VREF_SET: + break; + case VF610_ADCIOC_VR_VALT_SET: + cfg_data |= VF610_ADC_REFSEL_VALT; + break; + case VF610_ADCIOC_VR_VBG_SET: + cfg_data |= VF610_ADC_REFSEL_VBG; + break; + default: + dev_err(info->dev, "error voltage reference\n"); + } + + /* data overwrite enable */ + if (adc_feature->ovwren) + cfg_data |= VF610_ADC_OVWREN; + + writel(cfg_data, info->regs + VF610_REG_ADC_CFG); + writel(gc_data, info->regs + VF610_REG_ADC_GC); +} + +static void vf610_adc_calibration(struct vf610_adc *info) +{ + int adc_gc, hc_cfg; + int timeout; + + if (!info->adc_feature.calibration) + return; + + /* enable calibration interrupt */ + hc_cfg = VF610_ADC_AIEN | VF610_ADC_CONV_DISABLE; + writel(hc_cfg, info->regs + VF610_REG_ADC_HC0); + + adc_gc = readl(info->regs + VF610_REG_ADC_GC); + writel(adc_gc | VF610_ADC_CAL, info->regs + VF610_REG_ADC_GC); + + timeout = wait_for_completion_timeout + (&info->completion, VF610_ADC_TIMEOUT); + if (timeout == 0) + dev_err(info->dev, "Timeout for adc calibration\n"); + + adc_gc = readl(info->regs + VF610_REG_ADC_GS); + if (adc_gc & VF610_ADC_CALF) + dev_err(info->dev, "ADC calibration failed\n"); + + info->adc_feature.calibration = false; +} + +static void vf610_adc_cfg_set(struct vf610_adc *info) +{ + struct vf610_adc_feature *adc_feature = &(info->adc_feature); + int cfg_data; + + cfg_data = readl(info->regs + VF610_REG_ADC_CFG); + + /* low power configuration */ + cfg_data &= ~VF610_ADC_ADLPC_EN; + if (adc_feature->lpm) + cfg_data |= VF610_ADC_ADLPC_EN; + + /* disable high speed */ + cfg_data &= ~VF610_ADC_ADHSC_EN; + + writel(cfg_data, info->regs + VF610_REG_ADC_CFG); +} + +static void vf610_adc_sample_set(struct vf610_adc *info) +{ + struct vf610_adc_feature *adc_feature = &(info->adc_feature); + int cfg_data, gc_data; + + cfg_data = readl(info->regs + VF610_REG_ADC_CFG); + gc_data = readl(info->regs + VF610_REG_ADC_GC); + + /* resolution mode */ + cfg_data &= ~VF610_ADC_MODE_MASK; + switch (adc_feature->res_mode) { + case 8: + cfg_data |= VF610_ADC_MODE_BIT8; + break; + case 10: + cfg_data |= VF610_ADC_MODE_BIT10; + break; + case 12: + cfg_data |= VF610_ADC_MODE_BIT12; + break; + default: + dev_err(info->dev, "error resolution mode\n"); + break; + } + + /* clock select and clock divider */ + cfg_data &= ~(VF610_ADC_CLK_MASK | VF610_ADC_ADCCLK_MASK); + switch (adc_feature->clk_div) { + case 1: + break; + case 2: + cfg_data |= VF610_ADC_CLK_DIV2; + break; + case 4: + cfg_data |= VF610_ADC_CLK_DIV4; + break; + case 8: + cfg_data |= VF610_ADC_CLK_DIV8; + break; + case 16: + switch (adc_feature->clk_sel) { + case VF610_ADCIOC_BUSCLK_SET: + cfg_data |= VF610_ADC_BUSCLK2_SEL | VF610_ADC_CLK_DIV8; + break; + default: + dev_err(info->dev, "error clk divider\n"); + break; + } + break; + } + + /* Use the short sample mode */ + cfg_data &= ~(VF610_ADC_ADLSMP_LONG | VF610_ADC_ADSTS_MASK); + + /* update hardware average selection */ + cfg_data &= ~VF610_ADC_AVGS_MASK; + gc_data &= ~VF610_ADC_AVGEN; + switch (adc_feature->sample_rate) { + case VF610_ADC_SAMPLE_1: + break; + case VF610_ADC_SAMPLE_4: + gc_data |= VF610_ADC_AVGEN; + break; + case VF610_ADC_SAMPLE_8: + gc_data |= VF610_ADC_AVGEN; + cfg_data |= VF610_ADC_AVGS_8; + break; + case VF610_ADC_SAMPLE_16: + gc_data |= VF610_ADC_AVGEN; + cfg_data |= VF610_ADC_AVGS_16; + break; + case VF610_ADC_SAMPLE_32: + gc_data |= VF610_ADC_AVGEN; + cfg_data |= VF610_ADC_AVGS_32; + break; + default: + dev_err(info->dev, + "error hardware sample average select\n"); + } + + writel(cfg_data, info->regs + VF610_REG_ADC_CFG); + writel(gc_data, info->regs + VF610_REG_ADC_GC); +} + +static void vf610_adc_hw_init(struct vf610_adc *info) +{ + /* CFG: Feature set */ + vf610_adc_cfg_post_set(info); + vf610_adc_sample_set(info); + + /* adc calibration */ + vf610_adc_calibration(info); + + /* CFG: power and speed set */ + vf610_adc_cfg_set(info); +} + +static int vf610_adc_read_data(struct vf610_adc *info) +{ + int result; + + result = readl(info->regs + VF610_REG_ADC_R0); + + switch (info->adc_feature.res_mode) { + case 8: + result &= 0xFF; + break; + case 10: + result &= 0x3FF; + break; + case 12: + result &= 0xFFF; + break; + default: + break; + } + + return result; +} + +static irqreturn_t vf610_adc_isr(int irq, void *dev_id) +{ + struct vf610_adc *info = (struct vf610_adc *)dev_id; + int coco; + + coco = readl(info->regs + VF610_REG_ADC_HS); + if (coco & VF610_ADC_HS_COCO0) { + info->value = vf610_adc_read_data(info); + complete(&info->completion); + } + + return IRQ_HANDLED; +} + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("1941176, 559332, 286957, 145374, 73171"); + +static struct attribute *vf610_attributes[] = { + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group vf610_attribute_group = { + .attrs = vf610_attributes, +}; + +static int vf610_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + struct vf610_adc *info = iio_priv(indio_dev); + unsigned int hc_cfg; + unsigned long ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&indio_dev->mlock); + reinit_completion(&info->completion); + + hc_cfg = VF610_ADC_ADCHC(chan->channel); + hc_cfg |= VF610_ADC_AIEN; + writel(hc_cfg, info->regs + VF610_REG_ADC_HC0); + ret = wait_for_completion_interruptible_timeout + (&info->completion, VF610_ADC_TIMEOUT); + if (ret == 0) { + mutex_unlock(&indio_dev->mlock); + return -ETIMEDOUT; + } + if (ret < 0) { + mutex_unlock(&indio_dev->mlock); + return ret; + } + + *val = info->value; + mutex_unlock(&indio_dev->mlock); + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + *val = info->vref_uv / 1000; + *val2 = info->adc_feature.res_mode; + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_SAMP_FREQ: + *val = vf610_sample_freq_avail[info->adc_feature.sample_rate]; + *val2 = 0; + return IIO_VAL_INT; + + default: + break; + } + + return -EINVAL; +} + +static int vf610_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct vf610_adc *info = iio_priv(indio_dev); + int i; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + for (i = 0; + i < ARRAY_SIZE(vf610_sample_freq_avail); + i++) + if (val == vf610_sample_freq_avail[i]) { + info->adc_feature.sample_rate = i; + vf610_adc_sample_set(info); + return 0; + } + break; + + default: + break; + } + + return -EINVAL; +} + +static int vf610_adc_reg_access(struct iio_dev *indio_dev, + unsigned reg, unsigned writeval, + unsigned *readval) +{ + struct vf610_adc *info = iio_priv(indio_dev); + + if ((readval == NULL) || + (!(reg % 4) || (reg > VF610_REG_ADC_PCTL))) + return -EINVAL; + + *readval = readl(info->regs + reg); + + return 0; +} + +static const struct iio_info vf610_adc_iio_info = { + .driver_module = THIS_MODULE, + .read_raw = &vf610_read_raw, + .write_raw = &vf610_write_raw, + .debugfs_reg_access = &vf610_adc_reg_access, + .attrs = &vf610_attribute_group, +}; + +static const struct of_device_id vf610_adc_match[] = { + { .compatible = "fsl,vf610-adc", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, vf610_adc_match); + +static int vf610_adc_probe(struct platform_device *pdev) +{ + struct vf610_adc *info; + struct iio_dev *indio_dev; + struct resource *mem; + int irq; + int ret; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct vf610_adc)); + if (!indio_dev) { + dev_err(&pdev->dev, "Failed allocating iio device\n"); + return -ENOMEM; + } + + info = iio_priv(indio_dev); + info->dev = &pdev->dev; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + info->regs = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(info->regs)) + return PTR_ERR(info->regs); + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(&pdev->dev, "no irq resource?\n"); + return -EINVAL; + } + + ret = devm_request_irq(info->dev, irq, + vf610_adc_isr, 0, + dev_name(&pdev->dev), info); + if (ret < 0) { + dev_err(&pdev->dev, "failed requesting irq, irq = %d\n", irq); + return ret; + } + + info->clk = devm_clk_get(&pdev->dev, "adc"); + if (IS_ERR(info->clk)) { + dev_err(&pdev->dev, "failed getting clock, err = %ld\n", + PTR_ERR(info->clk)); + ret = PTR_ERR(info->clk); + return ret; + } + + info->vref = devm_regulator_get(&pdev->dev, "vref"); + if (IS_ERR(info->vref)) + return PTR_ERR(info->vref); + + ret = regulator_enable(info->vref); + if (ret) + return ret; + + info->vref_uv = regulator_get_voltage(info->vref); + + platform_set_drvdata(pdev, indio_dev); + + init_completion(&info->completion); + + indio_dev->name = dev_name(&pdev->dev); + indio_dev->dev.parent = &pdev->dev; + indio_dev->dev.of_node = pdev->dev.of_node; + indio_dev->info = &vf610_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = vf610_adc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(vf610_adc_iio_channels); + + ret = clk_prepare_enable(info->clk); + if (ret) { + dev_err(&pdev->dev, + "Could not prepare or enable the clock.\n"); + goto error_adc_clk_enable; + } + + vf610_adc_cfg_init(info); + vf610_adc_hw_init(info); + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "Couldn't register the device.\n"); + goto error_iio_device_register; + } + + return 0; + + +error_iio_device_register: + clk_disable_unprepare(info->clk); +error_adc_clk_enable: + regulator_disable(info->vref); + + return ret; +} + +static int vf610_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct vf610_adc *info = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + regulator_disable(info->vref); + clk_disable_unprepare(info->clk); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int vf610_adc_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct vf610_adc *info = iio_priv(indio_dev); + int hc_cfg; + + /* ADC controller enters to stop mode */ + hc_cfg = readl(info->regs + VF610_REG_ADC_HC0); + hc_cfg |= VF610_ADC_CONV_DISABLE; + writel(hc_cfg, info->regs + VF610_REG_ADC_HC0); + + clk_disable_unprepare(info->clk); + regulator_disable(info->vref); + + return 0; +} + +static int vf610_adc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct vf610_adc *info = iio_priv(indio_dev); + int ret; + + ret = regulator_enable(info->vref); + if (ret) + return ret; + + ret = clk_prepare_enable(info->clk); + if (ret) + return ret; + + vf610_adc_hw_init(info); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(vf610_adc_pm_ops, + vf610_adc_suspend, + vf610_adc_resume); + +static struct platform_driver vf610_adc_driver = { + .probe = vf610_adc_probe, + .remove = vf610_adc_remove, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = vf610_adc_match, + .pm = &vf610_adc_pm_ops, + }, +}; + +module_platform_driver(vf610_adc_driver); + +MODULE_AUTHOR("Fugang Duan <B38611@freescale.com>"); +MODULE_DESCRIPTION("Freescale VF610 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/viperboard_adc.c b/drivers/iio/adc/viperboard_adc.c index d0add8f..9acf6b6 100644 --- a/drivers/iio/adc/viperboard_adc.c +++ b/drivers/iio/adc/viperboard_adc.c @@ -139,8 +139,6 @@ static int vprbrd_adc_probe(struct platform_device *pdev) return ret; } - platform_set_drvdata(pdev, indio_dev); - return 0; } diff --git a/drivers/iio/buffer_cb.c b/drivers/iio/buffer_cb.c index 2d9c6f8..eb46e72 100644 --- a/drivers/iio/buffer_cb.c +++ b/drivers/iio/buffer_cb.c @@ -46,10 +46,8 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev, struct iio_channel *chan; cb_buff = kzalloc(sizeof(*cb_buff), GFP_KERNEL); - if (cb_buff == NULL) { - ret = -ENOMEM; - goto error_ret; - } + if (cb_buff == NULL) + return ERR_PTR(-ENOMEM); iio_buffer_init(&cb_buff->buffer); @@ -91,7 +89,6 @@ error_release_channels: iio_channel_release_all(cb_buff->channels); error_free_cb_buff: kfree(cb_buff); -error_ret: return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(iio_channel_get_all_cb); diff --git a/drivers/iio/dac/ad7303.c b/drivers/iio/dac/ad7303.c index d0505fd..fa28100 100644 --- a/drivers/iio/dac/ad7303.c +++ b/drivers/iio/dac/ad7303.c @@ -92,7 +92,7 @@ static ssize_t ad7303_write_dac_powerdown(struct iio_dev *indio_dev, ad7303_write(st, chan->channel, st->dac_cache[chan->channel]); mutex_unlock(&indio_dev->mlock); - return ret ? ret : len; + return len; } static int ad7303_get_vref(struct ad7303_state *st, diff --git a/drivers/iio/dac/max517.c b/drivers/iio/dac/max517.c index de76e6a..9a82a72 100644 --- a/drivers/iio/dac/max517.c +++ b/drivers/iio/dac/max517.c @@ -19,7 +19,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/jiffies.h> #include <linux/i2c.h> diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c index 7d9f5c3..43d1458 100644 --- a/drivers/iio/dac/mcp4725.c +++ b/drivers/iio/dac/mcp4725.c @@ -15,7 +15,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/i2c.h> #include <linux/err.h> #include <linux/delay.h> diff --git a/drivers/iio/humidity/Kconfig b/drivers/iio/humidity/Kconfig index 463c4d9..e116bd8 100644 --- a/drivers/iio/humidity/Kconfig +++ b/drivers/iio/humidity/Kconfig @@ -12,4 +12,14 @@ config DHT11 Other sensors should work as well as long as they speak the same protocol. +config SI7005 + tristate "SI7005 relative humidity and temperature sensor" + depends on I2C + help + Say yes here to build support for the Silabs Si7005 relative + humidity and temperature sensor. + + To compile this driver as a module, choose M here: the module + will be called si7005. + endmenu diff --git a/drivers/iio/humidity/Makefile b/drivers/iio/humidity/Makefile index d5d36c0..e3f3d94 100644 --- a/drivers/iio/humidity/Makefile +++ b/drivers/iio/humidity/Makefile @@ -3,3 +3,4 @@ # obj-$(CONFIG_DHT11) += dht11.o +obj-$(CONFIG_SI7005) += si7005.o diff --git a/drivers/iio/humidity/si7005.c b/drivers/iio/humidity/si7005.c new file mode 100644 index 0000000..bdd586e --- /dev/null +++ b/drivers/iio/humidity/si7005.c @@ -0,0 +1,189 @@ +/* + * si7005.c - Support for Silabs Si7005 humidity and temperature sensor + * + * Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net> + * + * This file is subject to the terms and conditions of version 2 of + * the GNU General Public License. See the file COPYING in the main + * directory of this archive for more details. + * + * (7-bit I2C slave address 0x40) + * + * TODO: heater, fast mode, processed mode (temp. / linearity compensation) + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/pm.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#define SI7005_STATUS 0x00 +#define SI7005_DATA 0x01 /* 16-bit, MSB */ +#define SI7005_CONFIG 0x03 +#define SI7005_ID 0x11 + +#define SI7005_STATUS_NRDY BIT(0) +#define SI7005_CONFIG_TEMP BIT(4) +#define SI7005_CONFIG_START BIT(0) + +#define SI7005_ID_7005 0x50 +#define SI7005_ID_7015 0xf0 + +struct si7005_data { + struct i2c_client *client; + struct mutex lock; + u8 config; +}; + +static int si7005_read_measurement(struct si7005_data *data, bool temp) +{ + int tries = 50; + int ret; + + mutex_lock(&data->lock); + + ret = i2c_smbus_write_byte_data(data->client, SI7005_CONFIG, + data->config | SI7005_CONFIG_START | + (temp ? SI7005_CONFIG_TEMP : 0)); + if (ret < 0) + goto done; + + while (tries-- > 0) { + msleep(20); + ret = i2c_smbus_read_byte_data(data->client, SI7005_STATUS); + if (ret < 0) + goto done; + if (!(ret & SI7005_STATUS_NRDY)) + break; + } + if (tries < 0) { + ret = -EIO; + goto done; + } + + ret = i2c_smbus_read_word_swapped(data->client, SI7005_DATA); + +done: + mutex_unlock(&data->lock); + + return ret; +} + +static int si7005_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct si7005_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = si7005_read_measurement(data, chan->type == IIO_TEMP); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + if (chan->type == IIO_TEMP) { + *val = 7; + *val2 = 812500; + } else { + *val = 3; + *val2 = 906250; + } + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_OFFSET: + if (chan->type == IIO_TEMP) + *val = -50 * 32 * 4; + else + *val = -24 * 16 * 16; + return IIO_VAL_INT; + default: + break; + } + + return -EINVAL; +} + +static const struct iio_chan_spec si7005_channels[] = { + { + .type = IIO_HUMIDITYRELATIVE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), + }, + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), + } +}; + +static const struct iio_info si7005_info = { + .read_raw = si7005_read_raw, + .driver_module = THIS_MODULE, +}; + +static int si7005_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct iio_dev *indio_dev; + struct si7005_data *data; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + mutex_init(&data->lock); + + indio_dev->dev.parent = &client->dev; + indio_dev->name = dev_name(&client->dev); + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &si7005_info; + + indio_dev->channels = si7005_channels; + indio_dev->num_channels = ARRAY_SIZE(si7005_channels); + + ret = i2c_smbus_read_byte_data(client, SI7005_ID); + if (ret < 0) + return ret; + if (ret != SI7005_ID_7005 && ret != SI7005_ID_7015) + return -ENODEV; + + ret = i2c_smbus_read_byte_data(client, SI7005_CONFIG); + if (ret < 0) + return ret; + data->config = ret; + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct i2c_device_id si7005_id[] = { + { "si7005", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, si7005_id); + +static struct i2c_driver si7005_driver = { + .driver = { + .name = "si7005", + .owner = THIS_MODULE, + }, + .probe = si7005_probe, + .id_table = si7005_id, +}; +module_i2c_driver(si7005_driver); + +MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); +MODULE_DESCRIPTION("Silabs Si7005 humidity and temperature sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig index 663e88a..2b0e451 100644 --- a/drivers/iio/imu/Kconfig +++ b/drivers/iio/imu/Kconfig @@ -25,6 +25,8 @@ config ADIS16480 Say yes here to build support for Analog Devices ADIS16375, ADIS16480, ADIS16485, ADIS16488 inertial sensors. +source "drivers/iio/imu/inv_mpu6050/Kconfig" + endmenu config IIO_ADIS_LIB @@ -38,5 +40,3 @@ config IIO_ADIS_LIB_BUFFER help A set of buffer helper functions for the Analog Devices ADIS* device family. - -source "drivers/iio/imu/inv_mpu6050/Kconfig" diff --git a/drivers/iio/imu/adis16400_core.c b/drivers/iio/imu/adis16400_core.c index 7c582f7..433583b 100644 --- a/drivers/iio/imu/adis16400_core.c +++ b/drivers/iio/imu/adis16400_core.c @@ -281,7 +281,7 @@ static ssize_t adis16400_write_frequency(struct device *dev, st->variant->set_freq(st, val); mutex_unlock(&indio_dev->mlock); - return ret ? ret : len; + return len; } /* Power down the device */ diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index df7f1e1..cb9f96b 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -12,7 +12,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/i2c.h> #include <linux/err.h> @@ -117,7 +116,7 @@ int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask) return result; if (en) { - /* Wait for output stablize */ + /* Wait for output stabilize */ msleep(INV_MPU6050_TEMP_UP_TIME); if (INV_MPU6050_BIT_PWR_GYRO_STBY == mask) { /* switch internal clock to PLL */ diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index f383955..0ab382b 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -126,35 +126,35 @@ struct inv_mpu6050_state { #define INV_MPU6050_REG_SAMPLE_RATE_DIV 0x19 #define INV_MPU6050_REG_CONFIG 0x1A #define INV_MPU6050_REG_GYRO_CONFIG 0x1B -#define INV_MPU6050_REG_ACCEL_CONFIG 0x1C +#define INV_MPU6050_REG_ACCEL_CONFIG 0x1C #define INV_MPU6050_REG_FIFO_EN 0x23 -#define INV_MPU6050_BIT_ACCEL_OUT 0x08 -#define INV_MPU6050_BITS_GYRO_OUT 0x70 +#define INV_MPU6050_BIT_ACCEL_OUT 0x08 +#define INV_MPU6050_BITS_GYRO_OUT 0x70 #define INV_MPU6050_REG_INT_ENABLE 0x38 -#define INV_MPU6050_BIT_DATA_RDY_EN 0x01 -#define INV_MPU6050_BIT_DMP_INT_EN 0x02 +#define INV_MPU6050_BIT_DATA_RDY_EN 0x01 +#define INV_MPU6050_BIT_DMP_INT_EN 0x02 #define INV_MPU6050_REG_RAW_ACCEL 0x3B #define INV_MPU6050_REG_TEMPERATURE 0x41 #define INV_MPU6050_REG_RAW_GYRO 0x43 #define INV_MPU6050_REG_USER_CTRL 0x6A -#define INV_MPU6050_BIT_FIFO_RST 0x04 -#define INV_MPU6050_BIT_DMP_RST 0x08 -#define INV_MPU6050_BIT_I2C_MST_EN 0x20 -#define INV_MPU6050_BIT_FIFO_EN 0x40 -#define INV_MPU6050_BIT_DMP_EN 0x80 +#define INV_MPU6050_BIT_FIFO_RST 0x04 +#define INV_MPU6050_BIT_DMP_RST 0x08 +#define INV_MPU6050_BIT_I2C_MST_EN 0x20 +#define INV_MPU6050_BIT_FIFO_EN 0x40 +#define INV_MPU6050_BIT_DMP_EN 0x80 #define INV_MPU6050_REG_PWR_MGMT_1 0x6B -#define INV_MPU6050_BIT_H_RESET 0x80 -#define INV_MPU6050_BIT_SLEEP 0x40 -#define INV_MPU6050_BIT_CLK_MASK 0x7 +#define INV_MPU6050_BIT_H_RESET 0x80 +#define INV_MPU6050_BIT_SLEEP 0x40 +#define INV_MPU6050_BIT_CLK_MASK 0x7 #define INV_MPU6050_REG_PWR_MGMT_2 0x6C -#define INV_MPU6050_BIT_PWR_ACCL_STBY 0x38 -#define INV_MPU6050_BIT_PWR_GYRO_STBY 0x07 +#define INV_MPU6050_BIT_PWR_ACCL_STBY 0x38 +#define INV_MPU6050_BIT_PWR_GYRO_STBY 0x07 #define INV_MPU6050_REG_FIFO_COUNT_H 0x72 #define INV_MPU6050_REG_FIFO_R_W 0x74 @@ -180,10 +180,10 @@ struct inv_mpu6050_state { /* init parameters */ #define INV_MPU6050_INIT_FIFO_RATE 50 -#define INV_MPU6050_TIME_STAMP_TOR 5 -#define INV_MPU6050_MAX_FIFO_RATE 1000 -#define INV_MPU6050_MIN_FIFO_RATE 4 -#define INV_MPU6050_ONE_K_HZ 1000 +#define INV_MPU6050_TIME_STAMP_TOR 5 +#define INV_MPU6050_MAX_FIFO_RATE 1000 +#define INV_MPU6050_MIN_FIFO_RATE 4 +#define INV_MPU6050_ONE_K_HZ 1000 /* scan element definition */ enum inv_mpu6050_scan { diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c index 4295171..0cd306a 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c @@ -12,7 +12,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/i2c.h> #include <linux/err.h> diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index c67d83b..e108f2a 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -264,7 +264,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev, &indio_dev->dev, &buffer->scan_el_dev_attr_list); if (ret) - goto error_ret; + return ret; attrcount++; ret = __iio_add_chan_devattr("type", chan, @@ -275,7 +275,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev, &indio_dev->dev, &buffer->scan_el_dev_attr_list); if (ret) - goto error_ret; + return ret; attrcount++; if (chan->type != IIO_TIMESTAMP) ret = __iio_add_chan_devattr("en", @@ -296,10 +296,9 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev, &indio_dev->dev, &buffer->scan_el_dev_attr_list); if (ret) - goto error_ret; + return ret; attrcount++; ret = attrcount; -error_ret: return ret; } @@ -553,13 +552,13 @@ static int __iio_update_buffers(struct iio_dev *indio_dev, if (indio_dev->setup_ops->predisable) { ret = indio_dev->setup_ops->predisable(indio_dev); if (ret) - goto error_ret; + return ret; } indio_dev->currentmode = INDIO_DIRECT_MODE; if (indio_dev->setup_ops->postdisable) { ret = indio_dev->setup_ops->postdisable(indio_dev); if (ret) - goto error_ret; + return ret; } } /* Keep a copy of current setup to allow roll back */ @@ -613,7 +612,7 @@ static int __iio_update_buffers(struct iio_dev *indio_dev, else { kfree(compound_mask); ret = -EINVAL; - goto error_ret; + return ret; } } } else { @@ -696,13 +695,10 @@ error_run_postdisable: if (indio_dev->setup_ops->postdisable) indio_dev->setup_ops->postdisable(indio_dev); error_remove_inserted: - if (insert_buffer) iio_buffer_deactivate(insert_buffer); indio_dev->active_scan_mask = old_mask; kfree(compound_mask); -error_ret: - return ret; } diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index acc911a..ede16aec 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -540,7 +540,7 @@ int __iio_device_attr_init(struct device_attribute *dev_attr, enum iio_shared_by shared_by) { int ret = 0; - char *name_format = NULL; + char *name = NULL; char *full_postfix; sysfs_attr_init(&dev_attr->attr); @@ -558,7 +558,7 @@ int __iio_device_attr_init(struct device_attribute *dev_attr, ->channel2], postfix); } else { - if (chan->extend_name == NULL) + if (chan->extend_name == NULL || shared_by != IIO_SEPARATE) full_postfix = kstrdup(postfix, GFP_KERNEL); else full_postfix = kasprintf(GFP_KERNEL, @@ -572,16 +572,15 @@ int __iio_device_attr_init(struct device_attribute *dev_attr, if (chan->differential) { /* Differential can not have modifier */ switch (shared_by) { case IIO_SHARED_BY_ALL: - name_format = kasprintf(GFP_KERNEL, "%s", full_postfix); + name = kasprintf(GFP_KERNEL, "%s", full_postfix); break; case IIO_SHARED_BY_DIR: - name_format = kasprintf(GFP_KERNEL, "%s_%s", + name = kasprintf(GFP_KERNEL, "%s_%s", iio_direction[chan->output], full_postfix); break; case IIO_SHARED_BY_TYPE: - name_format - = kasprintf(GFP_KERNEL, "%s_%s-%s_%s", + name = kasprintf(GFP_KERNEL, "%s_%s-%s_%s", iio_direction[chan->output], iio_chan_type_name_spec[chan->type], iio_chan_type_name_spec[chan->type], @@ -593,8 +592,7 @@ int __iio_device_attr_init(struct device_attribute *dev_attr, ret = -EINVAL; goto error_free_full_postfix; } - name_format - = kasprintf(GFP_KERNEL, + name = kasprintf(GFP_KERNEL, "%s_%s%d-%s%d_%s", iio_direction[chan->output], iio_chan_type_name_spec[chan->type], @@ -607,16 +605,15 @@ int __iio_device_attr_init(struct device_attribute *dev_attr, } else { /* Single ended */ switch (shared_by) { case IIO_SHARED_BY_ALL: - name_format = kasprintf(GFP_KERNEL, "%s", full_postfix); + name = kasprintf(GFP_KERNEL, "%s", full_postfix); break; case IIO_SHARED_BY_DIR: - name_format = kasprintf(GFP_KERNEL, "%s_%s", + name = kasprintf(GFP_KERNEL, "%s_%s", iio_direction[chan->output], full_postfix); break; case IIO_SHARED_BY_TYPE: - name_format - = kasprintf(GFP_KERNEL, "%s_%s_%s", + name = kasprintf(GFP_KERNEL, "%s_%s_%s", iio_direction[chan->output], iio_chan_type_name_spec[chan->type], full_postfix); @@ -624,33 +621,24 @@ int __iio_device_attr_init(struct device_attribute *dev_attr, case IIO_SEPARATE: if (chan->indexed) - name_format - = kasprintf(GFP_KERNEL, "%s_%s%d_%s", + name = kasprintf(GFP_KERNEL, "%s_%s%d_%s", iio_direction[chan->output], iio_chan_type_name_spec[chan->type], chan->channel, full_postfix); else - name_format - = kasprintf(GFP_KERNEL, "%s_%s_%s", + name = kasprintf(GFP_KERNEL, "%s_%s_%s", iio_direction[chan->output], iio_chan_type_name_spec[chan->type], full_postfix); break; } } - if (name_format == NULL) { + if (name == NULL) { ret = -ENOMEM; goto error_free_full_postfix; } - dev_attr->attr.name = kasprintf(GFP_KERNEL, - name_format, - chan->channel, - chan->channel2); - if (dev_attr->attr.name == NULL) { - ret = -ENOMEM; - goto error_free_name_format; - } + dev_attr->attr.name = name; if (readfunc) { dev_attr->attr.mode |= S_IRUGO; @@ -661,8 +649,7 @@ int __iio_device_attr_init(struct device_attribute *dev_attr, dev_attr->attr.mode |= S_IWUSR; dev_attr->store = writefunc; } -error_free_name_format: - kfree(name_format); + error_free_full_postfix: kfree(full_postfix); @@ -692,10 +679,8 @@ int __iio_add_chan_devattr(const char *postfix, struct iio_dev_attr *iio_attr, *t; iio_attr = kzalloc(sizeof(*iio_attr), GFP_KERNEL); - if (iio_attr == NULL) { - ret = -ENOMEM; - goto error_ret; - } + if (iio_attr == NULL) + return -ENOMEM; ret = __iio_device_attr_init(&iio_attr->dev_attr, postfix, chan, readfunc, writefunc, shared_by); @@ -720,7 +705,6 @@ error_device_attr_deinit: __iio_device_attr_deinit(&iio_attr->dev_attr); error_iio_dev_attr_free: kfree(iio_attr); -error_ret: return ret; } @@ -1134,7 +1118,7 @@ int iio_device_register(struct iio_dev *indio_dev) if (ret) { dev_err(indio_dev->dev.parent, "Failed to register debugfs interfaces\n"); - goto error_ret; + return ret; } ret = iio_device_register_sysfs(indio_dev); if (ret) { @@ -1175,7 +1159,6 @@ error_free_sysfs: iio_device_unregister_sysfs(indio_dev); error_unreg_debugfs: iio_device_unregister_debugfs(indio_dev); -error_ret: return ret; } EXPORT_SYMBOL(iio_device_register); diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c index c9c1419..ea6e06b 100644 --- a/drivers/iio/industrialio-event.c +++ b/drivers/iio/industrialio-event.c @@ -40,6 +40,7 @@ struct iio_event_interface { struct list_head dev_attr_list; unsigned long flags; struct attribute_group group; + struct mutex read_lock; }; /** @@ -47,16 +48,17 @@ struct iio_event_interface { * @indio_dev: IIO device structure * @ev_code: What event * @timestamp: When the event occurred + * + * Note: The caller must make sure that this function is not running + * concurrently for the same indio_dev more than once. **/ int iio_push_event(struct iio_dev *indio_dev, u64 ev_code, s64 timestamp) { struct iio_event_interface *ev_int = indio_dev->event_interface; struct iio_event_data ev; - unsigned long flags; int copied; /* Does anyone care? */ - spin_lock_irqsave(&ev_int->wait.lock, flags); if (test_bit(IIO_BUSY_BIT_POS, &ev_int->flags)) { ev.id = ev_code; @@ -64,9 +66,8 @@ int iio_push_event(struct iio_dev *indio_dev, u64 ev_code, s64 timestamp) copied = kfifo_put(&ev_int->det_events, ev); if (copied != 0) - wake_up_locked_poll(&ev_int->wait, POLLIN); + wake_up_poll(&ev_int->wait, POLLIN); } - spin_unlock_irqrestore(&ev_int->wait.lock, flags); return 0; } @@ -87,10 +88,8 @@ static unsigned int iio_event_poll(struct file *filep, poll_wait(filep, &ev_int->wait, wait); - spin_lock_irq(&ev_int->wait.lock); if (!kfifo_is_empty(&ev_int->det_events)) events = POLLIN | POLLRDNORM; - spin_unlock_irq(&ev_int->wait.lock); return events; } @@ -111,31 +110,40 @@ static ssize_t iio_event_chrdev_read(struct file *filep, if (count < sizeof(struct iio_event_data)) return -EINVAL; - spin_lock_irq(&ev_int->wait.lock); - if (kfifo_is_empty(&ev_int->det_events)) { - if (filep->f_flags & O_NONBLOCK) { - ret = -EAGAIN; - goto error_unlock; - } - /* Blocking on device; waiting for something to be there */ - ret = wait_event_interruptible_locked_irq(ev_int->wait, + do { + if (kfifo_is_empty(&ev_int->det_events)) { + if (filep->f_flags & O_NONBLOCK) + return -EAGAIN; + + ret = wait_event_interruptible(ev_int->wait, !kfifo_is_empty(&ev_int->det_events) || indio_dev->info == NULL); - if (ret) - goto error_unlock; - if (indio_dev->info == NULL) { - ret = -ENODEV; - goto error_unlock; + if (ret) + return ret; + if (indio_dev->info == NULL) + return -ENODEV; } - /* Single access device so no one else can get the data */ - } - ret = kfifo_to_user(&ev_int->det_events, buf, count, &copied); + if (mutex_lock_interruptible(&ev_int->read_lock)) + return -ERESTARTSYS; + ret = kfifo_to_user(&ev_int->det_events, buf, count, &copied); + mutex_unlock(&ev_int->read_lock); -error_unlock: - spin_unlock_irq(&ev_int->wait.lock); + if (ret) + return ret; + + /* + * If we couldn't read anything from the fifo (a different + * thread might have been faster) we either return -EAGAIN if + * the file descriptor is non-blocking, otherwise we go back to + * sleep and wait for more data to arrive. + */ + if (copied == 0 && (filep->f_flags & O_NONBLOCK)) + return -EAGAIN; - return ret ? ret : copied; + } while (copied == 0); + + return copied; } static int iio_event_chrdev_release(struct inode *inode, struct file *filep) @@ -143,15 +151,7 @@ static int iio_event_chrdev_release(struct inode *inode, struct file *filep) struct iio_dev *indio_dev = filep->private_data; struct iio_event_interface *ev_int = indio_dev->event_interface; - spin_lock_irq(&ev_int->wait.lock); - __clear_bit(IIO_BUSY_BIT_POS, &ev_int->flags); - /* - * In order to maintain a clean state for reopening, - * clear out any awaiting events. The mask will prevent - * any new __iio_push_event calls running. - */ - kfifo_reset_out(&ev_int->det_events); - spin_unlock_irq(&ev_int->wait.lock); + clear_bit(IIO_BUSY_BIT_POS, &ev_int->flags); iio_device_put(indio_dev); @@ -174,22 +174,20 @@ int iio_event_getfd(struct iio_dev *indio_dev) if (ev_int == NULL) return -ENODEV; - spin_lock_irq(&ev_int->wait.lock); - if (__test_and_set_bit(IIO_BUSY_BIT_POS, &ev_int->flags)) { - spin_unlock_irq(&ev_int->wait.lock); + if (test_and_set_bit(IIO_BUSY_BIT_POS, &ev_int->flags)) return -EBUSY; - } - spin_unlock_irq(&ev_int->wait.lock); + iio_device_get(indio_dev); fd = anon_inode_getfd("iio:event", &iio_event_chrdev_fileops, indio_dev, O_RDONLY | O_CLOEXEC); if (fd < 0) { - spin_lock_irq(&ev_int->wait.lock); - __clear_bit(IIO_BUSY_BIT_POS, &ev_int->flags); - spin_unlock_irq(&ev_int->wait.lock); + clear_bit(IIO_BUSY_BIT_POS, &ev_int->flags); iio_device_put(indio_dev); + } else { + kfifo_reset_out(&ev_int->det_events); } + return fd; } @@ -366,32 +364,31 @@ static int iio_device_add_event_sysfs(struct iio_dev *indio_dev, ret = iio_device_add_event(indio_dev, chan, i, type, dir, IIO_SEPARATE, &chan->event_spec[i].mask_separate); if (ret < 0) - goto error_ret; + return ret; attrcount += ret; ret = iio_device_add_event(indio_dev, chan, i, type, dir, IIO_SHARED_BY_TYPE, &chan->event_spec[i].mask_shared_by_type); if (ret < 0) - goto error_ret; + return ret; attrcount += ret; ret = iio_device_add_event(indio_dev, chan, i, type, dir, IIO_SHARED_BY_DIR, &chan->event_spec[i].mask_shared_by_dir); if (ret < 0) - goto error_ret; + return ret; attrcount += ret; ret = iio_device_add_event(indio_dev, chan, i, type, dir, IIO_SHARED_BY_ALL, &chan->event_spec[i].mask_shared_by_all); if (ret < 0) - goto error_ret; + return ret; attrcount += ret; } ret = attrcount; -error_ret: return ret; } @@ -425,6 +422,7 @@ static void iio_setup_ev_int(struct iio_event_interface *ev_int) { INIT_KFIFO(ev_int->det_events); init_waitqueue_head(&ev_int->wait); + mutex_init(&ev_int->read_lock); } static const char *iio_event_group_name = "events"; @@ -440,10 +438,8 @@ int iio_device_register_eventset(struct iio_dev *indio_dev) indio_dev->event_interface = kzalloc(sizeof(struct iio_event_interface), GFP_KERNEL); - if (indio_dev->event_interface == NULL) { - ret = -ENOMEM; - goto error_ret; - } + if (indio_dev->event_interface == NULL) + return -ENOMEM; INIT_LIST_HEAD(&indio_dev->event_interface->dev_attr_list); @@ -489,8 +485,6 @@ int iio_device_register_eventset(struct iio_dev *indio_dev) error_free_setup_event_lines: iio_free_chan_devattr_list(&indio_dev->event_interface->dev_attr_list); kfree(indio_dev->event_interface); -error_ret: - return ret; } diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index 766fab2..3383b02 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -62,10 +62,9 @@ int iio_trigger_register(struct iio_trigger *trig_info) int ret; trig_info->id = ida_simple_get(&iio_trigger_ida, 0, 0, GFP_KERNEL); - if (trig_info->id < 0) { - ret = trig_info->id; - goto error_ret; - } + if (trig_info->id < 0) + return trig_info->id; + /* Set the name used for the sysfs directory etc */ dev_set_name(&trig_info->dev, "trigger%ld", (unsigned long) trig_info->id); @@ -83,7 +82,6 @@ int iio_trigger_register(struct iio_trigger *trig_info) error_unregister_id: ida_simple_remove(&iio_trigger_ida, trig_info->id); -error_ret: return ret; } EXPORT_SYMBOL(iio_trigger_register); @@ -234,13 +232,12 @@ static int iio_trigger_detach_poll_func(struct iio_trigger *trig, if (trig->ops && trig->ops->set_trigger_state && no_other_users) { ret = trig->ops->set_trigger_state(trig, false); if (ret) - goto error_ret; + return ret; } iio_trigger_put_irq(trig, pf->irq); free_irq(pf->irq, pf); module_put(pf->indio_dev->info->driver_module); -error_ret: return ret; } diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index d12b2a0..c89740d 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -73,6 +73,20 @@ config HID_SENSOR_ALS Say yes here to build support for the HID SENSOR Ambient light sensor. +config HID_SENSOR_PROX + depends on HID_SENSOR_HUB + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + select HID_SENSOR_IIO_COMMON + select HID_SENSOR_IIO_TRIGGER + tristate "HID PROX" + help + Say yes here to build support for the HID SENSOR + Proximity sensor. + + To compile this driver as a module, choose M here: the + module will be called hid-sensor-prox. + config SENSORS_LM3533 tristate "LM3533 ambient light sensor" depends on MFD_LM3533 @@ -90,6 +104,18 @@ config SENSORS_LM3533 changes. The ALS-control output values can be set per zone for the three current output channels. +config LTR501 + tristate "LTR-501ALS-01 light sensor" + depends on I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + If you say yes here you get support for the Lite-On LTR-501ALS-01 + ambient light and proximity sensor. + + This driver can also be built as a module. If so, the module + will be called ltr501. + config TCS3472 tristate "TAOS TCS3472 color light-to-digital converter" depends on I2C diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index 60e35ac..3eb36e5 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -9,7 +9,9 @@ obj-$(CONFIG_CM32181) += cm32181.o obj-$(CONFIG_CM36651) += cm36651.o obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o +obj-$(CONFIG_HID_SENSOR_PROX) += hid-sensor-prox.o obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o +obj-$(CONFIG_LTR501) += ltr501.o obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o obj-$(CONFIG_TCS3472) += tcs3472.o obj-$(CONFIG_TSL4531) += tsl4531.o diff --git a/drivers/iio/light/adjd_s311.c b/drivers/iio/light/adjd_s311.c index f306847..09ad5f1 100644 --- a/drivers/iio/light/adjd_s311.c +++ b/drivers/iio/light/adjd_s311.c @@ -14,7 +14,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/i2c.h> #include <linux/slab.h> @@ -120,7 +119,6 @@ static irqreturn_t adjd_s311_trigger_handler(int irq, void *p) struct iio_dev *indio_dev = pf->indio_dev; struct adjd_s311_data *data = iio_priv(indio_dev); s64 time_ns = iio_get_time_ns(); - int len = 0; int i, j = 0; int ret = adjd_s311_req_data(indio_dev); @@ -135,7 +133,6 @@ static irqreturn_t adjd_s311_trigger_handler(int irq, void *p) goto done; data->buffer[j++] = ret & ADJD_S311_DATA_MASK; - len += 2; } iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, time_ns); diff --git a/drivers/iio/light/hid-sensor-prox.c b/drivers/iio/light/hid-sensor-prox.c new file mode 100644 index 0000000..1894ab1 --- /dev/null +++ b/drivers/iio/light/hid-sensor-prox.c @@ -0,0 +1,375 @@ +/* + * HID Sensors Driver + * Copyright (c) 2014, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. + * + */ +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/slab.h> +#include <linux/hid-sensor-hub.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> +#include "../common/hid-sensors/hid-sensor-trigger.h" + +#define CHANNEL_SCAN_INDEX_PRESENCE 0 + +struct prox_state { + struct hid_sensor_hub_callbacks callbacks; + struct hid_sensor_common common_attributes; + struct hid_sensor_hub_attribute_info prox_attr; + u32 human_presence; +}; + +/* Channel definitions */ +static const struct iio_chan_spec prox_channels[] = { + { + .type = IIO_PROXIMITY, + .modified = 1, + .channel2 = IIO_NO_MOD, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_HYSTERESIS), + .scan_index = CHANNEL_SCAN_INDEX_PRESENCE, + } +}; + +/* Adjust channel real bits based on report descriptor */ +static void prox_adjust_channel_bit_mask(struct iio_chan_spec *channels, + int channel, int size) +{ + channels[channel].scan_type.sign = 's'; + /* Real storage bits will change based on the report desc. */ + channels[channel].scan_type.realbits = size * 8; + /* Maximum size of a sample to capture is u32 */ + channels[channel].scan_type.storagebits = sizeof(u32) * 8; +} + +/* Channel read_raw handler */ +static int prox_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, + long mask) +{ + struct prox_state *prox_state = iio_priv(indio_dev); + int report_id = -1; + u32 address; + int ret; + int ret_type; + + *val = 0; + *val2 = 0; + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->scan_index) { + case CHANNEL_SCAN_INDEX_PRESENCE: + report_id = prox_state->prox_attr.report_id; + address = + HID_USAGE_SENSOR_HUMAN_PRESENCE; + break; + default: + report_id = -1; + break; + } + if (report_id >= 0) + *val = sensor_hub_input_attr_get_raw_value( + prox_state->common_attributes.hsdev, + HID_USAGE_SENSOR_PROX, address, + report_id); + else { + *val = 0; + return -EINVAL; + } + ret_type = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_SCALE: + *val = prox_state->prox_attr.units; + ret_type = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_OFFSET: + *val = hid_sensor_convert_exponent( + prox_state->prox_attr.unit_expo); + ret_type = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = hid_sensor_read_samp_freq_value( + &prox_state->common_attributes, val, val2); + ret_type = IIO_VAL_INT_PLUS_MICRO; + break; + case IIO_CHAN_INFO_HYSTERESIS: + ret = hid_sensor_read_raw_hyst_value( + &prox_state->common_attributes, val, val2); + ret_type = IIO_VAL_INT_PLUS_MICRO; + break; + default: + ret_type = -EINVAL; + break; + } + + return ret_type; +} + +/* Channel write_raw handler */ +static int prox_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct prox_state *prox_state = iio_priv(indio_dev); + int ret = 0; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + ret = hid_sensor_write_samp_freq_value( + &prox_state->common_attributes, val, val2); + break; + case IIO_CHAN_INFO_HYSTERESIS: + ret = hid_sensor_write_raw_hyst_value( + &prox_state->common_attributes, val, val2); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static const struct iio_info prox_info = { + .driver_module = THIS_MODULE, + .read_raw = &prox_read_raw, + .write_raw = &prox_write_raw, +}; + +/* Function to push data to buffer */ +static void hid_sensor_push_data(struct iio_dev *indio_dev, const void *data, + int len) +{ + dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n"); + iio_push_to_buffers(indio_dev, data); +} + +/* Callback handler to send event after all samples are received and captured */ +static int prox_proc_event(struct hid_sensor_hub_device *hsdev, + unsigned usage_id, + void *priv) +{ + struct iio_dev *indio_dev = platform_get_drvdata(priv); + struct prox_state *prox_state = iio_priv(indio_dev); + + dev_dbg(&indio_dev->dev, "prox_proc_event [%d]\n", + prox_state->common_attributes.data_ready); + if (prox_state->common_attributes.data_ready) + hid_sensor_push_data(indio_dev, + &prox_state->human_presence, + sizeof(prox_state->human_presence)); + + return 0; +} + +/* Capture samples in local storage */ +static int prox_capture_sample(struct hid_sensor_hub_device *hsdev, + unsigned usage_id, + size_t raw_len, char *raw_data, + void *priv) +{ + struct iio_dev *indio_dev = platform_get_drvdata(priv); + struct prox_state *prox_state = iio_priv(indio_dev); + int ret = -EINVAL; + + switch (usage_id) { + case HID_USAGE_SENSOR_HUMAN_PRESENCE: + prox_state->human_presence = *(u32 *)raw_data; + ret = 0; + break; + default: + break; + } + + return ret; +} + +/* Parse report which is specific to an usage id*/ +static int prox_parse_report(struct platform_device *pdev, + struct hid_sensor_hub_device *hsdev, + struct iio_chan_spec *channels, + unsigned usage_id, + struct prox_state *st) +{ + int ret; + + ret = sensor_hub_input_get_attribute_info(hsdev, HID_INPUT_REPORT, + usage_id, + HID_USAGE_SENSOR_HUMAN_PRESENCE, + &st->prox_attr); + if (ret < 0) + return ret; + prox_adjust_channel_bit_mask(channels, CHANNEL_SCAN_INDEX_PRESENCE, + st->prox_attr.size); + + dev_dbg(&pdev->dev, "prox %x:%x\n", st->prox_attr.index, + st->prox_attr.report_id); + + /* Set Sensitivity field ids, when there is no individual modifier */ + if (st->common_attributes.sensitivity.index < 0) { + sensor_hub_input_get_attribute_info(hsdev, + HID_FEATURE_REPORT, usage_id, + HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | + HID_USAGE_SENSOR_DATA_PRESENCE, + &st->common_attributes.sensitivity); + dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n", + st->common_attributes.sensitivity.index, + st->common_attributes.sensitivity.report_id); + } + return ret; +} + +/* Function to initialize the processing for usage id */ +static int hid_prox_probe(struct platform_device *pdev) +{ + int ret = 0; + static const char *name = "prox"; + struct iio_dev *indio_dev; + struct prox_state *prox_state; + struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; + struct iio_chan_spec *channels; + + indio_dev = devm_iio_device_alloc(&pdev->dev, + sizeof(struct prox_state)); + if (!indio_dev) + return -ENOMEM; + platform_set_drvdata(pdev, indio_dev); + + prox_state = iio_priv(indio_dev); + prox_state->common_attributes.hsdev = hsdev; + prox_state->common_attributes.pdev = pdev; + + ret = hid_sensor_parse_common_attributes(hsdev, HID_USAGE_SENSOR_PROX, + &prox_state->common_attributes); + if (ret) { + dev_err(&pdev->dev, "failed to setup common attributes\n"); + return ret; + } + + channels = kmemdup(prox_channels, sizeof(prox_channels), GFP_KERNEL); + if (!channels) { + dev_err(&pdev->dev, "failed to duplicate channels\n"); + return -ENOMEM; + } + + ret = prox_parse_report(pdev, hsdev, channels, + HID_USAGE_SENSOR_PROX, prox_state); + if (ret) { + dev_err(&pdev->dev, "failed to setup attributes\n"); + goto error_free_dev_mem; + } + + indio_dev->channels = channels; + indio_dev->num_channels = + ARRAY_SIZE(prox_channels); + indio_dev->dev.parent = &pdev->dev; + indio_dev->info = &prox_info; + indio_dev->name = name; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, + NULL, NULL); + if (ret) { + dev_err(&pdev->dev, "failed to initialize trigger buffer\n"); + goto error_free_dev_mem; + } + prox_state->common_attributes.data_ready = false; + ret = hid_sensor_setup_trigger(indio_dev, name, + &prox_state->common_attributes); + if (ret) { + dev_err(&pdev->dev, "trigger setup failed\n"); + goto error_unreg_buffer_funcs; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_trigger; + } + + prox_state->callbacks.send_event = prox_proc_event; + prox_state->callbacks.capture_sample = prox_capture_sample; + prox_state->callbacks.pdev = pdev; + ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_PROX, + &prox_state->callbacks); + if (ret < 0) { + dev_err(&pdev->dev, "callback reg failed\n"); + goto error_iio_unreg; + } + + return ret; + +error_iio_unreg: + iio_device_unregister(indio_dev); +error_remove_trigger: + hid_sensor_remove_trigger(&prox_state->common_attributes); +error_unreg_buffer_funcs: + iio_triggered_buffer_cleanup(indio_dev); +error_free_dev_mem: + kfree(indio_dev->channels); + return ret; +} + +/* Function to deinitialize the processing for usage id */ +static int hid_prox_remove(struct platform_device *pdev) +{ + struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct prox_state *prox_state = iio_priv(indio_dev); + + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PROX); + iio_device_unregister(indio_dev); + hid_sensor_remove_trigger(&prox_state->common_attributes); + iio_triggered_buffer_cleanup(indio_dev); + kfree(indio_dev->channels); + + return 0; +} + +static struct platform_device_id hid_prox_ids[] = { + { + /* Format: HID-SENSOR-usage_id_in_hex_lowercase */ + .name = "HID-SENSOR-200011", + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, hid_prox_ids); + +static struct platform_driver hid_prox_platform_driver = { + .id_table = hid_prox_ids, + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE, + }, + .probe = hid_prox_probe, + .remove = hid_prox_remove, +}; +module_platform_driver(hid_prox_platform_driver); + +MODULE_DESCRIPTION("HID Sensor Proximity"); +MODULE_AUTHOR("Archana Patni <archana.patni@intel.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c new file mode 100644 index 0000000..62b7072 --- /dev/null +++ b/drivers/iio/light/ltr501.c @@ -0,0 +1,445 @@ +/* + * ltr501.c - Support for Lite-On LTR501 ambient light and proximity sensor + * + * Copyright 2014 Peter Meerwald <pmeerw@pmeerw.net> + * + * This file is subject to the terms and conditions of version 2 of + * the GNU General Public License. See the file COPYING in the main + * directory of this archive for more details. + * + * 7-bit I2C slave address 0x23 + * + * TODO: interrupt, threshold, measurement rate, IR LED characteristics + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/buffer.h> +#include <linux/iio/triggered_buffer.h> + +#define LTR501_DRV_NAME "ltr501" + +#define LTR501_ALS_CONTR 0x80 /* ALS operation mode, SW reset */ +#define LTR501_PS_CONTR 0x81 /* PS operation mode */ +#define LTR501_PART_ID 0x86 +#define LTR501_MANUFAC_ID 0x87 +#define LTR501_ALS_DATA1 0x88 /* 16-bit, little endian */ +#define LTR501_ALS_DATA0 0x8a /* 16-bit, little endian */ +#define LTR501_ALS_PS_STATUS 0x8c +#define LTR501_PS_DATA 0x8d /* 16-bit, little endian */ + +#define LTR501_ALS_CONTR_SW_RESET BIT(2) +#define LTR501_CONTR_PS_GAIN_MASK (BIT(3) | BIT(2)) +#define LTR501_CONTR_PS_GAIN_SHIFT 2 +#define LTR501_CONTR_ALS_GAIN_MASK BIT(3) +#define LTR501_CONTR_ACTIVE BIT(1) + +#define LTR501_STATUS_ALS_RDY BIT(2) +#define LTR501_STATUS_PS_RDY BIT(0) + +#define LTR501_PS_DATA_MASK 0x7ff + +struct ltr501_data { + struct i2c_client *client; + struct mutex lock_als, lock_ps; + u8 als_contr, ps_contr; +}; + +static int ltr501_drdy(struct ltr501_data *data, u8 drdy_mask) +{ + int tries = 100; + int ret; + + while (tries--) { + ret = i2c_smbus_read_byte_data(data->client, + LTR501_ALS_PS_STATUS); + if (ret < 0) + return ret; + if ((ret & drdy_mask) == drdy_mask) + return 0; + msleep(25); + } + + dev_err(&data->client->dev, "ltr501_drdy() failed, data not ready\n"); + return -EIO; +} + +static int ltr501_read_als(struct ltr501_data *data, __le16 buf[2]) +{ + int ret = ltr501_drdy(data, LTR501_STATUS_ALS_RDY); + if (ret < 0) + return ret; + /* always read both ALS channels in given order */ + return i2c_smbus_read_i2c_block_data(data->client, + LTR501_ALS_DATA1, 2 * sizeof(__le16), (u8 *) buf); +} + +static int ltr501_read_ps(struct ltr501_data *data) +{ + int ret = ltr501_drdy(data, LTR501_STATUS_PS_RDY); + if (ret < 0) + return ret; + return i2c_smbus_read_word_data(data->client, LTR501_PS_DATA); +} + +#define LTR501_INTENSITY_CHANNEL(_idx, _addr, _mod, _shared) { \ + .type = IIO_INTENSITY, \ + .modified = 1, \ + .address = (_addr), \ + .channel2 = (_mod), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = (_shared), \ + .scan_index = (_idx), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + } \ +} + +static const struct iio_chan_spec ltr501_channels[] = { + LTR501_INTENSITY_CHANNEL(0, LTR501_ALS_DATA0, IIO_MOD_LIGHT_BOTH, 0), + LTR501_INTENSITY_CHANNEL(1, LTR501_ALS_DATA1, IIO_MOD_LIGHT_IR, + BIT(IIO_CHAN_INFO_SCALE)), + { + .type = IIO_PROXIMITY, + .address = LTR501_PS_DATA, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 2, + .scan_type = { + .sign = 'u', + .realbits = 11, + .storagebits = 16, + .endianness = IIO_CPU, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(3), +}; + +static const int ltr501_ps_gain[4][2] = { + {1, 0}, {0, 250000}, {0, 125000}, {0, 62500} +}; + +static int ltr501_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ltr501_data *data = iio_priv(indio_dev); + __le16 buf[2]; + int ret, i; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (iio_buffer_enabled(indio_dev)) + return -EBUSY; + + switch (chan->type) { + case IIO_INTENSITY: + mutex_lock(&data->lock_als); + ret = ltr501_read_als(data, buf); + mutex_unlock(&data->lock_als); + if (ret < 0) + return ret; + *val = le16_to_cpu(chan->address == LTR501_ALS_DATA1 ? + buf[0] : buf[1]); + return IIO_VAL_INT; + case IIO_PROXIMITY: + mutex_lock(&data->lock_ps); + ret = ltr501_read_ps(data); + mutex_unlock(&data->lock_ps); + if (ret < 0) + return ret; + *val = ret & LTR501_PS_DATA_MASK; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_INTENSITY: + if (data->als_contr & LTR501_CONTR_ALS_GAIN_MASK) { + *val = 0; + *val2 = 5000; + return IIO_VAL_INT_PLUS_MICRO; + } else { + *val = 1; + *val2 = 0; + return IIO_VAL_INT; + } + case IIO_PROXIMITY: + i = (data->ps_contr & LTR501_CONTR_PS_GAIN_MASK) >> + LTR501_CONTR_PS_GAIN_SHIFT; + *val = ltr501_ps_gain[i][0]; + *val2 = ltr501_ps_gain[i][1]; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + } + return -EINVAL; +} + +static int ltr501_get_ps_gain_index(int val, int val2) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ltr501_ps_gain); i++) + if (val == ltr501_ps_gain[i][0] && val2 == ltr501_ps_gain[i][1]) + return i; + + return -1; +} + +static int ltr501_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ltr501_data *data = iio_priv(indio_dev); + int i; + + if (iio_buffer_enabled(indio_dev)) + return -EBUSY; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_INTENSITY: + if (val == 0 && val2 == 5000) + data->als_contr |= LTR501_CONTR_ALS_GAIN_MASK; + else if (val == 1 && val2 == 0) + data->als_contr &= ~LTR501_CONTR_ALS_GAIN_MASK; + else + return -EINVAL; + return i2c_smbus_write_byte_data(data->client, + LTR501_ALS_CONTR, data->als_contr); + case IIO_PROXIMITY: + i = ltr501_get_ps_gain_index(val, val2); + if (i < 0) + return -EINVAL; + data->ps_contr &= ~LTR501_CONTR_PS_GAIN_MASK; + data->ps_contr |= i << LTR501_CONTR_PS_GAIN_SHIFT; + return i2c_smbus_write_byte_data(data->client, + LTR501_PS_CONTR, data->ps_contr); + default: + return -EINVAL; + } + } + return -EINVAL; +} + +static IIO_CONST_ATTR(in_proximity_scale_available, "1 0.25 0.125 0.0625"); +static IIO_CONST_ATTR(in_intensity_scale_available, "1 0.005"); + +static struct attribute *ltr501_attributes[] = { + &iio_const_attr_in_proximity_scale_available.dev_attr.attr, + &iio_const_attr_in_intensity_scale_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group ltr501_attribute_group = { + .attrs = ltr501_attributes, +}; + +static const struct iio_info ltr501_info = { + .read_raw = ltr501_read_raw, + .write_raw = ltr501_write_raw, + .attrs = <r501_attribute_group, + .driver_module = THIS_MODULE, +}; + +static int ltr501_write_contr(struct i2c_client *client, u8 als_val, u8 ps_val) +{ + int ret = i2c_smbus_write_byte_data(client, LTR501_ALS_CONTR, als_val); + if (ret < 0) + return ret; + + return i2c_smbus_write_byte_data(client, LTR501_PS_CONTR, ps_val); +} + +static irqreturn_t ltr501_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ltr501_data *data = iio_priv(indio_dev); + u16 buf[8]; + __le16 als_buf[2]; + u8 mask = 0; + int j = 0; + int ret; + + memset(buf, 0, sizeof(buf)); + + /* figure out which data needs to be ready */ + if (test_bit(0, indio_dev->active_scan_mask) || + test_bit(1, indio_dev->active_scan_mask)) + mask |= LTR501_STATUS_ALS_RDY; + if (test_bit(2, indio_dev->active_scan_mask)) + mask |= LTR501_STATUS_PS_RDY; + + ret = ltr501_drdy(data, mask); + if (ret < 0) + goto done; + + if (mask & LTR501_STATUS_ALS_RDY) { + ret = i2c_smbus_read_i2c_block_data(data->client, + LTR501_ALS_DATA1, sizeof(als_buf), (u8 *) als_buf); + if (ret < 0) + return ret; + if (test_bit(0, indio_dev->active_scan_mask)) + buf[j++] = le16_to_cpu(als_buf[1]); + if (test_bit(1, indio_dev->active_scan_mask)) + buf[j++] = le16_to_cpu(als_buf[0]); + } + + if (mask & LTR501_STATUS_PS_RDY) { + ret = i2c_smbus_read_word_data(data->client, LTR501_PS_DATA); + if (ret < 0) + goto done; + buf[j++] = ret & LTR501_PS_DATA_MASK; + } + + iio_push_to_buffers_with_timestamp(indio_dev, buf, + iio_get_time_ns()); + +done: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int ltr501_init(struct ltr501_data *data) +{ + int ret; + + ret = i2c_smbus_read_byte_data(data->client, LTR501_ALS_CONTR); + if (ret < 0) + return ret; + data->als_contr = ret | LTR501_CONTR_ACTIVE; + + ret = i2c_smbus_read_byte_data(data->client, LTR501_PS_CONTR); + if (ret < 0) + return ret; + data->ps_contr = ret | LTR501_CONTR_ACTIVE; + + return ltr501_write_contr(data->client, data->als_contr, + data->ps_contr); +} + +static int ltr501_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ltr501_data *data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + mutex_init(&data->lock_als); + mutex_init(&data->lock_ps); + + ret = i2c_smbus_read_byte_data(data->client, LTR501_PART_ID); + if (ret < 0) + return ret; + if ((ret >> 4) != 0x8) + return -ENODEV; + + indio_dev->dev.parent = &client->dev; + indio_dev->info = <r501_info; + indio_dev->channels = ltr501_channels; + indio_dev->num_channels = ARRAY_SIZE(ltr501_channels); + indio_dev->name = LTR501_DRV_NAME; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = ltr501_init(data); + if (ret < 0) + return ret; + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + ltr501_trigger_handler, NULL); + if (ret) + return ret; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_unreg_buffer; + + return 0; + +error_unreg_buffer: + iio_triggered_buffer_cleanup(indio_dev); + return ret; +} + +static int ltr501_powerdown(struct ltr501_data *data) +{ + return ltr501_write_contr(data->client, + data->als_contr & ~LTR501_CONTR_ACTIVE, + data->ps_contr & ~LTR501_CONTR_ACTIVE); +} + +static int ltr501_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + ltr501_powerdown(iio_priv(indio_dev)); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int ltr501_suspend(struct device *dev) +{ + struct ltr501_data *data = iio_priv(i2c_get_clientdata( + to_i2c_client(dev))); + return ltr501_powerdown(data); +} + +static int ltr501_resume(struct device *dev) +{ + struct ltr501_data *data = iio_priv(i2c_get_clientdata( + to_i2c_client(dev))); + + return ltr501_write_contr(data->client, data->als_contr, + data->ps_contr); +} +#endif + +static SIMPLE_DEV_PM_OPS(ltr501_pm_ops, ltr501_suspend, ltr501_resume); + +static const struct i2c_device_id ltr501_id[] = { + { "ltr501", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ltr501_id); + +static struct i2c_driver ltr501_driver = { + .driver = { + .name = LTR501_DRV_NAME, + .pm = <r501_pm_ops, + .owner = THIS_MODULE, + }, + .probe = ltr501_probe, + .remove = ltr501_remove, + .id_table = ltr501_id, +}; + +module_i2c_driver(ltr501_driver); + +MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); +MODULE_DESCRIPTION("Lite-On LTR501 ambient light and proximity sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/light/tcs3472.c b/drivers/iio/light/tcs3472.c index 887fecf..fe063a0 100644 --- a/drivers/iio/light/tcs3472.c +++ b/drivers/iio/light/tcs3472.c @@ -179,7 +179,6 @@ static irqreturn_t tcs3472_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct tcs3472_data *data = iio_priv(indio_dev); - int len = 0; int i, j = 0; int ret = tcs3472_req_data(data); @@ -194,7 +193,6 @@ static irqreturn_t tcs3472_trigger_handler(int irq, void *p) goto done; data->buffer[j++] = ret; - len += 2; } iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index 0542354..74866d1 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -513,6 +513,7 @@ static int ak8975_probe(struct i2c_client *client, indio_dev->channels = ak8975_channels; indio_dev->num_channels = ARRAY_SIZE(ak8975_channels); indio_dev->info = &ak8975_info; + indio_dev->name = id->name; indio_dev->modes = INDIO_DIRECT_MODE; err = iio_device_register(indio_dev); diff --git a/drivers/iio/magnetometer/mag3110.c b/drivers/iio/magnetometer/mag3110.c index f66955f..02e4f97 100644 --- a/drivers/iio/magnetometer/mag3110.c +++ b/drivers/iio/magnetometer/mag3110.c @@ -183,8 +183,17 @@ static int mag3110_read_raw(struct iio_dev *indio_dev, return -EINVAL; } case IIO_CHAN_INFO_SCALE: - *val = 0; - *val2 = 1000; + switch (chan->type) { + case IIO_MAGN: + *val = 0; + *val2 = 1000; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_TEMP: + *val = 1000; + return IIO_VAL_INT; + default: + return -EINVAL; + } return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_SAMP_FREQ: i = data->ctrl_reg1 >> MAG3110_CTRL_DR_SHIFT; @@ -270,7 +279,8 @@ static const struct iio_chan_spec mag3110_channels[] = { MAG3110_CHANNEL(Z, 2), { .type = IIO_TEMP, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), .scan_index = 3, .scan_type = { .sign = 's', diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig index a8b9cae..d88ff17 100644 --- a/drivers/iio/pressure/Kconfig +++ b/drivers/iio/pressure/Kconfig @@ -5,6 +5,20 @@ menu "Pressure sensors" +config HID_SENSOR_PRESS + depends on HID_SENSOR_HUB + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + select HID_SENSOR_IIO_COMMON + select HID_SENSOR_IIO_TRIGGER + tristate "HID PRESS" + help + Say yes here to build support for the HID SENSOR + Pressure driver + + To compile this driver as a module, choose M here: the module + will be called hid-sensor-press. + config MPL3115 tristate "Freescale MPL3115A2 pressure sensor driver" depends on I2C @@ -26,7 +40,7 @@ config IIO_ST_PRESS select IIO_TRIGGERED_BUFFER if (IIO_BUFFER) help Say yes here to build support for STMicroelectronics pressure - sensors: LPS001WP, LPS331AP. + sensors: LPS001WP, LPS25H, LPS331AP. This driver can also be built as a module. If so, these modules will be created: diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile index 42bb9fc..4a57bf6 100644 --- a/drivers/iio/pressure/Makefile +++ b/drivers/iio/pressure/Makefile @@ -3,6 +3,7 @@ # # When adding new entries keep the list in alphabetical order +obj-$(CONFIG_HID_SENSOR_PRESS) += hid-sensor-press.o obj-$(CONFIG_MPL3115) += mpl3115.o obj-$(CONFIG_IIO_ST_PRESS) += st_pressure.o st_pressure-y := st_pressure_core.o diff --git a/drivers/iio/pressure/hid-sensor-press.c b/drivers/iio/pressure/hid-sensor-press.c new file mode 100644 index 0000000..e0e6409 --- /dev/null +++ b/drivers/iio/pressure/hid-sensor-press.c @@ -0,0 +1,376 @@ +/* + * HID Sensors Driver + * Copyright (c) 2014, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. + * + */ +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/slab.h> +#include <linux/hid-sensor-hub.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> +#include "../common/hid-sensors/hid-sensor-trigger.h" + +#define CHANNEL_SCAN_INDEX_PRESSURE 0 + +struct press_state { + struct hid_sensor_hub_callbacks callbacks; + struct hid_sensor_common common_attributes; + struct hid_sensor_hub_attribute_info press_attr; + u32 press_data; +}; + +/* Channel definitions */ +static const struct iio_chan_spec press_channels[] = { + { + .type = IIO_PRESSURE, + .modified = 1, + .channel2 = IIO_NO_MOD, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_HYSTERESIS), + .scan_index = CHANNEL_SCAN_INDEX_PRESSURE, + } +}; + +/* Adjust channel real bits based on report descriptor */ +static void press_adjust_channel_bit_mask(struct iio_chan_spec *channels, + int channel, int size) +{ + channels[channel].scan_type.sign = 's'; + /* Real storage bits will change based on the report desc. */ + channels[channel].scan_type.realbits = size * 8; + /* Maximum size of a sample to capture is u32 */ + channels[channel].scan_type.storagebits = sizeof(u32) * 8; +} + +/* Channel read_raw handler */ +static int press_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, + long mask) +{ + struct press_state *press_state = iio_priv(indio_dev); + int report_id = -1; + u32 address; + int ret; + int ret_type; + + *val = 0; + *val2 = 0; + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->scan_index) { + case CHANNEL_SCAN_INDEX_PRESSURE: + report_id = press_state->press_attr.report_id; + address = + HID_USAGE_SENSOR_ATMOSPHERIC_PRESSURE; + break; + default: + report_id = -1; + break; + } + if (report_id >= 0) + *val = sensor_hub_input_attr_get_raw_value( + press_state->common_attributes.hsdev, + HID_USAGE_SENSOR_PRESSURE, address, + report_id); + else { + *val = 0; + return -EINVAL; + } + ret_type = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_SCALE: + *val = press_state->press_attr.units; + ret_type = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_OFFSET: + *val = hid_sensor_convert_exponent( + press_state->press_attr.unit_expo); + ret_type = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = hid_sensor_read_samp_freq_value( + &press_state->common_attributes, val, val2); + ret_type = IIO_VAL_INT_PLUS_MICRO; + break; + case IIO_CHAN_INFO_HYSTERESIS: + ret = hid_sensor_read_raw_hyst_value( + &press_state->common_attributes, val, val2); + ret_type = IIO_VAL_INT_PLUS_MICRO; + break; + default: + ret_type = -EINVAL; + break; + } + + return ret_type; +} + +/* Channel write_raw handler */ +static int press_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct press_state *press_state = iio_priv(indio_dev); + int ret = 0; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + ret = hid_sensor_write_samp_freq_value( + &press_state->common_attributes, val, val2); + break; + case IIO_CHAN_INFO_HYSTERESIS: + ret = hid_sensor_write_raw_hyst_value( + &press_state->common_attributes, val, val2); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static const struct iio_info press_info = { + .driver_module = THIS_MODULE, + .read_raw = &press_read_raw, + .write_raw = &press_write_raw, +}; + +/* Function to push data to buffer */ +static void hid_sensor_push_data(struct iio_dev *indio_dev, const void *data, + int len) +{ + dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n"); + iio_push_to_buffers(indio_dev, data); +} + +/* Callback handler to send event after all samples are received and captured */ +static int press_proc_event(struct hid_sensor_hub_device *hsdev, + unsigned usage_id, + void *priv) +{ + struct iio_dev *indio_dev = platform_get_drvdata(priv); + struct press_state *press_state = iio_priv(indio_dev); + + dev_dbg(&indio_dev->dev, "press_proc_event [%d]\n", + press_state->common_attributes.data_ready); + if (press_state->common_attributes.data_ready) + hid_sensor_push_data(indio_dev, + &press_state->press_data, + sizeof(press_state->press_data)); + + return 0; +} + +/* Capture samples in local storage */ +static int press_capture_sample(struct hid_sensor_hub_device *hsdev, + unsigned usage_id, + size_t raw_len, char *raw_data, + void *priv) +{ + struct iio_dev *indio_dev = platform_get_drvdata(priv); + struct press_state *press_state = iio_priv(indio_dev); + int ret = -EINVAL; + + switch (usage_id) { + case HID_USAGE_SENSOR_ATMOSPHERIC_PRESSURE: + press_state->press_data = *(u32 *)raw_data; + ret = 0; + break; + default: + break; + } + + return ret; +} + +/* Parse report which is specific to an usage id*/ +static int press_parse_report(struct platform_device *pdev, + struct hid_sensor_hub_device *hsdev, + struct iio_chan_spec *channels, + unsigned usage_id, + struct press_state *st) +{ + int ret; + + ret = sensor_hub_input_get_attribute_info(hsdev, HID_INPUT_REPORT, + usage_id, + HID_USAGE_SENSOR_ATMOSPHERIC_PRESSURE, + &st->press_attr); + if (ret < 0) + return ret; + press_adjust_channel_bit_mask(channels, CHANNEL_SCAN_INDEX_PRESSURE, + st->press_attr.size); + + dev_dbg(&pdev->dev, "press %x:%x\n", st->press_attr.index, + st->press_attr.report_id); + + /* Set Sensitivity field ids, when there is no individual modifier */ + if (st->common_attributes.sensitivity.index < 0) { + sensor_hub_input_get_attribute_info(hsdev, + HID_FEATURE_REPORT, usage_id, + HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | + HID_USAGE_SENSOR_DATA_ATMOSPHERIC_PRESSURE, + &st->common_attributes.sensitivity); + dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n", + st->common_attributes.sensitivity.index, + st->common_attributes.sensitivity.report_id); + } + return ret; +} + +/* Function to initialize the processing for usage id */ +static int hid_press_probe(struct platform_device *pdev) +{ + int ret = 0; + static const char *name = "press"; + struct iio_dev *indio_dev; + struct press_state *press_state; + struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; + struct iio_chan_spec *channels; + + indio_dev = devm_iio_device_alloc(&pdev->dev, + sizeof(struct press_state)); + if (!indio_dev) + return -ENOMEM; + platform_set_drvdata(pdev, indio_dev); + + press_state = iio_priv(indio_dev); + press_state->common_attributes.hsdev = hsdev; + press_state->common_attributes.pdev = pdev; + + ret = hid_sensor_parse_common_attributes(hsdev, + HID_USAGE_SENSOR_PRESSURE, + &press_state->common_attributes); + if (ret) { + dev_err(&pdev->dev, "failed to setup common attributes\n"); + return ret; + } + + channels = kmemdup(press_channels, sizeof(press_channels), GFP_KERNEL); + if (!channels) { + dev_err(&pdev->dev, "failed to duplicate channels\n"); + return -ENOMEM; + } + + ret = press_parse_report(pdev, hsdev, channels, + HID_USAGE_SENSOR_PRESSURE, press_state); + if (ret) { + dev_err(&pdev->dev, "failed to setup attributes\n"); + goto error_free_dev_mem; + } + + indio_dev->channels = channels; + indio_dev->num_channels = + ARRAY_SIZE(press_channels); + indio_dev->dev.parent = &pdev->dev; + indio_dev->info = &press_info; + indio_dev->name = name; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, + NULL, NULL); + if (ret) { + dev_err(&pdev->dev, "failed to initialize trigger buffer\n"); + goto error_free_dev_mem; + } + press_state->common_attributes.data_ready = false; + ret = hid_sensor_setup_trigger(indio_dev, name, + &press_state->common_attributes); + if (ret) { + dev_err(&pdev->dev, "trigger setup failed\n"); + goto error_unreg_buffer_funcs; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_trigger; + } + + press_state->callbacks.send_event = press_proc_event; + press_state->callbacks.capture_sample = press_capture_sample; + press_state->callbacks.pdev = pdev; + ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_PRESSURE, + &press_state->callbacks); + if (ret < 0) { + dev_err(&pdev->dev, "callback reg failed\n"); + goto error_iio_unreg; + } + + return ret; + +error_iio_unreg: + iio_device_unregister(indio_dev); +error_remove_trigger: + hid_sensor_remove_trigger(&press_state->common_attributes); +error_unreg_buffer_funcs: + iio_triggered_buffer_cleanup(indio_dev); +error_free_dev_mem: + kfree(indio_dev->channels); + return ret; +} + +/* Function to deinitialize the processing for usage id */ +static int hid_press_remove(struct platform_device *pdev) +{ + struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct press_state *press_state = iio_priv(indio_dev); + + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PRESSURE); + iio_device_unregister(indio_dev); + hid_sensor_remove_trigger(&press_state->common_attributes); + iio_triggered_buffer_cleanup(indio_dev); + kfree(indio_dev->channels); + + return 0; +} + +static struct platform_device_id hid_press_ids[] = { + { + /* Format: HID-SENSOR-usage_id_in_hex_lowercase */ + .name = "HID-SENSOR-200031", + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, hid_press_ids); + +static struct platform_driver hid_press_platform_driver = { + .id_table = hid_press_ids, + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE, + }, + .probe = hid_press_probe, + .remove = hid_press_remove, +}; +module_platform_driver(hid_press_platform_driver); + +MODULE_DESCRIPTION("HID Sensor Pressure"); +MODULE_AUTHOR("Archana Patni <archana.patni@intel.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/pressure/mpl3115.c b/drivers/iio/pressure/mpl3115.c index ac8c8ab..ba6d0c5 100644 --- a/drivers/iio/pressure/mpl3115.c +++ b/drivers/iio/pressure/mpl3115.c @@ -77,7 +77,7 @@ static int mpl3115_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct mpl3115_data *data = iio_priv(indio_dev); - s32 tmp = 0; + __be32 tmp = 0; int ret; switch (mask) { diff --git a/drivers/iio/pressure/st_pressure.h b/drivers/iio/pressure/st_pressure.h index 049c21a..242943c 100644 --- a/drivers/iio/pressure/st_pressure.h +++ b/drivers/iio/pressure/st_pressure.h @@ -15,6 +15,7 @@ #include <linux/iio/common/st_sensors.h> #define LPS001WP_PRESS_DEV_NAME "lps001wp" +#define LPS25H_PRESS_DEV_NAME "lps25h" #define LPS331AP_PRESS_DEV_NAME "lps331ap" /** diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c index 58083f9..7418768 100644 --- a/drivers/iio/pressure/st_pressure_core.c +++ b/drivers/iio/pressure/st_pressure_core.c @@ -40,6 +40,9 @@ /* FULLSCALE */ #define ST_PRESS_FS_AVL_1260MB 1260 +#define ST_PRESS_1_OUT_XL_ADDR 0x28 +#define ST_TEMP_1_OUT_L_ADDR 0x2b + /* CUSTOM VALUES FOR LPS331AP SENSOR */ #define ST_PRESS_LPS331AP_WAI_EXP 0xbb #define ST_PRESS_LPS331AP_ODR_ADDR 0x20 @@ -62,8 +65,6 @@ #define ST_PRESS_LPS331AP_DRDY_IRQ_INT2_MASK 0x20 #define ST_PRESS_LPS331AP_MULTIREAD_BIT true #define ST_PRESS_LPS331AP_TEMP_OFFSET 42500 -#define ST_PRESS_LPS331AP_OUT_XL_ADDR 0x28 -#define ST_TEMP_LPS331AP_OUT_L_ADDR 0x2b /* CUSTOM VALUES FOR LPS001WP SENSOR */ #define ST_PRESS_LPS001WP_WAI_EXP 0xba @@ -80,11 +81,36 @@ #define ST_PRESS_LPS001WP_OUT_L_ADDR 0x28 #define ST_TEMP_LPS001WP_OUT_L_ADDR 0x2a -static const struct iio_chan_spec st_press_lps331ap_channels[] = { +/* CUSTOM VALUES FOR LPS25H SENSOR */ +#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_FS_ADDR 0x00 +#define ST_PRESS_LPS25H_FS_MASK 0x00 +#define ST_PRESS_LPS25H_FS_AVL_1260_VAL 0x00 +#define ST_PRESS_LPS25H_FS_AVL_1260_GAIN ST_PRESS_KPASCAL_NANO_SCALE +#define ST_PRESS_LPS25H_FS_AVL_TEMP_GAIN ST_PRESS_CELSIUS_NANO_SCALE +#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_MULTIREAD_BIT true +#define ST_PRESS_LPS25H_TEMP_OFFSET 42500 +#define ST_PRESS_LPS25H_OUT_XL_ADDR 0x28 +#define ST_TEMP_LPS25H_OUT_L_ADDR 0x2b + +static const struct iio_chan_spec st_press_1_channels[] = { { .type = IIO_PRESSURE, .channel2 = IIO_NO_MOD, - .address = ST_PRESS_LPS331AP_OUT_XL_ADDR, + .address = ST_PRESS_1_OUT_XL_ADDR, .scan_index = ST_SENSORS_SCAN_X, .scan_type = { .sign = 'u', @@ -99,7 +125,7 @@ static const struct iio_chan_spec st_press_lps331ap_channels[] = { { .type = IIO_TEMP, .channel2 = IIO_NO_MOD, - .address = ST_TEMP_LPS331AP_OUT_L_ADDR, + .address = ST_TEMP_1_OUT_L_ADDR, .scan_index = -1, .scan_type = { .sign = 'u', @@ -156,8 +182,8 @@ static const struct st_sensors st_press_sensors[] = { .sensors_supported = { [0] = LPS331AP_PRESS_DEV_NAME, }, - .ch = (struct iio_chan_spec *)st_press_lps331ap_channels, - .num_ch = ARRAY_SIZE(st_press_lps331ap_channels), + .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, @@ -233,6 +259,53 @@ static const struct st_sensors st_press_sensors[] = { .multi_read_bit = ST_PRESS_LPS001WP_MULTIREAD_BIT, .bootime = 2, }, + { + .wai = ST_PRESS_LPS25H_WAI_EXP, + .sensors_supported = { + [0] = LPS25H_PRESS_DEV_NAME, + }, + .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, + .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, }, + }, + }, + .pw = { + .addr = ST_PRESS_LPS25H_PW_ADDR, + .mask = ST_PRESS_LPS25H_PW_MASK, + .value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE, + .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, + }, + .fs = { + .addr = ST_PRESS_LPS25H_FS_ADDR, + .mask = ST_PRESS_LPS25H_FS_MASK, + .fs_avl = { + [0] = { + .num = ST_PRESS_FS_AVL_1260MB, + .value = ST_PRESS_LPS25H_FS_AVL_1260_VAL, + .gain = ST_PRESS_LPS25H_FS_AVL_1260_GAIN, + .gain2 = ST_PRESS_LPS25H_FS_AVL_TEMP_GAIN, + }, + }, + }, + .bdu = { + .addr = ST_PRESS_LPS25H_BDU_ADDR, + .mask = ST_PRESS_LPS25H_BDU_MASK, + }, + .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, + }, + .multi_read_bit = ST_PRESS_LPS25H_MULTIREAD_BIT, + .bootime = 2, + }, }; static int st_press_read_raw(struct iio_dev *indio_dev, diff --git a/drivers/iio/pressure/st_pressure_i2c.c b/drivers/iio/pressure/st_pressure_i2c.c index 51eab7f..3cd73e3 100644 --- a/drivers/iio/pressure/st_pressure_i2c.c +++ b/drivers/iio/pressure/st_pressure_i2c.c @@ -50,6 +50,7 @@ static int st_press_i2c_remove(struct i2c_client *client) static const struct i2c_device_id st_press_id_table[] = { { LPS001WP_PRESS_DEV_NAME }, + { LPS25H_PRESS_DEV_NAME }, { LPS331AP_PRESS_DEV_NAME }, {}, }; diff --git a/drivers/iio/pressure/st_pressure_spi.c b/drivers/iio/pressure/st_pressure_spi.c index 27322af..f45d430 100644 --- a/drivers/iio/pressure/st_pressure_spi.c +++ b/drivers/iio/pressure/st_pressure_spi.c @@ -49,6 +49,7 @@ static int st_press_spi_remove(struct spi_device *spi) static const struct spi_device_id st_press_id_table[] = { { LPS001WP_PRESS_DEV_NAME }, + { LPS25H_PRESS_DEV_NAME }, { LPS331AP_PRESS_DEV_NAME }, {}, }; |