First round of new device support, features and cleanups for IIO in the 4.11 cycle.
It's shaping to be another fairly busy cycle. Lots more on the way! New device support * ads7950 - new driver supporting ads7950, ads7951, ads7952, ads7953, ads7954, ads7955, ads7956, ads7957, ads7958, ads7959, ads7960, and ads7961 ADCs. * cm3605 - New driver for this light sensor and proximity sensor which is an analog part with some additional digital controls. * hx711 - New driver. Core new stuff * Gravity sensor type. This is a processed datastream in which the device will try to work out which way is down. * Split the buffer.h file into two parts. One provides the interface to 'use' a buffer, the second provides the internals of the buffer functionality as needed by implementations of buffers. - Move documentation inline so as to allow use of private: tag when generating documentation. - Add some utility functions for the few things that are directly done with the buffers. - Stop exporting functions that no-one uses outside of the core code. - Push docs down by the code in the c file where they should have always been. - Fix typo in kernel-doc for buffer. - push down some includes that were previously happening implicitly. - stop enabling the timestamp of the dummy device. Features and cleanups * ad5592r - ACPI support * ad5593r -ACPI support. * ad5933 - Fix a false comment about size of a particular register. * ad7150 - replace S_IRUGO | S_IWUSR with 0644. I'm not that keen on these patches in general, but as it was nicely presented I took this one anyway. As a general rule will only take these as part of a larger driver cleanup. - don't eat an error but rather reutnr it in the write_event_config callback. * ad7606 - replace non standard range attibute with _scale * ade7753 - use usleep_range for short sleeps * ade7754 - use usleep_range for short sleeps * ade7758 - use usleep_range for short sleeps * ade7759 - use usleep_range for short sleeps *ade7854
- use usleep_range for short sleeps * adis16201 - fix description * adis16203 - fix description - fix copyright year * adis16209 - fix description * adt7316 - Add braces to arms of if else statement (for consistency) - Alignment fixes. * axp288 - Fix up an issue with accidental overwrites of data. * bmi160 - add deivce tables for i2c and spi to support correctly identifying the full dt name (including manufacturer). - device tree binding. * bmp280 - use usleep_range for short sleeps. * cm3232 - return error from cm3232_reg_init rather than eating it if the last write fails. * dummy driver - remove a semicolor found at end of a function defintition. * exynos-adc - use usleep_range for short sleeps. * hid-sensor (accel) - Add timestamp support. The hardware can provide timestamps so lets support them. If not fall back to timestamps estimated in kernel. * hid-sensor (light) - Add a duplicate ID for the light channels so as to keep existing interface whilst also using the more standard IIO interface. * hts221 - acpi probing * imx25-gcq - Add a macro call to allow this driver to be automatically loaded. * isl29028 - reorganise code to avoid deep nesting of if statements. - move chip test and default regs into a function suitable or sharing with power management code. - tidy up some code alignment. * lidar-lite-v3 - introduce compatible strings that make it clear Garmin have consideral friends. * mma8452 - avoid returning signed value when unsigned is appropriate * spmi-vadc - Update function for generic voltage conversion to take into account that different channels on this device should be handled differently. - Rework code to allow per channel voltage scaling and support the standard options for this hardware. - Fixup three minor issues with the above patches for this part. These all effect test builds rather than the native builds for the part, but good to clean them up anyway. * st_sensors - support device matching from the ACPI DST tables. - acpi based probing for accelerometers - acpi based probing for pressure sensors - Allow pressure sensors to read negative values. - Export sampling frequency for lps25h and lps331ap. - Add support for the old DT bindings from the period when these deivces were often supported through windows. Docs fixup: * typo in sysfs-bus-iio -----BEGIN PGP SIGNATURE----- iQJFBAABCAAvFiEEbilms4eEBlKRJoGxVIU0mcT0FogFAlh2bhcRHGppYzIzQGtl cm5lbC5vcmcACgkQVIU0mcT0FogdSQ/9Hyd3ic5AgWyDKr9JI1vJPj3kT0PIYHLk 4ucbQ8HUJQBBLnsausTb+UwuGV277DkVjGVsI4epn59H3CvkNh3Egl3XCmbhIpt2 oyl9Pw9dSon6n/7I5pNSwmqD45O+aB7qFLigIfLmciwpMHZly3ExzGBMpqUkgjf9 bVzndDqrfoNIrU7UaMD6QgrSxrgYJJJJwBDig/0zjAkGvu9L8a8ghrKkrRzMy62O nGP5mmEagivFF9lpGzzrw2pYBQWY4AFbsNvuLElE7nUz0PKKG+9J0oaBiglTYv2p mkNlYl7iTZfI3eFOXa+7HdSmeNhYX2dvbEM2jAA/Mr6ojWg2mSRu7Y19Kl2KP7K7 BDRKBw+Cp0wkVvJ8LU/6PiDSmqp09VfjjqevzVGJi/BmWLzG0Mi2OsQmmynbw2SV hxiGETRV7vBoyJWDJuwJoftZaRnHWJbiQ6ftEUbWOFA9RIBMgOJBiL+RtxerJkHv FWlCFpv/618TUB/uM15+EOLVJAT5b7K+6+l6EGEjWdjiS/7vL7QM5DL8w4zV43zz cQ98UeIlqCkvoFmx9uHbl44COLQgzNtxAjytLxqbsOmEb40wlzoNzgSoUGu73SaG 8qwznH0w0p8P1nyzwe9qKKK6SmFhj/tF6jKVmIi/H7wJdA1J5dAAmwFUBSxLs2DX u1eM3ikCl/M= =yjUA -----END PGP SIGNATURE----- Merge tag 'iio-for-4.11a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next Jonathan writes: First round of new device support, features and cleanups for IIO in the 4.11 cycle. It's shaping to be another fairly busy cycle. Lots more on the way! New device support * ads7950 - new driver supporting ads7950, ads7951, ads7952, ads7953, ads7954, ads7955, ads7956, ads7957, ads7958, ads7959, ads7960, and ads7961 ADCs. * cm3605 - New driver for this light sensor and proximity sensor which is an analog part with some additional digital controls. * hx711 - New driver. Core new stuff * Gravity sensor type. This is a processed datastream in which the device will try to work out which way is down. * Split the buffer.h file into two parts. One provides the interface to 'use' a buffer, the second provides the internals of the buffer functionality as needed by implementations of buffers. - Move documentation inline so as to allow use of private: tag when generating documentation. - Add some utility functions for the few things that are directly done with the buffers. - Stop exporting functions that no-one uses outside of the core code. - Push docs down by the code in the c file where they should have always been. - Fix typo in kernel-doc for buffer. - push down some includes that were previously happening implicitly. - stop enabling the timestamp of the dummy device. Features and cleanups * ad5592r - ACPI support * ad5593r -ACPI support. * ad5933 - Fix a false comment about size of a particular register. * ad7150 - replace S_IRUGO | S_IWUSR with 0644. I'm not that keen on these patches in general, but as it was nicely presented I took this one anyway. As a general rule will only take these as part of a larger driver cleanup. - don't eat an error but rather reutnr it in the write_event_config callback. * ad7606 - replace non standard range attibute with _scale * ade7753 - use usleep_range for short sleeps * ade7754 - use usleep_range for short sleeps * ade7758 - use usleep_range for short sleeps * ade7759 - use usleep_range for short sleeps *ade7854
- use usleep_range for short sleeps * adis16201 - fix description * adis16203 - fix description - fix copyright year * adis16209 - fix description * adt7316 - Add braces to arms of if else statement (for consistency) - Alignment fixes. * axp288 - Fix up an issue with accidental overwrites of data. * bmi160 - add deivce tables for i2c and spi to support correctly identifying the full dt name (including manufacturer). - device tree binding. * bmp280 - use usleep_range for short sleeps. * cm3232 - return error from cm3232_reg_init rather than eating it if the last write fails. * dummy driver - remove a semicolor found at end of a function defintition. * exynos-adc - use usleep_range for short sleeps. * hid-sensor (accel) - Add timestamp support. The hardware can provide timestamps so lets support them. If not fall back to timestamps estimated in kernel. * hid-sensor (light) - Add a duplicate ID for the light channels so as to keep existing interface whilst also using the more standard IIO interface. * hts221 - acpi probing * imx25-gcq - Add a macro call to allow this driver to be automatically loaded. * isl29028 - reorganise code to avoid deep nesting of if statements. - move chip test and default regs into a function suitable or sharing with power management code. - tidy up some code alignment. * lidar-lite-v3 - introduce compatible strings that make it clear Garmin have consideral friends. * mma8452 - avoid returning signed value when unsigned is appropriate * spmi-vadc - Update function for generic voltage conversion to take into account that different channels on this device should be handled differently. - Rework code to allow per channel voltage scaling and support the standard options for this hardware. - Fixup three minor issues with the above patches for this part. These all effect test builds rather than the native builds for the part, but good to clean them up anyway. * st_sensors - support device matching from the ACPI DST tables. - acpi based probing for accelerometers - acpi based probing for pressure sensors - Allow pressure sensors to read negative values. - Export sampling frequency for lps25h and lps331ap. - Add support for the old DT bindings from the period when these deivces were often supported through windows. Docs fixup: * typo in sysfs-bus-iio
This commit is contained in:
commit
4463c3e72d
|
@ -170,6 +170,16 @@ Description:
|
|||
Has all of the equivalent parameters as per voltageY. Units
|
||||
after application of scale and offset are m/s^2.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_gravity_x_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_gravity_y_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_gravity_z_raw
|
||||
KernelVersion: 4.11
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Gravity in direction x, y or z (may be arbitrarily assigned
|
||||
but should match other such assignments on device).
|
||||
Units after application of scale and offset are m/s^2.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_x_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_y_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_anglvel_z_raw
|
||||
|
@ -805,7 +815,7 @@ Description:
|
|||
attribute. E.g. if in_voltage0_raw_thresh_rising_value is set to 1200
|
||||
and in_voltage0_raw_thresh_rising_hysteresis is set to 50. The event
|
||||
will get activated once in_voltage0_raw goes above 1200 and will become
|
||||
deactived again once the value falls below 1150.
|
||||
deactivated again once the value falls below 1150.
|
||||
|
||||
What: /sys/.../events/in_accel_x_raw_roc_rising_value
|
||||
What: /sys/.../events/in_accel_x_raw_roc_falling_value
|
||||
|
|
|
@ -5,7 +5,7 @@ that apply in on the generic device (independent from the bus).
|
|||
|
||||
|
||||
Required properties for the SPI bindings:
|
||||
- compatible: should be set to "st,lis3lv02d_spi"
|
||||
- compatible: should be set to "st,lis3lv02d-spi"
|
||||
- reg: the chipselect index
|
||||
- spi-max-frequency: maximal bus speed, should be set to 1000000 unless
|
||||
constrained by external circuitry
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
* AVIA HX711 ADC chip for weight cells
|
||||
Bit-banging driver
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "avia,hx711"
|
||||
- sck-gpios: Definition of the GPIO for the clock
|
||||
- dout-gpios: Definition of the GPIO for data-out
|
||||
See Documentation/devicetree/bindings/gpio/gpio.txt
|
||||
- avdd-supply: Definition of the regulator used as analog supply
|
||||
|
||||
Example:
|
||||
weight@0 {
|
||||
compatible = "avia,hx711";
|
||||
sck-gpios = <&gpio3 10 GPIO_ACTIVE_HIGH>;
|
||||
dout-gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>;
|
||||
avdd-suppy = <&avdd>;
|
||||
};
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
Bosch BMI160 - Inertial Measurement Unit with Accelerometer, Gyroscope
|
||||
and externally connectable Magnetometer
|
||||
|
||||
https://www.bosch-sensortec.com/bst/products/all_products/bmi160
|
||||
|
||||
Required properties:
|
||||
- compatible : should be "bosch,bmi160"
|
||||
- reg : the I2C address or SPI chip select number of the sensor
|
||||
- spi-max-frequency : set maximum clock frequency (only for SPI)
|
||||
|
||||
Optional properties:
|
||||
- interrupt-parent : should be the phandle of the interrupt controller
|
||||
- interrupts : interrupt mapping for IRQ, must be IRQ_TYPE_LEVEL_LOW
|
||||
- interrupt-names : set to "INT1" if INT1 pin should be used as interrupt
|
||||
input, set to "INT2" if INT2 pin should be used instead
|
||||
|
||||
Examples:
|
||||
|
||||
bmi160@68 {
|
||||
compatible = "bosch,bmi160";
|
||||
reg = <0x68>;
|
||||
|
||||
interrupt-parent = <&gpio4>;
|
||||
interrupts = <12 IRQ_TYPE_LEVEL_LOW>;
|
||||
interrupt-names = "INT1";
|
||||
};
|
||||
|
||||
bmi160@0 {
|
||||
compatible = "bosch,bmi160";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <10000000>;
|
||||
|
||||
interrupt-parent = <&gpio2>;
|
||||
interrupts = <12 IRQ_TYPE_LEVEL_LOW>;
|
||||
interrupt-names = "INT2";
|
||||
};
|
|
@ -0,0 +1,41 @@
|
|||
Capella Microsystems CM3605
|
||||
Ambient Light and Short Distance Proximity Sensor
|
||||
|
||||
The CM3605 is an entirely analog part which however require quite a bit of
|
||||
software logic to interface a host operating system.
|
||||
|
||||
This ALS and proximity sensor was one of the very first deployed in mobile
|
||||
handsets, notably it is used in the very first Nexus One Android phone from
|
||||
2010.
|
||||
|
||||
Required properties:
|
||||
- compatible: must be: "capella,cm3605"
|
||||
- aset-gpios: GPIO line controlling the ASET line (drive low
|
||||
to activate the ALS, should be flagged GPIO_ACTIVE_LOW)
|
||||
- interrupts: the IRQ line (such as a GPIO) that is connected to
|
||||
the POUT (proximity sensor out) line. The edge detection must
|
||||
be set to IRQ_TYPE_EDGE_BOTH so as to detect movements toward
|
||||
and away from the proximity sensor.
|
||||
- io-channels: the ADC channel used for converting the voltage from
|
||||
AOUT to a digital representation.
|
||||
- io-channel-names: must be "aout"
|
||||
|
||||
Optional properties:
|
||||
- vdd-supply: regulator supplying VDD power to the component.
|
||||
- capella,aset-resistance-ohms: the sensitivity calibration resistance,
|
||||
in Ohms. Valid values are: 50000, 100000, 300000 and 600000,
|
||||
as these are the resistance values that we are supplied with
|
||||
calibration curves for. If not supplied, 100 kOhm will be assumed
|
||||
but it is strongly recommended to supply this.
|
||||
|
||||
Example:
|
||||
|
||||
cm3605 {
|
||||
compatible = "capella,cm3605";
|
||||
vdd-supply = <&foo_reg>;
|
||||
aset-gpios = <&foo_gpio 1 GPIO_ACTIVE_LOW>;
|
||||
capella,aset-resistance-ohms = <100000>;
|
||||
interrupts = <1 IRQ_TYPE_EDGE_BOTH>;
|
||||
io-channels = <&adc 0x01>;
|
||||
io-channel-names = "aout";
|
||||
};
|
|
@ -27,6 +27,8 @@ standard bindings from pinctrl/pinctrl-bindings.txt.
|
|||
Valid compatible strings:
|
||||
|
||||
Accelerometers:
|
||||
- st,lis3lv02d (deprecated, use st,lis3lv02dl-accel)
|
||||
- st,lis302dl-spi (deprecated, use st,lis3lv02dl-accel)
|
||||
- st,lis3lv02dl-accel
|
||||
- st,lsm303dlh-accel
|
||||
- st,lsm303dlhc-accel
|
||||
|
|
|
@ -40,6 +40,7 @@ atmel Atmel Corporation
|
|||
auo AU Optronics Corporation
|
||||
auvidea Auvidea GmbH
|
||||
avago Avago Technologies
|
||||
avia avia semiconductor
|
||||
avic Shanghai AVIC Optoelectronics Co., Ltd.
|
||||
axentia Axentia Technologies AB
|
||||
axis Axis Communications AB
|
||||
|
@ -107,6 +108,7 @@ firefly Firefly
|
|||
focaltech FocalTech Systems Co.,Ltd
|
||||
friendlyarm Guangzhou FriendlyARM Computer Tech Co., Ltd
|
||||
fsl Freescale Semiconductor
|
||||
grmn Garmin Limited
|
||||
ge General Electric Company
|
||||
geekbuying GeekBuying
|
||||
gef GE Fanuc Intelligent Platforms Embedded Systems, Inc.
|
||||
|
|
|
@ -140,11 +140,13 @@ config IIO_ST_ACCEL_3AXIS
|
|||
|
||||
config IIO_ST_ACCEL_I2C_3AXIS
|
||||
tristate
|
||||
depends on !SENSORS_LIS3_I2C
|
||||
depends on IIO_ST_ACCEL_3AXIS
|
||||
depends on IIO_ST_SENSORS_I2C
|
||||
|
||||
config IIO_ST_ACCEL_SPI_3AXIS
|
||||
tristate
|
||||
depends on !SENSORS_LIS3_SPI
|
||||
depends on IIO_ST_ACCEL_3AXIS
|
||||
depends on IIO_ST_SENSORS_SPI
|
||||
|
||||
|
|
|
@ -1638,7 +1638,8 @@ int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq,
|
|||
if (block_supported) {
|
||||
indio_dev->modes |= INDIO_BUFFER_SOFTWARE;
|
||||
indio_dev->info = &bmc150_accel_info_fifo;
|
||||
indio_dev->buffer->attrs = bmc150_accel_fifo_attributes;
|
||||
iio_buffer_set_attrs(indio_dev->buffer,
|
||||
bmc150_accel_fifo_attributes);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,11 +42,13 @@ struct accel_3d_state {
|
|||
struct hid_sensor_hub_callbacks callbacks;
|
||||
struct hid_sensor_common common_attributes;
|
||||
struct hid_sensor_hub_attribute_info accel[ACCEL_3D_CHANNEL_MAX];
|
||||
u32 accel_val[ACCEL_3D_CHANNEL_MAX];
|
||||
/* Reserve for 3 channels + padding + timestamp */
|
||||
u32 accel_val[ACCEL_3D_CHANNEL_MAX + 3];
|
||||
int scale_pre_decml;
|
||||
int scale_post_decml;
|
||||
int scale_precision;
|
||||
int value_offset;
|
||||
int64_t timestamp;
|
||||
};
|
||||
|
||||
static const u32 accel_3d_addresses[ACCEL_3D_CHANNEL_MAX] = {
|
||||
|
@ -87,6 +89,42 @@ static const struct iio_chan_spec accel_3d_channels[] = {
|
|||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_Z,
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3)
|
||||
};
|
||||
|
||||
/* Channel definitions */
|
||||
static const struct iio_chan_spec gravity_channels[] = {
|
||||
{
|
||||
.type = IIO_GRAVITY,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_X,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_X,
|
||||
}, {
|
||||
.type = IIO_GRAVITY,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_Y,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_Y,
|
||||
}, {
|
||||
.type = IIO_GRAVITY,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_Z,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_Z,
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -111,6 +149,8 @@ static int accel_3d_read_raw(struct iio_dev *indio_dev,
|
|||
int report_id = -1;
|
||||
u32 address;
|
||||
int ret_type;
|
||||
struct hid_sensor_hub_device *hsdev =
|
||||
accel_state->common_attributes.hsdev;
|
||||
|
||||
*val = 0;
|
||||
*val2 = 0;
|
||||
|
@ -122,8 +162,7 @@ static int accel_3d_read_raw(struct iio_dev *indio_dev,
|
|||
if (report_id >= 0)
|
||||
*val = sensor_hub_input_attr_get_raw_value(
|
||||
accel_state->common_attributes.hsdev,
|
||||
HID_USAGE_SENSOR_ACCEL_3D, address,
|
||||
report_id,
|
||||
hsdev->usage, address, report_id,
|
||||
SENSOR_HUB_SYNC);
|
||||
else {
|
||||
*val = 0;
|
||||
|
@ -192,11 +231,11 @@ static const struct iio_info accel_3d_info = {
|
|||
};
|
||||
|
||||
/* Function to push data to buffer */
|
||||
static void hid_sensor_push_data(struct iio_dev *indio_dev, const void *data,
|
||||
int len)
|
||||
static void hid_sensor_push_data(struct iio_dev *indio_dev, void *data,
|
||||
int len, int64_t timestamp)
|
||||
{
|
||||
dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
|
||||
iio_push_to_buffers(indio_dev, data);
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data, timestamp);
|
||||
}
|
||||
|
||||
/* Callback handler to send event after all samples are received and captured */
|
||||
|
@ -208,10 +247,17 @@ static int accel_3d_proc_event(struct hid_sensor_hub_device *hsdev,
|
|||
struct accel_3d_state *accel_state = iio_priv(indio_dev);
|
||||
|
||||
dev_dbg(&indio_dev->dev, "accel_3d_proc_event\n");
|
||||
if (atomic_read(&accel_state->common_attributes.data_ready))
|
||||
if (atomic_read(&accel_state->common_attributes.data_ready)) {
|
||||
if (!accel_state->timestamp)
|
||||
accel_state->timestamp = iio_get_time_ns(indio_dev);
|
||||
|
||||
hid_sensor_push_data(indio_dev,
|
||||
accel_state->accel_val,
|
||||
sizeof(accel_state->accel_val));
|
||||
accel_state->accel_val,
|
||||
sizeof(accel_state->accel_val),
|
||||
accel_state->timestamp);
|
||||
|
||||
accel_state->timestamp = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -236,6 +282,12 @@ static int accel_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
|
|||
*(u32 *)raw_data;
|
||||
ret = 0;
|
||||
break;
|
||||
case HID_USAGE_SENSOR_TIME_TIMESTAMP:
|
||||
accel_state->timestamp =
|
||||
hid_sensor_convert_timestamp(
|
||||
&accel_state->common_attributes,
|
||||
*(int64_t *)raw_data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -272,7 +324,7 @@ static int accel_3d_parse_report(struct platform_device *pdev,
|
|||
st->accel[2].index, st->accel[2].report_id);
|
||||
|
||||
st->scale_precision = hid_sensor_format_scale(
|
||||
HID_USAGE_SENSOR_ACCEL_3D,
|
||||
hsdev->usage,
|
||||
&st->accel[CHANNEL_SCAN_INDEX_X],
|
||||
&st->scale_pre_decml, &st->scale_post_decml);
|
||||
|
||||
|
@ -295,9 +347,12 @@ static int accel_3d_parse_report(struct platform_device *pdev,
|
|||
static int hid_accel_3d_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
static const char *name = "accel_3d";
|
||||
static const char *name;
|
||||
struct iio_dev *indio_dev;
|
||||
struct accel_3d_state *accel_state;
|
||||
const struct iio_chan_spec *channel_spec;
|
||||
int channel_size;
|
||||
|
||||
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev,
|
||||
|
@ -311,24 +366,30 @@ static int hid_accel_3d_probe(struct platform_device *pdev)
|
|||
accel_state->common_attributes.hsdev = hsdev;
|
||||
accel_state->common_attributes.pdev = pdev;
|
||||
|
||||
ret = hid_sensor_parse_common_attributes(hsdev,
|
||||
HID_USAGE_SENSOR_ACCEL_3D,
|
||||
if (hsdev->usage == HID_USAGE_SENSOR_ACCEL_3D) {
|
||||
name = "accel_3d";
|
||||
channel_spec = accel_3d_channels;
|
||||
channel_size = sizeof(accel_3d_channels);
|
||||
} else {
|
||||
name = "gravity";
|
||||
channel_spec = gravity_channels;
|
||||
channel_size = sizeof(gravity_channels);
|
||||
}
|
||||
ret = hid_sensor_parse_common_attributes(hsdev, hsdev->usage,
|
||||
&accel_state->common_attributes);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to setup common attributes\n");
|
||||
return ret;
|
||||
}
|
||||
indio_dev->channels = kmemdup(channel_spec, channel_size, GFP_KERNEL);
|
||||
|
||||
indio_dev->channels = kmemdup(accel_3d_channels,
|
||||
sizeof(accel_3d_channels), GFP_KERNEL);
|
||||
if (!indio_dev->channels) {
|
||||
dev_err(&pdev->dev, "failed to duplicate channels\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = accel_3d_parse_report(pdev, hsdev,
|
||||
(struct iio_chan_spec *)indio_dev->channels,
|
||||
HID_USAGE_SENSOR_ACCEL_3D, accel_state);
|
||||
(struct iio_chan_spec *)indio_dev->channels,
|
||||
hsdev->usage, accel_state);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to setup attributes\n");
|
||||
goto error_free_dev_mem;
|
||||
|
@ -363,7 +424,7 @@ static int hid_accel_3d_probe(struct platform_device *pdev)
|
|||
accel_state->callbacks.send_event = accel_3d_proc_event;
|
||||
accel_state->callbacks.capture_sample = accel_3d_capture_sample;
|
||||
accel_state->callbacks.pdev = pdev;
|
||||
ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_ACCEL_3D,
|
||||
ret = sensor_hub_register_callback(hsdev, hsdev->usage,
|
||||
&accel_state->callbacks);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "callback reg failed\n");
|
||||
|
@ -390,7 +451,7 @@ static int hid_accel_3d_remove(struct platform_device *pdev)
|
|||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct accel_3d_state *accel_state = iio_priv(indio_dev);
|
||||
|
||||
sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_ACCEL_3D);
|
||||
sensor_hub_remove_callback(hsdev, hsdev->usage);
|
||||
iio_device_unregister(indio_dev);
|
||||
hid_sensor_remove_trigger(&accel_state->common_attributes);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
@ -404,6 +465,9 @@ static const struct platform_device_id hid_accel_3d_ids[] = {
|
|||
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
|
||||
.name = "HID-SENSOR-200073",
|
||||
},
|
||||
{ /* gravity sensor */
|
||||
.name = "HID-SENSOR-20007b",
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, hid_accel_3d_ids);
|
||||
|
|
|
@ -248,7 +248,7 @@ static int mma8452_get_int_plus_micros_index(const int (*vals)[2], int n,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int mma8452_get_odr_index(struct mma8452_data *data)
|
||||
static unsigned int mma8452_get_odr_index(struct mma8452_data *data)
|
||||
{
|
||||
return (data->ctrl_reg1 & MMA8452_CTRL_DR_MASK) >>
|
||||
MMA8452_CTRL_DR_SHIFT;
|
||||
|
@ -260,7 +260,7 @@ static const int mma8452_samp_freq[8][2] = {
|
|||
};
|
||||
|
||||
/* Datasheet table: step time "Relationship with the ODR" (sample frequency) */
|
||||
static const int mma8452_transient_time_step_us[4][8] = {
|
||||
static const unsigned int mma8452_transient_time_step_us[4][8] = {
|
||||
{ 1250, 2500, 5000, 10000, 20000, 20000, 20000, 20000 }, /* normal */
|
||||
{ 1250, 2500, 5000, 10000, 20000, 80000, 80000, 80000 }, /* l p l n */
|
||||
{ 1250, 2500, 2500, 2500, 2500, 2500, 2500, 2500 }, /* high res*/
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include <linux/iio/common/ssp_sensors.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
|
|
@ -14,6 +14,24 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
|
||||
enum st_accel_type {
|
||||
LSM303DLH,
|
||||
LSM303DLHC,
|
||||
LIS3DH,
|
||||
LSM330D,
|
||||
LSM330DL,
|
||||
LSM330DLC,
|
||||
LIS331DLH,
|
||||
LSM303DL,
|
||||
LSM303DLM,
|
||||
LSM330,
|
||||
LSM303AGR,
|
||||
LIS2DH12,
|
||||
LIS3L02DQ,
|
||||
LNG2DM,
|
||||
ST_ACCEL_MAX,
|
||||
};
|
||||
|
||||
#define H3LIS331DL_DRIVER_NAME "h3lis331dl_accel"
|
||||
#define LIS3LV02DL_ACCEL_DEV_NAME "lis3lv02dl_accel"
|
||||
#define LSM303DLHC_ACCEL_DEV_NAME "lsm303dlhc_accel"
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
|
@ -20,6 +21,11 @@
|
|||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id st_accel_of_match[] = {
|
||||
{
|
||||
/* An older compatible */
|
||||
.compatible = "st,lis3lv02d",
|
||||
.data = LIS3LV02DL_ACCEL_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lis3lv02dl-accel",
|
||||
.data = LIS3LV02DL_ACCEL_DEV_NAME,
|
||||
|
@ -95,25 +101,67 @@ MODULE_DEVICE_TABLE(of, st_accel_of_match);
|
|||
#define st_accel_of_match NULL
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id st_accel_acpi_match[] = {
|
||||
{"SMO8A90", LNG2DM},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, st_accel_acpi_match);
|
||||
#else
|
||||
#define st_accel_acpi_match NULL
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id st_accel_id_table[] = {
|
||||
{ LSM303DLH_ACCEL_DEV_NAME, LSM303DLH },
|
||||
{ LSM303DLHC_ACCEL_DEV_NAME, LSM303DLHC },
|
||||
{ LIS3DH_ACCEL_DEV_NAME, LIS3DH },
|
||||
{ LSM330D_ACCEL_DEV_NAME, LSM330D },
|
||||
{ LSM330DL_ACCEL_DEV_NAME, LSM330DL },
|
||||
{ LSM330DLC_ACCEL_DEV_NAME, LSM330DLC },
|
||||
{ LIS331DLH_ACCEL_DEV_NAME, LIS331DLH },
|
||||
{ LSM303DL_ACCEL_DEV_NAME, LSM303DL },
|
||||
{ LSM303DLM_ACCEL_DEV_NAME, LSM303DLM },
|
||||
{ LSM330_ACCEL_DEV_NAME, LSM330 },
|
||||
{ LSM303AGR_ACCEL_DEV_NAME, LSM303AGR },
|
||||
{ LIS2DH12_ACCEL_DEV_NAME, LIS2DH12 },
|
||||
{ LIS3L02DQ_ACCEL_DEV_NAME, LIS3L02DQ },
|
||||
{ LNG2DM_ACCEL_DEV_NAME, LNG2DM },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
|
||||
|
||||
static int st_accel_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct st_sensor_data *adata;
|
||||
int err;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*adata));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
adata = iio_priv(indio_dev);
|
||||
st_sensors_of_i2c_probe(client, st_accel_of_match);
|
||||
|
||||
if (client->dev.of_node) {
|
||||
st_sensors_of_i2c_probe(client, st_accel_of_match);
|
||||
} else if (ACPI_HANDLE(&client->dev)) {
|
||||
ret = st_sensors_match_acpi_device(&client->dev);
|
||||
if ((ret < 0) || (ret >= ST_ACCEL_MAX))
|
||||
return -ENODEV;
|
||||
|
||||
strncpy(client->name, st_accel_id_table[ret].name,
|
||||
sizeof(client->name));
|
||||
client->name[sizeof(client->name) - 1] = '\0';
|
||||
} else if (!id)
|
||||
return -ENODEV;
|
||||
|
||||
|
||||
st_sensors_i2c_configure(indio_dev, client, adata);
|
||||
|
||||
err = st_accel_common_probe(indio_dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
ret = st_accel_common_probe(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -125,29 +173,11 @@ static int st_accel_i2c_remove(struct i2c_client *client)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id st_accel_id_table[] = {
|
||||
{ LSM303DLH_ACCEL_DEV_NAME },
|
||||
{ LSM303DLHC_ACCEL_DEV_NAME },
|
||||
{ LIS3DH_ACCEL_DEV_NAME },
|
||||
{ LSM330D_ACCEL_DEV_NAME },
|
||||
{ LSM330DL_ACCEL_DEV_NAME },
|
||||
{ LSM330DLC_ACCEL_DEV_NAME },
|
||||
{ LIS331DLH_ACCEL_DEV_NAME },
|
||||
{ LSM303DL_ACCEL_DEV_NAME },
|
||||
{ LSM303DLM_ACCEL_DEV_NAME },
|
||||
{ LSM330_ACCEL_DEV_NAME },
|
||||
{ LSM303AGR_ACCEL_DEV_NAME },
|
||||
{ LIS2DH12_ACCEL_DEV_NAME },
|
||||
{ LIS3L02DQ_ACCEL_DEV_NAME },
|
||||
{ LNG2DM_ACCEL_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
|
||||
|
||||
static struct i2c_driver st_accel_driver = {
|
||||
.driver = {
|
||||
.name = "st-accel-i2c",
|
||||
.of_match_table = of_match_ptr(st_accel_of_match),
|
||||
.acpi_match_table = ACPI_PTR(st_accel_acpi_match),
|
||||
},
|
||||
.probe = st_accel_i2c_probe,
|
||||
.remove = st_accel_i2c_remove,
|
||||
|
|
|
@ -65,9 +65,18 @@ static const struct spi_device_id st_accel_id_table[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(spi, st_accel_id_table);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id lis302dl_spi_dt_ids[] = {
|
||||
{ .compatible = "st,lis302dl-spi" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, lis302dl_spi_dt_ids);
|
||||
#endif
|
||||
|
||||
static struct spi_driver st_accel_driver = {
|
||||
.driver = {
|
||||
.name = "st-accel-spi",
|
||||
.of_match_table = of_match_ptr(lis302dl_spi_dt_ids),
|
||||
},
|
||||
.probe = st_accel_spi_probe,
|
||||
.remove = st_accel_spi_remove,
|
||||
|
|
|
@ -247,6 +247,25 @@ config HI8435
|
|||
This driver can also be built as a module. If so, the module will be
|
||||
called hi8435.
|
||||
|
||||
config HX711
|
||||
tristate "AVIA HX711 ADC for weight cells"
|
||||
depends on GPIOLIB
|
||||
help
|
||||
If you say yes here you get support for AVIA HX711 ADC which is used
|
||||
for weigh cells
|
||||
|
||||
This driver uses two GPIOs, one acts as the clock and controls the
|
||||
channel selection and gain, the other one is used for the measurement
|
||||
data
|
||||
|
||||
Currently the raw value is read from the chip and delivered.
|
||||
To get an actual weight one needs to subtract the
|
||||
zero offset and multiply by a scale factor.
|
||||
This should be done in userspace.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called hx711.
|
||||
|
||||
config INA2XX_ADC
|
||||
tristate "Texas Instruments INA2xx Power Monitors IIO driver"
|
||||
depends on I2C && !SENSORS_INA2XX
|
||||
|
@ -549,6 +568,19 @@ config TI_ADS1015
|
|||
This driver can also be built as a module. If so, the module will be
|
||||
called ti-ads1015.
|
||||
|
||||
config TI_ADS7950
|
||||
tristate "Texas Instruments ADS7950 ADC driver"
|
||||
depends on SPI
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for Texas Instruments ADS7950, ADS7951,
|
||||
ADS7952, ADS7953, ADS7954, ADS7955, ADS7956, ADS7957, ADS7958, ADS7959.
|
||||
ADS7960, ADS7961.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ti-ads7950.
|
||||
|
||||
config TI_ADS8688
|
||||
tristate "Texas Instruments ADS8688"
|
||||
depends on SPI && OF
|
||||
|
|
|
@ -25,6 +25,7 @@ obj-$(CONFIG_ENVELOPE_DETECTOR) += envelope-detector.o
|
|||
obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
|
||||
obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o
|
||||
obj-$(CONFIG_HI8435) += hi8435.o
|
||||
obj-$(CONFIG_HX711) += hx711.o
|
||||
obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
|
||||
obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
|
||||
obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
|
||||
|
@ -51,6 +52,7 @@ obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
|
|||
obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
|
||||
obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o
|
||||
obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o
|
||||
obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o
|
||||
obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o
|
||||
obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
|
||||
obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
|
||||
|
|
|
@ -28,8 +28,6 @@
|
|||
#include <linux/iio/driver.h>
|
||||
|
||||
#define AXP288_ADC_EN_MASK 0xF1
|
||||
#define AXP288_ADC_TS_PIN_GPADC 0xF2
|
||||
#define AXP288_ADC_TS_PIN_ON 0xF3
|
||||
|
||||
enum axp288_adc_id {
|
||||
AXP288_ADC_TS,
|
||||
|
@ -123,16 +121,6 @@ static int axp288_adc_read_channel(int *val, unsigned long address,
|
|||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int axp288_adc_set_ts(struct regmap *regmap, unsigned int mode,
|
||||
unsigned long address)
|
||||
{
|
||||
/* channels other than GPADC do not need to switch TS pin */
|
||||
if (address != AXP288_GP_ADC_H)
|
||||
return 0;
|
||||
|
||||
return regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, mode);
|
||||
}
|
||||
|
||||
static int axp288_adc_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
|
@ -143,16 +131,7 @@ static int axp288_adc_read_raw(struct iio_dev *indio_dev,
|
|||
mutex_lock(&indio_dev->mlock);
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_GPADC,
|
||||
chan->address)) {
|
||||
dev_err(&indio_dev->dev, "GPADC mode\n");
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
ret = axp288_adc_read_channel(val, chan->address, info->regmap);
|
||||
if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_ON,
|
||||
chan->address))
|
||||
dev_err(&indio_dev->dev, "TS pin restore\n");
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
|
@ -162,15 +141,6 @@ static int axp288_adc_read_raw(struct iio_dev *indio_dev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int axp288_adc_set_state(struct regmap *regmap)
|
||||
{
|
||||
/* ADC should be always enabled for internal FG to function */
|
||||
if (regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, AXP288_ADC_TS_PIN_ON))
|
||||
return -EIO;
|
||||
|
||||
return regmap_write(regmap, AXP20X_ADC_EN1, AXP288_ADC_EN_MASK);
|
||||
}
|
||||
|
||||
static const struct iio_info axp288_adc_iio_info = {
|
||||
.read_raw = &axp288_adc_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
|
@ -199,7 +169,7 @@ static int axp288_adc_probe(struct platform_device *pdev)
|
|||
* Set ADC to enabled state at all time, including system suspend.
|
||||
* otherwise internal fuel gauge functionality may be affected.
|
||||
*/
|
||||
ret = axp288_adc_set_state(axp20x->regmap);
|
||||
ret = regmap_write(info->regmap, AXP20X_ADC_EN1, AXP288_ADC_EN_MASK);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to enable ADC device\n");
|
||||
return ret;
|
||||
|
|
|
@ -632,7 +632,7 @@ static irqreturn_t exynos_ts_isr(int irq, void *dev_id)
|
|||
input_report_key(info->input, BTN_TOUCH, 1);
|
||||
input_sync(info->input);
|
||||
|
||||
msleep(1);
|
||||
usleep_range(1000, 1100);
|
||||
};
|
||||
|
||||
writel(0, ADC_V1_CLRINTPNDNUP(info->regs));
|
||||
|
|
|
@ -401,6 +401,7 @@ static const struct of_device_id mx25_gcq_ids[] = {
|
|||
{ .compatible = "fsl,imx25-gcq", },
|
||||
{ /* Sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mx25_gcq_ids);
|
||||
|
||||
static struct platform_driver mx25_gcq_driver = {
|
||||
.driver = {
|
||||
|
|
|
@ -0,0 +1,532 @@
|
|||
/*
|
||||
* HX711: analog to digital converter for weight sensor module
|
||||
*
|
||||
* Copyright (c) 2016 Andreas Klinger <ak@it-klinger.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
/* gain to pulse and scale conversion */
|
||||
#define HX711_GAIN_MAX 3
|
||||
|
||||
struct hx711_gain_to_scale {
|
||||
int gain;
|
||||
int gain_pulse;
|
||||
int scale;
|
||||
int channel;
|
||||
};
|
||||
|
||||
/*
|
||||
* .scale depends on AVDD which in turn is known as soon as the regulator
|
||||
* is available
|
||||
* therefore we set .scale in hx711_probe()
|
||||
*
|
||||
* channel A in documentation is channel 0 in source code
|
||||
* channel B in documentation is channel 1 in source code
|
||||
*/
|
||||
static struct hx711_gain_to_scale hx711_gain_to_scale[HX711_GAIN_MAX] = {
|
||||
{ 128, 1, 0, 0 },
|
||||
{ 32, 2, 0, 1 },
|
||||
{ 64, 3, 0, 0 }
|
||||
};
|
||||
|
||||
static int hx711_get_gain_to_pulse(int gain)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < HX711_GAIN_MAX; i++)
|
||||
if (hx711_gain_to_scale[i].gain == gain)
|
||||
return hx711_gain_to_scale[i].gain_pulse;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int hx711_get_gain_to_scale(int gain)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < HX711_GAIN_MAX; i++)
|
||||
if (hx711_gain_to_scale[i].gain == gain)
|
||||
return hx711_gain_to_scale[i].scale;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hx711_get_scale_to_gain(int scale)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < HX711_GAIN_MAX; i++)
|
||||
if (hx711_gain_to_scale[i].scale == scale)
|
||||
return hx711_gain_to_scale[i].gain;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct hx711_data {
|
||||
struct device *dev;
|
||||
struct gpio_desc *gpiod_pd_sck;
|
||||
struct gpio_desc *gpiod_dout;
|
||||
struct regulator *reg_avdd;
|
||||
int gain_set; /* gain set on device */
|
||||
int gain_chan_a; /* gain for channel A */
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
static int hx711_cycle(struct hx711_data *hx711_data)
|
||||
{
|
||||
int val;
|
||||
|
||||
/*
|
||||
* if preempted for more then 60us while PD_SCK is high:
|
||||
* hx711 is going in reset
|
||||
* ==> measuring is false
|
||||
*/
|
||||
preempt_disable();
|
||||
gpiod_set_value(hx711_data->gpiod_pd_sck, 1);
|
||||
val = gpiod_get_value(hx711_data->gpiod_dout);
|
||||
/*
|
||||
* here we are not waiting for 0.2 us as suggested by the datasheet,
|
||||
* because the oscilloscope showed in a test scenario
|
||||
* at least 1.15 us for PD_SCK high (T3 in datasheet)
|
||||
* and 0.56 us for PD_SCK low on TI Sitara with 800 MHz
|
||||
*/
|
||||
gpiod_set_value(hx711_data->gpiod_pd_sck, 0);
|
||||
preempt_enable();
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int hx711_read(struct hx711_data *hx711_data)
|
||||
{
|
||||
int i, ret;
|
||||
int value = 0;
|
||||
int val = gpiod_get_value(hx711_data->gpiod_dout);
|
||||
|
||||
/* we double check if it's really down */
|
||||
if (val)
|
||||
return -EIO;
|
||||
|
||||
for (i = 0; i < 24; i++) {
|
||||
value <<= 1;
|
||||
ret = hx711_cycle(hx711_data);
|
||||
if (ret)
|
||||
value++;
|
||||
}
|
||||
|
||||
value ^= 0x800000;
|
||||
|
||||
for (i = 0; i < hx711_get_gain_to_pulse(hx711_data->gain_set); i++)
|
||||
hx711_cycle(hx711_data);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static int hx711_wait_for_ready(struct hx711_data *hx711_data)
|
||||
{
|
||||
int i, val;
|
||||
|
||||
/*
|
||||
* a maximum reset cycle time of 56 ms was measured.
|
||||
* we round it up to 100 ms
|
||||
*/
|
||||
for (i = 0; i < 100; i++) {
|
||||
val = gpiod_get_value(hx711_data->gpiod_dout);
|
||||
if (!val)
|
||||
break;
|
||||
/* sleep at least 1 ms */
|
||||
msleep(1);
|
||||
}
|
||||
if (val)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hx711_reset(struct hx711_data *hx711_data)
|
||||
{
|
||||
int ret;
|
||||
int val = gpiod_get_value(hx711_data->gpiod_dout);
|
||||
|
||||
if (val) {
|
||||
/*
|
||||
* an examination with the oszilloscope indicated
|
||||
* that the first value read after the reset is not stable
|
||||
* if we reset too short;
|
||||
* the shorter the reset cycle
|
||||
* the less reliable the first value after reset is;
|
||||
* there were no problems encountered with a value
|
||||
* of 10 ms or higher
|
||||
*/
|
||||
gpiod_set_value(hx711_data->gpiod_pd_sck, 1);
|
||||
msleep(10);
|
||||
gpiod_set_value(hx711_data->gpiod_pd_sck, 0);
|
||||
|
||||
ret = hx711_wait_for_ready(hx711_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
/*
|
||||
* after a reset the gain is 128 so we do a dummy read
|
||||
* to set the gain for the next read
|
||||
*/
|
||||
ret = hx711_read(hx711_data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* after a dummy read we need to wait vor readiness
|
||||
* for not mixing gain pulses with the clock
|
||||
*/
|
||||
ret = hx711_wait_for_ready(hx711_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int hx711_set_gain_for_channel(struct hx711_data *hx711_data, int chan)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (chan == 0) {
|
||||
if (hx711_data->gain_set == 32) {
|
||||
hx711_data->gain_set = hx711_data->gain_chan_a;
|
||||
|
||||
ret = hx711_read(hx711_data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = hx711_wait_for_ready(hx711_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
if (hx711_data->gain_set != 32) {
|
||||
hx711_data->gain_set = 32;
|
||||
|
||||
ret = hx711_read(hx711_data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = hx711_wait_for_ready(hx711_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hx711_read_raw(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct hx711_data *hx711_data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&hx711_data->lock);
|
||||
|
||||
/*
|
||||
* hx711_reset() must be called from here
|
||||
* because it could be calling hx711_read() by itself
|
||||
*/
|
||||
if (hx711_reset(hx711_data)) {
|
||||
mutex_unlock(&hx711_data->lock);
|
||||
dev_err(hx711_data->dev, "reset failed!");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ret = hx711_set_gain_for_channel(hx711_data, chan->channel);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&hx711_data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*val = hx711_read(hx711_data);
|
||||
|
||||
mutex_unlock(&hx711_data->lock);
|
||||
|
||||
if (*val < 0)
|
||||
return *val;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
mutex_lock(&hx711_data->lock);
|
||||
|
||||
*val2 = hx711_get_gain_to_scale(hx711_data->gain_set);
|
||||
|
||||
mutex_unlock(&hx711_data->lock);
|
||||
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int hx711_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct hx711_data *hx711_data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
int gain;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
/*
|
||||
* a scale greater than 1 mV per LSB is not possible
|
||||
* with the HX711, therefore val must be 0
|
||||
*/
|
||||
if (val != 0)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&hx711_data->lock);
|
||||
|
||||
gain = hx711_get_scale_to_gain(val2);
|
||||
if (gain < 0) {
|
||||
mutex_unlock(&hx711_data->lock);
|
||||
return gain;
|
||||
}
|
||||
|
||||
if (gain != hx711_data->gain_set) {
|
||||
hx711_data->gain_set = gain;
|
||||
if (gain != 32)
|
||||
hx711_data->gain_chan_a = gain;
|
||||
|
||||
ret = hx711_read(hx711_data);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&hx711_data->lock);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&hx711_data->lock);
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hx711_write_raw_get_fmt(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
long mask)
|
||||
{
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
}
|
||||
|
||||
static ssize_t hx711_scale_available_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev_attr *iio_attr = to_iio_dev_attr(attr);
|
||||
int channel = iio_attr->address;
|
||||
int i, len = 0;
|
||||
|
||||
for (i = 0; i < HX711_GAIN_MAX; i++)
|
||||
if (hx711_gain_to_scale[i].channel == channel)
|
||||
len += sprintf(buf + len, "0.%09d ",
|
||||
hx711_gain_to_scale[i].scale);
|
||||
|
||||
len += sprintf(buf + len, "\n");
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(in_voltage0_scale_available, S_IRUGO,
|
||||
hx711_scale_available_show, NULL, 0);
|
||||
|
||||
static IIO_DEVICE_ATTR(in_voltage1_scale_available, S_IRUGO,
|
||||
hx711_scale_available_show, NULL, 1);
|
||||
|
||||
static struct attribute *hx711_attributes[] = {
|
||||
&iio_dev_attr_in_voltage0_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage1_scale_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group hx711_attribute_group = {
|
||||
.attrs = hx711_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info hx711_iio_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = hx711_read_raw,
|
||||
.write_raw = hx711_write_raw,
|
||||
.write_raw_get_fmt = hx711_write_raw_get_fmt,
|
||||
.attrs = &hx711_attribute_group,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec hx711_chan_spec[] = {
|
||||
{
|
||||
.type = IIO_VOLTAGE,
|
||||
.channel = 0,
|
||||
.indexed = 1,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
},
|
||||
{
|
||||
.type = IIO_VOLTAGE,
|
||||
.channel = 1,
|
||||
.indexed = 1,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
},
|
||||
};
|
||||
|
||||
static int hx711_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct hx711_data *hx711_data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(struct hx711_data));
|
||||
if (!indio_dev) {
|
||||
dev_err(dev, "failed to allocate IIO device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
hx711_data = iio_priv(indio_dev);
|
||||
hx711_data->dev = dev;
|
||||
|
||||
mutex_init(&hx711_data->lock);
|
||||
|
||||
/*
|
||||
* PD_SCK stands for power down and serial clock input of HX711
|
||||
* in the driver it is an output
|
||||
*/
|
||||
hx711_data->gpiod_pd_sck = devm_gpiod_get(dev, "sck", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(hx711_data->gpiod_pd_sck)) {
|
||||
dev_err(dev, "failed to get sck-gpiod: err=%ld\n",
|
||||
PTR_ERR(hx711_data->gpiod_pd_sck));
|
||||
return PTR_ERR(hx711_data->gpiod_pd_sck);
|
||||
}
|
||||
|
||||
/*
|
||||
* DOUT stands for serial data output of HX711
|
||||
* for the driver it is an input
|
||||
*/
|
||||
hx711_data->gpiod_dout = devm_gpiod_get(dev, "dout", GPIOD_IN);
|
||||
if (IS_ERR(hx711_data->gpiod_dout)) {
|
||||
dev_err(dev, "failed to get dout-gpiod: err=%ld\n",
|
||||
PTR_ERR(hx711_data->gpiod_dout));
|
||||
return PTR_ERR(hx711_data->gpiod_dout);
|
||||
}
|
||||
|
||||
hx711_data->reg_avdd = devm_regulator_get(dev, "avdd");
|
||||
if (IS_ERR(hx711_data->reg_avdd))
|
||||
return PTR_ERR(hx711_data->reg_avdd);
|
||||
|
||||
ret = regulator_enable(hx711_data->reg_avdd);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* with
|
||||
* full scale differential input range: AVDD / GAIN
|
||||
* full scale output data: 2^24
|
||||
* we can say:
|
||||
* AVDD / GAIN = 2^24
|
||||
* therefore:
|
||||
* 1 LSB = AVDD / GAIN / 2^24
|
||||
* AVDD is in uV, but we need 10^-9 mV
|
||||
* approximately to fit into a 32 bit number:
|
||||
* 1 LSB = (AVDD * 100) / GAIN / 1678 [10^-9 mV]
|
||||
*/
|
||||
ret = regulator_get_voltage(hx711_data->reg_avdd);
|
||||
if (ret < 0) {
|
||||
regulator_disable(hx711_data->reg_avdd);
|
||||
return ret;
|
||||
}
|
||||
/* we need 10^-9 mV */
|
||||
ret *= 100;
|
||||
|
||||
for (i = 0; i < HX711_GAIN_MAX; i++)
|
||||
hx711_gain_to_scale[i].scale =
|
||||
ret / hx711_gain_to_scale[i].gain / 1678;
|
||||
|
||||
hx711_data->gain_set = 128;
|
||||
hx711_data->gain_chan_a = 128;
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
indio_dev->name = "hx711";
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->info = &hx711_iio_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = hx711_chan_spec;
|
||||
indio_dev->num_channels = ARRAY_SIZE(hx711_chan_spec);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Couldn't register the device\n");
|
||||
regulator_disable(hx711_data->reg_avdd);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hx711_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct hx711_data *hx711_data;
|
||||
struct iio_dev *indio_dev;
|
||||
|
||||
indio_dev = platform_get_drvdata(pdev);
|
||||
hx711_data = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
regulator_disable(hx711_data->reg_avdd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id of_hx711_match[] = {
|
||||
{ .compatible = "avia,hx711", },
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, of_hx711_match);
|
||||
|
||||
static struct platform_driver hx711_driver = {
|
||||
.probe = hx711_probe,
|
||||
.remove = hx711_remove,
|
||||
.driver = {
|
||||
.name = "hx711-gpio",
|
||||
.of_match_table = of_hx711_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(hx711_driver);
|
||||
|
||||
MODULE_AUTHOR("Andreas Klinger <ak@it-klinger.de>");
|
||||
MODULE_DESCRIPTION("HX711 bitbanging driver - ADC for weight cells");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:hx711-gpio");
|
||||
|
|
@ -22,6 +22,8 @@
|
|||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/kthread.h>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2012-2016, 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
|
||||
|
@ -84,7 +84,7 @@
|
|||
#define VADC_MAX_ADC_CODE 0xa800
|
||||
|
||||
#define VADC_ABSOLUTE_RANGE_UV 625000
|
||||
#define VADC_RATIOMETRIC_RANGE_UV 1800000
|
||||
#define VADC_RATIOMETRIC_RANGE 1800
|
||||
|
||||
#define VADC_DEF_PRESCALING 0 /* 1:1 */
|
||||
#define VADC_DEF_DECIMATION 0 /* 512 */
|
||||
|
@ -100,9 +100,23 @@
|
|||
|
||||
#define KELVINMIL_CELSIUSMIL 273150
|
||||
|
||||
#define PMI_CHG_SCALE_1 -138890
|
||||
#define PMI_CHG_SCALE_2 391750000000LL
|
||||
|
||||
#define VADC_CHAN_MIN VADC_USBIN
|
||||
#define VADC_CHAN_MAX VADC_LR_MUX3_BUF_PU1_PU2_XO_THERM
|
||||
|
||||
/**
|
||||
* struct vadc_map_pt - Map the graph representation for ADC channel
|
||||
* @x: Represent the ADC digitized code.
|
||||
* @y: Represent the physical data which can be temperature, voltage,
|
||||
* resistance.
|
||||
*/
|
||||
struct vadc_map_pt {
|
||||
s32 x;
|
||||
s32 y;
|
||||
};
|
||||
|
||||
/*
|
||||
* VADC_CALIB_ABSOLUTE: uses the 625mV and 1.25V as reference channels.
|
||||
* VADC_CALIB_RATIOMETRIC: uses the reference voltage (1.8V) and GND for
|
||||
|
@ -148,6 +162,9 @@ struct vadc_prescale_ratio {
|
|||
* start of conversion.
|
||||
* @avg_samples: ability to provide single result from the ADC
|
||||
* that is an average of multiple measurements.
|
||||
* @scale_fn: Represents the scaling function to convert voltage
|
||||
* physical units desired by the client for the channel.
|
||||
* Referenced from enum vadc_scale_fn_type.
|
||||
*/
|
||||
struct vadc_channel_prop {
|
||||
unsigned int channel;
|
||||
|
@ -156,6 +173,7 @@ struct vadc_channel_prop {
|
|||
unsigned int prescale;
|
||||
unsigned int hw_settle_time;
|
||||
unsigned int avg_samples;
|
||||
unsigned int scale_fn;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -186,6 +204,35 @@ struct vadc_priv {
|
|||
struct mutex lock;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct vadc_scale_fn - Scaling function prototype
|
||||
* @scale: Function pointer to one of the scaling functions
|
||||
* which takes the adc properties, channel properties,
|
||||
* and returns the physical result.
|
||||
*/
|
||||
struct vadc_scale_fn {
|
||||
int (*scale)(struct vadc_priv *, const struct vadc_channel_prop *,
|
||||
u16, int *);
|
||||
};
|
||||
|
||||
/**
|
||||
* enum vadc_scale_fn_type - Scaling function to convert ADC code to
|
||||
* physical scaled units for the channel.
|
||||
* SCALE_DEFAULT: Default scaling to convert raw adc code to voltage (uV).
|
||||
* SCALE_THERM_100K_PULLUP: Returns temperature in millidegC.
|
||||
* Uses a mapping table with 100K pullup.
|
||||
* SCALE_PMIC_THERM: Returns result in milli degree's Centigrade.
|
||||
* SCALE_XOTHERM: Returns XO thermistor voltage in millidegC.
|
||||
* SCALE_PMI_CHG_TEMP: Conversion for PMI CHG temp
|
||||
*/
|
||||
enum vadc_scale_fn_type {
|
||||
SCALE_DEFAULT = 0,
|
||||
SCALE_THERM_100K_PULLUP,
|
||||
SCALE_PMIC_THERM,
|
||||
SCALE_XOTHERM,
|
||||
SCALE_PMI_CHG_TEMP,
|
||||
};
|
||||
|
||||
static const struct vadc_prescale_ratio vadc_prescale_ratios[] = {
|
||||
{.num = 1, .den = 1},
|
||||
{.num = 1, .den = 3},
|
||||
|
@ -197,6 +244,44 @@ static const struct vadc_prescale_ratio vadc_prescale_ratios[] = {
|
|||
{.num = 1, .den = 10}
|
||||
};
|
||||
|
||||
/* Voltage to temperature */
|
||||
static const struct vadc_map_pt adcmap_100k_104ef_104fb[] = {
|
||||
{1758, -40},
|
||||
{1742, -35},
|
||||
{1719, -30},
|
||||
{1691, -25},
|
||||
{1654, -20},
|
||||
{1608, -15},
|
||||
{1551, -10},
|
||||
{1483, -5},
|
||||
{1404, 0},
|
||||
{1315, 5},
|
||||
{1218, 10},
|
||||
{1114, 15},
|
||||
{1007, 20},
|
||||
{900, 25},
|
||||
{795, 30},
|
||||
{696, 35},
|
||||
{605, 40},
|
||||
{522, 45},
|
||||
{448, 50},
|
||||
{383, 55},
|
||||
{327, 60},
|
||||
{278, 65},
|
||||
{237, 70},
|
||||
{202, 75},
|
||||
{172, 80},
|
||||
{146, 85},
|
||||
{125, 90},
|
||||
{107, 95},
|
||||
{92, 100},
|
||||
{79, 105},
|
||||
{68, 110},
|
||||
{59, 115},
|
||||
{51, 120},
|
||||
{44, 125}
|
||||
};
|
||||
|
||||
static int vadc_read(struct vadc_priv *vadc, u16 offset, u8 *data)
|
||||
{
|
||||
return regmap_bulk_read(vadc->regmap, vadc->base + offset, data, 1);
|
||||
|
@ -418,7 +503,7 @@ static int vadc_measure_ref_points(struct vadc_priv *vadc)
|
|||
u16 read_1, read_2;
|
||||
int ret;
|
||||
|
||||
vadc->graph[VADC_CALIB_RATIOMETRIC].dx = VADC_RATIOMETRIC_RANGE_UV;
|
||||
vadc->graph[VADC_CALIB_RATIOMETRIC].dx = VADC_RATIOMETRIC_RANGE;
|
||||
vadc->graph[VADC_CALIB_ABSOLUTE].dx = VADC_ABSOLUTE_RANGE_UV;
|
||||
|
||||
prop = vadc_get_channel(vadc, VADC_REF_1250MV);
|
||||
|
@ -468,27 +553,148 @@ err:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static s32 vadc_calibrate(struct vadc_priv *vadc,
|
||||
const struct vadc_channel_prop *prop, u16 adc_code)
|
||||
static int vadc_map_voltage_temp(const struct vadc_map_pt *pts,
|
||||
u32 tablesize, s32 input, s64 *output)
|
||||
{
|
||||
bool descending = 1;
|
||||
u32 i = 0;
|
||||
|
||||
if (!pts)
|
||||
return -EINVAL;
|
||||
|
||||
/* Check if table is descending or ascending */
|
||||
if (tablesize > 1) {
|
||||
if (pts[0].x < pts[1].x)
|
||||
descending = 0;
|
||||
}
|
||||
|
||||
while (i < tablesize) {
|
||||
if ((descending) && (pts[i].x < input)) {
|
||||
/* table entry is less than measured*/
|
||||
/* value and table is descending, stop */
|
||||
break;
|
||||
} else if ((!descending) &&
|
||||
(pts[i].x > input)) {
|
||||
/* table entry is greater than measured*/
|
||||
/*value and table is ascending, stop */
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
*output = pts[0].y;
|
||||
} else if (i == tablesize) {
|
||||
*output = pts[tablesize - 1].y;
|
||||
} else {
|
||||
/* result is between search_index and search_index-1 */
|
||||
/* interpolate linearly */
|
||||
*output = (((s32)((pts[i].y - pts[i - 1].y) *
|
||||
(input - pts[i - 1].x)) /
|
||||
(pts[i].x - pts[i - 1].x)) +
|
||||
pts[i - 1].y);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vadc_scale_calib(struct vadc_priv *vadc, u16 adc_code,
|
||||
const struct vadc_channel_prop *prop,
|
||||
s64 *scale_voltage)
|
||||
{
|
||||
*scale_voltage = (adc_code -
|
||||
vadc->graph[prop->calibration].gnd);
|
||||
*scale_voltage *= vadc->graph[prop->calibration].dx;
|
||||
*scale_voltage = div64_s64(*scale_voltage,
|
||||
vadc->graph[prop->calibration].dy);
|
||||
if (prop->calibration == VADC_CALIB_ABSOLUTE)
|
||||
*scale_voltage +=
|
||||
vadc->graph[prop->calibration].dx;
|
||||
|
||||
if (*scale_voltage < 0)
|
||||
*scale_voltage = 0;
|
||||
}
|
||||
|
||||
static int vadc_scale_volt(struct vadc_priv *vadc,
|
||||
const struct vadc_channel_prop *prop, u16 adc_code,
|
||||
int *result_uv)
|
||||
{
|
||||
const struct vadc_prescale_ratio *prescale;
|
||||
s64 voltage;
|
||||
s64 voltage = 0, result = 0;
|
||||
|
||||
voltage = adc_code - vadc->graph[prop->calibration].gnd;
|
||||
voltage *= vadc->graph[prop->calibration].dx;
|
||||
voltage = div64_s64(voltage, vadc->graph[prop->calibration].dy);
|
||||
|
||||
if (prop->calibration == VADC_CALIB_ABSOLUTE)
|
||||
voltage += vadc->graph[prop->calibration].dx;
|
||||
|
||||
if (voltage < 0)
|
||||
voltage = 0;
|
||||
vadc_scale_calib(vadc, adc_code, prop, &voltage);
|
||||
|
||||
prescale = &vadc_prescale_ratios[prop->prescale];
|
||||
|
||||
voltage = voltage * prescale->den;
|
||||
result = div64_s64(voltage, prescale->num);
|
||||
*result_uv = result;
|
||||
|
||||
return div64_s64(voltage, prescale->num);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vadc_scale_therm(struct vadc_priv *vadc,
|
||||
const struct vadc_channel_prop *prop, u16 adc_code,
|
||||
int *result_mdec)
|
||||
{
|
||||
s64 voltage = 0, result = 0;
|
||||
|
||||
vadc_scale_calib(vadc, adc_code, prop, &voltage);
|
||||
|
||||
if (prop->calibration == VADC_CALIB_ABSOLUTE)
|
||||
voltage = div64_s64(voltage, 1000);
|
||||
|
||||
vadc_map_voltage_temp(adcmap_100k_104ef_104fb,
|
||||
ARRAY_SIZE(adcmap_100k_104ef_104fb),
|
||||
voltage, &result);
|
||||
result *= 1000;
|
||||
*result_mdec = result;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vadc_scale_die_temp(struct vadc_priv *vadc,
|
||||
const struct vadc_channel_prop *prop,
|
||||
u16 adc_code, int *result_mdec)
|
||||
{
|
||||
const struct vadc_prescale_ratio *prescale;
|
||||
s64 voltage = 0;
|
||||
u64 temp; /* Temporary variable for do_div */
|
||||
|
||||
vadc_scale_calib(vadc, adc_code, prop, &voltage);
|
||||
|
||||
if (voltage > 0) {
|
||||
prescale = &vadc_prescale_ratios[prop->prescale];
|
||||
temp = voltage * prescale->den;
|
||||
do_div(temp, prescale->num * 2);
|
||||
voltage = temp;
|
||||
} else {
|
||||
voltage = 0;
|
||||
}
|
||||
|
||||
voltage -= KELVINMIL_CELSIUSMIL;
|
||||
*result_mdec = voltage;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vadc_scale_chg_temp(struct vadc_priv *vadc,
|
||||
const struct vadc_channel_prop *prop,
|
||||
u16 adc_code, int *result_mdec)
|
||||
{
|
||||
const struct vadc_prescale_ratio *prescale;
|
||||
s64 voltage = 0, result = 0;
|
||||
|
||||
vadc_scale_calib(vadc, adc_code, prop, &voltage);
|
||||
|
||||
prescale = &vadc_prescale_ratios[prop->prescale];
|
||||
voltage = voltage * prescale->den;
|
||||
voltage = div64_s64(voltage, prescale->num);
|
||||
voltage = ((PMI_CHG_SCALE_1) * (voltage * 2));
|
||||
voltage = (voltage + PMI_CHG_SCALE_2);
|
||||
result = div64_s64(voltage, 1000000);
|
||||
*result_mdec = result;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vadc_decimation_from_dt(u32 value)
|
||||
|
@ -536,6 +742,14 @@ static int vadc_avg_samples_from_dt(u32 value)
|
|||
return __ffs64(value);
|
||||
}
|
||||
|
||||
static struct vadc_scale_fn scale_fn[] = {
|
||||
[SCALE_DEFAULT] = {vadc_scale_volt},
|
||||
[SCALE_THERM_100K_PULLUP] = {vadc_scale_therm},
|
||||
[SCALE_PMIC_THERM] = {vadc_scale_die_temp},
|
||||
[SCALE_XOTHERM] = {vadc_scale_therm},
|
||||
[SCALE_PMI_CHG_TEMP] = {vadc_scale_chg_temp},
|
||||
};
|
||||
|
||||
static int vadc_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val, int *val2,
|
||||
long mask)
|
||||
|
@ -552,11 +766,8 @@ static int vadc_read_raw(struct iio_dev *indio_dev,
|
|||
if (ret)
|
||||
break;
|
||||
|
||||
*val = vadc_calibrate(vadc, prop, adc_code);
|
||||
scale_fn[prop->scale_fn].scale(vadc, prop, adc_code, val);
|
||||
|
||||
/* 2mV/K, return milli Celsius */
|
||||
*val /= 2;
|
||||
*val -= KELVINMIL_CELSIUSMIL;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
prop = &vadc->chan_props[chan->address];
|
||||
|
@ -564,12 +775,8 @@ static int vadc_read_raw(struct iio_dev *indio_dev,
|
|||
if (ret)
|
||||
break;
|
||||
|
||||
*val = vadc_calibrate(vadc, prop, adc_code);
|
||||
*val = (int)adc_code;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
*val2 = 1000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
|
@ -602,22 +809,39 @@ struct vadc_channels {
|
|||
unsigned int prescale_index;
|
||||
enum iio_chan_type type;
|
||||
long info_mask;
|
||||
unsigned int scale_fn;
|
||||
};
|
||||
|
||||
#define VADC_CHAN(_dname, _type, _mask, _pre) \
|
||||
#define VADC_CHAN(_dname, _type, _mask, _pre, _scale) \
|
||||
[VADC_##_dname] = { \
|
||||
.datasheet_name = __stringify(_dname), \
|
||||
.prescale_index = _pre, \
|
||||
.type = _type, \
|
||||
.info_mask = _mask, \
|
||||
.scale_fn = _scale \
|
||||
}, \
|
||||
|
||||
#define VADC_NO_CHAN(_dname, _type, _mask, _pre) \
|
||||
[VADC_##_dname] = { \
|
||||
.datasheet_name = __stringify(_dname), \
|
||||
.prescale_index = _pre, \
|
||||
.type = _type, \
|
||||
.info_mask = _mask \
|
||||
}, \
|
||||
},
|
||||
|
||||
#define VADC_CHAN_TEMP(_dname, _pre) \
|
||||
VADC_CHAN(_dname, IIO_TEMP, BIT(IIO_CHAN_INFO_PROCESSED), _pre) \
|
||||
#define VADC_CHAN_TEMP(_dname, _pre, _scale) \
|
||||
VADC_CHAN(_dname, IIO_TEMP, \
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED), \
|
||||
_pre, _scale) \
|
||||
|
||||
#define VADC_CHAN_VOLT(_dname, _pre) \
|
||||
#define VADC_CHAN_VOLT(_dname, _pre, _scale) \
|
||||
VADC_CHAN(_dname, IIO_VOLTAGE, \
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), \
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),\
|
||||
_pre, _scale) \
|
||||
|
||||
#define VADC_CHAN_NO_SCALE(_dname, _pre) \
|
||||
VADC_NO_CHAN(_dname, IIO_VOLTAGE, \
|
||||
BIT(IIO_CHAN_INFO_RAW), \
|
||||
_pre) \
|
||||
|
||||
/*
|
||||
|
@ -626,106 +850,106 @@ struct vadc_channels {
|
|||
* gaps in the array should be treated as reserved channels.
|
||||
*/
|
||||
static const struct vadc_channels vadc_chans[] = {
|
||||
VADC_CHAN_VOLT(USBIN, 4)
|
||||
VADC_CHAN_VOLT(DCIN, 4)
|
||||
VADC_CHAN_VOLT(VCHG_SNS, 3)
|
||||
VADC_CHAN_VOLT(SPARE1_03, 1)
|
||||
VADC_CHAN_VOLT(USB_ID_MV, 1)
|
||||
VADC_CHAN_VOLT(VCOIN, 1)
|
||||
VADC_CHAN_VOLT(VBAT_SNS, 1)
|
||||
VADC_CHAN_VOLT(VSYS, 1)
|
||||
VADC_CHAN_TEMP(DIE_TEMP, 0)
|
||||
VADC_CHAN_VOLT(REF_625MV, 0)
|
||||
VADC_CHAN_VOLT(REF_1250MV, 0)
|
||||
VADC_CHAN_VOLT(CHG_TEMP, 0)
|
||||
VADC_CHAN_VOLT(SPARE1, 0)
|
||||
VADC_CHAN_VOLT(SPARE2, 0)
|
||||
VADC_CHAN_VOLT(GND_REF, 0)
|
||||
VADC_CHAN_VOLT(VDD_VADC, 0)
|
||||
VADC_CHAN_VOLT(USBIN, 4, SCALE_DEFAULT)
|
||||
VADC_CHAN_VOLT(DCIN, 4, SCALE_DEFAULT)
|
||||
VADC_CHAN_NO_SCALE(VCHG_SNS, 3)
|
||||
VADC_CHAN_NO_SCALE(SPARE1_03, 1)
|
||||
VADC_CHAN_NO_SCALE(USB_ID_MV, 1)
|
||||
VADC_CHAN_VOLT(VCOIN, 1, SCALE_DEFAULT)
|
||||
VADC_CHAN_NO_SCALE(VBAT_SNS, 1)
|
||||
VADC_CHAN_VOLT(VSYS, 1, SCALE_DEFAULT)
|
||||
VADC_CHAN_TEMP(DIE_TEMP, 0, SCALE_PMIC_THERM)
|
||||
VADC_CHAN_VOLT(REF_625MV, 0, SCALE_DEFAULT)
|
||||
VADC_CHAN_VOLT(REF_1250MV, 0, SCALE_DEFAULT)
|
||||
VADC_CHAN_NO_SCALE(CHG_TEMP, 0)
|
||||
VADC_CHAN_NO_SCALE(SPARE1, 0)
|
||||
VADC_CHAN_TEMP(SPARE2, 0, SCALE_PMI_CHG_TEMP)
|
||||
VADC_CHAN_VOLT(GND_REF, 0, SCALE_DEFAULT)
|
||||
VADC_CHAN_VOLT(VDD_VADC, 0, SCALE_DEFAULT)
|
||||
|
||||
VADC_CHAN_VOLT(P_MUX1_1_1, 0)
|
||||
VADC_CHAN_VOLT(P_MUX2_1_1, 0)
|
||||
VADC_CHAN_VOLT(P_MUX3_1_1, 0)
|
||||
VADC_CHAN_VOLT(P_MUX4_1_1, 0)
|
||||
VADC_CHAN_VOLT(P_MUX5_1_1, 0)
|
||||
VADC_CHAN_VOLT(P_MUX6_1_1, 0)
|
||||
VADC_CHAN_VOLT(P_MUX7_1_1, 0)
|
||||
VADC_CHAN_VOLT(P_MUX8_1_1, 0)
|
||||
VADC_CHAN_VOLT(P_MUX9_1_1, 0)
|
||||
VADC_CHAN_VOLT(P_MUX10_1_1, 0)
|
||||
VADC_CHAN_VOLT(P_MUX11_1_1, 0)
|
||||
VADC_CHAN_VOLT(P_MUX12_1_1, 0)
|
||||
VADC_CHAN_VOLT(P_MUX13_1_1, 0)
|
||||
VADC_CHAN_VOLT(P_MUX14_1_1, 0)
|
||||
VADC_CHAN_VOLT(P_MUX15_1_1, 0)
|
||||
VADC_CHAN_VOLT(P_MUX16_1_1, 0)
|
||||
VADC_CHAN_NO_SCALE(P_MUX1_1_1, 0)
|
||||
VADC_CHAN_NO_SCALE(P_MUX2_1_1, 0)
|
||||
VADC_CHAN_NO_SCALE(P_MUX3_1_1, 0)
|
||||
VADC_CHAN_NO_SCALE(P_MUX4_1_1, 0)
|
||||
VADC_CHAN_NO_SCALE(P_MUX5_1_1, 0)
|
||||
VADC_CHAN_NO_SCALE(P_MUX6_1_1, 0)
|
||||
VADC_CHAN_NO_SCALE(P_MUX7_1_1, 0)
|
||||
VADC_CHAN_NO_SCALE(P_MUX8_1_1, 0)
|
||||
VADC_CHAN_NO_SCALE(P_MUX9_1_1, 0)
|
||||
VADC_CHAN_NO_SCALE(P_MUX10_1_1, 0)
|
||||
VADC_CHAN_NO_SCALE(P_MUX11_1_1, 0)
|
||||
VADC_CHAN_NO_SCALE(P_MUX12_1_1, 0)
|
||||
VADC_CHAN_NO_SCALE(P_MUX13_1_1, 0)
|
||||
VADC_CHAN_NO_SCALE(P_MUX14_1_1, 0)
|
||||
VADC_CHAN_NO_SCALE(P_MUX15_1_1, 0)
|
||||
VADC_CHAN_NO_SCALE(P_MUX16_1_1, 0)
|
||||
|
||||
VADC_CHAN_VOLT(P_MUX1_1_3, 1)
|
||||
VADC_CHAN_VOLT(P_MUX2_1_3, 1)
|
||||
VADC_CHAN_VOLT(P_MUX3_1_3, 1)
|
||||
VADC_CHAN_VOLT(P_MUX4_1_3, 1)
|
||||
VADC_CHAN_VOLT(P_MUX5_1_3, 1)
|
||||
VADC_CHAN_VOLT(P_MUX6_1_3, 1)
|
||||
VADC_CHAN_VOLT(P_MUX7_1_3, 1)
|
||||
VADC_CHAN_VOLT(P_MUX8_1_3, 1)
|
||||
VADC_CHAN_VOLT(P_MUX9_1_3, 1)
|
||||
VADC_CHAN_VOLT(P_MUX10_1_3, 1)
|
||||
VADC_CHAN_VOLT(P_MUX11_1_3, 1)
|
||||
VADC_CHAN_VOLT(P_MUX12_1_3, 1)
|
||||
VADC_CHAN_VOLT(P_MUX13_1_3, 1)
|
||||
VADC_CHAN_VOLT(P_MUX14_1_3, 1)
|
||||
VADC_CHAN_VOLT(P_MUX15_1_3, 1)
|
||||
VADC_CHAN_VOLT(P_MUX16_1_3, 1)
|
||||
VADC_CHAN_NO_SCALE(P_MUX1_1_3, 1)
|
||||
VADC_CHAN_NO_SCALE(P_MUX2_1_3, 1)
|
||||
VADC_CHAN_NO_SCALE(P_MUX3_1_3, 1)
|
||||
VADC_CHAN_NO_SCALE(P_MUX4_1_3, 1)
|
||||
VADC_CHAN_NO_SCALE(P_MUX5_1_3, 1)
|
||||
VADC_CHAN_NO_SCALE(P_MUX6_1_3, 1)
|
||||
VADC_CHAN_NO_SCALE(P_MUX7_1_3, 1)
|
||||
VADC_CHAN_NO_SCALE(P_MUX8_1_3, 1)
|
||||
VADC_CHAN_NO_SCALE(P_MUX9_1_3, 1)
|
||||
VADC_CHAN_NO_SCALE(P_MUX10_1_3, 1)
|
||||
VADC_CHAN_NO_SCALE(P_MUX11_1_3, 1)
|
||||
VADC_CHAN_NO_SCALE(P_MUX12_1_3, 1)
|
||||
VADC_CHAN_NO_SCALE(P_MUX13_1_3, 1)
|
||||
VADC_CHAN_NO_SCALE(P_MUX14_1_3, 1)
|
||||
VADC_CHAN_NO_SCALE(P_MUX15_1_3, 1)
|
||||
VADC_CHAN_NO_SCALE(P_MUX16_1_3, 1)
|
||||
|
||||
VADC_CHAN_VOLT(LR_MUX1_BAT_THERM, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX2_BAT_ID, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX3_XO_THERM, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX4_AMUX_THM1, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX5_AMUX_THM2, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX6_AMUX_THM3, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX7_HW_ID, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX8_AMUX_THM4, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX9_AMUX_THM5, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX10_USB_ID, 0)
|
||||
VADC_CHAN_VOLT(AMUX_PU1, 0)
|
||||
VADC_CHAN_VOLT(AMUX_PU2, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX3_BUF_XO_THERM, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX1_BAT_THERM, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX2_BAT_ID, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX3_XO_THERM, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX4_AMUX_THM1, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX5_AMUX_THM2, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX6_AMUX_THM3, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX7_HW_ID, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX8_AMUX_THM4, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX9_AMUX_THM5, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX10_USB_ID, 0)
|
||||
VADC_CHAN_NO_SCALE(AMUX_PU1, 0)
|
||||
VADC_CHAN_NO_SCALE(AMUX_PU2, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX3_BUF_XO_THERM, 0)
|
||||
|
||||
VADC_CHAN_VOLT(LR_MUX1_PU1_BAT_THERM, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX2_PU1_BAT_ID, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX3_PU1_XO_THERM, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX4_PU1_AMUX_THM1, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX5_PU1_AMUX_THM2, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX6_PU1_AMUX_THM3, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX7_PU1_AMUX_HW_ID, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX8_PU1_AMUX_THM4, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX9_PU1_AMUX_THM5, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX10_PU1_AMUX_USB_ID, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX3_BUF_PU1_XO_THERM, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX1_PU1_BAT_THERM, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX2_PU1_BAT_ID, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX3_PU1_XO_THERM, 0)
|
||||
VADC_CHAN_TEMP(LR_MUX4_PU1_AMUX_THM1, 0, SCALE_THERM_100K_PULLUP)
|
||||
VADC_CHAN_TEMP(LR_MUX5_PU1_AMUX_THM2, 0, SCALE_THERM_100K_PULLUP)
|
||||
VADC_CHAN_TEMP(LR_MUX6_PU1_AMUX_THM3, 0, SCALE_THERM_100K_PULLUP)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX7_PU1_AMUX_HW_ID, 0)
|
||||
VADC_CHAN_TEMP(LR_MUX8_PU1_AMUX_THM4, 0, SCALE_THERM_100K_PULLUP)
|
||||
VADC_CHAN_TEMP(LR_MUX9_PU1_AMUX_THM5, 0, SCALE_THERM_100K_PULLUP)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX10_PU1_AMUX_USB_ID, 0)
|
||||
VADC_CHAN_TEMP(LR_MUX3_BUF_PU1_XO_THERM, 0, SCALE_XOTHERM)
|
||||
|
||||
VADC_CHAN_VOLT(LR_MUX1_PU2_BAT_THERM, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX2_PU2_BAT_ID, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX3_PU2_XO_THERM, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX4_PU2_AMUX_THM1, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX5_PU2_AMUX_THM2, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX6_PU2_AMUX_THM3, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX7_PU2_AMUX_HW_ID, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX8_PU2_AMUX_THM4, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX9_PU2_AMUX_THM5, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX10_PU2_AMUX_USB_ID, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX3_BUF_PU2_XO_THERM, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX1_PU2_BAT_THERM, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX2_PU2_BAT_ID, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX3_PU2_XO_THERM, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX4_PU2_AMUX_THM1, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX5_PU2_AMUX_THM2, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX6_PU2_AMUX_THM3, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX7_PU2_AMUX_HW_ID, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX8_PU2_AMUX_THM4, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX9_PU2_AMUX_THM5, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX10_PU2_AMUX_USB_ID, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX3_BUF_PU2_XO_THERM, 0)
|
||||
|
||||
VADC_CHAN_VOLT(LR_MUX1_PU1_PU2_BAT_THERM, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX2_PU1_PU2_BAT_ID, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX3_PU1_PU2_XO_THERM, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX4_PU1_PU2_AMUX_THM1, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX5_PU1_PU2_AMUX_THM2, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX6_PU1_PU2_AMUX_THM3, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX7_PU1_PU2_AMUX_HW_ID, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX8_PU1_PU2_AMUX_THM4, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX9_PU1_PU2_AMUX_THM5, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX10_PU1_PU2_AMUX_USB_ID, 0)
|
||||
VADC_CHAN_VOLT(LR_MUX3_BUF_PU1_PU2_XO_THERM, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX1_PU1_PU2_BAT_THERM, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX2_PU1_PU2_BAT_ID, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX3_PU1_PU2_XO_THERM, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX4_PU1_PU2_AMUX_THM1, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX5_PU1_PU2_AMUX_THM2, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX6_PU1_PU2_AMUX_THM3, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX7_PU1_PU2_AMUX_HW_ID, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX8_PU1_PU2_AMUX_THM4, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX9_PU1_PU2_AMUX_THM5, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX10_PU1_PU2_AMUX_USB_ID, 0)
|
||||
VADC_CHAN_NO_SCALE(LR_MUX3_BUF_PU1_PU2_XO_THERM, 0)
|
||||
};
|
||||
|
||||
static int vadc_get_dt_channel_data(struct device *dev,
|
||||
|
@ -844,6 +1068,7 @@ static int vadc_get_dt_data(struct vadc_priv *vadc, struct device_node *node)
|
|||
return ret;
|
||||
}
|
||||
|
||||
prop.scale_fn = vadc_chans[prop.channel].scale_fn;
|
||||
vadc->chan_props[index] = prop;
|
||||
|
||||
vadc_chan = &vadc_chans[prop.channel];
|
||||
|
|
|
@ -0,0 +1,490 @@
|
|||
/*
|
||||
* Texas Instruments ADS7950 SPI ADC driver
|
||||
*
|
||||
* Copyright 2016 David Lechner <david@lechnology.com>
|
||||
*
|
||||
* Based on iio/ad7923.c:
|
||||
* Copyright 2011 Analog Devices Inc
|
||||
* Copyright 2012 CS Systemes d'Information
|
||||
*
|
||||
* And also on hwmon/ads79xx.c
|
||||
* Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* Nishanth Menon
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; 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/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#define TI_ADS7950_CR_MANUAL BIT(12)
|
||||
#define TI_ADS7950_CR_WRITE BIT(11)
|
||||
#define TI_ADS7950_CR_CHAN(ch) ((ch) << 7)
|
||||
#define TI_ADS7950_CR_RANGE_5V BIT(6)
|
||||
|
||||
#define TI_ADS7950_MAX_CHAN 16
|
||||
|
||||
#define TI_ADS7950_TIMESTAMP_SIZE (sizeof(int64_t) / sizeof(__be16))
|
||||
|
||||
/* val = value, dec = left shift, bits = number of bits of the mask */
|
||||
#define TI_ADS7950_EXTRACT(val, dec, bits) \
|
||||
(((val) >> (dec)) & ((1 << (bits)) - 1))
|
||||
|
||||
struct ti_ads7950_state {
|
||||
struct spi_device *spi;
|
||||
struct spi_transfer ring_xfer[TI_ADS7950_MAX_CHAN + 2];
|
||||
struct spi_transfer scan_single_xfer[3];
|
||||
struct spi_message ring_msg;
|
||||
struct spi_message scan_single_msg;
|
||||
|
||||
struct regulator *reg;
|
||||
|
||||
unsigned int settings;
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
__be16 rx_buf[TI_ADS7950_MAX_CHAN + TI_ADS7950_TIMESTAMP_SIZE]
|
||||
____cacheline_aligned;
|
||||
__be16 tx_buf[TI_ADS7950_MAX_CHAN];
|
||||
};
|
||||
|
||||
struct ti_ads7950_chip_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int num_channels;
|
||||
};
|
||||
|
||||
enum ti_ads7950_id {
|
||||
TI_ADS7950,
|
||||
TI_ADS7951,
|
||||
TI_ADS7952,
|
||||
TI_ADS7953,
|
||||
TI_ADS7954,
|
||||
TI_ADS7955,
|
||||
TI_ADS7956,
|
||||
TI_ADS7957,
|
||||
TI_ADS7958,
|
||||
TI_ADS7959,
|
||||
TI_ADS7960,
|
||||
TI_ADS7961,
|
||||
};
|
||||
|
||||
#define TI_ADS7950_V_CHAN(index, bits) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = index, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.address = index, \
|
||||
.datasheet_name = "CH##index", \
|
||||
.scan_index = index, \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = bits, \
|
||||
.storagebits = 16, \
|
||||
.shift = 12 - (bits), \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define DECLARE_TI_ADS7950_4_CHANNELS(name, bits) \
|
||||
const struct iio_chan_spec name ## _channels[] = { \
|
||||
TI_ADS7950_V_CHAN(0, bits), \
|
||||
TI_ADS7950_V_CHAN(1, bits), \
|
||||
TI_ADS7950_V_CHAN(2, bits), \
|
||||
TI_ADS7950_V_CHAN(3, bits), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4), \
|
||||
}
|
||||
|
||||
#define DECLARE_TI_ADS7950_8_CHANNELS(name, bits) \
|
||||
const struct iio_chan_spec name ## _channels[] = { \
|
||||
TI_ADS7950_V_CHAN(0, bits), \
|
||||
TI_ADS7950_V_CHAN(1, bits), \
|
||||
TI_ADS7950_V_CHAN(2, bits), \
|
||||
TI_ADS7950_V_CHAN(3, bits), \
|
||||
TI_ADS7950_V_CHAN(4, bits), \
|
||||
TI_ADS7950_V_CHAN(5, bits), \
|
||||
TI_ADS7950_V_CHAN(6, bits), \
|
||||
TI_ADS7950_V_CHAN(7, bits), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(8), \
|
||||
}
|
||||
|
||||
#define DECLARE_TI_ADS7950_12_CHANNELS(name, bits) \
|
||||
const struct iio_chan_spec name ## _channels[] = { \
|
||||
TI_ADS7950_V_CHAN(0, bits), \
|
||||
TI_ADS7950_V_CHAN(1, bits), \
|
||||
TI_ADS7950_V_CHAN(2, bits), \
|
||||
TI_ADS7950_V_CHAN(3, bits), \
|
||||
TI_ADS7950_V_CHAN(4, bits), \
|
||||
TI_ADS7950_V_CHAN(5, bits), \
|
||||
TI_ADS7950_V_CHAN(6, bits), \
|
||||
TI_ADS7950_V_CHAN(7, bits), \
|
||||
TI_ADS7950_V_CHAN(8, bits), \
|
||||
TI_ADS7950_V_CHAN(9, bits), \
|
||||
TI_ADS7950_V_CHAN(10, bits), \
|
||||
TI_ADS7950_V_CHAN(11, bits), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(12), \
|
||||
}
|
||||
|
||||
#define DECLARE_TI_ADS7950_16_CHANNELS(name, bits) \
|
||||
const struct iio_chan_spec name ## _channels[] = { \
|
||||
TI_ADS7950_V_CHAN(0, bits), \
|
||||
TI_ADS7950_V_CHAN(1, bits), \
|
||||
TI_ADS7950_V_CHAN(2, bits), \
|
||||
TI_ADS7950_V_CHAN(3, bits), \
|
||||
TI_ADS7950_V_CHAN(4, bits), \
|
||||
TI_ADS7950_V_CHAN(5, bits), \
|
||||
TI_ADS7950_V_CHAN(6, bits), \
|
||||
TI_ADS7950_V_CHAN(7, bits), \
|
||||
TI_ADS7950_V_CHAN(8, bits), \
|
||||
TI_ADS7950_V_CHAN(9, bits), \
|
||||
TI_ADS7950_V_CHAN(10, bits), \
|
||||
TI_ADS7950_V_CHAN(11, bits), \
|
||||
TI_ADS7950_V_CHAN(12, bits), \
|
||||
TI_ADS7950_V_CHAN(13, bits), \
|
||||
TI_ADS7950_V_CHAN(14, bits), \
|
||||
TI_ADS7950_V_CHAN(15, bits), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(16), \
|
||||
}
|
||||
|
||||
static DECLARE_TI_ADS7950_4_CHANNELS(ti_ads7950, 12);
|
||||
static DECLARE_TI_ADS7950_8_CHANNELS(ti_ads7951, 12);
|
||||
static DECLARE_TI_ADS7950_12_CHANNELS(ti_ads7952, 12);
|
||||
static DECLARE_TI_ADS7950_16_CHANNELS(ti_ads7953, 12);
|
||||
static DECLARE_TI_ADS7950_4_CHANNELS(ti_ads7954, 10);
|
||||
static DECLARE_TI_ADS7950_8_CHANNELS(ti_ads7955, 10);
|
||||
static DECLARE_TI_ADS7950_12_CHANNELS(ti_ads7956, 10);
|
||||
static DECLARE_TI_ADS7950_16_CHANNELS(ti_ads7957, 10);
|
||||
static DECLARE_TI_ADS7950_4_CHANNELS(ti_ads7958, 8);
|
||||
static DECLARE_TI_ADS7950_8_CHANNELS(ti_ads7959, 8);
|
||||
static DECLARE_TI_ADS7950_12_CHANNELS(ti_ads7960, 8);
|
||||
static DECLARE_TI_ADS7950_16_CHANNELS(ti_ads7961, 8);
|
||||
|
||||
static const struct ti_ads7950_chip_info ti_ads7950_chip_info[] = {
|
||||
[TI_ADS7950] = {
|
||||
.channels = ti_ads7950_channels,
|
||||
.num_channels = ARRAY_SIZE(ti_ads7950_channels),
|
||||
},
|
||||
[TI_ADS7951] = {
|
||||
.channels = ti_ads7951_channels,
|
||||
.num_channels = ARRAY_SIZE(ti_ads7951_channels),
|
||||
},
|
||||
[TI_ADS7952] = {
|
||||
.channels = ti_ads7952_channels,
|
||||
.num_channels = ARRAY_SIZE(ti_ads7952_channels),
|
||||
},
|
||||
[TI_ADS7953] = {
|
||||
.channels = ti_ads7953_channels,
|
||||
.num_channels = ARRAY_SIZE(ti_ads7953_channels),
|
||||
},
|
||||
[TI_ADS7954] = {
|
||||
.channels = ti_ads7954_channels,
|
||||
.num_channels = ARRAY_SIZE(ti_ads7954_channels),
|
||||
},
|
||||
[TI_ADS7955] = {
|
||||
.channels = ti_ads7955_channels,
|
||||
.num_channels = ARRAY_SIZE(ti_ads7955_channels),
|
||||
},
|
||||
[TI_ADS7956] = {
|
||||
.channels = ti_ads7956_channels,
|
||||
.num_channels = ARRAY_SIZE(ti_ads7956_channels),
|
||||
},
|
||||
[TI_ADS7957] = {
|
||||
.channels = ti_ads7957_channels,
|
||||
.num_channels = ARRAY_SIZE(ti_ads7957_channels),
|
||||
},
|
||||
[TI_ADS7958] = {
|
||||
.channels = ti_ads7958_channels,
|
||||
.num_channels = ARRAY_SIZE(ti_ads7958_channels),
|
||||
},
|
||||
[TI_ADS7959] = {
|
||||
.channels = ti_ads7959_channels,
|
||||
.num_channels = ARRAY_SIZE(ti_ads7959_channels),
|
||||
},
|
||||
[TI_ADS7960] = {
|
||||
.channels = ti_ads7960_channels,
|
||||
.num_channels = ARRAY_SIZE(ti_ads7960_channels),
|
||||
},
|
||||
[TI_ADS7961] = {
|
||||
.channels = ti_ads7961_channels,
|
||||
.num_channels = ARRAY_SIZE(ti_ads7961_channels),
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* ti_ads7950_update_scan_mode() setup the spi transfer buffer for the new
|
||||
* scan mask
|
||||
*/
|
||||
static int ti_ads7950_update_scan_mode(struct iio_dev *indio_dev,
|
||||
const unsigned long *active_scan_mask)
|
||||
{
|
||||
struct ti_ads7950_state *st = iio_priv(indio_dev);
|
||||
int i, cmd, len;
|
||||
|
||||
len = 0;
|
||||
for_each_set_bit(i, active_scan_mask, indio_dev->num_channels) {
|
||||
cmd = TI_ADS7950_CR_WRITE | TI_ADS7950_CR_CHAN(i) | st->settings;
|
||||
st->tx_buf[len++] = cpu_to_be16(cmd);
|
||||
}
|
||||
|
||||
/* Data for the 1st channel is not returned until the 3rd transfer */
|
||||
len += 2;
|
||||
for (i = 0; i < len; i++) {
|
||||
if ((i + 2) < len)
|
||||
st->ring_xfer[i].tx_buf = &st->tx_buf[i];
|
||||
if (i >= 2)
|
||||
st->ring_xfer[i].rx_buf = &st->rx_buf[i - 2];
|
||||
st->ring_xfer[i].len = 2;
|
||||
st->ring_xfer[i].cs_change = 1;
|
||||
}
|
||||
/* make sure last transfer's cs_change is not set */
|
||||
st->ring_xfer[len - 1].cs_change = 0;
|
||||
|
||||
spi_message_init_with_transfers(&st->ring_msg, st->ring_xfer, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t ti_ads7950_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct ti_ads7950_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = spi_sync(st->spi, &st->ring_msg);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf,
|
||||
iio_get_time_ns(indio_dev));
|
||||
|
||||
out:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ti_ads7950_scan_direct(struct ti_ads7950_state *st, unsigned int ch)
|
||||
{
|
||||
int ret, cmd;
|
||||
|
||||
cmd = TI_ADS7950_CR_WRITE | TI_ADS7950_CR_CHAN(ch) | st->settings;
|
||||
st->tx_buf[0] = cpu_to_be16(cmd);
|
||||
|
||||
ret = spi_sync(st->spi, &st->scan_single_msg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return be16_to_cpu(st->rx_buf[0]);
|
||||
}
|
||||
|
||||
static int ti_ads7950_get_range(struct ti_ads7950_state *st)
|
||||
{
|
||||
int vref;
|
||||
|
||||
vref = regulator_get_voltage(st->reg);
|
||||
if (vref < 0)
|
||||
return vref;
|
||||
|
||||
vref /= 1000;
|
||||
|
||||
if (st->settings & TI_ADS7950_CR_RANGE_5V)
|
||||
vref *= 2;
|
||||
|
||||
return vref;
|
||||
}
|
||||
|
||||
static int ti_ads7950_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long m)
|
||||
{
|
||||
struct ti_ads7950_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = ti_ads7950_scan_direct(st, chan->address);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (chan->address != TI_ADS7950_EXTRACT(ret, 12, 4))
|
||||
return -EIO;
|
||||
|
||||
*val = TI_ADS7950_EXTRACT(ret, chan->scan_type.shift,
|
||||
chan->scan_type.realbits);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = ti_ads7950_get_range(st);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = ret;
|
||||
*val2 = (1 << chan->scan_type.realbits) - 1;
|
||||
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info ti_ads7950_info = {
|
||||
.read_raw = &ti_ads7950_read_raw,
|
||||
.update_scan_mode = ti_ads7950_update_scan_mode,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int ti_ads7950_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ti_ads7950_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
const struct ti_ads7950_chip_info *info;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st->spi = spi;
|
||||
st->settings = TI_ADS7950_CR_MANUAL | TI_ADS7950_CR_RANGE_5V;
|
||||
|
||||
info = &ti_ads7950_chip_info[spi_get_device_id(spi)->driver_data];
|
||||
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = info->channels;
|
||||
indio_dev->num_channels = info->num_channels;
|
||||
indio_dev->info = &ti_ads7950_info;
|
||||
|
||||
/*
|
||||
* Setup default message. The sample is read at the end of the first
|
||||
* transfer, then it takes one full cycle to convert the sample and one
|
||||
* more cycle to send the value. The conversion process is driven by
|
||||
* the SPI clock, which is why we have 3 transfers. The middle one is
|
||||
* just dummy data sent while the chip is converting the sample that
|
||||
* was read at the end of the first transfer.
|
||||
*/
|
||||
|
||||
st->scan_single_xfer[0].tx_buf = &st->tx_buf[0];
|
||||
st->scan_single_xfer[0].len = 2;
|
||||
st->scan_single_xfer[0].cs_change = 1;
|
||||
st->scan_single_xfer[1].tx_buf = &st->tx_buf[0];
|
||||
st->scan_single_xfer[1].len = 2;
|
||||
st->scan_single_xfer[1].cs_change = 1;
|
||||
st->scan_single_xfer[2].rx_buf = &st->rx_buf[0];
|
||||
st->scan_single_xfer[2].len = 2;
|
||||
|
||||
spi_message_init_with_transfers(&st->scan_single_msg,
|
||||
st->scan_single_xfer, 3);
|
||||
|
||||
st->reg = devm_regulator_get(&spi->dev, "refin");
|
||||
if (IS_ERR(st->reg)) {
|
||||
dev_err(&spi->dev, "Failed get get regulator \"refin\"\n");
|
||||
return PTR_ERR(st->reg);
|
||||
}
|
||||
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to enable regulator \"refin\"\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
&ti_ads7950_trigger_handler, NULL);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to setup triggered buffer\n");
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to register iio device\n");
|
||||
goto error_cleanup_ring;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_cleanup_ring:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
error_disable_reg:
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ti_ads7950_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ti_ads7950_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ti_ads7950_id[] = {
|
||||
{"ti-ads7950", TI_ADS7950},
|
||||
{"ti-ads7951", TI_ADS7951},
|
||||
{"ti-ads7952", TI_ADS7952},
|
||||
{"ti-ads7953", TI_ADS7953},
|
||||
{"ti-ads7954", TI_ADS7954},
|
||||
{"ti-ads7955", TI_ADS7955},
|
||||
{"ti-ads7956", TI_ADS7956},
|
||||
{"ti-ads7957", TI_ADS7957},
|
||||
{"ti-ads7958", TI_ADS7958},
|
||||
{"ti-ads7959", TI_ADS7959},
|
||||
{"ti-ads7960", TI_ADS7960},
|
||||
{"ti-ads7961", TI_ADS7961},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ti_ads7950_id);
|
||||
|
||||
static struct spi_driver ti_ads7950_driver = {
|
||||
.driver = {
|
||||
.name = "ti-ads7950",
|
||||
},
|
||||
.probe = ti_ads7950_probe,
|
||||
.remove = ti_ads7950_remove,
|
||||
.id_table = ti_ads7950_id,
|
||||
};
|
||||
module_spi_driver(ti_ads7950_driver);
|
||||
|
||||
MODULE_AUTHOR("David Lechner <david@lechnology.com>");
|
||||
MODULE_DESCRIPTION("TI TI_ADS7950 ADC");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -10,7 +10,8 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer_impl.h>
|
||||
#include <linux/iio/consumer.h>
|
||||
|
||||
struct iio_cb_buffer {
|
||||
|
|
|
@ -5,7 +5,10 @@
|
|||
#include <linux/workqueue.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/iio/buffer_impl.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/poll.h>
|
||||
|
||||
|
|
|
@ -58,6 +58,10 @@ static struct {
|
|||
|
||||
{HID_USAGE_SENSOR_PRESSURE, 0, 100, 0},
|
||||
{HID_USAGE_SENSOR_PRESSURE, HID_USAGE_SENSOR_UNITS_PASCAL, 0, 1000000},
|
||||
|
||||
{HID_USAGE_SENSOR_TIME_TIMESTAMP, 0, 1000000000, 0},
|
||||
{HID_USAGE_SENSOR_TIME_TIMESTAMP, HID_USAGE_SENSOR_UNITS_MILLISECOND,
|
||||
1000000, 0},
|
||||
};
|
||||
|
||||
static int pow_10(unsigned power)
|
||||
|
@ -346,6 +350,13 @@ int hid_sensor_format_scale(u32 usage_id,
|
|||
}
|
||||
EXPORT_SYMBOL(hid_sensor_format_scale);
|
||||
|
||||
int64_t hid_sensor_convert_timestamp(struct hid_sensor_common *st,
|
||||
int64_t raw_value)
|
||||
{
|
||||
return st->timestamp_ns_scale * raw_value;
|
||||
}
|
||||
EXPORT_SYMBOL(hid_sensor_convert_timestamp);
|
||||
|
||||
static
|
||||
int hid_sensor_get_reporting_interval(struct hid_sensor_hub_device *hsdev,
|
||||
u32 usage_id,
|
||||
|
@ -367,6 +378,7 @@ int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev,
|
|||
struct hid_sensor_common *st)
|
||||
{
|
||||
|
||||
struct hid_sensor_hub_attribute_info timestamp;
|
||||
|
||||
hid_sensor_get_reporting_interval(hsdev, usage_id, st);
|
||||
|
||||
|
@ -385,11 +397,25 @@ int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev,
|
|||
HID_USAGE_SENSOR_PROP_SENSITIVITY_ABS,
|
||||
&st->sensitivity);
|
||||
|
||||
hid_dbg(hsdev->hdev, "common attributes: %x:%x, %x:%x, %x:%x %x:%x\n",
|
||||
st->poll.index, st->poll.report_id,
|
||||
st->report_state.index, st->report_state.report_id,
|
||||
st->power_state.index, st->power_state.report_id,
|
||||
st->sensitivity.index, st->sensitivity.report_id);
|
||||
sensor_hub_input_get_attribute_info(hsdev,
|
||||
HID_INPUT_REPORT, usage_id,
|
||||
HID_USAGE_SENSOR_TIME_TIMESTAMP,
|
||||
×tamp);
|
||||
if (timestamp.index >= 0 && timestamp.report_id) {
|
||||
int val0, val1;
|
||||
|
||||
hid_sensor_format_scale(HID_USAGE_SENSOR_TIME_TIMESTAMP,
|
||||
×tamp, &val0, &val1);
|
||||
st->timestamp_ns_scale = val0;
|
||||
} else
|
||||
st->timestamp_ns_scale = 1000000000;
|
||||
|
||||
hid_dbg(hsdev->hdev, "common attributes: %x:%x, %x:%x, %x:%x %x:%x %x:%x\n",
|
||||
st->poll.index, st->poll.report_id,
|
||||
st->report_state.index, st->report_state.report_id,
|
||||
st->power_state.index, st->power_state.report_id,
|
||||
st->sensitivity.index, st->sensitivity.report_id,
|
||||
timestamp.index, timestamp.report_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/iio/common/ssp_sensors.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors_i2c.h>
|
||||
|
||||
|
@ -107,6 +108,25 @@ void st_sensors_of_i2c_probe(struct i2c_client *client,
|
|||
EXPORT_SYMBOL(st_sensors_of_i2c_probe);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
int st_sensors_match_acpi_device(struct device *dev)
|
||||
{
|
||||
const struct acpi_device_id *acpi_id;
|
||||
kernel_ulong_t driver_data = 0;
|
||||
|
||||
if (ACPI_HANDLE(dev)) {
|
||||
acpi_id = acpi_match_device(dev->driver->acpi_match_table, dev);
|
||||
if (!acpi_id) {
|
||||
dev_err(dev, "No driver data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
driver_data = acpi_id->driver_data;
|
||||
}
|
||||
return driver_data;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_match_acpi_device);
|
||||
#endif
|
||||
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics ST-sensors i2c driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#define AD5592R_GPIO_READBACK_EN BIT(10)
|
||||
#define AD5592R_LDAC_READBACK_EN BIT(6)
|
||||
|
@ -148,10 +149,17 @@ static const struct of_device_id ad5592r_of_match[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(of, ad5592r_of_match);
|
||||
|
||||
static const struct acpi_device_id ad5592r_acpi_match[] = {
|
||||
{"ADS5592", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, ad5592r_acpi_match);
|
||||
|
||||
static struct spi_driver ad5592r_spi_driver = {
|
||||
.driver = {
|
||||
.name = "ad5592r",
|
||||
.of_match_table = of_match_ptr(ad5592r_of_match),
|
||||
.acpi_match_table = ACPI_PTR(ad5592r_acpi_match),
|
||||
},
|
||||
.probe = ad5592r_spi_probe,
|
||||
.remove = ad5592r_spi_remove,
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#define AD5593R_MODE_CONF (0 << 4)
|
||||
#define AD5593R_MODE_DAC_WRITE (1 << 4)
|
||||
|
@ -115,10 +116,17 @@ static const struct of_device_id ad5593r_of_match[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(of, ad5593r_of_match);
|
||||
|
||||
static const struct acpi_device_id ad5593r_acpi_match[] = {
|
||||
{"ADS5593", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, ad5593r_acpi_match);
|
||||
|
||||
static struct i2c_driver ad5593r_driver = {
|
||||
.driver = {
|
||||
.name = "ad5593r",
|
||||
.of_match_table = of_match_ptr(ad5593r_of_match),
|
||||
.acpi_match_table = ACPI_PTR(ad5593r_acpi_match),
|
||||
},
|
||||
.probe = ad5593r_i2c_probe,
|
||||
.remove = ad5593r_i2c_remove,
|
||||
|
|
|
@ -88,11 +88,11 @@ static inline int
|
|||
iio_simple_dummy_events_register(struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
static inline void
|
||||
iio_simple_dummy_events_unregister(struct iio_dev *indio_dev)
|
||||
{ };
|
||||
{}
|
||||
|
||||
#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS*/
|
||||
|
||||
|
@ -119,11 +119,11 @@ void iio_simple_dummy_unconfigure_buffer(struct iio_dev *indio_dev);
|
|||
static inline int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
static inline
|
||||
void iio_simple_dummy_unconfigure_buffer(struct iio_dev *indio_dev)
|
||||
{};
|
||||
{}
|
||||
|
||||
#endif /* CONFIG_IIO_SIMPLE_DUMMY_BUFFER */
|
||||
#endif /* _IIO_SIMPLE_DUMMY_H_ */
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
|
||||
#include "iio_simple_dummy.h"
|
||||
|
@ -131,9 +132,6 @@ int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev)
|
|||
|
||||
iio_device_attach_buffer(indio_dev, buffer);
|
||||
|
||||
/* Enable timestamps by default */
|
||||
buffer->scan_timestamp = true;
|
||||
|
||||
/*
|
||||
* Tell the core what device type specific functions should
|
||||
* be run on either side of buffer capture enable / disable.
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include <linux/iio/common/ssp_sensors.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/slab.h>
|
||||
#include "hts221.h"
|
||||
|
@ -83,6 +84,12 @@ static int hts221_i2c_probe(struct i2c_client *client,
|
|||
return hts221_probe(iio_dev);
|
||||
}
|
||||
|
||||
static const struct acpi_device_id hts221_acpi_match[] = {
|
||||
{"SMO9100", 0},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, hts221_acpi_match);
|
||||
|
||||
static const struct of_device_id hts221_i2c_of_match[] = {
|
||||
{ .compatible = "st,hts221", },
|
||||
{},
|
||||
|
@ -99,6 +106,7 @@ static struct i2c_driver hts221_driver = {
|
|||
.driver = {
|
||||
.name = "hts221_i2c",
|
||||
.of_match_table = of_match_ptr(hts221_i2c_of_match),
|
||||
.acpi_match_table = ACPI_PTR(hts221_acpi_match),
|
||||
},
|
||||
.probe = hts221_i2c_probe,
|
||||
.id_table = hts221_i2c_id_table,
|
||||
|
|
|
@ -11,10 +11,11 @@
|
|||
* - 0x68 if SDO is pulled to GND
|
||||
* - 0x69 if SDO is pulled to VDDIO
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "bmi160.h"
|
||||
|
||||
|
@ -56,10 +57,19 @@ static const struct acpi_device_id bmi160_acpi_match[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(acpi, bmi160_acpi_match);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id bmi160_of_match[] = {
|
||||
{ .compatible = "bosch,bmi160" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bmi160_of_match);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver bmi160_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "bmi160_i2c",
|
||||
.acpi_match_table = ACPI_PTR(bmi160_acpi_match),
|
||||
.of_match_table = of_match_ptr(bmi160_of_match),
|
||||
},
|
||||
.probe = bmi160_i2c_probe,
|
||||
.remove = bmi160_i2c_remove,
|
||||
|
|
|
@ -7,10 +7,11 @@
|
|||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include "bmi160.h"
|
||||
|
||||
|
@ -47,13 +48,22 @@ static const struct acpi_device_id bmi160_acpi_match[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(acpi, bmi160_acpi_match);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id bmi160_of_match[] = {
|
||||
{ .compatible = "bosch,bmi160" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bmi160_of_match);
|
||||
#endif
|
||||
|
||||
static struct spi_driver bmi160_spi_driver = {
|
||||
.probe = bmi160_spi_probe,
|
||||
.remove = bmi160_spi_remove,
|
||||
.id_table = bmi160_spi_id,
|
||||
.driver = {
|
||||
.acpi_match_table = ACPI_PTR(bmi160_acpi_match),
|
||||
.name = "bmi160_spi",
|
||||
.acpi_match_table = ACPI_PTR(bmi160_acpi_match),
|
||||
.of_match_table = of_match_ptr(bmi160_of_match),
|
||||
.name = "bmi160_spi",
|
||||
},
|
||||
};
|
||||
module_spi_driver(bmi160_spi_driver);
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "iio_core.h"
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/buffer_impl.h>
|
||||
|
||||
static const char * const iio_endian_prefix[] = {
|
||||
[IIO_BE] = "be",
|
||||
|
@ -209,6 +210,18 @@ void iio_buffer_init(struct iio_buffer *buffer)
|
|||
}
|
||||
EXPORT_SYMBOL(iio_buffer_init);
|
||||
|
||||
/**
|
||||
* iio_buffer_set_attrs - Set buffer specific attributes
|
||||
* @buffer: The buffer for which we are setting attributes
|
||||
* @attrs: Pointer to a null terminated list of pointers to attributes
|
||||
*/
|
||||
void iio_buffer_set_attrs(struct iio_buffer *buffer,
|
||||
const struct attribute **attrs)
|
||||
{
|
||||
buffer->attrs = attrs;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_buffer_set_attrs);
|
||||
|
||||
static ssize_t iio_show_scan_index(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
@ -346,6 +359,19 @@ static int iio_scan_mask_clear(struct iio_buffer *buffer, int bit)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int iio_scan_mask_query(struct iio_dev *indio_dev,
|
||||
struct iio_buffer *buffer, int bit)
|
||||
{
|
||||
if (bit > indio_dev->masklength)
|
||||
return -EINVAL;
|
||||
|
||||
if (!buffer->scan_mask)
|
||||
return 0;
|
||||
|
||||
/* Ensure return value is 0 or 1. */
|
||||
return !!test_bit(bit, buffer->scan_mask);
|
||||
};
|
||||
|
||||
static ssize_t iio_scan_el_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
|
@ -751,6 +777,135 @@ static int iio_verify_update(struct iio_dev *indio_dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* struct iio_demux_table - table describing demux memcpy ops
|
||||
* @from: index to copy from
|
||||
* @to: index to copy to
|
||||
* @length: how many bytes to copy
|
||||
* @l: list head used for management
|
||||
*/
|
||||
struct iio_demux_table {
|
||||
unsigned from;
|
||||
unsigned to;
|
||||
unsigned length;
|
||||
struct list_head l;
|
||||
};
|
||||
|
||||
static void iio_buffer_demux_free(struct iio_buffer *buffer)
|
||||
{
|
||||
struct iio_demux_table *p, *q;
|
||||
list_for_each_entry_safe(p, q, &buffer->demux_list, l) {
|
||||
list_del(&p->l);
|
||||
kfree(p);
|
||||
}
|
||||
}
|
||||
|
||||
static int iio_buffer_add_demux(struct iio_buffer *buffer,
|
||||
struct iio_demux_table **p, unsigned int in_loc, unsigned int out_loc,
|
||||
unsigned int length)
|
||||
{
|
||||
|
||||
if (*p && (*p)->from + (*p)->length == in_loc &&
|
||||
(*p)->to + (*p)->length == out_loc) {
|
||||
(*p)->length += length;
|
||||
} else {
|
||||
*p = kmalloc(sizeof(**p), GFP_KERNEL);
|
||||
if (*p == NULL)
|
||||
return -ENOMEM;
|
||||
(*p)->from = in_loc;
|
||||
(*p)->to = out_loc;
|
||||
(*p)->length = length;
|
||||
list_add_tail(&(*p)->l, &buffer->demux_list);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iio_buffer_update_demux(struct iio_dev *indio_dev,
|
||||
struct iio_buffer *buffer)
|
||||
{
|
||||
int ret, in_ind = -1, out_ind, length;
|
||||
unsigned in_loc = 0, out_loc = 0;
|
||||
struct iio_demux_table *p = NULL;
|
||||
|
||||
/* Clear out any old demux */
|
||||
iio_buffer_demux_free(buffer);
|
||||
kfree(buffer->demux_bounce);
|
||||
buffer->demux_bounce = NULL;
|
||||
|
||||
/* First work out which scan mode we will actually have */
|
||||
if (bitmap_equal(indio_dev->active_scan_mask,
|
||||
buffer->scan_mask,
|
||||
indio_dev->masklength))
|
||||
return 0;
|
||||
|
||||
/* Now we have the two masks, work from least sig and build up sizes */
|
||||
for_each_set_bit(out_ind,
|
||||
buffer->scan_mask,
|
||||
indio_dev->masklength) {
|
||||
in_ind = find_next_bit(indio_dev->active_scan_mask,
|
||||
indio_dev->masklength,
|
||||
in_ind + 1);
|
||||
while (in_ind != out_ind) {
|
||||
in_ind = find_next_bit(indio_dev->active_scan_mask,
|
||||
indio_dev->masklength,
|
||||
in_ind + 1);
|
||||
length = iio_storage_bytes_for_si(indio_dev, in_ind);
|
||||
/* Make sure we are aligned */
|
||||
in_loc = roundup(in_loc, length) + length;
|
||||
}
|
||||
length = iio_storage_bytes_for_si(indio_dev, in_ind);
|
||||
out_loc = roundup(out_loc, length);
|
||||
in_loc = roundup(in_loc, length);
|
||||
ret = iio_buffer_add_demux(buffer, &p, in_loc, out_loc, length);
|
||||
if (ret)
|
||||
goto error_clear_mux_table;
|
||||
out_loc += length;
|
||||
in_loc += length;
|
||||
}
|
||||
/* Relies on scan_timestamp being last */
|
||||
if (buffer->scan_timestamp) {
|
||||
length = iio_storage_bytes_for_timestamp(indio_dev);
|
||||
out_loc = roundup(out_loc, length);
|
||||
in_loc = roundup(in_loc, length);
|
||||
ret = iio_buffer_add_demux(buffer, &p, in_loc, out_loc, length);
|
||||
if (ret)
|
||||
goto error_clear_mux_table;
|
||||
out_loc += length;
|
||||
in_loc += length;
|
||||
}
|
||||
buffer->demux_bounce = kzalloc(out_loc, GFP_KERNEL);
|
||||
if (buffer->demux_bounce == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_clear_mux_table;
|
||||
}
|
||||
return 0;
|
||||
|
||||
error_clear_mux_table:
|
||||
iio_buffer_demux_free(buffer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iio_update_demux(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct iio_buffer *buffer;
|
||||
int ret;
|
||||
|
||||
list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) {
|
||||
ret = iio_buffer_update_demux(indio_dev, buffer);
|
||||
if (ret < 0)
|
||||
goto error_clear_mux_table;
|
||||
}
|
||||
return 0;
|
||||
|
||||
error_clear_mux_table:
|
||||
list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list)
|
||||
iio_buffer_demux_free(buffer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iio_enable_buffers(struct iio_dev *indio_dev,
|
||||
struct iio_device_config *config)
|
||||
{
|
||||
|
@ -1199,34 +1354,6 @@ bool iio_validate_scan_mask_onehot(struct iio_dev *indio_dev,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(iio_validate_scan_mask_onehot);
|
||||
|
||||
int iio_scan_mask_query(struct iio_dev *indio_dev,
|
||||
struct iio_buffer *buffer, int bit)
|
||||
{
|
||||
if (bit > indio_dev->masklength)
|
||||
return -EINVAL;
|
||||
|
||||
if (!buffer->scan_mask)
|
||||
return 0;
|
||||
|
||||
/* Ensure return value is 0 or 1. */
|
||||
return !!test_bit(bit, buffer->scan_mask);
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(iio_scan_mask_query);
|
||||
|
||||
/**
|
||||
* struct iio_demux_table - table describing demux memcpy ops
|
||||
* @from: index to copy from
|
||||
* @to: index to copy to
|
||||
* @length: how many bytes to copy
|
||||
* @l: list head used for management
|
||||
*/
|
||||
struct iio_demux_table {
|
||||
unsigned from;
|
||||
unsigned to;
|
||||
unsigned length;
|
||||
struct list_head l;
|
||||
};
|
||||
|
||||
static const void *iio_demux(struct iio_buffer *buffer,
|
||||
const void *datain)
|
||||
{
|
||||
|
@ -1258,16 +1385,11 @@ static int iio_push_to_buffer(struct iio_buffer *buffer, const void *data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void iio_buffer_demux_free(struct iio_buffer *buffer)
|
||||
{
|
||||
struct iio_demux_table *p, *q;
|
||||
list_for_each_entry_safe(p, q, &buffer->demux_list, l) {
|
||||
list_del(&p->l);
|
||||
kfree(p);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* iio_push_to_buffers() - push to a registered buffer.
|
||||
* @indio_dev: iio_dev structure for device.
|
||||
* @data: Full scan.
|
||||
*/
|
||||
int iio_push_to_buffers(struct iio_dev *indio_dev, const void *data)
|
||||
{
|
||||
int ret;
|
||||
|
@ -1283,113 +1405,6 @@ int iio_push_to_buffers(struct iio_dev *indio_dev, const void *data)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(iio_push_to_buffers);
|
||||
|
||||
static int iio_buffer_add_demux(struct iio_buffer *buffer,
|
||||
struct iio_demux_table **p, unsigned int in_loc, unsigned int out_loc,
|
||||
unsigned int length)
|
||||
{
|
||||
|
||||
if (*p && (*p)->from + (*p)->length == in_loc &&
|
||||
(*p)->to + (*p)->length == out_loc) {
|
||||
(*p)->length += length;
|
||||
} else {
|
||||
*p = kmalloc(sizeof(**p), GFP_KERNEL);
|
||||
if (*p == NULL)
|
||||
return -ENOMEM;
|
||||
(*p)->from = in_loc;
|
||||
(*p)->to = out_loc;
|
||||
(*p)->length = length;
|
||||
list_add_tail(&(*p)->l, &buffer->demux_list);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iio_buffer_update_demux(struct iio_dev *indio_dev,
|
||||
struct iio_buffer *buffer)
|
||||
{
|
||||
int ret, in_ind = -1, out_ind, length;
|
||||
unsigned in_loc = 0, out_loc = 0;
|
||||
struct iio_demux_table *p = NULL;
|
||||
|
||||
/* Clear out any old demux */
|
||||
iio_buffer_demux_free(buffer);
|
||||
kfree(buffer->demux_bounce);
|
||||
buffer->demux_bounce = NULL;
|
||||
|
||||
/* First work out which scan mode we will actually have */
|
||||
if (bitmap_equal(indio_dev->active_scan_mask,
|
||||
buffer->scan_mask,
|
||||
indio_dev->masklength))
|
||||
return 0;
|
||||
|
||||
/* Now we have the two masks, work from least sig and build up sizes */
|
||||
for_each_set_bit(out_ind,
|
||||
buffer->scan_mask,
|
||||
indio_dev->masklength) {
|
||||
in_ind = find_next_bit(indio_dev->active_scan_mask,
|
||||
indio_dev->masklength,
|
||||
in_ind + 1);
|
||||
while (in_ind != out_ind) {
|
||||
in_ind = find_next_bit(indio_dev->active_scan_mask,
|
||||
indio_dev->masklength,
|
||||
in_ind + 1);
|
||||
length = iio_storage_bytes_for_si(indio_dev, in_ind);
|
||||
/* Make sure we are aligned */
|
||||
in_loc = roundup(in_loc, length) + length;
|
||||
}
|
||||
length = iio_storage_bytes_for_si(indio_dev, in_ind);
|
||||
out_loc = roundup(out_loc, length);
|
||||
in_loc = roundup(in_loc, length);
|
||||
ret = iio_buffer_add_demux(buffer, &p, in_loc, out_loc, length);
|
||||
if (ret)
|
||||
goto error_clear_mux_table;
|
||||
out_loc += length;
|
||||
in_loc += length;
|
||||
}
|
||||
/* Relies on scan_timestamp being last */
|
||||
if (buffer->scan_timestamp) {
|
||||
length = iio_storage_bytes_for_timestamp(indio_dev);
|
||||
out_loc = roundup(out_loc, length);
|
||||
in_loc = roundup(in_loc, length);
|
||||
ret = iio_buffer_add_demux(buffer, &p, in_loc, out_loc, length);
|
||||
if (ret)
|
||||
goto error_clear_mux_table;
|
||||
out_loc += length;
|
||||
in_loc += length;
|
||||
}
|
||||
buffer->demux_bounce = kzalloc(out_loc, GFP_KERNEL);
|
||||
if (buffer->demux_bounce == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_clear_mux_table;
|
||||
}
|
||||
return 0;
|
||||
|
||||
error_clear_mux_table:
|
||||
iio_buffer_demux_free(buffer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int iio_update_demux(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct iio_buffer *buffer;
|
||||
int ret;
|
||||
|
||||
list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) {
|
||||
ret = iio_buffer_update_demux(indio_dev, buffer);
|
||||
if (ret < 0)
|
||||
goto error_clear_mux_table;
|
||||
}
|
||||
return 0;
|
||||
|
||||
error_clear_mux_table:
|
||||
list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list)
|
||||
iio_buffer_demux_free(buffer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_update_demux);
|
||||
|
||||
/**
|
||||
* iio_buffer_release() - Free a buffer's resources
|
||||
* @ref: Pointer to the kref embedded in the iio_buffer struct
|
||||
|
@ -1431,3 +1446,19 @@ void iio_buffer_put(struct iio_buffer *buffer)
|
|||
kref_put(&buffer->ref, iio_buffer_release);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_buffer_put);
|
||||
|
||||
/**
|
||||
* iio_device_attach_buffer - Attach a buffer to a IIO device
|
||||
* @indio_dev: The device the buffer should be attached to
|
||||
* @buffer: The buffer to attach to the device
|
||||
*
|
||||
* This function attaches a buffer to a IIO device. The buffer stays attached to
|
||||
* the device until the device is freed. The function should only be called at
|
||||
* most once per device.
|
||||
*/
|
||||
void iio_device_attach_buffer(struct iio_dev *indio_dev,
|
||||
struct iio_buffer *buffer)
|
||||
{
|
||||
indio_dev->buffer = iio_buffer_get(buffer);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_device_attach_buffer);
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/buffer_impl.h>
|
||||
|
||||
/* IDA to assign each registered device a unique id */
|
||||
static DEFINE_IDA(iio_ida);
|
||||
|
@ -83,6 +84,7 @@ static const char * const iio_chan_type_name_spec[] = {
|
|||
[IIO_ELECTRICALCONDUCTIVITY] = "electricalconductivity",
|
||||
[IIO_COUNT] = "count",
|
||||
[IIO_INDEX] = "index",
|
||||
[IIO_GRAVITY] = "gravity",
|
||||
};
|
||||
|
||||
static const char * const iio_modifier_names[] = {
|
||||
|
|
|
@ -115,6 +115,16 @@ config CM3323
|
|||
To compile this driver as a module, choose M here: the module will
|
||||
be called cm3323.
|
||||
|
||||
config CM3605
|
||||
tristate "Capella CM3605 ambient light and proximity sensor"
|
||||
depends on OF
|
||||
help
|
||||
Say Y here if you want to build a driver for Capella CM3605
|
||||
ambient light and short range proximity sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called cm3605.
|
||||
|
||||
config CM36651
|
||||
depends on I2C
|
||||
tristate "CM36651 driver"
|
||||
|
|
|
@ -13,6 +13,7 @@ obj-$(CONFIG_BH1780) += bh1780.o
|
|||
obj-$(CONFIG_CM32181) += cm32181.o
|
||||
obj-$(CONFIG_CM3232) += cm3232.o
|
||||
obj-$(CONFIG_CM3323) += cm3323.o
|
||||
obj-$(CONFIG_CM3605) += cm3605.o
|
||||
obj-$(CONFIG_CM36651) += cm36651.o
|
||||
obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o
|
||||
obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o
|
||||
|
|
|
@ -119,7 +119,7 @@ static int cm3232_reg_init(struct cm3232_chip *chip)
|
|||
if (ret < 0)
|
||||
dev_err(&chip->client->dev, "Error writing reg_cmd\n");
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,330 @@
|
|||
/*
|
||||
* CM3605 Ambient Light and Proximity Sensor
|
||||
*
|
||||
* Copyright (C) 2016 Linaro Ltd.
|
||||
* Author: Linus Walleij <linus.walleij@linaro.org>
|
||||
*
|
||||
* This hardware was found in the very first Nexus One handset from Google/HTC
|
||||
* and an early endavour into mobile light and proximity sensors.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/consumer.h> /* To get our ADC channel */
|
||||
#include <linux/iio/types.h> /* To deal with our ADC channel */
|
||||
#include <linux/init.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/pm.h>
|
||||
|
||||
#define CM3605_PROX_CHANNEL 0
|
||||
#define CM3605_ALS_CHANNEL 1
|
||||
#define CM3605_AOUT_TYP_MAX_MV 1550
|
||||
/* It should not go above 1.650V according to the data sheet */
|
||||
#define CM3605_AOUT_MAX_MV 1650
|
||||
|
||||
/**
|
||||
* struct cm3605 - CM3605 state
|
||||
* @dev: pointer to parent device
|
||||
* @vdd: regulator controlling VDD
|
||||
* @aset: sleep enable GPIO, high = sleep
|
||||
* @aout: IIO ADC channel to convert the AOUT signal
|
||||
* @als_max: maximum LUX detection (depends on RSET)
|
||||
* @dir: proximity direction: start as FALLING
|
||||
* @led: trigger for the infrared LED used by the proximity sensor
|
||||
*/
|
||||
struct cm3605 {
|
||||
struct device *dev;
|
||||
struct regulator *vdd;
|
||||
struct gpio_desc *aset;
|
||||
struct iio_channel *aout;
|
||||
s32 als_max;
|
||||
enum iio_event_direction dir;
|
||||
struct led_trigger *led;
|
||||
};
|
||||
|
||||
static irqreturn_t cm3605_prox_irq(int irq, void *d)
|
||||
{
|
||||
struct iio_dev *indio_dev = d;
|
||||
struct cm3605 *cm3605 = iio_priv(indio_dev);
|
||||
u64 ev;
|
||||
|
||||
ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, CM3605_PROX_CHANNEL,
|
||||
IIO_EV_TYPE_THRESH, cm3605->dir);
|
||||
iio_push_event(indio_dev, ev, iio_get_time_ns(indio_dev));
|
||||
|
||||
/* Invert the edge for each event */
|
||||
if (cm3605->dir == IIO_EV_DIR_RISING)
|
||||
cm3605->dir = IIO_EV_DIR_FALLING;
|
||||
else
|
||||
cm3605->dir = IIO_EV_DIR_RISING;
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int cm3605_get_lux(struct cm3605 *cm3605)
|
||||
{
|
||||
int ret, res;
|
||||
s64 lux;
|
||||
|
||||
ret = iio_read_channel_processed(cm3605->aout, &res);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dev_dbg(cm3605->dev, "read %d mV from ADC\n", res);
|
||||
|
||||
/*
|
||||
* AOUT has an offset of ~30mV then linear at dark
|
||||
* then goes from 2.54 up to 650 LUX yielding 1.55V
|
||||
* (1550 mV) so scale the returned value to this interval
|
||||
* using simple linear interpolation.
|
||||
*/
|
||||
if (res < 30)
|
||||
return 0;
|
||||
if (res > CM3605_AOUT_MAX_MV)
|
||||
dev_err(cm3605->dev, "device out of range\n");
|
||||
|
||||
/* Remove bias */
|
||||
lux = res - 30;
|
||||
|
||||
/* Linear interpolation between 0 and ALS typ max */
|
||||
lux *= cm3605->als_max;
|
||||
lux = div64_s64(lux, CM3605_AOUT_TYP_MAX_MV);
|
||||
|
||||
return lux;
|
||||
}
|
||||
|
||||
static int cm3605_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct cm3605 *cm3605 = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
switch (chan->type) {
|
||||
case IIO_LIGHT:
|
||||
ret = cm3605_get_lux(cm3605);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info cm3605_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = cm3605_read_raw,
|
||||
};
|
||||
|
||||
static const struct iio_event_spec cm3605_events[] = {
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_EITHER,
|
||||
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec cm3605_channels[] = {
|
||||
{
|
||||
.type = IIO_PROXIMITY,
|
||||
.event_spec = cm3605_events,
|
||||
.num_event_specs = ARRAY_SIZE(cm3605_events),
|
||||
},
|
||||
{
|
||||
.type = IIO_LIGHT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.channel = CM3605_ALS_CHANNEL,
|
||||
},
|
||||
};
|
||||
|
||||
static int cm3605_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct cm3605 *cm3605;
|
||||
struct iio_dev *indio_dev;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
enum iio_chan_type ch_type;
|
||||
u32 rset;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*cm3605));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
cm3605 = iio_priv(indio_dev);
|
||||
cm3605->dev = dev;
|
||||
cm3605->dir = IIO_EV_DIR_FALLING;
|
||||
|
||||
ret = of_property_read_u32(np, "capella,aset-resistance-ohms", &rset);
|
||||
if (ret) {
|
||||
dev_info(dev, "no RSET specified, assuming 100K\n");
|
||||
rset = 100000;
|
||||
}
|
||||
switch (rset) {
|
||||
case 50000:
|
||||
cm3605->als_max = 650;
|
||||
break;
|
||||
case 100000:
|
||||
cm3605->als_max = 300;
|
||||
break;
|
||||
case 300000:
|
||||
cm3605->als_max = 100;
|
||||
break;
|
||||
case 600000:
|
||||
cm3605->als_max = 50;
|
||||
break;
|
||||
default:
|
||||
dev_info(dev, "non-standard resistance\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cm3605->aout = devm_iio_channel_get(dev, "aout");
|
||||
if (IS_ERR(cm3605->aout)) {
|
||||
if (PTR_ERR(cm3605->aout) == -ENODEV) {
|
||||
dev_err(dev, "no ADC, deferring...\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
dev_err(dev, "failed to get AOUT ADC channel\n");
|
||||
return PTR_ERR(cm3605->aout);
|
||||
}
|
||||
ret = iio_get_channel_type(cm3605->aout, &ch_type);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ch_type != IIO_VOLTAGE) {
|
||||
dev_err(dev, "wrong type of IIO channel specified for AOUT\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cm3605->vdd = devm_regulator_get(dev, "vdd");
|
||||
if (IS_ERR(cm3605->vdd)) {
|
||||
dev_err(dev, "failed to get VDD regulator\n");
|
||||
return PTR_ERR(cm3605->vdd);
|
||||
}
|
||||
ret = regulator_enable(cm3605->vdd);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable VDD regulator\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
cm3605->aset = devm_gpiod_get(dev, "aset", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(cm3605->aset)) {
|
||||
dev_err(dev, "no ASET GPIO\n");
|
||||
ret = PTR_ERR(cm3605->aset);
|
||||
goto out_disable_vdd;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(dev, platform_get_irq(pdev, 0),
|
||||
cm3605_prox_irq, NULL, 0, "cm3605", indio_dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "unable to request IRQ\n");
|
||||
goto out_disable_aset;
|
||||
}
|
||||
|
||||
/* Just name the trigger the same as the driver */
|
||||
led_trigger_register_simple("cm3605", &cm3605->led);
|
||||
led_trigger_event(cm3605->led, LED_FULL);
|
||||
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->info = &cm3605_info;
|
||||
indio_dev->name = "cm3605";
|
||||
indio_dev->channels = cm3605_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(cm3605_channels);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto out_remove_trigger;
|
||||
dev_info(dev, "Capella Microsystems CM3605 enabled range 0..%d LUX\n",
|
||||
cm3605->als_max);
|
||||
|
||||
return 0;
|
||||
|
||||
out_remove_trigger:
|
||||
led_trigger_event(cm3605->led, LED_OFF);
|
||||
led_trigger_unregister_simple(cm3605->led);
|
||||
out_disable_aset:
|
||||
gpiod_set_value_cansleep(cm3605->aset, 0);
|
||||
out_disable_vdd:
|
||||
regulator_disable(cm3605->vdd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cm3605_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct cm3605 *cm3605 = iio_priv(indio_dev);
|
||||
|
||||
led_trigger_event(cm3605->led, LED_OFF);
|
||||
led_trigger_unregister_simple(cm3605->led);
|
||||
gpiod_set_value_cansleep(cm3605->aset, 0);
|
||||
iio_device_unregister(indio_dev);
|
||||
regulator_disable(cm3605->vdd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cm3605_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct cm3605 *cm3605 = iio_priv(indio_dev);
|
||||
|
||||
led_trigger_event(cm3605->led, LED_OFF);
|
||||
regulator_disable(cm3605->vdd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cm3605_pm_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct cm3605 *cm3605 = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = regulator_enable(cm3605->vdd);
|
||||
if (ret)
|
||||
dev_err(dev, "failed to enable regulator in resume path\n");
|
||||
led_trigger_event(cm3605->led, LED_FULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops cm3605_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(cm3605_pm_suspend,
|
||||
cm3605_pm_resume)
|
||||
};
|
||||
|
||||
static const struct of_device_id cm3605_of_match[] = {
|
||||
{.compatible = "capella,cm3605"},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, cm3605_of_match);
|
||||
|
||||
static struct platform_driver cm3605_driver = {
|
||||
.driver = {
|
||||
.name = "cm3605",
|
||||
.of_match_table = cm3605_of_match,
|
||||
.pm = &cm3605_dev_pm_ops,
|
||||
},
|
||||
.probe = cm3605_probe,
|
||||
.remove = cm3605_remove,
|
||||
};
|
||||
module_platform_driver(cm3605_driver);
|
||||
|
||||
MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
|
||||
MODULE_DESCRIPTION("CM3605 ambient light and proximity sensor driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -31,13 +31,17 @@
|
|||
#include <linux/iio/triggered_buffer.h>
|
||||
#include "../common/hid-sensors/hid-sensor-trigger.h"
|
||||
|
||||
#define CHANNEL_SCAN_INDEX_ILLUM 0
|
||||
enum {
|
||||
CHANNEL_SCAN_INDEX_INTENSITY = 0,
|
||||
CHANNEL_SCAN_INDEX_ILLUM = 1,
|
||||
CHANNEL_SCAN_INDEX_MAX
|
||||
};
|
||||
|
||||
struct als_state {
|
||||
struct hid_sensor_hub_callbacks callbacks;
|
||||
struct hid_sensor_common common_attributes;
|
||||
struct hid_sensor_hub_attribute_info als_illum;
|
||||
u32 illum;
|
||||
u32 illum[CHANNEL_SCAN_INDEX_MAX];
|
||||
int scale_pre_decml;
|
||||
int scale_post_decml;
|
||||
int scale_precision;
|
||||
|
@ -55,6 +59,15 @@ static const struct iio_chan_spec als_channels[] = {
|
|||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_INTENSITY,
|
||||
},
|
||||
{
|
||||
.type = IIO_LIGHT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_ILLUM,
|
||||
}
|
||||
};
|
||||
|
@ -86,6 +99,7 @@ static int als_read_raw(struct iio_dev *indio_dev,
|
|||
switch (mask) {
|
||||
case 0:
|
||||
switch (chan->scan_index) {
|
||||
case CHANNEL_SCAN_INDEX_INTENSITY:
|
||||
case CHANNEL_SCAN_INDEX_ILLUM:
|
||||
report_id = als_state->als_illum.report_id;
|
||||
address =
|
||||
|
@ -202,10 +216,12 @@ static int als_capture_sample(struct hid_sensor_hub_device *hsdev,
|
|||
struct iio_dev *indio_dev = platform_get_drvdata(priv);
|
||||
struct als_state *als_state = iio_priv(indio_dev);
|
||||
int ret = -EINVAL;
|
||||
u32 sample_data = *(u32 *)raw_data;
|
||||
|
||||
switch (usage_id) {
|
||||
case HID_USAGE_SENSOR_LIGHT_ILLUM:
|
||||
als_state->illum = *(u32 *)raw_data;
|
||||
als_state->illum[CHANNEL_SCAN_INDEX_INTENSITY] = sample_data;
|
||||
als_state->illum[CHANNEL_SCAN_INDEX_ILLUM] = sample_data;
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
|
@ -230,6 +246,8 @@ static int als_parse_report(struct platform_device *pdev,
|
|||
&st->als_illum);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
als_adjust_channel_bit_mask(channels, CHANNEL_SCAN_INDEX_INTENSITY,
|
||||
st->als_illum.size);
|
||||
als_adjust_channel_bit_mask(channels, CHANNEL_SCAN_INDEX_ILLUM,
|
||||
st->als_illum.size);
|
||||
|
||||
|
|
|
@ -65,7 +65,7 @@ struct bmp280_data {
|
|||
struct bmp180_calib calib;
|
||||
struct regulator *vddd;
|
||||
struct regulator *vdda;
|
||||
unsigned int start_up_time; /* in milliseconds */
|
||||
unsigned int start_up_time; /* in microseconds */
|
||||
|
||||
/* log of base 2 of oversampling rate */
|
||||
u8 oversampling_press;
|
||||
|
@ -935,14 +935,14 @@ int bmp280_common_probe(struct device *dev,
|
|||
data->chip_info = &bmp180_chip_info;
|
||||
data->oversampling_press = ilog2(8);
|
||||
data->oversampling_temp = ilog2(1);
|
||||
data->start_up_time = 10;
|
||||
data->start_up_time = 10000;
|
||||
break;
|
||||
case BMP280_CHIP_ID:
|
||||
indio_dev->num_channels = 2;
|
||||
data->chip_info = &bmp280_chip_info;
|
||||
data->oversampling_press = ilog2(16);
|
||||
data->oversampling_temp = ilog2(2);
|
||||
data->start_up_time = 2;
|
||||
data->start_up_time = 2000;
|
||||
break;
|
||||
case BME280_CHIP_ID:
|
||||
indio_dev->num_channels = 3;
|
||||
|
@ -950,7 +950,7 @@ int bmp280_common_probe(struct device *dev,
|
|||
data->oversampling_press = ilog2(16);
|
||||
data->oversampling_humid = ilog2(16);
|
||||
data->oversampling_temp = ilog2(2);
|
||||
data->start_up_time = 2;
|
||||
data->start_up_time = 2000;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
@ -979,7 +979,7 @@ int bmp280_common_probe(struct device *dev,
|
|||
goto out_disable_vddd;
|
||||
}
|
||||
/* Wait to make sure we started up properly */
|
||||
mdelay(data->start_up_time);
|
||||
usleep_range(data->start_up_time, data->start_up_time + 100);
|
||||
|
||||
/* Bring chip out of reset if there is an assigned GPIO line */
|
||||
gpiod = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
|
||||
|
@ -1038,7 +1038,7 @@ int bmp280_common_probe(struct device *dev,
|
|||
* Set autosuspend to two orders of magnitude larger than the
|
||||
* start-up time.
|
||||
*/
|
||||
pm_runtime_set_autosuspend_delay(dev, data->start_up_time *100);
|
||||
pm_runtime_set_autosuspend_delay(dev, data->start_up_time / 10);
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
pm_runtime_put(dev);
|
||||
|
||||
|
@ -1101,7 +1101,7 @@ static int bmp280_runtime_resume(struct device *dev)
|
|||
ret = regulator_enable(data->vdda);
|
||||
if (ret)
|
||||
return ret;
|
||||
msleep(data->start_up_time);
|
||||
usleep_range(data->start_up_time, data->start_up_time + 100);
|
||||
return data->chip_info->chip_config(data);
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
|
|
@ -14,6 +14,14 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
|
||||
enum st_press_type {
|
||||
LPS001WP,
|
||||
LPS25H,
|
||||
LPS331AP,
|
||||
LPS22HB,
|
||||
ST_PRESS_MAX,
|
||||
};
|
||||
|
||||
#define LPS001WP_PRESS_DEV_NAME "lps001wp"
|
||||
#define LPS25H_PRESS_DEV_NAME "lps25h"
|
||||
#define LPS331AP_PRESS_DEV_NAME "lps331ap"
|
||||
|
|
|
@ -136,20 +136,21 @@ static const struct iio_chan_spec st_press_1_channels[] = {
|
|||
.address = ST_PRESS_1_OUT_XL_ADDR,
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.sign = 's',
|
||||
.realbits = 24,
|
||||
.storagebits = 32,
|
||||
.endianness = IIO_LE,
|
||||
},
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
},
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.address = ST_TEMP_1_OUT_L_ADDR,
|
||||
.scan_index = 1,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.sign = 's',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
.endianness = IIO_LE,
|
||||
|
@ -158,6 +159,7 @@ static const struct iio_chan_spec st_press_1_channels[] = {
|
|||
BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(2)
|
||||
};
|
||||
|
@ -168,7 +170,7 @@ static const struct iio_chan_spec st_press_lps001wp_channels[] = {
|
|||
.address = ST_PRESS_LPS001WP_OUT_L_ADDR,
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.sign = 's',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
.endianness = IIO_LE,
|
||||
|
@ -182,7 +184,7 @@ static const struct iio_chan_spec st_press_lps001wp_channels[] = {
|
|||
.address = ST_TEMP_LPS001WP_OUT_L_ADDR,
|
||||
.scan_index = 1,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.sign = 's',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
.endianness = IIO_LE,
|
||||
|
@ -200,7 +202,7 @@ static const struct iio_chan_spec st_press_lps22hb_channels[] = {
|
|||
.address = ST_PRESS_1_OUT_XL_ADDR,
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.sign = 's',
|
||||
.realbits = 24,
|
||||
.storagebits = 32,
|
||||
.endianness = IIO_LE,
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
|
@ -43,25 +44,56 @@ MODULE_DEVICE_TABLE(of, st_press_of_match);
|
|||
#define st_press_of_match NULL
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id st_press_acpi_match[] = {
|
||||
{"SNO9210", LPS22HB},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, st_press_acpi_match);
|
||||
#else
|
||||
#define st_press_acpi_match NULL
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id st_press_id_table[] = {
|
||||
{ LPS001WP_PRESS_DEV_NAME, LPS001WP },
|
||||
{ LPS25H_PRESS_DEV_NAME, LPS25H },
|
||||
{ LPS331AP_PRESS_DEV_NAME, LPS331AP },
|
||||
{ LPS22HB_PRESS_DEV_NAME, LPS22HB },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, st_press_id_table);
|
||||
|
||||
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 *press_data;
|
||||
int err;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*press_data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
press_data = iio_priv(indio_dev);
|
||||
st_sensors_of_i2c_probe(client, st_press_of_match);
|
||||
|
||||
if (client->dev.of_node) {
|
||||
st_sensors_of_i2c_probe(client, st_press_of_match);
|
||||
} else if (ACPI_HANDLE(&client->dev)) {
|
||||
ret = st_sensors_match_acpi_device(&client->dev);
|
||||
if ((ret < 0) || (ret >= ST_PRESS_MAX))
|
||||
return -ENODEV;
|
||||
|
||||
strncpy(client->name, st_press_id_table[ret].name,
|
||||
sizeof(client->name));
|
||||
client->name[sizeof(client->name) - 1] = '\0';
|
||||
} else if (!id)
|
||||
return -ENODEV;
|
||||
|
||||
st_sensors_i2c_configure(indio_dev, client, press_data);
|
||||
|
||||
err = st_press_common_probe(indio_dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
ret = st_press_common_probe(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -73,18 +105,11 @@ static int st_press_i2c_remove(struct i2c_client *client)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id st_press_id_table[] = {
|
||||
{ LPS001WP_PRESS_DEV_NAME },
|
||||
{ LPS25H_PRESS_DEV_NAME },
|
||||
{ LPS331AP_PRESS_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, st_press_id_table);
|
||||
|
||||
static struct i2c_driver st_press_driver = {
|
||||
.driver = {
|
||||
.name = "st-press-i2c",
|
||||
.of_match_table = of_match_ptr(st_press_of_match),
|
||||
.acpi_match_table = ACPI_PTR(st_press_acpi_match),
|
||||
},
|
||||
.probe = st_press_i2c_probe,
|
||||
.remove = st_press_i2c_remove,
|
||||
|
|
|
@ -326,12 +326,14 @@ static int lidar_remove(struct i2c_client *client)
|
|||
|
||||
static const struct i2c_device_id lidar_id[] = {
|
||||
{"lidar-lite-v2", 0},
|
||||
{"lidar-lite-v3", 0},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, lidar_id);
|
||||
|
||||
static const struct of_device_id lidar_dt_ids[] = {
|
||||
{ .compatible = "pulsedlight,lidar-lite-v2" },
|
||||
{ .compatible = "grmn,lidar-lite-v3" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, lidar_dt_ids);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* ADIS16201 Programmable Digital Vibration Sensor driver
|
||||
* ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer
|
||||
*
|
||||
* Copyright 2010 Analog Devices Inc.
|
||||
*
|
||||
|
@ -243,6 +243,6 @@ static struct spi_driver adis16201_driver = {
|
|||
module_spi_driver(adis16201_driver);
|
||||
|
||||
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices ADIS16201 Programmable Digital Vibration Sensor driver");
|
||||
MODULE_DESCRIPTION("Analog Devices ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("spi:adis16201");
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* ADIS16203 Programmable Digital Vibration Sensor driver
|
||||
* ADIS16203 Programmable 360 Degrees Inclinometer
|
||||
*
|
||||
* Copyright 2030 Analog Devices Inc.
|
||||
* Copyright 2010 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
@ -211,6 +211,6 @@ static struct spi_driver adis16203_driver = {
|
|||
module_spi_driver(adis16203_driver);
|
||||
|
||||
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices ADIS16203 Programmable Digital Vibration Sensor driver");
|
||||
MODULE_DESCRIPTION("Analog Devices ADIS16203 Programmable 360 Degrees Inclinometer");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("spi:adis16203");
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* ADIS16209 Programmable Digital Vibration Sensor driver
|
||||
* ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer
|
||||
*
|
||||
* Copyright 2010 Analog Devices Inc.
|
||||
*
|
||||
|
@ -243,6 +243,6 @@ static struct spi_driver adis16209_driver = {
|
|||
module_spi_driver(adis16209_driver);
|
||||
|
||||
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices ADIS16209 Digital Vibration Sensor driver");
|
||||
MODULE_DESCRIPTION("Analog Devices ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("spi:adis16209");
|
||||
|
|
|
@ -26,6 +26,11 @@
|
|||
|
||||
#include "ad7606.h"
|
||||
|
||||
/* Scales are computed as 2.5/2**16 and 5/2**16 respectively */
|
||||
static const unsigned int scale_avail[2][2] = {
|
||||
{0, 38147}, {0, 76294}
|
||||
};
|
||||
|
||||
static int ad7606_reset(struct ad7606_state *st)
|
||||
{
|
||||
if (st->gpio_reset) {
|
||||
|
@ -151,9 +156,9 @@ static int ad7606_read_raw(struct iio_dev *indio_dev,
|
|||
*val = (short)ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = st->range * 2;
|
||||
*val2 = st->chip_info->channels[0].scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
*val = scale_avail[st->range][0];
|
||||
*val2 = scale_avail[st->range][1];
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
*val = st->oversampling;
|
||||
return IIO_VAL_INT;
|
||||
|
@ -161,42 +166,22 @@ static int ad7606_read_raw(struct iio_dev *indio_dev,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static ssize_t ad7606_show_range(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
static ssize_t in_voltage_scale_available_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
int i, len = 0;
|
||||
|
||||
return sprintf(buf, "%u\n", st->range);
|
||||
for (i = 0; i < ARRAY_SIZE(scale_avail); i++)
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06u ",
|
||||
scale_avail[i][0], scale_avail[i][1]);
|
||||
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t ad7606_store_range(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
unsigned long lval;
|
||||
int ret;
|
||||
|
||||
ret = kstrtoul(buf, 10, &lval);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!(lval == 5000 || lval == 10000))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
gpiod_set_value(st->gpio_range, lval == 10000);
|
||||
st->range = lval;
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(in_voltage_range, S_IRUGO | S_IWUSR,
|
||||
ad7606_show_range, ad7606_store_range, 0);
|
||||
static IIO_CONST_ATTR(in_voltage_range_available, "5000 10000");
|
||||
static IIO_DEVICE_ATTR_RO(in_voltage_scale_available, 0);
|
||||
|
||||
static int ad7606_oversampling_get_index(unsigned int val)
|
||||
{
|
||||
|
@ -218,9 +203,23 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
|
|||
{
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
int values[3];
|
||||
int ret;
|
||||
int ret, i;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = -EINVAL;
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
for (i = 0; i < ARRAY_SIZE(scale_avail); i++)
|
||||
if (val2 == scale_avail[i][1]) {
|
||||
gpiod_set_value(st->gpio_range, i);
|
||||
st->range = i;
|
||||
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
if (val2)
|
||||
return -EINVAL;
|
||||
|
@ -247,8 +246,7 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
|
|||
static IIO_CONST_ATTR(oversampling_ratio_available, "1 2 4 8 16 32 64");
|
||||
|
||||
static struct attribute *ad7606_attributes_os_and_range[] = {
|
||||
&iio_dev_attr_in_voltage_range.dev_attr.attr,
|
||||
&iio_const_attr_in_voltage_range_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage_scale_available.dev_attr.attr,
|
||||
&iio_const_attr_oversampling_ratio_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
@ -267,8 +265,7 @@ static const struct attribute_group ad7606_attribute_group_os = {
|
|||
};
|
||||
|
||||
static struct attribute *ad7606_attributes_range[] = {
|
||||
&iio_dev_attr_in_voltage_range.dev_attr.attr,
|
||||
&iio_const_attr_in_voltage_range_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage_scale_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
@ -397,6 +394,7 @@ static const struct iio_info ad7606_info_os = {
|
|||
static const struct iio_info ad7606_info_range = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &ad7606_read_raw,
|
||||
.write_raw = &ad7606_write_raw,
|
||||
.attrs = &ad7606_attribute_group_range,
|
||||
};
|
||||
|
||||
|
@ -417,7 +415,8 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
|
|||
st->dev = dev;
|
||||
st->bops = bops;
|
||||
st->base_address = base_address;
|
||||
st->range = 5000;
|
||||
/* tied to logic low, analog input range is +/- 5V */
|
||||
st->range = 0;
|
||||
st->oversampling = 1;
|
||||
INIT_WORK(&st->poll_work, &ad7606_poll_bh_to_ring);
|
||||
|
||||
|
@ -525,7 +524,7 @@ static int ad7606_resume(struct device *dev)
|
|||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (st->gpio_standby) {
|
||||
gpiod_set_value(st->gpio_range, st->range == 10000);
|
||||
gpiod_set_value(st->gpio_range, st->range);
|
||||
gpiod_set_value(st->gpio_standby, 1);
|
||||
ad7606_reset(st);
|
||||
}
|
||||
|
|
|
@ -661,8 +661,9 @@ static ssize_t adt7316_store_da_high_resolution(struct device *dev,
|
|||
chip->dac_bits = 12;
|
||||
else if (chip->id == ID_ADT7317 || chip->id == ID_ADT7517)
|
||||
chip->dac_bits = 10;
|
||||
} else
|
||||
} else {
|
||||
config3 = chip->config3 & (~ADT7316_DA_HIGH_RESOLUTION);
|
||||
}
|
||||
|
||||
ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG3, config3);
|
||||
if (ret)
|
||||
|
|
|
@ -274,7 +274,7 @@ static int ad7150_write_event_config(struct iio_dev *indio_dev,
|
|||
error_ret:
|
||||
mutex_unlock(&chip->state_lock);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7150_read_event_value(struct iio_dev *indio_dev,
|
||||
|
@ -414,7 +414,7 @@ error_ret:
|
|||
|
||||
#define AD7150_TIMEOUT(chan, type, dir, ev_type, ev_dir) \
|
||||
IIO_DEVICE_ATTR(in_capacitance##chan##_##type##_##dir##_timeout, \
|
||||
S_IRUGO | S_IWUSR, \
|
||||
0644, \
|
||||
&ad7150_show_timeout, \
|
||||
&ad7150_store_timeout, \
|
||||
IIO_UNMOD_EVENT_CODE(IIO_CAPACITANCE, \
|
||||
|
|
|
@ -23,8 +23,8 @@
|
|||
#include <linux/iio/kfifo_buf.h>
|
||||
|
||||
/* AD5933/AD5934 Registers */
|
||||
#define AD5933_REG_CONTROL_HB 0x80 /* R/W, 2 bytes */
|
||||
#define AD5933_REG_CONTROL_LB 0x81 /* R/W, 2 bytes */
|
||||
#define AD5933_REG_CONTROL_HB 0x80 /* R/W, 1 byte */
|
||||
#define AD5933_REG_CONTROL_LB 0x81 /* R/W, 1 byte */
|
||||
#define AD5933_REG_FREQ_START 0x82 /* R/W, 3 bytes */
|
||||
#define AD5933_REG_FREQ_INC 0x85 /* R/W, 3 bytes */
|
||||
#define AD5933_REG_INC_NUM 0x88 /* R/W, 2 bytes, 9 bit */
|
||||
|
|
|
@ -27,38 +27,38 @@
|
|||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define ISL29028_CONV_TIME_MS 100
|
||||
#define ISL29028_CONV_TIME_MS 100
|
||||
|
||||
#define ISL29028_REG_CONFIGURE 0x01
|
||||
#define ISL29028_REG_CONFIGURE 0x01
|
||||
|
||||
#define ISL29028_CONF_ALS_IR_MODE_ALS 0
|
||||
#define ISL29028_CONF_ALS_IR_MODE_IR BIT(0)
|
||||
#define ISL29028_CONF_ALS_IR_MODE_MASK BIT(0)
|
||||
#define ISL29028_CONF_ALS_IR_MODE_ALS 0
|
||||
#define ISL29028_CONF_ALS_IR_MODE_IR BIT(0)
|
||||
#define ISL29028_CONF_ALS_IR_MODE_MASK BIT(0)
|
||||
|
||||
#define ISL29028_CONF_ALS_RANGE_LOW_LUX 0
|
||||
#define ISL29028_CONF_ALS_RANGE_LOW_LUX 0
|
||||
#define ISL29028_CONF_ALS_RANGE_HIGH_LUX BIT(1)
|
||||
#define ISL29028_CONF_ALS_RANGE_MASK BIT(1)
|
||||
#define ISL29028_CONF_ALS_RANGE_MASK BIT(1)
|
||||
|
||||
#define ISL29028_CONF_ALS_DIS 0
|
||||
#define ISL29028_CONF_ALS_EN BIT(2)
|
||||
#define ISL29028_CONF_ALS_EN_MASK BIT(2)
|
||||
#define ISL29028_CONF_ALS_DIS 0
|
||||
#define ISL29028_CONF_ALS_EN BIT(2)
|
||||
#define ISL29028_CONF_ALS_EN_MASK BIT(2)
|
||||
|
||||
#define ISL29028_CONF_PROX_SLP_SH 4
|
||||
#define ISL29028_CONF_PROX_SLP_MASK (7 << ISL29028_CONF_PROX_SLP_SH)
|
||||
#define ISL29028_CONF_PROX_SLP_SH 4
|
||||
#define ISL29028_CONF_PROX_SLP_MASK (7 << ISL29028_CONF_PROX_SLP_SH)
|
||||
|
||||
#define ISL29028_CONF_PROX_EN BIT(7)
|
||||
#define ISL29028_CONF_PROX_EN_MASK BIT(7)
|
||||
#define ISL29028_CONF_PROX_EN BIT(7)
|
||||
#define ISL29028_CONF_PROX_EN_MASK BIT(7)
|
||||
|
||||
#define ISL29028_REG_INTERRUPT 0x02
|
||||
#define ISL29028_REG_INTERRUPT 0x02
|
||||
|
||||
#define ISL29028_REG_PROX_DATA 0x08
|
||||
#define ISL29028_REG_ALSIR_L 0x09
|
||||
#define ISL29028_REG_ALSIR_U 0x0A
|
||||
#define ISL29028_REG_PROX_DATA 0x08
|
||||
#define ISL29028_REG_ALSIR_L 0x09
|
||||
#define ISL29028_REG_ALSIR_U 0x0A
|
||||
|
||||
#define ISL29028_REG_TEST1_MODE 0x0E
|
||||
#define ISL29028_REG_TEST2_MODE 0x0F
|
||||
#define ISL29028_REG_TEST1_MODE 0x0E
|
||||
#define ISL29028_REG_TEST2_MODE 0x0F
|
||||
|
||||
#define ISL29028_NUM_REGS (ISL29028_REG_TEST2_MODE + 1)
|
||||
#define ISL29028_NUM_REGS (ISL29028_REG_TEST2_MODE + 1)
|
||||
|
||||
enum isl29028_als_ir_mode {
|
||||
ISL29028_MODE_NONE = 0,
|
||||
|
@ -124,6 +124,9 @@ static int isl29028_set_als_ir_mode(struct isl29028_chip *chip,
|
|||
{
|
||||
int ret = 0;
|
||||
|
||||
if (chip->als_ir_mode == mode)
|
||||
return 0;
|
||||
|
||||
switch (mode) {
|
||||
case ISL29028_MODE_ALS:
|
||||
ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE,
|
||||
|
@ -160,6 +163,9 @@ static int isl29028_set_als_ir_mode(struct isl29028_chip *chip,
|
|||
|
||||
/* Need to wait for conversion time if ALS/IR mode enabled */
|
||||
mdelay(ISL29028_CONV_TIME_MS);
|
||||
|
||||
chip->als_ir_mode = mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -223,14 +229,10 @@ static int isl29028_als_get(struct isl29028_chip *chip, int *als_data)
|
|||
int ret;
|
||||
int als_ir_data;
|
||||
|
||||
if (chip->als_ir_mode != ISL29028_MODE_ALS) {
|
||||
ret = isl29028_set_als_ir_mode(chip, ISL29028_MODE_ALS);
|
||||
if (ret < 0) {
|
||||
dev_err(dev,
|
||||
"Error in enabling ALS mode err %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
chip->als_ir_mode = ISL29028_MODE_ALS;
|
||||
ret = isl29028_set_als_ir_mode(chip, ISL29028_MODE_ALS);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Error in enabling ALS mode err %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = isl29028_read_als_ir(chip, &als_ir_data);
|
||||
|
@ -256,14 +258,10 @@ static int isl29028_ir_get(struct isl29028_chip *chip, int *ir_data)
|
|||
struct device *dev = regmap_get_device(chip->regmap);
|
||||
int ret;
|
||||
|
||||
if (chip->als_ir_mode != ISL29028_MODE_IR) {
|
||||
ret = isl29028_set_als_ir_mode(chip, ISL29028_MODE_IR);
|
||||
if (ret < 0) {
|
||||
dev_err(dev,
|
||||
"Error in enabling IR mode err %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
chip->als_ir_mode = ISL29028_MODE_IR;
|
||||
ret = isl29028_set_als_ir_mode(chip, ISL29028_MODE_IR);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Error in enabling IR mode err %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
return isl29028_read_als_ir(chip, ir_data);
|
||||
}
|
||||
|
@ -420,29 +418,11 @@ static const struct iio_info isl29028_info = {
|
|||
.write_raw = isl29028_write_raw,
|
||||
};
|
||||
|
||||
static int isl29028_chip_init(struct isl29028_chip *chip)
|
||||
static int isl29028_chip_init_and_power_on(struct isl29028_chip *chip)
|
||||
{
|
||||
struct device *dev = regmap_get_device(chip->regmap);
|
||||
int ret;
|
||||
|
||||
chip->enable_prox = false;
|
||||
chip->prox_sampling = 20;
|
||||
chip->lux_scale = 2000;
|
||||
chip->als_ir_mode = ISL29028_MODE_NONE;
|
||||
|
||||
ret = regmap_write(chip->regmap, ISL29028_REG_TEST1_MODE, 0x0);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s(): write to reg %d failed, err = %d\n",
|
||||
__func__, ISL29028_REG_TEST1_MODE, ret);
|
||||
return ret;
|
||||
}
|
||||
ret = regmap_write(chip->regmap, ISL29028_REG_TEST2_MODE, 0x0);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s(): write to reg %d failed, err = %d\n",
|
||||
__func__, ISL29028_REG_TEST2_MODE, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_write(chip->regmap, ISL29028_REG_CONFIGURE, 0x0);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s(): write to reg %d failed, err = %d\n",
|
||||
|
@ -510,7 +490,27 @@ static int isl29028_probe(struct i2c_client *client,
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = isl29028_chip_init(chip);
|
||||
chip->enable_prox = false;
|
||||
chip->prox_sampling = 20;
|
||||
chip->lux_scale = 2000;
|
||||
chip->als_ir_mode = ISL29028_MODE_NONE;
|
||||
|
||||
ret = regmap_write(chip->regmap, ISL29028_REG_TEST1_MODE, 0x0);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev,
|
||||
"%s(): write to reg %d failed, err = %d\n", __func__,
|
||||
ISL29028_REG_TEST1_MODE, ret);
|
||||
return ret;
|
||||
}
|
||||
ret = regmap_write(chip->regmap, ISL29028_REG_TEST2_MODE, 0x0);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev,
|
||||
"%s(): write to reg %d failed, err = %d\n", __func__,
|
||||
ISL29028_REG_TEST2_MODE, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = isl29028_chip_init_and_power_on(chip);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "chip initialization failed: %d\n", ret);
|
||||
return ret;
|
||||
|
|
|
@ -377,7 +377,7 @@ static int ade7753_initial_setup(struct iio_dev *indio_dev)
|
|||
}
|
||||
|
||||
ade7753_reset(dev);
|
||||
msleep(ADE7753_STARTUP_DELAY);
|
||||
usleep_range(ADE7753_STARTUP_DELAY, ADE7753_STARTUP_DELAY + 100);
|
||||
|
||||
err_ret:
|
||||
return ret;
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
|
||||
#define ADE7753_MAX_TX 4
|
||||
#define ADE7753_MAX_RX 4
|
||||
#define ADE7753_STARTUP_DELAY 1
|
||||
#define ADE7753_STARTUP_DELAY 1000
|
||||
|
||||
#define ADE7753_SPI_SLOW (u32)(300 * 1000)
|
||||
#define ADE7753_SPI_BURST (u32)(1000 * 1000)
|
||||
|
|
|
@ -389,7 +389,7 @@ static int ade7754_initial_setup(struct iio_dev *indio_dev)
|
|||
}
|
||||
|
||||
ade7754_reset(dev);
|
||||
msleep(ADE7754_STARTUP_DELAY);
|
||||
usleep_range(ADE7754_STARTUP_DELAY, ADE7754_STARTUP_DELAY + 100);
|
||||
|
||||
err_ret:
|
||||
return ret;
|
||||
|
|
|
@ -67,7 +67,7 @@
|
|||
|
||||
#define ADE7754_MAX_TX 4
|
||||
#define ADE7754_MAX_RX 4
|
||||
#define ADE7754_STARTUP_DELAY 1
|
||||
#define ADE7754_STARTUP_DELAY 1000
|
||||
|
||||
#define ADE7754_SPI_SLOW (u32)(300 * 1000)
|
||||
#define ADE7754_SPI_BURST (u32)(1000 * 1000)
|
||||
|
|
|
@ -89,7 +89,7 @@
|
|||
|
||||
#define ADE7758_MAX_TX 8
|
||||
#define ADE7758_MAX_RX 4
|
||||
#define ADE7758_STARTUP_DELAY 1
|
||||
#define ADE7758_STARTUP_DELAY 1000
|
||||
|
||||
#define AD7758_NUM_WAVSEL 5
|
||||
#define AD7758_NUM_PHSEL 3
|
||||
|
|
|
@ -459,7 +459,7 @@ static int ade7758_initial_setup(struct iio_dev *indio_dev)
|
|||
}
|
||||
|
||||
ade7758_reset(dev);
|
||||
msleep(ADE7758_STARTUP_DELAY);
|
||||
usleep_range(ADE7758_STARTUP_DELAY, ADE7758_STARTUP_DELAY + 100);
|
||||
|
||||
err_ret:
|
||||
return ret;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <asm/unaligned.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include "ade7758.h"
|
||||
|
|
|
@ -338,7 +338,7 @@ static int ade7759_initial_setup(struct iio_dev *indio_dev)
|
|||
}
|
||||
|
||||
ade7759_reset(dev);
|
||||
msleep(ADE7759_STARTUP_DELAY);
|
||||
usleep_range(ADE7759_STARTUP_DELAY, ADE7759_STARTUP_DELAY + 100);
|
||||
|
||||
err_ret:
|
||||
return ret;
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
#define ADE7759_MAX_TX 6
|
||||
#define ADE7759_MAX_RX 6
|
||||
#define ADE7759_STARTUP_DELAY 1
|
||||
#define ADE7759_STARTUP_DELAY 1000
|
||||
|
||||
#define ADE7759_SPI_SLOW (u32)(300 * 1000)
|
||||
#define ADE7759_SPI_BURST (u32)(1000 * 1000)
|
||||
|
|
|
@ -444,7 +444,7 @@ static int ade7854_initial_setup(struct iio_dev *indio_dev)
|
|||
}
|
||||
|
||||
ade7854_reset(dev);
|
||||
msleep(ADE7854_STARTUP_DELAY);
|
||||
usleep_range(ADE7854_STARTUP_DELAY, ADE7854_STARTUP_DELAY + 100);
|
||||
|
||||
err_ret:
|
||||
return ret;
|
||||
|
|
|
@ -136,7 +136,7 @@
|
|||
|
||||
#define ADE7854_MAX_TX 7
|
||||
#define ADE7854_MAX_RX 7
|
||||
#define ADE7854_STARTUP_DELAY 1
|
||||
#define ADE7854_STARTUP_DELAY 1000
|
||||
|
||||
#define ADE7854_SPI_SLOW (u32)(300 * 1000)
|
||||
#define ADE7854_SPI_BURST (u32)(1000 * 1000)
|
||||
|
|
|
@ -133,7 +133,7 @@ static ssize_t iio_bfin_tmr_frequency_show(struct device *dev,
|
|||
return sprintf(buf, "%lu\n", val);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(frequency, S_IRUGO | S_IWUSR, iio_bfin_tmr_frequency_show,
|
||||
static DEVICE_ATTR(frequency, 0644, iio_bfin_tmr_frequency_show,
|
||||
iio_bfin_tmr_frequency_store);
|
||||
|
||||
static struct attribute *iio_bfin_tmr_trigger_attrs[] = {
|
||||
|
|
|
@ -232,6 +232,7 @@ struct hid_sensor_common {
|
|||
atomic_t data_ready;
|
||||
atomic_t user_requested_state;
|
||||
struct iio_trigger *trigger;
|
||||
int timestamp_ns_scale;
|
||||
struct hid_sensor_hub_attribute_info poll;
|
||||
struct hid_sensor_hub_attribute_info report_state;
|
||||
struct hid_sensor_hub_attribute_info power_state;
|
||||
|
@ -271,4 +272,7 @@ int hid_sensor_format_scale(u32 usage_id,
|
|||
|
||||
s32 hid_sensor_read_poll_value(struct hid_sensor_common *st);
|
||||
|
||||
int64_t hid_sensor_convert_timestamp(struct hid_sensor_common *st,
|
||||
int64_t raw_value);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -52,6 +52,9 @@
|
|||
#define HID_USAGE_SENSOR_ANGL_VELOCITY_Y_AXIS 0x200458
|
||||
#define HID_USAGE_SENSOR_ANGL_VELOCITY_Z_AXIS 0x200459
|
||||
|
||||
/* Gravity vector */
|
||||
#define HID_USAGE_SENSOR_GRAVITY_VECTOR 0x20007B
|
||||
|
||||
/* ORIENTATION: Compass 3D: (200083) */
|
||||
#define HID_USAGE_SENSOR_COMPASS_3D 0x200083
|
||||
#define HID_USAGE_SENSOR_DATA_ORIENTATION 0x200470
|
||||
|
@ -95,6 +98,7 @@
|
|||
#define HID_USAGE_SENSOR_TIME_HOUR 0x200525
|
||||
#define HID_USAGE_SENSOR_TIME_MINUTE 0x200526
|
||||
#define HID_USAGE_SENSOR_TIME_SECOND 0x200527
|
||||
#define HID_USAGE_SENSOR_TIME_TIMESTAMP 0x200529
|
||||
|
||||
/* Units */
|
||||
#define HID_USAGE_SENSOR_UNITS_NOT_SPECIFIED 0x00
|
||||
|
|
|
@ -11,139 +11,15 @@
|
|||
#define _IIO_BUFFER_GENERIC_H_
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/kref.h>
|
||||
|
||||
#ifdef CONFIG_IIO_BUFFER
|
||||
|
||||
struct iio_buffer;
|
||||
|
||||
/**
|
||||
* INDIO_BUFFER_FLAG_FIXED_WATERMARK - Watermark level of the buffer can not be
|
||||
* configured. It has a fixed value which will be buffer specific.
|
||||
*/
|
||||
#define INDIO_BUFFER_FLAG_FIXED_WATERMARK BIT(0)
|
||||
void iio_buffer_set_attrs(struct iio_buffer *buffer,
|
||||
const struct attribute **attrs);
|
||||
|
||||
/**
|
||||
* struct iio_buffer_access_funcs - access functions for buffers.
|
||||
* @store_to: actually store stuff to the buffer
|
||||
* @read_first_n: try to get a specified number of bytes (must exist)
|
||||
* @data_available: indicates how much data is available for reading from
|
||||
* the buffer.
|
||||
* @request_update: if a parameter change has been marked, update underlying
|
||||
* storage.
|
||||
* @set_bytes_per_datum:set number of bytes per datum
|
||||
* @set_length: set number of datums in buffer
|
||||
* @enable: called if the buffer is attached to a device and the
|
||||
* device starts sampling. Calls are balanced with
|
||||
* @disable.
|
||||
* @disable: called if the buffer is attached to a device and the
|
||||
* device stops sampling. Calles are balanced with @enable.
|
||||
* @release: called when the last reference to the buffer is dropped,
|
||||
* should free all resources allocated by the buffer.
|
||||
* @modes: Supported operating modes by this buffer type
|
||||
* @flags: A bitmask combination of INDIO_BUFFER_FLAG_*
|
||||
*
|
||||
* The purpose of this structure is to make the buffer element
|
||||
* modular as event for a given driver, different usecases may require
|
||||
* different buffer designs (space efficiency vs speed for example).
|
||||
*
|
||||
* It is worth noting that a given buffer implementation may only support a
|
||||
* small proportion of these functions. The core code 'should' cope fine with
|
||||
* any of them not existing.
|
||||
**/
|
||||
struct iio_buffer_access_funcs {
|
||||
int (*store_to)(struct iio_buffer *buffer, const void *data);
|
||||
int (*read_first_n)(struct iio_buffer *buffer,
|
||||
size_t n,
|
||||
char __user *buf);
|
||||
size_t (*data_available)(struct iio_buffer *buffer);
|
||||
|
||||
int (*request_update)(struct iio_buffer *buffer);
|
||||
|
||||
int (*set_bytes_per_datum)(struct iio_buffer *buffer, size_t bpd);
|
||||
int (*set_length)(struct iio_buffer *buffer, int length);
|
||||
|
||||
int (*enable)(struct iio_buffer *buffer, struct iio_dev *indio_dev);
|
||||
int (*disable)(struct iio_buffer *buffer, struct iio_dev *indio_dev);
|
||||
|
||||
void (*release)(struct iio_buffer *buffer);
|
||||
|
||||
unsigned int modes;
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iio_buffer - general buffer structure
|
||||
* @length: [DEVICE] number of datums in buffer
|
||||
* @bytes_per_datum: [DEVICE] size of individual datum including timestamp
|
||||
* @scan_el_attrs: [DRIVER] control of scan elements if that scan mode
|
||||
* control method is used
|
||||
* @scan_mask: [INTERN] bitmask used in masking scan mode elements
|
||||
* @scan_timestamp: [INTERN] does the scan mode include a timestamp
|
||||
* @access: [DRIVER] buffer access functions associated with the
|
||||
* implementation.
|
||||
* @scan_el_dev_attr_list:[INTERN] list of scan element related attributes.
|
||||
* @buffer_group: [INTERN] attributes of the buffer group
|
||||
* @scan_el_group: [DRIVER] attribute group for those attributes not
|
||||
* created from the iio_chan_info array.
|
||||
* @pollq: [INTERN] wait queue to allow for polling on the buffer.
|
||||
* @stufftoread: [INTERN] flag to indicate new data.
|
||||
* @attrs: [INTERN] standard attributes of the buffer
|
||||
* @demux_list: [INTERN] list of operations required to demux the scan.
|
||||
* @demux_bounce: [INTERN] buffer for doing gather from incoming scan.
|
||||
* @buffer_list: [INTERN] entry in the devices list of current buffers.
|
||||
* @ref: [INTERN] reference count of the buffer.
|
||||
* @watermark: [INTERN] number of datums to wait for poll/read.
|
||||
*/
|
||||
struct iio_buffer {
|
||||
int length;
|
||||
int bytes_per_datum;
|
||||
struct attribute_group *scan_el_attrs;
|
||||
long *scan_mask;
|
||||
bool scan_timestamp;
|
||||
const struct iio_buffer_access_funcs *access;
|
||||
struct list_head scan_el_dev_attr_list;
|
||||
struct attribute_group buffer_group;
|
||||
struct attribute_group scan_el_group;
|
||||
wait_queue_head_t pollq;
|
||||
bool stufftoread;
|
||||
const struct attribute **attrs;
|
||||
struct list_head demux_list;
|
||||
void *demux_bounce;
|
||||
struct list_head buffer_list;
|
||||
struct kref ref;
|
||||
unsigned int watermark;
|
||||
};
|
||||
|
||||
/**
|
||||
* iio_update_buffers() - add or remove buffer from active list
|
||||
* @indio_dev: device to add buffer to
|
||||
* @insert_buffer: buffer to insert
|
||||
* @remove_buffer: buffer_to_remove
|
||||
*
|
||||
* Note this will tear down the all buffering and build it up again
|
||||
*/
|
||||
int iio_update_buffers(struct iio_dev *indio_dev,
|
||||
struct iio_buffer *insert_buffer,
|
||||
struct iio_buffer *remove_buffer);
|
||||
|
||||
/**
|
||||
* iio_buffer_init() - Initialize the buffer structure
|
||||
* @buffer: buffer to be initialized
|
||||
**/
|
||||
void iio_buffer_init(struct iio_buffer *buffer);
|
||||
|
||||
int iio_scan_mask_query(struct iio_dev *indio_dev,
|
||||
struct iio_buffer *buffer, int bit);
|
||||
|
||||
/**
|
||||
* iio_push_to_buffers() - push to a registered buffer.
|
||||
* @indio_dev: iio_dev structure for device.
|
||||
* @data: Full scan.
|
||||
*/
|
||||
int iio_push_to_buffers(struct iio_dev *indio_dev, const void *data);
|
||||
|
||||
/*
|
||||
/**
|
||||
* iio_push_to_buffers_with_timestamp() - push data and timestamp to buffers
|
||||
* @indio_dev: iio_dev structure for device.
|
||||
* @data: sample data
|
||||
|
@ -168,34 +44,10 @@ static inline int iio_push_to_buffers_with_timestamp(struct iio_dev *indio_dev,
|
|||
return iio_push_to_buffers(indio_dev, data);
|
||||
}
|
||||
|
||||
int iio_update_demux(struct iio_dev *indio_dev);
|
||||
|
||||
bool iio_validate_scan_mask_onehot(struct iio_dev *indio_dev,
|
||||
const unsigned long *mask);
|
||||
const unsigned long *mask);
|
||||
|
||||
struct iio_buffer *iio_buffer_get(struct iio_buffer *buffer);
|
||||
void iio_buffer_put(struct iio_buffer *buffer);
|
||||
|
||||
/**
|
||||
* iio_device_attach_buffer - Attach a buffer to a IIO device
|
||||
* @indio_dev: The device the buffer should be attached to
|
||||
* @buffer: The buffer to attach to the device
|
||||
*
|
||||
* This function attaches a buffer to a IIO device. The buffer stays attached to
|
||||
* the device until the device is freed. The function should only be called at
|
||||
* most once per device.
|
||||
*/
|
||||
static inline void iio_device_attach_buffer(struct iio_dev *indio_dev,
|
||||
struct iio_buffer *buffer)
|
||||
{
|
||||
indio_dev->buffer = iio_buffer_get(buffer);
|
||||
}
|
||||
|
||||
#else /* CONFIG_IIO_BUFFER */
|
||||
|
||||
static inline void iio_buffer_get(struct iio_buffer *buffer) {}
|
||||
static inline void iio_buffer_put(struct iio_buffer *buffer) {}
|
||||
|
||||
#endif /* CONFIG_IIO_BUFFER */
|
||||
void iio_device_attach_buffer(struct iio_dev *indio_dev,
|
||||
struct iio_buffer *buffer);
|
||||
|
||||
#endif /* _IIO_BUFFER_GENERIC_H_ */
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
#ifndef _IIO_BUFFER_GENERIC_IMPL_H_
|
||||
#define _IIO_BUFFER_GENERIC_IMPL_H_
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/kref.h>
|
||||
|
||||
#ifdef CONFIG_IIO_BUFFER
|
||||
|
||||
struct iio_dev;
|
||||
struct iio_buffer;
|
||||
|
||||
/**
|
||||
* INDIO_BUFFER_FLAG_FIXED_WATERMARK - Watermark level of the buffer can not be
|
||||
* configured. It has a fixed value which will be buffer specific.
|
||||
*/
|
||||
#define INDIO_BUFFER_FLAG_FIXED_WATERMARK BIT(0)
|
||||
|
||||
/**
|
||||
* struct iio_buffer_access_funcs - access functions for buffers.
|
||||
* @store_to: actually store stuff to the buffer
|
||||
* @read_first_n: try to get a specified number of bytes (must exist)
|
||||
* @data_available: indicates how much data is available for reading from
|
||||
* the buffer.
|
||||
* @request_update: if a parameter change has been marked, update underlying
|
||||
* storage.
|
||||
* @set_bytes_per_datum:set number of bytes per datum
|
||||
* @set_length: set number of datums in buffer
|
||||
* @enable: called if the buffer is attached to a device and the
|
||||
* device starts sampling. Calls are balanced with
|
||||
* @disable.
|
||||
* @disable: called if the buffer is attached to a device and the
|
||||
* device stops sampling. Calles are balanced with @enable.
|
||||
* @release: called when the last reference to the buffer is dropped,
|
||||
* should free all resources allocated by the buffer.
|
||||
* @modes: Supported operating modes by this buffer type
|
||||
* @flags: A bitmask combination of INDIO_BUFFER_FLAG_*
|
||||
*
|
||||
* The purpose of this structure is to make the buffer element
|
||||
* modular as event for a given driver, different usecases may require
|
||||
* different buffer designs (space efficiency vs speed for example).
|
||||
*
|
||||
* It is worth noting that a given buffer implementation may only support a
|
||||
* small proportion of these functions. The core code 'should' cope fine with
|
||||
* any of them not existing.
|
||||
**/
|
||||
struct iio_buffer_access_funcs {
|
||||
int (*store_to)(struct iio_buffer *buffer, const void *data);
|
||||
int (*read_first_n)(struct iio_buffer *buffer,
|
||||
size_t n,
|
||||
char __user *buf);
|
||||
size_t (*data_available)(struct iio_buffer *buffer);
|
||||
|
||||
int (*request_update)(struct iio_buffer *buffer);
|
||||
|
||||
int (*set_bytes_per_datum)(struct iio_buffer *buffer, size_t bpd);
|
||||
int (*set_length)(struct iio_buffer *buffer, int length);
|
||||
|
||||
int (*enable)(struct iio_buffer *buffer, struct iio_dev *indio_dev);
|
||||
int (*disable)(struct iio_buffer *buffer, struct iio_dev *indio_dev);
|
||||
|
||||
void (*release)(struct iio_buffer *buffer);
|
||||
|
||||
unsigned int modes;
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iio_buffer - general buffer structure
|
||||
*
|
||||
* Note that the internals of this structure should only be of interest to
|
||||
* those writing new buffer implementations.
|
||||
*/
|
||||
struct iio_buffer {
|
||||
/** @length: Number of datums in buffer. */
|
||||
int length;
|
||||
|
||||
/** @bytes_per_datum: Size of individual datum including timestamp. */
|
||||
int bytes_per_datum;
|
||||
|
||||
/**
|
||||
* @access: Buffer access functions associated with the
|
||||
* implementation.
|
||||
*/
|
||||
const struct iio_buffer_access_funcs *access;
|
||||
|
||||
/** @scan_mask: Bitmask used in masking scan mode elements. */
|
||||
long *scan_mask;
|
||||
|
||||
/** @demux_list: List of operations required to demux the scan. */
|
||||
struct list_head demux_list;
|
||||
|
||||
/** @pollq: Wait queue to allow for polling on the buffer. */
|
||||
wait_queue_head_t pollq;
|
||||
|
||||
/** @watermark: Number of datums to wait for poll/read. */
|
||||
unsigned int watermark;
|
||||
|
||||
/* private: */
|
||||
/*
|
||||
* @scan_el_attrs: Control of scan elements if that scan mode
|
||||
* control method is used.
|
||||
*/
|
||||
struct attribute_group *scan_el_attrs;
|
||||
|
||||
/* @scan_timestamp: Does the scan mode include a timestamp. */
|
||||
bool scan_timestamp;
|
||||
|
||||
/* @scan_el_dev_attr_list: List of scan element related attributes. */
|
||||
struct list_head scan_el_dev_attr_list;
|
||||
|
||||
/* @buffer_group: Attributes of the buffer group. */
|
||||
struct attribute_group buffer_group;
|
||||
|
||||
/*
|
||||
* @scan_el_group: Attribute group for those attributes not
|
||||
* created from the iio_chan_info array.
|
||||
*/
|
||||
struct attribute_group scan_el_group;
|
||||
|
||||
/* @stufftoread: Flag to indicate new data. */
|
||||
bool stufftoread;
|
||||
|
||||
/* @attrs: Standard attributes of the buffer. */
|
||||
const struct attribute **attrs;
|
||||
|
||||
/* @demux_bounce: Buffer for doing gather from incoming scan. */
|
||||
void *demux_bounce;
|
||||
|
||||
/* @buffer_list: Entry in the devices list of current buffers. */
|
||||
struct list_head buffer_list;
|
||||
|
||||
/* @ref: Reference count of the buffer. */
|
||||
struct kref ref;
|
||||
};
|
||||
|
||||
/**
|
||||
* iio_update_buffers() - add or remove buffer from active list
|
||||
* @indio_dev: device to add buffer to
|
||||
* @insert_buffer: buffer to insert
|
||||
* @remove_buffer: buffer_to_remove
|
||||
*
|
||||
* Note this will tear down the all buffering and build it up again
|
||||
*/
|
||||
int iio_update_buffers(struct iio_dev *indio_dev,
|
||||
struct iio_buffer *insert_buffer,
|
||||
struct iio_buffer *remove_buffer);
|
||||
|
||||
/**
|
||||
* iio_buffer_init() - Initialize the buffer structure
|
||||
* @buffer: buffer to be initialized
|
||||
**/
|
||||
void iio_buffer_init(struct iio_buffer *buffer);
|
||||
|
||||
struct iio_buffer *iio_buffer_get(struct iio_buffer *buffer);
|
||||
void iio_buffer_put(struct iio_buffer *buffer);
|
||||
|
||||
#else /* CONFIG_IIO_BUFFER */
|
||||
|
||||
static inline void iio_buffer_get(struct iio_buffer *buffer) {}
|
||||
static inline void iio_buffer_put(struct iio_buffer *buffer) {}
|
||||
|
||||
#endif /* CONFIG_IIO_BUFFER */
|
||||
#endif /* _IIO_BUFFER_GENERIC_IMPL_H_ */
|
|
@ -28,4 +28,13 @@ static inline void st_sensors_of_i2c_probe(struct i2c_client *client,
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
int st_sensors_match_acpi_device(struct device *dev);
|
||||
#else
|
||||
static inline int st_sensors_match_acpi_device(struct device *dev)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ST_SENSORS_I2C_H */
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
#ifndef __LINUX_IIO_KFIFO_BUF_H__
|
||||
#define __LINUX_IIO_KFIFO_BUF_H__
|
||||
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
struct iio_buffer;
|
||||
struct device;
|
||||
|
||||
struct iio_buffer *iio_kfifo_allocate(void);
|
||||
void iio_kfifo_free(struct iio_buffer *r);
|
||||
|
|
|
@ -42,6 +42,7 @@ enum iio_chan_type {
|
|||
IIO_ELECTRICALCONDUCTIVITY,
|
||||
IIO_COUNT,
|
||||
IIO_INDEX,
|
||||
IIO_GRAVITY,
|
||||
};
|
||||
|
||||
enum iio_modifier {
|
||||
|
|
|
@ -57,6 +57,7 @@ static const char * const iio_chan_type_name_spec[] = {
|
|||
[IIO_RESISTANCE] = "resistance",
|
||||
[IIO_PH] = "ph",
|
||||
[IIO_UVINDEX] = "uvindex",
|
||||
[IIO_GRAVITY] = "gravity",
|
||||
};
|
||||
|
||||
static const char * const iio_ev_type_text[] = {
|
||||
|
@ -149,6 +150,7 @@ static bool event_is_known(struct iio_event_data *event)
|
|||
case IIO_RESISTANCE:
|
||||
case IIO_PH:
|
||||
case IIO_UVINDEX:
|
||||
case IIO_GRAVITY:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
|
|
Loading…
Reference in New Issue