First round of new drivers, features and cleanups for IIO in the 3.19 cycle.
New drivers / supported parts * rockchip - rk3066-tsadc variant * si7020 humidity and temperature sensor * mcp320x - add mcp3001, mcp3002, mcp3004, mcp3008, mcp3201, mcp3202 * bmp280 pressure and temperature sensor * Qualcomm SPMI PMIC current ADC driver * Exynos_adc - support exynos7 New features * vf610-adc - add temperature sensor support * Documentation of current attributes, scaled pressure, offset and scaled humidity, RGBC intensity gain factor and scale applied to differential voltage channels. * Bring iio_event_monitor up to date with newer modifiers. * Add of_xlate function to allow for complex channel mappings from the device tree. * Add -g parameter to generic_buffer example to allow for devices with directly fed (no trigger) buffers. * Move exynos driver over to syscon for PMU register access. Cleanups, fixes for new drivers * lis3l02dq drop an unneeded else. * st sensors - renam st_sensors to st_sensor_settings (for clarity) * st sensors - drop an unused parameter from all the probe utility functions. * vf610 better error handling and tidy up. * si7020 - cleanups following merge * as3935 - drop some unnecessary semicolons. * bmp280 - fix the pressure calculation. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABAgAGBQJUWnZBAAoJEFSFNJnE9BaIZAAQAJpkot0ZAYPwtIkmtV1JqrLQ EZRkBN+cIf3q5zp/TnQTqfyhKbpHbJ9/Pnb0zHvn7Yh3WpZod2tyoC3xb8JAlX2h 3tJRrA7plkdMjtwO/ryOxrVJYrm6rCeqKpciwMpDf4E/4fhU5CU++TpmWbhn04qB 7PEZJN5dkmdnQYSIyNle11MYN+NpCAB4zqDvRJoJHpqZV6zAqygJy03H1PqXFS/S iNBL3jS8/PTbeUURUFOQTiqnJw5KuDke7OoXxUfvCpUwQfNLD8mOTrVrEoId1cM0 Y2fmasfHteh83KP34/MOTwNA3EezM8iQOMzJnXtOpxpySPDrDSP68FDAJs15TUJm je8E1xxxdjOWuzgSd9djNm9qhlBkoYgbRwzc8wSAHMK7mCV7pP485WZk5E8rRX2z gMFgUmu4LkiZ2V9glGTAcHqnjhLNDTRCp1Nl/sMVgssFBBNjgR5+nZjUFqDR2QpD eo4ReIzev7Rzxe2lACRjnRrCnO+KKcjiDkyCdM3X2zdTddQRVrP5Uz+jgnvGAeMO hCYDHSOiMzV/r5emVfmNG9w8P9a6rZqu4KqcM/KjSzAfHFQTqmfr4Tkfn/hbeUjY h/zzB18EM4kUSxm3E6+CbFRWcfC7b/PLcUOwSitdujb9cYaX72gdesO2/P3jNFAK 2bjoLRr5l4M4n/DeHae4 =BDOo -----END PGP SIGNATURE----- Merge tag 'iio-for-3.19a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next Jonathan writes: First round of new drivers, features and cleanups for IIO in the 3.19 cycle. New drivers / supported parts * rockchip - rk3066-tsadc variant * si7020 humidity and temperature sensor * mcp320x - add mcp3001, mcp3002, mcp3004, mcp3008, mcp3201, mcp3202 * bmp280 pressure and temperature sensor * Qualcomm SPMI PMIC current ADC driver * Exynos_adc - support exynos7 New features * vf610-adc - add temperature sensor support * Documentation of current attributes, scaled pressure, offset and scaled humidity, RGBC intensity gain factor and scale applied to differential voltage channels. * Bring iio_event_monitor up to date with newer modifiers. * Add of_xlate function to allow for complex channel mappings from the device tree. * Add -g parameter to generic_buffer example to allow for devices with directly fed (no trigger) buffers. * Move exynos driver over to syscon for PMU register access. Cleanups, fixes for new drivers * lis3l02dq drop an unneeded else. * st sensors - renam st_sensors to st_sensor_settings (for clarity) * st sensors - drop an unused parameter from all the probe utility functions. * vf610 better error handling and tidy up. * si7020 - cleanups following merge * as3935 - drop some unnecessary semicolons. * bmp280 - fix the pressure calculation.
This commit is contained in:
commit
be61a0d784
@ -200,6 +200,13 @@ Description:
|
||||
Raw pressure measurement from channel Y. Units after
|
||||
application of scale and offset are kilopascal.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_input
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_pressure_input
|
||||
KernelVersion: 3.8
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Scaled pressure measurement from channel Y, in kilopascal.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_humidityrelative_raw
|
||||
KernelVersion: 3.14
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
@ -231,6 +238,7 @@ What: /sys/bus/iio/devices/iio:deviceX/in_tempY_offset
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_temp_offset
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_offset
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_pressure_offset
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_humidityrelative_offset
|
||||
KernelVersion: 2.6.35
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
@ -251,6 +259,7 @@ Description:
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_supply_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltage_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltage-voltage_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_accel_scale
|
||||
@ -266,6 +275,7 @@ What: /sys/bus/iio/devices/iio:deviceX/in_rot_from_north_magnetic_tilt_comp_sca
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_rot_from_north_true_tilt_comp_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_pressure_scale
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_humidityrelative_scale
|
||||
KernelVersion: 2.6.35
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
@ -328,6 +338,10 @@ Description:
|
||||
are listed in this attribute.
|
||||
|
||||
What /sys/bus/iio/devices/iio:deviceX/out_voltageY_hardwaregain
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_intensity_red_hardwaregain
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_intensity_green_hardwaregain
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_intensity_blue_hardwaregain
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_intensity_clear_hardwaregain
|
||||
KernelVersion: 2.6.35
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
@ -1028,3 +1042,12 @@ Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Raw value of rotation from true/magnetic north measured with
|
||||
or without compensation from tilt sensors.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_currentX_raw
|
||||
KernelVersion: 3.18
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Raw current measurement from channel X. Units are in milliamps
|
||||
after application of scale and offset. If no offset or scale is
|
||||
present, output should be considered as processed with the
|
||||
unit in milliamps.
|
||||
|
@ -16,6 +16,8 @@ Required properties:
|
||||
future controllers.
|
||||
Must be "samsung,exynos3250-adc" for
|
||||
controllers compatible with ADC of Exynos3250.
|
||||
Must be "samsung,exynos7-adc" for
|
||||
the ADC in Exynos7 and compatibles
|
||||
Must be "samsung,s3c2410-adc" for
|
||||
the ADC in s3c2410 and compatibles
|
||||
Must be "samsung,s3c2416-adc" for
|
||||
@ -43,13 +45,16 @@ Required properties:
|
||||
compatible ADC block)
|
||||
- vdd-supply VDD input supply.
|
||||
|
||||
- samsung,syscon-phandle Contains the PMU system controller node
|
||||
(To access the ADC_PHY register on Exynos5250/5420/5800/3250)
|
||||
|
||||
Note: child nodes can be added for auto probing from device tree.
|
||||
|
||||
Example: adding device info in dtsi file
|
||||
|
||||
adc: adc@12D10000 {
|
||||
compatible = "samsung,exynos-adc-v1";
|
||||
reg = <0x12D10000 0x100>, <0x10040718 0x4>;
|
||||
reg = <0x12D10000 0x100>;
|
||||
interrupts = <0 106 0>;
|
||||
#io-channel-cells = <1>;
|
||||
io-channel-ranges;
|
||||
@ -58,13 +63,14 @@ adc: adc@12D10000 {
|
||||
clock-names = "adc";
|
||||
|
||||
vdd-supply = <&buck5_reg>;
|
||||
samsung,syscon-phandle = <&pmu_system_controller>;
|
||||
};
|
||||
|
||||
Example: adding device info in dtsi file for Exynos3250 with additional sclk
|
||||
|
||||
adc: adc@126C0000 {
|
||||
compatible = "samsung,exynos3250-adc", "samsung,exynos-adc-v2;
|
||||
reg = <0x126C0000 0x100>, <0x10020718 0x4>;
|
||||
reg = <0x126C0000 0x100>;
|
||||
interrupts = <0 137 0>;
|
||||
#io-channel-cells = <1>;
|
||||
io-channel-ranges;
|
||||
@ -73,6 +79,7 @@ adc: adc@126C0000 {
|
||||
clock-names = "adc", "sclk";
|
||||
|
||||
vdd-supply = <&buck5_reg>;
|
||||
samsung,syscon-phandle = <&pmu_system_controller>;
|
||||
};
|
||||
|
||||
Example: Adding child nodes in dts file
|
||||
|
46
Documentation/devicetree/bindings/iio/adc/qcom,spmi-iadc.txt
Normal file
46
Documentation/devicetree/bindings/iio/adc/qcom,spmi-iadc.txt
Normal file
@ -0,0 +1,46 @@
|
||||
Qualcomm's SPMI PMIC current ADC
|
||||
|
||||
QPNP PMIC current ADC (IADC) provides interface to clients to read current.
|
||||
A 16 bit ADC is used for current measurements. IADC can measure the current
|
||||
through an external resistor (channel 1) or internal (built-in) resistor
|
||||
(channel 0). When using an external resistor it is to be described by
|
||||
qcom,external-resistor-micro-ohms property.
|
||||
|
||||
IADC node:
|
||||
|
||||
- compatible:
|
||||
Usage: required
|
||||
Value type: <string>
|
||||
Definition: Should contain "qcom,spmi-iadc".
|
||||
|
||||
- reg:
|
||||
Usage: required
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: IADC base address and length in the SPMI PMIC register map
|
||||
|
||||
- interrupts:
|
||||
Usage: optional
|
||||
Value type: <prop-encoded-array>
|
||||
Definition: End of ADC conversion.
|
||||
|
||||
- qcom,external-resistor-micro-ohms:
|
||||
Usage: optional
|
||||
Value type: <u32>
|
||||
Definition: Sense resister value in micro Ohm.
|
||||
If not defined value of 10000 micro Ohms will be used.
|
||||
|
||||
Example:
|
||||
/* IADC node */
|
||||
pmic_iadc: iadc@3600 {
|
||||
compatible = "qcom,spmi-iadc";
|
||||
reg = <0x3600 0x100>;
|
||||
interrupts = <0x0 0x36 0x0 IRQ_TYPE_EDGE_RISING>;
|
||||
qcom,external-resistor-micro-ohms = <10000>;
|
||||
#io-channel-cells = <1>;
|
||||
};
|
||||
|
||||
/* IIO client node */
|
||||
bat {
|
||||
io-channels = <&pmic_iadc 0>;
|
||||
io-channel-names = "iadc";
|
||||
};
|
@ -1,7 +1,7 @@
|
||||
Rockchip Successive Approximation Register (SAR) A/D Converter bindings
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "rockchip,saradc"
|
||||
- compatible: Should be "rockchip,saradc" or "rockchip,rk3066-tsadc"
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
- interrupts: The interrupt number to the cpu. The interrupt specifier format
|
||||
|
@ -311,12 +311,13 @@
|
||||
adc: adc@126C0000 {
|
||||
compatible = "samsung,exynos3250-adc",
|
||||
"samsung,exynos-adc-v2";
|
||||
reg = <0x126C0000 0x100>, <0x10020718 0x4>;
|
||||
reg = <0x126C0000 0x100>;
|
||||
interrupts = <0 137 0>;
|
||||
clock-names = "adc", "sclk";
|
||||
clocks = <&cmu CLK_TSADC>, <&cmu CLK_SCLK_TSADC>;
|
||||
#io-channel-cells = <1>;
|
||||
io-channel-ranges;
|
||||
samsung,syscon-phandle = <&pmu_system_controller>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
@ -108,13 +108,14 @@
|
||||
|
||||
adc: adc@126C0000 {
|
||||
compatible = "samsung,exynos-adc-v1";
|
||||
reg = <0x126C0000 0x100>, <0x10020718 0x4>;
|
||||
reg = <0x126C0000 0x100>;
|
||||
interrupt-parent = <&combiner>;
|
||||
interrupts = <10 3>;
|
||||
clocks = <&clock CLK_TSADC>;
|
||||
clock-names = "adc";
|
||||
#io-channel-cells = <1>;
|
||||
io-channel-ranges;
|
||||
samsung,syscon-phandle = <&pmu_system_controller>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
@ -750,12 +750,13 @@
|
||||
|
||||
adc: adc@12D10000 {
|
||||
compatible = "samsung,exynos-adc-v1";
|
||||
reg = <0x12D10000 0x100>, <0x10040718 0x4>;
|
||||
reg = <0x12D10000 0x100>;
|
||||
interrupts = <0 106 0>;
|
||||
clocks = <&clock CLK_ADC>;
|
||||
clock-names = "adc";
|
||||
#io-channel-cells = <1>;
|
||||
io-channel-ranges;
|
||||
samsung,syscon-phandle = <&pmu_system_controller>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
@ -541,12 +541,13 @@
|
||||
|
||||
adc: adc@12D10000 {
|
||||
compatible = "samsung,exynos-adc-v2";
|
||||
reg = <0x12D10000 0x100>, <0x10040720 0x4>;
|
||||
reg = <0x12D10000 0x100>;
|
||||
interrupts = <0 106 0>;
|
||||
clocks = <&clock CLK_TSADC>;
|
||||
clock-names = "adc";
|
||||
#io-channel-cells = <1>;
|
||||
io-channel-ranges;
|
||||
samsung,syscon-phandle = <&pmu_system_controller>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
|
@ -33,8 +33,7 @@ static const struct st_sensors_platform_data default_accel_pdata = {
|
||||
.drdy_int_pin = 1,
|
||||
};
|
||||
|
||||
int st_accel_common_probe(struct iio_dev *indio_dev,
|
||||
struct st_sensors_platform_data *pdata);
|
||||
int st_accel_common_probe(struct iio_dev *indio_dev);
|
||||
void st_accel_common_remove(struct iio_dev *indio_dev);
|
||||
|
||||
#ifdef CONFIG_IIO_BUFFER
|
||||
|
@ -161,7 +161,7 @@ static const struct iio_chan_spec st_accel_16bit_channels[] = {
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3)
|
||||
};
|
||||
|
||||
static const struct st_sensors st_accel_sensors[] = {
|
||||
static const struct st_sensor_settings st_accel_sensors_settings[] = {
|
||||
{
|
||||
.wai = ST_ACCEL_1_WAI_EXP,
|
||||
.sensors_supported = {
|
||||
@ -457,8 +457,7 @@ static const struct iio_trigger_ops st_accel_trigger_ops = {
|
||||
#define ST_ACCEL_TRIGGER_OPS NULL
|
||||
#endif
|
||||
|
||||
int st_accel_common_probe(struct iio_dev *indio_dev,
|
||||
struct st_sensors_platform_data *plat_data)
|
||||
int st_accel_common_probe(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *adata = iio_priv(indio_dev);
|
||||
int irq = adata->get_irq_data_ready(indio_dev);
|
||||
@ -470,24 +469,25 @@ int st_accel_common_probe(struct iio_dev *indio_dev,
|
||||
st_sensors_power_enable(indio_dev);
|
||||
|
||||
err = st_sensors_check_device_support(indio_dev,
|
||||
ARRAY_SIZE(st_accel_sensors), st_accel_sensors);
|
||||
ARRAY_SIZE(st_accel_sensors_settings),
|
||||
st_accel_sensors_settings);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
adata->num_data_channels = ST_ACCEL_NUMBER_DATA_CHANNELS;
|
||||
adata->multiread_bit = adata->sensor->multi_read_bit;
|
||||
indio_dev->channels = adata->sensor->ch;
|
||||
adata->multiread_bit = adata->sensor_settings->multi_read_bit;
|
||||
indio_dev->channels = adata->sensor_settings->ch;
|
||||
indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS;
|
||||
|
||||
adata->current_fullscale = (struct st_sensor_fullscale_avl *)
|
||||
&adata->sensor->fs.fs_avl[0];
|
||||
adata->odr = adata->sensor->odr.odr_avl[0].hz;
|
||||
&adata->sensor_settings->fs.fs_avl[0];
|
||||
adata->odr = adata->sensor_settings->odr.odr_avl[0].hz;
|
||||
|
||||
if (!plat_data)
|
||||
plat_data =
|
||||
if (!adata->dev->platform_data)
|
||||
adata->dev->platform_data =
|
||||
(struct st_sensors_platform_data *)&default_accel_pdata;
|
||||
|
||||
err = st_sensors_init_sensor(indio_dev, plat_data);
|
||||
err = st_sensors_init_sensor(indio_dev, adata->dev->platform_data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -79,12 +79,11 @@ static int st_accel_i2c_probe(struct i2c_client *client,
|
||||
return -ENOMEM;
|
||||
|
||||
adata = iio_priv(indio_dev);
|
||||
adata->dev = &client->dev;
|
||||
st_sensors_of_i2c_probe(client, st_accel_of_match);
|
||||
|
||||
st_sensors_i2c_configure(indio_dev, client, adata);
|
||||
|
||||
err = st_accel_common_probe(indio_dev, client->dev.platform_data);
|
||||
err = st_accel_common_probe(indio_dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -29,11 +29,10 @@ static int st_accel_spi_probe(struct spi_device *spi)
|
||||
return -ENOMEM;
|
||||
|
||||
adata = iio_priv(indio_dev);
|
||||
adata->dev = &spi->dev;
|
||||
|
||||
st_sensors_spi_configure(indio_dev, spi, adata);
|
||||
|
||||
err = st_accel_common_probe(indio_dev, spi->dev.platform_data);
|
||||
err = st_accel_common_probe(indio_dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -206,6 +206,20 @@ config NAU7802
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called nau7802.
|
||||
|
||||
config QCOM_SPMI_IADC
|
||||
tristate "Qualcomm SPMI PMIC current ADC"
|
||||
depends on SPMI
|
||||
select REGMAP_SPMI
|
||||
help
|
||||
This is the IIO Current ADC driver for Qualcomm QPNP IADC Chip.
|
||||
|
||||
The driver supports single mode operation to read from one of two
|
||||
channels (external or internal). Hardware have additional
|
||||
channels internally used for gain and offset calibration.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called qcom-spmi-iadc.
|
||||
|
||||
config ROCKCHIP_SARADC
|
||||
tristate "Rockchip SARADC driver"
|
||||
depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST)
|
||||
|
@ -22,6 +22,7 @@ obj-$(CONFIG_MCP320X) += mcp320x.o
|
||||
obj-$(CONFIG_MCP3422) += mcp3422.o
|
||||
obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
|
||||
obj-$(CONFIG_NAU7802) += nau7802.o
|
||||
obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
|
||||
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
|
||||
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
|
||||
obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
|
||||
|
@ -39,6 +39,8 @@
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/machine.h>
|
||||
#include <linux/iio/driver.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/* S3C/EXYNOS4412/5250 ADC_V1 registers definitions */
|
||||
#define ADC_V1_CON(x) ((x) + 0x00)
|
||||
@ -90,11 +92,14 @@
|
||||
|
||||
#define EXYNOS_ADC_TIMEOUT (msecs_to_jiffies(100))
|
||||
|
||||
#define EXYNOS_ADCV1_PHY_OFFSET 0x0718
|
||||
#define EXYNOS_ADCV2_PHY_OFFSET 0x0720
|
||||
|
||||
struct exynos_adc {
|
||||
struct exynos_adc_data *data;
|
||||
struct device *dev;
|
||||
void __iomem *regs;
|
||||
void __iomem *enable_reg;
|
||||
struct regmap *pmu_map;
|
||||
struct clk *clk;
|
||||
struct clk *sclk;
|
||||
unsigned int irq;
|
||||
@ -110,6 +115,7 @@ struct exynos_adc_data {
|
||||
int num_channels;
|
||||
bool needs_sclk;
|
||||
bool needs_adc_phy;
|
||||
int phy_offset;
|
||||
u32 mask;
|
||||
|
||||
void (*init_hw)(struct exynos_adc *info);
|
||||
@ -183,7 +189,7 @@ static void exynos_adc_v1_init_hw(struct exynos_adc *info)
|
||||
u32 con1;
|
||||
|
||||
if (info->data->needs_adc_phy)
|
||||
writel(1, info->enable_reg);
|
||||
regmap_write(info->pmu_map, info->data->phy_offset, 1);
|
||||
|
||||
/* set default prescaler values and Enable prescaler */
|
||||
con1 = ADC_V1_CON_PRSCLV(49) | ADC_V1_CON_PRSCEN;
|
||||
@ -198,7 +204,7 @@ static void exynos_adc_v1_exit_hw(struct exynos_adc *info)
|
||||
u32 con;
|
||||
|
||||
if (info->data->needs_adc_phy)
|
||||
writel(0, info->enable_reg);
|
||||
regmap_write(info->pmu_map, info->data->phy_offset, 0);
|
||||
|
||||
con = readl(ADC_V1_CON(info->regs));
|
||||
con |= ADC_V1_CON_STANDBY;
|
||||
@ -225,6 +231,7 @@ static const struct exynos_adc_data exynos_adc_v1_data = {
|
||||
.num_channels = MAX_ADC_V1_CHANNELS,
|
||||
.mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
|
||||
.needs_adc_phy = true,
|
||||
.phy_offset = EXYNOS_ADCV1_PHY_OFFSET,
|
||||
|
||||
.init_hw = exynos_adc_v1_init_hw,
|
||||
.exit_hw = exynos_adc_v1_exit_hw,
|
||||
@ -314,7 +321,7 @@ static void exynos_adc_v2_init_hw(struct exynos_adc *info)
|
||||
u32 con1, con2;
|
||||
|
||||
if (info->data->needs_adc_phy)
|
||||
writel(1, info->enable_reg);
|
||||
regmap_write(info->pmu_map, info->data->phy_offset, 1);
|
||||
|
||||
con1 = ADC_V2_CON1_SOFT_RESET;
|
||||
writel(con1, ADC_V2_CON1(info->regs));
|
||||
@ -332,7 +339,7 @@ static void exynos_adc_v2_exit_hw(struct exynos_adc *info)
|
||||
u32 con;
|
||||
|
||||
if (info->data->needs_adc_phy)
|
||||
writel(0, info->enable_reg);
|
||||
regmap_write(info->pmu_map, info->data->phy_offset, 0);
|
||||
|
||||
con = readl(ADC_V2_CON1(info->regs));
|
||||
con &= ~ADC_CON_EN_START;
|
||||
@ -362,6 +369,7 @@ static const struct exynos_adc_data exynos_adc_v2_data = {
|
||||
.num_channels = MAX_ADC_V2_CHANNELS,
|
||||
.mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
|
||||
.needs_adc_phy = true,
|
||||
.phy_offset = EXYNOS_ADCV2_PHY_OFFSET,
|
||||
|
||||
.init_hw = exynos_adc_v2_init_hw,
|
||||
.exit_hw = exynos_adc_v2_exit_hw,
|
||||
@ -374,6 +382,7 @@ static const struct exynos_adc_data exynos3250_adc_data = {
|
||||
.mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
|
||||
.needs_sclk = true,
|
||||
.needs_adc_phy = true,
|
||||
.phy_offset = EXYNOS_ADCV1_PHY_OFFSET,
|
||||
|
||||
.init_hw = exynos_adc_v2_init_hw,
|
||||
.exit_hw = exynos_adc_v2_exit_hw,
|
||||
@ -381,6 +390,35 @@ static const struct exynos_adc_data exynos3250_adc_data = {
|
||||
.start_conv = exynos_adc_v2_start_conv,
|
||||
};
|
||||
|
||||
static void exynos_adc_exynos7_init_hw(struct exynos_adc *info)
|
||||
{
|
||||
u32 con1, con2;
|
||||
|
||||
if (info->data->needs_adc_phy)
|
||||
regmap_write(info->pmu_map, info->data->phy_offset, 1);
|
||||
|
||||
con1 = ADC_V2_CON1_SOFT_RESET;
|
||||
writel(con1, ADC_V2_CON1(info->regs));
|
||||
|
||||
con2 = readl(ADC_V2_CON2(info->regs));
|
||||
con2 &= ~ADC_V2_CON2_C_TIME(7);
|
||||
con2 |= ADC_V2_CON2_C_TIME(0);
|
||||
writel(con2, ADC_V2_CON2(info->regs));
|
||||
|
||||
/* Enable interrupts */
|
||||
writel(1, ADC_V2_INT_EN(info->regs));
|
||||
}
|
||||
|
||||
static const struct exynos_adc_data exynos7_adc_data = {
|
||||
.num_channels = MAX_ADC_V1_CHANNELS,
|
||||
.mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
|
||||
|
||||
.init_hw = exynos_adc_exynos7_init_hw,
|
||||
.exit_hw = exynos_adc_v2_exit_hw,
|
||||
.clear_irq = exynos_adc_v2_clear_irq,
|
||||
.start_conv = exynos_adc_v2_start_conv,
|
||||
};
|
||||
|
||||
static const struct of_device_id exynos_adc_match[] = {
|
||||
{
|
||||
.compatible = "samsung,s3c2410-adc",
|
||||
@ -406,6 +444,9 @@ static const struct of_device_id exynos_adc_match[] = {
|
||||
}, {
|
||||
.compatible = "samsung,exynos3250-adc",
|
||||
.data = &exynos3250_adc_data,
|
||||
}, {
|
||||
.compatible = "samsung,exynos7-adc",
|
||||
.data = &exynos7_adc_data,
|
||||
},
|
||||
{},
|
||||
};
|
||||
@ -558,10 +599,13 @@ static int exynos_adc_probe(struct platform_device *pdev)
|
||||
|
||||
|
||||
if (info->data->needs_adc_phy) {
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
info->enable_reg = devm_ioremap_resource(&pdev->dev, mem);
|
||||
if (IS_ERR(info->enable_reg))
|
||||
return PTR_ERR(info->enable_reg);
|
||||
info->pmu_map = syscon_regmap_lookup_by_phandle(
|
||||
pdev->dev.of_node,
|
||||
"samsung,syscon-phandle");
|
||||
if (IS_ERR(info->pmu_map)) {
|
||||
dev_err(&pdev->dev, "syscon regmap lookup failed.\n");
|
||||
return PTR_ERR(info->pmu_map);
|
||||
}
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
|
@ -1,9 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Oskar Andero <oskar.andero@gmail.com>
|
||||
* Copyright (C) 2014 Rose Technology
|
||||
* Allan Bendorff Jensen <abj@rosetechnology.dk>
|
||||
* Soren Andersen <san@rosetechnology.dk>
|
||||
*
|
||||
* Driver for following ADC chips from Microchip Technology's:
|
||||
* 10 Bit converter
|
||||
* MCP3001
|
||||
* MCP3002
|
||||
* MCP3004
|
||||
* MCP3008
|
||||
* ------------
|
||||
* 12 bit converter
|
||||
* MCP3201
|
||||
* MCP3202
|
||||
* MCP3204
|
||||
* MCP3208
|
||||
* ------------
|
||||
*
|
||||
* Driver for Microchip Technology's MCP3204 and MCP3208 ADC chips.
|
||||
* Datasheet can be found here:
|
||||
* http://ww1.microchip.com/downloads/en/devicedoc/21298c.pdf
|
||||
* http://ww1.microchip.com/downloads/en/DeviceDoc/21293C.pdf mcp3001
|
||||
* http://ww1.microchip.com/downloads/en/DeviceDoc/21294E.pdf mcp3002
|
||||
* http://ww1.microchip.com/downloads/en/DeviceDoc/21295d.pdf mcp3004/08
|
||||
* http://ww1.microchip.com/downloads/en/DeviceDoc/21290D.pdf mcp3201
|
||||
* http://ww1.microchip.com/downloads/en/DeviceDoc/21034D.pdf mcp3202
|
||||
* http://ww1.microchip.com/downloads/en/DeviceDoc/21298c.pdf mcp3204/08
|
||||
*
|
||||
* 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
|
||||
@ -11,19 +32,29 @@
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#define MCP_SINGLE_ENDED (1 << 3)
|
||||
#define MCP_START_BIT (1 << 4)
|
||||
|
||||
enum {
|
||||
mcp3001,
|
||||
mcp3002,
|
||||
mcp3004,
|
||||
mcp3008,
|
||||
mcp3201,
|
||||
mcp3202,
|
||||
mcp3204,
|
||||
mcp3208,
|
||||
};
|
||||
|
||||
struct mcp320x_chip_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int num_channels;
|
||||
unsigned int resolution;
|
||||
};
|
||||
|
||||
struct mcp320x {
|
||||
struct spi_device *spi;
|
||||
struct spi_message msg;
|
||||
@ -34,19 +65,69 @@ struct mcp320x {
|
||||
|
||||
struct regulator *reg;
|
||||
struct mutex lock;
|
||||
const struct mcp320x_chip_info *chip_info;
|
||||
};
|
||||
|
||||
static int mcp320x_adc_conversion(struct mcp320x *adc, u8 msg)
|
||||
static int mcp320x_channel_to_tx_data(int device_index,
|
||||
const unsigned int channel, bool differential)
|
||||
{
|
||||
int start_bit = 1;
|
||||
|
||||
switch (device_index) {
|
||||
case mcp3001:
|
||||
case mcp3201:
|
||||
return 0;
|
||||
case mcp3002:
|
||||
case mcp3202:
|
||||
return ((start_bit << 4) | (!differential << 3) |
|
||||
(channel << 2));
|
||||
case mcp3004:
|
||||
case mcp3204:
|
||||
case mcp3008:
|
||||
case mcp3208:
|
||||
return ((start_bit << 6) | (!differential << 5) |
|
||||
(channel << 2));
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int mcp320x_adc_conversion(struct mcp320x *adc, u8 channel,
|
||||
bool differential, int device_index)
|
||||
{
|
||||
int ret;
|
||||
|
||||
adc->tx_buf = msg;
|
||||
adc->rx_buf[0] = 0;
|
||||
adc->rx_buf[1] = 0;
|
||||
adc->tx_buf = mcp320x_channel_to_tx_data(device_index,
|
||||
channel, differential);
|
||||
|
||||
if (device_index != mcp3001 && device_index != mcp3201) {
|
||||
ret = spi_sync(adc->spi, &adc->msg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
ret = spi_read(adc->spi, &adc->rx_buf, sizeof(adc->rx_buf));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ((adc->rx_buf[0] & 0x3f) << 6) |
|
||||
(adc->rx_buf[1] >> 2);
|
||||
switch (device_index) {
|
||||
case mcp3001:
|
||||
return (adc->rx_buf[0] << 5 | adc->rx_buf[1] >> 3);
|
||||
case mcp3002:
|
||||
case mcp3004:
|
||||
case mcp3008:
|
||||
return (adc->rx_buf[0] << 2 | adc->rx_buf[1] >> 6);
|
||||
case mcp3201:
|
||||
return (adc->rx_buf[0] << 7 | adc->rx_buf[1] >> 1);
|
||||
case mcp3202:
|
||||
case mcp3204:
|
||||
case mcp3208:
|
||||
return (adc->rx_buf[0] << 4 | adc->rx_buf[1] >> 4);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int mcp320x_read_raw(struct iio_dev *indio_dev,
|
||||
@ -55,18 +136,17 @@ static int mcp320x_read_raw(struct iio_dev *indio_dev,
|
||||
{
|
||||
struct mcp320x *adc = iio_priv(indio_dev);
|
||||
int ret = -EINVAL;
|
||||
int device_index = 0;
|
||||
|
||||
mutex_lock(&adc->lock);
|
||||
|
||||
device_index = spi_get_device_id(adc->spi)->driver_data;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (channel->differential)
|
||||
ret = mcp320x_adc_conversion(adc,
|
||||
MCP_START_BIT | channel->address);
|
||||
else
|
||||
ret = mcp320x_adc_conversion(adc,
|
||||
MCP_START_BIT | MCP_SINGLE_ENDED |
|
||||
channel->address);
|
||||
ret = mcp320x_adc_conversion(adc, channel->address,
|
||||
channel->differential, device_index);
|
||||
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
@ -75,18 +155,15 @@ static int mcp320x_read_raw(struct iio_dev *indio_dev,
|
||||
break;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
/* Digital output code = (4096 * Vin) / Vref */
|
||||
ret = regulator_get_voltage(adc->reg);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
/* convert regulator output voltage to mV */
|
||||
*val = ret / 1000;
|
||||
*val2 = 12;
|
||||
*val2 = adc->chip_info->resolution;
|
||||
ret = IIO_VAL_FRACTIONAL_LOG2;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
@ -117,6 +194,16 @@ out:
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec mcp3201_channels[] = {
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(0),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec mcp3202_channels[] = {
|
||||
MCP320X_VOLTAGE_CHANNEL(0),
|
||||
MCP320X_VOLTAGE_CHANNEL(1),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(0),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec mcp3204_channels[] = {
|
||||
MCP320X_VOLTAGE_CHANNEL(0),
|
||||
MCP320X_VOLTAGE_CHANNEL(1),
|
||||
@ -146,19 +233,46 @@ static const struct iio_info mcp320x_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
struct mcp3208_chip_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int num_channels;
|
||||
};
|
||||
|
||||
static const struct mcp3208_chip_info mcp3208_chip_infos[] = {
|
||||
static const struct mcp320x_chip_info mcp320x_chip_infos[] = {
|
||||
[mcp3001] = {
|
||||
.channels = mcp3201_channels,
|
||||
.num_channels = ARRAY_SIZE(mcp3201_channels),
|
||||
.resolution = 10
|
||||
},
|
||||
[mcp3002] = {
|
||||
.channels = mcp3202_channels,
|
||||
.num_channels = ARRAY_SIZE(mcp3202_channels),
|
||||
.resolution = 10
|
||||
},
|
||||
[mcp3004] = {
|
||||
.channels = mcp3204_channels,
|
||||
.num_channels = ARRAY_SIZE(mcp3204_channels),
|
||||
.resolution = 10
|
||||
},
|
||||
[mcp3008] = {
|
||||
.channels = mcp3208_channels,
|
||||
.num_channels = ARRAY_SIZE(mcp3208_channels),
|
||||
.resolution = 10
|
||||
},
|
||||
[mcp3201] = {
|
||||
.channels = mcp3201_channels,
|
||||
.num_channels = ARRAY_SIZE(mcp3201_channels),
|
||||
.resolution = 12
|
||||
},
|
||||
[mcp3202] = {
|
||||
.channels = mcp3202_channels,
|
||||
.num_channels = ARRAY_SIZE(mcp3202_channels),
|
||||
.resolution = 12
|
||||
},
|
||||
[mcp3204] = {
|
||||
.channels = mcp3204_channels,
|
||||
.num_channels = ARRAY_SIZE(mcp3204_channels)
|
||||
.num_channels = ARRAY_SIZE(mcp3204_channels),
|
||||
.resolution = 12
|
||||
},
|
||||
[mcp3208] = {
|
||||
.channels = mcp3208_channels,
|
||||
.num_channels = ARRAY_SIZE(mcp3208_channels)
|
||||
.num_channels = ARRAY_SIZE(mcp3208_channels),
|
||||
.resolution = 12
|
||||
},
|
||||
};
|
||||
|
||||
@ -166,7 +280,7 @@ static int mcp320x_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct mcp320x *adc;
|
||||
const struct mcp3208_chip_info *chip_info;
|
||||
const struct mcp320x_chip_info *chip_info;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
|
||||
@ -181,7 +295,7 @@ static int mcp320x_probe(struct spi_device *spi)
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &mcp320x_info;
|
||||
|
||||
chip_info = &mcp3208_chip_infos[spi_get_device_id(spi)->driver_data];
|
||||
chip_info = &mcp320x_chip_infos[spi_get_device_id(spi)->driver_data];
|
||||
indio_dev->channels = chip_info->channels;
|
||||
indio_dev->num_channels = chip_info->num_channels;
|
||||
|
||||
@ -226,7 +340,45 @@ static int mcp320x_remove(struct spi_device *spi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
static const struct of_device_id mcp320x_dt_ids[] = {
|
||||
{
|
||||
.compatible = "mcp3001",
|
||||
.data = &mcp320x_chip_infos[mcp3001],
|
||||
}, {
|
||||
.compatible = "mcp3002",
|
||||
.data = &mcp320x_chip_infos[mcp3002],
|
||||
}, {
|
||||
.compatible = "mcp3004",
|
||||
.data = &mcp320x_chip_infos[mcp3004],
|
||||
}, {
|
||||
.compatible = "mcp3008",
|
||||
.data = &mcp320x_chip_infos[mcp3008],
|
||||
}, {
|
||||
.compatible = "mcp3201",
|
||||
.data = &mcp320x_chip_infos[mcp3201],
|
||||
}, {
|
||||
.compatible = "mcp3202",
|
||||
.data = &mcp320x_chip_infos[mcp3202],
|
||||
}, {
|
||||
.compatible = "mcp3204",
|
||||
.data = &mcp320x_chip_infos[mcp3204],
|
||||
}, {
|
||||
.compatible = "mcp3208",
|
||||
.data = &mcp320x_chip_infos[mcp3208],
|
||||
}, {
|
||||
}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mcp320x_dt_ids);
|
||||
#endif
|
||||
|
||||
static const struct spi_device_id mcp320x_id[] = {
|
||||
{ "mcp3001", mcp3001 },
|
||||
{ "mcp3002", mcp3002 },
|
||||
{ "mcp3004", mcp3004 },
|
||||
{ "mcp3008", mcp3008 },
|
||||
{ "mcp3201", mcp3201 },
|
||||
{ "mcp3202", mcp3202 },
|
||||
{ "mcp3204", mcp3204 },
|
||||
{ "mcp3208", mcp3208 },
|
||||
{ }
|
||||
@ -245,5 +397,5 @@ static struct spi_driver mcp320x_driver = {
|
||||
module_spi_driver(mcp320x_driver);
|
||||
|
||||
MODULE_AUTHOR("Oskar Andero <oskar.andero@gmail.com>");
|
||||
MODULE_DESCRIPTION("Microchip Technology MCP3204/08");
|
||||
MODULE_DESCRIPTION("Microchip Technology MCP3x01/02/04/08");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
595
drivers/iio/adc/qcom-spmi-iadc.c
Normal file
595
drivers/iio/adc/qcom-spmi-iadc.c
Normal file
@ -0,0 +1,595 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/* IADC register and bit definition */
|
||||
#define IADC_REVISION2 0x1
|
||||
#define IADC_REVISION2_SUPPORTED_IADC 1
|
||||
|
||||
#define IADC_PERPH_TYPE 0x4
|
||||
#define IADC_PERPH_TYPE_ADC 8
|
||||
|
||||
#define IADC_PERPH_SUBTYPE 0x5
|
||||
#define IADC_PERPH_SUBTYPE_IADC 3
|
||||
|
||||
#define IADC_STATUS1 0x8
|
||||
#define IADC_STATUS1_OP_MODE 4
|
||||
#define IADC_STATUS1_REQ_STS BIT(1)
|
||||
#define IADC_STATUS1_EOC BIT(0)
|
||||
#define IADC_STATUS1_REQ_STS_EOC_MASK 0x3
|
||||
|
||||
#define IADC_MODE_CTL 0x40
|
||||
#define IADC_OP_MODE_SHIFT 3
|
||||
#define IADC_OP_MODE_NORMAL 0
|
||||
#define IADC_TRIM_EN BIT(0)
|
||||
|
||||
#define IADC_EN_CTL1 0x46
|
||||
#define IADC_EN_CTL1_SET BIT(7)
|
||||
|
||||
#define IADC_CH_SEL_CTL 0x48
|
||||
|
||||
#define IADC_DIG_PARAM 0x50
|
||||
#define IADC_DIG_DEC_RATIO_SEL_SHIFT 2
|
||||
|
||||
#define IADC_HW_SETTLE_DELAY 0x51
|
||||
|
||||
#define IADC_CONV_REQ 0x52
|
||||
#define IADC_CONV_REQ_SET BIT(7)
|
||||
|
||||
#define IADC_FAST_AVG_CTL 0x5a
|
||||
#define IADC_FAST_AVG_EN 0x5b
|
||||
#define IADC_FAST_AVG_EN_SET BIT(7)
|
||||
|
||||
#define IADC_PERH_RESET_CTL3 0xda
|
||||
#define IADC_FOLLOW_WARM_RB BIT(2)
|
||||
|
||||
#define IADC_DATA 0x60 /* 16 bits */
|
||||
|
||||
#define IADC_SEC_ACCESS 0xd0
|
||||
#define IADC_SEC_ACCESS_DATA 0xa5
|
||||
|
||||
#define IADC_NOMINAL_RSENSE 0xf4
|
||||
#define IADC_NOMINAL_RSENSE_SIGN_MASK BIT(7)
|
||||
|
||||
#define IADC_REF_GAIN_MICRO_VOLTS 17857
|
||||
|
||||
#define IADC_INT_RSENSE_DEVIATION 15625 /* nano Ohms per bit */
|
||||
|
||||
#define IADC_INT_RSENSE_IDEAL_VALUE 10000 /* micro Ohms */
|
||||
#define IADC_INT_RSENSE_DEFAULT_VALUE 7800 /* micro Ohms */
|
||||
#define IADC_INT_RSENSE_DEFAULT_GF 9000 /* micro Ohms */
|
||||
#define IADC_INT_RSENSE_DEFAULT_SMIC 9700 /* micro Ohms */
|
||||
|
||||
#define IADC_CONV_TIME_MIN_US 2000
|
||||
#define IADC_CONV_TIME_MAX_US 2100
|
||||
|
||||
#define IADC_DEF_PRESCALING 0 /* 1:1 */
|
||||
#define IADC_DEF_DECIMATION 0 /* 512 */
|
||||
#define IADC_DEF_HW_SETTLE_TIME 0 /* 0 us */
|
||||
#define IADC_DEF_AVG_SAMPLES 0 /* 1 sample */
|
||||
|
||||
/* IADC channel list */
|
||||
#define IADC_INT_RSENSE 0
|
||||
#define IADC_EXT_RSENSE 1
|
||||
#define IADC_GAIN_17P857MV 3
|
||||
#define IADC_EXT_OFFSET_CSP_CSN 5
|
||||
#define IADC_INT_OFFSET_CSP2_CSN2 6
|
||||
|
||||
/**
|
||||
* struct iadc_chip - IADC Current ADC device structure.
|
||||
* @regmap: regmap for register read/write.
|
||||
* @dev: This device pointer.
|
||||
* @base: base offset for the ADC peripheral.
|
||||
* @rsense: Values of the internal and external sense resister in micro Ohms.
|
||||
* @poll_eoc: Poll for end of conversion instead of waiting for IRQ.
|
||||
* @offset: Raw offset values for the internal and external channels.
|
||||
* @gain: Raw gain of the channels.
|
||||
* @lock: ADC lock for access to the peripheral.
|
||||
* @complete: ADC notification after end of conversion interrupt is received.
|
||||
*/
|
||||
struct iadc_chip {
|
||||
struct regmap *regmap;
|
||||
struct device *dev;
|
||||
u16 base;
|
||||
bool poll_eoc;
|
||||
u32 rsense[2];
|
||||
u16 offset[2];
|
||||
u16 gain;
|
||||
struct mutex lock;
|
||||
struct completion complete;
|
||||
};
|
||||
|
||||
static int iadc_read(struct iadc_chip *iadc, u16 offset, u8 *data)
|
||||
{
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(iadc->regmap, iadc->base + offset, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*data = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iadc_write(struct iadc_chip *iadc, u16 offset, u8 data)
|
||||
{
|
||||
return regmap_write(iadc->regmap, iadc->base + offset, data);
|
||||
}
|
||||
|
||||
static int iadc_reset(struct iadc_chip *iadc)
|
||||
{
|
||||
u8 data;
|
||||
int ret;
|
||||
|
||||
ret = iadc_write(iadc, IADC_SEC_ACCESS, IADC_SEC_ACCESS_DATA);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iadc_read(iadc, IADC_PERH_RESET_CTL3, &data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iadc_write(iadc, IADC_SEC_ACCESS, IADC_SEC_ACCESS_DATA);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data |= IADC_FOLLOW_WARM_RB;
|
||||
|
||||
return iadc_write(iadc, IADC_PERH_RESET_CTL3, data);
|
||||
}
|
||||
|
||||
static int iadc_set_state(struct iadc_chip *iadc, bool state)
|
||||
{
|
||||
return iadc_write(iadc, IADC_EN_CTL1, state ? IADC_EN_CTL1_SET : 0);
|
||||
}
|
||||
|
||||
static void iadc_status_show(struct iadc_chip *iadc)
|
||||
{
|
||||
u8 mode, sta1, chan, dig, en, req;
|
||||
int ret;
|
||||
|
||||
ret = iadc_read(iadc, IADC_MODE_CTL, &mode);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
ret = iadc_read(iadc, IADC_DIG_PARAM, &dig);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
ret = iadc_read(iadc, IADC_CH_SEL_CTL, &chan);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
ret = iadc_read(iadc, IADC_CONV_REQ, &req);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
ret = iadc_read(iadc, IADC_STATUS1, &sta1);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
ret = iadc_read(iadc, IADC_EN_CTL1, &en);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
dev_err(iadc->dev,
|
||||
"mode:%02x en:%02x chan:%02x dig:%02x req:%02x sta1:%02x\n",
|
||||
mode, en, chan, dig, req, sta1);
|
||||
}
|
||||
|
||||
static int iadc_configure(struct iadc_chip *iadc, int channel)
|
||||
{
|
||||
u8 decim, mode;
|
||||
int ret;
|
||||
|
||||
/* Mode selection */
|
||||
mode = (IADC_OP_MODE_NORMAL << IADC_OP_MODE_SHIFT) | IADC_TRIM_EN;
|
||||
ret = iadc_write(iadc, IADC_MODE_CTL, mode);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Channel selection */
|
||||
ret = iadc_write(iadc, IADC_CH_SEL_CTL, channel);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Digital parameter setup */
|
||||
decim = IADC_DEF_DECIMATION << IADC_DIG_DEC_RATIO_SEL_SHIFT;
|
||||
ret = iadc_write(iadc, IADC_DIG_PARAM, decim);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* HW settle time delay */
|
||||
ret = iadc_write(iadc, IADC_HW_SETTLE_DELAY, IADC_DEF_HW_SETTLE_TIME);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iadc_write(iadc, IADC_FAST_AVG_CTL, IADC_DEF_AVG_SAMPLES);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (IADC_DEF_AVG_SAMPLES)
|
||||
ret = iadc_write(iadc, IADC_FAST_AVG_EN, IADC_FAST_AVG_EN_SET);
|
||||
else
|
||||
ret = iadc_write(iadc, IADC_FAST_AVG_EN, 0);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!iadc->poll_eoc)
|
||||
reinit_completion(&iadc->complete);
|
||||
|
||||
ret = iadc_set_state(iadc, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Request conversion */
|
||||
return iadc_write(iadc, IADC_CONV_REQ, IADC_CONV_REQ_SET);
|
||||
}
|
||||
|
||||
static int iadc_poll_wait_eoc(struct iadc_chip *iadc, unsigned int interval_us)
|
||||
{
|
||||
unsigned int count, retry;
|
||||
int ret;
|
||||
u8 sta1;
|
||||
|
||||
retry = interval_us / IADC_CONV_TIME_MIN_US;
|
||||
|
||||
for (count = 0; count < retry; count++) {
|
||||
ret = iadc_read(iadc, IADC_STATUS1, &sta1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
sta1 &= IADC_STATUS1_REQ_STS_EOC_MASK;
|
||||
if (sta1 == IADC_STATUS1_EOC)
|
||||
return 0;
|
||||
|
||||
usleep_range(IADC_CONV_TIME_MIN_US, IADC_CONV_TIME_MAX_US);
|
||||
}
|
||||
|
||||
iadc_status_show(iadc);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int iadc_read_result(struct iadc_chip *iadc, u16 *data)
|
||||
{
|
||||
return regmap_bulk_read(iadc->regmap, iadc->base + IADC_DATA, data, 2);
|
||||
}
|
||||
|
||||
static int iadc_do_conversion(struct iadc_chip *iadc, int chan, u16 *data)
|
||||
{
|
||||
unsigned int wait;
|
||||
int ret;
|
||||
|
||||
ret = iadc_configure(iadc, chan);
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
|
||||
wait = BIT(IADC_DEF_AVG_SAMPLES) * IADC_CONV_TIME_MIN_US * 2;
|
||||
|
||||
if (iadc->poll_eoc) {
|
||||
ret = iadc_poll_wait_eoc(iadc, wait);
|
||||
} else {
|
||||
ret = wait_for_completion_timeout(&iadc->complete, wait);
|
||||
if (!ret)
|
||||
ret = -ETIMEDOUT;
|
||||
else
|
||||
/* double check conversion status */
|
||||
ret = iadc_poll_wait_eoc(iadc, IADC_CONV_TIME_MIN_US);
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
ret = iadc_read_result(iadc, data);
|
||||
exit:
|
||||
iadc_set_state(iadc, false);
|
||||
if (ret < 0)
|
||||
dev_err(iadc->dev, "conversion failed\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iadc_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct iadc_chip *iadc = iio_priv(indio_dev);
|
||||
s32 isense_ua, vsense_uv;
|
||||
u16 adc_raw, vsense_raw;
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&iadc->lock);
|
||||
ret = iadc_do_conversion(iadc, chan->channel, &adc_raw);
|
||||
mutex_unlock(&iadc->lock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
vsense_raw = adc_raw - iadc->offset[chan->channel];
|
||||
|
||||
vsense_uv = vsense_raw * IADC_REF_GAIN_MICRO_VOLTS;
|
||||
vsense_uv /= (s32)iadc->gain - iadc->offset[chan->channel];
|
||||
|
||||
isense_ua = vsense_uv / iadc->rsense[chan->channel];
|
||||
|
||||
dev_dbg(iadc->dev, "off %d gain %d adc %d %duV I %duA\n",
|
||||
iadc->offset[chan->channel], iadc->gain,
|
||||
adc_raw, vsense_uv, isense_ua);
|
||||
|
||||
*val = isense_ua;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
*val2 = 1000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info iadc_info = {
|
||||
.read_raw = iadc_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static irqreturn_t iadc_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct iadc_chip *iadc = dev_id;
|
||||
|
||||
complete(&iadc->complete);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int iadc_update_offset(struct iadc_chip *iadc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = iadc_do_conversion(iadc, IADC_GAIN_17P857MV, &iadc->gain);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iadc_do_conversion(iadc, IADC_INT_OFFSET_CSP2_CSN2,
|
||||
&iadc->offset[IADC_INT_RSENSE]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (iadc->gain == iadc->offset[IADC_INT_RSENSE]) {
|
||||
dev_err(iadc->dev, "error: internal offset == gain %d\n",
|
||||
iadc->gain);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = iadc_do_conversion(iadc, IADC_EXT_OFFSET_CSP_CSN,
|
||||
&iadc->offset[IADC_EXT_RSENSE]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (iadc->gain == iadc->offset[IADC_EXT_RSENSE]) {
|
||||
dev_err(iadc->dev, "error: external offset == gain %d\n",
|
||||
iadc->gain);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iadc_version_check(struct iadc_chip *iadc)
|
||||
{
|
||||
u8 val;
|
||||
int ret;
|
||||
|
||||
ret = iadc_read(iadc, IADC_PERPH_TYPE, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (val < IADC_PERPH_TYPE_ADC) {
|
||||
dev_err(iadc->dev, "%d is not ADC\n", val);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = iadc_read(iadc, IADC_PERPH_SUBTYPE, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (val < IADC_PERPH_SUBTYPE_IADC) {
|
||||
dev_err(iadc->dev, "%d is not IADC\n", val);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = iadc_read(iadc, IADC_REVISION2, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (val < IADC_REVISION2_SUPPORTED_IADC) {
|
||||
dev_err(iadc->dev, "revision %d not supported\n", val);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iadc_rsense_read(struct iadc_chip *iadc, struct device_node *node)
|
||||
{
|
||||
int ret, sign, int_sense;
|
||||
u8 deviation;
|
||||
|
||||
ret = of_property_read_u32(node, "qcom,external-resistor-micro-ohms",
|
||||
&iadc->rsense[IADC_EXT_RSENSE]);
|
||||
if (ret < 0)
|
||||
iadc->rsense[IADC_EXT_RSENSE] = IADC_INT_RSENSE_IDEAL_VALUE;
|
||||
|
||||
if (!iadc->rsense[IADC_EXT_RSENSE]) {
|
||||
dev_err(iadc->dev, "external resistor can't be zero Ohms");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = iadc_read(iadc, IADC_NOMINAL_RSENSE, &deviation);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Deviation value stored is an offset from 10 mili Ohms, bit 7 is
|
||||
* the sign, the remaining bits have an LSB of 15625 nano Ohms.
|
||||
*/
|
||||
sign = (deviation & IADC_NOMINAL_RSENSE_SIGN_MASK) ? -1 : 1;
|
||||
|
||||
deviation &= ~IADC_NOMINAL_RSENSE_SIGN_MASK;
|
||||
|
||||
/* Scale it to nono Ohms */
|
||||
int_sense = IADC_INT_RSENSE_IDEAL_VALUE * 1000;
|
||||
int_sense += sign * deviation * IADC_INT_RSENSE_DEVIATION;
|
||||
int_sense /= 1000; /* micro Ohms */
|
||||
|
||||
iadc->rsense[IADC_INT_RSENSE] = int_sense;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec iadc_channels[] = {
|
||||
{
|
||||
.type = IIO_CURRENT,
|
||||
.datasheet_name = "INTERNAL_RSENSE",
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.indexed = 1,
|
||||
},
|
||||
{
|
||||
.type = IIO_CURRENT,
|
||||
.datasheet_name = "EXTERNAL_RSENSE",
|
||||
.channel = 1,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.indexed = 1,
|
||||
},
|
||||
};
|
||||
|
||||
static int iadc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct iio_dev *indio_dev;
|
||||
struct iadc_chip *iadc;
|
||||
int ret, irq_eoc;
|
||||
u32 res;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*iadc));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
iadc = iio_priv(indio_dev);
|
||||
iadc->dev = dev;
|
||||
|
||||
iadc->regmap = dev_get_regmap(dev->parent, NULL);
|
||||
if (!iadc->regmap)
|
||||
return -ENODEV;
|
||||
|
||||
init_completion(&iadc->complete);
|
||||
mutex_init(&iadc->lock);
|
||||
|
||||
ret = of_property_read_u32(node, "reg", &res);
|
||||
if (ret < 0)
|
||||
return -ENODEV;
|
||||
|
||||
iadc->base = res;
|
||||
|
||||
ret = iadc_version_check(iadc);
|
||||
if (ret < 0)
|
||||
return -ENODEV;
|
||||
|
||||
ret = iadc_rsense_read(iadc, node);
|
||||
if (ret < 0)
|
||||
return -ENODEV;
|
||||
|
||||
dev_dbg(iadc->dev, "sense resistors %d and %d micro Ohm\n",
|
||||
iadc->rsense[IADC_INT_RSENSE],
|
||||
iadc->rsense[IADC_EXT_RSENSE]);
|
||||
|
||||
irq_eoc = platform_get_irq(pdev, 0);
|
||||
if (irq_eoc == -EPROBE_DEFER)
|
||||
return irq_eoc;
|
||||
|
||||
if (irq_eoc < 0)
|
||||
iadc->poll_eoc = true;
|
||||
|
||||
ret = iadc_reset(iadc);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "reset failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!iadc->poll_eoc) {
|
||||
ret = devm_request_irq(dev, irq_eoc, iadc_isr, 0,
|
||||
"spmi-iadc", iadc);
|
||||
if (!ret)
|
||||
enable_irq_wake(irq_eoc);
|
||||
else
|
||||
return ret;
|
||||
} else {
|
||||
device_init_wakeup(iadc->dev, 1);
|
||||
}
|
||||
|
||||
ret = iadc_update_offset(iadc);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed offset calibration\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->dev.of_node = node;
|
||||
indio_dev->name = pdev->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &iadc_info;
|
||||
indio_dev->channels = iadc_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(iadc_channels);
|
||||
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct of_device_id iadc_match_table[] = {
|
||||
{ .compatible = "qcom,spmi-iadc" },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, iadc_match_table);
|
||||
|
||||
static struct platform_driver iadc_driver = {
|
||||
.driver = {
|
||||
.name = "qcom-spmi-iadc",
|
||||
.of_match_table = iadc_match_table,
|
||||
},
|
||||
.probe = iadc_probe,
|
||||
};
|
||||
|
||||
module_platform_driver(iadc_driver);
|
||||
|
||||
MODULE_ALIAS("platform:qcom-spmi-iadc");
|
||||
MODULE_DESCRIPTION("Qualcomm SPMI PMIC current ADC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");
|
@ -18,13 +18,13 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#define SARADC_DATA 0x00
|
||||
#define SARADC_DATA_MASK 0x3ff
|
||||
|
||||
#define SARADC_STAS 0x04
|
||||
#define SARADC_STAS_BUSY BIT(0)
|
||||
@ -38,15 +38,22 @@
|
||||
#define SARADC_DLY_PU_SOC 0x0c
|
||||
#define SARADC_DLY_PU_SOC_MASK 0x3f
|
||||
|
||||
#define SARADC_BITS 10
|
||||
#define SARADC_TIMEOUT msecs_to_jiffies(100)
|
||||
|
||||
struct rockchip_saradc_data {
|
||||
int num_bits;
|
||||
const struct iio_chan_spec *channels;
|
||||
int num_channels;
|
||||
unsigned long clk_rate;
|
||||
};
|
||||
|
||||
struct rockchip_saradc {
|
||||
void __iomem *regs;
|
||||
struct clk *pclk;
|
||||
struct clk *clk;
|
||||
struct completion completion;
|
||||
struct regulator *vref;
|
||||
const struct rockchip_saradc_data *data;
|
||||
u16 last_val;
|
||||
};
|
||||
|
||||
@ -90,7 +97,7 @@ static int rockchip_saradc_read_raw(struct iio_dev *indio_dev,
|
||||
}
|
||||
|
||||
*val = ret / 1000;
|
||||
*val2 = SARADC_BITS;
|
||||
*val2 = info->data->num_bits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@ -103,7 +110,7 @@ static irqreturn_t rockchip_saradc_isr(int irq, void *dev_id)
|
||||
|
||||
/* Read value */
|
||||
info->last_val = readl_relaxed(info->regs + SARADC_DATA);
|
||||
info->last_val &= SARADC_DATA_MASK;
|
||||
info->last_val &= GENMASK(info->data->num_bits - 1, 0);
|
||||
|
||||
/* Clear irq & power down adc */
|
||||
writel_relaxed(0, info->regs + SARADC_CTRL);
|
||||
@ -133,12 +140,44 @@ static const struct iio_chan_spec rockchip_saradc_iio_channels[] = {
|
||||
ADC_CHANNEL(2, "adc2"),
|
||||
};
|
||||
|
||||
static const struct rockchip_saradc_data saradc_data = {
|
||||
.num_bits = 10,
|
||||
.channels = rockchip_saradc_iio_channels,
|
||||
.num_channels = ARRAY_SIZE(rockchip_saradc_iio_channels),
|
||||
.clk_rate = 1000000,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec rockchip_rk3066_tsadc_iio_channels[] = {
|
||||
ADC_CHANNEL(0, "adc0"),
|
||||
ADC_CHANNEL(1, "adc1"),
|
||||
};
|
||||
|
||||
static const struct rockchip_saradc_data rk3066_tsadc_data = {
|
||||
.num_bits = 12,
|
||||
.channels = rockchip_rk3066_tsadc_iio_channels,
|
||||
.num_channels = ARRAY_SIZE(rockchip_rk3066_tsadc_iio_channels),
|
||||
.clk_rate = 50000,
|
||||
};
|
||||
|
||||
static const struct of_device_id rockchip_saradc_match[] = {
|
||||
{
|
||||
.compatible = "rockchip,saradc",
|
||||
.data = &saradc_data,
|
||||
}, {
|
||||
.compatible = "rockchip,rk3066-tsadc",
|
||||
.data = &rk3066_tsadc_data,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rockchip_saradc_match);
|
||||
|
||||
static int rockchip_saradc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct rockchip_saradc *info = NULL;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct iio_dev *indio_dev = NULL;
|
||||
struct resource *mem;
|
||||
const struct of_device_id *match;
|
||||
int ret;
|
||||
int irq;
|
||||
|
||||
@ -152,6 +191,9 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
|
||||
}
|
||||
info = iio_priv(indio_dev);
|
||||
|
||||
match = of_match_device(rockchip_saradc_match, &pdev->dev);
|
||||
info->data = match->data;
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
info->regs = devm_ioremap_resource(&pdev->dev, mem);
|
||||
if (IS_ERR(info->regs))
|
||||
@ -192,10 +234,10 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
/*
|
||||
* Use a default of 1MHz for the converter clock.
|
||||
* Use a default value for the converter clock.
|
||||
* This may become user-configurable in the future.
|
||||
*/
|
||||
ret = clk_set_rate(info->clk, 1000000);
|
||||
ret = clk_set_rate(info->clk, info->data->clk_rate);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to set adc clk rate, %d\n", ret);
|
||||
return ret;
|
||||
@ -227,8 +269,8 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
|
||||
indio_dev->info = &rockchip_saradc_iio_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
indio_dev->channels = rockchip_saradc_iio_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(rockchip_saradc_iio_channels);
|
||||
indio_dev->channels = info->data->channels;
|
||||
indio_dev->num_channels = info->data->num_channels;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
@ -296,12 +338,6 @@ static int rockchip_saradc_resume(struct device *dev)
|
||||
static SIMPLE_DEV_PM_OPS(rockchip_saradc_pm_ops,
|
||||
rockchip_saradc_suspend, rockchip_saradc_resume);
|
||||
|
||||
static const struct of_device_id rockchip_saradc_match[] = {
|
||||
{ .compatible = "rockchip,saradc" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rockchip_saradc_match);
|
||||
|
||||
static struct platform_driver rockchip_saradc_driver = {
|
||||
.probe = rockchip_saradc_probe,
|
||||
.remove = rockchip_saradc_remove,
|
||||
|
@ -91,7 +91,7 @@
|
||||
#define VF610_ADC_CAL 0x80
|
||||
|
||||
/* Other field define */
|
||||
#define VF610_ADC_ADCHC(x) ((x) & 0xF)
|
||||
#define VF610_ADC_ADCHC(x) ((x) & 0x1F)
|
||||
#define VF610_ADC_AIEN (0x1 << 7)
|
||||
#define VF610_ADC_CONV_DISABLE 0x1F
|
||||
#define VF610_ADC_HS_COCO0 0x1
|
||||
@ -153,6 +153,12 @@ struct vf610_adc {
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
}
|
||||
|
||||
#define VF610_ADC_TEMPERATURE_CHAN(_idx, _chan_type) { \
|
||||
.type = (_chan_type), \
|
||||
.channel = (_idx), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec vf610_adc_iio_channels[] = {
|
||||
VF610_ADC_CHAN(0, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(1, IIO_VOLTAGE),
|
||||
@ -170,6 +176,7 @@ static const struct iio_chan_spec vf610_adc_iio_channels[] = {
|
||||
VF610_ADC_CHAN(13, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(14, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(15, IIO_VOLTAGE),
|
||||
VF610_ADC_TEMPERATURE_CHAN(26, IIO_TEMP),
|
||||
/* sentinel */
|
||||
};
|
||||
|
||||
@ -451,6 +458,7 @@ static int vf610_read_raw(struct iio_dev *indio_dev,
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
reinit_completion(&info->completion);
|
||||
|
||||
@ -468,7 +476,23 @@ static int vf610_read_raw(struct iio_dev *indio_dev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
*val = info->value;
|
||||
break;
|
||||
case IIO_TEMP:
|
||||
/*
|
||||
* Calculate in degree Celsius times 1000
|
||||
* Using sensor slope of 1.84 mV/°C and
|
||||
* V at 25°C of 696 mV
|
||||
*/
|
||||
*val = 25000 - ((int)info->value - 864) * 1000000 / 1840;
|
||||
break;
|
||||
default:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return IIO_VAL_INT;
|
||||
|
||||
@ -569,9 +593,9 @@ static int vf610_adc_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(info->regs);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq <= 0) {
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "no irq resource?\n");
|
||||
return -EINVAL;
|
||||
return irq;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(info->dev, irq,
|
||||
@ -586,8 +610,7 @@ static int vf610_adc_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(info->clk)) {
|
||||
dev_err(&pdev->dev, "failed getting clock, err = %ld\n",
|
||||
PTR_ERR(info->clk));
|
||||
ret = PTR_ERR(info->clk);
|
||||
return ret;
|
||||
return PTR_ERR(info->clk);
|
||||
}
|
||||
|
||||
info->vref = devm_regulator_get(&pdev->dev, "vref");
|
||||
@ -681,17 +704,19 @@ static int vf610_adc_resume(struct device *dev)
|
||||
|
||||
ret = clk_prepare_enable(info->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto disable_reg;
|
||||
|
||||
vf610_adc_hw_init(info);
|
||||
|
||||
return 0;
|
||||
|
||||
disable_reg:
|
||||
regulator_disable(info->vref);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(vf610_adc_pm_ops,
|
||||
vf610_adc_suspend,
|
||||
vf610_adc_resume);
|
||||
static SIMPLE_DEV_PM_OPS(vf610_adc_pm_ops, vf610_adc_suspend, vf610_adc_resume);
|
||||
|
||||
static struct platform_driver vf610_adc_driver = {
|
||||
.probe = vf610_adc_probe,
|
||||
|
@ -44,18 +44,18 @@ st_sensors_write_data_with_mask_error:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_sensors_match_odr(struct st_sensors *sensor,
|
||||
static int st_sensors_match_odr(struct st_sensor_settings *sensor_settings,
|
||||
unsigned int odr, struct st_sensor_odr_avl *odr_out)
|
||||
{
|
||||
int i, ret = -EINVAL;
|
||||
|
||||
for (i = 0; i < ST_SENSORS_ODR_LIST_MAX; i++) {
|
||||
if (sensor->odr.odr_avl[i].hz == 0)
|
||||
if (sensor_settings->odr.odr_avl[i].hz == 0)
|
||||
goto st_sensors_match_odr_error;
|
||||
|
||||
if (sensor->odr.odr_avl[i].hz == odr) {
|
||||
odr_out->hz = sensor->odr.odr_avl[i].hz;
|
||||
odr_out->value = sensor->odr.odr_avl[i].value;
|
||||
if (sensor_settings->odr.odr_avl[i].hz == odr) {
|
||||
odr_out->hz = sensor_settings->odr.odr_avl[i].hz;
|
||||
odr_out->value = sensor_settings->odr.odr_avl[i].value;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
@ -71,23 +71,26 @@ int st_sensors_set_odr(struct iio_dev *indio_dev, unsigned int odr)
|
||||
struct st_sensor_odr_avl odr_out = {0, 0};
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
err = st_sensors_match_odr(sdata->sensor, odr, &odr_out);
|
||||
err = st_sensors_match_odr(sdata->sensor_settings, odr, &odr_out);
|
||||
if (err < 0)
|
||||
goto st_sensors_match_odr_error;
|
||||
|
||||
if ((sdata->sensor->odr.addr == sdata->sensor->pw.addr) &&
|
||||
(sdata->sensor->odr.mask == sdata->sensor->pw.mask)) {
|
||||
if ((sdata->sensor_settings->odr.addr ==
|
||||
sdata->sensor_settings->pw.addr) &&
|
||||
(sdata->sensor_settings->odr.mask ==
|
||||
sdata->sensor_settings->pw.mask)) {
|
||||
if (sdata->enabled == true) {
|
||||
err = st_sensors_write_data_with_mask(indio_dev,
|
||||
sdata->sensor->odr.addr,
|
||||
sdata->sensor->odr.mask,
|
||||
sdata->sensor_settings->odr.addr,
|
||||
sdata->sensor_settings->odr.mask,
|
||||
odr_out.value);
|
||||
} else {
|
||||
err = 0;
|
||||
}
|
||||
} else {
|
||||
err = st_sensors_write_data_with_mask(indio_dev,
|
||||
sdata->sensor->odr.addr, sdata->sensor->odr.mask,
|
||||
sdata->sensor_settings->odr.addr,
|
||||
sdata->sensor_settings->odr.mask,
|
||||
odr_out.value);
|
||||
}
|
||||
if (err >= 0)
|
||||
@ -98,16 +101,16 @@ st_sensors_match_odr_error:
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_set_odr);
|
||||
|
||||
static int st_sensors_match_fs(struct st_sensors *sensor,
|
||||
static int st_sensors_match_fs(struct st_sensor_settings *sensor_settings,
|
||||
unsigned int fs, int *index_fs_avl)
|
||||
{
|
||||
int i, ret = -EINVAL;
|
||||
|
||||
for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
|
||||
if (sensor->fs.fs_avl[i].num == 0)
|
||||
if (sensor_settings->fs.fs_avl[i].num == 0)
|
||||
goto st_sensors_match_odr_error;
|
||||
|
||||
if (sensor->fs.fs_avl[i].num == fs) {
|
||||
if (sensor_settings->fs.fs_avl[i].num == fs) {
|
||||
*index_fs_avl = i;
|
||||
ret = 0;
|
||||
break;
|
||||
@ -118,25 +121,24 @@ st_sensors_match_odr_error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int st_sensors_set_fullscale(struct iio_dev *indio_dev,
|
||||
unsigned int fs)
|
||||
static int st_sensors_set_fullscale(struct iio_dev *indio_dev, unsigned int fs)
|
||||
{
|
||||
int err, i = 0;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
err = st_sensors_match_fs(sdata->sensor, fs, &i);
|
||||
err = st_sensors_match_fs(sdata->sensor_settings, fs, &i);
|
||||
if (err < 0)
|
||||
goto st_accel_set_fullscale_error;
|
||||
|
||||
err = st_sensors_write_data_with_mask(indio_dev,
|
||||
sdata->sensor->fs.addr,
|
||||
sdata->sensor->fs.mask,
|
||||
sdata->sensor->fs.fs_avl[i].value);
|
||||
sdata->sensor_settings->fs.addr,
|
||||
sdata->sensor_settings->fs.mask,
|
||||
sdata->sensor_settings->fs.fs_avl[i].value);
|
||||
if (err < 0)
|
||||
goto st_accel_set_fullscale_error;
|
||||
|
||||
sdata->current_fullscale = (struct st_sensor_fullscale_avl *)
|
||||
&sdata->sensor->fs.fs_avl[i];
|
||||
&sdata->sensor_settings->fs.fs_avl[i];
|
||||
return err;
|
||||
|
||||
st_accel_set_fullscale_error:
|
||||
@ -153,10 +155,12 @@ int st_sensors_set_enable(struct iio_dev *indio_dev, bool enable)
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
if (enable) {
|
||||
tmp_value = sdata->sensor->pw.value_on;
|
||||
if ((sdata->sensor->odr.addr == sdata->sensor->pw.addr) &&
|
||||
(sdata->sensor->odr.mask == sdata->sensor->pw.mask)) {
|
||||
err = st_sensors_match_odr(sdata->sensor,
|
||||
tmp_value = sdata->sensor_settings->pw.value_on;
|
||||
if ((sdata->sensor_settings->odr.addr ==
|
||||
sdata->sensor_settings->pw.addr) &&
|
||||
(sdata->sensor_settings->odr.mask ==
|
||||
sdata->sensor_settings->pw.mask)) {
|
||||
err = st_sensors_match_odr(sdata->sensor_settings,
|
||||
sdata->odr, &odr_out);
|
||||
if (err < 0)
|
||||
goto set_enable_error;
|
||||
@ -164,8 +168,8 @@ int st_sensors_set_enable(struct iio_dev *indio_dev, bool enable)
|
||||
found = true;
|
||||
}
|
||||
err = st_sensors_write_data_with_mask(indio_dev,
|
||||
sdata->sensor->pw.addr,
|
||||
sdata->sensor->pw.mask, tmp_value);
|
||||
sdata->sensor_settings->pw.addr,
|
||||
sdata->sensor_settings->pw.mask, tmp_value);
|
||||
if (err < 0)
|
||||
goto set_enable_error;
|
||||
|
||||
@ -175,9 +179,9 @@ int st_sensors_set_enable(struct iio_dev *indio_dev, bool enable)
|
||||
sdata->odr = odr_out.hz;
|
||||
} else {
|
||||
err = st_sensors_write_data_with_mask(indio_dev,
|
||||
sdata->sensor->pw.addr,
|
||||
sdata->sensor->pw.mask,
|
||||
sdata->sensor->pw.value_off);
|
||||
sdata->sensor_settings->pw.addr,
|
||||
sdata->sensor_settings->pw.mask,
|
||||
sdata->sensor_settings->pw.value_off);
|
||||
if (err < 0)
|
||||
goto set_enable_error;
|
||||
|
||||
@ -194,8 +198,9 @@ int st_sensors_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable)
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
return st_sensors_write_data_with_mask(indio_dev,
|
||||
sdata->sensor->enable_axis.addr,
|
||||
sdata->sensor->enable_axis.mask, axis_enable);
|
||||
sdata->sensor_settings->enable_axis.addr,
|
||||
sdata->sensor_settings->enable_axis.mask,
|
||||
axis_enable);
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_set_axis_enable);
|
||||
|
||||
@ -242,7 +247,7 @@ static int st_sensors_set_drdy_int_pin(struct iio_dev *indio_dev,
|
||||
|
||||
switch (pdata->drdy_int_pin) {
|
||||
case 1:
|
||||
if (sdata->sensor->drdy_irq.mask_int1 == 0) {
|
||||
if (sdata->sensor_settings->drdy_irq.mask_int1 == 0) {
|
||||
dev_err(&indio_dev->dev,
|
||||
"DRDY on INT1 not available.\n");
|
||||
return -EINVAL;
|
||||
@ -250,7 +255,7 @@ static int st_sensors_set_drdy_int_pin(struct iio_dev *indio_dev,
|
||||
sdata->drdy_int_pin = 1;
|
||||
break;
|
||||
case 2:
|
||||
if (sdata->sensor->drdy_irq.mask_int2 == 0) {
|
||||
if (sdata->sensor_settings->drdy_irq.mask_int2 == 0) {
|
||||
dev_err(&indio_dev->dev,
|
||||
"DRDY on INT2 not available.\n");
|
||||
return -EINVAL;
|
||||
@ -330,7 +335,8 @@ int st_sensors_init_sensor(struct iio_dev *indio_dev,
|
||||
|
||||
/* set BDU */
|
||||
err = st_sensors_write_data_with_mask(indio_dev,
|
||||
sdata->sensor->bdu.addr, sdata->sensor->bdu.mask, true);
|
||||
sdata->sensor_settings->bdu.addr,
|
||||
sdata->sensor_settings->bdu.mask, true);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
@ -346,26 +352,28 @@ 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)
|
||||
if (!sdata->sensor_settings->drdy_irq.addr)
|
||||
return 0;
|
||||
|
||||
/* Enable/Disable the interrupt generator 1. */
|
||||
if (sdata->sensor->drdy_irq.ig1.en_addr > 0) {
|
||||
if (sdata->sensor_settings->drdy_irq.ig1.en_addr > 0) {
|
||||
err = st_sensors_write_data_with_mask(indio_dev,
|
||||
sdata->sensor->drdy_irq.ig1.en_addr,
|
||||
sdata->sensor->drdy_irq.ig1.en_mask, (int)enable);
|
||||
sdata->sensor_settings->drdy_irq.ig1.en_addr,
|
||||
sdata->sensor_settings->drdy_irq.ig1.en_mask,
|
||||
(int)enable);
|
||||
if (err < 0)
|
||||
goto st_accel_set_dataready_irq_error;
|
||||
}
|
||||
|
||||
if (sdata->drdy_int_pin == 1)
|
||||
drdy_mask = sdata->sensor->drdy_irq.mask_int1;
|
||||
drdy_mask = sdata->sensor_settings->drdy_irq.mask_int1;
|
||||
else
|
||||
drdy_mask = sdata->sensor->drdy_irq.mask_int2;
|
||||
drdy_mask = sdata->sensor_settings->drdy_irq.mask_int2;
|
||||
|
||||
/* Enable/Disable the interrupt generator for data ready. */
|
||||
err = st_sensors_write_data_with_mask(indio_dev,
|
||||
sdata->sensor->drdy_irq.addr, drdy_mask, (int)enable);
|
||||
sdata->sensor_settings->drdy_irq.addr,
|
||||
drdy_mask, (int)enable);
|
||||
|
||||
st_accel_set_dataready_irq_error:
|
||||
return err;
|
||||
@ -378,8 +386,8 @@ int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev, int scale)
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
|
||||
if ((sdata->sensor->fs.fs_avl[i].gain == scale) &&
|
||||
(sdata->sensor->fs.fs_avl[i].gain != 0)) {
|
||||
if ((sdata->sensor_settings->fs.fs_avl[i].gain == scale) &&
|
||||
(sdata->sensor_settings->fs.fs_avl[i].gain != 0)) {
|
||||
err = 0;
|
||||
break;
|
||||
}
|
||||
@ -388,7 +396,7 @@ int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev, int scale)
|
||||
goto st_sensors_match_scale_error;
|
||||
|
||||
err = st_sensors_set_fullscale(indio_dev,
|
||||
sdata->sensor->fs.fs_avl[i].num);
|
||||
sdata->sensor_settings->fs.fs_avl[i].num);
|
||||
|
||||
st_sensors_match_scale_error:
|
||||
return err;
|
||||
@ -439,7 +447,7 @@ int st_sensors_read_info_raw(struct iio_dev *indio_dev,
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
msleep((sdata->sensor->bootime * 1000) / sdata->odr);
|
||||
msleep((sdata->sensor_settings->bootime * 1000) / sdata->odr);
|
||||
err = st_sensors_read_axis_data(indio_dev, ch, val);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
@ -456,7 +464,8 @@ out:
|
||||
EXPORT_SYMBOL(st_sensors_read_info_raw);
|
||||
|
||||
int st_sensors_check_device_support(struct iio_dev *indio_dev,
|
||||
int num_sensors_list, const struct st_sensors *sensors)
|
||||
int num_sensors_list,
|
||||
const struct st_sensor_settings *sensor_settings)
|
||||
{
|
||||
u8 wai;
|
||||
int i, n, err;
|
||||
@ -470,23 +479,24 @@ int st_sensors_check_device_support(struct iio_dev *indio_dev,
|
||||
}
|
||||
|
||||
for (i = 0; i < num_sensors_list; i++) {
|
||||
if (sensors[i].wai == wai)
|
||||
if (sensor_settings[i].wai == wai)
|
||||
break;
|
||||
}
|
||||
if (i == num_sensors_list)
|
||||
goto device_not_supported;
|
||||
|
||||
for (n = 0; n < ARRAY_SIZE(sensors[i].sensors_supported); n++) {
|
||||
for (n = 0; n < ARRAY_SIZE(sensor_settings[i].sensors_supported); n++) {
|
||||
if (strcmp(indio_dev->name,
|
||||
&sensors[i].sensors_supported[n][0]) == 0)
|
||||
&sensor_settings[i].sensors_supported[n][0]) == 0)
|
||||
break;
|
||||
}
|
||||
if (n == ARRAY_SIZE(sensors[i].sensors_supported)) {
|
||||
if (n == ARRAY_SIZE(sensor_settings[i].sensors_supported)) {
|
||||
dev_err(&indio_dev->dev, "device name and WhoAmI mismatch.\n");
|
||||
goto sensor_name_mismatch;
|
||||
}
|
||||
|
||||
sdata->sensor = (struct st_sensors *)&sensors[i];
|
||||
sdata->sensor_settings =
|
||||
(struct st_sensor_settings *)&sensor_settings[i];
|
||||
|
||||
return i;
|
||||
|
||||
@ -508,11 +518,11 @@ ssize_t st_sensors_sysfs_sampling_frequency_avail(struct device *dev,
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
for (i = 0; i < ST_SENSORS_ODR_LIST_MAX; i++) {
|
||||
if (sdata->sensor->odr.odr_avl[i].hz == 0)
|
||||
if (sdata->sensor_settings->odr.odr_avl[i].hz == 0)
|
||||
break;
|
||||
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
|
||||
sdata->sensor->odr.odr_avl[i].hz);
|
||||
sdata->sensor_settings->odr.odr_avl[i].hz);
|
||||
}
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
buf[len - 1] = '\n';
|
||||
@ -530,11 +540,11 @@ ssize_t st_sensors_sysfs_scale_avail(struct device *dev,
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
|
||||
if (sdata->sensor->fs.fs_avl[i].num == 0)
|
||||
if (sdata->sensor_settings->fs.fs_avl[i].num == 0)
|
||||
break;
|
||||
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ",
|
||||
sdata->sensor->fs.fs_avl[i].gain);
|
||||
sdata->sensor_settings->fs.fs_avl[i].gain);
|
||||
}
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
buf[len - 1] = '\n';
|
||||
|
@ -72,6 +72,7 @@ void st_sensors_i2c_configure(struct iio_dev *indio_dev,
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = client->name;
|
||||
|
||||
sdata->dev = &client->dev;
|
||||
sdata->tf = &st_sensors_tf_i2c;
|
||||
sdata->get_irq_data_ready = st_sensors_i2c_get_irq;
|
||||
}
|
||||
|
@ -111,6 +111,7 @@ void st_sensors_spi_configure(struct iio_dev *indio_dev,
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi->modalias;
|
||||
|
||||
sdata->dev = &spi->dev;
|
||||
sdata->tf = &st_sensors_tf_spi;
|
||||
sdata->get_irq_data_ready = st_sensors_spi_get_irq;
|
||||
}
|
||||
|
@ -30,8 +30,7 @@ static const struct st_sensors_platform_data gyro_pdata = {
|
||||
.drdy_int_pin = 2,
|
||||
};
|
||||
|
||||
int st_gyro_common_probe(struct iio_dev *indio_dev,
|
||||
struct st_sensors_platform_data *pdata);
|
||||
int st_gyro_common_probe(struct iio_dev *indio_dev);
|
||||
void st_gyro_common_remove(struct iio_dev *indio_dev);
|
||||
|
||||
#ifdef CONFIG_IIO_BUFFER
|
||||
|
@ -103,7 +103,7 @@ static const struct iio_chan_spec st_gyro_16bit_channels[] = {
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3)
|
||||
};
|
||||
|
||||
static const struct st_sensors st_gyro_sensors[] = {
|
||||
static const struct st_sensor_settings st_gyro_sensors_settings[] = {
|
||||
{
|
||||
.wai = ST_GYRO_1_WAI_EXP,
|
||||
.sensors_supported = {
|
||||
@ -309,8 +309,7 @@ static const struct iio_trigger_ops st_gyro_trigger_ops = {
|
||||
#define ST_GYRO_TRIGGER_OPS NULL
|
||||
#endif
|
||||
|
||||
int st_gyro_common_probe(struct iio_dev *indio_dev,
|
||||
struct st_sensors_platform_data *pdata)
|
||||
int st_gyro_common_probe(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *gdata = iio_priv(indio_dev);
|
||||
int irq = gdata->get_irq_data_ready(indio_dev);
|
||||
@ -322,20 +321,22 @@ int st_gyro_common_probe(struct iio_dev *indio_dev,
|
||||
st_sensors_power_enable(indio_dev);
|
||||
|
||||
err = st_sensors_check_device_support(indio_dev,
|
||||
ARRAY_SIZE(st_gyro_sensors), st_gyro_sensors);
|
||||
ARRAY_SIZE(st_gyro_sensors_settings),
|
||||
st_gyro_sensors_settings);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
gdata->num_data_channels = ST_GYRO_NUMBER_DATA_CHANNELS;
|
||||
gdata->multiread_bit = gdata->sensor->multi_read_bit;
|
||||
indio_dev->channels = gdata->sensor->ch;
|
||||
gdata->multiread_bit = gdata->sensor_settings->multi_read_bit;
|
||||
indio_dev->channels = gdata->sensor_settings->ch;
|
||||
indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS;
|
||||
|
||||
gdata->current_fullscale = (struct st_sensor_fullscale_avl *)
|
||||
&gdata->sensor->fs.fs_avl[0];
|
||||
gdata->odr = gdata->sensor->odr.odr_avl[0].hz;
|
||||
&gdata->sensor_settings->fs.fs_avl[0];
|
||||
gdata->odr = gdata->sensor_settings->odr.odr_avl[0].hz;
|
||||
|
||||
err = st_sensors_init_sensor(indio_dev, pdata);
|
||||
err = st_sensors_init_sensor(indio_dev,
|
||||
(struct st_sensors_platform_data *)&gyro_pdata);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -67,13 +67,11 @@ static int st_gyro_i2c_probe(struct i2c_client *client,
|
||||
return -ENOMEM;
|
||||
|
||||
gdata = iio_priv(indio_dev);
|
||||
gdata->dev = &client->dev;
|
||||
st_sensors_of_i2c_probe(client, st_gyro_of_match);
|
||||
|
||||
st_sensors_i2c_configure(indio_dev, client, gdata);
|
||||
|
||||
err = st_gyro_common_probe(indio_dev,
|
||||
(struct st_sensors_platform_data *)&gyro_pdata);
|
||||
err = st_gyro_common_probe(indio_dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -29,12 +29,10 @@ static int st_gyro_spi_probe(struct spi_device *spi)
|
||||
return -ENOMEM;
|
||||
|
||||
gdata = iio_priv(indio_dev);
|
||||
gdata->dev = &spi->dev;
|
||||
|
||||
st_sensors_spi_configure(indio_dev, spi, gdata);
|
||||
|
||||
err = st_gyro_common_probe(indio_dev,
|
||||
(struct st_sensors_platform_data *)&gyro_pdata);
|
||||
err = st_gyro_common_probe(indio_dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -22,4 +22,14 @@ config SI7005
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called si7005.
|
||||
|
||||
config SI7020
|
||||
tristate "Si7013/20/21 Relative Humidity and Temperature Sensors"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to build support for the Silicon Labs Si7013/20/21
|
||||
Relative Humidity and Temperature Sensors.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called si7020.
|
||||
|
||||
endmenu
|
||||
|
@ -4,3 +4,4 @@
|
||||
|
||||
obj-$(CONFIG_DHT11) += dht11.o
|
||||
obj-$(CONFIG_SI7005) += si7005.o
|
||||
obj-$(CONFIG_SI7020) += si7020.o
|
||||
|
161
drivers/iio/humidity/si7020.c
Normal file
161
drivers/iio/humidity/si7020.c
Normal file
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* si7020.c - Silicon Labs Si7013/20/21 Relative Humidity and Temp Sensors
|
||||
* Copyright (c) 2013,2014 Uplogix, Inc.
|
||||
* David Barksdale <dbarksdale@uplogix.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The Silicon Labs Si7013/20/21 Relative Humidity and Temperature Sensors
|
||||
* are i2c devices which have an identical programming interface for
|
||||
* measuring relative humidity and temperature. The Si7013 has an additional
|
||||
* temperature input which this driver does not support.
|
||||
*
|
||||
* Data Sheets:
|
||||
* Si7013: http://www.silabs.com/Support%20Documents/TechnicalDocs/Si7013.pdf
|
||||
* Si7020: http://www.silabs.com/Support%20Documents/TechnicalDocs/Si7020.pdf
|
||||
* Si7021: http://www.silabs.com/Support%20Documents/TechnicalDocs/Si7021.pdf
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
/* Measure Relative Humidity, Hold Master Mode */
|
||||
#define SI7020CMD_RH_HOLD 0xE5
|
||||
/* Measure Temperature, Hold Master Mode */
|
||||
#define SI7020CMD_TEMP_HOLD 0xE3
|
||||
/* Software Reset */
|
||||
#define SI7020CMD_RESET 0xFE
|
||||
|
||||
static int si7020_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct i2c_client *client = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = i2c_smbus_read_word_data(client,
|
||||
chan->type == IIO_TEMP ?
|
||||
SI7020CMD_TEMP_HOLD :
|
||||
SI7020CMD_RH_HOLD);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret >> 2;
|
||||
if (chan->type == IIO_HUMIDITYRELATIVE)
|
||||
*val &= GENMASK(11, 0);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (chan->type == IIO_TEMP)
|
||||
*val = 175720; /* = 175.72 * 1000 */
|
||||
else
|
||||
*val = 125 * 1000;
|
||||
*val2 = 65536 >> 2;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
/*
|
||||
* Since iio_convert_raw_to_processed_unlocked assumes offset
|
||||
* is an integer we have to round these values and lose
|
||||
* accuracy.
|
||||
* Relative humidity will be 0.0032959% too high and
|
||||
* temperature will be 0.00277344 degrees too high.
|
||||
* This is no big deal because it's within the accuracy of the
|
||||
* sensor.
|
||||
*/
|
||||
if (chan->type == IIO_TEMP)
|
||||
*val = -4368; /* = -46.85 * (65536 >> 2) / 175.72 */
|
||||
else
|
||||
*val = -786; /* = -6 * (65536 >> 2) / 125 */
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec si7020_channels[] = {
|
||||
{
|
||||
.type = IIO_HUMIDITYRELATIVE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET),
|
||||
},
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET),
|
||||
}
|
||||
};
|
||||
|
||||
static const struct iio_info si7020_info = {
|
||||
.read_raw = si7020_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int si7020_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct i2c_client **data;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_WRITE_BYTE |
|
||||
I2C_FUNC_SMBUS_READ_WORD_DATA))
|
||||
return -ENODEV;
|
||||
|
||||
/* Reset device, loads default settings. */
|
||||
ret = i2c_smbus_write_byte(client, SI7020CMD_RESET);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/* Wait the maximum power-up time after software reset. */
|
||||
msleep(15);
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*client));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
*data = client;
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = dev_name(&client->dev);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &si7020_info;
|
||||
indio_dev->channels = si7020_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(si7020_channels);
|
||||
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id si7020_id[] = {
|
||||
{ "si7020", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, si7020_id);
|
||||
|
||||
static struct i2c_driver si7020_driver = {
|
||||
.driver.name = "si7020",
|
||||
.probe = si7020_probe,
|
||||
.id_table = si7020_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(si7020_driver);
|
||||
MODULE_DESCRIPTION("Silicon Labs Si7013/20/21 Relative Humidity and Temperature Sensors");
|
||||
MODULE_AUTHOR("David Barksdale <dbarksdale@uplogix.com>");
|
||||
MODULE_LICENSE("GPL");
|
@ -100,6 +100,28 @@ static int iio_dev_node_match(struct device *dev, void *data)
|
||||
return dev->of_node == data && dev->type == &iio_device_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* __of_iio_simple_xlate - translate iiospec to the IIO channel index
|
||||
* @indio_dev: pointer to the iio_dev structure
|
||||
* @iiospec: IIO specifier as found in the device tree
|
||||
*
|
||||
* This is simple translation function, suitable for the most 1:1 mapped
|
||||
* channels in IIO chips. This function performs only one sanity check:
|
||||
* whether IIO index is less than num_channels (that is specified in the
|
||||
* iio_dev).
|
||||
*/
|
||||
static int __of_iio_simple_xlate(struct iio_dev *indio_dev,
|
||||
const struct of_phandle_args *iiospec)
|
||||
{
|
||||
if (!iiospec->args_count)
|
||||
return 0;
|
||||
|
||||
if (iiospec->args[0] >= indio_dev->num_channels)
|
||||
return -EINVAL;
|
||||
|
||||
return iiospec->args[0];
|
||||
}
|
||||
|
||||
static int __of_iio_channel_get(struct iio_channel *channel,
|
||||
struct device_node *np, int index)
|
||||
{
|
||||
@ -122,18 +144,19 @@ static int __of_iio_channel_get(struct iio_channel *channel,
|
||||
|
||||
indio_dev = dev_to_iio_dev(idev);
|
||||
channel->indio_dev = indio_dev;
|
||||
index = iiospec.args_count ? iiospec.args[0] : 0;
|
||||
if (index >= indio_dev->num_channels) {
|
||||
err = -EINVAL;
|
||||
if (indio_dev->info->of_xlate)
|
||||
index = indio_dev->info->of_xlate(indio_dev, &iiospec);
|
||||
else
|
||||
index = __of_iio_simple_xlate(indio_dev, &iiospec);
|
||||
if (index < 0)
|
||||
goto err_put;
|
||||
}
|
||||
channel->channel = &indio_dev->channels[index];
|
||||
|
||||
return 0;
|
||||
|
||||
err_put:
|
||||
iio_device_put(indio_dev);
|
||||
return err;
|
||||
return index;
|
||||
}
|
||||
|
||||
static struct iio_channel *of_iio_channel_get(struct device_node *np, int index)
|
||||
|
@ -18,8 +18,7 @@
|
||||
#define LSM303DLM_MAGN_DEV_NAME "lsm303dlm_magn"
|
||||
#define LIS3MDL_MAGN_DEV_NAME "lis3mdl"
|
||||
|
||||
int st_magn_common_probe(struct iio_dev *indio_dev,
|
||||
struct st_sensors_platform_data *pdata);
|
||||
int st_magn_common_probe(struct iio_dev *indio_dev);
|
||||
void st_magn_common_remove(struct iio_dev *indio_dev);
|
||||
|
||||
#ifdef CONFIG_IIO_BUFFER
|
||||
|
@ -149,7 +149,7 @@ static const struct iio_chan_spec st_magn_2_16bit_channels[] = {
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3)
|
||||
};
|
||||
|
||||
static const struct st_sensors st_magn_sensors[] = {
|
||||
static const struct st_sensor_settings st_magn_sensors_settings[] = {
|
||||
{
|
||||
.wai = ST_MAGN_1_WAI_EXP,
|
||||
.sensors_supported = {
|
||||
@ -361,8 +361,7 @@ static const struct iio_info magn_info = {
|
||||
.write_raw = &st_magn_write_raw,
|
||||
};
|
||||
|
||||
int st_magn_common_probe(struct iio_dev *indio_dev,
|
||||
struct st_sensors_platform_data *pdata)
|
||||
int st_magn_common_probe(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *mdata = iio_priv(indio_dev);
|
||||
int irq = mdata->get_irq_data_ready(indio_dev);
|
||||
@ -374,20 +373,21 @@ int st_magn_common_probe(struct iio_dev *indio_dev,
|
||||
st_sensors_power_enable(indio_dev);
|
||||
|
||||
err = st_sensors_check_device_support(indio_dev,
|
||||
ARRAY_SIZE(st_magn_sensors), st_magn_sensors);
|
||||
ARRAY_SIZE(st_magn_sensors_settings),
|
||||
st_magn_sensors_settings);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
mdata->num_data_channels = ST_MAGN_NUMBER_DATA_CHANNELS;
|
||||
mdata->multiread_bit = mdata->sensor->multi_read_bit;
|
||||
indio_dev->channels = mdata->sensor->ch;
|
||||
mdata->multiread_bit = mdata->sensor_settings->multi_read_bit;
|
||||
indio_dev->channels = mdata->sensor_settings->ch;
|
||||
indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS;
|
||||
|
||||
mdata->current_fullscale = (struct st_sensor_fullscale_avl *)
|
||||
&mdata->sensor->fs.fs_avl[0];
|
||||
mdata->odr = mdata->sensor->odr.odr_avl[0].hz;
|
||||
&mdata->sensor_settings->fs.fs_avl[0];
|
||||
mdata->odr = mdata->sensor_settings->odr.odr_avl[0].hz;
|
||||
|
||||
err = st_sensors_init_sensor(indio_dev, pdata);
|
||||
err = st_sensors_init_sensor(indio_dev, NULL);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -51,12 +51,11 @@ static int st_magn_i2c_probe(struct i2c_client *client,
|
||||
return -ENOMEM;
|
||||
|
||||
mdata = iio_priv(indio_dev);
|
||||
mdata->dev = &client->dev;
|
||||
st_sensors_of_i2c_probe(client, st_magn_of_match);
|
||||
|
||||
st_sensors_i2c_configure(indio_dev, client, mdata);
|
||||
|
||||
err = st_magn_common_probe(indio_dev, NULL);
|
||||
err = st_magn_common_probe(indio_dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -29,11 +29,10 @@ static int st_magn_spi_probe(struct spi_device *spi)
|
||||
return -ENOMEM;
|
||||
|
||||
mdata = iio_priv(indio_dev);
|
||||
mdata->dev = &spi->dev;
|
||||
|
||||
st_sensors_spi_configure(indio_dev, spi, mdata);
|
||||
|
||||
err = st_magn_common_probe(indio_dev, NULL);
|
||||
err = st_magn_common_probe(indio_dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -5,6 +5,17 @@
|
||||
|
||||
menu "Pressure sensors"
|
||||
|
||||
config BMP280
|
||||
tristate "Bosch Sensortec BMP280 pressure sensor driver"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say yes here to build support for Bosch Sensortec BMP280
|
||||
pressure and temperature sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called bmp280.
|
||||
|
||||
config HID_SENSOR_PRESS
|
||||
depends on HID_SENSOR_HUB
|
||||
select IIO_BUFFER
|
||||
|
@ -3,6 +3,7 @@
|
||||
#
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_BMP280) += bmp280.o
|
||||
obj-$(CONFIG_HID_SENSOR_PRESS) += hid-sensor-press.o
|
||||
obj-$(CONFIG_MPL115) += mpl115.o
|
||||
obj-$(CONFIG_MPL3115) += mpl3115.o
|
||||
|
455
drivers/iio/pressure/bmp280.c
Normal file
455
drivers/iio/pressure/bmp280.c
Normal file
@ -0,0 +1,455 @@
|
||||
/*
|
||||
* Copyright (c) 2014 Intel Corporation
|
||||
*
|
||||
* Driver for Bosch Sensortec BMP280 digital pressure sensor.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "bmp280: " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define BMP280_REG_TEMP_XLSB 0xFC
|
||||
#define BMP280_REG_TEMP_LSB 0xFB
|
||||
#define BMP280_REG_TEMP_MSB 0xFA
|
||||
#define BMP280_REG_PRESS_XLSB 0xF9
|
||||
#define BMP280_REG_PRESS_LSB 0xF8
|
||||
#define BMP280_REG_PRESS_MSB 0xF7
|
||||
|
||||
#define BMP280_REG_CONFIG 0xF5
|
||||
#define BMP280_REG_CTRL_MEAS 0xF4
|
||||
#define BMP280_REG_STATUS 0xF3
|
||||
#define BMP280_REG_RESET 0xE0
|
||||
#define BMP280_REG_ID 0xD0
|
||||
|
||||
#define BMP280_REG_COMP_TEMP_START 0x88
|
||||
#define BMP280_COMP_TEMP_REG_COUNT 6
|
||||
|
||||
#define BMP280_REG_COMP_PRESS_START 0x8E
|
||||
#define BMP280_COMP_PRESS_REG_COUNT 18
|
||||
|
||||
#define BMP280_FILTER_MASK (BIT(4) | BIT(3) | BIT(2))
|
||||
#define BMP280_FILTER_OFF 0
|
||||
#define BMP280_FILTER_2X BIT(2)
|
||||
#define BMP280_FILTER_4X BIT(3)
|
||||
#define BMP280_FILTER_8X (BIT(3) | BIT(2))
|
||||
#define BMP280_FILTER_16X BIT(4)
|
||||
|
||||
#define BMP280_OSRS_TEMP_MASK (BIT(7) | BIT(6) | BIT(5))
|
||||
#define BMP280_OSRS_TEMP_SKIP 0
|
||||
#define BMP280_OSRS_TEMP_1X BIT(5)
|
||||
#define BMP280_OSRS_TEMP_2X BIT(6)
|
||||
#define BMP280_OSRS_TEMP_4X (BIT(6) | BIT(5))
|
||||
#define BMP280_OSRS_TEMP_8X BIT(7)
|
||||
#define BMP280_OSRS_TEMP_16X (BIT(7) | BIT(5))
|
||||
|
||||
#define BMP280_OSRS_PRESS_MASK (BIT(4) | BIT(3) | BIT(2))
|
||||
#define BMP280_OSRS_PRESS_SKIP 0
|
||||
#define BMP280_OSRS_PRESS_1X BIT(2)
|
||||
#define BMP280_OSRS_PRESS_2X BIT(3)
|
||||
#define BMP280_OSRS_PRESS_4X (BIT(3) | BIT(2))
|
||||
#define BMP280_OSRS_PRESS_8X BIT(4)
|
||||
#define BMP280_OSRS_PRESS_16X (BIT(4) | BIT(2))
|
||||
|
||||
#define BMP280_MODE_MASK (BIT(1) | BIT(0))
|
||||
#define BMP280_MODE_SLEEP 0
|
||||
#define BMP280_MODE_FORCED BIT(0)
|
||||
#define BMP280_MODE_NORMAL (BIT(1) | BIT(0))
|
||||
|
||||
#define BMP280_CHIP_ID 0x58
|
||||
#define BMP280_SOFT_RESET_VAL 0xB6
|
||||
|
||||
struct bmp280_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock;
|
||||
struct regmap *regmap;
|
||||
|
||||
/*
|
||||
* Carryover value from temperature conversion, used in pressure
|
||||
* calculation.
|
||||
*/
|
||||
s32 t_fine;
|
||||
};
|
||||
|
||||
/* Compensation parameters. */
|
||||
struct bmp280_comp_temp {
|
||||
u16 dig_t1;
|
||||
s16 dig_t2, dig_t3;
|
||||
};
|
||||
|
||||
struct bmp280_comp_press {
|
||||
u16 dig_p1;
|
||||
s16 dig_p2, dig_p3, dig_p4, dig_p5, dig_p6, dig_p7, dig_p8, dig_p9;
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec bmp280_channels[] = {
|
||||
{
|
||||
.type = IIO_PRESSURE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||
},
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||
},
|
||||
};
|
||||
|
||||
static bool bmp280_is_writeable_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case BMP280_REG_CONFIG:
|
||||
case BMP280_REG_CTRL_MEAS:
|
||||
case BMP280_REG_RESET:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
static bool bmp280_is_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case BMP280_REG_TEMP_XLSB:
|
||||
case BMP280_REG_TEMP_LSB:
|
||||
case BMP280_REG_TEMP_MSB:
|
||||
case BMP280_REG_PRESS_XLSB:
|
||||
case BMP280_REG_PRESS_LSB:
|
||||
case BMP280_REG_PRESS_MSB:
|
||||
case BMP280_REG_STATUS:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct regmap_config bmp280_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
|
||||
.max_register = BMP280_REG_TEMP_XLSB,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
|
||||
.writeable_reg = bmp280_is_writeable_reg,
|
||||
.volatile_reg = bmp280_is_volatile_reg,
|
||||
};
|
||||
|
||||
static int bmp280_read_compensation_temp(struct bmp280_data *data,
|
||||
struct bmp280_comp_temp *comp)
|
||||
{
|
||||
int ret;
|
||||
__le16 buf[BMP280_COMP_TEMP_REG_COUNT / 2];
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_TEMP_START,
|
||||
buf, BMP280_COMP_TEMP_REG_COUNT);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev,
|
||||
"failed to read temperature calibration parameters\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
comp->dig_t1 = (u16) le16_to_cpu(buf[0]);
|
||||
comp->dig_t2 = (s16) le16_to_cpu(buf[1]);
|
||||
comp->dig_t3 = (s16) le16_to_cpu(buf[2]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bmp280_read_compensation_press(struct bmp280_data *data,
|
||||
struct bmp280_comp_press *comp)
|
||||
{
|
||||
int ret;
|
||||
__le16 buf[BMP280_COMP_PRESS_REG_COUNT / 2];
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_PRESS_START,
|
||||
buf, BMP280_COMP_PRESS_REG_COUNT);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev,
|
||||
"failed to read pressure calibration parameters\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
comp->dig_p1 = (u16) le16_to_cpu(buf[0]);
|
||||
comp->dig_p2 = (s16) le16_to_cpu(buf[1]);
|
||||
comp->dig_p3 = (s16) le16_to_cpu(buf[2]);
|
||||
comp->dig_p4 = (s16) le16_to_cpu(buf[3]);
|
||||
comp->dig_p5 = (s16) le16_to_cpu(buf[4]);
|
||||
comp->dig_p6 = (s16) le16_to_cpu(buf[5]);
|
||||
comp->dig_p7 = (s16) le16_to_cpu(buf[6]);
|
||||
comp->dig_p8 = (s16) le16_to_cpu(buf[7]);
|
||||
comp->dig_p9 = (s16) le16_to_cpu(buf[8]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns temperature in DegC, resolution is 0.01 DegC. Output value of
|
||||
* "5123" equals 51.23 DegC. t_fine carries fine temperature as global
|
||||
* value.
|
||||
*
|
||||
* Taken from datasheet, Section 3.11.3, "Compensation formula".
|
||||
*/
|
||||
static s32 bmp280_compensate_temp(struct bmp280_data *data,
|
||||
struct bmp280_comp_temp *comp,
|
||||
s32 adc_temp)
|
||||
{
|
||||
s32 var1, var2, t;
|
||||
|
||||
var1 = (((adc_temp >> 3) - ((s32) comp->dig_t1 << 1)) *
|
||||
((s32) comp->dig_t2)) >> 11;
|
||||
var2 = (((((adc_temp >> 4) - ((s32) comp->dig_t1)) *
|
||||
((adc_temp >> 4) - ((s32) comp->dig_t1))) >> 12) *
|
||||
((s32) comp->dig_t3)) >> 14;
|
||||
|
||||
data->t_fine = var1 + var2;
|
||||
t = (data->t_fine * 5 + 128) >> 8;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format (24
|
||||
* integer bits and 8 fractional bits). Output value of "24674867"
|
||||
* represents 24674867/256 = 96386.2 Pa = 963.862 hPa
|
||||
*
|
||||
* Taken from datasheet, Section 3.11.3, "Compensation formula".
|
||||
*/
|
||||
static u32 bmp280_compensate_press(struct bmp280_data *data,
|
||||
struct bmp280_comp_press *comp,
|
||||
s32 adc_press)
|
||||
{
|
||||
s64 var1, var2, p;
|
||||
|
||||
var1 = ((s64) data->t_fine) - 128000;
|
||||
var2 = var1 * var1 * (s64) comp->dig_p6;
|
||||
var2 = var2 + ((var1 * (s64) comp->dig_p5) << 17);
|
||||
var2 = var2 + (((s64) comp->dig_p4) << 35);
|
||||
var1 = ((var1 * var1 * (s64) comp->dig_p3) >> 8) +
|
||||
((var1 * (s64) comp->dig_p2) << 12);
|
||||
var1 = (((((s64) 1) << 47) + var1)) * ((s64) comp->dig_p1) >> 33;
|
||||
|
||||
if (var1 == 0)
|
||||
return 0;
|
||||
|
||||
p = ((((s64) 1048576 - adc_press) << 31) - var2) * 3125;
|
||||
p = div64_s64(p, var1);
|
||||
var1 = (((s64) comp->dig_p9) * (p >> 13) * (p >> 13)) >> 25;
|
||||
var2 = (((s64) comp->dig_p8) * p) >> 19;
|
||||
p = ((p + var1 + var2) >> 8) + (((s64) comp->dig_p7) << 4);
|
||||
|
||||
return (u32) p;
|
||||
}
|
||||
|
||||
static int bmp280_read_temp(struct bmp280_data *data,
|
||||
int *val)
|
||||
{
|
||||
int ret;
|
||||
__be32 tmp = 0;
|
||||
s32 adc_temp, comp_temp;
|
||||
struct bmp280_comp_temp comp;
|
||||
|
||||
ret = bmp280_read_compensation_temp(data, &comp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BMP280_REG_TEMP_MSB,
|
||||
(u8 *) &tmp, 3);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "failed to read temperature\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
adc_temp = be32_to_cpu(tmp) >> 12;
|
||||
comp_temp = bmp280_compensate_temp(data, &comp, adc_temp);
|
||||
|
||||
/*
|
||||
* val might be NULL if we're called by the read_press routine,
|
||||
* who only cares about the carry over t_fine value.
|
||||
*/
|
||||
if (val) {
|
||||
*val = comp_temp * 10;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bmp280_read_press(struct bmp280_data *data,
|
||||
int *val, int *val2)
|
||||
{
|
||||
int ret;
|
||||
__be32 tmp = 0;
|
||||
s32 adc_press;
|
||||
u32 comp_press;
|
||||
struct bmp280_comp_press comp;
|
||||
|
||||
ret = bmp280_read_compensation_press(data, &comp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Read and compensate temperature so we get a reading of t_fine. */
|
||||
ret = bmp280_read_temp(data, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, BMP280_REG_PRESS_MSB,
|
||||
(u8 *) &tmp, 3);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "failed to read pressure\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
adc_press = be32_to_cpu(tmp) >> 12;
|
||||
comp_press = bmp280_compensate_press(data, &comp, adc_press);
|
||||
|
||||
*val = comp_press;
|
||||
*val2 = 256000;
|
||||
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
}
|
||||
|
||||
static int bmp280_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
int ret;
|
||||
struct bmp280_data *data = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
switch (chan->type) {
|
||||
case IIO_PRESSURE:
|
||||
ret = bmp280_read_press(data, val, val2);
|
||||
break;
|
||||
case IIO_TEMP:
|
||||
ret = bmp280_read_temp(data, val);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info bmp280_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &bmp280_read_raw,
|
||||
};
|
||||
|
||||
static int bmp280_chip_init(struct bmp280_data *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(data->regmap, BMP280_REG_CTRL_MEAS,
|
||||
BMP280_OSRS_TEMP_MASK |
|
||||
BMP280_OSRS_PRESS_MASK |
|
||||
BMP280_MODE_MASK,
|
||||
BMP280_OSRS_TEMP_2X |
|
||||
BMP280_OSRS_PRESS_16X |
|
||||
BMP280_MODE_NORMAL);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev,
|
||||
"failed to write config register\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(data->regmap, BMP280_REG_CONFIG,
|
||||
BMP280_FILTER_MASK,
|
||||
BMP280_FILTER_4X);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev,
|
||||
"failed to write config register\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bmp280_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct iio_dev *indio_dev;
|
||||
struct bmp280_data *data;
|
||||
unsigned int chip_id;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data = iio_priv(indio_dev);
|
||||
mutex_init(&data->lock);
|
||||
data->client = client;
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->channels = bmp280_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(bmp280_channels);
|
||||
indio_dev->info = &bmp280_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
data->regmap = devm_regmap_init_i2c(client, &bmp280_regmap_config);
|
||||
if (IS_ERR(data->regmap)) {
|
||||
dev_err(&client->dev, "failed to allocate register map\n");
|
||||
return PTR_ERR(data->regmap);
|
||||
}
|
||||
|
||||
ret = regmap_read(data->regmap, BMP280_REG_ID, &chip_id);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (chip_id != BMP280_CHIP_ID) {
|
||||
dev_err(&client->dev, "bad chip id. expected %x got %x\n",
|
||||
BMP280_CHIP_ID, chip_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = bmp280_chip_init(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct acpi_device_id bmp280_acpi_match[] = {
|
||||
{"BMP0280", 0},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, bmp280_acpi_match);
|
||||
|
||||
static const struct i2c_device_id bmp280_id[] = {
|
||||
{"bmp280", 0},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, bmp280_id);
|
||||
|
||||
static struct i2c_driver bmp280_driver = {
|
||||
.driver = {
|
||||
.name = "bmp280",
|
||||
.acpi_match_table = ACPI_PTR(bmp280_acpi_match),
|
||||
},
|
||||
.probe = bmp280_probe,
|
||||
.id_table = bmp280_id,
|
||||
};
|
||||
module_i2c_driver(bmp280_driver);
|
||||
|
||||
MODULE_AUTHOR("Vlad Dogaru <vlad.dogaru@intel.com>");
|
||||
MODULE_DESCRIPTION("Driver for Bosch Sensortec BMP280 pressure and temperature sensor");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -26,8 +26,7 @@ static const struct st_sensors_platform_data default_press_pdata = {
|
||||
.drdy_int_pin = 1,
|
||||
};
|
||||
|
||||
int st_press_common_probe(struct iio_dev *indio_dev,
|
||||
struct st_sensors_platform_data *pdata);
|
||||
int st_press_common_probe(struct iio_dev *indio_dev);
|
||||
void st_press_common_remove(struct iio_dev *indio_dev);
|
||||
|
||||
#ifdef CONFIG_IIO_BUFFER
|
||||
|
@ -38,10 +38,10 @@ static int st_press_buffer_preenable(struct iio_dev *indio_dev)
|
||||
static int st_press_buffer_postenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *pdata = iio_priv(indio_dev);
|
||||
struct st_sensor_data *press_data = iio_priv(indio_dev);
|
||||
|
||||
pdata->buffer_data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
|
||||
if (pdata->buffer_data == NULL) {
|
||||
press_data->buffer_data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
|
||||
if (press_data->buffer_data == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto allocate_memory_error;
|
||||
}
|
||||
@ -53,7 +53,7 @@ static int st_press_buffer_postenable(struct iio_dev *indio_dev)
|
||||
return err;
|
||||
|
||||
st_press_buffer_postenable_error:
|
||||
kfree(pdata->buffer_data);
|
||||
kfree(press_data->buffer_data);
|
||||
allocate_memory_error:
|
||||
return err;
|
||||
}
|
||||
@ -61,7 +61,7 @@ allocate_memory_error:
|
||||
static int st_press_buffer_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *pdata = iio_priv(indio_dev);
|
||||
struct st_sensor_data *press_data = iio_priv(indio_dev);
|
||||
|
||||
err = iio_triggered_buffer_predisable(indio_dev);
|
||||
if (err < 0)
|
||||
@ -70,7 +70,7 @@ static int st_press_buffer_predisable(struct iio_dev *indio_dev)
|
||||
err = st_sensors_set_enable(indio_dev, false);
|
||||
|
||||
st_press_buffer_predisable_error:
|
||||
kfree(pdata->buffer_data);
|
||||
kfree(press_data->buffer_data);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -175,7 +175,7 @@ static const struct iio_chan_spec st_press_lps001wp_channels[] = {
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1)
|
||||
};
|
||||
|
||||
static const struct st_sensors st_press_sensors[] = {
|
||||
static const struct st_sensor_settings st_press_sensors_settings[] = {
|
||||
{
|
||||
.wai = ST_PRESS_LPS331AP_WAI_EXP,
|
||||
.sensors_supported = {
|
||||
@ -333,7 +333,7 @@ static int st_press_read_raw(struct iio_dev *indio_dev,
|
||||
int *val2, long mask)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *pdata = iio_priv(indio_dev);
|
||||
struct st_sensor_data *press_data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
@ -347,10 +347,10 @@ static int st_press_read_raw(struct iio_dev *indio_dev,
|
||||
|
||||
switch (ch->type) {
|
||||
case IIO_PRESSURE:
|
||||
*val2 = pdata->current_fullscale->gain;
|
||||
*val2 = press_data->current_fullscale->gain;
|
||||
break;
|
||||
case IIO_TEMP:
|
||||
*val2 = pdata->current_fullscale->gain2;
|
||||
*val2 = press_data->current_fullscale->gain2;
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
@ -371,7 +371,7 @@ static int st_press_read_raw(struct iio_dev *indio_dev,
|
||||
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val = pdata->odr;
|
||||
*val = press_data->odr;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@ -409,11 +409,10 @@ static const struct iio_trigger_ops st_press_trigger_ops = {
|
||||
#define ST_PRESS_TRIGGER_OPS NULL
|
||||
#endif
|
||||
|
||||
int st_press_common_probe(struct iio_dev *indio_dev,
|
||||
struct st_sensors_platform_data *plat_data)
|
||||
int st_press_common_probe(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *pdata = iio_priv(indio_dev);
|
||||
int irq = pdata->get_irq_data_ready(indio_dev);
|
||||
struct st_sensor_data *press_data = iio_priv(indio_dev);
|
||||
int irq = press_data->get_irq_data_ready(indio_dev);
|
||||
int err;
|
||||
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
@ -422,28 +421,30 @@ int st_press_common_probe(struct iio_dev *indio_dev,
|
||||
st_sensors_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_settings),
|
||||
st_press_sensors_settings);
|
||||
if (err < 0)
|
||||
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 = pdata->sensor->num_ch;
|
||||
press_data->num_data_channels = ST_PRESS_NUMBER_DATA_CHANNELS;
|
||||
press_data->multiread_bit = press_data->sensor_settings->multi_read_bit;
|
||||
indio_dev->channels = press_data->sensor_settings->ch;
|
||||
indio_dev->num_channels = press_data->sensor_settings->num_ch;
|
||||
|
||||
if (pdata->sensor->fs.addr != 0)
|
||||
pdata->current_fullscale = (struct st_sensor_fullscale_avl *)
|
||||
&pdata->sensor->fs.fs_avl[0];
|
||||
if (press_data->sensor_settings->fs.addr != 0)
|
||||
press_data->current_fullscale =
|
||||
(struct st_sensor_fullscale_avl *)
|
||||
&press_data->sensor_settings->fs.fs_avl[0];
|
||||
|
||||
pdata->odr = pdata->sensor->odr.odr_avl[0].hz;
|
||||
press_data->odr = press_data->sensor_settings->odr.odr_avl[0].hz;
|
||||
|
||||
/* Some devices don't support a data ready pin. */
|
||||
if (!plat_data && pdata->sensor->drdy_irq.addr)
|
||||
plat_data =
|
||||
if (!press_data->dev->platform_data &&
|
||||
press_data->sensor_settings->drdy_irq.addr)
|
||||
press_data->dev->platform_data =
|
||||
(struct st_sensors_platform_data *)&default_press_pdata;
|
||||
|
||||
err = st_sensors_init_sensor(indio_dev, plat_data);
|
||||
err = st_sensors_init_sensor(indio_dev, press_data->dev->platform_data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
@ -479,12 +480,12 @@ EXPORT_SYMBOL(st_press_common_probe);
|
||||
|
||||
void st_press_common_remove(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *pdata = iio_priv(indio_dev);
|
||||
struct st_sensor_data *press_data = iio_priv(indio_dev);
|
||||
|
||||
st_sensors_power_disable(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
if (pdata->get_irq_data_ready(indio_dev) > 0)
|
||||
if (press_data->get_irq_data_ready(indio_dev) > 0)
|
||||
st_sensors_deallocate_trigger(indio_dev);
|
||||
|
||||
st_press_deallocate_ring(indio_dev);
|
||||
|
@ -43,20 +43,19 @@ static int st_press_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct st_sensor_data *pdata;
|
||||
struct st_sensor_data *press_data;
|
||||
int err;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*pdata));
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*press_data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
pdata = iio_priv(indio_dev);
|
||||
pdata->dev = &client->dev;
|
||||
press_data = iio_priv(indio_dev);
|
||||
st_sensors_of_i2c_probe(client, st_press_of_match);
|
||||
|
||||
st_sensors_i2c_configure(indio_dev, client, pdata);
|
||||
st_sensors_i2c_configure(indio_dev, client, press_data);
|
||||
|
||||
err = st_press_common_probe(indio_dev, client->dev.platform_data);
|
||||
err = st_press_common_probe(indio_dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -21,19 +21,18 @@
|
||||
static int st_press_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct st_sensor_data *pdata;
|
||||
struct st_sensor_data *press_data;
|
||||
int err;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*pdata));
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*press_data));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
pdata = iio_priv(indio_dev);
|
||||
pdata->dev = &spi->dev;
|
||||
press_data = iio_priv(indio_dev);
|
||||
|
||||
st_sensors_spi_configure(indio_dev, spi, pdata);
|
||||
st_sensors_spi_configure(indio_dev, spi, press_data);
|
||||
|
||||
err = st_press_common_probe(indio_dev, spi->dev.platform_data);
|
||||
err = st_press_common_probe(indio_dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -95,7 +95,7 @@ static int as3935_read(struct as3935_state *st, unsigned int reg, int *val)
|
||||
*val = ret;
|
||||
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
static int as3935_write(struct as3935_state *st,
|
||||
unsigned int reg,
|
||||
@ -107,7 +107,7 @@ static int as3935_write(struct as3935_state *st,
|
||||
buf[1] = val;
|
||||
|
||||
return spi_write(st->spi, buf, 2);
|
||||
};
|
||||
}
|
||||
|
||||
static ssize_t as3935_sensor_sensitivity_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
@ -122,7 +122,7 @@ static ssize_t as3935_sensor_sensitivity_show(struct device *dev,
|
||||
val = (val & AS3935_AFE_MASK) >> 1;
|
||||
|
||||
return sprintf(buf, "%d\n", val);
|
||||
};
|
||||
}
|
||||
|
||||
static ssize_t as3935_sensor_sensitivity_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
@ -142,7 +142,7 @@ static ssize_t as3935_sensor_sensitivity_store(struct device *dev,
|
||||
as3935_write(st, AS3935_AFE_GAIN, val << 1);
|
||||
|
||||
return len;
|
||||
};
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(sensor_sensitivity, S_IRUGO | S_IWUSR,
|
||||
as3935_sensor_sensitivity_show, as3935_sensor_sensitivity_store, 0);
|
||||
@ -214,7 +214,7 @@ err_read:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
};
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops iio_interrupt_trigger_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
@ -238,7 +238,7 @@ static void as3935_event_work(struct work_struct *work)
|
||||
dev_warn(&st->spi->dev, "noise level is too high");
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static irqreturn_t as3935_interrupt_handler(int irq, void *private)
|
||||
{
|
||||
@ -417,7 +417,7 @@ unregister_trigger:
|
||||
iio_trigger_unregister(st->trig);
|
||||
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
|
||||
static int as3935_remove(struct spi_device *spi)
|
||||
{
|
||||
@ -429,7 +429,7 @@ static int as3935_remove(struct spi_device *spi)
|
||||
iio_trigger_unregister(st->trig);
|
||||
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
static const struct spi_device_id as3935_id[] = {
|
||||
{"as3935", 0},
|
||||
|
@ -162,11 +162,12 @@ int main(int argc, char **argv)
|
||||
char *buffer_access;
|
||||
int scan_size;
|
||||
int noevents = 0;
|
||||
int notrigger = 0;
|
||||
char *dummy;
|
||||
|
||||
struct iio_channel_info *channels;
|
||||
|
||||
while ((c = getopt(argc, argv, "l:w:c:et:n:")) != -1) {
|
||||
while ((c = getopt(argc, argv, "l:w:c:et:n:g")) != -1) {
|
||||
switch (c) {
|
||||
case 'n':
|
||||
device_name = optarg;
|
||||
@ -187,6 +188,9 @@ int main(int argc, char **argv)
|
||||
case 'l':
|
||||
buf_len = strtoul(optarg, &dummy, 10);
|
||||
break;
|
||||
case 'g':
|
||||
notrigger = 1;
|
||||
break;
|
||||
case '?':
|
||||
return -1;
|
||||
}
|
||||
@ -205,11 +209,13 @@ int main(int argc, char **argv)
|
||||
printf("iio device number being used is %d\n", dev_num);
|
||||
|
||||
asprintf(&dev_dir_name, "%siio:device%d", iio_dir, dev_num);
|
||||
|
||||
if (!notrigger) {
|
||||
if (trigger_name == NULL) {
|
||||
/*
|
||||
* Build the trigger name. If it is device associated its
|
||||
* name is <device_name>_dev[n] where n matches the device
|
||||
* number found above
|
||||
* Build the trigger name. If it is device associated
|
||||
* its name is <device_name>_dev[n] where n matches
|
||||
* the device number found above.
|
||||
*/
|
||||
ret = asprintf(&trigger_name,
|
||||
"%s-dev%d", device_name, dev_num);
|
||||
@ -227,6 +233,8 @@ int main(int argc, char **argv)
|
||||
goto error_free_triggername;
|
||||
}
|
||||
printf("iio trigger number being used is %d\n", trig_num);
|
||||
} else
|
||||
printf("trigger-less mode selected\n");
|
||||
|
||||
/*
|
||||
* Parse the files in scan_elements to identify what channels are
|
||||
@ -250,8 +258,11 @@ int main(int argc, char **argv)
|
||||
ret = -ENOMEM;
|
||||
goto error_free_triggername;
|
||||
}
|
||||
|
||||
if (!notrigger) {
|
||||
printf("%s %s\n", dev_dir_name, trigger_name);
|
||||
/* Set the device trigger to be the data ready trigger found above */
|
||||
/* Set the device trigger to be the data ready trigger found
|
||||
* above */
|
||||
ret = write_sysfs_string_and_verify("trigger/current_trigger",
|
||||
dev_dir_name,
|
||||
trigger_name);
|
||||
@ -259,6 +270,7 @@ int main(int argc, char **argv)
|
||||
printf("Failed to write current_trigger file\n");
|
||||
goto error_free_buf_dir_name;
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup ring buffer parameters */
|
||||
ret = write_sysfs_int("length", buf_dir_name, buf_len);
|
||||
@ -327,6 +339,7 @@ int main(int argc, char **argv)
|
||||
if (ret < 0)
|
||||
goto error_close_buffer_access;
|
||||
|
||||
if (!notrigger)
|
||||
/* Disconnect the trigger - just write a dummy name. */
|
||||
write_sysfs_string("trigger/current_trigger",
|
||||
dev_dir_name, "NULL");
|
||||
|
@ -35,6 +35,7 @@ irqreturn_t lis3l02dq_data_rdy_trig_poll(int irq, void *private)
|
||||
iio_trigger_poll(st->trig);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
|
@ -164,7 +164,7 @@ struct st_sensor_transfer_function {
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_sensors - ST sensors list
|
||||
* struct st_sensor_settings - ST specific sensor settings
|
||||
* @wai: Contents of WhoAmI register.
|
||||
* @sensors_supported: List of supported sensors by struct itself.
|
||||
* @ch: IIO channels for the sensor.
|
||||
@ -177,7 +177,7 @@ struct st_sensor_transfer_function {
|
||||
* @multi_read_bit: Use or not particular bit for [I2C/SPI] multi-read.
|
||||
* @bootime: samples to discard when sensor passing from power-down to power-up.
|
||||
*/
|
||||
struct st_sensors {
|
||||
struct st_sensor_settings {
|
||||
u8 wai;
|
||||
char sensors_supported[ST_SENSORS_MAX_4WAI][ST_SENSORS_MAX_NAME];
|
||||
struct iio_chan_spec *ch;
|
||||
@ -196,7 +196,7 @@ struct st_sensors {
|
||||
* struct st_sensor_data - ST sensor device status
|
||||
* @dev: Pointer to instance of struct device (I2C or SPI).
|
||||
* @trig: The trigger in use by the core driver.
|
||||
* @sensor: Pointer to the current sensor struct in use.
|
||||
* @sensor_settings: Pointer to the specific sensor settings in use.
|
||||
* @current_fullscale: Maximum range of measure by the sensor.
|
||||
* @vdd: Pointer to sensor's Vdd power supply
|
||||
* @vdd_io: Pointer to sensor's Vdd-IO power supply
|
||||
@ -213,7 +213,7 @@ struct st_sensors {
|
||||
struct st_sensor_data {
|
||||
struct device *dev;
|
||||
struct iio_trigger *trig;
|
||||
struct st_sensors *sensor;
|
||||
struct st_sensor_settings *sensor_settings;
|
||||
struct st_sensor_fullscale_avl *current_fullscale;
|
||||
struct regulator *vdd;
|
||||
struct regulator *vdd_io;
|
||||
@ -279,7 +279,7 @@ int st_sensors_read_info_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *ch, int *val);
|
||||
|
||||
int st_sensors_check_device_support(struct iio_dev *indio_dev,
|
||||
int num_sensors_list, const struct st_sensors *sensors);
|
||||
int num_sensors_list, const struct st_sensor_settings *sensor_settings);
|
||||
|
||||
ssize_t st_sensors_sysfs_sampling_frequency_avail(struct device *dev,
|
||||
struct device_attribute *attr, char *buf);
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/iio/types.h>
|
||||
#include <linux/of.h>
|
||||
/* IIO TODO LIST */
|
||||
/*
|
||||
* Provide means of adjusting timer accuracy.
|
||||
@ -326,6 +327,11 @@ struct iio_dev;
|
||||
* @update_scan_mode: function to configure device and scan buffer when
|
||||
* channels have changed
|
||||
* @debugfs_reg_access: function to read or write register value of device
|
||||
* @of_xlate: function pointer to obtain channel specifier index.
|
||||
* When #iio-cells is greater than '0', the driver could
|
||||
* provide a custom of_xlate function that reads the
|
||||
* *args* and returns the appropriate index in registered
|
||||
* IIO channels array.
|
||||
**/
|
||||
struct iio_info {
|
||||
struct module *driver_module;
|
||||
@ -385,6 +391,8 @@ struct iio_info {
|
||||
int (*debugfs_reg_access)(struct iio_dev *indio_dev,
|
||||
unsigned reg, unsigned writeval,
|
||||
unsigned *readval);
|
||||
int (*of_xlate)(struct iio_dev *indio_dev,
|
||||
const struct of_phandle_args *iiospec);
|
||||
};
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user