diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-04-01 16:45:00 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-04-01 16:45:00 -0700 |
commit | c12e69c6aaf785fd307d05cb6f36ca0e7577ead7 (patch) | |
tree | d12feba57d1f42f8a2a1a382d3bea29603312d14 /drivers/iio | |
parent | 158e0d3621683ee0cdfeeba56f0e5ddd97ae984f (diff) | |
parent | 94debda32429e1a348fec8543245f1190a92d68c (diff) | |
download | op-kernel-dev-c12e69c6aaf785fd307d05cb6f36ca0e7577ead7.zip op-kernel-dev-c12e69c6aaf785fd307d05cb6f36ca0e7577ead7.tar.gz |
Merge tag 'staging-3.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging
Pull staging driver updates from Greg KH:
"Here's the huge drivers/staging/ update for 3.15-rc1.
Loads of cleanup fixes, a few drivers removed, and some new ones
added.
All have been in linux-next for a while"
* tag 'staging-3.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (1375 commits)
staging: xillybus: XILLYBUS_PCIE depends on PCI_MSI
staging: xillybus: Added "select CRC32" for XILLYBUS in Kconfig
staging: comedi: poc: remove obsolete driver
staging: unisys: replace kzalloc/kfree with UISMALLOC/UISFREE
staging: octeon-usb: prevent memory corruption
staging: usbip: fix line over 80 characters
staging: usbip: fix quoted string split across lines
Staging: unisys: Remove RETINT macro
Staging: unisys: Remove FAIL macro
Staging: unisys: Remove RETVOID macro
Staging: unisys: Remove RETPTR macro
Staging: unisys: Remove RETBOOL macro
Staging: unisys: Remove FAIL_WPOSTCODE_1 macro
Staging: unisys: Cleanup macros to get rid of goto statements
Staging: unisys: include: Remove unused macros from timskmod.h
staging: dgap: fix the rest of the checkpatch warnings in dgap.c
Staging: bcm: Remove unnecessary parentheses
staging: wlags49_h2: Delete unnecessary braces
staging: wlags49_h2: Do not use assignment in if condition
staging: wlags49_h2: Enclose macro in a do-while loop
...
Diffstat (limited to 'drivers/iio')
43 files changed, 4178 insertions, 172 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 5c63f091..4bf4c16 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -207,6 +207,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 @@ -214,4 +224,17 @@ config VIPERBOARD_ADC Say yes here to access the ADC part of the Nano River Technologies Viperboard. +config XILINX_XADC + tristate "Xilinx XADC driver" + depends on ARCH_ZYNQ || MICROBLAZE || COMPILE_TEST + depends on HAS_IOMEM + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to have support for the Xilinx XADC. The driver does support + both the ZYNQ interface to the XADC as well as the AXI-XADC interface. + + The driver can also be build as a module. If so, the module will be called + xilinx-xadc. + endmenu diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 85a4a04..bb25254 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -22,4 +22,7 @@ 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 +xilinx-xadc-y := xilinx-xadc-core.o xilinx-xadc-events.o +obj-$(CONFIG_XILINX_XADC) += xilinx-xadc.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..44799eb5 --- /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; + 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/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c new file mode 100644 index 0000000..ab52be2 --- /dev/null +++ b/drivers/iio/adc/xilinx-xadc-core.c @@ -0,0 +1,1333 @@ +/* + * Xilinx XADC driver + * + * Copyright 2013-2014 Analog Devices Inc. + * Author: Lars-Peter Clauen <lars@metafoo.de> + * + * Licensed under the GPL-2. + * + * Documentation for the parts can be found at: + * - XADC hardmacro: Xilinx UG480 + * - ZYNQ XADC interface: Xilinx UG585 + * - AXI XADC interface: Xilinx PG019 + */ + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/sysfs.h> + +#include <linux/iio/buffer.h> +#include <linux/iio/events.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +#include "xilinx-xadc.h" + +static const unsigned int XADC_ZYNQ_UNMASK_TIMEOUT = 500; + +/* ZYNQ register definitions */ +#define XADC_ZYNQ_REG_CFG 0x00 +#define XADC_ZYNQ_REG_INTSTS 0x04 +#define XADC_ZYNQ_REG_INTMSK 0x08 +#define XADC_ZYNQ_REG_STATUS 0x0c +#define XADC_ZYNQ_REG_CFIFO 0x10 +#define XADC_ZYNQ_REG_DFIFO 0x14 +#define XADC_ZYNQ_REG_CTL 0x18 + +#define XADC_ZYNQ_CFG_ENABLE BIT(31) +#define XADC_ZYNQ_CFG_CFIFOTH_MASK (0xf << 20) +#define XADC_ZYNQ_CFG_CFIFOTH_OFFSET 20 +#define XADC_ZYNQ_CFG_DFIFOTH_MASK (0xf << 16) +#define XADC_ZYNQ_CFG_DFIFOTH_OFFSET 16 +#define XADC_ZYNQ_CFG_WEDGE BIT(13) +#define XADC_ZYNQ_CFG_REDGE BIT(12) +#define XADC_ZYNQ_CFG_TCKRATE_MASK (0x3 << 8) +#define XADC_ZYNQ_CFG_TCKRATE_DIV2 (0x0 << 8) +#define XADC_ZYNQ_CFG_TCKRATE_DIV4 (0x1 << 8) +#define XADC_ZYNQ_CFG_TCKRATE_DIV8 (0x2 << 8) +#define XADC_ZYNQ_CFG_TCKRATE_DIV16 (0x3 << 8) +#define XADC_ZYNQ_CFG_IGAP_MASK 0x1f +#define XADC_ZYNQ_CFG_IGAP(x) (x) + +#define XADC_ZYNQ_INT_CFIFO_LTH BIT(9) +#define XADC_ZYNQ_INT_DFIFO_GTH BIT(8) +#define XADC_ZYNQ_INT_ALARM_MASK 0xff +#define XADC_ZYNQ_INT_ALARM_OFFSET 0 + +#define XADC_ZYNQ_STATUS_CFIFO_LVL_MASK (0xf << 16) +#define XADC_ZYNQ_STATUS_CFIFO_LVL_OFFSET 16 +#define XADC_ZYNQ_STATUS_DFIFO_LVL_MASK (0xf << 12) +#define XADC_ZYNQ_STATUS_DFIFO_LVL_OFFSET 12 +#define XADC_ZYNQ_STATUS_CFIFOF BIT(11) +#define XADC_ZYNQ_STATUS_CFIFOE BIT(10) +#define XADC_ZYNQ_STATUS_DFIFOF BIT(9) +#define XADC_ZYNQ_STATUS_DFIFOE BIT(8) +#define XADC_ZYNQ_STATUS_OT BIT(7) +#define XADC_ZYNQ_STATUS_ALM(x) BIT(x) + +#define XADC_ZYNQ_CTL_RESET BIT(4) + +#define XADC_ZYNQ_CMD_NOP 0x00 +#define XADC_ZYNQ_CMD_READ 0x01 +#define XADC_ZYNQ_CMD_WRITE 0x02 + +#define XADC_ZYNQ_CMD(cmd, addr, data) (((cmd) << 26) | ((addr) << 16) | (data)) + +/* AXI register definitions */ +#define XADC_AXI_REG_RESET 0x00 +#define XADC_AXI_REG_STATUS 0x04 +#define XADC_AXI_REG_ALARM_STATUS 0x08 +#define XADC_AXI_REG_CONVST 0x0c +#define XADC_AXI_REG_XADC_RESET 0x10 +#define XADC_AXI_REG_GIER 0x5c +#define XADC_AXI_REG_IPISR 0x60 +#define XADC_AXI_REG_IPIER 0x68 +#define XADC_AXI_ADC_REG_OFFSET 0x200 + +#define XADC_AXI_RESET_MAGIC 0xa +#define XADC_AXI_GIER_ENABLE BIT(31) + +#define XADC_AXI_INT_EOS BIT(4) +#define XADC_AXI_INT_ALARM_MASK 0x3c0f + +#define XADC_FLAGS_BUFFERED BIT(0) + +static void xadc_write_reg(struct xadc *xadc, unsigned int reg, + uint32_t val) +{ + writel(val, xadc->base + reg); +} + +static void xadc_read_reg(struct xadc *xadc, unsigned int reg, + uint32_t *val) +{ + *val = readl(xadc->base + reg); +} + +/* + * The ZYNQ interface uses two asynchronous FIFOs for communication with the + * XADC. Reads and writes to the XADC register are performed by submitting a + * request to the command FIFO (CFIFO), once the request has been completed the + * result can be read from the data FIFO (DFIFO). The method currently used in + * this driver is to submit the request for a read/write operation, then go to + * sleep and wait for an interrupt that signals that a response is available in + * the data FIFO. + */ + +static void xadc_zynq_write_fifo(struct xadc *xadc, uint32_t *cmd, + unsigned int n) +{ + unsigned int i; + + for (i = 0; i < n; i++) + xadc_write_reg(xadc, XADC_ZYNQ_REG_CFIFO, cmd[i]); +} + +static void xadc_zynq_drain_fifo(struct xadc *xadc) +{ + uint32_t status, tmp; + + xadc_read_reg(xadc, XADC_ZYNQ_REG_STATUS, &status); + + while (!(status & XADC_ZYNQ_STATUS_DFIFOE)) { + xadc_read_reg(xadc, XADC_ZYNQ_REG_DFIFO, &tmp); + xadc_read_reg(xadc, XADC_ZYNQ_REG_STATUS, &status); + } +} + +static void xadc_zynq_update_intmsk(struct xadc *xadc, unsigned int mask, + unsigned int val) +{ + xadc->zynq_intmask &= ~mask; + xadc->zynq_intmask |= val; + + xadc_write_reg(xadc, XADC_ZYNQ_REG_INTMSK, + xadc->zynq_intmask | xadc->zynq_masked_alarm); +} + +static int xadc_zynq_write_adc_reg(struct xadc *xadc, unsigned int reg, + uint16_t val) +{ + uint32_t cmd[1]; + uint32_t tmp; + int ret; + + spin_lock_irq(&xadc->lock); + xadc_zynq_update_intmsk(xadc, XADC_ZYNQ_INT_DFIFO_GTH, + XADC_ZYNQ_INT_DFIFO_GTH); + + reinit_completion(&xadc->completion); + + cmd[0] = XADC_ZYNQ_CMD(XADC_ZYNQ_CMD_WRITE, reg, val); + xadc_zynq_write_fifo(xadc, cmd, ARRAY_SIZE(cmd)); + xadc_read_reg(xadc, XADC_ZYNQ_REG_CFG, &tmp); + tmp &= ~XADC_ZYNQ_CFG_DFIFOTH_MASK; + tmp |= 0 << XADC_ZYNQ_CFG_DFIFOTH_OFFSET; + xadc_write_reg(xadc, XADC_ZYNQ_REG_CFG, tmp); + + xadc_zynq_update_intmsk(xadc, XADC_ZYNQ_INT_DFIFO_GTH, 0); + spin_unlock_irq(&xadc->lock); + + ret = wait_for_completion_interruptible_timeout(&xadc->completion, HZ); + if (ret == 0) + ret = -EIO; + else + ret = 0; + + xadc_read_reg(xadc, XADC_ZYNQ_REG_DFIFO, &tmp); + + return ret; +} + +static int xadc_zynq_read_adc_reg(struct xadc *xadc, unsigned int reg, + uint16_t *val) +{ + uint32_t cmd[2]; + uint32_t resp, tmp; + int ret; + + cmd[0] = XADC_ZYNQ_CMD(XADC_ZYNQ_CMD_READ, reg, 0); + cmd[1] = XADC_ZYNQ_CMD(XADC_ZYNQ_CMD_NOP, 0, 0); + + spin_lock_irq(&xadc->lock); + xadc_zynq_update_intmsk(xadc, XADC_ZYNQ_INT_DFIFO_GTH, + XADC_ZYNQ_INT_DFIFO_GTH); + xadc_zynq_drain_fifo(xadc); + reinit_completion(&xadc->completion); + + xadc_zynq_write_fifo(xadc, cmd, ARRAY_SIZE(cmd)); + xadc_read_reg(xadc, XADC_ZYNQ_REG_CFG, &tmp); + tmp &= ~XADC_ZYNQ_CFG_DFIFOTH_MASK; + tmp |= 1 << XADC_ZYNQ_CFG_DFIFOTH_OFFSET; + xadc_write_reg(xadc, XADC_ZYNQ_REG_CFG, tmp); + + xadc_zynq_update_intmsk(xadc, XADC_ZYNQ_INT_DFIFO_GTH, 0); + spin_unlock_irq(&xadc->lock); + ret = wait_for_completion_interruptible_timeout(&xadc->completion, HZ); + if (ret == 0) + ret = -EIO; + if (ret < 0) + return ret; + + xadc_read_reg(xadc, XADC_ZYNQ_REG_DFIFO, &resp); + xadc_read_reg(xadc, XADC_ZYNQ_REG_DFIFO, &resp); + + *val = resp & 0xffff; + + return 0; +} + +static unsigned int xadc_zynq_transform_alarm(unsigned int alarm) +{ + return ((alarm & 0x80) >> 4) | + ((alarm & 0x78) << 1) | + (alarm & 0x07); +} + +/* + * The ZYNQ threshold interrupts are level sensitive. Since we can't make the + * threshold condition go way from within the interrupt handler, this means as + * soon as a threshold condition is present we would enter the interrupt handler + * again and again. To work around this we mask all active thresholds interrupts + * in the interrupt handler and start a timer. In this timer we poll the + * interrupt status and only if the interrupt is inactive we unmask it again. + */ +static void xadc_zynq_unmask_worker(struct work_struct *work) +{ + struct xadc *xadc = container_of(work, struct xadc, zynq_unmask_work.work); + unsigned int misc_sts, unmask; + + xadc_read_reg(xadc, XADC_ZYNQ_REG_STATUS, &misc_sts); + + misc_sts &= XADC_ZYNQ_INT_ALARM_MASK; + + spin_lock_irq(&xadc->lock); + + /* Clear those bits which are not active anymore */ + unmask = (xadc->zynq_masked_alarm ^ misc_sts) & xadc->zynq_masked_alarm; + xadc->zynq_masked_alarm &= misc_sts; + + /* Also clear those which are masked out anyway */ + xadc->zynq_masked_alarm &= ~xadc->zynq_intmask; + + /* Clear the interrupts before we unmask them */ + xadc_write_reg(xadc, XADC_ZYNQ_REG_INTSTS, unmask); + + xadc_zynq_update_intmsk(xadc, 0, 0); + + spin_unlock_irq(&xadc->lock); + + /* if still pending some alarm re-trigger the timer */ + if (xadc->zynq_masked_alarm) { + schedule_delayed_work(&xadc->zynq_unmask_work, + msecs_to_jiffies(XADC_ZYNQ_UNMASK_TIMEOUT)); + } +} + +static irqreturn_t xadc_zynq_threaded_interrupt_handler(int irq, void *devid) +{ + struct iio_dev *indio_dev = devid; + struct xadc *xadc = iio_priv(indio_dev); + unsigned int alarm; + + spin_lock_irq(&xadc->lock); + alarm = xadc->zynq_alarm; + xadc->zynq_alarm = 0; + spin_unlock_irq(&xadc->lock); + + xadc_handle_events(indio_dev, xadc_zynq_transform_alarm(alarm)); + + /* unmask the required interrupts in timer. */ + schedule_delayed_work(&xadc->zynq_unmask_work, + msecs_to_jiffies(XADC_ZYNQ_UNMASK_TIMEOUT)); + + return IRQ_HANDLED; +} + +static irqreturn_t xadc_zynq_interrupt_handler(int irq, void *devid) +{ + struct iio_dev *indio_dev = devid; + struct xadc *xadc = iio_priv(indio_dev); + irqreturn_t ret = IRQ_HANDLED; + uint32_t status; + + xadc_read_reg(xadc, XADC_ZYNQ_REG_INTSTS, &status); + + status &= ~(xadc->zynq_intmask | xadc->zynq_masked_alarm); + + if (!status) + return IRQ_NONE; + + spin_lock(&xadc->lock); + + xadc_write_reg(xadc, XADC_ZYNQ_REG_INTSTS, status); + + if (status & XADC_ZYNQ_INT_DFIFO_GTH) { + xadc_zynq_update_intmsk(xadc, XADC_ZYNQ_INT_DFIFO_GTH, + XADC_ZYNQ_INT_DFIFO_GTH); + complete(&xadc->completion); + } + + status &= XADC_ZYNQ_INT_ALARM_MASK; + if (status) { + xadc->zynq_alarm |= status; + xadc->zynq_masked_alarm |= status; + /* + * mask the current event interrupt, + * unmask it when the interrupt is no more active. + */ + xadc_zynq_update_intmsk(xadc, 0, 0); + ret = IRQ_WAKE_THREAD; + } + spin_unlock(&xadc->lock); + + return ret; +} + +#define XADC_ZYNQ_TCK_RATE_MAX 50000000 +#define XADC_ZYNQ_IGAP_DEFAULT 20 + +static int xadc_zynq_setup(struct platform_device *pdev, + struct iio_dev *indio_dev, int irq) +{ + struct xadc *xadc = iio_priv(indio_dev); + unsigned long pcap_rate; + unsigned int tck_div; + unsigned int div; + unsigned int igap; + unsigned int tck_rate; + + /* TODO: Figure out how to make igap and tck_rate configurable */ + igap = XADC_ZYNQ_IGAP_DEFAULT; + tck_rate = XADC_ZYNQ_TCK_RATE_MAX; + + xadc->zynq_intmask = ~0; + + pcap_rate = clk_get_rate(xadc->clk); + + if (tck_rate > XADC_ZYNQ_TCK_RATE_MAX) + tck_rate = XADC_ZYNQ_TCK_RATE_MAX; + if (tck_rate > pcap_rate / 2) { + div = 2; + } else { + div = pcap_rate / tck_rate; + if (pcap_rate / div > XADC_ZYNQ_TCK_RATE_MAX) + div++; + } + + if (div <= 3) + tck_div = XADC_ZYNQ_CFG_TCKRATE_DIV2; + else if (div <= 7) + tck_div = XADC_ZYNQ_CFG_TCKRATE_DIV4; + else if (div <= 15) + tck_div = XADC_ZYNQ_CFG_TCKRATE_DIV8; + else + tck_div = XADC_ZYNQ_CFG_TCKRATE_DIV16; + + xadc_write_reg(xadc, XADC_ZYNQ_REG_CTL, XADC_ZYNQ_CTL_RESET); + xadc_write_reg(xadc, XADC_ZYNQ_REG_CTL, 0); + xadc_write_reg(xadc, XADC_ZYNQ_REG_INTSTS, ~0); + xadc_write_reg(xadc, XADC_ZYNQ_REG_INTMSK, xadc->zynq_intmask); + xadc_write_reg(xadc, XADC_ZYNQ_REG_CFG, XADC_ZYNQ_CFG_ENABLE | + XADC_ZYNQ_CFG_REDGE | XADC_ZYNQ_CFG_WEDGE | + tck_div | XADC_ZYNQ_CFG_IGAP(igap)); + + return 0; +} + +static unsigned long xadc_zynq_get_dclk_rate(struct xadc *xadc) +{ + unsigned int div; + uint32_t val; + + xadc_read_reg(xadc, XADC_ZYNQ_REG_CFG, &val); + + switch (val & XADC_ZYNQ_CFG_TCKRATE_MASK) { + case XADC_ZYNQ_CFG_TCKRATE_DIV4: + div = 4; + break; + case XADC_ZYNQ_CFG_TCKRATE_DIV8: + div = 8; + break; + case XADC_ZYNQ_CFG_TCKRATE_DIV16: + div = 16; + break; + default: + div = 2; + break; + } + + return clk_get_rate(xadc->clk) / div; +} + +static void xadc_zynq_update_alarm(struct xadc *xadc, unsigned int alarm) +{ + unsigned long flags; + uint32_t status; + + /* Move OT to bit 7 */ + alarm = ((alarm & 0x08) << 4) | ((alarm & 0xf0) >> 1) | (alarm & 0x07); + + spin_lock_irqsave(&xadc->lock, flags); + + /* Clear previous interrupts if any. */ + xadc_read_reg(xadc, XADC_ZYNQ_REG_INTSTS, &status); + xadc_write_reg(xadc, XADC_ZYNQ_REG_INTSTS, status & alarm); + + xadc_zynq_update_intmsk(xadc, XADC_ZYNQ_INT_ALARM_MASK, + ~alarm & XADC_ZYNQ_INT_ALARM_MASK); + + spin_unlock_irqrestore(&xadc->lock, flags); +} + +static const struct xadc_ops xadc_zynq_ops = { + .read = xadc_zynq_read_adc_reg, + .write = xadc_zynq_write_adc_reg, + .setup = xadc_zynq_setup, + .get_dclk_rate = xadc_zynq_get_dclk_rate, + .interrupt_handler = xadc_zynq_interrupt_handler, + .threaded_interrupt_handler = xadc_zynq_threaded_interrupt_handler, + .update_alarm = xadc_zynq_update_alarm, +}; + +static int xadc_axi_read_adc_reg(struct xadc *xadc, unsigned int reg, + uint16_t *val) +{ + uint32_t val32; + + xadc_read_reg(xadc, XADC_AXI_ADC_REG_OFFSET + reg * 4, &val32); + *val = val32 & 0xffff; + + return 0; +} + +static int xadc_axi_write_adc_reg(struct xadc *xadc, unsigned int reg, + uint16_t val) +{ + xadc_write_reg(xadc, XADC_AXI_ADC_REG_OFFSET + reg * 4, val); + + return 0; +} + +static int xadc_axi_setup(struct platform_device *pdev, + struct iio_dev *indio_dev, int irq) +{ + struct xadc *xadc = iio_priv(indio_dev); + + xadc_write_reg(xadc, XADC_AXI_REG_RESET, XADC_AXI_RESET_MAGIC); + xadc_write_reg(xadc, XADC_AXI_REG_GIER, XADC_AXI_GIER_ENABLE); + + return 0; +} + +static irqreturn_t xadc_axi_interrupt_handler(int irq, void *devid) +{ + struct iio_dev *indio_dev = devid; + struct xadc *xadc = iio_priv(indio_dev); + uint32_t status, mask; + unsigned int events; + + xadc_read_reg(xadc, XADC_AXI_REG_IPISR, &status); + xadc_read_reg(xadc, XADC_AXI_REG_IPIER, &mask); + status &= mask; + + if (!status) + return IRQ_NONE; + + if ((status & XADC_AXI_INT_EOS) && xadc->trigger) + iio_trigger_poll(xadc->trigger, 0); + + if (status & XADC_AXI_INT_ALARM_MASK) { + /* + * The order of the bits in the AXI-XADC status register does + * not match the order of the bits in the XADC alarm enable + * register. xadc_handle_events() expects the events to be in + * the same order as the XADC alarm enable register. + */ + events = (status & 0x000e) >> 1; + events |= (status & 0x0001) << 3; + events |= (status & 0x3c00) >> 6; + xadc_handle_events(indio_dev, events); + } + + xadc_write_reg(xadc, XADC_AXI_REG_IPISR, status); + + return IRQ_HANDLED; +} + +static void xadc_axi_update_alarm(struct xadc *xadc, unsigned int alarm) +{ + uint32_t val; + unsigned long flags; + + /* + * The order of the bits in the AXI-XADC status register does not match + * the order of the bits in the XADC alarm enable register. We get + * passed the alarm mask in the same order as in the XADC alarm enable + * register. + */ + alarm = ((alarm & 0x07) << 1) | ((alarm & 0x08) >> 3) | + ((alarm & 0xf0) << 6); + + spin_lock_irqsave(&xadc->lock, flags); + xadc_read_reg(xadc, XADC_AXI_REG_IPIER, &val); + val &= ~XADC_AXI_INT_ALARM_MASK; + val |= alarm; + xadc_write_reg(xadc, XADC_AXI_REG_IPIER, val); + spin_unlock_irqrestore(&xadc->lock, flags); +} + +static unsigned long xadc_axi_get_dclk(struct xadc *xadc) +{ + return clk_get_rate(xadc->clk); +} + +static const struct xadc_ops xadc_axi_ops = { + .read = xadc_axi_read_adc_reg, + .write = xadc_axi_write_adc_reg, + .setup = xadc_axi_setup, + .get_dclk_rate = xadc_axi_get_dclk, + .update_alarm = xadc_axi_update_alarm, + .interrupt_handler = xadc_axi_interrupt_handler, + .flags = XADC_FLAGS_BUFFERED, +}; + +static int _xadc_update_adc_reg(struct xadc *xadc, unsigned int reg, + uint16_t mask, uint16_t val) +{ + uint16_t tmp; + int ret; + + ret = _xadc_read_adc_reg(xadc, reg, &tmp); + if (ret) + return ret; + + return _xadc_write_adc_reg(xadc, reg, (tmp & ~mask) | val); +} + +static int xadc_update_adc_reg(struct xadc *xadc, unsigned int reg, + uint16_t mask, uint16_t val) +{ + int ret; + + mutex_lock(&xadc->mutex); + ret = _xadc_update_adc_reg(xadc, reg, mask, val); + mutex_unlock(&xadc->mutex); + + return ret; +} + +static unsigned long xadc_get_dclk_rate(struct xadc *xadc) +{ + return xadc->ops->get_dclk_rate(xadc); +} + +static int xadc_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *mask) +{ + struct xadc *xadc = iio_priv(indio_dev); + unsigned int n; + + n = bitmap_weight(mask, indio_dev->masklength); + + kfree(xadc->data); + xadc->data = kcalloc(n, sizeof(*xadc->data), GFP_KERNEL); + if (!xadc->data) + return -ENOMEM; + + return 0; +} + +static unsigned int xadc_scan_index_to_channel(unsigned int scan_index) +{ + switch (scan_index) { + case 5: + return XADC_REG_VCCPINT; + case 6: + return XADC_REG_VCCPAUX; + case 7: + return XADC_REG_VCCO_DDR; + case 8: + return XADC_REG_TEMP; + case 9: + return XADC_REG_VCCINT; + case 10: + return XADC_REG_VCCAUX; + case 11: + return XADC_REG_VPVN; + case 12: + return XADC_REG_VREFP; + case 13: + return XADC_REG_VREFN; + case 14: + return XADC_REG_VCCBRAM; + default: + return XADC_REG_VAUX(scan_index - 16); + } +} + +static irqreturn_t xadc_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct xadc *xadc = iio_priv(indio_dev); + unsigned int chan; + int i, j; + + if (!xadc->data) + goto out; + + j = 0; + for_each_set_bit(i, indio_dev->active_scan_mask, + indio_dev->masklength) { + chan = xadc_scan_index_to_channel(i); + xadc_read_adc_reg(xadc, chan, &xadc->data[j]); + j++; + } + + iio_push_to_buffers(indio_dev, xadc->data); + +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int xadc_trigger_set_state(struct iio_trigger *trigger, bool state) +{ + struct xadc *xadc = iio_trigger_get_drvdata(trigger); + unsigned long flags; + unsigned int convst; + unsigned int val; + int ret = 0; + + mutex_lock(&xadc->mutex); + + if (state) { + /* Only one of the two triggers can be active at the a time. */ + if (xadc->trigger != NULL) { + ret = -EBUSY; + goto err_out; + } else { + xadc->trigger = trigger; + if (trigger == xadc->convst_trigger) + convst = XADC_CONF0_EC; + else + convst = 0; + } + ret = _xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF0_EC, + convst); + if (ret) + goto err_out; + } else { + xadc->trigger = NULL; + } + + spin_lock_irqsave(&xadc->lock, flags); + xadc_read_reg(xadc, XADC_AXI_REG_IPIER, &val); + xadc_write_reg(xadc, XADC_AXI_REG_IPISR, val & XADC_AXI_INT_EOS); + if (state) + val |= XADC_AXI_INT_EOS; + else + val &= ~XADC_AXI_INT_EOS; + xadc_write_reg(xadc, XADC_AXI_REG_IPIER, val); + spin_unlock_irqrestore(&xadc->lock, flags); + +err_out: + mutex_unlock(&xadc->mutex); + + return ret; +} + +static const struct iio_trigger_ops xadc_trigger_ops = { + .owner = THIS_MODULE, + .set_trigger_state = &xadc_trigger_set_state, +}; + +static struct iio_trigger *xadc_alloc_trigger(struct iio_dev *indio_dev, + const char *name) +{ + struct iio_trigger *trig; + int ret; + + trig = iio_trigger_alloc("%s%d-%s", indio_dev->name, + indio_dev->id, name); + if (trig == NULL) + return ERR_PTR(-ENOMEM); + + trig->dev.parent = indio_dev->dev.parent; + trig->ops = &xadc_trigger_ops; + iio_trigger_set_drvdata(trig, iio_priv(indio_dev)); + + ret = iio_trigger_register(trig); + if (ret) + goto error_free_trig; + + return trig; + +error_free_trig: + iio_trigger_free(trig); + return ERR_PTR(ret); +} + +static int xadc_power_adc_b(struct xadc *xadc, unsigned int seq_mode) +{ + uint16_t val; + + switch (seq_mode) { + case XADC_CONF1_SEQ_SIMULTANEOUS: + case XADC_CONF1_SEQ_INDEPENDENT: + val = XADC_CONF2_PD_ADC_B; + break; + default: + val = 0; + break; + } + + return xadc_update_adc_reg(xadc, XADC_REG_CONF2, XADC_CONF2_PD_MASK, + val); +} + +static int xadc_get_seq_mode(struct xadc *xadc, unsigned long scan_mode) +{ + unsigned int aux_scan_mode = scan_mode >> 16; + + if (xadc->external_mux_mode == XADC_EXTERNAL_MUX_DUAL) + return XADC_CONF1_SEQ_SIMULTANEOUS; + + if ((aux_scan_mode & 0xff00) == 0 || + (aux_scan_mode & 0x00ff) == 0) + return XADC_CONF1_SEQ_CONTINUOUS; + + return XADC_CONF1_SEQ_SIMULTANEOUS; +} + +static int xadc_postdisable(struct iio_dev *indio_dev) +{ + struct xadc *xadc = iio_priv(indio_dev); + unsigned long scan_mask; + int ret; + int i; + + scan_mask = 1; /* Run calibration as part of the sequence */ + for (i = 0; i < indio_dev->num_channels; i++) + scan_mask |= BIT(indio_dev->channels[i].scan_index); + + /* Enable all channels and calibration */ + ret = xadc_write_adc_reg(xadc, XADC_REG_SEQ(0), scan_mask & 0xffff); + if (ret) + return ret; + + ret = xadc_write_adc_reg(xadc, XADC_REG_SEQ(1), scan_mask >> 16); + if (ret) + return ret; + + ret = xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF1_SEQ_MASK, + XADC_CONF1_SEQ_CONTINUOUS); + if (ret) + return ret; + + return xadc_power_adc_b(xadc, XADC_CONF1_SEQ_CONTINUOUS); +} + +static int xadc_preenable(struct iio_dev *indio_dev) +{ + struct xadc *xadc = iio_priv(indio_dev); + unsigned long scan_mask; + int seq_mode; + int ret; + + ret = xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF1_SEQ_MASK, + XADC_CONF1_SEQ_DEFAULT); + if (ret) + goto err; + + scan_mask = *indio_dev->active_scan_mask; + seq_mode = xadc_get_seq_mode(xadc, scan_mask); + + ret = xadc_write_adc_reg(xadc, XADC_REG_SEQ(0), scan_mask & 0xffff); + if (ret) + goto err; + + ret = xadc_write_adc_reg(xadc, XADC_REG_SEQ(1), scan_mask >> 16); + if (ret) + goto err; + + ret = xadc_power_adc_b(xadc, seq_mode); + if (ret) + goto err; + + ret = xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF1_SEQ_MASK, + seq_mode); + if (ret) + goto err; + + return 0; +err: + xadc_postdisable(indio_dev); + return ret; +} + +static struct iio_buffer_setup_ops xadc_buffer_ops = { + .preenable = &xadc_preenable, + .postenable = &iio_triggered_buffer_postenable, + .predisable = &iio_triggered_buffer_predisable, + .postdisable = &xadc_postdisable, +}; + +static int xadc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, long info) +{ + struct xadc *xadc = iio_priv(indio_dev); + unsigned int div; + uint16_t val16; + int ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + if (iio_buffer_enabled(indio_dev)) + return -EBUSY; + ret = xadc_read_adc_reg(xadc, chan->address, &val16); + if (ret < 0) + return ret; + + val16 >>= 4; + if (chan->scan_type.sign == 'u') + *val = val16; + else + *val = sign_extend32(val16, 11); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + /* V = (val * 3.0) / 4096 */ + switch (chan->address) { + case XADC_REG_VCCINT: + case XADC_REG_VCCAUX: + case XADC_REG_VCCBRAM: + case XADC_REG_VCCPINT: + case XADC_REG_VCCPAUX: + case XADC_REG_VCCO_DDR: + *val = 3000; + break; + default: + *val = 1000; + break; + } + *val2 = 12; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_TEMP: + /* Temp in C = (val * 503.975) / 4096 - 273.15 */ + *val = 503975; + *val2 = 12; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + /* Only the temperature channel has an offset */ + *val = -((273150 << 12) / 503975); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = xadc_read_adc_reg(xadc, XADC_REG_CONF2, &val16); + if (ret) + return ret; + + div = (val16 & XADC_CONF2_DIV_MASK) >> XADC_CONF2_DIV_OFFSET; + if (div < 2) + div = 2; + + *val = xadc_get_dclk_rate(xadc) / div / 26; + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int xadc_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, long info) +{ + struct xadc *xadc = iio_priv(indio_dev); + unsigned long clk_rate = xadc_get_dclk_rate(xadc); + unsigned int div; + + if (info != IIO_CHAN_INFO_SAMP_FREQ) + return -EINVAL; + + if (val <= 0) + return -EINVAL; + + /* Max. 150 kSPS */ + if (val > 150000) + val = 150000; + + val *= 26; + + /* Min 1MHz */ + if (val < 1000000) + val = 1000000; + + /* + * We want to round down, but only if we do not exceed the 150 kSPS + * limit. + */ + div = clk_rate / val; + if (clk_rate / div / 26 > 150000) + div++; + if (div < 2) + div = 2; + else if (div > 0xff) + div = 0xff; + + return xadc_update_adc_reg(xadc, XADC_REG_CONF2, XADC_CONF2_DIV_MASK, + div << XADC_CONF2_DIV_OFFSET); +} + +static const struct iio_event_spec xadc_temp_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_HYSTERESIS), + }, +}; + +/* Separate values for upper and lower thresholds, but only a shared enabled */ +static const struct iio_event_spec xadc_voltage_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + }, +}; + +#define XADC_CHAN_TEMP(_chan, _scan_index, _addr) { \ + .type = IIO_TEMP, \ + .indexed = 1, \ + .channel = (_chan), \ + .address = (_addr), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .event_spec = xadc_temp_events, \ + .num_event_specs = ARRAY_SIZE(xadc_temp_events), \ + .scan_index = (_scan_index), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 12, \ + .storagebits = 16, \ + .shift = 4, \ + .endianness = IIO_CPU, \ + }, \ +} + +#define XADC_CHAN_VOLTAGE(_chan, _scan_index, _addr, _ext, _alarm) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_chan), \ + .address = (_addr), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .event_spec = (_alarm) ? xadc_voltage_events : NULL, \ + .num_event_specs = (_alarm) ? ARRAY_SIZE(xadc_voltage_events) : 0, \ + .scan_index = (_scan_index), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 12, \ + .storagebits = 16, \ + .shift = 4, \ + .endianness = IIO_CPU, \ + }, \ + .extend_name = _ext, \ +} + +static const struct iio_chan_spec xadc_channels[] = { + XADC_CHAN_TEMP(0, 8, XADC_REG_TEMP), + XADC_CHAN_VOLTAGE(0, 9, XADC_REG_VCCINT, "vccint", true), + XADC_CHAN_VOLTAGE(1, 10, XADC_REG_VCCINT, "vccaux", true), + XADC_CHAN_VOLTAGE(2, 14, XADC_REG_VCCBRAM, "vccbram", true), + XADC_CHAN_VOLTAGE(3, 5, XADC_REG_VCCPINT, "vccpint", true), + XADC_CHAN_VOLTAGE(4, 6, XADC_REG_VCCPAUX, "vccpaux", true), + XADC_CHAN_VOLTAGE(5, 7, XADC_REG_VCCO_DDR, "vccoddr", true), + XADC_CHAN_VOLTAGE(6, 12, XADC_REG_VREFP, "vrefp", false), + XADC_CHAN_VOLTAGE(7, 13, XADC_REG_VREFN, "vrefn", false), + XADC_CHAN_VOLTAGE(8, 11, XADC_REG_VPVN, NULL, false), + XADC_CHAN_VOLTAGE(9, 16, XADC_REG_VAUX(0), NULL, false), + XADC_CHAN_VOLTAGE(10, 17, XADC_REG_VAUX(1), NULL, false), + XADC_CHAN_VOLTAGE(11, 18, XADC_REG_VAUX(2), NULL, false), + XADC_CHAN_VOLTAGE(12, 19, XADC_REG_VAUX(3), NULL, false), + XADC_CHAN_VOLTAGE(13, 20, XADC_REG_VAUX(4), NULL, false), + XADC_CHAN_VOLTAGE(14, 21, XADC_REG_VAUX(5), NULL, false), + XADC_CHAN_VOLTAGE(15, 22, XADC_REG_VAUX(6), NULL, false), + XADC_CHAN_VOLTAGE(16, 23, XADC_REG_VAUX(7), NULL, false), + XADC_CHAN_VOLTAGE(17, 24, XADC_REG_VAUX(8), NULL, false), + XADC_CHAN_VOLTAGE(18, 25, XADC_REG_VAUX(9), NULL, false), + XADC_CHAN_VOLTAGE(19, 26, XADC_REG_VAUX(10), NULL, false), + XADC_CHAN_VOLTAGE(20, 27, XADC_REG_VAUX(11), NULL, false), + XADC_CHAN_VOLTAGE(21, 28, XADC_REG_VAUX(12), NULL, false), + XADC_CHAN_VOLTAGE(22, 29, XADC_REG_VAUX(13), NULL, false), + XADC_CHAN_VOLTAGE(23, 30, XADC_REG_VAUX(14), NULL, false), + XADC_CHAN_VOLTAGE(24, 31, XADC_REG_VAUX(15), NULL, false), +}; + +static const struct iio_info xadc_info = { + .read_raw = &xadc_read_raw, + .write_raw = &xadc_write_raw, + .read_event_config = &xadc_read_event_config, + .write_event_config = &xadc_write_event_config, + .read_event_value = &xadc_read_event_value, + .write_event_value = &xadc_write_event_value, + .update_scan_mode = &xadc_update_scan_mode, + .driver_module = THIS_MODULE, +}; + +static const struct of_device_id xadc_of_match_table[] = { + { .compatible = "xlnx,zynq-xadc-1.00.a", (void *)&xadc_zynq_ops }, + { .compatible = "xlnx,axi-xadc-1.00.a", (void *)&xadc_axi_ops }, + { }, +}; +MODULE_DEVICE_TABLE(of, xadc_of_match_table); + +static int xadc_parse_dt(struct iio_dev *indio_dev, struct device_node *np, + unsigned int *conf) +{ + struct xadc *xadc = iio_priv(indio_dev); + struct iio_chan_spec *channels, *chan; + struct device_node *chan_node, *child; + unsigned int num_channels; + const char *external_mux; + u32 ext_mux_chan; + int reg; + int ret; + + *conf = 0; + + ret = of_property_read_string(np, "xlnx,external-mux", &external_mux); + if (ret < 0 || strcasecmp(external_mux, "none") == 0) + xadc->external_mux_mode = XADC_EXTERNAL_MUX_NONE; + else if (strcasecmp(external_mux, "single") == 0) + xadc->external_mux_mode = XADC_EXTERNAL_MUX_SINGLE; + else if (strcasecmp(external_mux, "dual") == 0) + xadc->external_mux_mode = XADC_EXTERNAL_MUX_DUAL; + else + return -EINVAL; + + if (xadc->external_mux_mode != XADC_EXTERNAL_MUX_NONE) { + ret = of_property_read_u32(np, "xlnx,external-mux-channel", + &ext_mux_chan); + if (ret < 0) + return ret; + + if (xadc->external_mux_mode == XADC_EXTERNAL_MUX_SINGLE) { + if (ext_mux_chan == 0) + ext_mux_chan = XADC_REG_VPVN; + else if (ext_mux_chan <= 16) + ext_mux_chan = XADC_REG_VAUX(ext_mux_chan - 1); + else + return -EINVAL; + } else { + if (ext_mux_chan > 0 && ext_mux_chan <= 8) + ext_mux_chan = XADC_REG_VAUX(ext_mux_chan - 1); + else + return -EINVAL; + } + + *conf |= XADC_CONF0_MUX | XADC_CONF0_CHAN(ext_mux_chan); + } + + channels = kmemdup(xadc_channels, sizeof(xadc_channels), GFP_KERNEL); + if (!channels) + return -ENOMEM; + + num_channels = 9; + chan = &channels[9]; + + chan_node = of_get_child_by_name(np, "xlnx,channels"); + if (chan_node) { + for_each_child_of_node(chan_node, child) { + if (num_channels >= ARRAY_SIZE(xadc_channels)) { + of_node_put(child); + break; + } + + ret = of_property_read_u32(child, "reg", ®); + if (ret || reg > 16) + continue; + + if (of_property_read_bool(child, "xlnx,bipolar")) + chan->scan_type.sign = 's'; + + if (reg == 0) { + chan->scan_index = 11; + chan->address = XADC_REG_VPVN; + } else { + chan->scan_index = 15 + reg; + chan->scan_index = XADC_REG_VAUX(reg - 1); + } + num_channels++; + chan++; + } + } + of_node_put(chan_node); + + indio_dev->num_channels = num_channels; + indio_dev->channels = krealloc(channels, sizeof(*channels) * + num_channels, GFP_KERNEL); + /* If we can't resize the channels array, just use the original */ + if (!indio_dev->channels) + indio_dev->channels = channels; + + return 0; +} + +static int xadc_probe(struct platform_device *pdev) +{ + const struct of_device_id *id; + struct iio_dev *indio_dev; + unsigned int bipolar_mask; + struct resource *mem; + unsigned int conf0; + struct xadc *xadc; + int ret; + int irq; + int i; + + if (!pdev->dev.of_node) + return -ENODEV; + + id = of_match_node(xadc_of_match_table, pdev->dev.of_node); + if (!id) + return -EINVAL; + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) + return -ENXIO; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*xadc)); + if (!indio_dev) + return -ENOMEM; + + xadc = iio_priv(indio_dev); + xadc->ops = id->data; + init_completion(&xadc->completion); + mutex_init(&xadc->mutex); + spin_lock_init(&xadc->lock); + INIT_DELAYED_WORK(&xadc->zynq_unmask_work, xadc_zynq_unmask_worker); + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + xadc->base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(xadc->base)) + return PTR_ERR(xadc->base); + + indio_dev->dev.parent = &pdev->dev; + indio_dev->dev.of_node = pdev->dev.of_node; + indio_dev->name = "xadc"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &xadc_info; + + ret = xadc_parse_dt(indio_dev, pdev->dev.of_node, &conf0); + if (ret) + goto err_device_free; + + if (xadc->ops->flags & XADC_FLAGS_BUFFERED) { + ret = iio_triggered_buffer_setup(indio_dev, + &iio_pollfunc_store_time, &xadc_trigger_handler, + &xadc_buffer_ops); + if (ret) + goto err_device_free; + + xadc->convst_trigger = xadc_alloc_trigger(indio_dev, "convst"); + if (IS_ERR(xadc->convst_trigger)) + goto err_triggered_buffer_cleanup; + xadc->samplerate_trigger = xadc_alloc_trigger(indio_dev, + "samplerate"); + if (IS_ERR(xadc->samplerate_trigger)) + goto err_free_convst_trigger; + } + + xadc->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(xadc->clk)) { + ret = PTR_ERR(xadc->clk); + goto err_free_samplerate_trigger; + } + clk_prepare_enable(xadc->clk); + + ret = xadc->ops->setup(pdev, indio_dev, irq); + if (ret) + goto err_free_samplerate_trigger; + + ret = request_threaded_irq(irq, xadc->ops->interrupt_handler, + xadc->ops->threaded_interrupt_handler, + 0, dev_name(&pdev->dev), indio_dev); + if (ret) + goto err_clk_disable_unprepare; + + for (i = 0; i < 16; i++) + xadc_read_adc_reg(xadc, XADC_REG_THRESHOLD(i), + &xadc->threshold[i]); + + ret = xadc_write_adc_reg(xadc, XADC_REG_CONF0, conf0); + if (ret) + goto err_free_irq; + + bipolar_mask = 0; + for (i = 0; i < indio_dev->num_channels; i++) { + if (indio_dev->channels[i].scan_type.sign == 's') + bipolar_mask |= BIT(indio_dev->channels[i].scan_index); + } + + ret = xadc_write_adc_reg(xadc, XADC_REG_INPUT_MODE(0), bipolar_mask); + if (ret) + goto err_free_irq; + ret = xadc_write_adc_reg(xadc, XADC_REG_INPUT_MODE(1), + bipolar_mask >> 16); + if (ret) + goto err_free_irq; + + /* Disable all alarms */ + xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF1_ALARM_MASK, + XADC_CONF1_ALARM_MASK); + + /* Set thresholds to min/max */ + for (i = 0; i < 16; i++) { + /* + * Set max voltage threshold and both temperature thresholds to + * 0xffff, min voltage threshold to 0. + */ + if (i % 8 < 4 || i == 7) + xadc->threshold[i] = 0xffff; + else + xadc->threshold[i] = 0; + xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(i), + xadc->threshold[i]); + } + + /* Go to non-buffered mode */ + xadc_postdisable(indio_dev); + + ret = iio_device_register(indio_dev); + if (ret) + goto err_free_irq; + + platform_set_drvdata(pdev, indio_dev); + + return 0; + +err_free_irq: + free_irq(irq, indio_dev); +err_free_samplerate_trigger: + if (xadc->ops->flags & XADC_FLAGS_BUFFERED) + iio_trigger_free(xadc->samplerate_trigger); +err_free_convst_trigger: + if (xadc->ops->flags & XADC_FLAGS_BUFFERED) + iio_trigger_free(xadc->convst_trigger); +err_triggered_buffer_cleanup: + if (xadc->ops->flags & XADC_FLAGS_BUFFERED) + iio_triggered_buffer_cleanup(indio_dev); +err_clk_disable_unprepare: + clk_disable_unprepare(xadc->clk); +err_device_free: + kfree(indio_dev->channels); + + return ret; +} + +static int xadc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct xadc *xadc = iio_priv(indio_dev); + int irq = platform_get_irq(pdev, 0); + + iio_device_unregister(indio_dev); + if (xadc->ops->flags & XADC_FLAGS_BUFFERED) { + iio_trigger_free(xadc->samplerate_trigger); + iio_trigger_free(xadc->convst_trigger); + iio_triggered_buffer_cleanup(indio_dev); + } + free_irq(irq, indio_dev); + clk_disable_unprepare(xadc->clk); + cancel_delayed_work(&xadc->zynq_unmask_work); + kfree(xadc->data); + kfree(indio_dev->channels); + + return 0; +} + +static struct platform_driver xadc_driver = { + .probe = xadc_probe, + .remove = xadc_remove, + .driver = { + .name = "xadc", + .owner = THIS_MODULE, + .of_match_table = xadc_of_match_table, + }, +}; +module_platform_driver(xadc_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("Xilinx XADC IIO driver"); diff --git a/drivers/iio/adc/xilinx-xadc-events.c b/drivers/iio/adc/xilinx-xadc-events.c new file mode 100644 index 0000000..3e7f0d7 --- /dev/null +++ b/drivers/iio/adc/xilinx-xadc-events.c @@ -0,0 +1,254 @@ +/* + * Xilinx XADC driver + * + * Copyright 2013 Analog Devices Inc. + * Author: Lars-Peter Clauen <lars@metafoo.de> + * + * Licensed under the GPL-2. + */ + +#include <linux/iio/events.h> +#include <linux/iio/iio.h> +#include <linux/kernel.h> + +#include "xilinx-xadc.h" + +static const struct iio_chan_spec *xadc_event_to_channel( + struct iio_dev *indio_dev, unsigned int event) +{ + switch (event) { + case XADC_THRESHOLD_OT_MAX: + case XADC_THRESHOLD_TEMP_MAX: + return &indio_dev->channels[0]; + case XADC_THRESHOLD_VCCINT_MAX: + case XADC_THRESHOLD_VCCAUX_MAX: + return &indio_dev->channels[event]; + default: + return &indio_dev->channels[event-1]; + } +} + +static void xadc_handle_event(struct iio_dev *indio_dev, unsigned int event) +{ + const struct iio_chan_spec *chan; + unsigned int offset; + + /* Temperature threshold error, we don't handle this yet */ + if (event == 0) + return; + + if (event < 4) + offset = event; + else + offset = event + 4; + + chan = xadc_event_to_channel(indio_dev, event); + + if (chan->type == IIO_TEMP) { + /* + * The temperature channel only supports over-temperature + * events. + */ + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(chan->type, chan->channel, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), + iio_get_time_ns()); + } else { + /* + * For other channels we don't know whether it is a upper or + * lower threshold event. Userspace will have to check the + * channel value if it wants to know. + */ + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(chan->type, chan->channel, + IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER), + iio_get_time_ns()); + } +} + +void xadc_handle_events(struct iio_dev *indio_dev, unsigned long events) +{ + unsigned int i; + + for_each_set_bit(i, &events, 8) + xadc_handle_event(indio_dev, i); +} + +static unsigned xadc_get_threshold_offset(const struct iio_chan_spec *chan, + enum iio_event_direction dir) +{ + unsigned int offset; + + if (chan->type == IIO_TEMP) { + offset = XADC_THRESHOLD_OT_MAX; + } else { + if (chan->channel < 2) + offset = chan->channel + 1; + else + offset = chan->channel + 6; + } + + if (dir == IIO_EV_DIR_FALLING) + offset += 4; + + return offset; +} + +static unsigned int xadc_get_alarm_mask(const struct iio_chan_spec *chan) +{ + if (chan->type == IIO_TEMP) { + return XADC_ALARM_OT_MASK; + } else { + switch (chan->channel) { + case 0: + return XADC_ALARM_VCCINT_MASK; + case 1: + return XADC_ALARM_VCCAUX_MASK; + case 2: + return XADC_ALARM_VCCBRAM_MASK; + case 3: + return XADC_ALARM_VCCPINT_MASK; + case 4: + return XADC_ALARM_VCCPAUX_MASK; + case 5: + return XADC_ALARM_VCCODDR_MASK; + default: + /* We will never get here */ + return 0; + } + } +} + +int xadc_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir) +{ + struct xadc *xadc = iio_priv(indio_dev); + + return (bool)(xadc->alarm_mask & xadc_get_alarm_mask(chan)); +} + +int xadc_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, int state) +{ + unsigned int alarm = xadc_get_alarm_mask(chan); + struct xadc *xadc = iio_priv(indio_dev); + uint16_t cfg, old_cfg; + int ret; + + mutex_lock(&xadc->mutex); + + if (state) + xadc->alarm_mask |= alarm; + else + xadc->alarm_mask &= ~alarm; + + xadc->ops->update_alarm(xadc, xadc->alarm_mask); + + ret = _xadc_read_adc_reg(xadc, XADC_REG_CONF1, &cfg); + if (ret) + goto err_out; + + old_cfg = cfg; + cfg |= XADC_CONF1_ALARM_MASK; + cfg &= ~((xadc->alarm_mask & 0xf0) << 4); /* bram, pint, paux, ddr */ + cfg &= ~((xadc->alarm_mask & 0x08) >> 3); /* ot */ + cfg &= ~((xadc->alarm_mask & 0x07) << 1); /* temp, vccint, vccaux */ + if (old_cfg != cfg) + ret = _xadc_write_adc_reg(xadc, XADC_REG_CONF1, cfg); + +err_out: + mutex_unlock(&xadc->mutex); + + return ret; +} + +/* Register value is msb aligned, the lower 4 bits are ignored */ +#define XADC_THRESHOLD_VALUE_SHIFT 4 + +int xadc_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, enum iio_event_info info, + int *val, int *val2) +{ + unsigned int offset = xadc_get_threshold_offset(chan, dir); + struct xadc *xadc = iio_priv(indio_dev); + + switch (info) { + case IIO_EV_INFO_VALUE: + *val = xadc->threshold[offset]; + break; + case IIO_EV_INFO_HYSTERESIS: + *val = xadc->temp_hysteresis; + break; + default: + return -EINVAL; + } + + *val >>= XADC_THRESHOLD_VALUE_SHIFT; + + return IIO_VAL_INT; +} + +int xadc_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, enum iio_event_info info, + int val, int val2) +{ + unsigned int offset = xadc_get_threshold_offset(chan, dir); + struct xadc *xadc = iio_priv(indio_dev); + int ret = 0; + + val <<= XADC_THRESHOLD_VALUE_SHIFT; + + if (val < 0 || val > 0xffff) + return -EINVAL; + + mutex_lock(&xadc->mutex); + + switch (info) { + case IIO_EV_INFO_VALUE: + xadc->threshold[offset] = val; + break; + case IIO_EV_INFO_HYSTERESIS: + xadc->temp_hysteresis = val; + break; + default: + mutex_unlock(&xadc->mutex); + return -EINVAL; + } + + if (chan->type == IIO_TEMP) { + /* + * According to the datasheet we need to set the lower 4 bits to + * 0x3, otherwise 125 degree celsius will be used as the + * threshold. + */ + val |= 0x3; + + /* + * Since we store the hysteresis as relative (to the threshold) + * value, but the hardware expects an absolute value we need to + * recalcualte this value whenever the hysteresis or the + * threshold changes. + */ + if (xadc->threshold[offset] < xadc->temp_hysteresis) + xadc->threshold[offset + 4] = 0; + else + xadc->threshold[offset + 4] = xadc->threshold[offset] - + xadc->temp_hysteresis; + ret = _xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(offset + 4), + xadc->threshold[offset + 4]); + if (ret) + goto out_unlock; + } + + if (info == IIO_EV_INFO_VALUE) + ret = _xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(offset), val); + +out_unlock: + mutex_unlock(&xadc->mutex); + + return ret; +} diff --git a/drivers/iio/adc/xilinx-xadc.h b/drivers/iio/adc/xilinx-xadc.h new file mode 100644 index 0000000..c7487e8 --- /dev/null +++ b/drivers/iio/adc/xilinx-xadc.h @@ -0,0 +1,209 @@ +/* + * Xilinx XADC driver + * + * Copyright 2013 Analog Devices Inc. + * Author: Lars-Peter Clauen <lars@metafoo.de> + * + * Licensed under the GPL-2. + */ + +#ifndef __IIO_XILINX_XADC__ +#define __IIO_XILINX_XADC__ + +#include <linux/interrupt.h> +#include <linux/mutex.h> +#include <linux/spinlock.h> + +struct iio_dev; +struct clk; +struct xadc_ops; +struct platform_device; + +void xadc_handle_events(struct iio_dev *indio_dev, unsigned long events); + +int xadc_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir); +int xadc_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, int state); +int xadc_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, enum iio_event_info info, + int *val, int *val2); +int xadc_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, enum iio_event_type type, + enum iio_event_direction dir, enum iio_event_info info, + int val, int val2); + +enum xadc_external_mux_mode { + XADC_EXTERNAL_MUX_NONE, + XADC_EXTERNAL_MUX_SINGLE, + XADC_EXTERNAL_MUX_DUAL, +}; + +struct xadc { + void __iomem *base; + struct clk *clk; + + const struct xadc_ops *ops; + + uint16_t threshold[16]; + uint16_t temp_hysteresis; + unsigned int alarm_mask; + + uint16_t *data; + + struct iio_trigger *trigger; + struct iio_trigger *convst_trigger; + struct iio_trigger *samplerate_trigger; + + enum xadc_external_mux_mode external_mux_mode; + + unsigned int zynq_alarm; + unsigned int zynq_masked_alarm; + unsigned int zynq_intmask; + struct delayed_work zynq_unmask_work; + + struct mutex mutex; + spinlock_t lock; + + struct completion completion; +}; + +struct xadc_ops { + int (*read)(struct xadc *, unsigned int, uint16_t *); + int (*write)(struct xadc *, unsigned int, uint16_t); + int (*setup)(struct platform_device *pdev, struct iio_dev *indio_dev, + int irq); + void (*update_alarm)(struct xadc *, unsigned int); + unsigned long (*get_dclk_rate)(struct xadc *); + irqreturn_t (*interrupt_handler)(int, void *); + irqreturn_t (*threaded_interrupt_handler)(int, void *); + + unsigned int flags; +}; + +static inline int _xadc_read_adc_reg(struct xadc *xadc, unsigned int reg, + uint16_t *val) +{ + lockdep_assert_held(&xadc->mutex); + return xadc->ops->read(xadc, reg, val); +} + +static inline int _xadc_write_adc_reg(struct xadc *xadc, unsigned int reg, + uint16_t val) +{ + lockdep_assert_held(&xadc->mutex); + return xadc->ops->write(xadc, reg, val); +} + +static inline int xadc_read_adc_reg(struct xadc *xadc, unsigned int reg, + uint16_t *val) +{ + int ret; + + mutex_lock(&xadc->mutex); + ret = _xadc_read_adc_reg(xadc, reg, val); + mutex_unlock(&xadc->mutex); + return ret; +} + +static inline int xadc_write_adc_reg(struct xadc *xadc, unsigned int reg, + uint16_t val) +{ + int ret; + + mutex_lock(&xadc->mutex); + ret = _xadc_write_adc_reg(xadc, reg, val); + mutex_unlock(&xadc->mutex); + return ret; +} + +/* XADC hardmacro register definitions */ +#define XADC_REG_TEMP 0x00 +#define XADC_REG_VCCINT 0x01 +#define XADC_REG_VCCAUX 0x02 +#define XADC_REG_VPVN 0x03 +#define XADC_REG_VREFP 0x04 +#define XADC_REG_VREFN 0x05 +#define XADC_REG_VCCBRAM 0x06 + +#define XADC_REG_VCCPINT 0x0d +#define XADC_REG_VCCPAUX 0x0e +#define XADC_REG_VCCO_DDR 0x0f +#define XADC_REG_VAUX(x) (0x10 + (x)) + +#define XADC_REG_MAX_TEMP 0x20 +#define XADC_REG_MAX_VCCINT 0x21 +#define XADC_REG_MAX_VCCAUX 0x22 +#define XADC_REG_MAX_VCCBRAM 0x23 +#define XADC_REG_MIN_TEMP 0x24 +#define XADC_REG_MIN_VCCINT 0x25 +#define XADC_REG_MIN_VCCAUX 0x26 +#define XADC_REG_MIN_VCCBRAM 0x27 +#define XADC_REG_MAX_VCCPINT 0x28 +#define XADC_REG_MAX_VCCPAUX 0x29 +#define XADC_REG_MAX_VCCO_DDR 0x2a +#define XADC_REG_MIN_VCCPINT 0x2b +#define XADC_REG_MIN_VCCPAUX 0x2c +#define XADC_REG_MIN_VCCO_DDR 0x2d + +#define XADC_REG_CONF0 0x40 +#define XADC_REG_CONF1 0x41 +#define XADC_REG_CONF2 0x42 +#define XADC_REG_SEQ(x) (0x48 + (x)) +#define XADC_REG_INPUT_MODE(x) (0x4c + (x)) +#define XADC_REG_THRESHOLD(x) (0x50 + (x)) + +#define XADC_REG_FLAG 0x3f + +#define XADC_CONF0_EC BIT(9) +#define XADC_CONF0_ACQ BIT(8) +#define XADC_CONF0_MUX BIT(11) +#define XADC_CONF0_CHAN(x) (x) + +#define XADC_CONF1_SEQ_MASK (0xf << 12) +#define XADC_CONF1_SEQ_DEFAULT (0 << 12) +#define XADC_CONF1_SEQ_SINGLE_PASS (1 << 12) +#define XADC_CONF1_SEQ_CONTINUOUS (2 << 12) +#define XADC_CONF1_SEQ_SINGLE_CHANNEL (3 << 12) +#define XADC_CONF1_SEQ_SIMULTANEOUS (4 << 12) +#define XADC_CONF1_SEQ_INDEPENDENT (8 << 12) +#define XADC_CONF1_ALARM_MASK 0x0f0f + +#define XADC_CONF2_DIV_MASK 0xff00 +#define XADC_CONF2_DIV_OFFSET 8 + +#define XADC_CONF2_PD_MASK (0x3 << 4) +#define XADC_CONF2_PD_NONE (0x0 << 4) +#define XADC_CONF2_PD_ADC_B (0x2 << 4) +#define XADC_CONF2_PD_BOTH (0x3 << 4) + +#define XADC_ALARM_TEMP_MASK BIT(0) +#define XADC_ALARM_VCCINT_MASK BIT(1) +#define XADC_ALARM_VCCAUX_MASK BIT(2) +#define XADC_ALARM_OT_MASK BIT(3) +#define XADC_ALARM_VCCBRAM_MASK BIT(4) +#define XADC_ALARM_VCCPINT_MASK BIT(5) +#define XADC_ALARM_VCCPAUX_MASK BIT(6) +#define XADC_ALARM_VCCODDR_MASK BIT(7) + +#define XADC_THRESHOLD_TEMP_MAX 0x0 +#define XADC_THRESHOLD_VCCINT_MAX 0x1 +#define XADC_THRESHOLD_VCCAUX_MAX 0x2 +#define XADC_THRESHOLD_OT_MAX 0x3 +#define XADC_THRESHOLD_TEMP_MIN 0x4 +#define XADC_THRESHOLD_VCCINT_MIN 0x5 +#define XADC_THRESHOLD_VCCAUX_MIN 0x6 +#define XADC_THRESHOLD_OT_MIN 0x7 +#define XADC_THRESHOLD_VCCBRAM_MAX 0x8 +#define XADC_THRESHOLD_VCCPINT_MAX 0x9 +#define XADC_THRESHOLD_VCCPAUX_MAX 0xa +#define XADC_THRESHOLD_VCCODDR_MAX 0xb +#define XADC_THRESHOLD_VCCBRAM_MIN 0xc +#define XADC_THRESHOLD_VCCPINT_MIN 0xd +#define XADC_THRESHOLD_VCCPAUX_MIN 0xe +#define XADC_THRESHOLD_VCCODDR_MIN 0xf + +#endif 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..8b77782 100644 --- a/drivers/iio/magnetometer/mag3110.c +++ b/drivers/iio/magnetometer/mag3110.c @@ -183,9 +183,17 @@ static int mag3110_read_raw(struct iio_dev *indio_dev, return -EINVAL; } case IIO_CHAN_INFO_SCALE: - *val = 0; - *val2 = 1000; - return IIO_VAL_INT_PLUS_MICRO; + 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; + } case IIO_CHAN_INFO_SAMP_FREQ: i = data->ctrl_reg1 >> MAG3110_CTRL_DR_SHIFT; *val = mag3110_samp_freq[i][0]; @@ -270,7 +278,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 }, {}, }; |