mirror of https://gitee.com/openkylin/linux.git
Second round of IIO new driver, functionality and cleanups for the 3.15 series.
There are a few fixes in here that might, earlier in a cycle, have gone to Greg as fixes. Given they are either minor or have never actually been observed as causing trouble (the locking bug in the event code) and are invasive, I have included them in this pull request, targeting the 3.15 merge window instead. The rest are pretty uncontroversial new drivers, a handy little tool for the example code in our documentation and little cleanups. New drivers * Freescale Vybrid and i.MX6SLX ADC driver. * HID Sensor hub proximity sensors. * HID Sensor hub pressure sensors. * LPS25H Pressure sensors added to the ST micro pressure sensor driver. New functionality * lsiio tool. This is added to the staging tree as we haven't yet moved the example code it sits with out. Moving this code out is now a reasonably high priority but holding up this tool in the meantime did not seem worthwhile. * mag3110 - add missing scale factor for temperature output to userspace. Cleanups * Fix a bug in the event reporting in which a spin lock might be held over when a sleep occured. A similar bug was found by Lars in the buffer code. It has not to our knowledge been observed as actually occuring and is a little too invasive to push out as a fix. * Drop the IIO_ST macro after clearing out all users. This macro was a very bad idea leading to a number of bugs after it stopped covering all elements of the structure being assigned and people started making assumptions about what it did cover. Glad to see it go! * Avoid applying extended name to shared attributes as it makes no sense. No in tree drivers were using the combination, hence not pushed out as a fix. * ad799x - move to devm_request_threaded_irq to reduce boilerplate clean up. * bma180 - make the low_pass_filter_3db_frequency info element shared rather than per attribute. The old approach was valid but not as clean as it might be and was setting a bad example. Hence the cleanup. * mxs-lradc - propogate the error code form a platform_get_irq call rather than eating it up by returning -EINVAL on all errors. * ad799x - typo fix in the copyright message. Either that or Michael was asserting a copyright that moved backwards in time by about a thousand years. * ad799x - use a regulator for vref rather than platform data. The driver dates from just as the regulator framework was coming into common use so provides an alternative way of specifying the reference voltage. We no longer need that approach so drop it in favour of a regulator only approach. * max1363 - some internal vref values were out by a small amount. The effect would have been tiny and no one noticed hence not pushing this through as a fix. * core - replace some pointless goto error_ret (with no clean up) lines with direct returns. This is my bad coding style so I'm glad to see it cleaned up. * core - avoid a kasprintf that just directly prints a string with no formatting elements. This has always been there but Lars just noticed it. Oops. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQIcBAABAgAGBQJTCht4AAoJEFSFNJnE9BaIsssQALHkJOTts8eAMy+N8JQkUcWE JhDEbu689OoR9NtVc2o/4nfFaVwzWk6MBtLY1wxpsNFi4IxL/utVBv4iP4pPCBZ1 g3VFiHgREPOxgup8MDXhmwx+n0tRyLJd+vX6uBFDMF/+MNqXMUy/38eWZCYMXz87 icdxk9Ircr+ud7q0JVcpeNGjMUNHo+yEORFSKbdnTfKCuRJe7xdSqNpRQBQEr6y9 ohRJ6CXf9FNsoDuInk3VNEo6HtbZmVi215fy8nGknP5t6/q6exl0utPihoNpWJNZ aP1qjUrS1XEAIozQAoJi9Q2hJSxIRNXwuVz1modcPEeattKGajGWJggHZJxk27Sf fiSeRbbw1vJ8hLN7mRcg1FIF2m79cA7pkhUWwKO2m7Dp/sJQ/eKnhh/JJML6sZB5 3/zKFlvpgANfSZTmqDGCQ/hVBNUcJ7xJA1Vbh1t2FIYctZlkh9ogfX/T2wHEJTfg Mpg7BoDgOaicUyiZBFADsHxTDuhlhx6en9JfYqR6YZHd1eMU7SR1IHzGpo4mDxkB fUUmu0bWnEwslmzek7CaGMVZ00WcSlsP6mpQzfk/R98bIZJb1MmKWyuefVMKyr4K JxNbPIBwR70A6j8qh/lh3egQsUpuamsXoO7Y68RxylYOf1XL7zYWo/1xoOh4Jl4Q +gihFn6B6ggHjv1wup2G =Dg3R -----END PGP SIGNATURE----- Merge tag 'iio-for-3.15b' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next Jonathan writes: Second round of IIO new driver, functionality and cleanups for the 3.15 series. There are a few fixes in here that might, earlier in a cycle, have gone to Greg as fixes. Given they are either minor or have never actually been observed as causing trouble (the locking bug in the event code) and are invasive, I have included them in this pull request, targeting the 3.15 merge window instead. The rest are pretty uncontroversial new drivers, a handy little tool for the example code in our documentation and little cleanups. New drivers * Freescale Vybrid and i.MX6SLX ADC driver. * HID Sensor hub proximity sensors. * HID Sensor hub pressure sensors. * LPS25H Pressure sensors added to the ST micro pressure sensor driver. New functionality * lsiio tool. This is added to the staging tree as we haven't yet moved the example code it sits with out. Moving this code out is now a reasonably high priority but holding up this tool in the meantime did not seem worthwhile. * mag3110 - add missing scale factor for temperature output to userspace. Cleanups * Fix a bug in the event reporting in which a spin lock might be held over when a sleep occured. A similar bug was found by Lars in the buffer code. It has not to our knowledge been observed as actually occuring and is a little too invasive to push out as a fix. * Drop the IIO_ST macro after clearing out all users. This macro was a very bad idea leading to a number of bugs after it stopped covering all elements of the structure being assigned and people started making assumptions about what it did cover. Glad to see it go! * Avoid applying extended name to shared attributes as it makes no sense. No in tree drivers were using the combination, hence not pushed out as a fix. * ad799x - move to devm_request_threaded_irq to reduce boilerplate clean up. * bma180 - make the low_pass_filter_3db_frequency info element shared rather than per attribute. The old approach was valid but not as clean as it might be and was setting a bad example. Hence the cleanup. * mxs-lradc - propogate the error code form a platform_get_irq call rather than eating it up by returning -EINVAL on all errors. * ad799x - typo fix in the copyright message. Either that or Michael was asserting a copyright that moved backwards in time by about a thousand years. * ad799x - use a regulator for vref rather than platform data. The driver dates from just as the regulator framework was coming into common use so provides an alternative way of specifying the reference voltage. We no longer need that approach so drop it in favour of a regulator only approach. * max1363 - some internal vref values were out by a small amount. The effect would have been tiny and no one noticed hence not pushing this through as a fix. * core - replace some pointless goto error_ret (with no clean up) lines with direct returns. This is my bad coding style so I'm glad to see it cleaned up. * core - avoid a kasprintf that just directly prints a string with no formatting elements. This has always been there but Lars just noticed it. Oops.
This commit is contained in:
commit
aa256f8d0d
|
@ -0,0 +1,22 @@
|
|||
Freescale vf610 Analog to Digital Converter bindings
|
||||
|
||||
The devicetree bindings are for the new ADC driver written for
|
||||
vf610/i.MX6slx and upward SoCs from Freescale.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should contain "fsl,vf610-adc"
|
||||
- reg: Offset and length of the register set for the device
|
||||
- interrupts: Should contain the interrupt for the device
|
||||
- clocks: The clock is needed by the ADC controller, ADC clock source is ipg clock.
|
||||
- clock-names: Must contain "adc", matching entry in the clocks property.
|
||||
- vref-supply: The regulator supply ADC refrence voltage.
|
||||
|
||||
Example:
|
||||
adc0: adc@4003b000 {
|
||||
compatible = "fsl,vf610-adc";
|
||||
reg = <0x4003b000 0x1000>;
|
||||
interrupts = <0 53 0x04>;
|
||||
clocks = <&clks VF610_CLK_ADC0>;
|
||||
clock-names = "adc";
|
||||
vref-supply = <®_vcc_3v3_mcu>;
|
||||
};
|
|
@ -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', \
|
||||
|
|
|
@ -197,6 +197,16 @@ config TWL6030_GPADC
|
|||
This driver can also be built as a module. If so, the module will be
|
||||
called twl6030-gpadc.
|
||||
|
||||
config VF610_ADC
|
||||
tristate "Freescale vf610 ADC driver"
|
||||
depends on OF
|
||||
help
|
||||
Say yes here to support for Vybrid board analog-to-digital converter.
|
||||
Since the IP is used for i.MX6SLX, the driver also support i.MX6SLX.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called vf610_adc.
|
||||
|
||||
config VIPERBOARD_ADC
|
||||
tristate "Viperboard ADC support"
|
||||
depends on MFD_VIPERBOARD && USB
|
||||
|
|
|
@ -21,4 +21,5 @@ obj-$(CONFIG_NAU7802) += nau7802.o
|
|||
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
|
||||
obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
|
||||
obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o
|
||||
obj-$(CONFIG_VF610_ADC) += vf610_adc.o
|
||||
obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o
|
||||
|
|
|
@ -1247,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,
|
||||
|
@ -1307,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,
|
||||
|
@ -1367,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,
|
||||
|
|
|
@ -0,0 +1,711 @@
|
|||
/*
|
||||
* Freescale Vybrid vf610 ADC driver
|
||||
*
|
||||
* Copyright 2013 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/driver.h>
|
||||
|
||||
/* This will be the driver name the kernel reports */
|
||||
#define DRIVER_NAME "vf610-adc"
|
||||
|
||||
/* Vybrid/IMX ADC registers */
|
||||
#define VF610_REG_ADC_HC0 0x00
|
||||
#define VF610_REG_ADC_HC1 0x04
|
||||
#define VF610_REG_ADC_HS 0x08
|
||||
#define VF610_REG_ADC_R0 0x0c
|
||||
#define VF610_REG_ADC_R1 0x10
|
||||
#define VF610_REG_ADC_CFG 0x14
|
||||
#define VF610_REG_ADC_GC 0x18
|
||||
#define VF610_REG_ADC_GS 0x1c
|
||||
#define VF610_REG_ADC_CV 0x20
|
||||
#define VF610_REG_ADC_OFS 0x24
|
||||
#define VF610_REG_ADC_CAL 0x28
|
||||
#define VF610_REG_ADC_PCTL 0x30
|
||||
|
||||
/* Configuration register field define */
|
||||
#define VF610_ADC_MODE_BIT8 0x00
|
||||
#define VF610_ADC_MODE_BIT10 0x04
|
||||
#define VF610_ADC_MODE_BIT12 0x08
|
||||
#define VF610_ADC_MODE_MASK 0x0c
|
||||
#define VF610_ADC_BUSCLK2_SEL 0x01
|
||||
#define VF610_ADC_ALTCLK_SEL 0x02
|
||||
#define VF610_ADC_ADACK_SEL 0x03
|
||||
#define VF610_ADC_ADCCLK_MASK 0x03
|
||||
#define VF610_ADC_CLK_DIV2 0x20
|
||||
#define VF610_ADC_CLK_DIV4 0x40
|
||||
#define VF610_ADC_CLK_DIV8 0x60
|
||||
#define VF610_ADC_CLK_MASK 0x60
|
||||
#define VF610_ADC_ADLSMP_LONG 0x10
|
||||
#define VF610_ADC_ADSTS_MASK 0x300
|
||||
#define VF610_ADC_ADLPC_EN 0x80
|
||||
#define VF610_ADC_ADHSC_EN 0x400
|
||||
#define VF610_ADC_REFSEL_VALT 0x100
|
||||
#define VF610_ADC_REFSEL_VBG 0x1000
|
||||
#define VF610_ADC_ADTRG_HARD 0x2000
|
||||
#define VF610_ADC_AVGS_8 0x4000
|
||||
#define VF610_ADC_AVGS_16 0x8000
|
||||
#define VF610_ADC_AVGS_32 0xC000
|
||||
#define VF610_ADC_AVGS_MASK 0xC000
|
||||
#define VF610_ADC_OVWREN 0x10000
|
||||
|
||||
/* General control register field define */
|
||||
#define VF610_ADC_ADACKEN 0x1
|
||||
#define VF610_ADC_DMAEN 0x2
|
||||
#define VF610_ADC_ACREN 0x4
|
||||
#define VF610_ADC_ACFGT 0x8
|
||||
#define VF610_ADC_ACFE 0x10
|
||||
#define VF610_ADC_AVGEN 0x20
|
||||
#define VF610_ADC_ADCON 0x40
|
||||
#define VF610_ADC_CAL 0x80
|
||||
|
||||
/* Other field define */
|
||||
#define VF610_ADC_ADCHC(x) ((x) & 0xF)
|
||||
#define VF610_ADC_AIEN (0x1 << 7)
|
||||
#define VF610_ADC_CONV_DISABLE 0x1F
|
||||
#define VF610_ADC_HS_COCO0 0x1
|
||||
#define VF610_ADC_CALF 0x2
|
||||
#define VF610_ADC_TIMEOUT msecs_to_jiffies(100)
|
||||
|
||||
enum clk_sel {
|
||||
VF610_ADCIOC_BUSCLK_SET,
|
||||
VF610_ADCIOC_ALTCLK_SET,
|
||||
VF610_ADCIOC_ADACK_SET,
|
||||
};
|
||||
|
||||
enum vol_ref {
|
||||
VF610_ADCIOC_VR_VREF_SET,
|
||||
VF610_ADCIOC_VR_VALT_SET,
|
||||
VF610_ADCIOC_VR_VBG_SET,
|
||||
};
|
||||
|
||||
enum average_sel {
|
||||
VF610_ADC_SAMPLE_1,
|
||||
VF610_ADC_SAMPLE_4,
|
||||
VF610_ADC_SAMPLE_8,
|
||||
VF610_ADC_SAMPLE_16,
|
||||
VF610_ADC_SAMPLE_32,
|
||||
};
|
||||
|
||||
struct vf610_adc_feature {
|
||||
enum clk_sel clk_sel;
|
||||
enum vol_ref vol_ref;
|
||||
|
||||
int clk_div;
|
||||
int sample_rate;
|
||||
int res_mode;
|
||||
|
||||
bool lpm;
|
||||
bool calibration;
|
||||
bool ovwren;
|
||||
};
|
||||
|
||||
struct vf610_adc {
|
||||
struct device *dev;
|
||||
void __iomem *regs;
|
||||
struct clk *clk;
|
||||
|
||||
u32 vref_uv;
|
||||
u32 value;
|
||||
struct regulator *vref;
|
||||
struct vf610_adc_feature adc_feature;
|
||||
|
||||
struct completion completion;
|
||||
};
|
||||
|
||||
#define VF610_ADC_CHAN(_idx, _chan_type) { \
|
||||
.type = (_chan_type), \
|
||||
.indexed = 1, \
|
||||
.channel = (_idx), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec vf610_adc_iio_channels[] = {
|
||||
VF610_ADC_CHAN(0, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(1, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(2, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(3, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(4, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(5, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(6, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(7, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(8, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(9, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(10, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(11, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(12, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(13, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(14, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(15, IIO_VOLTAGE),
|
||||
/* sentinel */
|
||||
};
|
||||
|
||||
/*
|
||||
* ADC sample frequency, unit is ADCK cycles.
|
||||
* ADC clk source is ipg clock, which is the same as bus clock.
|
||||
*
|
||||
* ADC conversion time = SFCAdder + AverageNum x (BCT + LSTAdder)
|
||||
* SFCAdder: fixed to 6 ADCK cycles
|
||||
* AverageNum: 1, 4, 8, 16, 32 samples for hardware average.
|
||||
* BCT (Base Conversion Time): fixed to 25 ADCK cycles for 12 bit mode
|
||||
* LSTAdder(Long Sample Time): fixed to 3 ADCK cycles
|
||||
*
|
||||
* By default, enable 12 bit resolution mode, clock source
|
||||
* set to ipg clock, So get below frequency group:
|
||||
*/
|
||||
static const u32 vf610_sample_freq_avail[5] =
|
||||
{1941176, 559332, 286957, 145374, 73171};
|
||||
|
||||
static inline void vf610_adc_cfg_init(struct vf610_adc *info)
|
||||
{
|
||||
/* set default Configuration for ADC controller */
|
||||
info->adc_feature.clk_sel = VF610_ADCIOC_BUSCLK_SET;
|
||||
info->adc_feature.vol_ref = VF610_ADCIOC_VR_VREF_SET;
|
||||
|
||||
info->adc_feature.calibration = true;
|
||||
info->adc_feature.ovwren = true;
|
||||
|
||||
info->adc_feature.clk_div = 1;
|
||||
info->adc_feature.res_mode = 12;
|
||||
info->adc_feature.sample_rate = 1;
|
||||
info->adc_feature.lpm = true;
|
||||
}
|
||||
|
||||
static void vf610_adc_cfg_post_set(struct vf610_adc *info)
|
||||
{
|
||||
struct vf610_adc_feature *adc_feature = &info->adc_feature;
|
||||
int cfg_data = 0;
|
||||
int gc_data = 0;
|
||||
|
||||
switch (adc_feature->clk_sel) {
|
||||
case VF610_ADCIOC_ALTCLK_SET:
|
||||
cfg_data |= VF610_ADC_ALTCLK_SEL;
|
||||
break;
|
||||
case VF610_ADCIOC_ADACK_SET:
|
||||
cfg_data |= VF610_ADC_ADACK_SEL;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* low power set for calibration */
|
||||
cfg_data |= VF610_ADC_ADLPC_EN;
|
||||
|
||||
/* enable high speed for calibration */
|
||||
cfg_data |= VF610_ADC_ADHSC_EN;
|
||||
|
||||
/* voltage reference */
|
||||
switch (adc_feature->vol_ref) {
|
||||
case VF610_ADCIOC_VR_VREF_SET:
|
||||
break;
|
||||
case VF610_ADCIOC_VR_VALT_SET:
|
||||
cfg_data |= VF610_ADC_REFSEL_VALT;
|
||||
break;
|
||||
case VF610_ADCIOC_VR_VBG_SET:
|
||||
cfg_data |= VF610_ADC_REFSEL_VBG;
|
||||
break;
|
||||
default:
|
||||
dev_err(info->dev, "error voltage reference\n");
|
||||
}
|
||||
|
||||
/* data overwrite enable */
|
||||
if (adc_feature->ovwren)
|
||||
cfg_data |= VF610_ADC_OVWREN;
|
||||
|
||||
writel(cfg_data, info->regs + VF610_REG_ADC_CFG);
|
||||
writel(gc_data, info->regs + VF610_REG_ADC_GC);
|
||||
}
|
||||
|
||||
static void vf610_adc_calibration(struct vf610_adc *info)
|
||||
{
|
||||
int adc_gc, hc_cfg;
|
||||
int timeout;
|
||||
|
||||
if (!info->adc_feature.calibration)
|
||||
return;
|
||||
|
||||
/* enable calibration interrupt */
|
||||
hc_cfg = VF610_ADC_AIEN | VF610_ADC_CONV_DISABLE;
|
||||
writel(hc_cfg, info->regs + VF610_REG_ADC_HC0);
|
||||
|
||||
adc_gc = readl(info->regs + VF610_REG_ADC_GC);
|
||||
writel(adc_gc | VF610_ADC_CAL, info->regs + VF610_REG_ADC_GC);
|
||||
|
||||
timeout = wait_for_completion_timeout
|
||||
(&info->completion, VF610_ADC_TIMEOUT);
|
||||
if (timeout == 0)
|
||||
dev_err(info->dev, "Timeout for adc calibration\n");
|
||||
|
||||
adc_gc = readl(info->regs + VF610_REG_ADC_GS);
|
||||
if (adc_gc & VF610_ADC_CALF)
|
||||
dev_err(info->dev, "ADC calibration failed\n");
|
||||
|
||||
info->adc_feature.calibration = false;
|
||||
}
|
||||
|
||||
static void vf610_adc_cfg_set(struct vf610_adc *info)
|
||||
{
|
||||
struct vf610_adc_feature *adc_feature = &(info->adc_feature);
|
||||
int cfg_data;
|
||||
|
||||
cfg_data = readl(info->regs + VF610_REG_ADC_CFG);
|
||||
|
||||
/* low power configuration */
|
||||
cfg_data &= ~VF610_ADC_ADLPC_EN;
|
||||
if (adc_feature->lpm)
|
||||
cfg_data |= VF610_ADC_ADLPC_EN;
|
||||
|
||||
/* disable high speed */
|
||||
cfg_data &= ~VF610_ADC_ADHSC_EN;
|
||||
|
||||
writel(cfg_data, info->regs + VF610_REG_ADC_CFG);
|
||||
}
|
||||
|
||||
static void vf610_adc_sample_set(struct vf610_adc *info)
|
||||
{
|
||||
struct vf610_adc_feature *adc_feature = &(info->adc_feature);
|
||||
int cfg_data, gc_data;
|
||||
|
||||
cfg_data = readl(info->regs + VF610_REG_ADC_CFG);
|
||||
gc_data = readl(info->regs + VF610_REG_ADC_GC);
|
||||
|
||||
/* resolution mode */
|
||||
cfg_data &= ~VF610_ADC_MODE_MASK;
|
||||
switch (adc_feature->res_mode) {
|
||||
case 8:
|
||||
cfg_data |= VF610_ADC_MODE_BIT8;
|
||||
break;
|
||||
case 10:
|
||||
cfg_data |= VF610_ADC_MODE_BIT10;
|
||||
break;
|
||||
case 12:
|
||||
cfg_data |= VF610_ADC_MODE_BIT12;
|
||||
break;
|
||||
default:
|
||||
dev_err(info->dev, "error resolution mode\n");
|
||||
break;
|
||||
}
|
||||
|
||||
/* clock select and clock divider */
|
||||
cfg_data &= ~(VF610_ADC_CLK_MASK | VF610_ADC_ADCCLK_MASK);
|
||||
switch (adc_feature->clk_div) {
|
||||
case 1:
|
||||
break;
|
||||
case 2:
|
||||
cfg_data |= VF610_ADC_CLK_DIV2;
|
||||
break;
|
||||
case 4:
|
||||
cfg_data |= VF610_ADC_CLK_DIV4;
|
||||
break;
|
||||
case 8:
|
||||
cfg_data |= VF610_ADC_CLK_DIV8;
|
||||
break;
|
||||
case 16:
|
||||
switch (adc_feature->clk_sel) {
|
||||
case VF610_ADCIOC_BUSCLK_SET:
|
||||
cfg_data |= VF610_ADC_BUSCLK2_SEL | VF610_ADC_CLK_DIV8;
|
||||
break;
|
||||
default:
|
||||
dev_err(info->dev, "error clk divider\n");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Use the short sample mode */
|
||||
cfg_data &= ~(VF610_ADC_ADLSMP_LONG | VF610_ADC_ADSTS_MASK);
|
||||
|
||||
/* update hardware average selection */
|
||||
cfg_data &= ~VF610_ADC_AVGS_MASK;
|
||||
gc_data &= ~VF610_ADC_AVGEN;
|
||||
switch (adc_feature->sample_rate) {
|
||||
case VF610_ADC_SAMPLE_1:
|
||||
break;
|
||||
case VF610_ADC_SAMPLE_4:
|
||||
gc_data |= VF610_ADC_AVGEN;
|
||||
break;
|
||||
case VF610_ADC_SAMPLE_8:
|
||||
gc_data |= VF610_ADC_AVGEN;
|
||||
cfg_data |= VF610_ADC_AVGS_8;
|
||||
break;
|
||||
case VF610_ADC_SAMPLE_16:
|
||||
gc_data |= VF610_ADC_AVGEN;
|
||||
cfg_data |= VF610_ADC_AVGS_16;
|
||||
break;
|
||||
case VF610_ADC_SAMPLE_32:
|
||||
gc_data |= VF610_ADC_AVGEN;
|
||||
cfg_data |= VF610_ADC_AVGS_32;
|
||||
break;
|
||||
default:
|
||||
dev_err(info->dev,
|
||||
"error hardware sample average select\n");
|
||||
}
|
||||
|
||||
writel(cfg_data, info->regs + VF610_REG_ADC_CFG);
|
||||
writel(gc_data, info->regs + VF610_REG_ADC_GC);
|
||||
}
|
||||
|
||||
static void vf610_adc_hw_init(struct vf610_adc *info)
|
||||
{
|
||||
/* CFG: Feature set */
|
||||
vf610_adc_cfg_post_set(info);
|
||||
vf610_adc_sample_set(info);
|
||||
|
||||
/* adc calibration */
|
||||
vf610_adc_calibration(info);
|
||||
|
||||
/* CFG: power and speed set */
|
||||
vf610_adc_cfg_set(info);
|
||||
}
|
||||
|
||||
static int vf610_adc_read_data(struct vf610_adc *info)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = readl(info->regs + VF610_REG_ADC_R0);
|
||||
|
||||
switch (info->adc_feature.res_mode) {
|
||||
case 8:
|
||||
result &= 0xFF;
|
||||
break;
|
||||
case 10:
|
||||
result &= 0x3FF;
|
||||
break;
|
||||
case 12:
|
||||
result &= 0xFFF;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static irqreturn_t vf610_adc_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct vf610_adc *info = (struct vf610_adc *)dev_id;
|
||||
int coco;
|
||||
|
||||
coco = readl(info->regs + VF610_REG_ADC_HS);
|
||||
if (coco & VF610_ADC_HS_COCO0) {
|
||||
info->value = vf610_adc_read_data(info);
|
||||
complete(&info->completion);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("1941176, 559332, 286957, 145374, 73171");
|
||||
|
||||
static struct attribute *vf610_attributes[] = {
|
||||
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group vf610_attribute_group = {
|
||||
.attrs = vf610_attributes,
|
||||
};
|
||||
|
||||
static int vf610_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct vf610_adc *info = iio_priv(indio_dev);
|
||||
unsigned int hc_cfg;
|
||||
unsigned long ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
reinit_completion(&info->completion);
|
||||
|
||||
hc_cfg = VF610_ADC_ADCHC(chan->channel);
|
||||
hc_cfg |= VF610_ADC_AIEN;
|
||||
writel(hc_cfg, info->regs + VF610_REG_ADC_HC0);
|
||||
ret = wait_for_completion_interruptible_timeout
|
||||
(&info->completion, VF610_ADC_TIMEOUT);
|
||||
if (ret == 0) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*val = info->value;
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = info->vref_uv / 1000;
|
||||
*val2 = info->adc_feature.res_mode;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val = vf610_sample_freq_avail[info->adc_feature.sample_rate];
|
||||
*val2 = 0;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int vf610_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct vf610_adc *info = iio_priv(indio_dev);
|
||||
int i;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
for (i = 0;
|
||||
i < ARRAY_SIZE(vf610_sample_freq_avail);
|
||||
i++)
|
||||
if (val == vf610_sample_freq_avail[i]) {
|
||||
info->adc_feature.sample_rate = i;
|
||||
vf610_adc_sample_set(info);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int vf610_adc_reg_access(struct iio_dev *indio_dev,
|
||||
unsigned reg, unsigned writeval,
|
||||
unsigned *readval)
|
||||
{
|
||||
struct vf610_adc *info = iio_priv(indio_dev);
|
||||
|
||||
if ((readval == NULL) ||
|
||||
(!(reg % 4) || (reg > VF610_REG_ADC_PCTL)))
|
||||
return -EINVAL;
|
||||
|
||||
*readval = readl(info->regs + reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_info vf610_adc_iio_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &vf610_read_raw,
|
||||
.write_raw = &vf610_write_raw,
|
||||
.debugfs_reg_access = &vf610_adc_reg_access,
|
||||
.attrs = &vf610_attribute_group,
|
||||
};
|
||||
|
||||
static const struct of_device_id vf610_adc_match[] = {
|
||||
{ .compatible = "fsl,vf610-adc", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, vf610_adc_match);
|
||||
|
||||
static int vf610_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct vf610_adc *info;
|
||||
struct iio_dev *indio_dev;
|
||||
struct resource *mem;
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct vf610_adc));
|
||||
if (!indio_dev) {
|
||||
dev_err(&pdev->dev, "Failed allocating iio device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
info = iio_priv(indio_dev);
|
||||
info->dev = &pdev->dev;
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
info->regs = devm_ioremap_resource(&pdev->dev, mem);
|
||||
if (IS_ERR(info->regs))
|
||||
return PTR_ERR(info->regs);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq <= 0) {
|
||||
dev_err(&pdev->dev, "no irq resource?\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(info->dev, irq,
|
||||
vf610_adc_isr, 0,
|
||||
dev_name(&pdev->dev), info);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed requesting irq, irq = %d\n", irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
info->clk = devm_clk_get(&pdev->dev, "adc");
|
||||
if (IS_ERR(info->clk)) {
|
||||
dev_err(&pdev->dev, "failed getting clock, err = %ld\n",
|
||||
PTR_ERR(info->clk));
|
||||
ret = PTR_ERR(info->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
info->vref = devm_regulator_get(&pdev->dev, "vref");
|
||||
if (IS_ERR(info->vref))
|
||||
return PTR_ERR(info->vref);
|
||||
|
||||
ret = regulator_enable(info->vref);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
info->vref_uv = regulator_get_voltage(info->vref);
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
init_completion(&info->completion);
|
||||
|
||||
indio_dev->name = dev_name(&pdev->dev);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->dev.of_node = pdev->dev.of_node;
|
||||
indio_dev->info = &vf610_adc_iio_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = vf610_adc_iio_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(vf610_adc_iio_channels);
|
||||
|
||||
ret = clk_prepare_enable(info->clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"Could not prepare or enable the clock.\n");
|
||||
goto error_adc_clk_enable;
|
||||
}
|
||||
|
||||
vf610_adc_cfg_init(info);
|
||||
vf610_adc_hw_init(info);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Couldn't register the device.\n");
|
||||
goto error_iio_device_register;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
|
||||
error_iio_device_register:
|
||||
clk_disable_unprepare(info->clk);
|
||||
error_adc_clk_enable:
|
||||
regulator_disable(info->vref);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vf610_adc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct vf610_adc *info = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
regulator_disable(info->vref);
|
||||
clk_disable_unprepare(info->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int vf610_adc_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct vf610_adc *info = iio_priv(indio_dev);
|
||||
int hc_cfg;
|
||||
|
||||
/* ADC controller enters to stop mode */
|
||||
hc_cfg = readl(info->regs + VF610_REG_ADC_HC0);
|
||||
hc_cfg |= VF610_ADC_CONV_DISABLE;
|
||||
writel(hc_cfg, info->regs + VF610_REG_ADC_HC0);
|
||||
|
||||
clk_disable_unprepare(info->clk);
|
||||
regulator_disable(info->vref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vf610_adc_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct vf610_adc *info = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = regulator_enable(info->vref);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(info->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
vf610_adc_hw_init(info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(vf610_adc_pm_ops,
|
||||
vf610_adc_suspend,
|
||||
vf610_adc_resume);
|
||||
|
||||
static struct platform_driver vf610_adc_driver = {
|
||||
.probe = vf610_adc_probe,
|
||||
.remove = vf610_adc_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = vf610_adc_match,
|
||||
.pm = &vf610_adc_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(vf610_adc_driver);
|
||||
|
||||
MODULE_AUTHOR("Fugang Duan <B38611@freescale.com>");
|
||||
MODULE_DESCRIPTION("Freescale VF610 ADC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -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 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
|
|||
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);
|
||||
|
|
|
@ -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 @@ static int __iio_update_buffers(struct iio_dev *indio_dev,
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 @@ int __iio_add_chan_devattr(const char *postfix,
|
|||
__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 @@ int iio_device_register(struct iio_dev *indio_dev)
|
|||
iio_device_unregister_sysfs(indio_dev);
|
||||
error_unreg_debugfs:
|
||||
iio_device_unregister_debugfs(indio_dev);
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(iio_device_register);
|
||||
|
|
|
@ -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;
|
||||
|
||||
return ret ? ret : copied;
|
||||
/*
|
||||
* 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;
|
||||
|
||||
} 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -9,6 +9,7 @@ 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
|
||||
|
|
|
@ -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");
|
|
@ -183,8 +183,17 @@ static int mag3110_read_raw(struct iio_dev *indio_dev,
|
|||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
*val2 = 1000;
|
||||
switch (chan->type) {
|
||||
case IIO_MAGN:
|
||||
*val = 0;
|
||||
*val2 = 1000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_TEMP:
|
||||
*val = 1000;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
i = data->ctrl_reg1 >> MAG3110_CTRL_DR_SHIFT;
|
||||
|
@ -270,7 +279,8 @@ static const struct iio_chan_spec mag3110_channels[] = {
|
|||
MAG3110_CHANNEL(Z, 2),
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.scan_index = 3,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
|
@ -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"
|
||||
|
||||
/**
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 },
|
||||
{},
|
||||
};
|
||||
|
|
|
@ -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 },
|
||||
{},
|
||||
};
|
||||
|
|
|
@ -652,3 +652,25 @@ int read_sysfs_float(char *filename, char *basedir, float *val)
|
|||
free(temp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
read_sysfs_string(const char *filename, const char *basedir, char *str)
|
||||
{
|
||||
float ret = 0;
|
||||
FILE *sysfsfp;
|
||||
char *temp = malloc(strlen(basedir) + strlen(filename) + 2);
|
||||
if (temp == NULL) {
|
||||
printf("Memory allocation failed");
|
||||
return -ENOMEM;
|
||||
}
|
||||
sprintf(temp, "%s/%s", basedir, filename);
|
||||
sysfsfp = fopen(temp, "r");
|
||||
if (sysfsfp == NULL) {
|
||||
ret = -errno;
|
||||
goto error_free;
|
||||
}
|
||||
fscanf(sysfsfp, "%s\n", str);
|
||||
fclose(sysfsfp);
|
||||
error_free:
|
||||
free(temp);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* Industrial I/O utilities - lsiio.c
|
||||
*
|
||||
* Copyright (c) 2010 Manuel Stahl <manuel.stahl@iis.fraunhofer.de>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/dir.h>
|
||||
#include "iio_utils.h"
|
||||
|
||||
|
||||
static enum verbosity {
|
||||
VERBLEVEL_DEFAULT, /* 0 gives lspci behaviour */
|
||||
VERBLEVEL_SENSORS, /* 1 lists sensors */
|
||||
} verblevel = VERBLEVEL_DEFAULT;
|
||||
|
||||
const char *type_device = "iio:device";
|
||||
const char *type_trigger = "trigger";
|
||||
|
||||
|
||||
static inline int check_prefix(const char *str, const char *prefix)
|
||||
{
|
||||
return strlen(str) > strlen(prefix) &&
|
||||
strncmp(str, prefix, strlen(prefix)) == 0;
|
||||
}
|
||||
|
||||
static inline int check_postfix(const char *str, const char *postfix)
|
||||
{
|
||||
return strlen(str) > strlen(postfix) &&
|
||||
strcmp(str + strlen(str) - strlen(postfix), postfix) == 0;
|
||||
}
|
||||
|
||||
static int dump_channels(const char *dev_dir_name)
|
||||
{
|
||||
DIR *dp;
|
||||
const struct dirent *ent;
|
||||
dp = opendir(dev_dir_name);
|
||||
if (dp == NULL)
|
||||
return -errno;
|
||||
while (ent = readdir(dp), ent != NULL)
|
||||
if (check_prefix(ent->d_name, "in_") &&
|
||||
check_postfix(ent->d_name, "_raw")) {
|
||||
printf(" %-10s\n", ent->d_name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dump_one_device(const char *dev_dir_name)
|
||||
{
|
||||
char name[IIO_MAX_NAME_LENGTH];
|
||||
int dev_idx;
|
||||
|
||||
sscanf(dev_dir_name + strlen(iio_dir) + strlen(type_device),
|
||||
"%i", &dev_idx);
|
||||
read_sysfs_string("name", dev_dir_name, name);
|
||||
printf("Device %03d: %s\n", dev_idx, name);
|
||||
|
||||
if (verblevel >= VERBLEVEL_SENSORS) {
|
||||
int ret = dump_channels(dev_dir_name);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dump_one_trigger(const char *dev_dir_name)
|
||||
{
|
||||
char name[IIO_MAX_NAME_LENGTH];
|
||||
int dev_idx;
|
||||
|
||||
sscanf(dev_dir_name + strlen(iio_dir) + strlen(type_trigger),
|
||||
"%i", &dev_idx);
|
||||
read_sysfs_string("name", dev_dir_name, name);
|
||||
printf("Trigger %03d: %s\n", dev_idx, name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dump_devices(void)
|
||||
{
|
||||
const struct dirent *ent;
|
||||
int number, numstrlen;
|
||||
|
||||
FILE *nameFile;
|
||||
DIR *dp;
|
||||
char thisname[IIO_MAX_NAME_LENGTH];
|
||||
char *filename;
|
||||
|
||||
dp = opendir(iio_dir);
|
||||
if (dp == NULL) {
|
||||
printf("No industrial I/O devices available\n");
|
||||
return;
|
||||
}
|
||||
|
||||
while (ent = readdir(dp), ent != NULL) {
|
||||
if (check_prefix(ent->d_name, type_device)) {
|
||||
char *dev_dir_name;
|
||||
asprintf(&dev_dir_name, "%s%s", iio_dir, ent->d_name);
|
||||
dump_one_device(dev_dir_name);
|
||||
free(dev_dir_name);
|
||||
if (verblevel >= VERBLEVEL_SENSORS)
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
rewinddir(dp);
|
||||
while (ent = readdir(dp), ent != NULL) {
|
||||
if (check_prefix(ent->d_name, type_trigger)) {
|
||||
char *dev_dir_name;
|
||||
asprintf(&dev_dir_name, "%s%s", iio_dir, ent->d_name);
|
||||
dump_one_trigger(dev_dir_name);
|
||||
free(dev_dir_name);
|
||||
}
|
||||
}
|
||||
closedir(dp);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int c, err = 0;
|
||||
|
||||
while ((c = getopt(argc, argv, "d:D:v")) != EOF) {
|
||||
switch (c) {
|
||||
case 'v':
|
||||
verblevel++;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
default:
|
||||
err++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (err || argc > optind) {
|
||||
fprintf(stderr, "Usage: lsiio [options]...\n"
|
||||
"List industrial I/O devices\n"
|
||||
" -v, --verbose\n"
|
||||
" Increase verbosity (may be given multiple times)\n"
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
dump_devices();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -95,7 +95,7 @@ struct ad799x_state {
|
|||
struct i2c_client *client;
|
||||
const struct ad799x_chip_info *chip_info;
|
||||
struct regulator *reg;
|
||||
u16 int_vref_mv;
|
||||
struct regulator *vref;
|
||||
unsigned id;
|
||||
u16 config;
|
||||
|
||||
|
@ -103,14 +103,6 @@ struct ad799x_state {
|
|||
unsigned int transfer_size;
|
||||
};
|
||||
|
||||
/*
|
||||
* TODO: struct ad799x_platform_data needs to go into include/linux/iio
|
||||
*/
|
||||
|
||||
struct ad799x_platform_data {
|
||||
u16 vref_mv;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_AD799X_RING_BUFFER
|
||||
int ad799x_register_ring_funcs_and_init(struct iio_dev *indio_dev);
|
||||
void ad799x_ring_cleanup(struct iio_dev *indio_dev);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* iio/adc/ad799x.c
|
||||
* Copyright (C) 2010-1011 Michael Hennerich, Analog Devices Inc.
|
||||
* Copyright (C) 2010-2011 Michael Hennerich, Analog Devices Inc.
|
||||
*
|
||||
* based on iio/adc/max1363
|
||||
* Copyright (C) 2008-2010 Jonathan Cameron
|
||||
|
@ -179,7 +179,10 @@ static int ad799x_read_raw(struct iio_dev *indio_dev,
|
|||
RES_MASK(chan->scan_type.realbits);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = st->int_vref_mv;
|
||||
ret = regulator_get_voltage(st->vref);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret / 1000;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
|
@ -533,7 +536,6 @@ static int ad799x_probe(struct i2c_client *client,
|
|||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct ad799x_platform_data *pdata = client->dev.platform_data;
|
||||
struct ad799x_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
|
||||
|
@ -551,17 +553,21 @@ static int ad799x_probe(struct i2c_client *client,
|
|||
|
||||
/* TODO: Add pdata options for filtering and bit delay */
|
||||
|
||||
if (!pdata)
|
||||
return -EINVAL;
|
||||
|
||||
st->int_vref_mv = pdata->vref_mv;
|
||||
|
||||
st->reg = devm_regulator_get(&client->dev, "vcc");
|
||||
if (!IS_ERR(st->reg)) {
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (IS_ERR(st->reg))
|
||||
return PTR_ERR(st->reg);
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
st->vref = devm_regulator_get(&client->dev, "vref");
|
||||
if (IS_ERR(st->vref)) {
|
||||
ret = PTR_ERR(st->vref);
|
||||
goto error_disable_reg;
|
||||
}
|
||||
ret = regulator_enable(st->vref);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
st->client = client;
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
|
@ -577,28 +583,28 @@ static int ad799x_probe(struct i2c_client *client,
|
|||
goto error_disable_reg;
|
||||
|
||||
if (client->irq > 0) {
|
||||
ret = request_threaded_irq(client->irq,
|
||||
NULL,
|
||||
ad799x_event_handler,
|
||||
IRQF_TRIGGER_FALLING |
|
||||
IRQF_ONESHOT,
|
||||
client->name,
|
||||
indio_dev);
|
||||
ret = devm_request_threaded_irq(&client->dev,
|
||||
client->irq,
|
||||
NULL,
|
||||
ad799x_event_handler,
|
||||
IRQF_TRIGGER_FALLING |
|
||||
IRQF_ONESHOT,
|
||||
client->name,
|
||||
indio_dev);
|
||||
if (ret)
|
||||
goto error_cleanup_ring;
|
||||
}
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_free_irq;
|
||||
goto error_cleanup_ring;
|
||||
|
||||
return 0;
|
||||
|
||||
error_free_irq:
|
||||
if (client->irq > 0)
|
||||
free_irq(client->irq, indio_dev);
|
||||
error_cleanup_ring:
|
||||
ad799x_ring_cleanup(indio_dev);
|
||||
error_disable_reg:
|
||||
if (!IS_ERR(st->vref))
|
||||
regulator_disable(st->vref);
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
|
||||
|
@ -611,10 +617,10 @@ static int ad799x_remove(struct i2c_client *client)
|
|||
struct ad799x_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
if (client->irq > 0)
|
||||
free_irq(client->irq, indio_dev);
|
||||
|
||||
ad799x_ring_cleanup(indio_dev);
|
||||
if (!IS_ERR(st->vref))
|
||||
regulator_disable(st->vref);
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
kfree(st->rx_buf);
|
||||
|
|
|
@ -1558,7 +1558,7 @@ static int mxs_lradc_probe(struct platform_device *pdev)
|
|||
for (i = 0; i < of_cfg->irq_count; i++) {
|
||||
lradc->irq[i] = platform_get_irq(pdev, i);
|
||||
if (lradc->irq[i] < 0)
|
||||
return -EINVAL;
|
||||
return lradc->irq[i];
|
||||
|
||||
ret = devm_request_irq(dev, lradc->irq[i],
|
||||
mxs_lradc_handle_irq, 0,
|
||||
|
|
|
@ -33,6 +33,16 @@
|
|||
#define HID_USAGE_SENSOR_DATA_LIGHT 0x2004d0
|
||||
#define HID_USAGE_SENSOR_LIGHT_ILLUM 0x2004d1
|
||||
|
||||
/* PROX (200011) */
|
||||
#define HID_USAGE_SENSOR_PROX 0x200011
|
||||
#define HID_USAGE_SENSOR_DATA_PRESENCE 0x2004b0
|
||||
#define HID_USAGE_SENSOR_HUMAN_PRESENCE 0x2004b1
|
||||
|
||||
/* Pressure (200031) */
|
||||
#define HID_USAGE_SENSOR_PRESSURE 0x200031
|
||||
#define HID_USAGE_SENSOR_DATA_ATMOSPHERIC_PRESSURE 0x200430
|
||||
#define HID_USAGE_SENSOR_ATMOSPHERIC_PRESSURE 0x200431
|
||||
|
||||
/* Gyro 3D: (200076) */
|
||||
#define HID_USAGE_SENSOR_GYRO_3D 0x200076
|
||||
#define HID_USAGE_SENSOR_DATA_ANGL_VELOCITY 0x200456
|
||||
|
|
|
@ -254,12 +254,16 @@ static inline bool iio_channel_has_info(const struct iio_chan_spec *chan,
|
|||
(chan->info_mask_shared_by_all & BIT(type));
|
||||
}
|
||||
|
||||
#define IIO_ST(si, rb, sb, sh) \
|
||||
{ .sign = si, .realbits = rb, .storagebits = sb, .shift = sh }
|
||||
|
||||
#define IIO_CHAN_SOFT_TIMESTAMP(_si) \
|
||||
{ .type = IIO_TIMESTAMP, .channel = -1, \
|
||||
.scan_index = _si, .scan_type = IIO_ST('s', 64, 64, 0) }
|
||||
#define IIO_CHAN_SOFT_TIMESTAMP(_si) { \
|
||||
.type = IIO_TIMESTAMP, \
|
||||
.channel = -1, \
|
||||
.scan_index = _si, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 64, \
|
||||
.storagebits = 64, \
|
||||
}, \
|
||||
}
|
||||
|
||||
/**
|
||||
* iio_get_time_ns() - utility function to get a time stamp for events etc
|
||||
|
|
Loading…
Reference in New Issue