Staging driver update for 3.13-rc1
Here's the big drivers/staging/ update for 3.13-rc1. Nothing major here, just a _ton_ of fixes and cleanups, mostly driven by the new round of OPW applicants, but also there are lots of other people doing staging tree cleanups these days in order to help get the drivers into mergable shape. We also merge, and then revert, the ktap code, as Ingo and the other perf/ftrace developers feel it should go into the "real" part of the kernel with only a bit more work, so no need to put it in staging for now. All of this has been in linux-next for a while with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.19 (GNU/Linux) iEYEABECAAYFAlJ6xPsACgkQMUfUDdst+ykAbwCg1hOktgHPFZp/t6xmsSj6cZHj AfQAnRN/lr/TFw5SKUek2sluAzO4Fz7c =g/MD -----END PGP SIGNATURE----- Merge tag 'staging-3.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging Pull staging driver update from Greg KH: "Here's the big drivers/staging/ update for 3.13-rc1. Nothing major here, just a _ton_ of fixes and cleanups, mostly driven by the new round of OPW applicants, but also there are lots of other people doing staging tree cleanups these days in order to help get the drivers into mergable shape. We also merge, and then revert, the ktap code, as Ingo and the other perf/ftrace developers feel it should go into the "real" part of the kernel with only a bit more work, so no need to put it in staging for now. All of this has been in linux-next for a while with no reported issues" * tag 'staging-3.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (1045 commits) staging: drm/imx: fix return value check in ipu_add_subdevice_pdata() Staging: zram: Fix access of NULL pointer Staging: zram: Fix variable dereferenced before check Staging: rtl8187se: space prohibited before semicolon in r8185b_init.c Staging: rtl8187se: fix space prohibited after that open parenthesis '(' in r8185b_init.c Staging: rtl8187se: fix braces {} are not necessary for single statement blocks in r8185b_init.c Staging: rtl8187se: fix trailing whitespace in r8185b_init.c Staging: rtl8187se: fix please, no space before tabs in r8185b_init.c drivers/staging/nvec/Kconfig: remove trailing whitespace Staging: dwc2: Fix variable dereferenced before check Staging: xgifb: fix braces {} are not necessary for any arm of this statement staging: rtl8192e: remove unneeded semicolons staging: rtl8192e: use true and false for bool variables staging: ft1000: return values corrected in scram_start_dwnld staging: ft1000: change values of status return variable in write_dpram32_and_check staging: bcm: Remove unnecessary pointer casting imx-drm: ipuv3-crtc: Invert IPU DI0 clock polarity staging: r8188eu: Fix sparse warnings in rtl_p2p.c staging: r8188eu: Fix sparse warnings in rtw_mlme_ext.c staging: r8188eu: Fix sparse warnings in rtl8188e.cmd.c ...
This commit is contained in:
commit
0b1e73ed22
|
@ -79,7 +79,7 @@ Description:
|
|||
correspond to externally available input one of the named
|
||||
versions may be used. The number must always be specified and
|
||||
unique to allow association with event codes. Units after
|
||||
application of scale and offset are microvolts.
|
||||
application of scale and offset are millivolts.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY-voltageZ_raw
|
||||
KernelVersion: 2.6.35
|
||||
|
@ -90,7 +90,7 @@ Description:
|
|||
physically equivalent inputs when non differential readings are
|
||||
separately available. In differential only parts, then all that
|
||||
is required is a consistent labeling. Units after application
|
||||
of scale and offset are microvolts.
|
||||
of scale and offset are millivolts.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_capacitanceY_raw
|
||||
KernelVersion: 3.2
|
||||
|
@ -537,6 +537,62 @@ Description:
|
|||
value is in raw device units or in processed units (as _raw
|
||||
and _input do on sysfs direct channel read attributes).
|
||||
|
||||
What: /sys/.../events/in_accel_x_thresh_rising_hysteresis
|
||||
What: /sys/.../events/in_accel_x_thresh_falling_hysteresis
|
||||
What: /sys/.../events/in_accel_x_thresh_either_hysteresis
|
||||
What: /sys/.../events/in_accel_y_thresh_rising_hysteresis
|
||||
What: /sys/.../events/in_accel_y_thresh_falling_hysteresis
|
||||
What: /sys/.../events/in_accel_y_thresh_either_hysteresis
|
||||
What: /sys/.../events/in_accel_z_thresh_rising_hysteresis
|
||||
What: /sys/.../events/in_accel_z_thresh_falling_hysteresis
|
||||
What: /sys/.../events/in_accel_z_thresh_either_hysteresis
|
||||
What: /sys/.../events/in_anglvel_x_thresh_rising_hysteresis
|
||||
What: /sys/.../events/in_anglvel_x_thresh_falling_hysteresis
|
||||
What: /sys/.../events/in_anglvel_x_thresh_either_hysteresis
|
||||
What: /sys/.../events/in_anglvel_y_thresh_rising_hysteresis
|
||||
What: /sys/.../events/in_anglvel_y_thresh_falling_hysteresis
|
||||
What: /sys/.../events/in_anglvel_y_thresh_either_hysteresis
|
||||
What: /sys/.../events/in_anglvel_z_thresh_rising_hysteresis
|
||||
What: /sys/.../events/in_anglvel_z_thresh_falling_hysteresis
|
||||
What: /sys/.../events/in_anglvel_z_thresh_either_hysteresis
|
||||
What: /sys/.../events/in_magn_x_thresh_rising_hysteresis
|
||||
What: /sys/.../events/in_magn_x_thresh_falling_hysteresis
|
||||
What: /sys/.../events/in_magn_x_thresh_either_hysteresis
|
||||
What: /sys/.../events/in_magn_y_thresh_rising_hysteresis
|
||||
What: /sys/.../events/in_magn_y_thresh_falling_hysteresis
|
||||
What: /sys/.../events/in_magn_y_thresh_either_hysteresis
|
||||
What: /sys/.../events/in_magn_z_thresh_rising_hysteresis
|
||||
What: /sys/.../events/in_magn_z_thresh_falling_hysteresis
|
||||
What: /sys/.../events/in_magn_z_thresh_either_hysteresis
|
||||
What: /sys/.../events/in_voltageY_thresh_rising_hysteresis
|
||||
What: /sys/.../events/in_voltageY_thresh_falling_hysteresis
|
||||
What: /sys/.../events/in_voltageY_thresh_either_hysteresis
|
||||
What: /sys/.../events/in_tempY_thresh_rising_hysteresis
|
||||
What: /sys/.../events/in_tempY_thresh_falling_hysteresis
|
||||
What: /sys/.../events/in_tempY_thresh_either_hysteresis
|
||||
What: /sys/.../events/in_illuminance0_thresh_falling_hysteresis
|
||||
what: /sys/.../events/in_illuminance0_thresh_rising_hysteresis
|
||||
what: /sys/.../events/in_illuminance0_thresh_either_hysteresis
|
||||
what: /sys/.../events/in_proximity0_thresh_falling_hysteresis
|
||||
what: /sys/.../events/in_proximity0_thresh_rising_hysteresis
|
||||
what: /sys/.../events/in_proximity0_thresh_either_hysteresis
|
||||
KernelVersion: 3.13
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Specifies the hysteresis of threshold that the device is comparing
|
||||
against for the events enabled by
|
||||
<type>Y[_name]_thresh[_(rising|falling)]_hysteresis.
|
||||
If separate attributes exist for the two directions, but
|
||||
direction is not specified for this attribute, then a single
|
||||
hysteresis value applies to both directions.
|
||||
For falling events the hysteresis is added to the _value attribute for
|
||||
this event to get the upper threshold for when the event goes back to
|
||||
normal, for rising events the hysteresis is subtracted from the _value
|
||||
attribute. E.g. if in_voltage0_raw_thresh_rising_value is set to 1200
|
||||
and in_voltage0_raw_thresh_rising_hysteresis is set to 50. The event
|
||||
will get activated once in_voltage0_raw goes above 1200 and will become
|
||||
deactived again once the value falls below 1150.
|
||||
|
||||
What: /sys/.../events/in_accel_x_raw_roc_rising_value
|
||||
What: /sys/.../events/in_accel_x_raw_roc_falling_value
|
||||
What: /sys/.../events/in_accel_y_raw_roc_rising_value
|
||||
|
@ -811,3 +867,14 @@ Description:
|
|||
Writing '1' stores the current device configuration into
|
||||
on-chip EEPROM. After power-up or chip reset the device will
|
||||
automatically load the saved configuration.
|
||||
|
||||
What: /sys/.../iio:deviceX/in_intensity_red_integration_time
|
||||
What: /sys/.../iio:deviceX/in_intensity_green_integration_time
|
||||
What: /sys/.../iio:deviceX/in_intensity_blue_integration_time
|
||||
What: /sys/.../iio:deviceX/in_intensity_clear_integration_time
|
||||
What: /sys/.../iio:deviceX/in_illuminance_integration_time
|
||||
KernelVersion: 3.12
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
This attribute is used to get/set the integration time in
|
||||
seconds.
|
||||
|
|
|
@ -7,7 +7,6 @@ Required properties:
|
|||
- interrupts: Should contain the IRQ line for the ADC
|
||||
- atmel,adc-channels-used: Bitmask of the channels muxed and enable for this
|
||||
device
|
||||
- atmel,adc-num-channels: Number of channels available in the ADC
|
||||
- atmel,adc-startup-time: Startup Time of the ADC in microseconds as
|
||||
defined in the datasheet
|
||||
- atmel,adc-vref: Reference voltage in millivolts for the conversions
|
||||
|
@ -24,6 +23,13 @@ Optional properties:
|
|||
resolution will be used.
|
||||
- atmel,adc-sleep-mode: Boolean to enable sleep mode when no conversion
|
||||
- atmel,adc-sample-hold-time: Sample and Hold Time in microseconds
|
||||
- atmel,adc-ts-wires: Number of touch screen wires. Should be 4 or 5. If this
|
||||
value is set, then adc driver will enable touch screen
|
||||
support.
|
||||
NOTE: when adc touch screen enabled, the adc hardware trigger will be
|
||||
disabled. Since touch screen will occupied the trigger register.
|
||||
- atmel,adc-ts-pressure-threshold: a pressure threshold for touchscreen. It
|
||||
make touch detect more precision.
|
||||
|
||||
Optional trigger Nodes:
|
||||
- Required properties:
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
* Capella CM36651 I2C Proximity and Color Light sensor
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "capella,cm36651"
|
||||
- reg: the I2C address of the device
|
||||
- interrupts: interrupt-specifier for the sole interrupt
|
||||
generated by the device
|
||||
- vled-supply: regulator for the IR LED. IR_LED is a part
|
||||
of the cm36651 for proximity detection.
|
||||
As covered in ../../regulator/regulator.txt
|
||||
|
||||
Example:
|
||||
|
||||
i2c_cm36651: i2c-gpio {
|
||||
/* ... */
|
||||
|
||||
cm36651@18 {
|
||||
compatible = "capella,cm36651";
|
||||
reg = <0x18>;
|
||||
interrupt-parent = <&gpx0>;
|
||||
interrupts = <2 0>;
|
||||
vled-supply = <&ps_als_reg>;
|
||||
};
|
||||
|
||||
/* ... */
|
||||
};
|
|
@ -0,0 +1,21 @@
|
|||
* Sharp GP2AP020A00F I2C Proximity/ALS sensor
|
||||
|
||||
The proximity detector sensor requires power supply
|
||||
for its built-in led. It is also defined by this binding.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : should be "sharp,gp2ap020a00f"
|
||||
- reg : the I2C slave address of the light sensor
|
||||
- interrupts : interrupt specifier for the sole interrupt generated
|
||||
by the device
|
||||
- vled-supply : VLED power supply, as covered in ../regulator/regulator.txt
|
||||
|
||||
Example:
|
||||
|
||||
gp2ap020a00f@39 {
|
||||
compatible = "sharp,gp2ap020a00f";
|
||||
reg = <0x39>;
|
||||
interrupts = <2 0>;
|
||||
vled-supply = <...>;
|
||||
};
|
|
@ -1,7 +1,8 @@
|
|||
* Freescale i.MX28 LRADC device driver
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "fsl,imx28-lradc"
|
||||
- compatible: Should be "fsl,imx23-lradc" for i.MX23 SoC and "fsl,imx28-lradc"
|
||||
for i.MX28 SoC
|
||||
- reg: Address and length of the register set for the device
|
||||
- interrupts: Should contain the LRADC interrupts
|
||||
|
||||
|
@ -9,13 +10,38 @@ Optional properties:
|
|||
- fsl,lradc-touchscreen-wires: Number of wires used to connect the touchscreen
|
||||
to LRADC. Valid value is either 4 or 5. If this
|
||||
property is not present, then the touchscreen is
|
||||
disabled.
|
||||
disabled. 5 wires is valid for i.MX28 SoC only.
|
||||
- fsl,ave-ctrl: number of samples per direction to calculate an average value.
|
||||
Allowed value is 1 ... 31, default is 4
|
||||
- fsl,ave-delay: delay between consecutive samples. Allowed value is
|
||||
1 ... 2047. It is used if 'fsl,ave-ctrl' > 1, counts at
|
||||
2 kHz and its default is 2 (= 1 ms)
|
||||
- fsl,settling: delay between plate switch to next sample. Allowed value is
|
||||
1 ... 2047. It counts at 2 kHz and its default is
|
||||
10 (= 5 ms)
|
||||
|
||||
Examples:
|
||||
Example for i.MX23 SoC:
|
||||
|
||||
lradc@80050000 {
|
||||
compatible = "fsl,imx23-lradc";
|
||||
reg = <0x80050000 0x2000>;
|
||||
interrupts = <36 37 38 39 40 41 42 43 44>;
|
||||
status = "okay";
|
||||
fsl,lradc-touchscreen-wires = <4>;
|
||||
fsl,ave-ctrl = <4>;
|
||||
fsl,ave-delay = <2>;
|
||||
fsl,settling = <10>;
|
||||
};
|
||||
|
||||
Example for i.MX28 SoC:
|
||||
|
||||
lradc@80050000 {
|
||||
compatible = "fsl,imx28-lradc";
|
||||
reg = <0x80050000 0x2000>;
|
||||
interrupts = <10 14 15 16 17 18 19
|
||||
20 21 22 23 24 25>;
|
||||
interrupts = <10 14 15 16 17 18 19 20 21 22 23 24 25>;
|
||||
status = "okay";
|
||||
fsl,lradc-touchscreen-wires = <5>;
|
||||
fsl,ave-ctrl = <4>;
|
||||
fsl,ave-delay = <2>;
|
||||
fsl,settling = <10>;
|
||||
};
|
||||
|
|
|
@ -15,6 +15,7 @@ atmel Atmel Corporation
|
|||
avago Avago Technologies
|
||||
bosch Bosch Sensortec GmbH
|
||||
brcm Broadcom Corporation
|
||||
capella Capella Microsystems, Inc
|
||||
cavium Cavium, Inc.
|
||||
chrp Common Hardware Reference Platform
|
||||
cirrus Cirrus Logic, Inc.
|
||||
|
|
11
MAINTAINERS
11
MAINTAINERS
|
@ -4240,7 +4240,7 @@ S: Maintained
|
|||
F: drivers/media/rc/iguanair.c
|
||||
|
||||
IIO SUBSYSTEM AND DRIVERS
|
||||
M: Jonathan Cameron <jic23@cam.ac.uk>
|
||||
M: Jonathan Cameron <jic23@kernel.org>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/iio/
|
||||
|
@ -4777,6 +4777,13 @@ S: Maintained
|
|||
F: Documentation/hwmon/k8temp
|
||||
F: drivers/hwmon/k8temp.c
|
||||
|
||||
KTAP
|
||||
M: Jovi Zhangwei <jovi.zhangwei@gmail.com>
|
||||
W: http://www.ktap.org
|
||||
L: ktap@freelists.org
|
||||
S: Maintained
|
||||
F: drivers/staging/ktap/
|
||||
|
||||
KCONFIG
|
||||
M: Michal Marek <mmarek@suse.cz>
|
||||
L: linux-kbuild@vger.kernel.org
|
||||
|
@ -8009,7 +8016,7 @@ S: Maintained
|
|||
F: drivers/staging/media/go7007/
|
||||
|
||||
STAGING - INDUSTRIAL IO
|
||||
M: Jonathan Cameron <jic23@cam.ac.uk>
|
||||
M: Jonathan Cameron <jic23@kernel.org>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Odd Fixes
|
||||
F: drivers/staging/iio/
|
||||
|
|
|
@ -430,6 +430,7 @@
|
|||
reg = <0x80050000 0x2000>;
|
||||
interrupts = <36 37 38 39 40 41 42 43 44>;
|
||||
status = "disabled";
|
||||
clocks = <&clks 26>;
|
||||
};
|
||||
|
||||
spdif@80054000 {
|
||||
|
|
|
@ -183,6 +183,10 @@
|
|||
|
||||
lradc@80050000 {
|
||||
status = "okay";
|
||||
fsl,lradc-touchscreen-wires = <4>;
|
||||
fsl,ave-ctrl = <4>;
|
||||
fsl,ave-delay = <2>;
|
||||
fsl,settling = <10>;
|
||||
};
|
||||
|
||||
i2c0: i2c@80058000 {
|
||||
|
|
|
@ -902,6 +902,7 @@
|
|||
interrupts = <10 14 15 16 17 18 19
|
||||
20 21 22 23 24 25>;
|
||||
status = "disabled";
|
||||
clocks = <&clks 41>;
|
||||
};
|
||||
|
||||
spdif: spdif@80054000 {
|
||||
|
|
|
@ -60,14 +60,48 @@
|
|||
#define AT91_ADC_IER 0x24 /* Interrupt Enable Register */
|
||||
#define AT91_ADC_IDR 0x28 /* Interrupt Disable Register */
|
||||
#define AT91_ADC_IMR 0x2C /* Interrupt Mask Register */
|
||||
#define AT91_ADC_IER_PEN (1 << 29)
|
||||
#define AT91_ADC_IER_NOPEN (1 << 30)
|
||||
#define AT91_ADC_IER_XRDY (1 << 20)
|
||||
#define AT91_ADC_IER_YRDY (1 << 21)
|
||||
#define AT91_ADC_IER_PRDY (1 << 22)
|
||||
#define AT91_ADC_ISR_PENS (1 << 31)
|
||||
|
||||
#define AT91_ADC_CHR(n) (0x30 + ((n) * 4)) /* Channel Data Register N */
|
||||
#define AT91_ADC_DATA (0x3ff)
|
||||
|
||||
#define AT91_ADC_CDR0_9X5 (0x50) /* Channel Data Register 0 for 9X5 */
|
||||
|
||||
#define AT91_ADC_ACR 0x94 /* Analog Control Register */
|
||||
#define AT91_ADC_ACR_PENDETSENS (0x3 << 0) /* pull-up resistor */
|
||||
|
||||
#define AT91_ADC_TSMR 0xB0
|
||||
#define AT91_ADC_TSMR_TSMODE (3 << 0) /* Touch Screen Mode */
|
||||
#define AT91_ADC_TSMR_TSMODE_NONE (0 << 0)
|
||||
#define AT91_ADC_TSMR_TSMODE_4WIRE_NO_PRESS (1 << 0)
|
||||
#define AT91_ADC_TSMR_TSMODE_4WIRE_PRESS (2 << 0)
|
||||
#define AT91_ADC_TSMR_TSMODE_5WIRE (3 << 0)
|
||||
#define AT91_ADC_TSMR_TSAV (3 << 4) /* Averages samples */
|
||||
#define AT91_ADC_TSMR_TSAV_(x) ((x) << 4)
|
||||
#define AT91_ADC_TSMR_SCTIM (0x0f << 16) /* Switch closure time */
|
||||
#define AT91_ADC_TSMR_PENDBC (0x0f << 28) /* Pen Debounce time */
|
||||
#define AT91_ADC_TSMR_PENDBC_(x) ((x) << 28)
|
||||
#define AT91_ADC_TSMR_NOTSDMA (1 << 22) /* No Touchscreen DMA */
|
||||
#define AT91_ADC_TSMR_PENDET_DIS (0 << 24) /* Pen contact detection disable */
|
||||
#define AT91_ADC_TSMR_PENDET_ENA (1 << 24) /* Pen contact detection enable */
|
||||
|
||||
#define AT91_ADC_TSXPOSR 0xB4
|
||||
#define AT91_ADC_TSYPOSR 0xB8
|
||||
#define AT91_ADC_TSPRESSR 0xBC
|
||||
|
||||
#define AT91_ADC_TRGR_9260 AT91_ADC_MR
|
||||
#define AT91_ADC_TRGR_9G45 0x08
|
||||
#define AT91_ADC_TRGR_9X5 0xC0
|
||||
|
||||
/* Trigger Register bit field */
|
||||
#define AT91_ADC_TRGR_TRGPER (0xffff << 16)
|
||||
#define AT91_ADC_TRGR_TRGPER_(x) ((x) << 16)
|
||||
#define AT91_ADC_TRGR_TRGMOD (0x7 << 0)
|
||||
#define AT91_ADC_TRGR_MOD_PERIOD_TRIG (5 << 0)
|
||||
|
||||
#endif
|
||||
|
|
|
@ -465,6 +465,39 @@ static int sensor_hub_raw_event(struct hid_device *hdev,
|
|||
return 1;
|
||||
}
|
||||
|
||||
int sensor_hub_device_open(struct hid_sensor_hub_device *hsdev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev);
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
if (!hsdev->ref_cnt) {
|
||||
ret = hid_hw_open(hsdev->hdev);
|
||||
if (ret) {
|
||||
hid_err(hsdev->hdev, "failed to open hid device\n");
|
||||
mutex_unlock(&data->mutex);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
hsdev->ref_cnt++;
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sensor_hub_device_open);
|
||||
|
||||
void sensor_hub_device_close(struct hid_sensor_hub_device *hsdev)
|
||||
{
|
||||
struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev);
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
hsdev->ref_cnt--;
|
||||
if (!hsdev->ref_cnt)
|
||||
hid_hw_close(hsdev->hdev);
|
||||
mutex_unlock(&data->mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sensor_hub_device_close);
|
||||
|
||||
static int sensor_hub_probe(struct hid_device *hdev,
|
||||
const struct hid_device_id *id)
|
||||
{
|
||||
|
@ -506,12 +539,6 @@ static int sensor_hub_probe(struct hid_device *hdev,
|
|||
hid_err(hdev, "hw start failed\n");
|
||||
return ret;
|
||||
}
|
||||
ret = hid_hw_open(hdev);
|
||||
if (ret) {
|
||||
hid_err(hdev, "failed to open input interrupt pipe\n");
|
||||
goto err_stop_hw;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&sd->dyn_callback_list);
|
||||
sd->hid_sensor_client_cnt = 0;
|
||||
report_enum = &hdev->report_enum[HID_INPUT_REPORT];
|
||||
|
@ -520,7 +547,7 @@ static int sensor_hub_probe(struct hid_device *hdev,
|
|||
if (dev_cnt > HID_MAX_PHY_DEVICES) {
|
||||
hid_err(hdev, "Invalid Physical device count\n");
|
||||
ret = -EINVAL;
|
||||
goto err_close;
|
||||
goto err_stop_hw;
|
||||
}
|
||||
sd->hid_sensor_hub_client_devs = kzalloc(dev_cnt *
|
||||
sizeof(struct mfd_cell),
|
||||
|
@ -528,7 +555,7 @@ static int sensor_hub_probe(struct hid_device *hdev,
|
|||
if (sd->hid_sensor_hub_client_devs == NULL) {
|
||||
hid_err(hdev, "Failed to allocate memory for mfd cells\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_close;
|
||||
goto err_stop_hw;
|
||||
}
|
||||
list_for_each_entry(report, &report_enum->report_list, list) {
|
||||
hid_dbg(hdev, "Report id:%x\n", report->id);
|
||||
|
@ -565,8 +592,6 @@ err_free_names:
|
|||
for (i = 0; i < sd->hid_sensor_client_cnt ; ++i)
|
||||
kfree(sd->hid_sensor_hub_client_devs[i].name);
|
||||
kfree(sd->hid_sensor_hub_client_devs);
|
||||
err_close:
|
||||
hid_hw_close(hdev);
|
||||
err_stop_hw:
|
||||
hid_hw_stop(hdev);
|
||||
|
||||
|
|
|
@ -471,13 +471,10 @@ static irqreturn_t bma180_trigger_handler(int irq, void *p)
|
|||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct bma180_data *data = iio_priv(indio_dev);
|
||||
int64_t time_ns = iio_get_time_ns();
|
||||
int bit, ret, i = 0;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
if (indio_dev->scan_timestamp) {
|
||||
ret = indio_dev->scan_bytes / sizeof(s64) - 1;
|
||||
((s64 *)data->buff)[ret] = iio_get_time_ns();
|
||||
}
|
||||
|
||||
for_each_set_bit(bit, indio_dev->buffer->scan_mask,
|
||||
indio_dev->masklength) {
|
||||
|
@ -490,7 +487,7 @@ static irqreturn_t bma180_trigger_handler(int irq, void *p)
|
|||
}
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
iio_push_to_buffers(indio_dev, (u8 *)data->buff);
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data->buff, time_ns);
|
||||
err:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
|
|
|
@ -182,10 +182,11 @@ static const struct iio_info accel_3d_info = {
|
|||
};
|
||||
|
||||
/* Function to push data to buffer */
|
||||
static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len)
|
||||
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, (u8 *)data);
|
||||
iio_push_to_buffers(indio_dev, data);
|
||||
}
|
||||
|
||||
/* Callback handler to send event after all samples are received and captured */
|
||||
|
@ -200,7 +201,7 @@ static int accel_3d_proc_event(struct hid_sensor_hub_device *hsdev,
|
|||
accel_state->common_attributes.data_ready);
|
||||
if (accel_state->common_attributes.data_ready)
|
||||
hid_sensor_push_data(indio_dev,
|
||||
(u8 *)accel_state->accel_val,
|
||||
accel_state->accel_val,
|
||||
sizeof(accel_state->accel_val));
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -222,7 +222,6 @@ static int kxsd9_probe(struct spi_device *spi)
|
|||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct kxsd9_state *st;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
|
@ -244,11 +243,7 @@ static int kxsd9_probe(struct spi_device *spi)
|
|||
spi_setup(spi);
|
||||
kxsd9_power_up(st);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
return iio_device_register(indio_dev);
|
||||
}
|
||||
|
||||
static int kxsd9_remove(struct spi_device *spi)
|
||||
|
|
|
@ -32,16 +32,7 @@ int st_accel_trig_set_state(struct iio_trigger *trig, bool state)
|
|||
|
||||
static int st_accel_buffer_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = st_sensors_set_enable(indio_dev, true);
|
||||
if (err < 0)
|
||||
goto st_accel_set_enable_error;
|
||||
|
||||
err = iio_sw_buffer_preenable(indio_dev);
|
||||
|
||||
st_accel_set_enable_error:
|
||||
return err;
|
||||
return st_sensors_set_enable(indio_dev, true);
|
||||
}
|
||||
|
||||
static int st_accel_buffer_postenable(struct iio_dev *indio_dev)
|
||||
|
|
|
@ -452,8 +452,9 @@ static const struct iio_trigger_ops st_accel_trigger_ops = {
|
|||
int st_accel_common_probe(struct iio_dev *indio_dev,
|
||||
struct st_sensors_platform_data *plat_data)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *adata = iio_priv(indio_dev);
|
||||
int irq = adata->get_irq_data_ready(indio_dev);
|
||||
int err;
|
||||
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &accel_info;
|
||||
|
@ -461,7 +462,7 @@ int st_accel_common_probe(struct iio_dev *indio_dev,
|
|||
err = st_sensors_check_device_support(indio_dev,
|
||||
ARRAY_SIZE(st_accel_sensors), st_accel_sensors);
|
||||
if (err < 0)
|
||||
goto st_accel_common_probe_error;
|
||||
return err;
|
||||
|
||||
adata->num_data_channels = ST_ACCEL_NUMBER_DATA_CHANNELS;
|
||||
adata->multiread_bit = adata->sensor->multi_read_bit;
|
||||
|
@ -478,13 +479,13 @@ int st_accel_common_probe(struct iio_dev *indio_dev,
|
|||
|
||||
err = st_sensors_init_sensor(indio_dev, plat_data);
|
||||
if (err < 0)
|
||||
goto st_accel_common_probe_error;
|
||||
return err;
|
||||
|
||||
if (adata->get_irq_data_ready(indio_dev) > 0) {
|
||||
err = st_accel_allocate_ring(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_accel_common_probe_error;
|
||||
err = st_accel_allocate_ring(indio_dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (irq > 0) {
|
||||
err = st_sensors_allocate_trigger(indio_dev,
|
||||
ST_ACCEL_TRIGGER_OPS);
|
||||
if (err < 0)
|
||||
|
@ -495,15 +496,14 @@ int st_accel_common_probe(struct iio_dev *indio_dev,
|
|||
if (err)
|
||||
goto st_accel_device_register_error;
|
||||
|
||||
return err;
|
||||
return 0;
|
||||
|
||||
st_accel_device_register_error:
|
||||
if (adata->get_irq_data_ready(indio_dev) > 0)
|
||||
if (irq > 0)
|
||||
st_sensors_deallocate_trigger(indio_dev);
|
||||
st_accel_probe_trigger_error:
|
||||
if (adata->get_irq_data_ready(indio_dev) > 0)
|
||||
st_accel_deallocate_ring(indio_dev);
|
||||
st_accel_common_probe_error:
|
||||
st_accel_deallocate_ring(indio_dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(st_accel_common_probe);
|
||||
|
@ -513,10 +513,10 @@ void st_accel_common_remove(struct iio_dev *indio_dev)
|
|||
struct st_sensor_data *adata = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
if (adata->get_irq_data_ready(indio_dev) > 0) {
|
||||
if (adata->get_irq_data_ready(indio_dev) > 0)
|
||||
st_sensors_deallocate_trigger(indio_dev);
|
||||
st_accel_deallocate_ring(indio_dev);
|
||||
}
|
||||
|
||||
st_accel_deallocate_ring(indio_dev);
|
||||
}
|
||||
EXPORT_SYMBOL(st_accel_common_remove);
|
||||
|
||||
|
|
|
@ -145,6 +145,16 @@ config MCP320X
|
|||
This driver can also be built as a module. If so, the module will be
|
||||
called mcp320x.
|
||||
|
||||
config MCP3422
|
||||
tristate "Microchip Technology MCP3422/3/4 driver"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to build support for Microchip Technology's MCP3422,
|
||||
MCP3423 or MCP3424 analog to digital converters.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called mcp3422.
|
||||
|
||||
config NAU7802
|
||||
tristate "Nuvoton NAU7802 ADC driver"
|
||||
depends on I2C
|
||||
|
@ -167,6 +177,8 @@ config TI_ADC081C
|
|||
config TI_AM335X_ADC
|
||||
tristate "TI's AM335X ADC driver"
|
||||
depends on MFD_TI_AM335X_TSCADC
|
||||
select IIO_BUFFER
|
||||
select IIO_KFIFO_BUF
|
||||
help
|
||||
Say yes here to build support for Texas Instruments ADC
|
||||
driver which is also a MFD client.
|
||||
|
|
|
@ -16,6 +16,7 @@ obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
|
|||
obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
|
||||
obj-$(CONFIG_MAX1363) += max1363.o
|
||||
obj-$(CONFIG_MCP320X) += mcp320x.o
|
||||
obj-$(CONFIG_MCP3422) += mcp3422.o
|
||||
obj-$(CONFIG_NAU7802) += nau7802.o
|
||||
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
|
||||
obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
struct ad7266_state {
|
||||
struct spi_device *spi;
|
||||
struct regulator *reg;
|
||||
unsigned long vref_uv;
|
||||
unsigned long vref_mv;
|
||||
|
||||
struct spi_transfer single_xfer[3];
|
||||
struct spi_message single_msg;
|
||||
|
@ -61,17 +61,7 @@ static int ad7266_powerdown(struct ad7266_state *st)
|
|||
static int ad7266_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad7266_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = ad7266_wakeup(st);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iio_sw_buffer_preenable(indio_dev);
|
||||
if (ret)
|
||||
ad7266_powerdown(st);
|
||||
|
||||
return ret;
|
||||
return ad7266_wakeup(st);
|
||||
}
|
||||
|
||||
static int ad7266_postdisable(struct iio_dev *indio_dev)
|
||||
|
@ -96,9 +86,8 @@ static irqreturn_t ad7266_trigger_handler(int irq, void *p)
|
|||
|
||||
ret = spi_read(st->spi, st->data, 4);
|
||||
if (ret == 0) {
|
||||
if (indio_dev->scan_timestamp)
|
||||
((s64 *)st->data)[1] = pf->timestamp;
|
||||
iio_push_to_buffers(indio_dev, (u8 *)st->data);
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, st->data,
|
||||
pf->timestamp);
|
||||
}
|
||||
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
@ -157,7 +146,7 @@ static int ad7266_read_raw(struct iio_dev *indio_dev,
|
|||
struct iio_chan_spec const *chan, int *val, int *val2, long m)
|
||||
{
|
||||
struct ad7266_state *st = iio_priv(indio_dev);
|
||||
unsigned long scale_uv;
|
||||
unsigned long scale_mv;
|
||||
int ret;
|
||||
|
||||
switch (m) {
|
||||
|
@ -175,16 +164,15 @@ static int ad7266_read_raw(struct iio_dev *indio_dev,
|
|||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
scale_uv = (st->vref_uv * 100);
|
||||
scale_mv = st->vref_mv;
|
||||
if (st->mode == AD7266_MODE_DIFF)
|
||||
scale_uv *= 2;
|
||||
scale_mv *= 2;
|
||||
if (st->range == AD7266_RANGE_2VREF)
|
||||
scale_uv *= 2;
|
||||
scale_mv *= 2;
|
||||
|
||||
scale_uv >>= chan->scan_type.realbits;
|
||||
*val = scale_uv / 100000;
|
||||
*val2 = (scale_uv % 100000) * 10;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
*val = scale_mv;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
if (st->range == AD7266_RANGE_2VREF &&
|
||||
st->mode != AD7266_MODE_DIFF)
|
||||
|
@ -293,7 +281,7 @@ static const struct iio_info ad7266_info = {
|
|||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static unsigned long ad7266_available_scan_masks[] = {
|
||||
static const unsigned long ad7266_available_scan_masks[] = {
|
||||
0x003,
|
||||
0x00c,
|
||||
0x030,
|
||||
|
@ -303,14 +291,14 @@ static unsigned long ad7266_available_scan_masks[] = {
|
|||
0x000,
|
||||
};
|
||||
|
||||
static unsigned long ad7266_available_scan_masks_diff[] = {
|
||||
static const unsigned long ad7266_available_scan_masks_diff[] = {
|
||||
0x003,
|
||||
0x00c,
|
||||
0x030,
|
||||
0x000,
|
||||
};
|
||||
|
||||
static unsigned long ad7266_available_scan_masks_fixed[] = {
|
||||
static const unsigned long ad7266_available_scan_masks_fixed[] = {
|
||||
0x003,
|
||||
0x000,
|
||||
};
|
||||
|
@ -318,7 +306,7 @@ static unsigned long ad7266_available_scan_masks_fixed[] = {
|
|||
struct ad7266_chan_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int num_channels;
|
||||
unsigned long *scan_masks;
|
||||
const unsigned long *scan_masks;
|
||||
};
|
||||
|
||||
#define AD7266_CHAN_INFO_INDEX(_differential, _signed, _fixed) \
|
||||
|
@ -415,10 +403,10 @@ static int ad7266_probe(struct spi_device *spi)
|
|||
if (ret < 0)
|
||||
goto error_disable_reg;
|
||||
|
||||
st->vref_uv = ret;
|
||||
st->vref_mv = ret / 1000;
|
||||
} else {
|
||||
/* Use internal reference */
|
||||
st->vref_uv = 2500000;
|
||||
st->vref_mv = 2500;
|
||||
}
|
||||
|
||||
if (pdata) {
|
||||
|
|
|
@ -159,20 +159,14 @@ static irqreturn_t ad7298_trigger_handler(int irq, void *p)
|
|||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct ad7298_state *st = iio_priv(indio_dev);
|
||||
s64 time_ns = 0;
|
||||
int b_sent;
|
||||
|
||||
b_sent = spi_sync(st->spi, &st->ring_msg);
|
||||
if (b_sent)
|
||||
goto done;
|
||||
|
||||
if (indio_dev->scan_timestamp) {
|
||||
time_ns = iio_get_time_ns();
|
||||
memcpy((u8 *)st->rx_buf + indio_dev->scan_bytes - sizeof(s64),
|
||||
&time_ns, sizeof(time_ns));
|
||||
}
|
||||
|
||||
iio_push_to_buffers(indio_dev, (u8 *)st->rx_buf);
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf,
|
||||
iio_get_time_ns());
|
||||
|
||||
done:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
|
|
@ -64,19 +64,14 @@ static irqreturn_t ad7476_trigger_handler(int irq, void *p)
|
|||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct ad7476_state *st = iio_priv(indio_dev);
|
||||
s64 time_ns;
|
||||
int b_sent;
|
||||
|
||||
b_sent = spi_sync(st->spi, &st->msg);
|
||||
if (b_sent < 0)
|
||||
goto done;
|
||||
|
||||
time_ns = iio_get_time_ns();
|
||||
|
||||
if (indio_dev->scan_timestamp)
|
||||
((s64 *)st->data)[1] = time_ns;
|
||||
|
||||
iio_push_to_buffers(indio_dev, st->data);
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, st->data,
|
||||
iio_get_time_ns());
|
||||
done:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
|
@ -132,10 +127,9 @@ static int ad7476_read_raw(struct iio_dev *indio_dev,
|
|||
} else {
|
||||
scale_uv = st->chip_info->int_vref_uv;
|
||||
}
|
||||
scale_uv >>= chan->scan_type.realbits;
|
||||
*val = scale_uv / 1000;
|
||||
*val2 = (scale_uv % 1000) * 1000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
*val = scale_uv / 1000;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
|
@ -202,7 +202,6 @@ static int ad7791_read_raw(struct iio_dev *indio_dev,
|
|||
{
|
||||
struct ad7791_state *st = iio_priv(indio_dev);
|
||||
bool unipolar = !!(st->mode & AD7791_MODE_UNIPOLAR);
|
||||
unsigned long long scale_pv;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
|
@ -220,23 +219,26 @@ static int ad7791_read_raw(struct iio_dev *indio_dev,
|
|||
case IIO_CHAN_INFO_SCALE:
|
||||
/* The monitor channel uses an internal reference. */
|
||||
if (chan->address == AD7791_CH_AVDD_MONITOR) {
|
||||
scale_pv = 5850000000000ULL;
|
||||
/*
|
||||
* The signal is attenuated by a factor of 5 and
|
||||
* compared against a 1.17V internal reference.
|
||||
*/
|
||||
*val = 1170 * 5;
|
||||
} else {
|
||||
int voltage_uv;
|
||||
|
||||
voltage_uv = regulator_get_voltage(st->reg);
|
||||
if (voltage_uv < 0)
|
||||
return voltage_uv;
|
||||
scale_pv = (unsigned long long)voltage_uv * 1000000;
|
||||
|
||||
*val = voltage_uv / 1000;
|
||||
}
|
||||
if (unipolar)
|
||||
scale_pv >>= chan->scan_type.realbits;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
else
|
||||
scale_pv >>= chan->scan_type.realbits - 1;
|
||||
*val2 = do_div(scale_pv, 1000000000);
|
||||
*val = scale_pv;
|
||||
*val2 = chan->scan_type.realbits - 1;
|
||||
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
|
|
|
@ -78,11 +78,6 @@ enum ad7887_supported_device_ids {
|
|||
static int ad7887_ring_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad7887_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = iio_sw_buffer_preenable(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* We know this is a single long so can 'cheat' */
|
||||
switch (*indio_dev->active_scan_mask) {
|
||||
|
@ -121,20 +116,14 @@ static irqreturn_t ad7887_trigger_handler(int irq, void *p)
|
|||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct ad7887_state *st = iio_priv(indio_dev);
|
||||
s64 time_ns;
|
||||
int b_sent;
|
||||
|
||||
b_sent = spi_sync(st->spi, st->ring_msg);
|
||||
if (b_sent)
|
||||
goto done;
|
||||
|
||||
time_ns = iio_get_time_ns();
|
||||
|
||||
if (indio_dev->scan_timestamp)
|
||||
memcpy(st->data + indio_dev->scan_bytes - sizeof(s64),
|
||||
&time_ns, sizeof(time_ns));
|
||||
|
||||
iio_push_to_buffers(indio_dev, st->data);
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, st->data,
|
||||
iio_get_time_ns());
|
||||
done:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
|
|
|
@ -174,20 +174,14 @@ static irqreturn_t ad7923_trigger_handler(int irq, void *p)
|
|||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct ad7923_state *st = iio_priv(indio_dev);
|
||||
s64 time_ns = 0;
|
||||
int b_sent;
|
||||
|
||||
b_sent = spi_sync(st->spi, &st->ring_msg);
|
||||
if (b_sent)
|
||||
goto done;
|
||||
|
||||
if (indio_dev->scan_timestamp) {
|
||||
time_ns = iio_get_time_ns();
|
||||
memcpy((u8 *)st->rx_buf + indio_dev->scan_bytes - sizeof(s64),
|
||||
&time_ns, sizeof(time_ns));
|
||||
}
|
||||
|
||||
iio_push_to_buffers(indio_dev, (u8 *)st->rx_buf);
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf,
|
||||
iio_get_time_ns());
|
||||
|
||||
done:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
|
|
@ -368,10 +368,6 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p)
|
|||
|
||||
memset(data, 0x00, 16);
|
||||
|
||||
/* Guaranteed to be aligned with 8 byte boundary */
|
||||
if (indio_dev->scan_timestamp)
|
||||
((s64 *)data)[1] = pf->timestamp;
|
||||
|
||||
reg_size = indio_dev->channels[0].scan_type.realbits +
|
||||
indio_dev->channels[0].scan_type.shift;
|
||||
reg_size = DIV_ROUND_UP(reg_size, 8);
|
||||
|
@ -391,7 +387,7 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p)
|
|||
break;
|
||||
}
|
||||
|
||||
iio_push_to_buffers(indio_dev, (uint8_t *)data);
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data, pf->timestamp);
|
||||
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
sigma_delta->irq_dis = false;
|
||||
|
@ -401,7 +397,6 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p)
|
|||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops ad_sd_buffer_setup_ops = {
|
||||
.preenable = &iio_sw_buffer_preenable,
|
||||
.postenable = &ad_sd_buffer_postenable,
|
||||
.predisable = &iio_triggered_buffer_predisable,
|
||||
.postdisable = &ad_sd_buffer_postdisable,
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/kernel.h>
|
||||
|
@ -39,10 +40,36 @@
|
|||
#define at91_adc_writel(st, reg, val) \
|
||||
(writel_relaxed(val, st->reg_base + reg))
|
||||
|
||||
#define DRIVER_NAME "at91_adc"
|
||||
#define MAX_POS_BITS 12
|
||||
|
||||
#define TOUCH_SAMPLE_PERIOD_US 2000 /* 2ms */
|
||||
#define TOUCH_PEN_DETECT_DEBOUNCE_US 200
|
||||
|
||||
struct at91_adc_caps {
|
||||
bool has_ts; /* Support touch screen */
|
||||
bool has_tsmr; /* only at91sam9x5, sama5d3 have TSMR reg */
|
||||
/*
|
||||
* Numbers of sampling data will be averaged. Can be 0~3.
|
||||
* Hardware can average (2 ^ ts_filter_average) sample data.
|
||||
*/
|
||||
u8 ts_filter_average;
|
||||
/* Pen Detection input pull-up resistor, can be 0~3 */
|
||||
u8 ts_pen_detect_sensitivity;
|
||||
|
||||
/* startup time calculate function */
|
||||
u32 (*calc_startup_ticks)(u8 startup_time, u32 adc_clk_khz);
|
||||
|
||||
u8 num_channels;
|
||||
struct at91_adc_reg_desc registers;
|
||||
};
|
||||
|
||||
enum atmel_adc_ts_type {
|
||||
ATMEL_ADC_TOUCHSCREEN_NONE = 0,
|
||||
ATMEL_ADC_TOUCHSCREEN_4WIRE = 4,
|
||||
ATMEL_ADC_TOUCHSCREEN_5WIRE = 5,
|
||||
};
|
||||
|
||||
struct at91_adc_state {
|
||||
struct clk *adc_clk;
|
||||
u16 *buffer;
|
||||
|
@ -67,6 +94,26 @@ struct at91_adc_state {
|
|||
bool low_res; /* the resolution corresponds to the lowest one */
|
||||
wait_queue_head_t wq_data_avail;
|
||||
struct at91_adc_caps *caps;
|
||||
|
||||
/*
|
||||
* Following ADC channels are shared by touchscreen:
|
||||
*
|
||||
* CH0 -- Touch screen XP/UL
|
||||
* CH1 -- Touch screen XM/UR
|
||||
* CH2 -- Touch screen YP/LL
|
||||
* CH3 -- Touch screen YM/Sense
|
||||
* CH4 -- Touch screen LR(5-wire only)
|
||||
*
|
||||
* The bitfields below represents the reserved channel in the
|
||||
* touchscreen mode.
|
||||
*/
|
||||
#define CHAN_MASK_TOUCHSCREEN_4WIRE (0xf << 0)
|
||||
#define CHAN_MASK_TOUCHSCREEN_5WIRE (0x1f << 0)
|
||||
enum atmel_adc_ts_type touchscreen_type;
|
||||
struct input_dev *ts_input;
|
||||
|
||||
u16 ts_sample_period_val;
|
||||
u32 ts_pressure_threshold;
|
||||
};
|
||||
|
||||
static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
|
||||
|
@ -83,13 +130,7 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
|
|||
j++;
|
||||
}
|
||||
|
||||
if (idev->scan_timestamp) {
|
||||
s64 *timestamp = (s64 *)((u8 *)st->buffer +
|
||||
ALIGN(j, sizeof(s64)));
|
||||
*timestamp = pf->timestamp;
|
||||
}
|
||||
|
||||
iio_push_to_buffers(idev, (u8 *)st->buffer);
|
||||
iio_push_to_buffers_with_timestamp(idev, st->buffer, pf->timestamp);
|
||||
|
||||
iio_trigger_notify_done(idev->trig);
|
||||
|
||||
|
@ -101,14 +142,10 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t at91_adc_eoc_trigger(int irq, void *private)
|
||||
/* Handler for classic adc channel eoc trigger */
|
||||
void handle_adc_eoc_trigger(int irq, struct iio_dev *idev)
|
||||
{
|
||||
struct iio_dev *idev = private;
|
||||
struct at91_adc_state *st = iio_priv(idev);
|
||||
u32 status = at91_adc_readl(st, st->registers->status_register);
|
||||
|
||||
if (!(status & st->registers->drdy_mask))
|
||||
return IRQ_HANDLED;
|
||||
|
||||
if (iio_buffer_enabled(idev)) {
|
||||
disable_irq_nosync(irq);
|
||||
|
@ -118,6 +155,115 @@ static irqreturn_t at91_adc_eoc_trigger(int irq, void *private)
|
|||
st->done = true;
|
||||
wake_up_interruptible(&st->wq_data_avail);
|
||||
}
|
||||
}
|
||||
|
||||
static int at91_ts_sample(struct at91_adc_state *st)
|
||||
{
|
||||
unsigned int xscale, yscale, reg, z1, z2;
|
||||
unsigned int x, y, pres, xpos, ypos;
|
||||
unsigned int rxp = 1;
|
||||
unsigned int factor = 1000;
|
||||
struct iio_dev *idev = iio_priv_to_dev(st);
|
||||
|
||||
unsigned int xyz_mask_bits = st->res;
|
||||
unsigned int xyz_mask = (1 << xyz_mask_bits) - 1;
|
||||
|
||||
/* calculate position */
|
||||
/* x position = (x / xscale) * max, max = 2^MAX_POS_BITS - 1 */
|
||||
reg = at91_adc_readl(st, AT91_ADC_TSXPOSR);
|
||||
xpos = reg & xyz_mask;
|
||||
x = (xpos << MAX_POS_BITS) - xpos;
|
||||
xscale = (reg >> 16) & xyz_mask;
|
||||
if (xscale == 0) {
|
||||
dev_err(&idev->dev, "Error: xscale == 0!\n");
|
||||
return -1;
|
||||
}
|
||||
x /= xscale;
|
||||
|
||||
/* y position = (y / yscale) * max, max = 2^MAX_POS_BITS - 1 */
|
||||
reg = at91_adc_readl(st, AT91_ADC_TSYPOSR);
|
||||
ypos = reg & xyz_mask;
|
||||
y = (ypos << MAX_POS_BITS) - ypos;
|
||||
yscale = (reg >> 16) & xyz_mask;
|
||||
if (yscale == 0) {
|
||||
dev_err(&idev->dev, "Error: yscale == 0!\n");
|
||||
return -1;
|
||||
}
|
||||
y /= yscale;
|
||||
|
||||
/* calculate the pressure */
|
||||
reg = at91_adc_readl(st, AT91_ADC_TSPRESSR);
|
||||
z1 = reg & xyz_mask;
|
||||
z2 = (reg >> 16) & xyz_mask;
|
||||
|
||||
if (z1 != 0)
|
||||
pres = rxp * (x * factor / 1024) * (z2 * factor / z1 - factor)
|
||||
/ factor;
|
||||
else
|
||||
pres = st->ts_pressure_threshold; /* no pen contacted */
|
||||
|
||||
dev_dbg(&idev->dev, "xpos = %d, xscale = %d, ypos = %d, yscale = %d, z1 = %d, z2 = %d, press = %d\n",
|
||||
xpos, xscale, ypos, yscale, z1, z2, pres);
|
||||
|
||||
if (pres < st->ts_pressure_threshold) {
|
||||
dev_dbg(&idev->dev, "x = %d, y = %d, pressure = %d\n",
|
||||
x, y, pres / factor);
|
||||
input_report_abs(st->ts_input, ABS_X, x);
|
||||
input_report_abs(st->ts_input, ABS_Y, y);
|
||||
input_report_abs(st->ts_input, ABS_PRESSURE, pres);
|
||||
input_report_key(st->ts_input, BTN_TOUCH, 1);
|
||||
input_sync(st->ts_input);
|
||||
} else {
|
||||
dev_dbg(&idev->dev, "pressure too low: not reporting\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t at91_adc_interrupt(int irq, void *private)
|
||||
{
|
||||
struct iio_dev *idev = private;
|
||||
struct at91_adc_state *st = iio_priv(idev);
|
||||
u32 status = at91_adc_readl(st, st->registers->status_register);
|
||||
const uint32_t ts_data_irq_mask =
|
||||
AT91_ADC_IER_XRDY |
|
||||
AT91_ADC_IER_YRDY |
|
||||
AT91_ADC_IER_PRDY;
|
||||
|
||||
if (status & st->registers->drdy_mask)
|
||||
handle_adc_eoc_trigger(irq, idev);
|
||||
|
||||
if (status & AT91_ADC_IER_PEN) {
|
||||
at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_PEN);
|
||||
at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_NOPEN |
|
||||
ts_data_irq_mask);
|
||||
/* Set up period trigger for sampling */
|
||||
at91_adc_writel(st, st->registers->trigger_register,
|
||||
AT91_ADC_TRGR_MOD_PERIOD_TRIG |
|
||||
AT91_ADC_TRGR_TRGPER_(st->ts_sample_period_val));
|
||||
} else if (status & AT91_ADC_IER_NOPEN) {
|
||||
at91_adc_writel(st, st->registers->trigger_register, 0);
|
||||
at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_NOPEN |
|
||||
ts_data_irq_mask);
|
||||
at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_PEN);
|
||||
|
||||
input_report_key(st->ts_input, BTN_TOUCH, 0);
|
||||
input_sync(st->ts_input);
|
||||
} else if ((status & ts_data_irq_mask) == ts_data_irq_mask) {
|
||||
/* Now all touchscreen data is ready */
|
||||
|
||||
if (status & AT91_ADC_ISR_PENS) {
|
||||
/* validate data by pen contact */
|
||||
at91_ts_sample(st);
|
||||
} else {
|
||||
/* triggered by event that is no pen contact, just read
|
||||
* them to clean the interrupt and discard all.
|
||||
*/
|
||||
at91_adc_readl(st, AT91_ADC_TSXPOSR);
|
||||
at91_adc_readl(st, AT91_ADC_TSYPOSR);
|
||||
at91_adc_readl(st, AT91_ADC_TSPRESSR);
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
@ -127,6 +273,16 @@ static int at91_adc_channel_init(struct iio_dev *idev)
|
|||
struct at91_adc_state *st = iio_priv(idev);
|
||||
struct iio_chan_spec *chan_array, *timestamp;
|
||||
int bit, idx = 0;
|
||||
unsigned long rsvd_mask = 0;
|
||||
|
||||
/* If touchscreen is enable, then reserve the adc channels */
|
||||
if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_4WIRE)
|
||||
rsvd_mask = CHAN_MASK_TOUCHSCREEN_4WIRE;
|
||||
else if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_5WIRE)
|
||||
rsvd_mask = CHAN_MASK_TOUCHSCREEN_5WIRE;
|
||||
|
||||
/* set up the channel mask to reserve touchscreen channels */
|
||||
st->channels_mask &= ~rsvd_mask;
|
||||
|
||||
idev->num_channels = bitmap_weight(&st->channels_mask,
|
||||
st->num_channels) + 1;
|
||||
|
@ -279,7 +435,7 @@ static int at91_adc_trigger_init(struct iio_dev *idev)
|
|||
int i, ret;
|
||||
|
||||
st->trig = devm_kzalloc(&idev->dev,
|
||||
st->trigger_number * sizeof(st->trig),
|
||||
st->trigger_number * sizeof(*st->trig),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (st->trig == NULL) {
|
||||
|
@ -372,9 +528,9 @@ static int at91_adc_read_raw(struct iio_dev *idev,
|
|||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = (st->vref_mv * 1000) >> chan->scan_type.realbits;
|
||||
*val2 = 0;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
*val = st->vref_mv;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -434,8 +590,80 @@ ret:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static u32 calc_startup_ticks_9260(u8 startup_time, u32 adc_clk_khz)
|
||||
{
|
||||
/*
|
||||
* Number of ticks needed to cover the startup time of the ADC
|
||||
* as defined in the electrical characteristics of the board,
|
||||
* divided by 8. The formula thus is :
|
||||
* Startup Time = (ticks + 1) * 8 / ADC Clock
|
||||
*/
|
||||
return round_up((startup_time * adc_clk_khz / 1000) - 1, 8) / 8;
|
||||
}
|
||||
|
||||
static u32 calc_startup_ticks_9x5(u8 startup_time, u32 adc_clk_khz)
|
||||
{
|
||||
/*
|
||||
* For sama5d3x and at91sam9x5, the formula changes to:
|
||||
* Startup Time = <lookup_table_value> / ADC Clock
|
||||
*/
|
||||
const int startup_lookup[] = {
|
||||
0 , 8 , 16 , 24 ,
|
||||
64 , 80 , 96 , 112,
|
||||
512, 576, 640, 704,
|
||||
768, 832, 896, 960
|
||||
};
|
||||
int i, size = ARRAY_SIZE(startup_lookup);
|
||||
unsigned int ticks;
|
||||
|
||||
ticks = startup_time * adc_clk_khz / 1000;
|
||||
for (i = 0; i < size; i++)
|
||||
if (ticks < startup_lookup[i])
|
||||
break;
|
||||
|
||||
ticks = i;
|
||||
if (ticks == size)
|
||||
/* Reach the end of lookup table */
|
||||
ticks = size - 1;
|
||||
|
||||
return ticks;
|
||||
}
|
||||
|
||||
static const struct of_device_id at91_adc_dt_ids[];
|
||||
|
||||
static int at91_adc_probe_dt_ts(struct device_node *node,
|
||||
struct at91_adc_state *st, struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
u32 prop;
|
||||
|
||||
ret = of_property_read_u32(node, "atmel,adc-ts-wires", &prop);
|
||||
if (ret) {
|
||||
dev_info(dev, "ADC Touch screen is disabled.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (prop) {
|
||||
case 4:
|
||||
case 5:
|
||||
st->touchscreen_type = prop;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "Unsupported number of touchscreen wires (%d). Should be 4 or 5.\n", prop);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
prop = 0;
|
||||
of_property_read_u32(node, "atmel,adc-ts-pressure-threshold", &prop);
|
||||
st->ts_pressure_threshold = prop;
|
||||
if (st->ts_pressure_threshold) {
|
||||
return 0;
|
||||
} else {
|
||||
dev_err(dev, "Invalid pressure threshold for the touchscreen\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int at91_adc_probe_dt(struct at91_adc_state *st,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
|
@ -460,13 +688,6 @@ static int at91_adc_probe_dt(struct at91_adc_state *st,
|
|||
}
|
||||
st->channels_mask = prop;
|
||||
|
||||
if (of_property_read_u32(node, "atmel,adc-num-channels", &prop)) {
|
||||
dev_err(&idev->dev, "Missing adc-num-channels property in the DT.\n");
|
||||
ret = -EINVAL;
|
||||
goto error_ret;
|
||||
}
|
||||
st->num_channels = prop;
|
||||
|
||||
st->sleep_mode = of_property_read_bool(node, "atmel,adc-sleep-mode");
|
||||
|
||||
if (of_property_read_u32(node, "atmel,adc-startup-time", &prop)) {
|
||||
|
@ -492,6 +713,7 @@ static int at91_adc_probe_dt(struct at91_adc_state *st,
|
|||
goto error_ret;
|
||||
|
||||
st->registers = &st->caps->registers;
|
||||
st->num_channels = st->caps->num_channels;
|
||||
st->trigger_number = of_get_child_count(node);
|
||||
st->trigger_list = devm_kzalloc(&idev->dev, st->trigger_number *
|
||||
sizeof(struct at91_adc_trigger),
|
||||
|
@ -523,6 +745,12 @@ static int at91_adc_probe_dt(struct at91_adc_state *st,
|
|||
i++;
|
||||
}
|
||||
|
||||
/* Check if touchscreen is supported. */
|
||||
if (st->caps->has_ts)
|
||||
return at91_adc_probe_dt_ts(node, st, &idev->dev);
|
||||
else
|
||||
dev_info(&idev->dev, "not support touchscreen in the adc compatible string.\n");
|
||||
|
||||
return 0;
|
||||
|
||||
error_ret:
|
||||
|
@ -554,6 +782,114 @@ static const struct iio_info at91_adc_info = {
|
|||
.read_raw = &at91_adc_read_raw,
|
||||
};
|
||||
|
||||
/* Touchscreen related functions */
|
||||
static int atmel_ts_open(struct input_dev *dev)
|
||||
{
|
||||
struct at91_adc_state *st = input_get_drvdata(dev);
|
||||
|
||||
at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_PEN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void atmel_ts_close(struct input_dev *dev)
|
||||
{
|
||||
struct at91_adc_state *st = input_get_drvdata(dev);
|
||||
|
||||
at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_PEN);
|
||||
}
|
||||
|
||||
static int at91_ts_hw_init(struct at91_adc_state *st, u32 adc_clk_khz)
|
||||
{
|
||||
u32 reg = 0, pendbc;
|
||||
int i = 0;
|
||||
|
||||
if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_4WIRE)
|
||||
reg = AT91_ADC_TSMR_TSMODE_4WIRE_PRESS;
|
||||
else
|
||||
reg = AT91_ADC_TSMR_TSMODE_5WIRE;
|
||||
|
||||
/* a Pen Detect Debounce Time is necessary for the ADC Touch to avoid
|
||||
* pen detect noise.
|
||||
* The formula is : Pen Detect Debounce Time = (2 ^ pendbc) / ADCClock
|
||||
*/
|
||||
pendbc = round_up(TOUCH_PEN_DETECT_DEBOUNCE_US * adc_clk_khz / 1000, 1);
|
||||
|
||||
while (pendbc >> ++i)
|
||||
; /* Empty! Find the shift offset */
|
||||
if (abs(pendbc - (1 << i)) < abs(pendbc - (1 << (i - 1))))
|
||||
pendbc = i;
|
||||
else
|
||||
pendbc = i - 1;
|
||||
|
||||
if (st->caps->has_tsmr) {
|
||||
reg |= AT91_ADC_TSMR_TSAV_(st->caps->ts_filter_average)
|
||||
& AT91_ADC_TSMR_TSAV;
|
||||
reg |= AT91_ADC_TSMR_PENDBC_(pendbc) & AT91_ADC_TSMR_PENDBC;
|
||||
reg |= AT91_ADC_TSMR_NOTSDMA;
|
||||
reg |= AT91_ADC_TSMR_PENDET_ENA;
|
||||
reg |= 0x03 << 8; /* TSFREQ, need bigger than TSAV */
|
||||
|
||||
at91_adc_writel(st, AT91_ADC_TSMR, reg);
|
||||
} else {
|
||||
/* TODO: for 9g45 which has no TSMR */
|
||||
}
|
||||
|
||||
/* Change adc internal resistor value for better pen detection,
|
||||
* default value is 100 kOhm.
|
||||
* 0 = 200 kOhm, 1 = 150 kOhm, 2 = 100 kOhm, 3 = 50 kOhm
|
||||
* option only available on ES2 and higher
|
||||
*/
|
||||
at91_adc_writel(st, AT91_ADC_ACR, st->caps->ts_pen_detect_sensitivity
|
||||
& AT91_ADC_ACR_PENDETSENS);
|
||||
|
||||
/* Sample Peroid Time = (TRGPER + 1) / ADCClock */
|
||||
st->ts_sample_period_val = round_up((TOUCH_SAMPLE_PERIOD_US *
|
||||
adc_clk_khz / 1000) - 1, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at91_ts_register(struct at91_adc_state *st,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
struct input_dev *input;
|
||||
struct iio_dev *idev = iio_priv_to_dev(st);
|
||||
int ret;
|
||||
|
||||
input = input_allocate_device();
|
||||
if (!input) {
|
||||
dev_err(&idev->dev, "Failed to allocate TS device!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
input->name = DRIVER_NAME;
|
||||
input->id.bustype = BUS_HOST;
|
||||
input->dev.parent = &pdev->dev;
|
||||
input->open = atmel_ts_open;
|
||||
input->close = atmel_ts_close;
|
||||
|
||||
__set_bit(EV_ABS, input->evbit);
|
||||
__set_bit(EV_KEY, input->evbit);
|
||||
__set_bit(BTN_TOUCH, input->keybit);
|
||||
input_set_abs_params(input, ABS_X, 0, (1 << MAX_POS_BITS) - 1, 0, 0);
|
||||
input_set_abs_params(input, ABS_Y, 0, (1 << MAX_POS_BITS) - 1, 0, 0);
|
||||
input_set_abs_params(input, ABS_PRESSURE, 0, 0xffffff, 0, 0);
|
||||
|
||||
st->ts_input = input;
|
||||
input_set_drvdata(input, st);
|
||||
|
||||
ret = input_register_device(input);
|
||||
if (ret)
|
||||
input_free_device(st->ts_input);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void at91_ts_unregister(struct at91_adc_state *st)
|
||||
{
|
||||
input_unregister_device(st->ts_input);
|
||||
}
|
||||
|
||||
static int at91_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
unsigned int prsc, mstrclk, ticks, adc_clk, adc_clk_khz, shtim;
|
||||
|
@ -605,7 +941,7 @@ static int at91_adc_probe(struct platform_device *pdev)
|
|||
at91_adc_writel(st, AT91_ADC_CR, AT91_ADC_SWRST);
|
||||
at91_adc_writel(st, AT91_ADC_IDR, 0xFFFFFFFF);
|
||||
ret = request_irq(st->irq,
|
||||
at91_adc_eoc_trigger,
|
||||
at91_adc_interrupt,
|
||||
0,
|
||||
pdev->dev.driver->name,
|
||||
idev);
|
||||
|
@ -650,6 +986,10 @@ static int at91_adc_probe(struct platform_device *pdev)
|
|||
mstrclk = clk_get_rate(st->clk);
|
||||
adc_clk = clk_get_rate(st->adc_clk);
|
||||
adc_clk_khz = adc_clk / 1000;
|
||||
|
||||
dev_dbg(&pdev->dev, "Master clock is set as: %d Hz, adc_clk should set as: %d Hz\n",
|
||||
mstrclk, adc_clk);
|
||||
|
||||
prsc = (mstrclk / (2 * adc_clk)) - 1;
|
||||
|
||||
if (!st->startup_time) {
|
||||
|
@ -657,14 +997,8 @@ static int at91_adc_probe(struct platform_device *pdev)
|
|||
ret = -EINVAL;
|
||||
goto error_disable_adc_clk;
|
||||
}
|
||||
ticks = (*st->caps->calc_startup_ticks)(st->startup_time, adc_clk_khz);
|
||||
|
||||
/*
|
||||
* Number of ticks needed to cover the startup time of the ADC as
|
||||
* defined in the electrical characteristics of the board, divided by 8.
|
||||
* The formula thus is : Startup Time = (ticks + 1) * 8 / ADC Clock
|
||||
*/
|
||||
ticks = round_up((st->startup_time * adc_clk_khz /
|
||||
1000) - 1, 8) / 8;
|
||||
/*
|
||||
* a minimal Sample and Hold Time is necessary for the ADC to guarantee
|
||||
* the best converted final value between two channels selection
|
||||
|
@ -692,30 +1026,52 @@ static int at91_adc_probe(struct platform_device *pdev)
|
|||
init_waitqueue_head(&st->wq_data_avail);
|
||||
mutex_init(&st->lock);
|
||||
|
||||
ret = at91_adc_buffer_init(idev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Couldn't initialize the buffer.\n");
|
||||
goto error_disable_adc_clk;
|
||||
}
|
||||
/*
|
||||
* Since touch screen will set trigger register as period trigger. So
|
||||
* when touch screen is enabled, then we have to disable hardware
|
||||
* trigger for classic adc.
|
||||
*/
|
||||
if (!st->touchscreen_type) {
|
||||
ret = at91_adc_buffer_init(idev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Couldn't initialize the buffer.\n");
|
||||
goto error_disable_adc_clk;
|
||||
}
|
||||
|
||||
ret = at91_adc_trigger_init(idev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Couldn't setup the triggers.\n");
|
||||
goto error_unregister_buffer;
|
||||
ret = at91_adc_trigger_init(idev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Couldn't setup the triggers.\n");
|
||||
at91_adc_buffer_remove(idev);
|
||||
goto error_disable_adc_clk;
|
||||
}
|
||||
} else {
|
||||
if (!st->caps->has_tsmr) {
|
||||
dev_err(&pdev->dev, "We don't support non-TSMR adc\n");
|
||||
goto error_disable_adc_clk;
|
||||
}
|
||||
|
||||
ret = at91_ts_register(st, pdev);
|
||||
if (ret)
|
||||
goto error_disable_adc_clk;
|
||||
|
||||
at91_ts_hw_init(st, adc_clk_khz);
|
||||
}
|
||||
|
||||
ret = iio_device_register(idev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Couldn't register the device.\n");
|
||||
goto error_remove_triggers;
|
||||
goto error_iio_device_register;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_remove_triggers:
|
||||
at91_adc_trigger_remove(idev);
|
||||
error_unregister_buffer:
|
||||
at91_adc_buffer_remove(idev);
|
||||
error_iio_device_register:
|
||||
if (!st->touchscreen_type) {
|
||||
at91_adc_trigger_remove(idev);
|
||||
at91_adc_buffer_remove(idev);
|
||||
} else {
|
||||
at91_ts_unregister(st);
|
||||
}
|
||||
error_disable_adc_clk:
|
||||
clk_disable_unprepare(st->adc_clk);
|
||||
error_disable_clk:
|
||||
|
@ -731,8 +1087,12 @@ static int at91_adc_remove(struct platform_device *pdev)
|
|||
struct at91_adc_state *st = iio_priv(idev);
|
||||
|
||||
iio_device_unregister(idev);
|
||||
at91_adc_trigger_remove(idev);
|
||||
at91_adc_buffer_remove(idev);
|
||||
if (!st->touchscreen_type) {
|
||||
at91_adc_trigger_remove(idev);
|
||||
at91_adc_buffer_remove(idev);
|
||||
} else {
|
||||
at91_ts_unregister(st);
|
||||
}
|
||||
clk_disable_unprepare(st->adc_clk);
|
||||
clk_disable_unprepare(st->clk);
|
||||
free_irq(st->irq, idev);
|
||||
|
@ -742,6 +1102,8 @@ static int at91_adc_remove(struct platform_device *pdev)
|
|||
|
||||
#ifdef CONFIG_OF
|
||||
static struct at91_adc_caps at91sam9260_caps = {
|
||||
.calc_startup_ticks = calc_startup_ticks_9260,
|
||||
.num_channels = 4,
|
||||
.registers = {
|
||||
.channel_base = AT91_ADC_CHR(0),
|
||||
.drdy_mask = AT91_ADC_DRDY,
|
||||
|
@ -753,6 +1115,9 @@ static struct at91_adc_caps at91sam9260_caps = {
|
|||
};
|
||||
|
||||
static struct at91_adc_caps at91sam9g45_caps = {
|
||||
.has_ts = true,
|
||||
.calc_startup_ticks = calc_startup_ticks_9260, /* same as 9260 */
|
||||
.num_channels = 8,
|
||||
.registers = {
|
||||
.channel_base = AT91_ADC_CHR(0),
|
||||
.drdy_mask = AT91_ADC_DRDY,
|
||||
|
@ -764,6 +1129,12 @@ static struct at91_adc_caps at91sam9g45_caps = {
|
|||
};
|
||||
|
||||
static struct at91_adc_caps at91sam9x5_caps = {
|
||||
.has_ts = true,
|
||||
.has_tsmr = true,
|
||||
.ts_filter_average = 3,
|
||||
.ts_pen_detect_sensitivity = 2,
|
||||
.calc_startup_ticks = calc_startup_ticks_9x5,
|
||||
.num_channels = 12,
|
||||
.registers = {
|
||||
.channel_base = AT91_ADC_CDR0_9X5,
|
||||
.drdy_mask = AT91_ADC_SR_DRDY_9X5,
|
||||
|
@ -788,7 +1159,7 @@ static struct platform_driver at91_adc_driver = {
|
|||
.probe = at91_adc_probe,
|
||||
.remove = at91_adc_remove,
|
||||
.driver = {
|
||||
.name = "at91_adc",
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = of_match_ptr(at91_adc_dt_ids),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -165,6 +165,8 @@ struct max1363_chip_info {
|
|||
* @thresh_low: low threshold values
|
||||
* @vref: Reference voltage regulator
|
||||
* @vref_uv: Actual (external or internal) reference voltage
|
||||
* @send: function used to send data to the chip
|
||||
* @recv: function used to receive data from the chip
|
||||
*/
|
||||
struct max1363_state {
|
||||
struct i2c_client *client;
|
||||
|
@ -186,6 +188,10 @@ struct max1363_state {
|
|||
s16 thresh_low[8];
|
||||
struct regulator *vref;
|
||||
u32 vref_uv;
|
||||
int (*send)(const struct i2c_client *client,
|
||||
const char *buf, int count);
|
||||
int (*recv)(const struct i2c_client *client,
|
||||
char *buf, int count);
|
||||
};
|
||||
|
||||
#define MAX1363_MODE_SINGLE(_num, _mask) { \
|
||||
|
@ -311,13 +317,37 @@ static const struct max1363_mode
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static int max1363_write_basic_config(struct i2c_client *client,
|
||||
unsigned char d1,
|
||||
unsigned char d2)
|
||||
static int max1363_smbus_send(const struct i2c_client *client, const char *buf,
|
||||
int count)
|
||||
{
|
||||
u8 tx_buf[2] = {d1, d2};
|
||||
int i, err;
|
||||
|
||||
return i2c_master_send(client, tx_buf, 2);
|
||||
for (i = err = 0; err == 0 && i < count; ++i)
|
||||
err = i2c_smbus_write_byte(client, buf[i]);
|
||||
|
||||
return err ? err : count;
|
||||
}
|
||||
|
||||
static int max1363_smbus_recv(const struct i2c_client *client, char *buf,
|
||||
int count)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
ret = i2c_smbus_read_byte(client);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
buf[i] = ret;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int max1363_write_basic_config(struct max1363_state *st)
|
||||
{
|
||||
u8 tx_buf[2] = { st->setupbyte, st->configbyte };
|
||||
|
||||
return st->send(st->client, tx_buf, 2);
|
||||
}
|
||||
|
||||
static int max1363_set_scan_mode(struct max1363_state *st)
|
||||
|
@ -327,9 +357,7 @@ static int max1363_set_scan_mode(struct max1363_state *st)
|
|||
| MAX1363_SE_DE_MASK);
|
||||
st->configbyte |= st->current_mode->conf;
|
||||
|
||||
return max1363_write_basic_config(st->client,
|
||||
st->setupbyte,
|
||||
st->configbyte);
|
||||
return max1363_write_basic_config(st);
|
||||
}
|
||||
|
||||
static int max1363_read_single_chan(struct iio_dev *indio_dev,
|
||||
|
@ -366,7 +394,7 @@ static int max1363_read_single_chan(struct iio_dev *indio_dev,
|
|||
}
|
||||
if (st->chip_info->bits != 8) {
|
||||
/* Get reading */
|
||||
data = i2c_master_recv(client, rxbuf, 2);
|
||||
data = st->recv(client, rxbuf, 2);
|
||||
if (data < 0) {
|
||||
ret = data;
|
||||
goto error_ret;
|
||||
|
@ -375,7 +403,7 @@ static int max1363_read_single_chan(struct iio_dev *indio_dev,
|
|||
((1 << st->chip_info->bits) - 1);
|
||||
} else {
|
||||
/* Get reading */
|
||||
data = i2c_master_recv(client, rxbuf, 1);
|
||||
data = st->recv(client, rxbuf, 1);
|
||||
if (data < 0) {
|
||||
ret = data;
|
||||
goto error_ret;
|
||||
|
@ -397,7 +425,6 @@ static int max1363_read_raw(struct iio_dev *indio_dev,
|
|||
{
|
||||
struct max1363_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
unsigned long scale_uv;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
|
@ -406,10 +433,9 @@ static int max1363_read_raw(struct iio_dev *indio_dev,
|
|||
return ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
scale_uv = st->vref_uv >> st->chip_info->bits;
|
||||
*val = scale_uv / 1000;
|
||||
*val2 = (scale_uv % 1000) * 1000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
*val = st->vref_uv / 1000;
|
||||
*val2 = st->chip_info->bits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -424,11 +450,21 @@ static const enum max1363_modes max1363_mode_list[] = {
|
|||
d0m1to2m3, d1m0to3m2,
|
||||
};
|
||||
|
||||
#define MAX1363_EV_M \
|
||||
(IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING) \
|
||||
| IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING))
|
||||
static const struct iio_event_spec max1363_events[] = {
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_RISING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
||||
BIT(IIO_EV_INFO_ENABLE),
|
||||
}, {
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_FALLING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
||||
BIT(IIO_EV_INFO_ENABLE),
|
||||
},
|
||||
};
|
||||
|
||||
#define MAX1363_CHAN_U(num, addr, si, bits, evmask) \
|
||||
#define MAX1363_CHAN_U(num, addr, si, bits, ev_spec, num_ev_spec) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
|
@ -444,11 +480,12 @@ static const enum max1363_modes max1363_mode_list[] = {
|
|||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
.scan_index = si, \
|
||||
.event_mask = evmask, \
|
||||
.event_spec = ev_spec, \
|
||||
.num_event_specs = num_ev_spec, \
|
||||
}
|
||||
|
||||
/* bipolar channel */
|
||||
#define MAX1363_CHAN_B(num, num2, addr, si, bits, evmask) \
|
||||
#define MAX1363_CHAN_B(num, num2, addr, si, bits, ev_spec, num_ev_spec) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.differential = 1, \
|
||||
|
@ -466,28 +503,32 @@ static const enum max1363_modes max1363_mode_list[] = {
|
|||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
.scan_index = si, \
|
||||
.event_mask = evmask, \
|
||||
.event_spec = ev_spec, \
|
||||
.num_event_specs = num_ev_spec, \
|
||||
}
|
||||
|
||||
#define MAX1363_4X_CHANS(bits, em) { \
|
||||
MAX1363_CHAN_U(0, _s0, 0, bits, em), \
|
||||
MAX1363_CHAN_U(1, _s1, 1, bits, em), \
|
||||
MAX1363_CHAN_U(2, _s2, 2, bits, em), \
|
||||
MAX1363_CHAN_U(3, _s3, 3, bits, em), \
|
||||
MAX1363_CHAN_B(0, 1, d0m1, 4, bits, em), \
|
||||
MAX1363_CHAN_B(2, 3, d2m3, 5, bits, em), \
|
||||
MAX1363_CHAN_B(1, 0, d1m0, 6, bits, em), \
|
||||
MAX1363_CHAN_B(3, 2, d3m2, 7, bits, em), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(8) \
|
||||
#define MAX1363_4X_CHANS(bits, ev_spec, num_ev_spec) { \
|
||||
MAX1363_CHAN_U(0, _s0, 0, bits, ev_spec, num_ev_spec), \
|
||||
MAX1363_CHAN_U(1, _s1, 1, bits, ev_spec, num_ev_spec), \
|
||||
MAX1363_CHAN_U(2, _s2, 2, bits, ev_spec, num_ev_spec), \
|
||||
MAX1363_CHAN_U(3, _s3, 3, bits, ev_spec, num_ev_spec), \
|
||||
MAX1363_CHAN_B(0, 1, d0m1, 4, bits, ev_spec, num_ev_spec), \
|
||||
MAX1363_CHAN_B(2, 3, d2m3, 5, bits, ev_spec, num_ev_spec), \
|
||||
MAX1363_CHAN_B(1, 0, d1m0, 6, bits, ev_spec, num_ev_spec), \
|
||||
MAX1363_CHAN_B(3, 2, d3m2, 7, bits, ev_spec, num_ev_spec), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(8) \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec max1036_channels[] = MAX1363_4X_CHANS(8, 0);
|
||||
static const struct iio_chan_spec max1136_channels[] = MAX1363_4X_CHANS(10, 0);
|
||||
static const struct iio_chan_spec max1236_channels[] = MAX1363_4X_CHANS(12, 0);
|
||||
static const struct iio_chan_spec max1036_channels[] =
|
||||
MAX1363_4X_CHANS(8, NULL, 0);
|
||||
static const struct iio_chan_spec max1136_channels[] =
|
||||
MAX1363_4X_CHANS(10, NULL, 0);
|
||||
static const struct iio_chan_spec max1236_channels[] =
|
||||
MAX1363_4X_CHANS(12, NULL, 0);
|
||||
static const struct iio_chan_spec max1361_channels[] =
|
||||
MAX1363_4X_CHANS(10, MAX1363_EV_M);
|
||||
MAX1363_4X_CHANS(10, max1363_events, ARRAY_SIZE(max1363_events));
|
||||
static const struct iio_chan_spec max1363_channels[] =
|
||||
MAX1363_4X_CHANS(12, MAX1363_EV_M);
|
||||
MAX1363_4X_CHANS(12, max1363_events, ARRAY_SIZE(max1363_events));
|
||||
|
||||
/* Applies to max1236, max1237 */
|
||||
static const enum max1363_modes max1236_mode_list[] = {
|
||||
|
@ -511,32 +552,32 @@ static const enum max1363_modes max1238_mode_list[] = {
|
|||
d6m7to8m9, d6m7to10m11, d7m6to9m8, d7m6to11m10,
|
||||
};
|
||||
|
||||
#define MAX1363_12X_CHANS(bits) { \
|
||||
MAX1363_CHAN_U(0, _s0, 0, bits, 0), \
|
||||
MAX1363_CHAN_U(1, _s1, 1, bits, 0), \
|
||||
MAX1363_CHAN_U(2, _s2, 2, bits, 0), \
|
||||
MAX1363_CHAN_U(3, _s3, 3, bits, 0), \
|
||||
MAX1363_CHAN_U(4, _s4, 4, bits, 0), \
|
||||
MAX1363_CHAN_U(5, _s5, 5, bits, 0), \
|
||||
MAX1363_CHAN_U(6, _s6, 6, bits, 0), \
|
||||
MAX1363_CHAN_U(7, _s7, 7, bits, 0), \
|
||||
MAX1363_CHAN_U(8, _s8, 8, bits, 0), \
|
||||
MAX1363_CHAN_U(9, _s9, 9, bits, 0), \
|
||||
MAX1363_CHAN_U(10, _s10, 10, bits, 0), \
|
||||
MAX1363_CHAN_U(11, _s11, 11, bits, 0), \
|
||||
MAX1363_CHAN_B(0, 1, d0m1, 12, bits, 0), \
|
||||
MAX1363_CHAN_B(2, 3, d2m3, 13, bits, 0), \
|
||||
MAX1363_CHAN_B(4, 5, d4m5, 14, bits, 0), \
|
||||
MAX1363_CHAN_B(6, 7, d6m7, 15, bits, 0), \
|
||||
MAX1363_CHAN_B(8, 9, d8m9, 16, bits, 0), \
|
||||
MAX1363_CHAN_B(10, 11, d10m11, 17, bits, 0), \
|
||||
MAX1363_CHAN_B(1, 0, d1m0, 18, bits, 0), \
|
||||
MAX1363_CHAN_B(3, 2, d3m2, 19, bits, 0), \
|
||||
MAX1363_CHAN_B(5, 4, d5m4, 20, bits, 0), \
|
||||
MAX1363_CHAN_B(7, 6, d7m6, 21, bits, 0), \
|
||||
MAX1363_CHAN_B(9, 8, d9m8, 22, bits, 0), \
|
||||
MAX1363_CHAN_B(11, 10, d11m10, 23, bits, 0), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(24) \
|
||||
#define MAX1363_12X_CHANS(bits) { \
|
||||
MAX1363_CHAN_U(0, _s0, 0, bits, NULL, 0), \
|
||||
MAX1363_CHAN_U(1, _s1, 1, bits, NULL, 0), \
|
||||
MAX1363_CHAN_U(2, _s2, 2, bits, NULL, 0), \
|
||||
MAX1363_CHAN_U(3, _s3, 3, bits, NULL, 0), \
|
||||
MAX1363_CHAN_U(4, _s4, 4, bits, NULL, 0), \
|
||||
MAX1363_CHAN_U(5, _s5, 5, bits, NULL, 0), \
|
||||
MAX1363_CHAN_U(6, _s6, 6, bits, NULL, 0), \
|
||||
MAX1363_CHAN_U(7, _s7, 7, bits, NULL, 0), \
|
||||
MAX1363_CHAN_U(8, _s8, 8, bits, NULL, 0), \
|
||||
MAX1363_CHAN_U(9, _s9, 9, bits, NULL, 0), \
|
||||
MAX1363_CHAN_U(10, _s10, 10, bits, NULL, 0), \
|
||||
MAX1363_CHAN_U(11, _s11, 11, bits, NULL, 0), \
|
||||
MAX1363_CHAN_B(0, 1, d0m1, 12, bits, NULL, 0), \
|
||||
MAX1363_CHAN_B(2, 3, d2m3, 13, bits, NULL, 0), \
|
||||
MAX1363_CHAN_B(4, 5, d4m5, 14, bits, NULL, 0), \
|
||||
MAX1363_CHAN_B(6, 7, d6m7, 15, bits, NULL, 0), \
|
||||
MAX1363_CHAN_B(8, 9, d8m9, 16, bits, NULL, 0), \
|
||||
MAX1363_CHAN_B(10, 11, d10m11, 17, bits, NULL, 0), \
|
||||
MAX1363_CHAN_B(1, 0, d1m0, 18, bits, NULL, 0), \
|
||||
MAX1363_CHAN_B(3, 2, d3m2, 19, bits, NULL, 0), \
|
||||
MAX1363_CHAN_B(5, 4, d5m4, 20, bits, NULL, 0), \
|
||||
MAX1363_CHAN_B(7, 6, d7m6, 21, bits, NULL, 0), \
|
||||
MAX1363_CHAN_B(9, 8, d9m8, 22, bits, NULL, 0), \
|
||||
MAX1363_CHAN_B(11, 10, d11m10, 23, bits, NULL, 0), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(24) \
|
||||
}
|
||||
static const struct iio_chan_spec max1038_channels[] = MAX1363_12X_CHANS(8);
|
||||
static const struct iio_chan_spec max1138_channels[] = MAX1363_12X_CHANS(10);
|
||||
|
@ -561,22 +602,22 @@ static const enum max1363_modes max11608_mode_list[] = {
|
|||
};
|
||||
|
||||
#define MAX1363_8X_CHANS(bits) { \
|
||||
MAX1363_CHAN_U(0, _s0, 0, bits, 0), \
|
||||
MAX1363_CHAN_U(1, _s1, 1, bits, 0), \
|
||||
MAX1363_CHAN_U(2, _s2, 2, bits, 0), \
|
||||
MAX1363_CHAN_U(3, _s3, 3, bits, 0), \
|
||||
MAX1363_CHAN_U(4, _s4, 4, bits, 0), \
|
||||
MAX1363_CHAN_U(5, _s5, 5, bits, 0), \
|
||||
MAX1363_CHAN_U(6, _s6, 6, bits, 0), \
|
||||
MAX1363_CHAN_U(7, _s7, 7, bits, 0), \
|
||||
MAX1363_CHAN_B(0, 1, d0m1, 8, bits, 0), \
|
||||
MAX1363_CHAN_B(2, 3, d2m3, 9, bits, 0), \
|
||||
MAX1363_CHAN_B(4, 5, d4m5, 10, bits, 0), \
|
||||
MAX1363_CHAN_B(6, 7, d6m7, 11, bits, 0), \
|
||||
MAX1363_CHAN_B(1, 0, d1m0, 12, bits, 0), \
|
||||
MAX1363_CHAN_B(3, 2, d3m2, 13, bits, 0), \
|
||||
MAX1363_CHAN_B(5, 4, d5m4, 14, bits, 0), \
|
||||
MAX1363_CHAN_B(7, 6, d7m6, 15, bits, 0), \
|
||||
MAX1363_CHAN_U(0, _s0, 0, bits, NULL, 0), \
|
||||
MAX1363_CHAN_U(1, _s1, 1, bits, NULL, 0), \
|
||||
MAX1363_CHAN_U(2, _s2, 2, bits, NULL, 0), \
|
||||
MAX1363_CHAN_U(3, _s3, 3, bits, NULL, 0), \
|
||||
MAX1363_CHAN_U(4, _s4, 4, bits, NULL, 0), \
|
||||
MAX1363_CHAN_U(5, _s5, 5, bits, NULL, 0), \
|
||||
MAX1363_CHAN_U(6, _s6, 6, bits, NULL, 0), \
|
||||
MAX1363_CHAN_U(7, _s7, 7, bits, NULL, 0), \
|
||||
MAX1363_CHAN_B(0, 1, d0m1, 8, bits, NULL, 0), \
|
||||
MAX1363_CHAN_B(2, 3, d2m3, 9, bits, NULL, 0), \
|
||||
MAX1363_CHAN_B(4, 5, d4m5, 10, bits, NULL, 0), \
|
||||
MAX1363_CHAN_B(6, 7, d6m7, 11, bits, NULL, 0), \
|
||||
MAX1363_CHAN_B(1, 0, d1m0, 12, bits, NULL, 0), \
|
||||
MAX1363_CHAN_B(3, 2, d3m2, 13, bits, NULL, 0), \
|
||||
MAX1363_CHAN_B(5, 4, d5m4, 14, bits, NULL, 0), \
|
||||
MAX1363_CHAN_B(7, 6, d7m6, 15, bits, NULL, 0), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(16) \
|
||||
}
|
||||
static const struct iio_chan_spec max11602_channels[] = MAX1363_8X_CHANS(8);
|
||||
|
@ -588,10 +629,10 @@ static const enum max1363_modes max11644_mode_list[] = {
|
|||
};
|
||||
|
||||
#define MAX1363_2X_CHANS(bits) { \
|
||||
MAX1363_CHAN_U(0, _s0, 0, bits, 0), \
|
||||
MAX1363_CHAN_U(1, _s1, 1, bits, 0), \
|
||||
MAX1363_CHAN_B(0, 1, d0m1, 2, bits, 0), \
|
||||
MAX1363_CHAN_B(1, 0, d1m0, 3, bits, 0), \
|
||||
MAX1363_CHAN_U(0, _s0, 0, bits, NULL, 0), \
|
||||
MAX1363_CHAN_U(1, _s1, 1, bits, NULL, 0), \
|
||||
MAX1363_CHAN_B(0, 1, d0m1, 2, bits, NULL, 0), \
|
||||
MAX1363_CHAN_B(1, 0, d1m0, 3, bits, NULL, 0), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4) \
|
||||
}
|
||||
|
||||
|
@ -686,20 +727,22 @@ static IIO_CONST_ATTR(sampling_frequency_available,
|
|||
"133000 665000 33300 16600 8300 4200 2000 1000");
|
||||
|
||||
static int max1363_read_thresh(struct iio_dev *indio_dev,
|
||||
u64 event_code,
|
||||
int *val)
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir, enum iio_event_info info, int *val,
|
||||
int *val2)
|
||||
{
|
||||
struct max1363_state *st = iio_priv(indio_dev);
|
||||
if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_FALLING)
|
||||
*val = st->thresh_low[IIO_EVENT_CODE_EXTRACT_CHAN(event_code)];
|
||||
if (dir == IIO_EV_DIR_FALLING)
|
||||
*val = st->thresh_low[chan->channel];
|
||||
else
|
||||
*val = st->thresh_high[IIO_EVENT_CODE_EXTRACT_CHAN(event_code)];
|
||||
return 0;
|
||||
*val = st->thresh_high[chan->channel];
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int max1363_write_thresh(struct iio_dev *indio_dev,
|
||||
u64 event_code,
|
||||
int val)
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir, enum iio_event_info info, int val,
|
||||
int val2)
|
||||
{
|
||||
struct max1363_state *st = iio_priv(indio_dev);
|
||||
/* make it handle signed correctly as well */
|
||||
|
@ -714,13 +757,15 @@ static int max1363_write_thresh(struct iio_dev *indio_dev,
|
|||
break;
|
||||
}
|
||||
|
||||
switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
|
||||
switch (dir) {
|
||||
case IIO_EV_DIR_FALLING:
|
||||
st->thresh_low[IIO_EVENT_CODE_EXTRACT_CHAN(event_code)] = val;
|
||||
st->thresh_low[chan->channel] = val;
|
||||
break;
|
||||
case IIO_EV_DIR_RISING:
|
||||
st->thresh_high[IIO_EVENT_CODE_EXTRACT_CHAN(event_code)] = val;
|
||||
st->thresh_high[chan->channel] = val;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -755,24 +800,25 @@ static irqreturn_t max1363_event_handler(int irq, void *private)
|
|||
u8 tx[2] = { st->setupbyte,
|
||||
MAX1363_MON_INT_ENABLE | (st->monitor_speed << 1) | 0xF0 };
|
||||
|
||||
i2c_master_recv(st->client, &rx, 1);
|
||||
st->recv(st->client, &rx, 1);
|
||||
mask = rx;
|
||||
for_each_set_bit(loc, &mask, 8)
|
||||
iio_push_event(indio_dev, max1363_event_codes[loc], timestamp);
|
||||
i2c_master_send(st->client, tx, 2);
|
||||
st->send(st->client, tx, 2);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int max1363_read_event_config(struct iio_dev *indio_dev,
|
||||
u64 event_code)
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir)
|
||||
{
|
||||
struct max1363_state *st = iio_priv(indio_dev);
|
||||
int val;
|
||||
int number = IIO_EVENT_CODE_EXTRACT_CHAN(event_code);
|
||||
int number = chan->channel;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_FALLING)
|
||||
if (dir == IIO_EV_DIR_FALLING)
|
||||
val = (1 << number) & st->mask_low;
|
||||
else
|
||||
val = (1 << number) & st->mask_high;
|
||||
|
@ -794,9 +840,7 @@ static int max1363_monitor_mode_update(struct max1363_state *st, int enabled)
|
|||
st->setupbyte &= ~MAX1363_SETUP_MONITOR_SETUP;
|
||||
st->configbyte &= ~MAX1363_SCAN_MASK;
|
||||
st->monitor_on = false;
|
||||
return max1363_write_basic_config(st->client,
|
||||
st->setupbyte,
|
||||
st->configbyte);
|
||||
return max1363_write_basic_config(st);
|
||||
}
|
||||
|
||||
/* Ensure we are in the relevant mode */
|
||||
|
@ -858,7 +902,7 @@ static int max1363_monitor_mode_update(struct max1363_state *st, int enabled)
|
|||
}
|
||||
|
||||
|
||||
ret = i2c_master_send(st->client, tx_buf, len);
|
||||
ret = st->send(st->client, tx_buf, len);
|
||||
if (ret < 0)
|
||||
goto error_ret;
|
||||
if (ret != len) {
|
||||
|
@ -875,7 +919,7 @@ static int max1363_monitor_mode_update(struct max1363_state *st, int enabled)
|
|||
*/
|
||||
tx_buf[0] = st->setupbyte;
|
||||
tx_buf[1] = MAX1363_MON_INT_ENABLE | (st->monitor_speed << 1) | 0xF0;
|
||||
ret = i2c_master_send(st->client, tx_buf, 2);
|
||||
ret = st->send(st->client, tx_buf, 2);
|
||||
if (ret < 0)
|
||||
goto error_ret;
|
||||
if (ret != 2) {
|
||||
|
@ -917,17 +961,17 @@ error_ret:
|
|||
}
|
||||
|
||||
static int max1363_write_event_config(struct iio_dev *indio_dev,
|
||||
u64 event_code,
|
||||
int state)
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir, int state)
|
||||
{
|
||||
int ret = 0;
|
||||
struct max1363_state *st = iio_priv(indio_dev);
|
||||
u16 unifiedmask;
|
||||
int number = IIO_EVENT_CODE_EXTRACT_CHAN(event_code);
|
||||
int number = chan->channel;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
unifiedmask = st->mask_low | st->mask_high;
|
||||
if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_FALLING) {
|
||||
if (dir == IIO_EV_DIR_FALLING) {
|
||||
|
||||
if (state == 0)
|
||||
st->mask_low &= ~(1 << number);
|
||||
|
@ -995,10 +1039,10 @@ static const struct iio_info max1238_info = {
|
|||
};
|
||||
|
||||
static const struct iio_info max1363_info = {
|
||||
.read_event_value = &max1363_read_thresh,
|
||||
.write_event_value = &max1363_write_thresh,
|
||||
.read_event_config = &max1363_read_event_config,
|
||||
.write_event_config = &max1363_write_event_config,
|
||||
.read_event_value_new = &max1363_read_thresh,
|
||||
.write_event_value_new = &max1363_write_thresh,
|
||||
.read_event_config_new = &max1363_read_event_config,
|
||||
.write_event_config_new = &max1363_write_event_config,
|
||||
.read_raw = &max1363_read_raw,
|
||||
.update_scan_mode = &max1363_update_scan_mode,
|
||||
.driver_module = THIS_MODULE,
|
||||
|
@ -1436,7 +1480,6 @@ static irqreturn_t max1363_trigger_handler(int irq, void *p)
|
|||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct max1363_state *st = iio_priv(indio_dev);
|
||||
s64 time_ns;
|
||||
__u8 *rxbuf;
|
||||
int b_sent;
|
||||
size_t d_size;
|
||||
|
@ -1464,17 +1507,13 @@ static irqreturn_t max1363_trigger_handler(int irq, void *p)
|
|||
if (rxbuf == NULL)
|
||||
goto done;
|
||||
if (st->chip_info->bits != 8)
|
||||
b_sent = i2c_master_recv(st->client, rxbuf, numvals*2);
|
||||
b_sent = st->recv(st->client, rxbuf, numvals * 2);
|
||||
else
|
||||
b_sent = i2c_master_recv(st->client, rxbuf, numvals);
|
||||
b_sent = st->recv(st->client, rxbuf, numvals);
|
||||
if (b_sent < 0)
|
||||
goto done_free;
|
||||
|
||||
time_ns = iio_get_time_ns();
|
||||
|
||||
if (indio_dev->scan_timestamp)
|
||||
memcpy(rxbuf + d_size - sizeof(s64), &time_ns, sizeof(time_ns));
|
||||
iio_push_to_buffers(indio_dev, rxbuf);
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, rxbuf, iio_get_time_ns());
|
||||
|
||||
done_free:
|
||||
kfree(rxbuf);
|
||||
|
@ -1484,12 +1523,6 @@ done:
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops max1363_buffered_setup_ops = {
|
||||
.postenable = &iio_triggered_buffer_postenable,
|
||||
.preenable = &iio_sw_buffer_preenable,
|
||||
.predisable = &iio_triggered_buffer_predisable,
|
||||
};
|
||||
|
||||
static int max1363_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
|
@ -1543,6 +1576,18 @@ static int max1363_probe(struct i2c_client *client,
|
|||
st->vref_uv = vref_uv;
|
||||
}
|
||||
|
||||
if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
||||
st->send = i2c_master_send;
|
||||
st->recv = i2c_master_recv;
|
||||
} else if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)
|
||||
&& st->chip_info->bits == 8) {
|
||||
st->send = max1363_smbus_send;
|
||||
st->recv = max1363_smbus_recv;
|
||||
} else {
|
||||
ret = -EOPNOTSUPP;
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
ret = max1363_alloc_scan_masks(indio_dev);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
@ -1559,7 +1604,7 @@ static int max1363_probe(struct i2c_client *client,
|
|||
goto error_disable_reg;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
&max1363_trigger_handler, &max1363_buffered_setup_ops);
|
||||
&max1363_trigger_handler, NULL);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
|
|
|
@ -0,0 +1,410 @@
|
|||
/*
|
||||
* mcp3422.c - driver for the Microchip mcp3422/3/4 chip family
|
||||
*
|
||||
* Copyright (C) 2013, Angelo Compagnucci
|
||||
* Author: Angelo Compagnucci <angelo.compagnucci@gmail.com>
|
||||
*
|
||||
* Datasheet: http://ww1.microchip.com/downloads/en/devicedoc/22088b.pdf
|
||||
*
|
||||
* This driver exports the value of analog input voltage to sysfs, the
|
||||
* voltage unit is nV.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
/* Masks */
|
||||
#define MCP3422_CHANNEL_MASK 0x60
|
||||
#define MCP3422_PGA_MASK 0x03
|
||||
#define MCP3422_SRATE_MASK 0x0C
|
||||
#define MCP3422_SRATE_240 0x0
|
||||
#define MCP3422_SRATE_60 0x1
|
||||
#define MCP3422_SRATE_15 0x2
|
||||
#define MCP3422_SRATE_3 0x3
|
||||
#define MCP3422_PGA_1 0
|
||||
#define MCP3422_PGA_2 1
|
||||
#define MCP3422_PGA_4 2
|
||||
#define MCP3422_PGA_8 3
|
||||
#define MCP3422_CONT_SAMPLING 0x10
|
||||
|
||||
#define MCP3422_CHANNEL(config) (((config) & MCP3422_CHANNEL_MASK) >> 5)
|
||||
#define MCP3422_PGA(config) ((config) & MCP3422_PGA_MASK)
|
||||
#define MCP3422_SAMPLE_RATE(config) (((config) & MCP3422_SRATE_MASK) >> 2)
|
||||
|
||||
#define MCP3422_CHANNEL_VALUE(value) (((value) << 5) & MCP3422_CHANNEL_MASK)
|
||||
#define MCP3422_PGA_VALUE(value) ((value) & MCP3422_PGA_MASK)
|
||||
#define MCP3422_SAMPLE_RATE_VALUE(value) ((value << 2) & MCP3422_SRATE_MASK)
|
||||
|
||||
#define MCP3422_CHAN(_index) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = _index, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \
|
||||
| BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
}
|
||||
|
||||
/* LSB is in nV to eliminate floating point */
|
||||
static const u32 rates_to_lsb[] = {1000000, 250000, 62500, 15625};
|
||||
|
||||
/*
|
||||
* scales calculated as:
|
||||
* rates_to_lsb[sample_rate] / (1 << pga);
|
||||
* pga is 1 for 0, 2
|
||||
*/
|
||||
|
||||
static const int mcp3422_scales[4][4] = {
|
||||
{ 1000000, 250000, 62500, 15625 },
|
||||
{ 500000 , 125000, 31250, 7812 },
|
||||
{ 250000 , 62500 , 15625, 3906 },
|
||||
{ 125000 , 31250 , 7812 , 1953 } };
|
||||
|
||||
/* Constant msleep times for data acquisitions */
|
||||
static const int mcp3422_read_times[4] = {
|
||||
[MCP3422_SRATE_240] = 1000 / 240,
|
||||
[MCP3422_SRATE_60] = 1000 / 60,
|
||||
[MCP3422_SRATE_15] = 1000 / 15,
|
||||
[MCP3422_SRATE_3] = 1000 / 3 };
|
||||
|
||||
/* sample rates to integer conversion table */
|
||||
static const int mcp3422_sample_rates[4] = {
|
||||
[MCP3422_SRATE_240] = 240,
|
||||
[MCP3422_SRATE_60] = 60,
|
||||
[MCP3422_SRATE_15] = 15,
|
||||
[MCP3422_SRATE_3] = 3 };
|
||||
|
||||
/* sample rates to sign extension table */
|
||||
static const int mcp3422_sign_extend[4] = {
|
||||
[MCP3422_SRATE_240] = 12,
|
||||
[MCP3422_SRATE_60] = 14,
|
||||
[MCP3422_SRATE_15] = 16,
|
||||
[MCP3422_SRATE_3] = 18 };
|
||||
|
||||
/* Client data (each client gets its own) */
|
||||
struct mcp3422 {
|
||||
struct i2c_client *i2c;
|
||||
u8 config;
|
||||
u8 pga[4];
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
static int mcp3422_update_config(struct mcp3422 *adc, u8 newconfig)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&adc->lock);
|
||||
|
||||
ret = i2c_master_send(adc->i2c, &newconfig, 1);
|
||||
if (ret > 0) {
|
||||
adc->config = newconfig;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
mutex_unlock(&adc->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mcp3422_read(struct mcp3422 *adc, int *value, u8 *config)
|
||||
{
|
||||
int ret = 0;
|
||||
u8 sample_rate = MCP3422_SAMPLE_RATE(adc->config);
|
||||
u8 buf[4] = {0, 0, 0, 0};
|
||||
u32 temp;
|
||||
|
||||
if (sample_rate == MCP3422_SRATE_3) {
|
||||
ret = i2c_master_recv(adc->i2c, buf, 4);
|
||||
temp = buf[0] << 16 | buf[1] << 8 | buf[2];
|
||||
*config = buf[3];
|
||||
} else {
|
||||
ret = i2c_master_recv(adc->i2c, buf, 3);
|
||||
temp = buf[0] << 8 | buf[1];
|
||||
*config = buf[2];
|
||||
}
|
||||
|
||||
*value = sign_extend32(temp, mcp3422_sign_extend[sample_rate]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mcp3422_read_channel(struct mcp3422 *adc,
|
||||
struct iio_chan_spec const *channel, int *value)
|
||||
{
|
||||
int ret;
|
||||
u8 config;
|
||||
u8 req_channel = channel->channel;
|
||||
|
||||
if (req_channel != MCP3422_CHANNEL(adc->config)) {
|
||||
config = adc->config;
|
||||
config &= ~MCP3422_CHANNEL_MASK;
|
||||
config |= MCP3422_CHANNEL_VALUE(req_channel);
|
||||
config &= ~MCP3422_PGA_MASK;
|
||||
config |= MCP3422_PGA_VALUE(adc->pga[req_channel]);
|
||||
ret = mcp3422_update_config(adc, config);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
msleep(mcp3422_read_times[MCP3422_SAMPLE_RATE(adc->config)]);
|
||||
}
|
||||
|
||||
return mcp3422_read(adc, value, &config);
|
||||
}
|
||||
|
||||
static int mcp3422_read_raw(struct iio_dev *iio,
|
||||
struct iio_chan_spec const *channel, int *val1,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct mcp3422 *adc = iio_priv(iio);
|
||||
int err;
|
||||
|
||||
u8 sample_rate = MCP3422_SAMPLE_RATE(adc->config);
|
||||
u8 pga = MCP3422_PGA(adc->config);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
err = mcp3422_read_channel(adc, channel, val1);
|
||||
if (err < 0)
|
||||
return -EINVAL;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
|
||||
*val1 = 0;
|
||||
*val2 = mcp3422_scales[sample_rate][pga];
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val1 = mcp3422_sample_rates[MCP3422_SAMPLE_RATE(adc->config)];
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int mcp3422_write_raw(struct iio_dev *iio,
|
||||
struct iio_chan_spec const *channel, int val1,
|
||||
int val2, long mask)
|
||||
{
|
||||
struct mcp3422 *adc = iio_priv(iio);
|
||||
u8 temp;
|
||||
u8 config = adc->config;
|
||||
u8 req_channel = channel->channel;
|
||||
u8 sample_rate = MCP3422_SAMPLE_RATE(config);
|
||||
u8 i;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (val1 != 0)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mcp3422_scales[0]); i++) {
|
||||
if (val2 == mcp3422_scales[sample_rate][i]) {
|
||||
adc->pga[req_channel] = i;
|
||||
|
||||
config &= ~MCP3422_CHANNEL_MASK;
|
||||
config |= MCP3422_CHANNEL_VALUE(req_channel);
|
||||
config &= ~MCP3422_PGA_MASK;
|
||||
config |= MCP3422_PGA_VALUE(adc->pga[req_channel]);
|
||||
|
||||
return mcp3422_update_config(adc, config);
|
||||
}
|
||||
}
|
||||
return -EINVAL;
|
||||
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
switch (val1) {
|
||||
case 240:
|
||||
temp = MCP3422_SRATE_240;
|
||||
break;
|
||||
case 60:
|
||||
temp = MCP3422_SRATE_60;
|
||||
break;
|
||||
case 15:
|
||||
temp = MCP3422_SRATE_15;
|
||||
break;
|
||||
case 3:
|
||||
temp = MCP3422_SRATE_3;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
config &= ~MCP3422_CHANNEL_MASK;
|
||||
config |= MCP3422_CHANNEL_VALUE(req_channel);
|
||||
config &= ~MCP3422_SRATE_MASK;
|
||||
config |= MCP3422_SAMPLE_RATE_VALUE(temp);
|
||||
|
||||
return mcp3422_update_config(adc, config);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int mcp3422_write_raw_get_fmt(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, long mask)
|
||||
{
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t mcp3422_show_scales(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct mcp3422 *adc = iio_priv(dev_to_iio_dev(dev));
|
||||
u8 sample_rate = MCP3422_SAMPLE_RATE(adc->config);
|
||||
|
||||
return sprintf(buf, "0.%09u 0.%09u 0.%09u 0.%09u\n",
|
||||
mcp3422_scales[sample_rate][0],
|
||||
mcp3422_scales[sample_rate][1],
|
||||
mcp3422_scales[sample_rate][2],
|
||||
mcp3422_scales[sample_rate][3]);
|
||||
}
|
||||
|
||||
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("240 60 15 3");
|
||||
static IIO_DEVICE_ATTR(in_voltage_scale_available, S_IRUGO,
|
||||
mcp3422_show_scales, NULL, 0);
|
||||
|
||||
static struct attribute *mcp3422_attributes[] = {
|
||||
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage_scale_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group mcp3422_attribute_group = {
|
||||
.attrs = mcp3422_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec mcp3422_channels[] = {
|
||||
MCP3422_CHAN(0),
|
||||
MCP3422_CHAN(1),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec mcp3424_channels[] = {
|
||||
MCP3422_CHAN(0),
|
||||
MCP3422_CHAN(1),
|
||||
MCP3422_CHAN(2),
|
||||
MCP3422_CHAN(3),
|
||||
};
|
||||
|
||||
static const struct iio_info mcp3422_info = {
|
||||
.read_raw = mcp3422_read_raw,
|
||||
.write_raw = mcp3422_write_raw,
|
||||
.write_raw_get_fmt = mcp3422_write_raw_get_fmt,
|
||||
.attrs = &mcp3422_attribute_group,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int mcp3422_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct mcp3422 *adc;
|
||||
int err;
|
||||
u8 config;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
|
||||
return -ENODEV;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*adc));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
adc = iio_priv(indio_dev);
|
||||
adc->i2c = client;
|
||||
|
||||
mutex_init(&adc->lock);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = dev_name(&client->dev);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &mcp3422_info;
|
||||
|
||||
switch ((unsigned int)(id->driver_data)) {
|
||||
case 2:
|
||||
case 3:
|
||||
indio_dev->channels = mcp3422_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(mcp3422_channels);
|
||||
break;
|
||||
case 4:
|
||||
indio_dev->channels = mcp3424_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(mcp3424_channels);
|
||||
break;
|
||||
}
|
||||
|
||||
/* meaningful default configuration */
|
||||
config = (MCP3422_CONT_SAMPLING
|
||||
| MCP3422_CHANNEL_VALUE(1)
|
||||
| MCP3422_PGA_VALUE(MCP3422_PGA_1)
|
||||
| MCP3422_SAMPLE_RATE_VALUE(MCP3422_SRATE_240));
|
||||
mcp3422_update_config(adc, config);
|
||||
|
||||
err = iio_device_register(indio_dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcp3422_remove(struct i2c_client *client)
|
||||
{
|
||||
iio_device_unregister(i2c_get_clientdata(client));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id mcp3422_id[] = {
|
||||
{ "mcp3422", 2 },
|
||||
{ "mcp3423", 3 },
|
||||
{ "mcp3424", 4 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mcp3422_id);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id mcp3422_of_match[] = {
|
||||
{ .compatible = "mcp3422" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mcp3422_of_match);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver mcp3422_driver = {
|
||||
.driver = {
|
||||
.name = "mcp3422",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(mcp3422_of_match),
|
||||
},
|
||||
.probe = mcp3422_probe,
|
||||
.remove = mcp3422_remove,
|
||||
.id_table = mcp3422_id,
|
||||
};
|
||||
module_i2c_driver(mcp3422_driver);
|
||||
|
||||
MODULE_AUTHOR("Angelo Compagnucci <angelo.compagnucci@gmail.com>");
|
||||
MODULE_DESCRIPTION("Microchip mcp3422/3/4 driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -12,6 +12,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
@ -569,7 +570,7 @@ static struct i2c_driver nau7802_driver = {
|
|||
.id_table = nau7802_i2c_id,
|
||||
.driver = {
|
||||
.name = "nau7802",
|
||||
.of_match_table = of_match_ptr(nau7802_dt_ids),
|
||||
.of_match_table = nau7802_dt_ids,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
|
|
@ -28,12 +28,16 @@
|
|||
#include <linux/iio/driver.h>
|
||||
|
||||
#include <linux/mfd/ti_am335x_tscadc.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
|
||||
struct tiadc_device {
|
||||
struct ti_tscadc_dev *mfd_tscadc;
|
||||
int channels;
|
||||
u8 channel_line[8];
|
||||
u8 channel_step[8];
|
||||
int buffer_en_ch_steps;
|
||||
u16 data[8];
|
||||
};
|
||||
|
||||
static unsigned int tiadc_readl(struct tiadc_device *adc, unsigned int reg)
|
||||
|
@ -56,8 +60,14 @@ static u32 get_adc_step_mask(struct tiadc_device *adc_dev)
|
|||
return step_en;
|
||||
}
|
||||
|
||||
static void tiadc_step_config(struct tiadc_device *adc_dev)
|
||||
static u32 get_adc_step_bit(struct tiadc_device *adc_dev, int chan)
|
||||
{
|
||||
return 1 << adc_dev->channel_step[chan];
|
||||
}
|
||||
|
||||
static void tiadc_step_config(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
unsigned int stepconfig;
|
||||
int i, steps;
|
||||
|
||||
|
@ -72,7 +82,11 @@ static void tiadc_step_config(struct tiadc_device *adc_dev)
|
|||
*/
|
||||
|
||||
steps = TOTAL_STEPS - adc_dev->channels;
|
||||
stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1;
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1
|
||||
| STEPCONFIG_MODE_SWCNT;
|
||||
else
|
||||
stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1;
|
||||
|
||||
for (i = 0; i < adc_dev->channels; i++) {
|
||||
int chan;
|
||||
|
@ -85,9 +99,175 @@ static void tiadc_step_config(struct tiadc_device *adc_dev)
|
|||
adc_dev->channel_step[i] = steps;
|
||||
steps++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static irqreturn_t tiadc_irq_h(int irq, void *private)
|
||||
{
|
||||
struct iio_dev *indio_dev = private;
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
unsigned int status, config;
|
||||
status = tiadc_readl(adc_dev, REG_IRQSTATUS);
|
||||
|
||||
/*
|
||||
* ADC and touchscreen share the IRQ line.
|
||||
* FIFO0 interrupts are used by TSC. Handle FIFO1 IRQs here only
|
||||
*/
|
||||
if (status & IRQENB_FIFO1OVRRUN) {
|
||||
/* FIFO Overrun. Clear flag. Disable/Enable ADC to recover */
|
||||
config = tiadc_readl(adc_dev, REG_CTRL);
|
||||
config &= ~(CNTRLREG_TSCSSENB);
|
||||
tiadc_writel(adc_dev, REG_CTRL, config);
|
||||
tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1OVRRUN
|
||||
| IRQENB_FIFO1UNDRFLW | IRQENB_FIFO1THRES);
|
||||
tiadc_writel(adc_dev, REG_CTRL, (config | CNTRLREG_TSCSSENB));
|
||||
return IRQ_HANDLED;
|
||||
} else if (status & IRQENB_FIFO1THRES) {
|
||||
/* Disable irq and wake worker thread */
|
||||
tiadc_writel(adc_dev, REG_IRQCLR, IRQENB_FIFO1THRES);
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static irqreturn_t tiadc_worker_h(int irq, void *private)
|
||||
{
|
||||
struct iio_dev *indio_dev = private;
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
int i, k, fifo1count, read;
|
||||
u16 *data = adc_dev->data;
|
||||
|
||||
fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
|
||||
for (k = 0; k < fifo1count; k = k + i) {
|
||||
for (i = 0; i < (indio_dev->scan_bytes)/2; i++) {
|
||||
read = tiadc_readl(adc_dev, REG_FIFO1);
|
||||
data[i] = read & FIFOREAD_DATA_MASK;
|
||||
}
|
||||
iio_push_to_buffers(indio_dev, (u8 *) data);
|
||||
}
|
||||
|
||||
tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1THRES);
|
||||
tiadc_writel(adc_dev, REG_IRQENABLE, IRQENB_FIFO1THRES);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
int i, fifo1count, read;
|
||||
|
||||
tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES |
|
||||
IRQENB_FIFO1OVRRUN |
|
||||
IRQENB_FIFO1UNDRFLW));
|
||||
|
||||
/* Flush FIFO. Needed in corner cases in simultaneous tsc/adc use */
|
||||
fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
|
||||
for (i = 0; i < fifo1count; i++)
|
||||
read = tiadc_readl(adc_dev, REG_FIFO1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
struct iio_buffer *buffer = indio_dev->buffer;
|
||||
unsigned int enb = 0;
|
||||
u8 bit;
|
||||
|
||||
tiadc_step_config(indio_dev);
|
||||
for_each_set_bit(bit, buffer->scan_mask, adc_dev->channels)
|
||||
enb |= (get_adc_step_bit(adc_dev, bit) << 1);
|
||||
adc_dev->buffer_en_ch_steps = enb;
|
||||
|
||||
am335x_tsc_se_set(adc_dev->mfd_tscadc, enb);
|
||||
|
||||
tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1THRES
|
||||
| IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW);
|
||||
tiadc_writel(adc_dev, REG_IRQENABLE, IRQENB_FIFO1THRES
|
||||
| IRQENB_FIFO1OVRRUN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tiadc_buffer_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
int fifo1count, i, read;
|
||||
|
||||
tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES |
|
||||
IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW));
|
||||
am335x_tsc_se_clr(adc_dev->mfd_tscadc, adc_dev->buffer_en_ch_steps);
|
||||
|
||||
/* Flush FIFO of leftover data in the time it takes to disable adc */
|
||||
fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
|
||||
for (i = 0; i < fifo1count; i++)
|
||||
read = tiadc_readl(adc_dev, REG_FIFO1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tiadc_buffer_postdisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
tiadc_step_config(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops tiadc_buffer_setup_ops = {
|
||||
.preenable = &tiadc_buffer_preenable,
|
||||
.postenable = &tiadc_buffer_postenable,
|
||||
.predisable = &tiadc_buffer_predisable,
|
||||
.postdisable = &tiadc_buffer_postdisable,
|
||||
};
|
||||
|
||||
static int tiadc_iio_buffered_hardware_setup(struct iio_dev *indio_dev,
|
||||
irqreturn_t (*pollfunc_bh)(int irq, void *p),
|
||||
irqreturn_t (*pollfunc_th)(int irq, void *p),
|
||||
int irq,
|
||||
unsigned long flags,
|
||||
const struct iio_buffer_setup_ops *setup_ops)
|
||||
{
|
||||
int ret;
|
||||
|
||||
indio_dev->buffer = iio_kfifo_allocate(indio_dev);
|
||||
if (!indio_dev->buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = request_threaded_irq(irq, pollfunc_th, pollfunc_bh,
|
||||
flags, indio_dev->name, indio_dev);
|
||||
if (ret)
|
||||
goto error_kfifo_free;
|
||||
|
||||
indio_dev->setup_ops = setup_ops;
|
||||
indio_dev->modes |= INDIO_BUFFER_HARDWARE;
|
||||
|
||||
ret = iio_buffer_register(indio_dev,
|
||||
indio_dev->channels,
|
||||
indio_dev->num_channels);
|
||||
if (ret)
|
||||
goto error_free_irq;
|
||||
|
||||
return 0;
|
||||
|
||||
error_free_irq:
|
||||
free_irq(irq, indio_dev);
|
||||
error_kfifo_free:
|
||||
iio_kfifo_free(indio_dev->buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void tiadc_iio_buffered_hardware_remove(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
|
||||
free_irq(adc_dev->mfd_tscadc->irq, indio_dev);
|
||||
iio_kfifo_free(indio_dev->buffer);
|
||||
iio_buffer_unregister(indio_dev);
|
||||
}
|
||||
|
||||
|
||||
static const char * const chan_name_ain[] = {
|
||||
"AIN0",
|
||||
"AIN1",
|
||||
|
@ -120,9 +300,10 @@ static int tiadc_channel_init(struct iio_dev *indio_dev, int channels)
|
|||
chan->channel = adc_dev->channel_line[i];
|
||||
chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
|
||||
chan->datasheet_name = chan_name_ain[chan->channel];
|
||||
chan->scan_index = i;
|
||||
chan->scan_type.sign = 'u';
|
||||
chan->scan_type.realbits = 12;
|
||||
chan->scan_type.storagebits = 32;
|
||||
chan->scan_type.storagebits = 16;
|
||||
}
|
||||
|
||||
indio_dev->channels = chan_array;
|
||||
|
@ -142,11 +323,14 @@ static int tiadc_read_raw(struct iio_dev *indio_dev,
|
|||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
int i, map_val;
|
||||
unsigned int fifo1count, read, stepid;
|
||||
u32 step = UINT_MAX;
|
||||
bool found = false;
|
||||
u32 step_en;
|
||||
unsigned long timeout = jiffies + usecs_to_jiffies
|
||||
(IDLE_TIMEOUT * adc_dev->channels);
|
||||
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
return -EBUSY;
|
||||
|
||||
step_en = get_adc_step_mask(adc_dev);
|
||||
am335x_tsc_se_set(adc_dev->mfd_tscadc, step_en);
|
||||
|
||||
|
@ -168,15 +352,6 @@ static int tiadc_read_raw(struct iio_dev *indio_dev,
|
|||
* Hence we need to flush out this data.
|
||||
*/
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(adc_dev->channel_step); i++) {
|
||||
if (chan->channel == adc_dev->channel_line[i]) {
|
||||
step = adc_dev->channel_step[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (WARN_ON_ONCE(step == UINT_MAX))
|
||||
return -EINVAL;
|
||||
|
||||
fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
|
||||
for (i = 0; i < fifo1count; i++) {
|
||||
read = tiadc_readl(adc_dev, REG_FIFO1);
|
||||
|
@ -186,7 +361,7 @@ static int tiadc_read_raw(struct iio_dev *indio_dev,
|
|||
if (stepid == map_val) {
|
||||
read = read & FIFOREAD_DATA_MASK;
|
||||
found = true;
|
||||
*val = read;
|
||||
*val = (u16) read;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -237,20 +412,33 @@ static int tiadc_probe(struct platform_device *pdev)
|
|||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &tiadc_info;
|
||||
|
||||
tiadc_step_config(adc_dev);
|
||||
tiadc_step_config(indio_dev);
|
||||
tiadc_writel(adc_dev, REG_FIFO1THR, FIFO1_THRESHOLD);
|
||||
|
||||
err = tiadc_channel_init(indio_dev, adc_dev->channels);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = iio_device_register(indio_dev);
|
||||
err = tiadc_iio_buffered_hardware_setup(indio_dev,
|
||||
&tiadc_worker_h,
|
||||
&tiadc_irq_h,
|
||||
adc_dev->mfd_tscadc->irq,
|
||||
IRQF_SHARED,
|
||||
&tiadc_buffer_setup_ops);
|
||||
|
||||
if (err)
|
||||
goto err_free_channels;
|
||||
|
||||
err = iio_device_register(indio_dev);
|
||||
if (err)
|
||||
goto err_buffer_unregister;
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_buffer_unregister:
|
||||
tiadc_iio_buffered_hardware_remove(indio_dev);
|
||||
err_free_channels:
|
||||
tiadc_channels_remove(indio_dev);
|
||||
return err;
|
||||
|
@ -263,6 +451,7 @@ static int tiadc_remove(struct platform_device *pdev)
|
|||
u32 step_en;
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
tiadc_iio_buffered_hardware_remove(indio_dev);
|
||||
tiadc_channels_remove(indio_dev);
|
||||
|
||||
step_en = get_adc_step_mask(adc_dev);
|
||||
|
@ -301,7 +490,7 @@ static int tiadc_resume(struct device *dev)
|
|||
restore &= ~(CNTRLREG_POWERDOWN);
|
||||
tiadc_writel(adc_dev, REG_CTRL, restore);
|
||||
|
||||
tiadc_step_config(adc_dev);
|
||||
tiadc_step_config(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -326,7 +515,7 @@ static struct platform_driver tiadc_driver = {
|
|||
.name = "TI-am335x-adc",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = TIADC_PM_OPS,
|
||||
.of_match_table = of_match_ptr(ti_adc_dt_ids),
|
||||
.of_match_table = ti_adc_dt_ids,
|
||||
},
|
||||
.probe = tiadc_probe,
|
||||
.remove = tiadc_remove,
|
||||
|
|
|
@ -887,7 +887,7 @@ static int twl6030_gpadc_probe(struct platform_device *pdev)
|
|||
int irq;
|
||||
int ret;
|
||||
|
||||
match = of_match_device(of_match_ptr(of_twl6030_match_tbl), dev);
|
||||
match = of_match_device(of_twl6030_match_tbl, dev);
|
||||
if (!match)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -948,9 +948,7 @@ static int twl6030_gpadc_probe(struct platform_device *pdev)
|
|||
indio_dev->channels = pdata->iio_channels;
|
||||
indio_dev->num_channels = pdata->nchannels;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
|
||||
return ret;
|
||||
return iio_device_register(indio_dev);
|
||||
}
|
||||
|
||||
static int twl6030_gpadc_remove(struct platform_device *pdev)
|
||||
|
|
|
@ -7,26 +7,36 @@
|
|||
|
||||
struct iio_cb_buffer {
|
||||
struct iio_buffer buffer;
|
||||
int (*cb)(u8 *data, void *private);
|
||||
int (*cb)(const void *data, void *private);
|
||||
void *private;
|
||||
struct iio_channel *channels;
|
||||
};
|
||||
|
||||
static int iio_buffer_cb_store_to(struct iio_buffer *buffer, u8 *data)
|
||||
static struct iio_cb_buffer *buffer_to_cb_buffer(struct iio_buffer *buffer)
|
||||
{
|
||||
struct iio_cb_buffer *cb_buff = container_of(buffer,
|
||||
struct iio_cb_buffer,
|
||||
buffer);
|
||||
return container_of(buffer, struct iio_cb_buffer, buffer);
|
||||
}
|
||||
|
||||
static int iio_buffer_cb_store_to(struct iio_buffer *buffer, const void *data)
|
||||
{
|
||||
struct iio_cb_buffer *cb_buff = buffer_to_cb_buffer(buffer);
|
||||
return cb_buff->cb(data, cb_buff->private);
|
||||
}
|
||||
|
||||
static struct iio_buffer_access_funcs iio_cb_access = {
|
||||
static void iio_buffer_cb_release(struct iio_buffer *buffer)
|
||||
{
|
||||
struct iio_cb_buffer *cb_buff = buffer_to_cb_buffer(buffer);
|
||||
kfree(cb_buff->buffer.scan_mask);
|
||||
kfree(cb_buff);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_access_funcs iio_cb_access = {
|
||||
.store_to = &iio_buffer_cb_store_to,
|
||||
.release = &iio_buffer_cb_release,
|
||||
};
|
||||
|
||||
struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
|
||||
int (*cb)(u8 *data,
|
||||
int (*cb)(const void *data,
|
||||
void *private),
|
||||
void *private)
|
||||
{
|
||||
|
@ -104,9 +114,8 @@ EXPORT_SYMBOL_GPL(iio_channel_stop_all_cb);
|
|||
|
||||
void iio_channel_release_all_cb(struct iio_cb_buffer *cb_buff)
|
||||
{
|
||||
kfree(cb_buff->buffer.scan_mask);
|
||||
iio_channel_release_all(cb_buff->channels);
|
||||
kfree(cb_buff);
|
||||
iio_buffer_put(&cb_buff->buffer);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_channel_release_all_cb);
|
||||
|
||||
|
|
|
@ -34,6 +34,12 @@ static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
|||
struct hid_sensor_common *st = iio_trigger_get_drvdata(trig);
|
||||
int state_val;
|
||||
|
||||
if (state) {
|
||||
if (sensor_hub_device_open(st->hsdev))
|
||||
return -EIO;
|
||||
} else
|
||||
sensor_hub_device_close(st->hsdev);
|
||||
|
||||
state_val = state ? 1 : 0;
|
||||
if (IS_ENABLED(CONFIG_HID_SENSOR_ENUM_BASE_QUIRKS))
|
||||
++state_val;
|
||||
|
|
|
@ -113,11 +113,8 @@ irqreturn_t st_sensors_trigger_handler(int irq, void *p)
|
|||
if (len < 0)
|
||||
goto st_sensors_get_buffer_element_error;
|
||||
|
||||
if (indio_dev->scan_timestamp)
|
||||
*(s64 *)((u8 *)sdata->buffer_data +
|
||||
ALIGN(len, sizeof(s64))) = pf->timestamp;
|
||||
|
||||
iio_push_to_buffers(indio_dev, sdata->buffer_data);
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, sdata->buffer_data,
|
||||
pf->timestamp);
|
||||
|
||||
st_sensors_get_buffer_element_error:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
|
|
@ -198,21 +198,17 @@ int st_sensors_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable)
|
|||
}
|
||||
EXPORT_SYMBOL(st_sensors_set_axis_enable);
|
||||
|
||||
int st_sensors_init_sensor(struct iio_dev *indio_dev,
|
||||
struct st_sensors_platform_data *pdata)
|
||||
static int st_sensors_set_drdy_int_pin(struct iio_dev *indio_dev,
|
||||
struct st_sensors_platform_data *pdata)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
mutex_init(&sdata->tb.buf_lock);
|
||||
|
||||
switch (pdata->drdy_int_pin) {
|
||||
case 1:
|
||||
if (sdata->sensor->drdy_irq.mask_int1 == 0) {
|
||||
dev_err(&indio_dev->dev,
|
||||
"DRDY on INT1 not available.\n");
|
||||
err = -EINVAL;
|
||||
goto init_error;
|
||||
return -EINVAL;
|
||||
}
|
||||
sdata->drdy_int_pin = 1;
|
||||
break;
|
||||
|
@ -220,39 +216,53 @@ int st_sensors_init_sensor(struct iio_dev *indio_dev,
|
|||
if (sdata->sensor->drdy_irq.mask_int2 == 0) {
|
||||
dev_err(&indio_dev->dev,
|
||||
"DRDY on INT2 not available.\n");
|
||||
err = -EINVAL;
|
||||
goto init_error;
|
||||
return -EINVAL;
|
||||
}
|
||||
sdata->drdy_int_pin = 2;
|
||||
break;
|
||||
default:
|
||||
dev_err(&indio_dev->dev, "DRDY on pdata not valid.\n");
|
||||
err = -EINVAL;
|
||||
goto init_error;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int st_sensors_init_sensor(struct iio_dev *indio_dev,
|
||||
struct st_sensors_platform_data *pdata)
|
||||
{
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
int err = 0;
|
||||
|
||||
mutex_init(&sdata->tb.buf_lock);
|
||||
|
||||
if (pdata)
|
||||
err = st_sensors_set_drdy_int_pin(indio_dev, pdata);
|
||||
|
||||
err = st_sensors_set_enable(indio_dev, false);
|
||||
if (err < 0)
|
||||
goto init_error;
|
||||
return err;
|
||||
|
||||
err = st_sensors_set_fullscale(indio_dev,
|
||||
sdata->current_fullscale->num);
|
||||
if (err < 0)
|
||||
goto init_error;
|
||||
if (sdata->current_fullscale) {
|
||||
err = st_sensors_set_fullscale(indio_dev,
|
||||
sdata->current_fullscale->num);
|
||||
if (err < 0)
|
||||
return err;
|
||||
} else
|
||||
dev_info(&indio_dev->dev, "Full-scale not possible\n");
|
||||
|
||||
err = st_sensors_set_odr(indio_dev, sdata->odr);
|
||||
if (err < 0)
|
||||
goto init_error;
|
||||
return err;
|
||||
|
||||
/* set BDU */
|
||||
err = st_sensors_write_data_with_mask(indio_dev,
|
||||
sdata->sensor->bdu.addr, sdata->sensor->bdu.mask, true);
|
||||
if (err < 0)
|
||||
goto init_error;
|
||||
return err;
|
||||
|
||||
err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
|
||||
|
||||
init_error:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_init_sensor);
|
||||
|
@ -263,6 +273,9 @@ int st_sensors_set_dataready_irq(struct iio_dev *indio_dev, bool enable)
|
|||
u8 drdy_mask;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
if (!sdata->sensor->drdy_irq.addr)
|
||||
return 0;
|
||||
|
||||
/* Enable/Disable the interrupt generator 1. */
|
||||
if (sdata->sensor->drdy_irq.ig1.en_addr > 0) {
|
||||
err = st_sensors_write_data_with_mask(indio_dev,
|
||||
|
@ -318,10 +331,8 @@ static int st_sensors_read_axis_data(struct iio_dev *indio_dev,
|
|||
unsigned int byte_for_channel = ch->scan_type.storagebits >> 3;
|
||||
|
||||
outdata = kmalloc(byte_for_channel, GFP_KERNEL);
|
||||
if (!outdata) {
|
||||
err = -EINVAL;
|
||||
goto st_sensors_read_axis_data_error;
|
||||
}
|
||||
if (!outdata)
|
||||
return -ENOMEM;
|
||||
|
||||
err = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
|
||||
ch->address, byte_for_channel,
|
||||
|
@ -336,7 +347,7 @@ static int st_sensors_read_axis_data(struct iio_dev *indio_dev,
|
|||
|
||||
st_sensors_free_memory:
|
||||
kfree(outdata);
|
||||
st_sensors_read_axis_data_error:
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -349,28 +360,25 @@ int st_sensors_read_info_raw(struct iio_dev *indio_dev,
|
|||
mutex_lock(&indio_dev->mlock);
|
||||
if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
|
||||
err = -EBUSY;
|
||||
goto read_error;
|
||||
goto out;
|
||||
} else {
|
||||
err = st_sensors_set_enable(indio_dev, true);
|
||||
if (err < 0)
|
||||
goto read_error;
|
||||
goto out;
|
||||
|
||||
msleep((sdata->sensor->bootime * 1000) / sdata->odr);
|
||||
err = st_sensors_read_axis_data(indio_dev, ch, val);
|
||||
if (err < 0)
|
||||
goto read_error;
|
||||
goto out;
|
||||
|
||||
*val = *val >> ch->scan_type.shift;
|
||||
|
||||
err = st_sensors_set_enable(indio_dev, false);
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return err;
|
||||
|
||||
read_error:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_read_info_raw);
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ config AD5446
|
|||
Say yes here to build support for Analog Devices AD5300, AD5301, AD5310,
|
||||
AD5311, AD5320, AD5321, AD5444, AD5446, AD5450, AD5451, AD5452, AD5453,
|
||||
AD5512A, AD5541A, AD5542A, AD5543, AD5553, AD5601, AD5602, AD5611, AD5612,
|
||||
AD5620, AD5621, AD5622, AD5640, AD5660, AD5662 DACs.
|
||||
AD5620, AD5621, AD5622, AD5640, AD5641, AD5660, AD5662 DACs.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad5446.
|
||||
|
|
|
@ -239,10 +239,9 @@ static int ad5064_read_raw(struct iio_dev *indio_dev,
|
|||
if (scale_uv < 0)
|
||||
return scale_uv;
|
||||
|
||||
scale_uv = (scale_uv * 100) >> chan->scan_type.realbits;
|
||||
*val = scale_uv / 100000;
|
||||
*val2 = (scale_uv % 100000) * 10;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
*val = scale_uv / 1000;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -285,8 +284,9 @@ static const struct iio_chan_spec_ext_info ad5064_ext_info[] = {
|
|||
.name = "powerdown",
|
||||
.read = ad5064_read_dac_powerdown,
|
||||
.write = ad5064_write_dac_powerdown,
|
||||
.shared = IIO_SEPARATE,
|
||||
},
|
||||
IIO_ENUM("powerdown_mode", false, &ad5064_powerdown_mode_enum),
|
||||
IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad5064_powerdown_mode_enum),
|
||||
IIO_ENUM_AVAILABLE("powerdown_mode", &ad5064_powerdown_mode_enum),
|
||||
{ },
|
||||
};
|
||||
|
|
|
@ -379,15 +379,14 @@ static int ad5360_read_raw(struct iio_dev *indio_dev,
|
|||
*val = ret >> chan->scan_type.shift;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
/* vout = 4 * vref * dac_code */
|
||||
scale_uv = ad5360_get_channel_vref(st, chan->channel) * 4 * 100;
|
||||
scale_uv = ad5360_get_channel_vref(st, chan->channel);
|
||||
if (scale_uv < 0)
|
||||
return scale_uv;
|
||||
|
||||
scale_uv >>= (chan->scan_type.realbits);
|
||||
*val = scale_uv / 100000;
|
||||
*val2 = (scale_uv % 100000) * 10;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
/* vout = 4 * vref * dac_code */
|
||||
*val = scale_uv * 4 / 1000;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
ret = ad5360_read(indio_dev, AD5360_READBACK_OFFSET,
|
||||
chan->address);
|
||||
|
|
|
@ -204,7 +204,6 @@ static int ad5380_read_raw(struct iio_dev *indio_dev,
|
|||
struct iio_chan_spec const *chan, int *val, int *val2, long info)
|
||||
{
|
||||
struct ad5380_state *st = iio_priv(indio_dev);
|
||||
unsigned long scale_uv;
|
||||
int ret;
|
||||
|
||||
switch (info) {
|
||||
|
@ -225,10 +224,9 @@ static int ad5380_read_raw(struct iio_dev *indio_dev,
|
|||
val -= (1 << chan->scan_type.realbits) / 2;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
scale_uv = ((2 * st->vref) >> chan->scan_type.realbits) * 100;
|
||||
*val = scale_uv / 100000;
|
||||
*val2 = (scale_uv % 100000) * 10;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
*val = 2 * st->vref;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -247,8 +245,10 @@ static struct iio_chan_spec_ext_info ad5380_ext_info[] = {
|
|||
.name = "powerdown",
|
||||
.read = ad5380_read_dac_powerdown,
|
||||
.write = ad5380_write_dac_powerdown,
|
||||
.shared = IIO_SEPARATE,
|
||||
},
|
||||
IIO_ENUM("powerdown_mode", true, &ad5380_powerdown_mode_enum),
|
||||
IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE,
|
||||
&ad5380_powerdown_mode_enum),
|
||||
IIO_ENUM_AVAILABLE("powerdown_mode", &ad5380_powerdown_mode_enum),
|
||||
{ },
|
||||
};
|
||||
|
@ -269,72 +269,72 @@ static const struct ad5380_chip_info ad5380_chip_info_tbl[] = {
|
|||
[ID_AD5380_3] = {
|
||||
.channel_template = AD5380_CHANNEL(14),
|
||||
.num_channels = 40,
|
||||
.int_vref = 1250000,
|
||||
.int_vref = 1250,
|
||||
},
|
||||
[ID_AD5380_5] = {
|
||||
.channel_template = AD5380_CHANNEL(14),
|
||||
.num_channels = 40,
|
||||
.int_vref = 2500000,
|
||||
.int_vref = 2500,
|
||||
},
|
||||
[ID_AD5381_3] = {
|
||||
.channel_template = AD5380_CHANNEL(12),
|
||||
.num_channels = 16,
|
||||
.int_vref = 1250000,
|
||||
.int_vref = 1250,
|
||||
},
|
||||
[ID_AD5381_5] = {
|
||||
.channel_template = AD5380_CHANNEL(12),
|
||||
.num_channels = 16,
|
||||
.int_vref = 2500000,
|
||||
.int_vref = 2500,
|
||||
},
|
||||
[ID_AD5382_3] = {
|
||||
.channel_template = AD5380_CHANNEL(14),
|
||||
.num_channels = 32,
|
||||
.int_vref = 1250000,
|
||||
.int_vref = 1250,
|
||||
},
|
||||
[ID_AD5382_5] = {
|
||||
.channel_template = AD5380_CHANNEL(14),
|
||||
.num_channels = 32,
|
||||
.int_vref = 2500000,
|
||||
.int_vref = 2500,
|
||||
},
|
||||
[ID_AD5383_3] = {
|
||||
.channel_template = AD5380_CHANNEL(12),
|
||||
.num_channels = 32,
|
||||
.int_vref = 1250000,
|
||||
.int_vref = 1250,
|
||||
},
|
||||
[ID_AD5383_5] = {
|
||||
.channel_template = AD5380_CHANNEL(12),
|
||||
.num_channels = 32,
|
||||
.int_vref = 2500000,
|
||||
.int_vref = 2500,
|
||||
},
|
||||
[ID_AD5390_3] = {
|
||||
.channel_template = AD5380_CHANNEL(14),
|
||||
.num_channels = 16,
|
||||
.int_vref = 1250000,
|
||||
.int_vref = 1250,
|
||||
},
|
||||
[ID_AD5390_5] = {
|
||||
.channel_template = AD5380_CHANNEL(14),
|
||||
.num_channels = 16,
|
||||
.int_vref = 2500000,
|
||||
.int_vref = 2500,
|
||||
},
|
||||
[ID_AD5391_3] = {
|
||||
.channel_template = AD5380_CHANNEL(12),
|
||||
.num_channels = 16,
|
||||
.int_vref = 1250000,
|
||||
.int_vref = 1250,
|
||||
},
|
||||
[ID_AD5391_5] = {
|
||||
.channel_template = AD5380_CHANNEL(12),
|
||||
.num_channels = 16,
|
||||
.int_vref = 2500000,
|
||||
.int_vref = 2500,
|
||||
},
|
||||
[ID_AD5392_3] = {
|
||||
.channel_template = AD5380_CHANNEL(14),
|
||||
.num_channels = 8,
|
||||
.int_vref = 1250000,
|
||||
.int_vref = 1250,
|
||||
},
|
||||
[ID_AD5392_5] = {
|
||||
.channel_template = AD5380_CHANNEL(14),
|
||||
.num_channels = 8,
|
||||
.int_vref = 2500000,
|
||||
.int_vref = 2500,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -393,7 +393,7 @@ static int ad5380_probe(struct device *dev, struct regmap *regmap,
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (st->chip_info->int_vref == 2500000)
|
||||
if (st->chip_info->int_vref == 2500)
|
||||
ctrl |= AD5380_CTRL_INT_VREF_2V5;
|
||||
|
||||
st->vref_reg = devm_regulator_get(dev, "vref");
|
||||
|
@ -409,7 +409,7 @@ static int ad5380_probe(struct device *dev, struct regmap *regmap,
|
|||
if (ret < 0)
|
||||
goto error_disable_reg;
|
||||
|
||||
st->vref = ret;
|
||||
st->vref = ret / 1000;
|
||||
} else {
|
||||
st->vref = st->chip_info->int_vref;
|
||||
ctrl |= AD5380_CTRL_INT_VREF_EN;
|
||||
|
|
|
@ -80,6 +80,29 @@ struct ad5421_state {
|
|||
} data[2] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static const struct iio_event_spec ad5421_current_event[] = {
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_RISING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
||||
BIT(IIO_EV_INFO_ENABLE),
|
||||
}, {
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_FALLING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
||||
BIT(IIO_EV_INFO_ENABLE),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_event_spec ad5421_temp_event[] = {
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_RISING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
||||
BIT(IIO_EV_INFO_ENABLE),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec ad5421_channels[] = {
|
||||
{
|
||||
.type = IIO_CURRENT,
|
||||
|
@ -92,13 +115,14 @@ static const struct iio_chan_spec ad5421_channels[] = {
|
|||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.scan_type = IIO_ST('u', 16, 16, 0),
|
||||
.event_mask = IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING) |
|
||||
IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING),
|
||||
.event_spec = ad5421_current_event,
|
||||
.num_event_specs = ARRAY_SIZE(ad5421_current_event),
|
||||
},
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.channel = -1,
|
||||
.event_mask = IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING),
|
||||
.event_spec = ad5421_temp_event,
|
||||
.num_event_specs = ARRAY_SIZE(ad5421_temp_event),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -281,18 +305,11 @@ static inline unsigned int ad5421_get_offset(struct ad5421_state *st)
|
|||
return (min * (1 << 16)) / (max - min);
|
||||
}
|
||||
|
||||
static inline unsigned int ad5421_get_scale(struct ad5421_state *st)
|
||||
{
|
||||
unsigned int min, max;
|
||||
|
||||
ad5421_get_current_min_max(st, &min, &max);
|
||||
return ((max - min) * 1000) / (1 << 16);
|
||||
}
|
||||
|
||||
static int ad5421_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val, int *val2, long m)
|
||||
{
|
||||
struct ad5421_state *st = iio_priv(indio_dev);
|
||||
unsigned int min, max;
|
||||
int ret;
|
||||
|
||||
if (chan->type != IIO_CURRENT)
|
||||
|
@ -306,9 +323,10 @@ static int ad5421_read_raw(struct iio_dev *indio_dev,
|
|||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
*val2 = ad5421_get_scale(st);
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
ad5421_get_current_min_max(st, &min, &max);
|
||||
*val = max - min;
|
||||
*val2 = (1 << 16) * 1000;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = ad5421_get_offset(st);
|
||||
return IIO_VAL_INT;
|
||||
|
@ -359,15 +377,15 @@ static int ad5421_write_raw(struct iio_dev *indio_dev,
|
|||
}
|
||||
|
||||
static int ad5421_write_event_config(struct iio_dev *indio_dev,
|
||||
u64 event_code, int state)
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir, int state)
|
||||
{
|
||||
struct ad5421_state *st = iio_priv(indio_dev);
|
||||
unsigned int mask;
|
||||
|
||||
switch (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code)) {
|
||||
switch (chan->type) {
|
||||
case IIO_CURRENT:
|
||||
if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) ==
|
||||
IIO_EV_DIR_RISING)
|
||||
if (dir == IIO_EV_DIR_RISING)
|
||||
mask = AD5421_FAULT_OVER_CURRENT;
|
||||
else
|
||||
mask = AD5421_FAULT_UNDER_CURRENT;
|
||||
|
@ -390,15 +408,15 @@ static int ad5421_write_event_config(struct iio_dev *indio_dev,
|
|||
}
|
||||
|
||||
static int ad5421_read_event_config(struct iio_dev *indio_dev,
|
||||
u64 event_code)
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir)
|
||||
{
|
||||
struct ad5421_state *st = iio_priv(indio_dev);
|
||||
unsigned int mask;
|
||||
|
||||
switch (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code)) {
|
||||
switch (chan->type) {
|
||||
case IIO_CURRENT:
|
||||
if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) ==
|
||||
IIO_EV_DIR_RISING)
|
||||
if (dir == IIO_EV_DIR_RISING)
|
||||
mask = AD5421_FAULT_OVER_CURRENT;
|
||||
else
|
||||
mask = AD5421_FAULT_UNDER_CURRENT;
|
||||
|
@ -413,12 +431,14 @@ static int ad5421_read_event_config(struct iio_dev *indio_dev,
|
|||
return (bool)(st->fault_mask & mask);
|
||||
}
|
||||
|
||||
static int ad5421_read_event_value(struct iio_dev *indio_dev, u64 event_code,
|
||||
int *val)
|
||||
static int ad5421_read_event_value(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir, enum iio_event_info info, int *val,
|
||||
int *val2)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code)) {
|
||||
switch (chan->type) {
|
||||
case IIO_CURRENT:
|
||||
ret = ad5421_read(indio_dev, AD5421_REG_DAC_DATA);
|
||||
if (ret < 0)
|
||||
|
@ -432,15 +452,15 @@ static int ad5421_read_event_value(struct iio_dev *indio_dev, u64 event_code,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static const struct iio_info ad5421_info = {
|
||||
.read_raw = ad5421_read_raw,
|
||||
.write_raw = ad5421_write_raw,
|
||||
.read_event_config = ad5421_read_event_config,
|
||||
.write_event_config = ad5421_write_event_config,
|
||||
.read_event_value = ad5421_read_event_value,
|
||||
.read_event_config_new = ad5421_read_event_config,
|
||||
.write_event_config_new = ad5421_write_event_config,
|
||||
.read_event_value_new = ad5421_read_event_value,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
@ -494,13 +514,7 @@ static int ad5421_probe(struct spi_device *spi)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to register iio device: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return iio_device_register(indio_dev);
|
||||
}
|
||||
|
||||
static int ad5421_remove(struct spi_device *spi)
|
||||
|
|
|
@ -132,8 +132,9 @@ static const struct iio_chan_spec_ext_info ad5446_ext_info_powerdown[] = {
|
|||
.name = "powerdown",
|
||||
.read = ad5446_read_dac_powerdown,
|
||||
.write = ad5446_write_dac_powerdown,
|
||||
.shared = IIO_SEPARATE,
|
||||
},
|
||||
IIO_ENUM("powerdown_mode", false, &ad5446_powerdown_mode_enum),
|
||||
IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad5446_powerdown_mode_enum),
|
||||
IIO_ENUM_AVAILABLE("powerdown_mode", &ad5446_powerdown_mode_enum),
|
||||
{ },
|
||||
};
|
||||
|
@ -162,18 +163,15 @@ static int ad5446_read_raw(struct iio_dev *indio_dev,
|
|||
long m)
|
||||
{
|
||||
struct ad5446_state *st = iio_priv(indio_dev);
|
||||
unsigned long scale_uv;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
*val = st->cached_val;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits;
|
||||
*val = scale_uv / 1000;
|
||||
*val2 = (scale_uv % 1000) * 1000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
|
||||
*val = st->vref_mv;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -329,6 +327,7 @@ enum ad5446_supported_spi_device_ids {
|
|||
ID_AD5601,
|
||||
ID_AD5611,
|
||||
ID_AD5621,
|
||||
ID_AD5641,
|
||||
ID_AD5620_2500,
|
||||
ID_AD5620_1250,
|
||||
ID_AD5640_2500,
|
||||
|
@ -391,6 +390,10 @@ static const struct ad5446_chip_info ad5446_spi_chip_info[] = {
|
|||
.channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2),
|
||||
.write = ad5446_write,
|
||||
},
|
||||
[ID_AD5641] = {
|
||||
.channel = AD5446_CHANNEL_POWERDOWN(14, 16, 0),
|
||||
.write = ad5446_write,
|
||||
},
|
||||
[ID_AD5620_2500] = {
|
||||
.channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2),
|
||||
.int_vref_mv = 2500,
|
||||
|
@ -445,6 +448,7 @@ static const struct spi_device_id ad5446_spi_ids[] = {
|
|||
{"ad5601", ID_AD5601},
|
||||
{"ad5611", ID_AD5611},
|
||||
{"ad5621", ID_AD5621},
|
||||
{"ad5641", ID_AD5641},
|
||||
{"ad5620-2500", ID_AD5620_2500}, /* AD5620/40/60: */
|
||||
{"ad5620-1250", ID_AD5620_1250}, /* part numbers may look differently */
|
||||
{"ad5640-2500", ID_AD5640_2500},
|
||||
|
|
|
@ -101,7 +101,6 @@ static int ad5449_read(struct iio_dev *indio_dev, unsigned int addr,
|
|||
{
|
||||
struct ad5449 *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
struct spi_message msg;
|
||||
struct spi_transfer t[] = {
|
||||
{
|
||||
.tx_buf = &st->data[0],
|
||||
|
@ -114,15 +113,11 @@ static int ad5449_read(struct iio_dev *indio_dev, unsigned int addr,
|
|||
},
|
||||
};
|
||||
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&t[0], &msg);
|
||||
spi_message_add_tail(&t[1], &msg);
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
st->data[0] = cpu_to_be16(addr << 12);
|
||||
st->data[1] = cpu_to_be16(AD5449_CMD_NOOP);
|
||||
|
||||
ret = spi_sync(st->spi, &msg);
|
||||
ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
|
||||
if (ret < 0)
|
||||
goto out_unlock;
|
||||
|
||||
|
|
|
@ -100,7 +100,6 @@ static int ad5504_read_raw(struct iio_dev *indio_dev,
|
|||
long m)
|
||||
{
|
||||
struct ad5504_state *st = iio_priv(indio_dev);
|
||||
unsigned long scale_uv;
|
||||
int ret;
|
||||
|
||||
switch (m) {
|
||||
|
@ -113,11 +112,9 @@ static int ad5504_read_raw(struct iio_dev *indio_dev,
|
|||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits;
|
||||
*val = scale_uv / 1000;
|
||||
*val2 = (scale_uv % 1000) * 1000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
|
||||
*val = st->vref_mv;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -248,8 +245,10 @@ static const struct iio_chan_spec_ext_info ad5504_ext_info[] = {
|
|||
.name = "powerdown",
|
||||
.read = ad5504_read_dac_powerdown,
|
||||
.write = ad5504_write_dac_powerdown,
|
||||
.shared = IIO_SEPARATE,
|
||||
},
|
||||
IIO_ENUM("powerdown_mode", true, &ad5504_powerdown_mode_enum),
|
||||
IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE,
|
||||
&ad5504_powerdown_mode_enum),
|
||||
IIO_ENUM_AVAILABLE("powerdown_mode", &ad5504_powerdown_mode_enum),
|
||||
{ },
|
||||
};
|
||||
|
|
|
@ -50,15 +50,12 @@ static int ad5624r_read_raw(struct iio_dev *indio_dev,
|
|||
long m)
|
||||
{
|
||||
struct ad5624r_state *st = iio_priv(indio_dev);
|
||||
unsigned long scale_uv;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits;
|
||||
*val = scale_uv / 1000;
|
||||
*val2 = (scale_uv % 1000) * 1000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
|
||||
*val = st->vref_mv;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -163,8 +160,10 @@ static const struct iio_chan_spec_ext_info ad5624r_ext_info[] = {
|
|||
.name = "powerdown",
|
||||
.read = ad5624r_read_dac_powerdown,
|
||||
.write = ad5624r_write_dac_powerdown,
|
||||
.shared = IIO_SEPARATE,
|
||||
},
|
||||
IIO_ENUM("powerdown_mode", true, &ad5624r_powerdown_mode_enum),
|
||||
IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE,
|
||||
&ad5624r_powerdown_mode_enum),
|
||||
IIO_ENUM_AVAILABLE("powerdown_mode", &ad5624r_powerdown_mode_enum),
|
||||
{ },
|
||||
};
|
||||
|
|
|
@ -201,7 +201,6 @@ static int ad5686_read_raw(struct iio_dev *indio_dev,
|
|||
long m)
|
||||
{
|
||||
struct ad5686_state *st = iio_priv(indio_dev);
|
||||
unsigned long scale_uv;
|
||||
int ret;
|
||||
|
||||
switch (m) {
|
||||
|
@ -213,14 +212,10 @@ static int ad5686_read_raw(struct iio_dev *indio_dev,
|
|||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
scale_uv = (st->vref_mv * 100000)
|
||||
>> (chan->scan_type.realbits);
|
||||
*val = scale_uv / 100000;
|
||||
*val2 = (scale_uv % 100000) * 10;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
|
||||
*val = st->vref_mv;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -265,8 +260,9 @@ static const struct iio_chan_spec_ext_info ad5686_ext_info[] = {
|
|||
.name = "powerdown",
|
||||
.read = ad5686_read_dac_powerdown,
|
||||
.write = ad5686_write_dac_powerdown,
|
||||
.shared = IIO_SEPARATE,
|
||||
},
|
||||
IIO_ENUM("powerdown_mode", false, &ad5686_powerdown_mode_enum),
|
||||
IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad5686_powerdown_mode_enum),
|
||||
IIO_ENUM_AVAILABLE("powerdown_mode", &ad5686_powerdown_mode_enum),
|
||||
{ },
|
||||
};
|
||||
|
|
|
@ -253,15 +253,6 @@ static inline int ad5755_get_offset(struct ad5755_state *st,
|
|||
return (min * (1 << chan->scan_type.realbits)) / (max - min);
|
||||
}
|
||||
|
||||
static inline int ad5755_get_scale(struct ad5755_state *st,
|
||||
struct iio_chan_spec const *chan)
|
||||
{
|
||||
int min, max;
|
||||
|
||||
ad5755_get_min_max(st, chan, &min, &max);
|
||||
return ((max - min) * 1000000000ULL) >> chan->scan_type.realbits;
|
||||
}
|
||||
|
||||
static int ad5755_chan_reg_info(struct ad5755_state *st,
|
||||
struct iio_chan_spec const *chan, long info, bool write,
|
||||
unsigned int *reg, unsigned int *shift, unsigned int *offset)
|
||||
|
@ -303,13 +294,15 @@ static int ad5755_read_raw(struct iio_dev *indio_dev,
|
|||
{
|
||||
struct ad5755_state *st = iio_priv(indio_dev);
|
||||
unsigned int reg, shift, offset;
|
||||
int min, max;
|
||||
int ret;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
*val2 = ad5755_get_scale(st, chan);
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
ad5755_get_min_max(st, chan, &min, &max);
|
||||
*val = max - min;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = ad5755_get_offset(st, chan);
|
||||
return IIO_VAL_INT;
|
||||
|
@ -386,6 +379,7 @@ static const struct iio_chan_spec_ext_info ad5755_ext_info[] = {
|
|||
.name = "powerdown",
|
||||
.read = ad5755_read_powerdown,
|
||||
.write = ad5755_write_powerdown,
|
||||
.shared = IIO_SEPARATE,
|
||||
},
|
||||
{ },
|
||||
};
|
||||
|
@ -595,13 +589,7 @@ static int ad5755_probe(struct spi_device *spi)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to register iio device: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return iio_device_register(indio_dev);
|
||||
}
|
||||
|
||||
static int ad5755_remove(struct spi_device *spi)
|
||||
|
|
|
@ -217,7 +217,6 @@ static int ad5764_read_raw(struct iio_dev *indio_dev,
|
|||
struct iio_chan_spec const *chan, int *val, int *val2, long info)
|
||||
{
|
||||
struct ad5764_state *st = iio_priv(indio_dev);
|
||||
unsigned long scale_uv;
|
||||
unsigned int reg;
|
||||
int vref;
|
||||
int ret;
|
||||
|
@ -245,15 +244,14 @@ static int ad5764_read_raw(struct iio_dev *indio_dev,
|
|||
*val = sign_extend32(*val, 5);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
/* vout = 4 * vref + ((dac_code / 65535) - 0.5) */
|
||||
/* vout = 4 * vref + ((dac_code / 65536) - 0.5) */
|
||||
vref = ad5764_get_channel_vref(st, chan->channel);
|
||||
if (vref < 0)
|
||||
return vref;
|
||||
|
||||
scale_uv = (vref * 4 * 100) >> chan->scan_type.realbits;
|
||||
*val = scale_uv / 100000;
|
||||
*val2 = (scale_uv % 100000) * 10;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
*val = vref * 4 / 1000;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = -(1 << chan->scan_type.realbits) / 2;
|
||||
return IIO_VAL_INT;
|
||||
|
|
|
@ -270,9 +270,9 @@ static int ad5791_read_raw(struct iio_dev *indio_dev,
|
|||
*val >>= chan->scan_type.shift;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
*val2 = (((u64)st->vref_mv) * 1000000ULL) >> chan->scan_type.realbits;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
*val = st->vref_mv;
|
||||
*val2 = (1 << chan->scan_type.realbits) - 1;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
val64 = (((u64)st->vref_neg_mv) << chan->scan_type.realbits);
|
||||
do_div(val64, st->vref_mv);
|
||||
|
@ -287,11 +287,12 @@ static int ad5791_read_raw(struct iio_dev *indio_dev,
|
|||
static const struct iio_chan_spec_ext_info ad5791_ext_info[] = {
|
||||
{
|
||||
.name = "powerdown",
|
||||
.shared = true,
|
||||
.shared = IIO_SHARED_BY_TYPE,
|
||||
.read = ad5791_read_dac_powerdown,
|
||||
.write = ad5791_write_dac_powerdown,
|
||||
},
|
||||
IIO_ENUM("powerdown_mode", true, &ad5791_powerdown_mode_enum),
|
||||
IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE,
|
||||
&ad5791_powerdown_mode_enum),
|
||||
IIO_ENUM_AVAILABLE("powerdown_mode", &ad5791_powerdown_mode_enum),
|
||||
{ },
|
||||
};
|
||||
|
|
|
@ -169,6 +169,7 @@ static const struct iio_chan_spec_ext_info ad7303_ext_info[] = {
|
|||
.name = "powerdown",
|
||||
.read = ad7303_read_dac_powerdown,
|
||||
.write = ad7303_write_dac_powerdown,
|
||||
.shared = IIO_SEPARATE,
|
||||
},
|
||||
{ },
|
||||
};
|
||||
|
|
|
@ -82,15 +82,13 @@ static int max517_read_raw(struct iio_dev *indio_dev,
|
|||
long m)
|
||||
{
|
||||
struct max517_data *data = iio_priv(indio_dev);
|
||||
unsigned int scale_uv;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
/* Corresponds to Vref / 2^(bits) */
|
||||
scale_uv = (data->vref_mv[chan->channel] * 1000) >> 8;
|
||||
*val = scale_uv / 1000000;
|
||||
*val2 = scale_uv % 1000000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
*val = data->vref_mv[chan->channel];
|
||||
*val2 = 8;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -162,7 +160,6 @@ static int max517_probe(struct i2c_client *client,
|
|||
struct max517_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
struct max517_platform_data *platform_data = client->dev.platform_data;
|
||||
int err;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
|
@ -194,13 +191,7 @@ static int max517_probe(struct i2c_client *client,
|
|||
data->vref_mv[1] = platform_data->vref_mv[1];
|
||||
}
|
||||
|
||||
err = iio_device_register(indio_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
dev_info(&client->dev, "DAC registered\n");
|
||||
|
||||
return 0;
|
||||
return iio_device_register(indio_dev);
|
||||
}
|
||||
|
||||
static int max517_remove(struct i2c_client *client)
|
||||
|
|
|
@ -195,8 +195,9 @@ static const struct iio_chan_spec_ext_info mcp4725_ext_info[] = {
|
|||
.name = "powerdown",
|
||||
.read = mcp4725_read_powerdown,
|
||||
.write = mcp4725_write_powerdown,
|
||||
.shared = IIO_SEPARATE,
|
||||
},
|
||||
IIO_ENUM("powerdown_mode", false, &mcp4725_powerdown_mode_enum),
|
||||
IIO_ENUM("powerdown_mode", IIO_SEPARATE, &mcp4725_powerdown_mode_enum),
|
||||
IIO_ENUM_AVAILABLE("powerdown_mode", &mcp4725_powerdown_mode_enum),
|
||||
{ },
|
||||
};
|
||||
|
@ -238,17 +239,15 @@ static int mcp4725_read_raw(struct iio_dev *indio_dev,
|
|||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct mcp4725_data *data = iio_priv(indio_dev);
|
||||
unsigned long scale_uv;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
*val = data->dac_value;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
scale_uv = (data->vref_mv * 1000) >> 12;
|
||||
*val = scale_uv / 1000000;
|
||||
*val2 = scale_uv % 1000000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
*val = data->vref_mv;
|
||||
*val2 = 12;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -321,13 +320,7 @@ static int mcp4725_probe(struct i2c_client *client,
|
|||
data->powerdown_mode = pd ? pd-1 : 2; /* 500kohm_to_gnd */
|
||||
data->dac_value = (inbuf[1] << 4) | (inbuf[2] >> 4);
|
||||
|
||||
err = iio_device_register(indio_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
dev_info(&client->dev, "MCP4725 DAC registered\n");
|
||||
|
||||
return 0;
|
||||
return iio_device_register(indio_dev);
|
||||
}
|
||||
|
||||
static int mcp4725_remove(struct i2c_client *client)
|
||||
|
|
|
@ -351,6 +351,7 @@ static ssize_t adf4350_read(struct iio_dev *indio_dev,
|
|||
.read = adf4350_read, \
|
||||
.write = adf4350_write, \
|
||||
.private = _ident, \
|
||||
.shared = IIO_SEPARATE, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec_ext_info adf4350_ext_info[] = {
|
||||
|
|
|
@ -51,7 +51,6 @@ static int adis16080_read_sample(struct iio_dev *indio_dev,
|
|||
u16 addr, int *val)
|
||||
{
|
||||
struct adis16080_state *st = iio_priv(indio_dev);
|
||||
struct spi_message m;
|
||||
int ret;
|
||||
struct spi_transfer t[] = {
|
||||
{
|
||||
|
@ -66,11 +65,7 @@ static int adis16080_read_sample(struct iio_dev *indio_dev,
|
|||
|
||||
st->buf = cpu_to_be16(addr | ADIS16080_DIN_WRITE);
|
||||
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&t[0], &m);
|
||||
spi_message_add_tail(&t[1], &m);
|
||||
|
||||
ret = spi_sync(st->us, &m);
|
||||
ret = spi_sync_transfer(st->us, t, ARRAY_SIZE(t));
|
||||
if (ret == 0)
|
||||
*val = sign_extend32(be16_to_cpu(st->buf), 11);
|
||||
|
||||
|
|
|
@ -47,7 +47,6 @@ static int adis16130_spi_read(struct iio_dev *indio_dev, u8 reg_addr, u32 *val)
|
|||
{
|
||||
int ret;
|
||||
struct adis16130_state *st = iio_priv(indio_dev);
|
||||
struct spi_message msg;
|
||||
struct spi_transfer xfer = {
|
||||
.tx_buf = st->buf,
|
||||
.rx_buf = st->buf,
|
||||
|
@ -59,10 +58,7 @@ static int adis16130_spi_read(struct iio_dev *indio_dev, u8 reg_addr, u32 *val)
|
|||
st->buf[0] = ADIS16130_CON_RD | reg_addr;
|
||||
st->buf[1] = st->buf[2] = st->buf[3] = 0;
|
||||
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&xfer, &msg);
|
||||
ret = spi_sync(st->us, &msg);
|
||||
|
||||
ret = spi_sync_transfer(st->us, &xfer, 1);
|
||||
if (ret == 0)
|
||||
*val = (st->buf[1] << 16) | (st->buf[2] << 8) | st->buf[3];
|
||||
mutex_unlock(&st->buf_lock);
|
||||
|
@ -103,7 +99,6 @@ static int adis16130_read_raw(struct iio_dev *indio_dev,
|
|||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
switch (chan->type) {
|
||||
case IIO_ANGL_VEL:
|
||||
|
@ -115,7 +110,6 @@ static int adis16130_read_raw(struct iio_dev *indio_dev,
|
|||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
|
|
|
@ -239,7 +239,6 @@ static int adis16260_read_raw(struct iio_dev *indio_dev,
|
|||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = 250000 / 1453; /* 25 C = 0x00 */
|
||||
return IIO_VAL_INT;
|
||||
|
|
|
@ -90,7 +90,6 @@ static int adxrs450_spi_read_reg_16(struct iio_dev *indio_dev,
|
|||
u8 reg_address,
|
||||
u16 *val)
|
||||
{
|
||||
struct spi_message msg;
|
||||
struct adxrs450_state *st = iio_priv(indio_dev);
|
||||
u32 tx;
|
||||
int ret;
|
||||
|
@ -114,10 +113,7 @@ static int adxrs450_spi_read_reg_16(struct iio_dev *indio_dev,
|
|||
tx |= ADXRS450_P;
|
||||
|
||||
st->tx = cpu_to_be32(tx);
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&xfers[0], &msg);
|
||||
spi_message_add_tail(&xfers[1], &msg);
|
||||
ret = spi_sync(st->us, &msg);
|
||||
ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers));
|
||||
if (ret) {
|
||||
dev_err(&st->us->dev, "problem while reading 16 bit register 0x%02x\n",
|
||||
reg_address);
|
||||
|
@ -169,7 +165,6 @@ static int adxrs450_spi_write_reg_16(struct iio_dev *indio_dev,
|
|||
**/
|
||||
static int adxrs450_spi_sensor_data(struct iio_dev *indio_dev, s16 *val)
|
||||
{
|
||||
struct spi_message msg;
|
||||
struct adxrs450_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
struct spi_transfer xfers[] = {
|
||||
|
@ -188,10 +183,7 @@ static int adxrs450_spi_sensor_data(struct iio_dev *indio_dev, s16 *val)
|
|||
mutex_lock(&st->buf_lock);
|
||||
st->tx = cpu_to_be32(ADXRS450_SENSOR_DATA);
|
||||
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&xfers[0], &msg);
|
||||
spi_message_add_tail(&xfers[1], &msg);
|
||||
ret = spi_sync(st->us, &msg);
|
||||
ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers));
|
||||
if (ret) {
|
||||
dev_err(&st->us->dev, "Problem while reading sensor data\n");
|
||||
goto error_ret;
|
||||
|
@ -354,7 +346,6 @@ static int adxrs450_read_raw(struct iio_dev *indio_dev,
|
|||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW:
|
||||
ret = adxrs450_spi_read_reg_16(indio_dev, ADXRS450_QUAD1, &t);
|
||||
if (ret)
|
||||
|
|
|
@ -182,10 +182,11 @@ static const struct iio_info gyro_3d_info = {
|
|||
};
|
||||
|
||||
/* Function to push data to buffer */
|
||||
static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len)
|
||||
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, (u8 *)data);
|
||||
iio_push_to_buffers(indio_dev, data);
|
||||
}
|
||||
|
||||
/* Callback handler to send event after all samples are received and captured */
|
||||
|
@ -200,7 +201,7 @@ static int gyro_3d_proc_event(struct hid_sensor_hub_device *hsdev,
|
|||
gyro_state->common_attributes.data_ready);
|
||||
if (gyro_state->common_attributes.data_ready)
|
||||
hid_sensor_push_data(indio_dev,
|
||||
(u8 *)gyro_state->gyro_val,
|
||||
gyro_state->gyro_val,
|
||||
sizeof(gyro_state->gyro_val));
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -55,11 +55,8 @@ static irqreturn_t itg3200_trigger_handler(int irq, void *p)
|
|||
if (ret < 0)
|
||||
goto error_ret;
|
||||
|
||||
if (indio_dev->scan_timestamp)
|
||||
memcpy(buf + indio_dev->scan_bytes - sizeof(s64),
|
||||
&pf->timestamp, sizeof(pf->timestamp));
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, buf, pf->timestamp);
|
||||
|
||||
iio_push_to_buffers(indio_dev, (u8 *)buf);
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
error_ret:
|
||||
|
|
|
@ -32,16 +32,7 @@ int st_gyro_trig_set_state(struct iio_trigger *trig, bool state)
|
|||
|
||||
static int st_gyro_buffer_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = st_sensors_set_enable(indio_dev, true);
|
||||
if (err < 0)
|
||||
goto st_gyro_set_enable_error;
|
||||
|
||||
err = iio_sw_buffer_preenable(indio_dev);
|
||||
|
||||
st_gyro_set_enable_error:
|
||||
return err;
|
||||
return st_sensors_set_enable(indio_dev, true);
|
||||
}
|
||||
|
||||
static int st_gyro_buffer_postenable(struct iio_dev *indio_dev)
|
||||
|
|
|
@ -305,8 +305,9 @@ static const struct iio_trigger_ops st_gyro_trigger_ops = {
|
|||
int st_gyro_common_probe(struct iio_dev *indio_dev,
|
||||
struct st_sensors_platform_data *pdata)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *gdata = iio_priv(indio_dev);
|
||||
int irq = gdata->get_irq_data_ready(indio_dev);
|
||||
int err;
|
||||
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &gyro_info;
|
||||
|
@ -314,7 +315,7 @@ int st_gyro_common_probe(struct iio_dev *indio_dev,
|
|||
err = st_sensors_check_device_support(indio_dev,
|
||||
ARRAY_SIZE(st_gyro_sensors), st_gyro_sensors);
|
||||
if (err < 0)
|
||||
goto st_gyro_common_probe_error;
|
||||
return err;
|
||||
|
||||
gdata->num_data_channels = ST_GYRO_NUMBER_DATA_CHANNELS;
|
||||
gdata->multiread_bit = gdata->sensor->multi_read_bit;
|
||||
|
@ -327,13 +328,13 @@ int st_gyro_common_probe(struct iio_dev *indio_dev,
|
|||
|
||||
err = st_sensors_init_sensor(indio_dev, pdata);
|
||||
if (err < 0)
|
||||
goto st_gyro_common_probe_error;
|
||||
return err;
|
||||
|
||||
if (gdata->get_irq_data_ready(indio_dev) > 0) {
|
||||
err = st_gyro_allocate_ring(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_gyro_common_probe_error;
|
||||
err = st_gyro_allocate_ring(indio_dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (irq > 0) {
|
||||
err = st_sensors_allocate_trigger(indio_dev,
|
||||
ST_GYRO_TRIGGER_OPS);
|
||||
if (err < 0)
|
||||
|
@ -344,15 +345,14 @@ int st_gyro_common_probe(struct iio_dev *indio_dev,
|
|||
if (err)
|
||||
goto st_gyro_device_register_error;
|
||||
|
||||
return err;
|
||||
return 0;
|
||||
|
||||
st_gyro_device_register_error:
|
||||
if (gdata->get_irq_data_ready(indio_dev) > 0)
|
||||
if (irq > 0)
|
||||
st_sensors_deallocate_trigger(indio_dev);
|
||||
st_gyro_probe_trigger_error:
|
||||
if (gdata->get_irq_data_ready(indio_dev) > 0)
|
||||
st_gyro_deallocate_ring(indio_dev);
|
||||
st_gyro_common_probe_error:
|
||||
st_gyro_deallocate_ring(indio_dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(st_gyro_common_probe);
|
||||
|
@ -362,10 +362,10 @@ void st_gyro_common_remove(struct iio_dev *indio_dev)
|
|||
struct st_sensor_data *gdata = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
if (gdata->get_irq_data_ready(indio_dev) > 0) {
|
||||
if (gdata->get_irq_data_ready(indio_dev) > 0)
|
||||
st_sensors_deallocate_trigger(indio_dev);
|
||||
st_gyro_deallocate_ring(indio_dev);
|
||||
}
|
||||
|
||||
st_gyro_deallocate_ring(indio_dev);
|
||||
}
|
||||
EXPORT_SYMBOL(st_gyro_common_remove);
|
||||
|
||||
|
|
|
@ -30,9 +30,12 @@ int __iio_add_chan_devattr(const char *postfix,
|
|||
const char *buf,
|
||||
size_t len),
|
||||
u64 mask,
|
||||
bool generic,
|
||||
enum iio_shared_by shared_by,
|
||||
struct device *dev,
|
||||
struct list_head *attr_list);
|
||||
void iio_free_chan_devattr_list(struct list_head *attr_list);
|
||||
|
||||
ssize_t iio_format_value(char *buf, unsigned int type, int val, int val2);
|
||||
|
||||
/* Event interface flags */
|
||||
#define IIO_BUSY_BIT_POS 1
|
||||
|
@ -50,6 +53,7 @@ ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf,
|
|||
#define iio_buffer_read_first_n_outer_addr (&iio_buffer_read_first_n_outer)
|
||||
|
||||
void iio_disable_all_buffers(struct iio_dev *indio_dev);
|
||||
void iio_buffer_wakeup_poll(struct iio_dev *indio_dev);
|
||||
|
||||
#else
|
||||
|
||||
|
@ -57,11 +61,13 @@ void iio_disable_all_buffers(struct iio_dev *indio_dev);
|
|||
#define iio_buffer_read_first_n_outer_addr NULL
|
||||
|
||||
static inline void iio_disable_all_buffers(struct iio_dev *indio_dev) {}
|
||||
static inline void iio_buffer_wakeup_poll(struct iio_dev *indio_dev) {}
|
||||
|
||||
#endif
|
||||
|
||||
int iio_device_register_eventset(struct iio_dev *indio_dev);
|
||||
void iio_device_unregister_eventset(struct iio_dev *indio_dev);
|
||||
void iio_device_wakeup_eventset(struct iio_dev *indio_dev);
|
||||
int iio_event_getfd(struct iio_dev *indio_dev);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -82,13 +82,8 @@ irqreturn_t adis16400_trigger_handler(int irq, void *p)
|
|||
spi_setup(st->adis.spi);
|
||||
}
|
||||
|
||||
/* Guaranteed to be aligned with 8 byte boundary */
|
||||
if (indio_dev->scan_timestamp) {
|
||||
void *b = adis->buffer + indio_dev->scan_bytes - sizeof(s64);
|
||||
*(s64 *)b = pf->timestamp;
|
||||
}
|
||||
|
||||
iio_push_to_buffers(indio_dev, adis->buffer);
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, adis->buffer,
|
||||
pf->timestamp);
|
||||
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
|
|
|
@ -102,13 +102,8 @@ static irqreturn_t adis_trigger_handler(int irq, void *p)
|
|||
mutex_unlock(&adis->txrx_lock);
|
||||
}
|
||||
|
||||
/* Guaranteed to be aligned with 8 byte boundary */
|
||||
if (indio_dev->scan_timestamp) {
|
||||
void *b = adis->buffer + indio_dev->scan_bytes - sizeof(s64);
|
||||
*(s64 *)b = pf->timestamp;
|
||||
}
|
||||
|
||||
iio_push_to_buffers(indio_dev, adis->buffer);
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, adis->buffer,
|
||||
pf->timestamp);
|
||||
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
|
|
|
@ -124,7 +124,6 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
|
|||
u8 data[INV_MPU6050_OUTPUT_DATA_SIZE];
|
||||
u16 fifo_count;
|
||||
s64 timestamp;
|
||||
u64 *tmp;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (!(st->chip_config.accl_fifo_enable |
|
||||
|
@ -170,9 +169,8 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
|
|||
if (0 == result)
|
||||
timestamp = 0;
|
||||
|
||||
tmp = (u64 *)data;
|
||||
tmp[DIV_ROUND_UP(bytes_per_datum, 8)] = timestamp;
|
||||
result = iio_push_to_buffers(indio_dev, data);
|
||||
result = iio_push_to_buffers_with_timestamp(indio_dev, data,
|
||||
timestamp);
|
||||
if (result)
|
||||
goto flush_fifo;
|
||||
fifo_count -= bytes_per_datum;
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <linux/cdev.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include "iio_core.h"
|
||||
|
@ -31,16 +32,9 @@ static const char * const iio_endian_prefix[] = {
|
|||
[IIO_LE] = "le",
|
||||
};
|
||||
|
||||
static bool iio_buffer_is_active(struct iio_dev *indio_dev,
|
||||
struct iio_buffer *buf)
|
||||
static bool iio_buffer_is_active(struct iio_buffer *buf)
|
||||
{
|
||||
struct list_head *p;
|
||||
|
||||
list_for_each(p, &indio_dev->buffer_list)
|
||||
if (p == &buf->buffer_list)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
return !list_empty(&buf->buffer_list);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -55,6 +49,9 @@ ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf,
|
|||
struct iio_dev *indio_dev = filp->private_data;
|
||||
struct iio_buffer *rb = indio_dev->buffer;
|
||||
|
||||
if (!indio_dev->info)
|
||||
return -ENODEV;
|
||||
|
||||
if (!rb || !rb->access->read_first_n)
|
||||
return -EINVAL;
|
||||
return rb->access->read_first_n(rb, n, buf);
|
||||
|
@ -69,6 +66,9 @@ unsigned int iio_buffer_poll(struct file *filp,
|
|||
struct iio_dev *indio_dev = filp->private_data;
|
||||
struct iio_buffer *rb = indio_dev->buffer;
|
||||
|
||||
if (!indio_dev->info)
|
||||
return -ENODEV;
|
||||
|
||||
poll_wait(filp, &rb->pollq, wait);
|
||||
if (rb->stufftoread)
|
||||
return POLLIN | POLLRDNORM;
|
||||
|
@ -76,10 +76,27 @@ unsigned int iio_buffer_poll(struct file *filp,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* iio_buffer_wakeup_poll - Wakes up the buffer waitqueue
|
||||
* @indio_dev: The IIO device
|
||||
*
|
||||
* Wakes up the event waitqueue used for poll(). Should usually
|
||||
* be called when the device is unregistered.
|
||||
*/
|
||||
void iio_buffer_wakeup_poll(struct iio_dev *indio_dev)
|
||||
{
|
||||
if (!indio_dev->buffer)
|
||||
return;
|
||||
|
||||
wake_up(&indio_dev->buffer->pollq);
|
||||
}
|
||||
|
||||
void iio_buffer_init(struct iio_buffer *buffer)
|
||||
{
|
||||
INIT_LIST_HEAD(&buffer->demux_list);
|
||||
INIT_LIST_HEAD(&buffer->buffer_list);
|
||||
init_waitqueue_head(&buffer->pollq);
|
||||
kref_init(&buffer->ref);
|
||||
}
|
||||
EXPORT_SYMBOL(iio_buffer_init);
|
||||
|
||||
|
@ -146,7 +163,7 @@ static ssize_t iio_scan_el_store(struct device *dev,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (iio_buffer_is_active(indio_dev, indio_dev->buffer)) {
|
||||
if (iio_buffer_is_active(indio_dev->buffer)) {
|
||||
ret = -EBUSY;
|
||||
goto error_ret;
|
||||
}
|
||||
|
@ -192,7 +209,7 @@ static ssize_t iio_scan_el_ts_store(struct device *dev,
|
|||
return ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (iio_buffer_is_active(indio_dev, indio_dev->buffer)) {
|
||||
if (iio_buffer_is_active(indio_dev->buffer)) {
|
||||
ret = -EBUSY;
|
||||
goto error_ret;
|
||||
}
|
||||
|
@ -214,7 +231,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,
|
|||
&iio_show_scan_index,
|
||||
NULL,
|
||||
0,
|
||||
0,
|
||||
IIO_SEPARATE,
|
||||
&indio_dev->dev,
|
||||
&buffer->scan_el_dev_attr_list);
|
||||
if (ret)
|
||||
|
@ -249,29 +266,14 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,
|
|||
0,
|
||||
&indio_dev->dev,
|
||||
&buffer->scan_el_dev_attr_list);
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
attrcount++;
|
||||
ret = attrcount;
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void iio_buffer_remove_and_free_scan_dev_attr(struct iio_dev *indio_dev,
|
||||
struct iio_dev_attr *p)
|
||||
{
|
||||
kfree(p->dev_attr.attr.name);
|
||||
kfree(p);
|
||||
}
|
||||
|
||||
static void __iio_buffer_attr_cleanup(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct iio_dev_attr *p, *n;
|
||||
struct iio_buffer *buffer = indio_dev->buffer;
|
||||
|
||||
list_for_each_entry_safe(p, n,
|
||||
&buffer->scan_el_dev_attr_list, l)
|
||||
iio_buffer_remove_and_free_scan_dev_attr(indio_dev, p);
|
||||
}
|
||||
|
||||
static const char * const iio_scan_elements_group_name = "scan_elements";
|
||||
|
||||
int iio_buffer_register(struct iio_dev *indio_dev,
|
||||
|
@ -348,7 +350,7 @@ int iio_buffer_register(struct iio_dev *indio_dev,
|
|||
error_free_scan_mask:
|
||||
kfree(buffer->scan_mask);
|
||||
error_cleanup_dynamic:
|
||||
__iio_buffer_attr_cleanup(indio_dev);
|
||||
iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -358,7 +360,7 @@ void iio_buffer_unregister(struct iio_dev *indio_dev)
|
|||
{
|
||||
kfree(indio_dev->buffer->scan_mask);
|
||||
kfree(indio_dev->buffer->scan_el_group.attrs);
|
||||
__iio_buffer_attr_cleanup(indio_dev);
|
||||
iio_free_chan_devattr_list(&indio_dev->buffer->scan_el_dev_attr_list);
|
||||
}
|
||||
EXPORT_SYMBOL(iio_buffer_unregister);
|
||||
|
||||
|
@ -396,7 +398,7 @@ ssize_t iio_buffer_write_length(struct device *dev,
|
|||
return len;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (iio_buffer_is_active(indio_dev, indio_dev->buffer)) {
|
||||
if (iio_buffer_is_active(indio_dev->buffer)) {
|
||||
ret = -EBUSY;
|
||||
} else {
|
||||
if (buffer->access->set_length)
|
||||
|
@ -414,13 +416,11 @@ ssize_t iio_buffer_show_enable(struct device *dev,
|
|||
char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
return sprintf(buf, "%d\n",
|
||||
iio_buffer_is_active(indio_dev,
|
||||
indio_dev->buffer));
|
||||
return sprintf(buf, "%d\n", iio_buffer_is_active(indio_dev->buffer));
|
||||
}
|
||||
EXPORT_SYMBOL(iio_buffer_show_enable);
|
||||
|
||||
/* note NULL used as error indicator as it doesn't make sense. */
|
||||
/* Note NULL used as error indicator as it doesn't make sense. */
|
||||
static const unsigned long *iio_scan_mask_match(const unsigned long *av_masks,
|
||||
unsigned int masklength,
|
||||
const unsigned long *mask)
|
||||
|
@ -435,8 +435,8 @@ static const unsigned long *iio_scan_mask_match(const unsigned long *av_masks,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static int iio_compute_scan_bytes(struct iio_dev *indio_dev, const long *mask,
|
||||
bool timestamp)
|
||||
static int iio_compute_scan_bytes(struct iio_dev *indio_dev,
|
||||
const unsigned long *mask, bool timestamp)
|
||||
{
|
||||
const struct iio_chan_spec *ch;
|
||||
unsigned bytes = 0;
|
||||
|
@ -460,6 +460,19 @@ static int iio_compute_scan_bytes(struct iio_dev *indio_dev, const long *mask,
|
|||
return bytes;
|
||||
}
|
||||
|
||||
static void iio_buffer_activate(struct iio_dev *indio_dev,
|
||||
struct iio_buffer *buffer)
|
||||
{
|
||||
iio_buffer_get(buffer);
|
||||
list_add(&buffer->buffer_list, &indio_dev->buffer_list);
|
||||
}
|
||||
|
||||
static void iio_buffer_deactivate(struct iio_buffer *buffer)
|
||||
{
|
||||
list_del_init(&buffer->buffer_list);
|
||||
iio_buffer_put(buffer);
|
||||
}
|
||||
|
||||
void iio_disable_all_buffers(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct iio_buffer *buffer, *_buffer;
|
||||
|
@ -472,7 +485,7 @@ void iio_disable_all_buffers(struct iio_dev *indio_dev)
|
|||
|
||||
list_for_each_entry_safe(buffer, _buffer,
|
||||
&indio_dev->buffer_list, buffer_list)
|
||||
list_del_init(&buffer->buffer_list);
|
||||
iio_buffer_deactivate(buffer);
|
||||
|
||||
indio_dev->currentmode = INDIO_DIRECT_MODE;
|
||||
if (indio_dev->setup_ops->postdisable)
|
||||
|
@ -482,7 +495,21 @@ void iio_disable_all_buffers(struct iio_dev *indio_dev)
|
|||
kfree(indio_dev->active_scan_mask);
|
||||
}
|
||||
|
||||
int iio_update_buffers(struct iio_dev *indio_dev,
|
||||
static void iio_buffer_update_bytes_per_datum(struct iio_dev *indio_dev,
|
||||
struct iio_buffer *buffer)
|
||||
{
|
||||
unsigned int bytes;
|
||||
|
||||
if (!buffer->access->set_bytes_per_datum)
|
||||
return;
|
||||
|
||||
bytes = iio_compute_scan_bytes(indio_dev, buffer->scan_mask,
|
||||
buffer->scan_timestamp);
|
||||
|
||||
buffer->access->set_bytes_per_datum(buffer, bytes);
|
||||
}
|
||||
|
||||
static int __iio_update_buffers(struct iio_dev *indio_dev,
|
||||
struct iio_buffer *insert_buffer,
|
||||
struct iio_buffer *remove_buffer)
|
||||
{
|
||||
|
@ -512,9 +539,9 @@ int iio_update_buffers(struct iio_dev *indio_dev,
|
|||
indio_dev->active_scan_mask = NULL;
|
||||
|
||||
if (remove_buffer)
|
||||
list_del(&remove_buffer->buffer_list);
|
||||
iio_buffer_deactivate(remove_buffer);
|
||||
if (insert_buffer)
|
||||
list_add(&insert_buffer->buffer_list, &indio_dev->buffer_list);
|
||||
iio_buffer_activate(indio_dev, insert_buffer);
|
||||
|
||||
/* If no buffers in list, we are done */
|
||||
if (list_empty(&indio_dev->buffer_list)) {
|
||||
|
@ -524,7 +551,7 @@ int iio_update_buffers(struct iio_dev *indio_dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* What scan mask do we actually have ?*/
|
||||
/* What scan mask do we actually have? */
|
||||
compound_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength),
|
||||
sizeof(long), GFP_KERNEL);
|
||||
if (compound_mask == NULL) {
|
||||
|
@ -549,7 +576,7 @@ int iio_update_buffers(struct iio_dev *indio_dev,
|
|||
* Roll back.
|
||||
* Note can only occur when adding a buffer.
|
||||
*/
|
||||
list_del(&insert_buffer->buffer_list);
|
||||
iio_buffer_deactivate(insert_buffer);
|
||||
if (old_mask) {
|
||||
indio_dev->active_scan_mask = old_mask;
|
||||
success = -EINVAL;
|
||||
|
@ -579,7 +606,8 @@ int iio_update_buffers(struct iio_dev *indio_dev,
|
|||
iio_compute_scan_bytes(indio_dev,
|
||||
indio_dev->active_scan_mask,
|
||||
indio_dev->scan_timestamp);
|
||||
list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list)
|
||||
list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) {
|
||||
iio_buffer_update_bytes_per_datum(indio_dev, buffer);
|
||||
if (buffer->access->request_update) {
|
||||
ret = buffer->access->request_update(buffer);
|
||||
if (ret) {
|
||||
|
@ -588,6 +616,7 @@ int iio_update_buffers(struct iio_dev *indio_dev,
|
|||
goto error_run_postdisable;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (indio_dev->info->update_scan_mode) {
|
||||
ret = indio_dev->info
|
||||
->update_scan_mode(indio_dev,
|
||||
|
@ -597,7 +626,7 @@ int iio_update_buffers(struct iio_dev *indio_dev,
|
|||
goto error_run_postdisable;
|
||||
}
|
||||
}
|
||||
/* Definitely possible for devices to support both of these.*/
|
||||
/* Definitely possible for devices to support both of these. */
|
||||
if (indio_dev->modes & INDIO_BUFFER_TRIGGERED) {
|
||||
if (!indio_dev->trig) {
|
||||
printk(KERN_INFO "Buffer not started: no trigger\n");
|
||||
|
@ -608,7 +637,7 @@ int iio_update_buffers(struct iio_dev *indio_dev,
|
|||
indio_dev->currentmode = INDIO_BUFFER_TRIGGERED;
|
||||
} else if (indio_dev->modes & INDIO_BUFFER_HARDWARE) {
|
||||
indio_dev->currentmode = INDIO_BUFFER_HARDWARE;
|
||||
} else { /* should never be reached */
|
||||
} else { /* Should never be reached */
|
||||
ret = -EINVAL;
|
||||
goto error_run_postdisable;
|
||||
}
|
||||
|
@ -640,13 +669,50 @@ error_run_postdisable:
|
|||
error_remove_inserted:
|
||||
|
||||
if (insert_buffer)
|
||||
list_del(&insert_buffer->buffer_list);
|
||||
iio_buffer_deactivate(insert_buffer);
|
||||
indio_dev->active_scan_mask = old_mask;
|
||||
kfree(compound_mask);
|
||||
error_ret:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int iio_update_buffers(struct iio_dev *indio_dev,
|
||||
struct iio_buffer *insert_buffer,
|
||||
struct iio_buffer *remove_buffer)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (insert_buffer == remove_buffer)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&indio_dev->info_exist_lock);
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
if (insert_buffer && iio_buffer_is_active(insert_buffer))
|
||||
insert_buffer = NULL;
|
||||
|
||||
if (remove_buffer && !iio_buffer_is_active(remove_buffer))
|
||||
remove_buffer = NULL;
|
||||
|
||||
if (!insert_buffer && !remove_buffer) {
|
||||
ret = 0;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (indio_dev->info == NULL) {
|
||||
ret = -ENODEV;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
ret = __iio_update_buffers(indio_dev, insert_buffer, remove_buffer);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
mutex_unlock(&indio_dev->info_exist_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_update_buffers);
|
||||
|
||||
ssize_t iio_buffer_store_enable(struct device *dev,
|
||||
|
@ -657,7 +723,6 @@ ssize_t iio_buffer_store_enable(struct device *dev,
|
|||
int ret;
|
||||
bool requested_state;
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct iio_buffer *pbuf = indio_dev->buffer;
|
||||
bool inlist;
|
||||
|
||||
ret = strtobool(buf, &requested_state);
|
||||
|
@ -667,16 +732,16 @@ ssize_t iio_buffer_store_enable(struct device *dev,
|
|||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
/* Find out if it is in the list */
|
||||
inlist = iio_buffer_is_active(indio_dev, pbuf);
|
||||
inlist = iio_buffer_is_active(indio_dev->buffer);
|
||||
/* Already in desired state */
|
||||
if (inlist == requested_state)
|
||||
goto done;
|
||||
|
||||
if (requested_state)
|
||||
ret = iio_update_buffers(indio_dev,
|
||||
ret = __iio_update_buffers(indio_dev,
|
||||
indio_dev->buffer, NULL);
|
||||
else
|
||||
ret = iio_update_buffers(indio_dev,
|
||||
ret = __iio_update_buffers(indio_dev,
|
||||
NULL, indio_dev->buffer);
|
||||
|
||||
if (ret < 0)
|
||||
|
@ -687,24 +752,6 @@ done:
|
|||
}
|
||||
EXPORT_SYMBOL(iio_buffer_store_enable);
|
||||
|
||||
int iio_sw_buffer_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct iio_buffer *buffer;
|
||||
unsigned bytes;
|
||||
dev_dbg(&indio_dev->dev, "%s\n", __func__);
|
||||
|
||||
list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list)
|
||||
if (buffer->access->set_bytes_per_datum) {
|
||||
bytes = iio_compute_scan_bytes(indio_dev,
|
||||
buffer->scan_mask,
|
||||
buffer->scan_timestamp);
|
||||
|
||||
buffer->access->set_bytes_per_datum(buffer, bytes);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(iio_sw_buffer_preenable);
|
||||
|
||||
/**
|
||||
* iio_validate_scan_mask_onehot() - Validates that exactly one channel is selected
|
||||
* @indio_dev: the iio device
|
||||
|
@ -732,6 +779,7 @@ static bool iio_validate_scan_mask(struct iio_dev *indio_dev,
|
|||
|
||||
/**
|
||||
* iio_scan_mask_set() - set particular bit in the scan mask
|
||||
* @indio_dev: the iio device
|
||||
* @buffer: the buffer whose scan mask we are interested in
|
||||
* @bit: the bit to be set.
|
||||
*
|
||||
|
@ -752,7 +800,7 @@ int iio_scan_mask_set(struct iio_dev *indio_dev,
|
|||
if (trialmask == NULL)
|
||||
return -ENOMEM;
|
||||
if (!indio_dev->masklength) {
|
||||
WARN_ON("trying to set scanmask prior to registering buffer\n");
|
||||
WARN_ON("Trying to set scanmask prior to registering buffer\n");
|
||||
goto err_invalid_mask;
|
||||
}
|
||||
bitmap_copy(trialmask, buffer->scan_mask, indio_dev->masklength);
|
||||
|
@ -807,8 +855,8 @@ struct iio_demux_table {
|
|||
struct list_head l;
|
||||
};
|
||||
|
||||
static unsigned char *iio_demux(struct iio_buffer *buffer,
|
||||
unsigned char *datain)
|
||||
static const void *iio_demux(struct iio_buffer *buffer,
|
||||
const void *datain)
|
||||
{
|
||||
struct iio_demux_table *t;
|
||||
|
||||
|
@ -821,9 +869,9 @@ static unsigned char *iio_demux(struct iio_buffer *buffer,
|
|||
return buffer->demux_bounce;
|
||||
}
|
||||
|
||||
static int iio_push_to_buffer(struct iio_buffer *buffer, unsigned char *data)
|
||||
static int iio_push_to_buffer(struct iio_buffer *buffer, const void *data)
|
||||
{
|
||||
unsigned char *dataout = iio_demux(buffer, data);
|
||||
const void *dataout = iio_demux(buffer, data);
|
||||
|
||||
return buffer->access->store_to(buffer, dataout);
|
||||
}
|
||||
|
@ -838,7 +886,7 @@ static void iio_buffer_demux_free(struct iio_buffer *buffer)
|
|||
}
|
||||
|
||||
|
||||
int iio_push_to_buffers(struct iio_dev *indio_dev, unsigned char *data)
|
||||
int iio_push_to_buffers(struct iio_dev *indio_dev, const void *data)
|
||||
{
|
||||
int ret;
|
||||
struct iio_buffer *buf;
|
||||
|
@ -961,3 +1009,45 @@ error_clear_mux_table:
|
|||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_update_demux);
|
||||
|
||||
/**
|
||||
* iio_buffer_release() - Free a buffer's resources
|
||||
* @ref: Pointer to the kref embedded in the iio_buffer struct
|
||||
*
|
||||
* This function is called when the last reference to the buffer has been
|
||||
* dropped. It will typically free all resources allocated by the buffer. Do not
|
||||
* call this function manually, always use iio_buffer_put() when done using a
|
||||
* buffer.
|
||||
*/
|
||||
static void iio_buffer_release(struct kref *ref)
|
||||
{
|
||||
struct iio_buffer *buffer = container_of(ref, struct iio_buffer, ref);
|
||||
|
||||
buffer->access->release(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* iio_buffer_get() - Grab a reference to the buffer
|
||||
* @buffer: The buffer to grab a reference for, may be NULL
|
||||
*
|
||||
* Returns the pointer to the buffer that was passed into the function.
|
||||
*/
|
||||
struct iio_buffer *iio_buffer_get(struct iio_buffer *buffer)
|
||||
{
|
||||
if (buffer)
|
||||
kref_get(&buffer->ref);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_buffer_get);
|
||||
|
||||
/**
|
||||
* iio_buffer_put() - Release the reference to the buffer
|
||||
* @buffer: The buffer to release the reference for, may be NULL
|
||||
*/
|
||||
void iio_buffer_put(struct iio_buffer *buffer)
|
||||
{
|
||||
if (buffer)
|
||||
kref_put(&buffer->ref, iio_buffer_release);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_buffer_put);
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
* Based on elements of hwmon and input subsystems.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "iio-core: " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/idr.h>
|
||||
|
@ -28,6 +30,7 @@
|
|||
#include "iio_core_trigger.h"
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
|
||||
/* IDA to assign each registered device a unique id */
|
||||
static DEFINE_IDA(iio_ida);
|
||||
|
@ -101,6 +104,7 @@ static const char * const iio_chan_info_postfix[] = {
|
|||
[IIO_CHAN_INFO_PHASE] = "phase",
|
||||
[IIO_CHAN_INFO_HARDWAREGAIN] = "hardwaregain",
|
||||
[IIO_CHAN_INFO_HYSTERESIS] = "hysteresis",
|
||||
[IIO_CHAN_INFO_INT_TIME] = "integration_time",
|
||||
};
|
||||
|
||||
const struct iio_chan_spec
|
||||
|
@ -130,16 +134,13 @@ static int __init iio_init(void)
|
|||
/* Register sysfs bus */
|
||||
ret = bus_register(&iio_bus_type);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR
|
||||
"%s could not register bus type\n",
|
||||
__FILE__);
|
||||
pr_err("could not register bus type\n");
|
||||
goto error_nothing;
|
||||
}
|
||||
|
||||
ret = alloc_chrdev_region(&iio_devt, 0, IIO_DEV_MAX, "iio");
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "%s: failed to allocate char dev region\n",
|
||||
__FILE__);
|
||||
pr_err("failed to allocate char dev region\n");
|
||||
goto error_unregister_bus_type;
|
||||
}
|
||||
|
||||
|
@ -361,22 +362,20 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(iio_enum_write);
|
||||
|
||||
static ssize_t iio_read_channel_info(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
/**
|
||||
* iio_format_value() - Formats a IIO value into its string representation
|
||||
* @buf: The buffer to which the formated value gets written
|
||||
* @type: One of the IIO_VAL_... constants. This decides how the val and val2
|
||||
* parameters are formatted.
|
||||
* @val: First part of the value, exact meaning depends on the type parameter.
|
||||
* @val2: Second part of the value, exact meaning depends on the type parameter.
|
||||
*/
|
||||
ssize_t iio_format_value(char *buf, unsigned int type, int val, int val2)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
||||
unsigned long long tmp;
|
||||
int val, val2;
|
||||
bool scale_db = false;
|
||||
int ret = indio_dev->info->read_raw(indio_dev, this_attr->c,
|
||||
&val, &val2, this_attr->address);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
switch (ret) {
|
||||
switch (type) {
|
||||
case IIO_VAL_INT:
|
||||
return sprintf(buf, "%d\n", val);
|
||||
case IIO_VAL_INT_PLUS_MICRO_DB:
|
||||
|
@ -408,6 +407,22 @@ static ssize_t iio_read_channel_info(struct device *dev,
|
|||
}
|
||||
}
|
||||
|
||||
static ssize_t iio_read_channel_info(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
||||
int val, val2;
|
||||
int ret = indio_dev->info->read_raw(indio_dev, this_attr->c,
|
||||
&val, &val2, this_attr->address);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return iio_format_value(buf, ret, val, val2);
|
||||
}
|
||||
|
||||
/**
|
||||
* iio_str_to_fixpoint() - Parse a fixed-point number from a string
|
||||
* @str: The string to parse
|
||||
|
@ -516,14 +531,15 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
|
|||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t len),
|
||||
bool generic)
|
||||
enum iio_shared_by shared_by)
|
||||
{
|
||||
int ret;
|
||||
char *name_format, *full_postfix;
|
||||
int ret = 0;
|
||||
char *name_format = NULL;
|
||||
char *full_postfix;
|
||||
sysfs_attr_init(&dev_attr->attr);
|
||||
|
||||
/* Build up postfix of <extend_name>_<modifier>_postfix */
|
||||
if (chan->modified && !generic) {
|
||||
if (chan->modified && (shared_by == IIO_SEPARATE)) {
|
||||
if (chan->extend_name)
|
||||
full_postfix = kasprintf(GFP_KERNEL, "%s_%s_%s",
|
||||
iio_modifier_names[chan
|
||||
|
@ -544,53 +560,78 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
|
|||
chan->extend_name,
|
||||
postfix);
|
||||
}
|
||||
if (full_postfix == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
if (full_postfix == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (chan->differential) { /* Differential can not have modifier */
|
||||
if (generic)
|
||||
switch (shared_by) {
|
||||
case IIO_SHARED_BY_ALL:
|
||||
name_format = kasprintf(GFP_KERNEL, "%s", full_postfix);
|
||||
break;
|
||||
case IIO_SHARED_BY_DIR:
|
||||
name_format = 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",
|
||||
iio_direction[chan->output],
|
||||
iio_chan_type_name_spec[chan->type],
|
||||
iio_chan_type_name_spec[chan->type],
|
||||
full_postfix);
|
||||
else if (chan->indexed)
|
||||
break;
|
||||
case IIO_SEPARATE:
|
||||
if (!chan->indexed) {
|
||||
WARN_ON("Differential channels must be indexed\n");
|
||||
ret = -EINVAL;
|
||||
goto error_free_full_postfix;
|
||||
}
|
||||
name_format
|
||||
= kasprintf(GFP_KERNEL, "%s_%s%d-%s%d_%s",
|
||||
= kasprintf(GFP_KERNEL,
|
||||
"%s_%s%d-%s%d_%s",
|
||||
iio_direction[chan->output],
|
||||
iio_chan_type_name_spec[chan->type],
|
||||
chan->channel,
|
||||
iio_chan_type_name_spec[chan->type],
|
||||
chan->channel2,
|
||||
full_postfix);
|
||||
else {
|
||||
WARN_ON("Differential channels must be indexed\n");
|
||||
ret = -EINVAL;
|
||||
goto error_free_full_postfix;
|
||||
break;
|
||||
}
|
||||
} else { /* Single ended */
|
||||
if (generic)
|
||||
name_format
|
||||
= kasprintf(GFP_KERNEL, "%s_%s_%s",
|
||||
iio_direction[chan->output],
|
||||
iio_chan_type_name_spec[chan->type],
|
||||
full_postfix);
|
||||
else if (chan->indexed)
|
||||
name_format
|
||||
= kasprintf(GFP_KERNEL, "%s_%s%d_%s",
|
||||
iio_direction[chan->output],
|
||||
iio_chan_type_name_spec[chan->type],
|
||||
chan->channel,
|
||||
full_postfix);
|
||||
else
|
||||
switch (shared_by) {
|
||||
case IIO_SHARED_BY_ALL:
|
||||
name_format = kasprintf(GFP_KERNEL, "%s", full_postfix);
|
||||
break;
|
||||
case IIO_SHARED_BY_DIR:
|
||||
name_format = 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",
|
||||
iio_direction[chan->output],
|
||||
iio_chan_type_name_spec[chan->type],
|
||||
full_postfix);
|
||||
break;
|
||||
|
||||
case IIO_SEPARATE:
|
||||
if (chan->indexed)
|
||||
name_format
|
||||
= 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",
|
||||
iio_direction[chan->output],
|
||||
iio_chan_type_name_spec[chan->type],
|
||||
full_postfix);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (name_format == NULL) {
|
||||
ret = -ENOMEM;
|
||||
|
@ -614,16 +655,11 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
|
|||
dev_attr->attr.mode |= S_IWUSR;
|
||||
dev_attr->store = writefunc;
|
||||
}
|
||||
kfree(name_format);
|
||||
kfree(full_postfix);
|
||||
|
||||
return 0;
|
||||
|
||||
error_free_name_format:
|
||||
kfree(name_format);
|
||||
error_free_full_postfix:
|
||||
kfree(full_postfix);
|
||||
error_ret:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -642,21 +678,21 @@ int __iio_add_chan_devattr(const char *postfix,
|
|||
const char *buf,
|
||||
size_t len),
|
||||
u64 mask,
|
||||
bool generic,
|
||||
enum iio_shared_by shared_by,
|
||||
struct device *dev,
|
||||
struct list_head *attr_list)
|
||||
{
|
||||
int ret;
|
||||
struct iio_dev_attr *iio_attr, *t;
|
||||
|
||||
iio_attr = kzalloc(sizeof *iio_attr, GFP_KERNEL);
|
||||
iio_attr = kzalloc(sizeof(*iio_attr), GFP_KERNEL);
|
||||
if (iio_attr == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
ret = __iio_device_attr_init(&iio_attr->dev_attr,
|
||||
postfix, chan,
|
||||
readfunc, writefunc, generic);
|
||||
readfunc, writefunc, shared_by);
|
||||
if (ret)
|
||||
goto error_iio_dev_attr_free;
|
||||
iio_attr->c = chan;
|
||||
|
@ -664,7 +700,7 @@ int __iio_add_chan_devattr(const char *postfix,
|
|||
list_for_each_entry(t, attr_list, l)
|
||||
if (strcmp(t->dev_attr.attr.name,
|
||||
iio_attr->dev_attr.attr.name) == 0) {
|
||||
if (!generic)
|
||||
if (shared_by == IIO_SEPARATE)
|
||||
dev_err(dev, "tried to double register : %s\n",
|
||||
t->dev_attr.attr.name);
|
||||
ret = -EBUSY;
|
||||
|
@ -682,45 +718,67 @@ error_ret:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int iio_device_add_info_mask_type(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
enum iio_shared_by shared_by,
|
||||
const long *infomask)
|
||||
{
|
||||
int i, ret, attrcount = 0;
|
||||
|
||||
for_each_set_bit(i, infomask, sizeof(infomask)*8) {
|
||||
ret = __iio_add_chan_devattr(iio_chan_info_postfix[i],
|
||||
chan,
|
||||
&iio_read_channel_info,
|
||||
&iio_write_channel_info,
|
||||
i,
|
||||
shared_by,
|
||||
&indio_dev->dev,
|
||||
&indio_dev->channel_attr_list);
|
||||
if ((ret == -EBUSY) && (shared_by != IIO_SEPARATE))
|
||||
continue;
|
||||
else if (ret < 0)
|
||||
return ret;
|
||||
attrcount++;
|
||||
}
|
||||
|
||||
return attrcount;
|
||||
}
|
||||
|
||||
static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan)
|
||||
{
|
||||
int ret, attrcount = 0;
|
||||
int i;
|
||||
const struct iio_chan_spec_ext_info *ext_info;
|
||||
|
||||
if (chan->channel < 0)
|
||||
return 0;
|
||||
for_each_set_bit(i, &chan->info_mask_separate, sizeof(long)*8) {
|
||||
ret = __iio_add_chan_devattr(iio_chan_info_postfix[i],
|
||||
chan,
|
||||
&iio_read_channel_info,
|
||||
&iio_write_channel_info,
|
||||
i,
|
||||
0,
|
||||
&indio_dev->dev,
|
||||
&indio_dev->channel_attr_list);
|
||||
if (ret < 0)
|
||||
goto error_ret;
|
||||
attrcount++;
|
||||
}
|
||||
for_each_set_bit(i, &chan->info_mask_shared_by_type, sizeof(long)*8) {
|
||||
ret = __iio_add_chan_devattr(iio_chan_info_postfix[i],
|
||||
chan,
|
||||
&iio_read_channel_info,
|
||||
&iio_write_channel_info,
|
||||
i,
|
||||
1,
|
||||
&indio_dev->dev,
|
||||
&indio_dev->channel_attr_list);
|
||||
if (ret == -EBUSY) {
|
||||
ret = 0;
|
||||
continue;
|
||||
} else if (ret < 0) {
|
||||
goto error_ret;
|
||||
}
|
||||
attrcount++;
|
||||
}
|
||||
ret = iio_device_add_info_mask_type(indio_dev, chan,
|
||||
IIO_SEPARATE,
|
||||
&chan->info_mask_separate);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
attrcount += ret;
|
||||
|
||||
ret = iio_device_add_info_mask_type(indio_dev, chan,
|
||||
IIO_SHARED_BY_TYPE,
|
||||
&chan->info_mask_shared_by_type);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
attrcount += ret;
|
||||
|
||||
ret = iio_device_add_info_mask_type(indio_dev, chan,
|
||||
IIO_SHARED_BY_DIR,
|
||||
&chan->info_mask_shared_by_dir);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
attrcount += ret;
|
||||
|
||||
ret = iio_device_add_info_mask_type(indio_dev, chan,
|
||||
IIO_SHARED_BY_ALL,
|
||||
&chan->info_mask_shared_by_all);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
attrcount += ret;
|
||||
|
||||
if (chan->ext_info) {
|
||||
unsigned int i = 0;
|
||||
|
@ -740,22 +798,31 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev,
|
|||
continue;
|
||||
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
return ret;
|
||||
|
||||
attrcount++;
|
||||
}
|
||||
}
|
||||
|
||||
ret = attrcount;
|
||||
error_ret:
|
||||
return ret;
|
||||
return attrcount;
|
||||
}
|
||||
|
||||
static void iio_device_remove_and_free_read_attr(struct iio_dev *indio_dev,
|
||||
struct iio_dev_attr *p)
|
||||
/**
|
||||
* iio_free_chan_devattr_list() - Free a list of IIO device attributes
|
||||
* @attr_list: List of IIO device attributes
|
||||
*
|
||||
* This function frees the memory allocated for each of the IIO device
|
||||
* attributes in the list. Note: if you want to reuse the list after calling
|
||||
* this function you have to reinitialize it using INIT_LIST_HEAD().
|
||||
*/
|
||||
void iio_free_chan_devattr_list(struct list_head *attr_list)
|
||||
{
|
||||
kfree(p->dev_attr.attr.name);
|
||||
kfree(p);
|
||||
struct iio_dev_attr *p, *n;
|
||||
|
||||
list_for_each_entry_safe(p, n, attr_list, l) {
|
||||
kfree(p->dev_attr.attr.name);
|
||||
kfree(p);
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t iio_show_dev_name(struct device *dev,
|
||||
|
@ -771,7 +838,7 @@ static DEVICE_ATTR(name, S_IRUGO, iio_show_dev_name, NULL);
|
|||
static int iio_device_register_sysfs(struct iio_dev *indio_dev)
|
||||
{
|
||||
int i, ret = 0, attrcount, attrn, attrcount_orig = 0;
|
||||
struct iio_dev_attr *p, *n;
|
||||
struct iio_dev_attr *p;
|
||||
struct attribute **attr;
|
||||
|
||||
/* First count elements in any existing group */
|
||||
|
@ -824,11 +891,7 @@ static int iio_device_register_sysfs(struct iio_dev *indio_dev)
|
|||
return 0;
|
||||
|
||||
error_clear_attrs:
|
||||
list_for_each_entry_safe(p, n,
|
||||
&indio_dev->channel_attr_list, l) {
|
||||
list_del(&p->l);
|
||||
iio_device_remove_and_free_read_attr(indio_dev, p);
|
||||
}
|
||||
iio_free_chan_devattr_list(&indio_dev->channel_attr_list);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -836,12 +899,7 @@ error_clear_attrs:
|
|||
static void iio_device_unregister_sysfs(struct iio_dev *indio_dev)
|
||||
{
|
||||
|
||||
struct iio_dev_attr *p, *n;
|
||||
|
||||
list_for_each_entry_safe(p, n, &indio_dev->channel_attr_list, l) {
|
||||
list_del(&p->l);
|
||||
iio_device_remove_and_free_read_attr(indio_dev, p);
|
||||
}
|
||||
iio_free_chan_devattr_list(&indio_dev->channel_attr_list);
|
||||
kfree(indio_dev->chan_attr_group.attrs);
|
||||
}
|
||||
|
||||
|
@ -853,6 +911,8 @@ static void iio_dev_release(struct device *device)
|
|||
iio_device_unregister_eventset(indio_dev);
|
||||
iio_device_unregister_sysfs(indio_dev);
|
||||
|
||||
iio_buffer_put(indio_dev->buffer);
|
||||
|
||||
ida_simple_remove(&iio_ida, indio_dev->id);
|
||||
kfree(indio_dev);
|
||||
}
|
||||
|
@ -890,7 +950,7 @@ struct iio_dev *iio_device_alloc(int sizeof_priv)
|
|||
dev->id = ida_simple_get(&iio_ida, 0, 0, GFP_KERNEL);
|
||||
if (dev->id < 0) {
|
||||
/* cannot use a dev_err as the name isn't available */
|
||||
printk(KERN_ERR "Failed to get id\n");
|
||||
pr_err("failed to get device id\n");
|
||||
kfree(dev);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -995,6 +1055,9 @@ static long iio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|||
int __user *ip = (int __user *)arg;
|
||||
int fd;
|
||||
|
||||
if (!indio_dev->info)
|
||||
return -ENODEV;
|
||||
|
||||
if (cmd == IIO_GET_EVENT_FD_IOCTL) {
|
||||
fd = iio_event_getfd(indio_dev);
|
||||
if (copy_to_user(ip, &fd, sizeof(fd)))
|
||||
|
@ -1091,6 +1154,10 @@ void iio_device_unregister(struct iio_dev *indio_dev)
|
|||
iio_disable_all_buffers(indio_dev);
|
||||
|
||||
indio_dev->info = NULL;
|
||||
|
||||
iio_device_wakeup_eventset(indio_dev);
|
||||
iio_buffer_wakeup_poll(indio_dev);
|
||||
|
||||
mutex_unlock(&indio_dev->info_exist_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(iio_device_unregister);
|
||||
|
|
|
@ -76,6 +76,9 @@ static unsigned int iio_event_poll(struct file *filep,
|
|||
struct iio_event_interface *ev_int = indio_dev->event_interface;
|
||||
unsigned int events = 0;
|
||||
|
||||
if (!indio_dev->info)
|
||||
return -ENODEV;
|
||||
|
||||
poll_wait(filep, &ev_int->wait, wait);
|
||||
|
||||
spin_lock_irq(&ev_int->wait.lock);
|
||||
|
@ -96,6 +99,9 @@ static ssize_t iio_event_chrdev_read(struct file *filep,
|
|||
unsigned int copied;
|
||||
int ret;
|
||||
|
||||
if (!indio_dev->info)
|
||||
return -ENODEV;
|
||||
|
||||
if (count < sizeof(struct iio_event_data))
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -107,9 +113,14 @@ static ssize_t iio_event_chrdev_read(struct file *filep,
|
|||
}
|
||||
/* Blocking on device; waiting for something to be there */
|
||||
ret = wait_event_interruptible_locked_irq(ev_int->wait,
|
||||
!kfifo_is_empty(&ev_int->det_events));
|
||||
!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;
|
||||
}
|
||||
/* Single access device so no one else can get the data */
|
||||
}
|
||||
|
||||
|
@ -166,7 +177,7 @@ int iio_event_getfd(struct iio_dev *indio_dev)
|
|||
iio_device_get(indio_dev);
|
||||
|
||||
fd = anon_inode_getfd("iio:event", &iio_event_chrdev_fileops,
|
||||
indio_dev, O_RDONLY);
|
||||
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);
|
||||
|
@ -190,6 +201,27 @@ static const char * const iio_ev_dir_text[] = {
|
|||
[IIO_EV_DIR_FALLING] = "falling"
|
||||
};
|
||||
|
||||
static const char * const iio_ev_info_text[] = {
|
||||
[IIO_EV_INFO_ENABLE] = "en",
|
||||
[IIO_EV_INFO_VALUE] = "value",
|
||||
[IIO_EV_INFO_HYSTERESIS] = "hysteresis",
|
||||
};
|
||||
|
||||
static enum iio_event_direction iio_ev_attr_dir(struct iio_dev_attr *attr)
|
||||
{
|
||||
return attr->c->event_spec[attr->address & 0xffff].dir;
|
||||
}
|
||||
|
||||
static enum iio_event_type iio_ev_attr_type(struct iio_dev_attr *attr)
|
||||
{
|
||||
return attr->c->event_spec[attr->address & 0xffff].type;
|
||||
}
|
||||
|
||||
static enum iio_event_info iio_ev_attr_info(struct iio_dev_attr *attr)
|
||||
{
|
||||
return (attr->address >> 16) & 0xffff;
|
||||
}
|
||||
|
||||
static ssize_t iio_ev_state_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
|
@ -204,9 +236,14 @@ static ssize_t iio_ev_state_store(struct device *dev,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = indio_dev->info->write_event_config(indio_dev,
|
||||
this_attr->address,
|
||||
val);
|
||||
if (indio_dev->info->write_event_config)
|
||||
ret = indio_dev->info->write_event_config(indio_dev,
|
||||
this_attr->address, val);
|
||||
else
|
||||
ret = indio_dev->info->write_event_config_new(indio_dev,
|
||||
this_attr->c, iio_ev_attr_type(this_attr),
|
||||
iio_ev_attr_dir(this_attr), val);
|
||||
|
||||
return (ret < 0) ? ret : len;
|
||||
}
|
||||
|
||||
|
@ -216,9 +253,15 @@ static ssize_t iio_ev_state_show(struct device *dev,
|
|||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
||||
int val = indio_dev->info->read_event_config(indio_dev,
|
||||
this_attr->address);
|
||||
int val;
|
||||
|
||||
if (indio_dev->info->read_event_config)
|
||||
val = indio_dev->info->read_event_config(indio_dev,
|
||||
this_attr->address);
|
||||
else
|
||||
val = indio_dev->info->read_event_config_new(indio_dev,
|
||||
this_attr->c, iio_ev_attr_type(this_attr),
|
||||
iio_ev_attr_dir(this_attr));
|
||||
if (val < 0)
|
||||
return val;
|
||||
else
|
||||
|
@ -231,14 +274,24 @@ static ssize_t iio_ev_value_show(struct device *dev,
|
|||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
||||
int val, ret;
|
||||
int val, val2;
|
||||
int ret;
|
||||
|
||||
ret = indio_dev->info->read_event_value(indio_dev,
|
||||
this_attr->address, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%d\n", val);
|
||||
if (indio_dev->info->read_event_value) {
|
||||
ret = indio_dev->info->read_event_value(indio_dev,
|
||||
this_attr->address, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return sprintf(buf, "%d\n", val);
|
||||
} else {
|
||||
ret = indio_dev->info->read_event_value_new(indio_dev,
|
||||
this_attr->c, iio_ev_attr_type(this_attr),
|
||||
iio_ev_attr_dir(this_attr), iio_ev_attr_info(this_attr),
|
||||
&val, &val2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return iio_format_value(buf, ret, val, val2);
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t iio_ev_value_store(struct device *dev,
|
||||
|
@ -248,25 +301,120 @@ static ssize_t iio_ev_value_store(struct device *dev,
|
|||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
||||
int val;
|
||||
int val, val2;
|
||||
int ret;
|
||||
|
||||
if (!indio_dev->info->write_event_value)
|
||||
if (!indio_dev->info->write_event_value &&
|
||||
!indio_dev->info->write_event_value_new)
|
||||
return -EINVAL;
|
||||
|
||||
ret = kstrtoint(buf, 10, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = indio_dev->info->write_event_value(indio_dev, this_attr->address,
|
||||
val);
|
||||
if (indio_dev->info->write_event_value) {
|
||||
ret = kstrtoint(buf, 10, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = indio_dev->info->write_event_value(indio_dev,
|
||||
this_attr->address, val);
|
||||
} else {
|
||||
ret = iio_str_to_fixpoint(buf, 100000, &val, &val2);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = indio_dev->info->write_event_value_new(indio_dev,
|
||||
this_attr->c, iio_ev_attr_type(this_attr),
|
||||
iio_ev_attr_dir(this_attr), iio_ev_attr_info(this_attr),
|
||||
val, val2);
|
||||
}
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int iio_device_add_event_sysfs(struct iio_dev *indio_dev,
|
||||
static int iio_device_add_event(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, unsigned int spec_index,
|
||||
enum iio_event_type type, enum iio_event_direction dir,
|
||||
enum iio_shared_by shared_by, const unsigned long *mask)
|
||||
{
|
||||
ssize_t (*show)(struct device *, struct device_attribute *, char *);
|
||||
ssize_t (*store)(struct device *, struct device_attribute *,
|
||||
const char *, size_t);
|
||||
unsigned int attrcount = 0;
|
||||
unsigned int i;
|
||||
char *postfix;
|
||||
int ret;
|
||||
|
||||
for_each_set_bit(i, mask, sizeof(*mask)) {
|
||||
postfix = kasprintf(GFP_KERNEL, "%s_%s_%s",
|
||||
iio_ev_type_text[type], iio_ev_dir_text[dir],
|
||||
iio_ev_info_text[i]);
|
||||
if (postfix == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (i == IIO_EV_INFO_ENABLE) {
|
||||
show = iio_ev_state_show;
|
||||
store = iio_ev_state_store;
|
||||
} else {
|
||||
show = iio_ev_value_show;
|
||||
store = iio_ev_value_store;
|
||||
}
|
||||
|
||||
ret = __iio_add_chan_devattr(postfix, chan, show, store,
|
||||
(i << 16) | spec_index, shared_by, &indio_dev->dev,
|
||||
&indio_dev->event_interface->dev_attr_list);
|
||||
kfree(postfix);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
attrcount++;
|
||||
}
|
||||
|
||||
return attrcount;
|
||||
}
|
||||
|
||||
static int iio_device_add_event_sysfs_new(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan)
|
||||
{
|
||||
int ret = 0, i, attrcount = 0;
|
||||
enum iio_event_direction dir;
|
||||
enum iio_event_type type;
|
||||
|
||||
for (i = 0; i < chan->num_event_specs; i++) {
|
||||
type = chan->event_spec[i].type;
|
||||
dir = chan->event_spec[i].dir;
|
||||
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
attrcount += ret;
|
||||
}
|
||||
ret = attrcount;
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iio_device_add_event_sysfs_old(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan)
|
||||
{
|
||||
int ret = 0, i, attrcount = 0;
|
||||
|
@ -339,15 +487,14 @@ error_ret:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static inline void __iio_remove_event_config_attrs(struct iio_dev *indio_dev)
|
||||
|
||||
static int iio_device_add_event_sysfs(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan)
|
||||
{
|
||||
struct iio_dev_attr *p, *n;
|
||||
list_for_each_entry_safe(p, n,
|
||||
&indio_dev->event_interface->
|
||||
dev_attr_list, l) {
|
||||
kfree(p->dev_attr.attr.name);
|
||||
kfree(p);
|
||||
}
|
||||
if (chan->event_mask)
|
||||
return iio_device_add_event_sysfs_old(indio_dev, chan);
|
||||
else
|
||||
return iio_device_add_event_sysfs_new(indio_dev, chan);
|
||||
}
|
||||
|
||||
static inline int __iio_add_event_config_attrs(struct iio_dev *indio_dev)
|
||||
|
@ -369,9 +516,12 @@ static bool iio_check_for_dynamic_events(struct iio_dev *indio_dev)
|
|||
{
|
||||
int j;
|
||||
|
||||
for (j = 0; j < indio_dev->num_channels; j++)
|
||||
for (j = 0; j < indio_dev->num_channels; j++) {
|
||||
if (indio_dev->channels[j].event_mask != 0)
|
||||
return true;
|
||||
if (indio_dev->channels[j].num_event_specs != 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -441,18 +591,32 @@ int iio_device_register_eventset(struct iio_dev *indio_dev)
|
|||
return 0;
|
||||
|
||||
error_free_setup_event_lines:
|
||||
__iio_remove_event_config_attrs(indio_dev);
|
||||
iio_free_chan_devattr_list(&indio_dev->event_interface->dev_attr_list);
|
||||
kfree(indio_dev->event_interface);
|
||||
error_ret:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* iio_device_wakeup_eventset - Wakes up the event waitqueue
|
||||
* @indio_dev: The IIO device
|
||||
*
|
||||
* Wakes up the event waitqueue used for poll() and blocking read().
|
||||
* Should usually be called when the device is unregistered.
|
||||
*/
|
||||
void iio_device_wakeup_eventset(struct iio_dev *indio_dev)
|
||||
{
|
||||
if (indio_dev->event_interface == NULL)
|
||||
return;
|
||||
wake_up(&indio_dev->event_interface->wait);
|
||||
}
|
||||
|
||||
void iio_device_unregister_eventset(struct iio_dev *indio_dev)
|
||||
{
|
||||
if (indio_dev->event_interface == NULL)
|
||||
return;
|
||||
__iio_remove_event_config_attrs(indio_dev);
|
||||
iio_free_chan_devattr_list(&indio_dev->event_interface->dev_attr_list);
|
||||
kfree(indio_dev->event_interface->group.attrs);
|
||||
kfree(indio_dev->event_interface);
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include <linux/iio/trigger_consumer.h>
|
||||
|
||||
static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {
|
||||
.preenable = &iio_sw_buffer_preenable,
|
||||
.postenable = &iio_triggered_buffer_postenable,
|
||||
.predisable = &iio_triggered_buffer_predisable,
|
||||
};
|
||||
|
@ -47,14 +46,17 @@ int iio_triggered_buffer_setup(struct iio_dev *indio_dev,
|
|||
irqreturn_t (*pollfunc_th)(int irq, void *p),
|
||||
const struct iio_buffer_setup_ops *setup_ops)
|
||||
{
|
||||
struct iio_buffer *buffer;
|
||||
int ret;
|
||||
|
||||
indio_dev->buffer = iio_kfifo_allocate(indio_dev);
|
||||
if (!indio_dev->buffer) {
|
||||
buffer = iio_kfifo_allocate(indio_dev);
|
||||
if (!buffer) {
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
|
||||
iio_device_attach_buffer(indio_dev, buffer);
|
||||
|
||||
indio_dev->pollfunc = iio_alloc_pollfunc(pollfunc_bh,
|
||||
pollfunc_th,
|
||||
IRQF_ONESHOT,
|
||||
|
|
|
@ -7,10 +7,12 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/poll.h>
|
||||
|
||||
struct iio_kfifo {
|
||||
struct iio_buffer buffer;
|
||||
struct kfifo kf;
|
||||
struct mutex user_lock;
|
||||
int update_needed;
|
||||
};
|
||||
|
||||
|
@ -31,13 +33,18 @@ static int iio_request_update_kfifo(struct iio_buffer *r)
|
|||
int ret = 0;
|
||||
struct iio_kfifo *buf = iio_to_kfifo(r);
|
||||
|
||||
if (!buf->update_needed)
|
||||
goto error_ret;
|
||||
kfifo_free(&buf->kf);
|
||||
ret = __iio_allocate_kfifo(buf, buf->buffer.bytes_per_datum,
|
||||
mutex_lock(&buf->user_lock);
|
||||
if (buf->update_needed) {
|
||||
kfifo_free(&buf->kf);
|
||||
ret = __iio_allocate_kfifo(buf, buf->buffer.bytes_per_datum,
|
||||
buf->buffer.length);
|
||||
buf->update_needed = false;
|
||||
} else {
|
||||
kfifo_reset_out(&buf->kf);
|
||||
}
|
||||
r->stufftoread = false;
|
||||
error_ret:
|
||||
mutex_unlock(&buf->user_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -94,7 +101,7 @@ static int iio_set_length_kfifo(struct iio_buffer *r, int length)
|
|||
}
|
||||
|
||||
static int iio_store_to_kfifo(struct iio_buffer *r,
|
||||
u8 *data)
|
||||
const void *data)
|
||||
{
|
||||
int ret;
|
||||
struct iio_kfifo *kf = iio_to_kfifo(r);
|
||||
|
@ -102,7 +109,7 @@ static int iio_store_to_kfifo(struct iio_buffer *r,
|
|||
if (ret != 1)
|
||||
return -EBUSY;
|
||||
r->stufftoread = true;
|
||||
wake_up_interruptible(&r->pollq);
|
||||
wake_up_interruptible_poll(&r->pollq, POLLIN | POLLRDNORM);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -113,12 +120,13 @@ static int iio_read_first_n_kfifo(struct iio_buffer *r,
|
|||
int ret, copied;
|
||||
struct iio_kfifo *kf = iio_to_kfifo(r);
|
||||
|
||||
if (n < r->bytes_per_datum || r->bytes_per_datum == 0)
|
||||
return -EINVAL;
|
||||
if (mutex_lock_interruptible(&kf->user_lock))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
ret = kfifo_to_user(&kf->kf, buf, n, &copied);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (!kfifo_initialized(&kf->kf) || n < kfifo_esize(&kf->kf))
|
||||
ret = -EINVAL;
|
||||
else
|
||||
ret = kfifo_to_user(&kf->kf, buf, n, &copied);
|
||||
|
||||
if (kfifo_is_empty(&kf->kf))
|
||||
r->stufftoread = false;
|
||||
|
@ -126,9 +134,22 @@ static int iio_read_first_n_kfifo(struct iio_buffer *r,
|
|||
if (!kfifo_is_empty(&kf->kf))
|
||||
r->stufftoread = true;
|
||||
|
||||
mutex_unlock(&kf->user_lock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return copied;
|
||||
}
|
||||
|
||||
static void iio_kfifo_buffer_release(struct iio_buffer *buffer)
|
||||
{
|
||||
struct iio_kfifo *kf = iio_to_kfifo(buffer);
|
||||
|
||||
mutex_destroy(&kf->user_lock);
|
||||
kfifo_free(&kf->kf);
|
||||
kfree(kf);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_access_funcs kfifo_access_funcs = {
|
||||
.store_to = &iio_store_to_kfifo,
|
||||
.read_first_n = &iio_read_first_n_kfifo,
|
||||
|
@ -137,6 +158,7 @@ static const struct iio_buffer_access_funcs kfifo_access_funcs = {
|
|||
.set_bytes_per_datum = &iio_set_bytes_per_datum_kfifo,
|
||||
.get_length = &iio_get_length_kfifo,
|
||||
.set_length = &iio_set_length_kfifo,
|
||||
.release = &iio_kfifo_buffer_release,
|
||||
};
|
||||
|
||||
struct iio_buffer *iio_kfifo_allocate(struct iio_dev *indio_dev)
|
||||
|
@ -151,13 +173,14 @@ struct iio_buffer *iio_kfifo_allocate(struct iio_dev *indio_dev)
|
|||
kf->buffer.attrs = &iio_kfifo_attribute_group;
|
||||
kf->buffer.access = &kfifo_access_funcs;
|
||||
kf->buffer.length = 2;
|
||||
mutex_init(&kf->user_lock);
|
||||
return &kf->buffer;
|
||||
}
|
||||
EXPORT_SYMBOL(iio_kfifo_allocate);
|
||||
|
||||
void iio_kfifo_free(struct iio_buffer *r)
|
||||
{
|
||||
kfree(iio_to_kfifo(r));
|
||||
iio_buffer_put(r);
|
||||
}
|
||||
EXPORT_SYMBOL(iio_kfifo_free);
|
||||
|
||||
|
|
|
@ -27,6 +27,29 @@ config APDS9300
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called apds9300.
|
||||
|
||||
config CM36651
|
||||
depends on I2C
|
||||
tristate "CM36651 driver"
|
||||
help
|
||||
Say Y here if you use cm36651.
|
||||
This option enables proximity & RGB sensor using
|
||||
Capella cm36651 device driver.
|
||||
|
||||
To compile this driver as a module, choose M here:
|
||||
the module will be called cm36651.
|
||||
|
||||
config GP2AP020A00F
|
||||
tristate "Sharp GP2AP020A00F Proximity/ALS sensor"
|
||||
depends on I2C
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say Y here if you have a Sharp GP2AP020A00F proximity/ALS combo-chip
|
||||
hooked to an I2C bus.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called gp2ap020a00f.
|
||||
|
||||
config HID_SENSOR_ALS
|
||||
depends on HID_SENSOR_HUB
|
||||
select IIO_BUFFER
|
||||
|
@ -55,6 +78,16 @@ config SENSORS_LM3533
|
|||
changes. The ALS-control output values can be set per zone for the
|
||||
three current output channels.
|
||||
|
||||
config TCS3472
|
||||
tristate "TAOS TCS3472 color light-to-digital converter"
|
||||
depends on I2C
|
||||
help
|
||||
If you say yes here you get support for the TAOS TCS3472
|
||||
family of color light-to-digital converters with IR filter.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called tcs3472.
|
||||
|
||||
config SENSORS_TSL2563
|
||||
tristate "TAOS TSL2560, TSL2561, TSL2562 and TSL2563 ambient light sensors"
|
||||
depends on I2C
|
||||
|
@ -65,6 +98,16 @@ config SENSORS_TSL2563
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called tsl2563.
|
||||
|
||||
config TSL4531
|
||||
tristate "TAOS TSL4531 ambient light sensors"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here if you want to build a driver for the TAOS TSL4531 family
|
||||
of ambient light sensors with direct lux output.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called tsl4531.
|
||||
|
||||
config VCNL4000
|
||||
tristate "VCNL4000 combined ALS and proximity sensor"
|
||||
depends on I2C
|
||||
|
|
|
@ -5,7 +5,11 @@
|
|||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_ADJD_S311) += adjd_s311.o
|
||||
obj-$(CONFIG_APDS9300) += apds9300.o
|
||||
obj-$(CONFIG_CM36651) += cm36651.o
|
||||
obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o
|
||||
obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o
|
||||
obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o
|
||||
obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o
|
||||
obj-$(CONFIG_TCS3472) += tcs3472.o
|
||||
obj-$(CONFIG_TSL4531) += tsl4531.o
|
||||
obj-$(CONFIG_VCNL4000) += vcnl4000.o
|
||||
|
|
|
@ -114,43 +114,6 @@ static int adjd_s311_read_data(struct iio_dev *indio_dev, u8 reg, int *val)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t adjd_s311_read_int_time(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, char *buf)
|
||||
{
|
||||
struct adjd_s311_data *data = iio_priv(indio_dev);
|
||||
s32 ret;
|
||||
|
||||
ret = i2c_smbus_read_word_data(data->client,
|
||||
ADJD_S311_INT_REG(chan->address));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%d\n", ret & ADJD_S311_INT_MASK);
|
||||
}
|
||||
|
||||
static ssize_t adjd_s311_write_int_time(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct adjd_s311_data *data = iio_priv(indio_dev);
|
||||
unsigned long int_time;
|
||||
int ret;
|
||||
|
||||
ret = kstrtoul(buf, 10, &int_time);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (int_time > ADJD_S311_INT_MASK)
|
||||
return -EINVAL;
|
||||
|
||||
ret = i2c_smbus_write_word_data(data->client,
|
||||
ADJD_S311_INT_REG(chan->address), int_time);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static irqreturn_t adjd_s311_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
|
@ -175,10 +138,7 @@ static irqreturn_t adjd_s311_trigger_handler(int irq, void *p)
|
|||
len += 2;
|
||||
}
|
||||
|
||||
if (indio_dev->scan_timestamp)
|
||||
*(s64 *)((u8 *)data->buffer + ALIGN(len, sizeof(s64)))
|
||||
= time_ns;
|
||||
iio_push_to_buffers(indio_dev, (u8 *)data->buffer);
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, time_ns);
|
||||
|
||||
done:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
@ -186,25 +146,16 @@ done:
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec_ext_info adjd_s311_ext_info[] = {
|
||||
{
|
||||
.name = "integration_time",
|
||||
.read = adjd_s311_read_int_time,
|
||||
.write = adjd_s311_write_int_time,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
#define ADJD_S311_CHANNEL(_color, _scan_idx) { \
|
||||
.type = IIO_INTENSITY, \
|
||||
.modified = 1, \
|
||||
.address = (IDX_##_color), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_HARDWAREGAIN), \
|
||||
BIT(IIO_CHAN_INFO_HARDWAREGAIN) | \
|
||||
BIT(IIO_CHAN_INFO_INT_TIME), \
|
||||
.channel2 = (IIO_MOD_LIGHT_##_color), \
|
||||
.scan_index = (_scan_idx), \
|
||||
.scan_type = IIO_ST('u', 10, 16, 0), \
|
||||
.ext_info = adjd_s311_ext_info, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec adjd_s311_channels[] = {
|
||||
|
@ -236,6 +187,18 @@ static int adjd_s311_read_raw(struct iio_dev *indio_dev,
|
|||
return ret;
|
||||
*val = ret & ADJD_S311_CAP_MASK;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
ret = i2c_smbus_read_word_data(data->client,
|
||||
ADJD_S311_INT_REG(chan->address));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = 0;
|
||||
/*
|
||||
* not documented, based on measurement:
|
||||
* 4095 LSBs correspond to roughly 4 ms
|
||||
*/
|
||||
*val2 = ret & ADJD_S311_INT_MASK;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -245,16 +208,20 @@ static int adjd_s311_write_raw(struct iio_dev *indio_dev,
|
|||
int val, int val2, long mask)
|
||||
{
|
||||
struct adjd_s311_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_HARDWAREGAIN:
|
||||
if (val < 0 || val > ADJD_S311_CAP_MASK)
|
||||
return -EINVAL;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
return i2c_smbus_write_byte_data(data->client,
|
||||
ADJD_S311_CAP_REG(chan->address), val);
|
||||
return ret;
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
if (val != 0 || val2 < 0 || val2 > ADJD_S311_INT_MASK)
|
||||
return -EINVAL;
|
||||
|
||||
return i2c_smbus_write_word_data(data->client,
|
||||
ADJD_S311_INT_REG(chan->address), val2);
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
|
@ -273,12 +273,14 @@ static int apds9300_read_raw(struct iio_dev *indio_dev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int apds9300_read_thresh(struct iio_dev *indio_dev, u64 event_code,
|
||||
int *val)
|
||||
static int apds9300_read_thresh(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir, enum iio_event_info info,
|
||||
int *val, int *val2)
|
||||
{
|
||||
struct apds9300_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
|
||||
switch (dir) {
|
||||
case IIO_EV_DIR_RISING:
|
||||
*val = data->thresh_hi;
|
||||
break;
|
||||
|
@ -289,17 +291,19 @@ static int apds9300_read_thresh(struct iio_dev *indio_dev, u64 event_code,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int apds9300_write_thresh(struct iio_dev *indio_dev, u64 event_code,
|
||||
int val)
|
||||
static int apds9300_write_thresh(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir, enum iio_event_info info, int val,
|
||||
int val2)
|
||||
{
|
||||
struct apds9300_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_RISING)
|
||||
if (dir == IIO_EV_DIR_RISING)
|
||||
ret = apds9300_set_thresh_hi(data, val);
|
||||
else
|
||||
ret = apds9300_set_thresh_low(data, val);
|
||||
|
@ -309,7 +313,9 @@ static int apds9300_write_thresh(struct iio_dev *indio_dev, u64 event_code,
|
|||
}
|
||||
|
||||
static int apds9300_read_interrupt_config(struct iio_dev *indio_dev,
|
||||
u64 event_code)
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir)
|
||||
{
|
||||
struct apds9300_data *data = iio_priv(indio_dev);
|
||||
|
||||
|
@ -317,7 +323,8 @@ static int apds9300_read_interrupt_config(struct iio_dev *indio_dev,
|
|||
}
|
||||
|
||||
static int apds9300_write_interrupt_config(struct iio_dev *indio_dev,
|
||||
u64 event_code, int state)
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir, int state)
|
||||
{
|
||||
struct apds9300_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
@ -337,10 +344,24 @@ static const struct iio_info apds9300_info_no_irq = {
|
|||
static const struct iio_info apds9300_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = apds9300_read_raw,
|
||||
.read_event_value = apds9300_read_thresh,
|
||||
.write_event_value = apds9300_write_thresh,
|
||||
.read_event_config = apds9300_read_interrupt_config,
|
||||
.write_event_config = apds9300_write_interrupt_config,
|
||||
.read_event_value_new = apds9300_read_thresh,
|
||||
.write_event_value_new = apds9300_write_thresh,
|
||||
.read_event_config_new = apds9300_read_interrupt_config,
|
||||
.write_event_config_new = apds9300_write_interrupt_config,
|
||||
};
|
||||
|
||||
static const struct iio_event_spec apds9300_event_spec[] = {
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_RISING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
||||
BIT(IIO_EV_INFO_ENABLE),
|
||||
}, {
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_FALLING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
||||
BIT(IIO_EV_INFO_ENABLE),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec apds9300_channels[] = {
|
||||
|
@ -355,10 +376,8 @@ static const struct iio_chan_spec apds9300_channels[] = {
|
|||
.channel2 = IIO_MOD_LIGHT_BOTH,
|
||||
.indexed = true,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.event_mask = (IIO_EV_BIT(IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_RISING) |
|
||||
IIO_EV_BIT(IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_FALLING)),
|
||||
.event_spec = apds9300_event_spec,
|
||||
.num_event_specs = ARRAY_SIZE(apds9300_event_spec),
|
||||
}, {
|
||||
.type = IIO_INTENSITY,
|
||||
.channel = 1,
|
||||
|
|
|
@ -0,0 +1,708 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
|
||||
* Author: Beomho Seo <beomho.seo@samsung.com>
|
||||
*
|
||||
* 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 <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/events.h>
|
||||
|
||||
/* Slave address 0x19 for PS of 7 bit addressing protocol for I2C */
|
||||
#define CM36651_I2C_ADDR_PS 0x19
|
||||
/* Alert Response Address */
|
||||
#define CM36651_ARA 0x0C
|
||||
|
||||
/* Ambient light sensor */
|
||||
#define CM36651_CS_CONF1 0x00
|
||||
#define CM36651_CS_CONF2 0x01
|
||||
#define CM36651_ALS_WH_M 0x02
|
||||
#define CM36651_ALS_WH_L 0x03
|
||||
#define CM36651_ALS_WL_M 0x04
|
||||
#define CM36651_ALS_WL_L 0x05
|
||||
#define CM36651_CS_CONF3 0x06
|
||||
#define CM36651_CS_CONF_REG_NUM 0x02
|
||||
|
||||
/* Proximity sensor */
|
||||
#define CM36651_PS_CONF1 0x00
|
||||
#define CM36651_PS_THD 0x01
|
||||
#define CM36651_PS_CANC 0x02
|
||||
#define CM36651_PS_CONF2 0x03
|
||||
#define CM36651_PS_REG_NUM 0x04
|
||||
|
||||
/* CS_CONF1 command code */
|
||||
#define CM36651_ALS_ENABLE 0x00
|
||||
#define CM36651_ALS_DISABLE 0x01
|
||||
#define CM36651_ALS_INT_EN 0x02
|
||||
#define CM36651_ALS_THRES 0x04
|
||||
|
||||
/* CS_CONF2 command code */
|
||||
#define CM36651_CS_CONF2_DEFAULT_BIT 0x08
|
||||
|
||||
/* CS_CONF3 channel integration time */
|
||||
#define CM36651_CS_IT1 0x00 /* Integration time 80000 usec */
|
||||
#define CM36651_CS_IT2 0x40 /* Integration time 160000 usec */
|
||||
#define CM36651_CS_IT3 0x80 /* Integration time 320000 usec */
|
||||
#define CM36651_CS_IT4 0xC0 /* Integration time 640000 usec */
|
||||
|
||||
/* PS_CONF1 command code */
|
||||
#define CM36651_PS_ENABLE 0x00
|
||||
#define CM36651_PS_DISABLE 0x01
|
||||
#define CM36651_PS_INT_EN 0x02
|
||||
#define CM36651_PS_PERS2 0x04
|
||||
#define CM36651_PS_PERS3 0x08
|
||||
#define CM36651_PS_PERS4 0x0C
|
||||
|
||||
/* PS_CONF1 command code: integration time */
|
||||
#define CM36651_PS_IT1 0x00 /* Integration time 320 usec */
|
||||
#define CM36651_PS_IT2 0x10 /* Integration time 420 usec */
|
||||
#define CM36651_PS_IT3 0x20 /* Integration time 520 usec */
|
||||
#define CM36651_PS_IT4 0x30 /* Integration time 640 usec */
|
||||
|
||||
/* PS_CONF1 command code: duty ratio */
|
||||
#define CM36651_PS_DR1 0x00 /* Duty ratio 1/80 */
|
||||
#define CM36651_PS_DR2 0x40 /* Duty ratio 1/160 */
|
||||
#define CM36651_PS_DR3 0x80 /* Duty ratio 1/320 */
|
||||
#define CM36651_PS_DR4 0xC0 /* Duty ratio 1/640 */
|
||||
|
||||
/* PS_THD command code */
|
||||
#define CM36651_PS_INITIAL_THD 0x05
|
||||
|
||||
/* PS_CANC command code */
|
||||
#define CM36651_PS_CANC_DEFAULT 0x00
|
||||
|
||||
/* PS_CONF2 command code */
|
||||
#define CM36651_PS_HYS1 0x00
|
||||
#define CM36651_PS_HYS2 0x01
|
||||
#define CM36651_PS_SMART_PERS_EN 0x02
|
||||
#define CM36651_PS_DIR_INT 0x04
|
||||
#define CM36651_PS_MS 0x10
|
||||
|
||||
#define CM36651_CS_COLOR_NUM 4
|
||||
|
||||
#define CM36651_CLOSE_PROXIMITY 0x32
|
||||
#define CM36651_FAR_PROXIMITY 0x33
|
||||
|
||||
#define CM36651_CS_INT_TIME_AVAIL "80000 160000 320000 640000"
|
||||
#define CM36651_PS_INT_TIME_AVAIL "320 420 520 640"
|
||||
|
||||
enum cm36651_operation_mode {
|
||||
CM36651_LIGHT_EN,
|
||||
CM36651_PROXIMITY_EN,
|
||||
CM36651_PROXIMITY_EV_EN,
|
||||
};
|
||||
|
||||
enum cm36651_light_channel_idx {
|
||||
CM36651_LIGHT_CHANNEL_IDX_RED,
|
||||
CM36651_LIGHT_CHANNEL_IDX_GREEN,
|
||||
CM36651_LIGHT_CHANNEL_IDX_BLUE,
|
||||
CM36651_LIGHT_CHANNEL_IDX_CLEAR,
|
||||
};
|
||||
|
||||
enum cm36651_command {
|
||||
CM36651_CMD_READ_RAW_LIGHT,
|
||||
CM36651_CMD_READ_RAW_PROXIMITY,
|
||||
CM36651_CMD_PROX_EV_EN,
|
||||
CM36651_CMD_PROX_EV_DIS,
|
||||
};
|
||||
|
||||
static const u8 cm36651_cs_reg[CM36651_CS_CONF_REG_NUM] = {
|
||||
CM36651_CS_CONF1,
|
||||
CM36651_CS_CONF2,
|
||||
};
|
||||
|
||||
static const u8 cm36651_ps_reg[CM36651_PS_REG_NUM] = {
|
||||
CM36651_PS_CONF1,
|
||||
CM36651_PS_THD,
|
||||
CM36651_PS_CANC,
|
||||
CM36651_PS_CONF2,
|
||||
};
|
||||
|
||||
struct cm36651_data {
|
||||
const struct cm36651_platform_data *pdata;
|
||||
struct i2c_client *client;
|
||||
struct i2c_client *ps_client;
|
||||
struct i2c_client *ara_client;
|
||||
struct mutex lock;
|
||||
struct regulator *vled_reg;
|
||||
unsigned long flags;
|
||||
int cs_int_time[CM36651_CS_COLOR_NUM];
|
||||
int ps_int_time;
|
||||
u8 cs_ctrl_regs[CM36651_CS_CONF_REG_NUM];
|
||||
u8 ps_ctrl_regs[CM36651_PS_REG_NUM];
|
||||
u16 color[CM36651_CS_COLOR_NUM];
|
||||
};
|
||||
|
||||
static int cm36651_setup_reg(struct cm36651_data *cm36651)
|
||||
{
|
||||
struct i2c_client *client = cm36651->client;
|
||||
struct i2c_client *ps_client = cm36651->ps_client;
|
||||
int i, ret;
|
||||
|
||||
/* CS initialization */
|
||||
cm36651->cs_ctrl_regs[CM36651_CS_CONF1] = CM36651_ALS_ENABLE |
|
||||
CM36651_ALS_THRES;
|
||||
cm36651->cs_ctrl_regs[CM36651_CS_CONF2] = CM36651_CS_CONF2_DEFAULT_BIT;
|
||||
|
||||
for (i = 0; i < CM36651_CS_CONF_REG_NUM; i++) {
|
||||
ret = i2c_smbus_write_byte_data(client, cm36651_cs_reg[i],
|
||||
cm36651->cs_ctrl_regs[i]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* PS initialization */
|
||||
cm36651->ps_ctrl_regs[CM36651_PS_CONF1] = CM36651_PS_ENABLE |
|
||||
CM36651_PS_IT2;
|
||||
cm36651->ps_ctrl_regs[CM36651_PS_THD] = CM36651_PS_INITIAL_THD;
|
||||
cm36651->ps_ctrl_regs[CM36651_PS_CANC] = CM36651_PS_CANC_DEFAULT;
|
||||
cm36651->ps_ctrl_regs[CM36651_PS_CONF2] = CM36651_PS_HYS2 |
|
||||
CM36651_PS_DIR_INT | CM36651_PS_SMART_PERS_EN;
|
||||
|
||||
for (i = 0; i < CM36651_PS_REG_NUM; i++) {
|
||||
ret = i2c_smbus_write_byte_data(ps_client, cm36651_ps_reg[i],
|
||||
cm36651->ps_ctrl_regs[i]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Set shutdown mode */
|
||||
ret = i2c_smbus_write_byte_data(client, CM36651_CS_CONF1,
|
||||
CM36651_ALS_DISABLE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(cm36651->ps_client,
|
||||
CM36651_PS_CONF1, CM36651_PS_DISABLE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cm36651_read_output(struct cm36651_data *cm36651,
|
||||
struct iio_chan_spec const *chan, int *val)
|
||||
{
|
||||
struct i2c_client *client = cm36651->client;
|
||||
int ret = -EINVAL;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_LIGHT:
|
||||
*val = i2c_smbus_read_word_data(client, chan->address);
|
||||
if (*val < 0)
|
||||
return ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, CM36651_CS_CONF1,
|
||||
CM36651_ALS_DISABLE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_PROXIMITY:
|
||||
*val = i2c_smbus_read_byte(cm36651->ps_client);
|
||||
if (*val < 0)
|
||||
return ret;
|
||||
|
||||
if (!test_bit(CM36651_PROXIMITY_EV_EN, &cm36651->flags)) {
|
||||
ret = i2c_smbus_write_byte_data(cm36651->ps_client,
|
||||
CM36651_PS_CONF1, CM36651_PS_DISABLE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t cm36651_irq_handler(int irq, void *data)
|
||||
{
|
||||
struct iio_dev *indio_dev = data;
|
||||
struct cm36651_data *cm36651 = iio_priv(indio_dev);
|
||||
struct i2c_client *client = cm36651->client;
|
||||
int ev_dir, ret;
|
||||
u64 ev_code;
|
||||
|
||||
/*
|
||||
* The PS INT pin is an active low signal that PS INT move logic low
|
||||
* when the object is detect. Once the MCU host received the PS INT
|
||||
* "LOW" signal, the Host needs to read the data at Alert Response
|
||||
* Address(ARA) to clear the PS INT signal. After clearing the PS
|
||||
* INT pin, the PS INT signal toggles from low to high.
|
||||
*/
|
||||
ret = i2c_smbus_read_byte(cm36651->ara_client);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev,
|
||||
"%s: Data read failed: %d\n", __func__, ret);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
switch (ret) {
|
||||
case CM36651_CLOSE_PROXIMITY:
|
||||
ev_dir = IIO_EV_DIR_RISING;
|
||||
break;
|
||||
case CM36651_FAR_PROXIMITY:
|
||||
ev_dir = IIO_EV_DIR_FALLING;
|
||||
break;
|
||||
default:
|
||||
dev_err(&client->dev,
|
||||
"%s: Data read wrong: %d\n", __func__, ret);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
ev_code = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY,
|
||||
CM36651_CMD_READ_RAW_PROXIMITY,
|
||||
IIO_EV_TYPE_THRESH, ev_dir);
|
||||
|
||||
iio_push_event(indio_dev, ev_code, iio_get_time_ns());
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int cm36651_set_operation_mode(struct cm36651_data *cm36651, int cmd)
|
||||
{
|
||||
struct i2c_client *client = cm36651->client;
|
||||
struct i2c_client *ps_client = cm36651->ps_client;
|
||||
int ret = -EINVAL;
|
||||
|
||||
switch (cmd) {
|
||||
case CM36651_CMD_READ_RAW_LIGHT:
|
||||
ret = i2c_smbus_write_byte_data(client, CM36651_CS_CONF1,
|
||||
cm36651->cs_ctrl_regs[CM36651_CS_CONF1]);
|
||||
break;
|
||||
case CM36651_CMD_READ_RAW_PROXIMITY:
|
||||
if (test_bit(CM36651_PROXIMITY_EV_EN, &cm36651->flags))
|
||||
return CM36651_PROXIMITY_EV_EN;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(ps_client, CM36651_PS_CONF1,
|
||||
cm36651->ps_ctrl_regs[CM36651_PS_CONF1]);
|
||||
break;
|
||||
case CM36651_CMD_PROX_EV_EN:
|
||||
if (test_bit(CM36651_PROXIMITY_EV_EN, &cm36651->flags)) {
|
||||
dev_err(&client->dev,
|
||||
"Already proximity event enable state\n");
|
||||
return ret;
|
||||
}
|
||||
set_bit(CM36651_PROXIMITY_EV_EN, &cm36651->flags);
|
||||
|
||||
ret = i2c_smbus_write_byte_data(ps_client,
|
||||
cm36651_ps_reg[CM36651_PS_CONF1],
|
||||
CM36651_PS_INT_EN | CM36651_PS_PERS2 | CM36651_PS_IT2);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Proximity enable event failed\n");
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
case CM36651_CMD_PROX_EV_DIS:
|
||||
if (!test_bit(CM36651_PROXIMITY_EV_EN, &cm36651->flags)) {
|
||||
dev_err(&client->dev,
|
||||
"Already proximity event disable state\n");
|
||||
return ret;
|
||||
}
|
||||
clear_bit(CM36651_PROXIMITY_EV_EN, &cm36651->flags);
|
||||
ret = i2c_smbus_write_byte_data(ps_client,
|
||||
CM36651_PS_CONF1, CM36651_PS_DISABLE);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "Write register failed\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cm36651_read_channel(struct cm36651_data *cm36651,
|
||||
struct iio_chan_spec const *chan, int *val)
|
||||
{
|
||||
struct i2c_client *client = cm36651->client;
|
||||
int cmd, ret;
|
||||
|
||||
if (chan->type == IIO_LIGHT)
|
||||
cmd = CM36651_CMD_READ_RAW_LIGHT;
|
||||
else if (chan->type == IIO_PROXIMITY)
|
||||
cmd = CM36651_CMD_READ_RAW_PROXIMITY;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
ret = cm36651_set_operation_mode(cm36651, cmd);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "CM36651 set operation mode failed\n");
|
||||
return ret;
|
||||
}
|
||||
/* Delay for work after enable operation */
|
||||
msleep(50);
|
||||
ret = cm36651_read_output(cm36651, chan, val);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "CM36651 read output failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cm36651_read_int_time(struct cm36651_data *cm36651,
|
||||
struct iio_chan_spec const *chan, int *val)
|
||||
{
|
||||
switch (chan->type) {
|
||||
case IIO_LIGHT:
|
||||
if (cm36651->cs_int_time[chan->address] == CM36651_CS_IT1)
|
||||
*val = 80000;
|
||||
else if (cm36651->cs_int_time[chan->address] == CM36651_CS_IT2)
|
||||
*val = 160000;
|
||||
else if (cm36651->cs_int_time[chan->address] == CM36651_CS_IT3)
|
||||
*val = 320000;
|
||||
else if (cm36651->cs_int_time[chan->address] == CM36651_CS_IT4)
|
||||
*val = 640000;
|
||||
else
|
||||
return -EINVAL;
|
||||
break;
|
||||
case IIO_PROXIMITY:
|
||||
if (cm36651->ps_int_time == CM36651_PS_IT1)
|
||||
*val = 320;
|
||||
else if (cm36651->ps_int_time == CM36651_PS_IT2)
|
||||
*val = 420;
|
||||
else if (cm36651->ps_int_time == CM36651_PS_IT3)
|
||||
*val = 520;
|
||||
else if (cm36651->ps_int_time == CM36651_PS_IT4)
|
||||
*val = 640;
|
||||
else
|
||||
return -EINVAL;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
|
||||
static int cm36651_write_int_time(struct cm36651_data *cm36651,
|
||||
struct iio_chan_spec const *chan, int val)
|
||||
{
|
||||
struct i2c_client *client = cm36651->client;
|
||||
struct i2c_client *ps_client = cm36651->ps_client;
|
||||
int int_time, ret;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_LIGHT:
|
||||
if (val == 80000)
|
||||
int_time = CM36651_CS_IT1;
|
||||
else if (val == 160000)
|
||||
int_time = CM36651_CS_IT2;
|
||||
else if (val == 320000)
|
||||
int_time = CM36651_CS_IT3;
|
||||
else if (val == 640000)
|
||||
int_time = CM36651_CS_IT4;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, CM36651_CS_CONF3,
|
||||
int_time >> 2 * (chan->address));
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "CS integration time write failed\n");
|
||||
return ret;
|
||||
}
|
||||
cm36651->cs_int_time[chan->address] = int_time;
|
||||
break;
|
||||
case IIO_PROXIMITY:
|
||||
if (val == 320)
|
||||
int_time = CM36651_PS_IT1;
|
||||
else if (val == 420)
|
||||
int_time = CM36651_PS_IT2;
|
||||
else if (val == 520)
|
||||
int_time = CM36651_PS_IT3;
|
||||
else if (val == 640)
|
||||
int_time = CM36651_PS_IT4;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(ps_client,
|
||||
CM36651_PS_CONF1, int_time);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "PS integration time write failed\n");
|
||||
return ret;
|
||||
}
|
||||
cm36651->ps_int_time = int_time;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cm36651_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct cm36651_data *cm36651 = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&cm36651->lock);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = cm36651_read_channel(cm36651, chan, val);
|
||||
break;
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
ret = cm36651_read_int_time(cm36651, chan, val);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
mutex_unlock(&cm36651->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cm36651_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct cm36651_data *cm36651 = iio_priv(indio_dev);
|
||||
struct i2c_client *client = cm36651->client;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (mask == IIO_CHAN_INFO_INT_TIME) {
|
||||
ret = cm36651_write_int_time(cm36651, chan, val);
|
||||
if (ret < 0)
|
||||
dev_err(&client->dev, "Integration time write failed\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cm36651_read_prox_thresh(struct iio_dev *indio_dev,
|
||||
u64 event_code, int *val)
|
||||
{
|
||||
struct cm36651_data *cm36651 = iio_priv(indio_dev);
|
||||
|
||||
*val = cm36651->ps_ctrl_regs[CM36651_PS_THD];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cm36651_write_prox_thresh(struct iio_dev *indio_dev,
|
||||
u64 event_code, int val)
|
||||
{
|
||||
struct cm36651_data *cm36651 = iio_priv(indio_dev);
|
||||
struct i2c_client *client = cm36651->client;
|
||||
int ret;
|
||||
|
||||
if (val < 3 || val > 255)
|
||||
return -EINVAL;
|
||||
|
||||
cm36651->ps_ctrl_regs[CM36651_PS_THD] = val;
|
||||
ret = i2c_smbus_write_byte_data(cm36651->ps_client, CM36651_PS_THD,
|
||||
cm36651->ps_ctrl_regs[CM36651_PS_THD]);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "PS threshold write failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cm36651_write_prox_event_config(struct iio_dev *indio_dev,
|
||||
u64 event_code, int state)
|
||||
{
|
||||
struct cm36651_data *cm36651 = iio_priv(indio_dev);
|
||||
int cmd, ret = -EINVAL;
|
||||
|
||||
mutex_lock(&cm36651->lock);
|
||||
|
||||
cmd = state ? CM36651_CMD_PROX_EV_EN : CM36651_CMD_PROX_EV_DIS;
|
||||
ret = cm36651_set_operation_mode(cm36651, cmd);
|
||||
|
||||
mutex_unlock(&cm36651->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cm36651_read_prox_event_config(struct iio_dev *indio_dev,
|
||||
u64 event_code)
|
||||
{
|
||||
struct cm36651_data *cm36651 = iio_priv(indio_dev);
|
||||
int event_en;
|
||||
|
||||
mutex_lock(&cm36651->lock);
|
||||
|
||||
event_en = test_bit(CM36651_PROXIMITY_EV_EN, &cm36651->flags);
|
||||
|
||||
mutex_unlock(&cm36651->lock);
|
||||
|
||||
return event_en;
|
||||
}
|
||||
|
||||
#define CM36651_LIGHT_CHANNEL(_color, _idx) { \
|
||||
.type = IIO_LIGHT, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_INT_TIME), \
|
||||
.address = _idx, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_LIGHT_##_color, \
|
||||
} \
|
||||
|
||||
static const struct iio_chan_spec cm36651_channels[] = {
|
||||
{
|
||||
.type = IIO_PROXIMITY,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_INT_TIME),
|
||||
.event_mask = IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER)
|
||||
},
|
||||
CM36651_LIGHT_CHANNEL(RED, CM36651_LIGHT_CHANNEL_IDX_RED),
|
||||
CM36651_LIGHT_CHANNEL(GREEN, CM36651_LIGHT_CHANNEL_IDX_GREEN),
|
||||
CM36651_LIGHT_CHANNEL(BLUE, CM36651_LIGHT_CHANNEL_IDX_BLUE),
|
||||
CM36651_LIGHT_CHANNEL(CLEAR, CM36651_LIGHT_CHANNEL_IDX_CLEAR),
|
||||
};
|
||||
|
||||
static IIO_CONST_ATTR(in_illuminance_integration_time_available,
|
||||
CM36651_CS_INT_TIME_AVAIL);
|
||||
static IIO_CONST_ATTR(in_proximity_integration_time_available,
|
||||
CM36651_PS_INT_TIME_AVAIL);
|
||||
|
||||
static struct attribute *cm36651_attributes[] = {
|
||||
&iio_const_attr_in_illuminance_integration_time_available.dev_attr.attr,
|
||||
&iio_const_attr_in_proximity_integration_time_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group cm36651_attribute_group = {
|
||||
.attrs = cm36651_attributes
|
||||
};
|
||||
|
||||
static const struct iio_info cm36651_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &cm36651_read_raw,
|
||||
.write_raw = &cm36651_write_raw,
|
||||
.read_event_value = &cm36651_read_prox_thresh,
|
||||
.write_event_value = &cm36651_write_prox_thresh,
|
||||
.read_event_config = &cm36651_read_prox_event_config,
|
||||
.write_event_config = &cm36651_write_prox_event_config,
|
||||
.attrs = &cm36651_attribute_group,
|
||||
};
|
||||
|
||||
static int cm36651_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct cm36651_data *cm36651;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*cm36651));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
cm36651 = iio_priv(indio_dev);
|
||||
|
||||
cm36651->vled_reg = devm_regulator_get(&client->dev, "vled");
|
||||
if (IS_ERR(cm36651->vled_reg)) {
|
||||
dev_err(&client->dev, "get regulator vled failed\n");
|
||||
return PTR_ERR(cm36651->vled_reg);
|
||||
}
|
||||
|
||||
ret = regulator_enable(cm36651->vled_reg);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "enable regulator vled failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
cm36651->client = client;
|
||||
cm36651->ps_client = i2c_new_dummy(client->adapter,
|
||||
CM36651_I2C_ADDR_PS);
|
||||
cm36651->ara_client = i2c_new_dummy(client->adapter, CM36651_ARA);
|
||||
mutex_init(&cm36651->lock);
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->channels = cm36651_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(cm36651_channels);
|
||||
indio_dev->info = &cm36651_info;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = cm36651_setup_reg(cm36651);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "%s: register setup failed\n", __func__);
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
ret = request_threaded_irq(client->irq, NULL, cm36651_irq_handler,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
"cm36651", indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "%s: request irq failed\n", __func__);
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "%s: regist device failed\n", __func__);
|
||||
goto error_free_irq;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_free_irq:
|
||||
free_irq(client->irq, indio_dev);
|
||||
error_disable_reg:
|
||||
regulator_disable(cm36651->vled_reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cm36651_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct cm36651_data *cm36651 = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
regulator_disable(cm36651->vled_reg);
|
||||
free_irq(client->irq, indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id cm36651_id[] = {
|
||||
{ "cm36651", 0 },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, cm36651_id);
|
||||
|
||||
static const struct of_device_id cm36651_of_match[] = {
|
||||
{ .compatible = "capella,cm36651" },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct i2c_driver cm36651_driver = {
|
||||
.driver = {
|
||||
.name = "cm36651",
|
||||
.of_match_table = of_match_ptr(cm36651_of_match),
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = cm36651_probe,
|
||||
.remove = cm36651_remove,
|
||||
.id_table = cm36651_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(cm36651_driver);
|
||||
|
||||
MODULE_AUTHOR("Beomho Seo <beomho.seo@samsung.com>");
|
||||
MODULE_DESCRIPTION("CM36651 proximity/ambient light sensor driver");
|
||||
MODULE_LICENSE("GPL v2");
|
File diff suppressed because it is too large
Load Diff
|
@ -161,10 +161,11 @@ static const struct iio_info als_info = {
|
|||
};
|
||||
|
||||
/* Function to push data to buffer */
|
||||
static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len)
|
||||
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, (u8 *)data);
|
||||
iio_push_to_buffers(indio_dev, data);
|
||||
}
|
||||
|
||||
/* Callback handler to send event after all samples are received and captured */
|
||||
|
@ -179,7 +180,7 @@ static int als_proc_event(struct hid_sensor_hub_device *hsdev,
|
|||
als_state->common_attributes.data_ready);
|
||||
if (als_state->common_attributes.data_ready)
|
||||
hid_sensor_push_data(indio_dev,
|
||||
(u8 *)&als_state->illum,
|
||||
&als_state->illum,
|
||||
sizeof(als_state->illum));
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -0,0 +1,367 @@
|
|||
/*
|
||||
* tcs3472.c - Support for TAOS TCS3472 color light-to-digital converter
|
||||
*
|
||||
* Copyright (c) 2013 Peter Meerwald <pmeerw@pmeerw.net>
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* Color light sensor with 16-bit channels for red, green, blue, clear);
|
||||
* 7-bit I2C slave address 0x39 (TCS34721, TCS34723) or 0x29 (TCS34725,
|
||||
* TCS34727)
|
||||
*
|
||||
* TODO: interrupt support, thresholds, wait time
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pm.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#define TCS3472_DRV_NAME "tcs3472"
|
||||
|
||||
#define TCS3472_COMMAND BIT(7)
|
||||
#define TCS3472_AUTO_INCR BIT(5)
|
||||
|
||||
#define TCS3472_ENABLE (TCS3472_COMMAND | 0x00)
|
||||
#define TCS3472_ATIME (TCS3472_COMMAND | 0x01)
|
||||
#define TCS3472_WTIME (TCS3472_COMMAND | 0x03)
|
||||
#define TCS3472_AILT (TCS3472_COMMAND | 0x04)
|
||||
#define TCS3472_AIHT (TCS3472_COMMAND | 0x06)
|
||||
#define TCS3472_PERS (TCS3472_COMMAND | 0x0c)
|
||||
#define TCS3472_CONFIG (TCS3472_COMMAND | 0x0d)
|
||||
#define TCS3472_CONTROL (TCS3472_COMMAND | 0x0f)
|
||||
#define TCS3472_ID (TCS3472_COMMAND | 0x12)
|
||||
#define TCS3472_STATUS (TCS3472_COMMAND | 0x13)
|
||||
#define TCS3472_CDATA (TCS3472_COMMAND | TCS3472_AUTO_INCR | 0x14)
|
||||
#define TCS3472_RDATA (TCS3472_COMMAND | TCS3472_AUTO_INCR | 0x16)
|
||||
#define TCS3472_GDATA (TCS3472_COMMAND | TCS3472_AUTO_INCR | 0x18)
|
||||
#define TCS3472_BDATA (TCS3472_COMMAND | TCS3472_AUTO_INCR | 0x1a)
|
||||
|
||||
#define TCS3472_STATUS_AVALID BIT(0)
|
||||
#define TCS3472_ENABLE_AEN BIT(1)
|
||||
#define TCS3472_ENABLE_PON BIT(0)
|
||||
#define TCS3472_CONTROL_AGAIN_MASK (BIT(0) | BIT(1))
|
||||
|
||||
struct tcs3472_data {
|
||||
struct i2c_client *client;
|
||||
u8 enable;
|
||||
u8 control;
|
||||
u8 atime;
|
||||
u16 buffer[8]; /* 4 16-bit channels + 64-bit timestamp */
|
||||
};
|
||||
|
||||
#define TCS3472_CHANNEL(_color, _si, _addr) { \
|
||||
.type = IIO_INTENSITY, \
|
||||
.modified = 1, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBSCALE) | \
|
||||
BIT(IIO_CHAN_INFO_INT_TIME), \
|
||||
.channel2 = IIO_MOD_LIGHT_##_color, \
|
||||
.address = _addr, \
|
||||
.scan_index = _si, \
|
||||
.scan_type = IIO_ST('u', 16, 16, 0), \
|
||||
}
|
||||
|
||||
static const int tcs3472_agains[] = { 1, 4, 16, 60 };
|
||||
|
||||
static const struct iio_chan_spec tcs3472_channels[] = {
|
||||
TCS3472_CHANNEL(CLEAR, 0, TCS3472_CDATA),
|
||||
TCS3472_CHANNEL(RED, 1, TCS3472_RDATA),
|
||||
TCS3472_CHANNEL(GREEN, 2, TCS3472_GDATA),
|
||||
TCS3472_CHANNEL(BLUE, 3, TCS3472_BDATA),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4),
|
||||
};
|
||||
|
||||
static int tcs3472_req_data(struct tcs3472_data *data)
|
||||
{
|
||||
int tries = 50;
|
||||
int ret;
|
||||
|
||||
while (tries--) {
|
||||
ret = i2c_smbus_read_byte_data(data->client, TCS3472_STATUS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret & TCS3472_STATUS_AVALID)
|
||||
break;
|
||||
msleep(20);
|
||||
}
|
||||
|
||||
if (tries < 0) {
|
||||
dev_err(&data->client->dev, "data not ready\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tcs3472_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct tcs3472_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = tcs3472_req_data(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = i2c_smbus_read_word_data(data->client, chan->address);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
*val = tcs3472_agains[data->control &
|
||||
TCS3472_CONTROL_AGAIN_MASK];
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
*val = 0;
|
||||
*val2 = (256 - data->atime) * 2400;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int tcs3472_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct tcs3472_data *data = iio_priv(indio_dev);
|
||||
int i;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
if (val2 != 0)
|
||||
return -EINVAL;
|
||||
for (i = 0; i < ARRAY_SIZE(tcs3472_agains); i++) {
|
||||
if (val == tcs3472_agains[i]) {
|
||||
data->control &= ~TCS3472_CONTROL_AGAIN_MASK;
|
||||
data->control |= i;
|
||||
return i2c_smbus_write_byte_data(
|
||||
data->client, TCS3472_CONTROL,
|
||||
data->control);
|
||||
}
|
||||
}
|
||||
return -EINVAL;
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
if (val != 0)
|
||||
return -EINVAL;
|
||||
for (i = 0; i < 256; i++) {
|
||||
if (val2 == (256 - i) * 2400) {
|
||||
data->atime = i;
|
||||
return i2c_smbus_write_word_data(
|
||||
data->client, TCS3472_ATIME,
|
||||
data->atime);
|
||||
}
|
||||
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static irqreturn_t tcs3472_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct tcs3472_data *data = iio_priv(indio_dev);
|
||||
int len = 0;
|
||||
int i, j = 0;
|
||||
|
||||
int ret = tcs3472_req_data(data);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
|
||||
for_each_set_bit(i, indio_dev->active_scan_mask,
|
||||
indio_dev->masklength) {
|
||||
ret = i2c_smbus_read_word_data(data->client,
|
||||
TCS3472_CDATA + 2*i);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
|
||||
data->buffer[j++] = ret;
|
||||
len += 2;
|
||||
}
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
|
||||
iio_get_time_ns());
|
||||
|
||||
done:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static ssize_t tcs3472_show_int_time_available(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
size_t len = 0;
|
||||
int i;
|
||||
|
||||
for (i = 1; i <= 256; i++)
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06d ",
|
||||
2400 * i);
|
||||
|
||||
/* replace trailing space by newline */
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static IIO_CONST_ATTR(calibscale_available, "1 4 16 60");
|
||||
static IIO_DEV_ATTR_INT_TIME_AVAIL(tcs3472_show_int_time_available);
|
||||
|
||||
static struct attribute *tcs3472_attributes[] = {
|
||||
&iio_const_attr_calibscale_available.dev_attr.attr,
|
||||
&iio_dev_attr_integration_time_available.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group tcs3472_attribute_group = {
|
||||
.attrs = tcs3472_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info tcs3472_info = {
|
||||
.read_raw = tcs3472_read_raw,
|
||||
.write_raw = tcs3472_write_raw,
|
||||
.attrs = &tcs3472_attribute_group,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int tcs3472_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct tcs3472_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &tcs3472_info;
|
||||
indio_dev->name = TCS3472_DRV_NAME;
|
||||
indio_dev->channels = tcs3472_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(tcs3472_channels);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(data->client, TCS3472_ID);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret == 0x44)
|
||||
dev_info(&client->dev, "TCS34721/34725 found\n");
|
||||
else if (ret == 0x4d)
|
||||
dev_info(&client->dev, "TCS34723/34727 found\n");
|
||||
else
|
||||
return -ENODEV;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(data->client, TCS3472_CONTROL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->control = ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(data->client, TCS3472_ATIME);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->atime = ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(data->client, TCS3472_ENABLE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* enable device */
|
||||
data->enable = ret | TCS3472_ENABLE_PON | TCS3472_ENABLE_AEN;
|
||||
ret = i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE,
|
||||
data->enable);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
tcs3472_trigger_handler, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
goto buffer_cleanup;
|
||||
|
||||
return 0;
|
||||
|
||||
buffer_cleanup:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tcs3472_powerdown(struct tcs3472_data *data)
|
||||
{
|
||||
return i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE,
|
||||
data->enable & ~(TCS3472_ENABLE_AEN | TCS3472_ENABLE_PON));
|
||||
}
|
||||
|
||||
static int tcs3472_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
tcs3472_powerdown(iio_priv(indio_dev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int tcs3472_suspend(struct device *dev)
|
||||
{
|
||||
struct tcs3472_data *data = iio_priv(i2c_get_clientdata(
|
||||
to_i2c_client(dev)));
|
||||
return tcs3472_powerdown(data);
|
||||
}
|
||||
|
||||
static int tcs3472_resume(struct device *dev)
|
||||
{
|
||||
struct tcs3472_data *data = iio_priv(i2c_get_clientdata(
|
||||
to_i2c_client(dev)));
|
||||
return i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE,
|
||||
data->enable | (TCS3472_ENABLE_AEN | TCS3472_ENABLE_PON));
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(tcs3472_pm_ops, tcs3472_suspend, tcs3472_resume);
|
||||
|
||||
static const struct i2c_device_id tcs3472_id[] = {
|
||||
{ "tcs3472", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, tcs3472_id);
|
||||
|
||||
static struct i2c_driver tcs3472_driver = {
|
||||
.driver = {
|
||||
.name = TCS3472_DRV_NAME,
|
||||
.pm = &tcs3472_pm_ops,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = tcs3472_probe,
|
||||
.remove = tcs3472_remove,
|
||||
.id_table = tcs3472_id,
|
||||
};
|
||||
module_i2c_driver(tcs3472_driver);
|
||||
|
||||
MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
|
||||
MODULE_DESCRIPTION("TCS3472 color light sensors driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -526,6 +526,20 @@ error_ret:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_event_spec tsl2563_events[] = {
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_RISING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
||||
BIT(IIO_EV_INFO_ENABLE),
|
||||
}, {
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_FALLING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
||||
BIT(IIO_EV_INFO_ENABLE),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec tsl2563_channels[] = {
|
||||
{
|
||||
.type = IIO_LIGHT,
|
||||
|
@ -538,10 +552,8 @@ static const struct iio_chan_spec tsl2563_channels[] = {
|
|||
.channel2 = IIO_MOD_LIGHT_BOTH,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE),
|
||||
.event_mask = (IIO_EV_BIT(IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_RISING) |
|
||||
IIO_EV_BIT(IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_FALLING)),
|
||||
.event_spec = tsl2563_events,
|
||||
.num_event_specs = ARRAY_SIZE(tsl2563_events),
|
||||
}, {
|
||||
.type = IIO_INTENSITY,
|
||||
.modified = 1,
|
||||
|
@ -552,12 +564,13 @@ static const struct iio_chan_spec tsl2563_channels[] = {
|
|||
};
|
||||
|
||||
static int tsl2563_read_thresh(struct iio_dev *indio_dev,
|
||||
u64 event_code,
|
||||
int *val)
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir, enum iio_event_info info, int *val,
|
||||
int *val2)
|
||||
{
|
||||
struct tsl2563_chip *chip = iio_priv(indio_dev);
|
||||
|
||||
switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
|
||||
switch (dir) {
|
||||
case IIO_EV_DIR_RISING:
|
||||
*val = chip->high_thres;
|
||||
break;
|
||||
|
@ -568,18 +581,19 @@ static int tsl2563_read_thresh(struct iio_dev *indio_dev,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int tsl2563_write_thresh(struct iio_dev *indio_dev,
|
||||
u64 event_code,
|
||||
int val)
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir, enum iio_event_info info, int val,
|
||||
int val2)
|
||||
{
|
||||
struct tsl2563_chip *chip = iio_priv(indio_dev);
|
||||
int ret;
|
||||
u8 address;
|
||||
|
||||
if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_RISING)
|
||||
if (dir == IIO_EV_DIR_RISING)
|
||||
address = TSL2563_REG_HIGHLOW;
|
||||
else
|
||||
address = TSL2563_REG_LOWLOW;
|
||||
|
@ -591,7 +605,7 @@ static int tsl2563_write_thresh(struct iio_dev *indio_dev,
|
|||
ret = i2c_smbus_write_byte_data(chip->client,
|
||||
TSL2563_CMD | (address + 1),
|
||||
(val >> 8) & 0xFF);
|
||||
if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_RISING)
|
||||
if (dir == IIO_EV_DIR_RISING)
|
||||
chip->high_thres = val;
|
||||
else
|
||||
chip->low_thres = val;
|
||||
|
@ -620,8 +634,8 @@ static irqreturn_t tsl2563_event_handler(int irq, void *private)
|
|||
}
|
||||
|
||||
static int tsl2563_write_interrupt_config(struct iio_dev *indio_dev,
|
||||
u64 event_code,
|
||||
int state)
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir, int state)
|
||||
{
|
||||
struct tsl2563_chip *chip = iio_priv(indio_dev);
|
||||
int ret = 0;
|
||||
|
@ -662,7 +676,8 @@ out:
|
|||
}
|
||||
|
||||
static int tsl2563_read_interrupt_config(struct iio_dev *indio_dev,
|
||||
u64 event_code)
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir)
|
||||
{
|
||||
struct tsl2563_chip *chip = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
@ -687,10 +702,10 @@ static const struct iio_info tsl2563_info = {
|
|||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &tsl2563_read_raw,
|
||||
.write_raw = &tsl2563_write_raw,
|
||||
.read_event_value = &tsl2563_read_thresh,
|
||||
.write_event_value = &tsl2563_write_thresh,
|
||||
.read_event_config = &tsl2563_read_interrupt_config,
|
||||
.write_event_config = &tsl2563_write_interrupt_config,
|
||||
.read_event_value_new = &tsl2563_read_thresh,
|
||||
.write_event_value_new = &tsl2563_write_thresh,
|
||||
.read_event_config_new = &tsl2563_read_interrupt_config,
|
||||
.write_event_config_new = &tsl2563_write_interrupt_config,
|
||||
};
|
||||
|
||||
static int tsl2563_probe(struct i2c_client *client,
|
||||
|
|
|
@ -0,0 +1,258 @@
|
|||
/*
|
||||
* tsl4531.c - Support for TAOS TSL4531 ambient light sensor
|
||||
*
|
||||
* Copyright 2013 Peter Meerwald <pmeerw@pmeerw.net>
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* IIO driver for the TSL4531x family
|
||||
* TSL45311/TSL45313: 7-bit I2C slave address 0x39
|
||||
* TSL45315/TSL45317: 7-bit I2C slave address 0x29
|
||||
*
|
||||
* TODO: single cycle measurement
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define TSL4531_DRV_NAME "tsl4531"
|
||||
|
||||
#define TCS3472_COMMAND BIT(7)
|
||||
|
||||
#define TSL4531_CONTROL (TCS3472_COMMAND | 0x00)
|
||||
#define TSL4531_CONFIG (TCS3472_COMMAND | 0x01)
|
||||
#define TSL4531_DATA (TCS3472_COMMAND | 0x04)
|
||||
#define TSL4531_ID (TCS3472_COMMAND | 0x0a)
|
||||
|
||||
/* operating modes in control register */
|
||||
#define TSL4531_MODE_POWERDOWN 0x00
|
||||
#define TSL4531_MODE_SINGLE_ADC 0x02
|
||||
#define TSL4531_MODE_NORMAL 0x03
|
||||
|
||||
/* integration time control in config register */
|
||||
#define TSL4531_TCNTRL_400MS 0x00
|
||||
#define TSL4531_TCNTRL_200MS 0x01
|
||||
#define TSL4531_TCNTRL_100MS 0x02
|
||||
|
||||
/* part number in id register */
|
||||
#define TSL45311_ID 0x8
|
||||
#define TSL45313_ID 0x9
|
||||
#define TSL45315_ID 0xa
|
||||
#define TSL45317_ID 0xb
|
||||
#define TSL4531_ID_SHIFT 4
|
||||
|
||||
struct tsl4531_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock;
|
||||
int int_time;
|
||||
};
|
||||
|
||||
static IIO_CONST_ATTR_INT_TIME_AVAIL("0.1 0.2 0.4");
|
||||
|
||||
static struct attribute *tsl4531_attributes[] = {
|
||||
&iio_const_attr_integration_time_available.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group tsl4531_attribute_group = {
|
||||
.attrs = tsl4531_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec tsl4531_channels[] = {
|
||||
{
|
||||
.type = IIO_LIGHT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_INT_TIME)
|
||||
}
|
||||
};
|
||||
|
||||
static int tsl4531_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct tsl4531_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = i2c_smbus_read_word_data(data->client,
|
||||
TSL4531_DATA);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
/* 0.. 1x, 1 .. 2x, 2 .. 4x */
|
||||
*val = 1 << data->int_time;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
if (data->int_time == 0)
|
||||
*val2 = 400000;
|
||||
else if (data->int_time == 1)
|
||||
*val2 = 200000;
|
||||
else if (data->int_time == 2)
|
||||
*val2 = 100000;
|
||||
else
|
||||
return -EINVAL;
|
||||
*val = 0;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int tsl4531_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct tsl4531_data *data = iio_priv(indio_dev);
|
||||
int int_time, ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
if (val != 0)
|
||||
return -EINVAL;
|
||||
if (val2 == 400000)
|
||||
int_time = 0;
|
||||
else if (val2 == 200000)
|
||||
int_time = 1;
|
||||
else if (val2 == 100000)
|
||||
int_time = 2;
|
||||
else
|
||||
return -EINVAL;
|
||||
mutex_lock(&data->lock);
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
TSL4531_CONFIG, int_time);
|
||||
if (ret >= 0)
|
||||
data->int_time = int_time;
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info tsl4531_info = {
|
||||
.read_raw = tsl4531_read_raw,
|
||||
.write_raw = tsl4531_write_raw,
|
||||
.attrs = &tsl4531_attribute_group,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int tsl4531_check_id(struct i2c_client *client)
|
||||
{
|
||||
int ret = i2c_smbus_read_byte_data(client, TSL4531_ID);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
switch (ret >> TSL4531_ID_SHIFT) {
|
||||
case TSL45311_ID:
|
||||
case TSL45313_ID:
|
||||
case TSL45315_ID:
|
||||
case TSL45317_ID:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int tsl4531_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct tsl4531_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
mutex_init(&data->lock);
|
||||
|
||||
if (!tsl4531_check_id(client)) {
|
||||
dev_err(&client->dev, "no TSL4531 sensor\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client, TSL4531_CONTROL,
|
||||
TSL4531_MODE_NORMAL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client, TSL4531_CONFIG,
|
||||
TSL4531_TCNTRL_400MS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &tsl4531_info;
|
||||
indio_dev->channels = tsl4531_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(tsl4531_channels);
|
||||
indio_dev->name = TSL4531_DRV_NAME;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
return iio_device_register(indio_dev);
|
||||
}
|
||||
|
||||
static int tsl4531_powerdown(struct i2c_client *client)
|
||||
{
|
||||
return i2c_smbus_write_byte_data(client, TSL4531_CONTROL,
|
||||
TSL4531_MODE_POWERDOWN);
|
||||
}
|
||||
|
||||
static int tsl4531_remove(struct i2c_client *client)
|
||||
{
|
||||
iio_device_unregister(i2c_get_clientdata(client));
|
||||
tsl4531_powerdown(client);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int tsl4531_suspend(struct device *dev)
|
||||
{
|
||||
return tsl4531_powerdown(to_i2c_client(dev));
|
||||
}
|
||||
|
||||
static int tsl4531_resume(struct device *dev)
|
||||
{
|
||||
return i2c_smbus_write_byte_data(to_i2c_client(dev), TSL4531_CONTROL,
|
||||
TSL4531_MODE_NORMAL);
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(tsl4531_pm_ops, tsl4531_suspend, tsl4531_resume);
|
||||
|
||||
static const struct i2c_device_id tsl4531_id[] = {
|
||||
{ "tsl4531", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, tsl4531_id);
|
||||
|
||||
static struct i2c_driver tsl4531_driver = {
|
||||
.driver = {
|
||||
.name = TSL4531_DRV_NAME,
|
||||
.pm = &tsl4531_pm_ops,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = tsl4531_probe,
|
||||
.remove = tsl4531_remove,
|
||||
.id_table = tsl4531_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(tsl4531_driver);
|
||||
|
||||
MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
|
||||
MODULE_DESCRIPTION("TAOS TSL4531 ambient light sensors driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -179,11 +179,7 @@ static int vcnl4000_probe(struct i2c_client *client,
|
|||
indio_dev->name = VCNL4000_DRV_NAME;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
return iio_device_register(indio_dev);
|
||||
}
|
||||
|
||||
static int vcnl4000_remove(struct i2c_client *client)
|
||||
|
|
|
@ -16,6 +16,16 @@ config AK8975
|
|||
To compile this driver as a module, choose M here: the module
|
||||
will be called ak8975.
|
||||
|
||||
config MAG3110
|
||||
tristate "Freescale MAG3110 3-Axis Magnetometer"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to build support for the Freescale MAG3110 3-Axis
|
||||
magnetometer.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called mag3110.
|
||||
|
||||
config HID_SENSOR_MAGNETOMETER_3D
|
||||
depends on HID_SENSOR_HUB
|
||||
select IIO_BUFFER
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_AK8975) += ak8975.o
|
||||
obj-$(CONFIG_MAG3110) += mag3110.o
|
||||
obj-$(CONFIG_HID_SENSOR_MAGNETOMETER_3D) += hid-sensor-magn-3d.o
|
||||
|
||||
obj-$(CONFIG_IIO_ST_MAGN_3AXIS) += st_magn.o
|
||||
|
|
|
@ -263,7 +263,7 @@ static int ak8975_setup(struct i2c_client *client)
|
|||
*
|
||||
* HuT = H * 1229/4096, or roughly, 3/10.
|
||||
*
|
||||
* Since 1uT = 100 gauss, our final scale factor becomes:
|
||||
* Since 1uT = 0.01 gauss, our final scale factor becomes:
|
||||
*
|
||||
* Hadj = H * ((ASA + 128) / 256) * 3/10 * 100
|
||||
* Hadj = H * ((ASA + 128) * 30 / 256
|
||||
|
|
|
@ -183,10 +183,11 @@ static const struct iio_info magn_3d_info = {
|
|||
};
|
||||
|
||||
/* Function to push data to buffer */
|
||||
static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len)
|
||||
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, (u8 *)data);
|
||||
iio_push_to_buffers(indio_dev, data);
|
||||
}
|
||||
|
||||
/* Callback handler to send event after all samples are received and captured */
|
||||
|
@ -201,7 +202,7 @@ static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev,
|
|||
magn_state->common_attributes.data_ready);
|
||||
if (magn_state->common_attributes.data_ready)
|
||||
hid_sensor_push_data(indio_dev,
|
||||
(u8 *)magn_state->magn_val,
|
||||
magn_state->magn_val,
|
||||
sizeof(magn_state->magn_val));
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -0,0 +1,401 @@
|
|||
/*
|
||||
* mag3110.c - Support for Freescale MAG3110 magnetometer sensor
|
||||
*
|
||||
* Copyright (c) 2013 Peter Meerwald <pmeerw@pmeerw.net>
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* (7-bit I2C slave address 0x0e)
|
||||
*
|
||||
* TODO: irq, user offset, oversampling, continuous mode
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#define MAG3110_STATUS 0x00
|
||||
#define MAG3110_OUT_X 0x01 /* MSB first */
|
||||
#define MAG3110_OUT_Y 0x03
|
||||
#define MAG3110_OUT_Z 0x05
|
||||
#define MAG3110_WHO_AM_I 0x07
|
||||
#define MAG3110_OFF_X 0x09 /* MSB first */
|
||||
#define MAG3110_OFF_Y 0x0b
|
||||
#define MAG3110_OFF_Z 0x0d
|
||||
#define MAG3110_DIE_TEMP 0x0f
|
||||
#define MAG3110_CTRL_REG1 0x10
|
||||
#define MAG3110_CTRL_REG2 0x11
|
||||
|
||||
#define MAG3110_STATUS_DRDY (BIT(2) | BIT(1) | BIT(0))
|
||||
|
||||
#define MAG3110_CTRL_DR_MASK (BIT(7) | BIT(6) | BIT(5))
|
||||
#define MAG3110_CTRL_DR_SHIFT 5
|
||||
#define MAG3110_CTRL_DR_DEFAULT 0
|
||||
|
||||
#define MAG3110_CTRL_TM BIT(1) /* trigger single measurement */
|
||||
#define MAG3110_CTRL_AC BIT(0) /* continuous measurements */
|
||||
|
||||
#define MAG3110_CTRL_AUTO_MRST_EN BIT(7) /* magnetic auto-reset */
|
||||
#define MAG3110_CTRL_RAW BIT(5) /* measurements not user-offset corrected */
|
||||
|
||||
#define MAG3110_DEVICE_ID 0xc4
|
||||
|
||||
/* Each client has this additional data */
|
||||
struct mag3110_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock;
|
||||
u8 ctrl_reg1;
|
||||
};
|
||||
|
||||
static int mag3110_request(struct mag3110_data *data)
|
||||
{
|
||||
int ret, tries = 150;
|
||||
|
||||
/* trigger measurement */
|
||||
ret = i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1,
|
||||
data->ctrl_reg1 | MAG3110_CTRL_TM);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
while (tries-- > 0) {
|
||||
ret = i2c_smbus_read_byte_data(data->client, MAG3110_STATUS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/* wait for data ready */
|
||||
if ((ret & MAG3110_STATUS_DRDY) == MAG3110_STATUS_DRDY)
|
||||
break;
|
||||
msleep(20);
|
||||
}
|
||||
|
||||
if (tries < 0) {
|
||||
dev_err(&data->client->dev, "data not ready\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mag3110_read(struct mag3110_data *data, __be16 buf[3])
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
ret = mag3110_request(data);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
ret = i2c_smbus_read_i2c_block_data(data->client,
|
||||
MAG3110_OUT_X, 3 * sizeof(__be16), (u8 *) buf);
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t mag3110_show_int_plus_micros(char *buf,
|
||||
const int (*vals)[2], int n)
|
||||
{
|
||||
size_t len = 0;
|
||||
|
||||
while (n-- > 0)
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len,
|
||||
"%d.%d ", vals[n][0], vals[n][1]);
|
||||
|
||||
/* replace trailing space by newline */
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int mag3110_get_int_plus_micros_index(const int (*vals)[2], int n,
|
||||
int val, int val2)
|
||||
{
|
||||
while (n-- > 0)
|
||||
if (val == vals[n][0] && val2 == vals[n][1])
|
||||
return n;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const int mag3110_samp_freq[8][2] = {
|
||||
{80, 0}, {40, 0}, {20, 0}, {10, 0}, {5, 0}, {2, 500000},
|
||||
{1, 250000}, {0, 625000}
|
||||
};
|
||||
|
||||
static ssize_t mag3110_show_samp_freq_avail(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return mag3110_show_int_plus_micros(buf, mag3110_samp_freq, 8);
|
||||
}
|
||||
|
||||
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(mag3110_show_samp_freq_avail);
|
||||
|
||||
static int mag3110_get_samp_freq_index(struct mag3110_data *data,
|
||||
int val, int val2)
|
||||
{
|
||||
return mag3110_get_int_plus_micros_index(mag3110_samp_freq, 8, val,
|
||||
val2);
|
||||
}
|
||||
|
||||
static int mag3110_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct mag3110_data *data = iio_priv(indio_dev);
|
||||
__be16 buffer[3];
|
||||
int i, ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
switch (chan->type) {
|
||||
case IIO_MAGN: /* in 0.1 uT / LSB */
|
||||
ret = mag3110_read(data, buffer);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = sign_extend32(
|
||||
be16_to_cpu(buffer[chan->scan_index]), 15);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_TEMP: /* in 1 C / LSB */
|
||||
mutex_lock(&data->lock);
|
||||
ret = mag3110_request(data);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
ret = i2c_smbus_read_byte_data(data->client,
|
||||
MAG3110_DIE_TEMP);
|
||||
mutex_unlock(&data->lock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = sign_extend32(ret, 7);
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
*val2 = 1000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
i = data->ctrl_reg1 >> MAG3110_CTRL_DR_SHIFT;
|
||||
*val = mag3110_samp_freq[i][0];
|
||||
*val2 = mag3110_samp_freq[i][1];
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int mag3110_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct mag3110_data *data = iio_priv(indio_dev);
|
||||
int rate;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
rate = mag3110_get_samp_freq_index(data, val, val2);
|
||||
if (rate < 0)
|
||||
return -EINVAL;
|
||||
|
||||
data->ctrl_reg1 &= ~MAG3110_CTRL_DR_MASK;
|
||||
data->ctrl_reg1 |= rate << MAG3110_CTRL_DR_SHIFT;
|
||||
return i2c_smbus_write_byte_data(data->client,
|
||||
MAG3110_CTRL_REG1, data->ctrl_reg1);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t mag3110_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct mag3110_data *data = iio_priv(indio_dev);
|
||||
u8 buffer[16]; /* 3 16-bit channels + 1 byte temp + padding + ts */
|
||||
int ret;
|
||||
|
||||
ret = mag3110_read(data, (__be16 *) buffer);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
|
||||
if (test_bit(3, indio_dev->active_scan_mask)) {
|
||||
ret = i2c_smbus_read_byte_data(data->client,
|
||||
MAG3110_DIE_TEMP);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
buffer[6] = ret;
|
||||
}
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, buffer,
|
||||
iio_get_time_ns());
|
||||
|
||||
done:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
#define MAG3110_CHANNEL(axis, idx) { \
|
||||
.type = IIO_MAGN, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_##axis, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.scan_index = idx, \
|
||||
.scan_type = IIO_ST('s', 16, 16, IIO_BE), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec mag3110_channels[] = {
|
||||
MAG3110_CHANNEL(X, 0),
|
||||
MAG3110_CHANNEL(Y, 1),
|
||||
MAG3110_CHANNEL(Z, 2),
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.scan_index = 3,
|
||||
.scan_type = IIO_ST('s', 8, 8, 0),
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4),
|
||||
};
|
||||
|
||||
static struct attribute *mag3110_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group mag3110_group = {
|
||||
.attrs = mag3110_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info mag3110_info = {
|
||||
.attrs = &mag3110_group,
|
||||
.read_raw = &mag3110_read_raw,
|
||||
.write_raw = &mag3110_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const unsigned long mag3110_scan_masks[] = {0x7, 0xf, 0};
|
||||
|
||||
static int mag3110_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct mag3110_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, MAG3110_WHO_AM_I);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != MAG3110_DEVICE_ID)
|
||||
return -ENODEV;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->client = client;
|
||||
mutex_init(&data->lock);
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
indio_dev->info = &mag3110_info;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = mag3110_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(mag3110_channels);
|
||||
indio_dev->available_scan_masks = mag3110_scan_masks;
|
||||
|
||||
data->ctrl_reg1 = MAG3110_CTRL_DR_DEFAULT;
|
||||
ret = i2c_smbus_write_byte_data(client, MAG3110_CTRL_REG1,
|
||||
data->ctrl_reg1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, MAG3110_CTRL_REG2,
|
||||
MAG3110_CTRL_AUTO_MRST_EN | MAG3110_CTRL_RAW);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
mag3110_trigger_handler, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
goto buffer_cleanup;
|
||||
return 0;
|
||||
|
||||
buffer_cleanup:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mag3110_standby(struct mag3110_data *data)
|
||||
{
|
||||
return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1,
|
||||
data->ctrl_reg1 & ~MAG3110_CTRL_AC);
|
||||
}
|
||||
|
||||
static int mag3110_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
mag3110_standby(iio_priv(indio_dev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mag3110_suspend(struct device *dev)
|
||||
{
|
||||
return mag3110_standby(iio_priv(i2c_get_clientdata(
|
||||
to_i2c_client(dev))));
|
||||
}
|
||||
|
||||
static int mag3110_resume(struct device *dev)
|
||||
{
|
||||
struct mag3110_data *data = iio_priv(i2c_get_clientdata(
|
||||
to_i2c_client(dev)));
|
||||
|
||||
return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1,
|
||||
data->ctrl_reg1);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(mag3110_pm_ops, mag3110_suspend, mag3110_resume);
|
||||
#define MAG3110_PM_OPS (&mag3110_pm_ops)
|
||||
#else
|
||||
#define MAG3110_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id mag3110_id[] = {
|
||||
{ "mag3110", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mag3110_id);
|
||||
|
||||
static struct i2c_driver mag3110_driver = {
|
||||
.driver = {
|
||||
.name = "mag3110",
|
||||
.pm = MAG3110_PM_OPS,
|
||||
},
|
||||
.probe = mag3110_probe,
|
||||
.remove = mag3110_remove,
|
||||
.id_table = mag3110_id,
|
||||
};
|
||||
module_i2c_driver(mag3110_driver);
|
||||
|
||||
MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
|
||||
MODULE_DESCRIPTION("Freescale MAG3110 magnetometer driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -25,16 +25,7 @@
|
|||
|
||||
static int st_magn_buffer_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = st_sensors_set_enable(indio_dev, true);
|
||||
if (err < 0)
|
||||
goto st_magn_set_enable_error;
|
||||
|
||||
err = iio_sw_buffer_preenable(indio_dev);
|
||||
|
||||
st_magn_set_enable_error:
|
||||
return err;
|
||||
return st_sensors_set_enable(indio_dev, true);
|
||||
}
|
||||
|
||||
static int st_magn_buffer_postenable(struct iio_dev *indio_dev)
|
||||
|
|
|
@ -348,8 +348,9 @@ static const struct iio_info magn_info = {
|
|||
int st_magn_common_probe(struct iio_dev *indio_dev,
|
||||
struct st_sensors_platform_data *pdata)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *mdata = iio_priv(indio_dev);
|
||||
int irq = mdata->get_irq_data_ready(indio_dev);
|
||||
int err;
|
||||
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &magn_info;
|
||||
|
@ -357,7 +358,7 @@ int st_magn_common_probe(struct iio_dev *indio_dev,
|
|||
err = st_sensors_check_device_support(indio_dev,
|
||||
ARRAY_SIZE(st_magn_sensors), st_magn_sensors);
|
||||
if (err < 0)
|
||||
goto st_magn_common_probe_error;
|
||||
return err;
|
||||
|
||||
mdata->num_data_channels = ST_MAGN_NUMBER_DATA_CHANNELS;
|
||||
mdata->multiread_bit = mdata->sensor->multi_read_bit;
|
||||
|
@ -370,12 +371,13 @@ int st_magn_common_probe(struct iio_dev *indio_dev,
|
|||
|
||||
err = st_sensors_init_sensor(indio_dev, pdata);
|
||||
if (err < 0)
|
||||
goto st_magn_common_probe_error;
|
||||
return err;
|
||||
|
||||
if (mdata->get_irq_data_ready(indio_dev) > 0) {
|
||||
err = st_magn_allocate_ring(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_magn_common_probe_error;
|
||||
err = st_magn_allocate_ring(indio_dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (irq > 0) {
|
||||
err = st_sensors_allocate_trigger(indio_dev, NULL);
|
||||
if (err < 0)
|
||||
goto st_magn_probe_trigger_error;
|
||||
|
@ -385,15 +387,14 @@ int st_magn_common_probe(struct iio_dev *indio_dev,
|
|||
if (err)
|
||||
goto st_magn_device_register_error;
|
||||
|
||||
return err;
|
||||
return 0;
|
||||
|
||||
st_magn_device_register_error:
|
||||
if (mdata->get_irq_data_ready(indio_dev) > 0)
|
||||
if (irq > 0)
|
||||
st_sensors_deallocate_trigger(indio_dev);
|
||||
st_magn_probe_trigger_error:
|
||||
if (mdata->get_irq_data_ready(indio_dev) > 0)
|
||||
st_magn_deallocate_ring(indio_dev);
|
||||
st_magn_common_probe_error:
|
||||
st_magn_deallocate_ring(indio_dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(st_magn_common_probe);
|
||||
|
@ -403,10 +404,10 @@ void st_magn_common_remove(struct iio_dev *indio_dev)
|
|||
struct st_sensor_data *mdata = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
if (mdata->get_irq_data_ready(indio_dev) > 0) {
|
||||
if (mdata->get_irq_data_ready(indio_dev) > 0)
|
||||
st_sensors_deallocate_trigger(indio_dev);
|
||||
st_magn_deallocate_ring(indio_dev);
|
||||
}
|
||||
|
||||
st_magn_deallocate_ring(indio_dev);
|
||||
}
|
||||
EXPORT_SYMBOL(st_magn_common_remove);
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ config IIO_ST_PRESS
|
|||
select IIO_TRIGGERED_BUFFER if (IIO_BUFFER)
|
||||
help
|
||||
Say yes here to build support for STMicroelectronics pressure
|
||||
sensors: LPS331AP.
|
||||
sensors: LPS001WP, LPS331AP.
|
||||
|
||||
This driver can also be built as a module. If so, these modules
|
||||
will be created:
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
|
||||
#define LPS001WP_PRESS_DEV_NAME "lps001wp"
|
||||
#define LPS331AP_PRESS_DEV_NAME "lps331ap"
|
||||
|
||||
/**
|
||||
|
|
|
@ -32,16 +32,7 @@ int st_press_trig_set_state(struct iio_trigger *trig, bool state)
|
|||
|
||||
static int st_press_buffer_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = st_sensors_set_enable(indio_dev, true);
|
||||
if (err < 0)
|
||||
goto st_press_set_enable_error;
|
||||
|
||||
err = iio_sw_buffer_preenable(indio_dev);
|
||||
|
||||
st_press_set_enable_error:
|
||||
return err;
|
||||
return st_sensors_set_enable(indio_dev, true);
|
||||
}
|
||||
|
||||
static int st_press_buffer_postenable(struct iio_dev *indio_dev)
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
|
@ -36,94 +37,200 @@
|
|||
ST_PRESS_LSB_PER_CELSIUS)
|
||||
#define ST_PRESS_NUMBER_DATA_CHANNELS 1
|
||||
|
||||
/* DEFAULT VALUE FOR SENSORS */
|
||||
#define ST_PRESS_DEFAULT_OUT_XL_ADDR 0x28
|
||||
#define ST_TEMP_DEFAULT_OUT_L_ADDR 0x2b
|
||||
|
||||
/* FULLSCALE */
|
||||
#define ST_PRESS_FS_AVL_1260MB 1260
|
||||
|
||||
/* CUSTOM VALUES FOR SENSOR 1 */
|
||||
#define ST_PRESS_1_WAI_EXP 0xbb
|
||||
#define ST_PRESS_1_ODR_ADDR 0x20
|
||||
#define ST_PRESS_1_ODR_MASK 0x70
|
||||
#define ST_PRESS_1_ODR_AVL_1HZ_VAL 0x01
|
||||
#define ST_PRESS_1_ODR_AVL_7HZ_VAL 0x05
|
||||
#define ST_PRESS_1_ODR_AVL_13HZ_VAL 0x06
|
||||
#define ST_PRESS_1_ODR_AVL_25HZ_VAL 0x07
|
||||
#define ST_PRESS_1_PW_ADDR 0x20
|
||||
#define ST_PRESS_1_PW_MASK 0x80
|
||||
#define ST_PRESS_1_FS_ADDR 0x23
|
||||
#define ST_PRESS_1_FS_MASK 0x30
|
||||
#define ST_PRESS_1_FS_AVL_1260_VAL 0x00
|
||||
#define ST_PRESS_1_FS_AVL_1260_GAIN ST_PRESS_KPASCAL_NANO_SCALE
|
||||
#define ST_PRESS_1_FS_AVL_TEMP_GAIN ST_PRESS_CELSIUS_NANO_SCALE
|
||||
#define ST_PRESS_1_BDU_ADDR 0x20
|
||||
#define ST_PRESS_1_BDU_MASK 0x04
|
||||
#define ST_PRESS_1_DRDY_IRQ_ADDR 0x22
|
||||
#define ST_PRESS_1_DRDY_IRQ_INT1_MASK 0x04
|
||||
#define ST_PRESS_1_DRDY_IRQ_INT2_MASK 0x20
|
||||
#define ST_PRESS_1_MULTIREAD_BIT true
|
||||
#define ST_PRESS_1_TEMP_OFFSET 42500
|
||||
/* CUSTOM VALUES FOR LPS331AP SENSOR */
|
||||
#define ST_PRESS_LPS331AP_WAI_EXP 0xbb
|
||||
#define ST_PRESS_LPS331AP_ODR_ADDR 0x20
|
||||
#define ST_PRESS_LPS331AP_ODR_MASK 0x70
|
||||
#define ST_PRESS_LPS331AP_ODR_AVL_1HZ_VAL 0x01
|
||||
#define ST_PRESS_LPS331AP_ODR_AVL_7HZ_VAL 0x05
|
||||
#define ST_PRESS_LPS331AP_ODR_AVL_13HZ_VAL 0x06
|
||||
#define ST_PRESS_LPS331AP_ODR_AVL_25HZ_VAL 0x07
|
||||
#define ST_PRESS_LPS331AP_PW_ADDR 0x20
|
||||
#define ST_PRESS_LPS331AP_PW_MASK 0x80
|
||||
#define ST_PRESS_LPS331AP_FS_ADDR 0x23
|
||||
#define ST_PRESS_LPS331AP_FS_MASK 0x30
|
||||
#define ST_PRESS_LPS331AP_FS_AVL_1260_VAL 0x00
|
||||
#define ST_PRESS_LPS331AP_FS_AVL_1260_GAIN ST_PRESS_KPASCAL_NANO_SCALE
|
||||
#define ST_PRESS_LPS331AP_FS_AVL_TEMP_GAIN ST_PRESS_CELSIUS_NANO_SCALE
|
||||
#define ST_PRESS_LPS331AP_BDU_ADDR 0x20
|
||||
#define ST_PRESS_LPS331AP_BDU_MASK 0x04
|
||||
#define ST_PRESS_LPS331AP_DRDY_IRQ_ADDR 0x22
|
||||
#define ST_PRESS_LPS331AP_DRDY_IRQ_INT1_MASK 0x04
|
||||
#define ST_PRESS_LPS331AP_DRDY_IRQ_INT2_MASK 0x20
|
||||
#define ST_PRESS_LPS331AP_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
|
||||
|
||||
static const struct iio_chan_spec st_press_channels[] = {
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_PRESSURE,
|
||||
/* CUSTOM VALUES FOR LPS001WP SENSOR */
|
||||
#define ST_PRESS_LPS001WP_WAI_EXP 0xba
|
||||
#define ST_PRESS_LPS001WP_ODR_ADDR 0x20
|
||||
#define ST_PRESS_LPS001WP_ODR_MASK 0x30
|
||||
#define ST_PRESS_LPS001WP_ODR_AVL_1HZ_VAL 0x01
|
||||
#define ST_PRESS_LPS001WP_ODR_AVL_7HZ_VAL 0x02
|
||||
#define ST_PRESS_LPS001WP_ODR_AVL_13HZ_VAL 0x03
|
||||
#define ST_PRESS_LPS001WP_PW_ADDR 0x20
|
||||
#define ST_PRESS_LPS001WP_PW_MASK 0x40
|
||||
#define ST_PRESS_LPS001WP_BDU_ADDR 0x20
|
||||
#define ST_PRESS_LPS001WP_BDU_MASK 0x04
|
||||
#define ST_PRESS_LPS001WP_MULTIREAD_BIT true
|
||||
#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[] = {
|
||||
{
|
||||
.type = IIO_PRESSURE,
|
||||
.channel2 = IIO_NO_MOD,
|
||||
.address = ST_PRESS_LPS331AP_OUT_XL_ADDR,
|
||||
.scan_index = ST_SENSORS_SCAN_X,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 24,
|
||||
.storagebits = 24,
|
||||
.endianness = IIO_LE,
|
||||
},
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
ST_SENSORS_SCAN_X, 0, IIO_NO_MOD, 'u', IIO_LE, 24, 24,
|
||||
ST_PRESS_DEFAULT_OUT_XL_ADDR),
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_TEMP,
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
-1, 0, IIO_NO_MOD, 's', IIO_LE, 16, 16,
|
||||
ST_TEMP_DEFAULT_OUT_L_ADDR),
|
||||
.modified = 0,
|
||||
},
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.channel2 = IIO_NO_MOD,
|
||||
.address = ST_TEMP_LPS331AP_OUT_L_ADDR,
|
||||
.scan_index = -1,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
.endianness = IIO_LE,
|
||||
},
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.modified = 0,
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1)
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec st_press_lps001wp_channels[] = {
|
||||
{
|
||||
.type = IIO_PRESSURE,
|
||||
.channel2 = IIO_NO_MOD,
|
||||
.address = ST_PRESS_LPS001WP_OUT_L_ADDR,
|
||||
.scan_index = ST_SENSORS_SCAN_X,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
.endianness = IIO_LE,
|
||||
},
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.modified = 0,
|
||||
},
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.channel2 = IIO_NO_MOD,
|
||||
.address = ST_TEMP_LPS001WP_OUT_L_ADDR,
|
||||
.scan_index = -1,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
.endianness = IIO_LE,
|
||||
},
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.modified = 0,
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1)
|
||||
};
|
||||
|
||||
static const struct st_sensors st_press_sensors[] = {
|
||||
{
|
||||
.wai = ST_PRESS_1_WAI_EXP,
|
||||
.wai = ST_PRESS_LPS331AP_WAI_EXP,
|
||||
.sensors_supported = {
|
||||
[0] = LPS331AP_PRESS_DEV_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_press_channels,
|
||||
.ch = (struct iio_chan_spec *)st_press_lps331ap_channels,
|
||||
.num_ch = ARRAY_SIZE(st_press_lps331ap_channels),
|
||||
.odr = {
|
||||
.addr = ST_PRESS_1_ODR_ADDR,
|
||||
.mask = ST_PRESS_1_ODR_MASK,
|
||||
.addr = ST_PRESS_LPS331AP_ODR_ADDR,
|
||||
.mask = ST_PRESS_LPS331AP_ODR_MASK,
|
||||
.odr_avl = {
|
||||
{ 1, ST_PRESS_1_ODR_AVL_1HZ_VAL, },
|
||||
{ 7, ST_PRESS_1_ODR_AVL_7HZ_VAL, },
|
||||
{ 13, ST_PRESS_1_ODR_AVL_13HZ_VAL, },
|
||||
{ 25, ST_PRESS_1_ODR_AVL_25HZ_VAL, },
|
||||
{ 1, ST_PRESS_LPS331AP_ODR_AVL_1HZ_VAL, },
|
||||
{ 7, ST_PRESS_LPS331AP_ODR_AVL_7HZ_VAL, },
|
||||
{ 13, ST_PRESS_LPS331AP_ODR_AVL_13HZ_VAL, },
|
||||
{ 25, ST_PRESS_LPS331AP_ODR_AVL_25HZ_VAL, },
|
||||
},
|
||||
},
|
||||
.pw = {
|
||||
.addr = ST_PRESS_1_PW_ADDR,
|
||||
.mask = ST_PRESS_1_PW_MASK,
|
||||
.addr = ST_PRESS_LPS331AP_PW_ADDR,
|
||||
.mask = ST_PRESS_LPS331AP_PW_MASK,
|
||||
.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
|
||||
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
|
||||
},
|
||||
.fs = {
|
||||
.addr = ST_PRESS_1_FS_ADDR,
|
||||
.mask = ST_PRESS_1_FS_MASK,
|
||||
.addr = ST_PRESS_LPS331AP_FS_ADDR,
|
||||
.mask = ST_PRESS_LPS331AP_FS_MASK,
|
||||
.fs_avl = {
|
||||
[0] = {
|
||||
.num = ST_PRESS_FS_AVL_1260MB,
|
||||
.value = ST_PRESS_1_FS_AVL_1260_VAL,
|
||||
.gain = ST_PRESS_1_FS_AVL_1260_GAIN,
|
||||
.gain2 = ST_PRESS_1_FS_AVL_TEMP_GAIN,
|
||||
.value = ST_PRESS_LPS331AP_FS_AVL_1260_VAL,
|
||||
.gain = ST_PRESS_LPS331AP_FS_AVL_1260_GAIN,
|
||||
.gain2 = ST_PRESS_LPS331AP_FS_AVL_TEMP_GAIN,
|
||||
},
|
||||
},
|
||||
},
|
||||
.bdu = {
|
||||
.addr = ST_PRESS_1_BDU_ADDR,
|
||||
.mask = ST_PRESS_1_BDU_MASK,
|
||||
.addr = ST_PRESS_LPS331AP_BDU_ADDR,
|
||||
.mask = ST_PRESS_LPS331AP_BDU_MASK,
|
||||
},
|
||||
.drdy_irq = {
|
||||
.addr = ST_PRESS_1_DRDY_IRQ_ADDR,
|
||||
.mask_int1 = ST_PRESS_1_DRDY_IRQ_INT1_MASK,
|
||||
.mask_int2 = ST_PRESS_1_DRDY_IRQ_INT2_MASK,
|
||||
.addr = ST_PRESS_LPS331AP_DRDY_IRQ_ADDR,
|
||||
.mask_int1 = ST_PRESS_LPS331AP_DRDY_IRQ_INT1_MASK,
|
||||
.mask_int2 = ST_PRESS_LPS331AP_DRDY_IRQ_INT2_MASK,
|
||||
},
|
||||
.multi_read_bit = ST_PRESS_1_MULTIREAD_BIT,
|
||||
.multi_read_bit = ST_PRESS_LPS331AP_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
},
|
||||
{
|
||||
.wai = ST_PRESS_LPS001WP_WAI_EXP,
|
||||
.sensors_supported = {
|
||||
[0] = LPS001WP_PRESS_DEV_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_press_lps001wp_channels,
|
||||
.num_ch = ARRAY_SIZE(st_press_lps001wp_channels),
|
||||
.odr = {
|
||||
.addr = ST_PRESS_LPS001WP_ODR_ADDR,
|
||||
.mask = ST_PRESS_LPS001WP_ODR_MASK,
|
||||
.odr_avl = {
|
||||
{ 1, ST_PRESS_LPS001WP_ODR_AVL_1HZ_VAL, },
|
||||
{ 7, ST_PRESS_LPS001WP_ODR_AVL_7HZ_VAL, },
|
||||
{ 13, ST_PRESS_LPS001WP_ODR_AVL_13HZ_VAL, },
|
||||
},
|
||||
},
|
||||
.pw = {
|
||||
.addr = ST_PRESS_LPS001WP_PW_ADDR,
|
||||
.mask = ST_PRESS_LPS001WP_PW_MASK,
|
||||
.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
|
||||
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
|
||||
},
|
||||
.fs = {
|
||||
.addr = 0,
|
||||
},
|
||||
.bdu = {
|
||||
.addr = ST_PRESS_LPS001WP_BDU_ADDR,
|
||||
.mask = ST_PRESS_LPS001WP_BDU_MASK,
|
||||
},
|
||||
.drdy_irq = {
|
||||
.addr = 0,
|
||||
},
|
||||
.multi_read_bit = ST_PRESS_LPS001WP_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
},
|
||||
};
|
||||
|
@ -207,44 +314,85 @@ static const struct iio_trigger_ops st_press_trigger_ops = {
|
|||
#define ST_PRESS_TRIGGER_OPS NULL
|
||||
#endif
|
||||
|
||||
static void st_press_power_enable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *pdata = iio_priv(indio_dev);
|
||||
int err;
|
||||
|
||||
/* Regulators not mandatory, but if requested we should enable them. */
|
||||
pdata->vdd = devm_regulator_get_optional(&indio_dev->dev, "vdd");
|
||||
if (!IS_ERR(pdata->vdd)) {
|
||||
err = regulator_enable(pdata->vdd);
|
||||
if (err != 0)
|
||||
dev_warn(&indio_dev->dev,
|
||||
"Failed to enable specified Vdd supply\n");
|
||||
}
|
||||
|
||||
pdata->vdd_io = devm_regulator_get_optional(&indio_dev->dev, "vddio");
|
||||
if (!IS_ERR(pdata->vdd_io)) {
|
||||
err = regulator_enable(pdata->vdd_io);
|
||||
if (err != 0)
|
||||
dev_warn(&indio_dev->dev,
|
||||
"Failed to enable specified Vdd_IO supply\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void st_press_power_disable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *pdata = iio_priv(indio_dev);
|
||||
|
||||
if (!IS_ERR(pdata->vdd))
|
||||
regulator_disable(pdata->vdd);
|
||||
|
||||
if (!IS_ERR(pdata->vdd_io))
|
||||
regulator_disable(pdata->vdd_io);
|
||||
}
|
||||
|
||||
int st_press_common_probe(struct iio_dev *indio_dev,
|
||||
struct st_sensors_platform_data *plat_data)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *pdata = iio_priv(indio_dev);
|
||||
int irq = pdata->get_irq_data_ready(indio_dev);
|
||||
int err;
|
||||
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &press_info;
|
||||
|
||||
st_press_power_enable(indio_dev);
|
||||
|
||||
err = st_sensors_check_device_support(indio_dev,
|
||||
ARRAY_SIZE(st_press_sensors), st_press_sensors);
|
||||
ARRAY_SIZE(st_press_sensors),
|
||||
st_press_sensors);
|
||||
if (err < 0)
|
||||
goto st_press_common_probe_error;
|
||||
return err;
|
||||
|
||||
pdata->num_data_channels = ST_PRESS_NUMBER_DATA_CHANNELS;
|
||||
pdata->multiread_bit = pdata->sensor->multi_read_bit;
|
||||
indio_dev->channels = pdata->sensor->ch;
|
||||
indio_dev->num_channels = ARRAY_SIZE(st_press_channels);
|
||||
pdata->multiread_bit = pdata->sensor->multi_read_bit;
|
||||
indio_dev->channels = pdata->sensor->ch;
|
||||
indio_dev->num_channels = pdata->sensor->num_ch;
|
||||
|
||||
if (pdata->sensor->fs.addr != 0)
|
||||
pdata->current_fullscale = (struct st_sensor_fullscale_avl *)
|
||||
&pdata->sensor->fs.fs_avl[0];
|
||||
|
||||
pdata->current_fullscale = (struct st_sensor_fullscale_avl *)
|
||||
&pdata->sensor->fs.fs_avl[0];
|
||||
pdata->odr = pdata->sensor->odr.odr_avl[0].hz;
|
||||
|
||||
if (!plat_data)
|
||||
/* Some devices don't support a data ready pin. */
|
||||
if (!plat_data && pdata->sensor->drdy_irq.addr)
|
||||
plat_data =
|
||||
(struct st_sensors_platform_data *)&default_press_pdata;
|
||||
|
||||
err = st_sensors_init_sensor(indio_dev, plat_data);
|
||||
if (err < 0)
|
||||
goto st_press_common_probe_error;
|
||||
return err;
|
||||
|
||||
if (pdata->get_irq_data_ready(indio_dev) > 0) {
|
||||
err = st_press_allocate_ring(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_press_common_probe_error;
|
||||
err = st_press_allocate_ring(indio_dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (irq > 0) {
|
||||
err = st_sensors_allocate_trigger(indio_dev,
|
||||
ST_PRESS_TRIGGER_OPS);
|
||||
ST_PRESS_TRIGGER_OPS);
|
||||
if (err < 0)
|
||||
goto st_press_probe_trigger_error;
|
||||
}
|
||||
|
@ -256,12 +404,11 @@ int st_press_common_probe(struct iio_dev *indio_dev,
|
|||
return err;
|
||||
|
||||
st_press_device_register_error:
|
||||
if (pdata->get_irq_data_ready(indio_dev) > 0)
|
||||
if (irq > 0)
|
||||
st_sensors_deallocate_trigger(indio_dev);
|
||||
st_press_probe_trigger_error:
|
||||
if (pdata->get_irq_data_ready(indio_dev) > 0)
|
||||
st_press_deallocate_ring(indio_dev);
|
||||
st_press_common_probe_error:
|
||||
st_press_deallocate_ring(indio_dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(st_press_common_probe);
|
||||
|
@ -270,11 +417,13 @@ void st_press_common_remove(struct iio_dev *indio_dev)
|
|||
{
|
||||
struct st_sensor_data *pdata = iio_priv(indio_dev);
|
||||
|
||||
st_press_power_disable(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
if (pdata->get_irq_data_ready(indio_dev) > 0) {
|
||||
if (pdata->get_irq_data_ready(indio_dev) > 0)
|
||||
st_sensors_deallocate_trigger(indio_dev);
|
||||
st_press_deallocate_ring(indio_dev);
|
||||
}
|
||||
|
||||
st_press_deallocate_ring(indio_dev);
|
||||
}
|
||||
EXPORT_SYMBOL(st_press_common_remove);
|
||||
|
||||
|
|
|
@ -49,6 +49,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 },
|
||||
{ LPS331AP_PRESS_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
|
|
|
@ -48,6 +48,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 },
|
||||
{ LPS331AP_PRESS_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
|
|
|
@ -70,12 +70,16 @@ static int tmp006_read_measurement(struct tmp006_data *data, u8 reg)
|
|||
return i2c_smbus_read_word_swapped(data->client, reg);
|
||||
}
|
||||
|
||||
static const int tmp006_freqs[5][2] = { {4, 0}, {2, 0}, {1, 0},
|
||||
{0, 500000}, {0, 250000} };
|
||||
|
||||
static int tmp006_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *channel, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct tmp006_data *data = iio_priv(indio_dev);
|
||||
s32 ret;
|
||||
int cr;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
|
@ -106,6 +110,12 @@ static int tmp006_read_raw(struct iio_dev *indio_dev,
|
|||
break;
|
||||
}
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
cr = (data->config & TMP006_CONFIG_CR_MASK)
|
||||
>> TMP006_CONFIG_CR_SHIFT;
|
||||
*val = tmp006_freqs[cr][0];
|
||||
*val2 = tmp006_freqs[cr][1];
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -113,48 +123,32 @@ static int tmp006_read_raw(struct iio_dev *indio_dev,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const char * const tmp006_freqs[] = { "4", "2", "1", "0.5", "0.25" };
|
||||
|
||||
static ssize_t tmp006_show_freq(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
static int tmp006_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct tmp006_data *data = iio_priv(dev_to_iio_dev(dev));
|
||||
int cr = (data->config & TMP006_CONFIG_CR_MASK)
|
||||
>> TMP006_CONFIG_CR_SHIFT;
|
||||
return sprintf(buf, "%s\n", tmp006_freqs[cr]);
|
||||
}
|
||||
|
||||
static ssize_t tmp006_store_freq(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct tmp006_data *data = iio_priv(indio_dev);
|
||||
int i;
|
||||
bool found = false;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tmp006_freqs); i++)
|
||||
if (sysfs_streq(buf, tmp006_freqs[i])) {
|
||||
found = true;
|
||||
break;
|
||||
if ((val == tmp006_freqs[i][0]) &&
|
||||
(val2 == tmp006_freqs[i][1])) {
|
||||
data->config &= ~TMP006_CONFIG_CR_MASK;
|
||||
data->config |= i << TMP006_CONFIG_CR_SHIFT;
|
||||
|
||||
return i2c_smbus_write_word_swapped(data->client,
|
||||
TMP006_CONFIG,
|
||||
data->config);
|
||||
|
||||
}
|
||||
if (!found)
|
||||
return -EINVAL;
|
||||
|
||||
data->config &= ~TMP006_CONFIG_CR_MASK;
|
||||
data->config |= i << TMP006_CONFIG_CR_SHIFT;
|
||||
|
||||
return i2c_smbus_write_word_swapped(data->client, TMP006_CONFIG,
|
||||
data->config);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static IIO_DEV_ATTR_SAMP_FREQ(S_IRUGO | S_IWUSR,
|
||||
tmp006_show_freq, tmp006_store_freq);
|
||||
|
||||
static IIO_CONST_ATTR(sampling_frequency_available, "4 2 1 0.5 0.25");
|
||||
|
||||
static struct attribute *tmp006_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency.dev_attr.attr,
|
||||
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
@ -168,16 +162,19 @@ static const struct iio_chan_spec tmp006_channels[] = {
|
|||
.type = IIO_VOLTAGE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
},
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
}
|
||||
};
|
||||
|
||||
static const struct iio_info tmp006_info = {
|
||||
.read_raw = tmp006_read_raw,
|
||||
.write_raw = tmp006_write_raw,
|
||||
.attrs = &tmp006_attribute_group,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
|
|
@ -23,7 +23,7 @@ struct iio_sysfs_trig {
|
|||
};
|
||||
|
||||
static LIST_HEAD(iio_sysfs_trig_list);
|
||||
static DEFINE_MUTEX(iio_syfs_trig_list_mut);
|
||||
static DEFINE_MUTEX(iio_sysfs_trig_list_mut);
|
||||
|
||||
static int iio_sysfs_trigger_probe(int id);
|
||||
static ssize_t iio_sysfs_trig_add(struct device *dev,
|
||||
|
@ -135,7 +135,7 @@ static int iio_sysfs_trigger_probe(int id)
|
|||
struct iio_sysfs_trig *t;
|
||||
int ret;
|
||||
bool foundit = false;
|
||||
mutex_lock(&iio_syfs_trig_list_mut);
|
||||
mutex_lock(&iio_sysfs_trig_list_mut);
|
||||
list_for_each_entry(t, &iio_sysfs_trig_list, l)
|
||||
if (id == t->id) {
|
||||
foundit = true;
|
||||
|
@ -169,7 +169,7 @@ static int iio_sysfs_trigger_probe(int id)
|
|||
goto out2;
|
||||
list_add(&t->l, &iio_sysfs_trig_list);
|
||||
__module_get(THIS_MODULE);
|
||||
mutex_unlock(&iio_syfs_trig_list_mut);
|
||||
mutex_unlock(&iio_sysfs_trig_list_mut);
|
||||
return 0;
|
||||
|
||||
out2:
|
||||
|
@ -177,7 +177,7 @@ out2:
|
|||
free_t:
|
||||
kfree(t);
|
||||
out1:
|
||||
mutex_unlock(&iio_syfs_trig_list_mut);
|
||||
mutex_unlock(&iio_sysfs_trig_list_mut);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -185,14 +185,14 @@ static int iio_sysfs_trigger_remove(int id)
|
|||
{
|
||||
bool foundit = false;
|
||||
struct iio_sysfs_trig *t;
|
||||
mutex_lock(&iio_syfs_trig_list_mut);
|
||||
mutex_lock(&iio_sysfs_trig_list_mut);
|
||||
list_for_each_entry(t, &iio_sysfs_trig_list, l)
|
||||
if (id == t->id) {
|
||||
foundit = true;
|
||||
break;
|
||||
}
|
||||
if (!foundit) {
|
||||
mutex_unlock(&iio_syfs_trig_list_mut);
|
||||
mutex_unlock(&iio_sysfs_trig_list_mut);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -202,7 +202,7 @@ static int iio_sysfs_trigger_remove(int id)
|
|||
list_del(&t->l);
|
||||
kfree(t);
|
||||
module_put(THIS_MODULE);
|
||||
mutex_unlock(&iio_syfs_trig_list_mut);
|
||||
mutex_unlock(&iio_sysfs_trig_list_mut);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -52,6 +52,7 @@ struct titsc {
|
|||
u32 config_inp[4];
|
||||
u32 bit_xp, bit_xn, bit_yp, bit_yn;
|
||||
u32 inp_xp, inp_xn, inp_yp, inp_yn;
|
||||
u32 step_mask;
|
||||
};
|
||||
|
||||
static unsigned int titsc_readl(struct titsc *ts, unsigned int reg)
|
||||
|
@ -196,7 +197,8 @@ static void titsc_step_config(struct titsc *ts_dev)
|
|||
|
||||
/* The steps1 … end and bit 0 for TS_Charge */
|
||||
stepenable = (1 << (end_step + 2)) - 1;
|
||||
am335x_tsc_se_set(ts_dev->mfd_tscadc, stepenable);
|
||||
ts_dev->step_mask = stepenable;
|
||||
am335x_tsc_se_set(ts_dev->mfd_tscadc, ts_dev->step_mask);
|
||||
}
|
||||
|
||||
static void titsc_read_coordinates(struct titsc *ts_dev,
|
||||
|
@ -260,6 +262,10 @@ static irqreturn_t titsc_irq(int irq, void *dev)
|
|||
unsigned int fsm;
|
||||
|
||||
status = titsc_readl(ts_dev, REG_IRQSTATUS);
|
||||
/*
|
||||
* ADC and touchscreen share the IRQ line.
|
||||
* FIFO1 interrupts are used by ADC. Handle FIFO0 IRQs here only
|
||||
*/
|
||||
if (status & IRQENB_FIFO0THRES) {
|
||||
|
||||
titsc_read_coordinates(ts_dev, &x, &y, &z1, &z2);
|
||||
|
@ -316,7 +322,7 @@ static irqreturn_t titsc_irq(int irq, void *dev)
|
|||
|
||||
if (irqclr) {
|
||||
titsc_writel(ts_dev, REG_IRQSTATUS, irqclr);
|
||||
am335x_tsc_se_update(ts_dev->mfd_tscadc);
|
||||
am335x_tsc_se_set(ts_dev->mfd_tscadc, ts_dev->step_mask);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
return IRQ_NONE;
|
||||
|
@ -389,7 +395,7 @@ static int titsc_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
err = request_irq(ts_dev->irq, titsc_irq,
|
||||
0, pdev->dev.driver->name, ts_dev);
|
||||
IRQF_SHARED, pdev->dev.driver->name, ts_dev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to allocate irq.\n");
|
||||
goto err_free_mem;
|
||||
|
|
|
@ -275,6 +275,12 @@ static int hid_time_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = sensor_hub_device_open(hsdev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to open sensor hub device!\n");
|
||||
goto err_open;
|
||||
}
|
||||
|
||||
time_state->rtc = devm_rtc_device_register(&pdev->dev,
|
||||
"hid-sensor-time", &hid_time_rtc_ops,
|
||||
THIS_MODULE);
|
||||
|
@ -282,17 +288,24 @@ static int hid_time_probe(struct platform_device *pdev)
|
|||
if (IS_ERR_OR_NULL(time_state->rtc)) {
|
||||
ret = time_state->rtc ? PTR_ERR(time_state->rtc) : -ENODEV;
|
||||
time_state->rtc = NULL;
|
||||
sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_TIME);
|
||||
dev_err(&pdev->dev, "rtc device register failed!\n");
|
||||
goto err_rtc;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
err_rtc:
|
||||
sensor_hub_device_close(hsdev);
|
||||
err_open:
|
||||
sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_TIME);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hid_time_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
||||
|
||||
sensor_hub_device_close(hsdev);
|
||||
sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_TIME);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -136,6 +136,8 @@ source "drivers/staging/goldfish/Kconfig"
|
|||
|
||||
source "drivers/staging/netlogic/Kconfig"
|
||||
|
||||
source "drivers/staging/mt29f_spinand/Kconfig"
|
||||
|
||||
source "drivers/staging/dwc2/Kconfig"
|
||||
|
||||
source "drivers/staging/lustre/Kconfig"
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue