mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-30 19:51:31 -04:00
Second set of new device support, features and cleanup for IIO in the 4.17 cycle
The uptick in staging cleanup is partly due to GSoC Applications
process being underway and one of Daniel's tasks being to try
cleaning up an IIO driver to move out of staging.
Naturally there is some normal staging cleanup progress in here
as well.
New device support
* Microchip mcp4018
- New driver supproting MCP4017, MCP4018 and MCP4019 digital pots.
* On Semiconductor lv0104cs
- New driver to support this ambient light sensor.
Cleanup
* axp20x_adc
- remove a !! in favour of clear ternary operator.
* ad2s1210 (staging cleanup)
- Reorganise to avoid ending a line with [
- Remove some unnecessary defines.
- Remove unsed variable.
* ad5380
- Replace magic 0 with IIO_CHAN_INFO_RAW
* ad5764
- Replace magic 0 with IIO_CHA_INFO_RAW
* ad7150 (staging cleanup)
- Align arguements with parenthesis.
* ad7152 (staging cleanup)
- Align arguements.
* ad7746 (staging cleanup)
- Align arguements.
* ad7816
- Remove pointless void pointer cast.
* ade7753
- Replace IIO_DEV_ATTR_CH_OFF with equivalent to avoid confusing
checkpatch (this macro didn't really help anyway). Also drop the
macro from the meter.h header.
* ade7754 (staging cleanup)
- Add names to funciton definition arguements.
- Align arguements with open parenthesis where possible.
* ade7758 (staging cleanup)
- Remove __func__ from dev_dbg statements as provided by dynamic
debug anyway.
- Align arguements with open parenthesis where possible.
* ade7759 (staging cleanup)
- Replace IIO_DEV_ATTR_CH_OFF with equiavalent to avoid confusing
checkpatch.
* adis16201 (staging cleanup)
- Headers in alphabetical order.
- Blank lines before returns.
* adis16209 (staging cleanup)
- Headers in alphabetical order
- Change some definition names to make them more meaningful (2 rounds
of this).
- Add explicit _REG prefix to register names to make them
obviously different from fields within those registers.
- Remove some superflous comments and group definitions better.
- Use a switch statement to make it semantically obvious that we
only have two options (rather than an unlimited 'else').
- Use sign_extent32 instead of open coding.
* adt7316 (staging cleanup)
- Move an export next to symbol.
* bmc150
- drop redundant __func__ in dynamic debug.
* ccs811
- Rename varaible to better reflect what it does.
* cros_ec
- Reduce sampling frequency before suspending to avoid preventing
suspend.
* dummy
- Correct whitespace in Kconfig.
- Add extra description in Kconfig.
* ds1803
- Remove a VLA which we always know is 2 long.
* hid-sensor-accel
- Replace magic number 0 by IIO_CHAN_INFO_RAW.
* hid-sensor-gyro
- Replace magic number 0 by IIO_CHAN_INFO_RAW.
* hid-sensor-light
- Replace magic number 0 by IIO_CHAN_INFO_RAW.
* hid-sensor-magn
- Replace magic number 0 by IIO_CHAN_INFO_RAW.
* lm3533
- Replace magic number 0 by IIO_CHAN_INFO_RAW
* mlx90632
- Squash a smatch warning - no runtime effect.
* stm32_dfsdm:
- Cleanup the dt bindings.
* sx9500
- Add GPIO ACPI mapping table to behave correctly when firmware
doesn't provide the mapping.
* tsl2x7x (staging cleanup)
- Fix the proximity sensor functionality.
- Remove platform data provided power functions. There are much
better ways to do this these days.
- Introduce some common functions to avoid various repititions.
- Stop using mutex_trylock when mutex_lock and wait a bit is fine.
- Improve error handling in various places.
- Drop some 'Camel case' (which wasn't actually strickly camel case
but was a bit odd.
- Drop some _available sysfs attributes for things that don't exist
(for particular supported parts).
-----BEGIN PGP SIGNATURE-----
iQJFBAABCAAvFiEEbilms4eEBlKRJoGxVIU0mcT0FogFAlqlObQRHGppYzIzQGtl
cm5lbC5vcmcACgkQVIU0mcT0FoissQ/+IQjcyxNdtOhHagvRgZ5z+6GYj6JzIX8s
Qqnep34sc6yUaygbJBXdcBQ5qdnPjrTk2CD3VYANOdC1bqXa80yuTGCGN6lBIBEr
/qBso6UbUiobA1PXektUj7Qel5HfVfYCyQ4NssSwKbdza6pyqoZA+iPzwiztWAwE
JsU5EdsTVxB/39kMOnoQJM6l5Lc5Ljg5pS3Du++3U49neiDk1Z/+VXsimFSPK3zt
FrSmMehNRYr0iFPwi7IZf8beG5ixmpRyY+KZzdM45Gm4hrpgE9ZIMd7A8B+ypx5N
WM1HJxu3PC6xHc7DIbcJG82sQ9DDrIeCkN4wN29ESE0JUt/GPvySSFZRAhsVp+xO
8rObv0r9Va+ones5WhZeAx/x3FowommZ2Jn+0FpX5H0X0mgi34au4YiOOAhg2gz5
9fnj39k7WarYozn1VVY8D8copO6C7j/UQVup/qfjmufkc7bDrQMml3rBWZbNxSUC
0TfZZJ9k+bxw2fvIedrjU/CFYbJJSpkRF/Sm3TZW28OMpxt/3qmAECtREOXVLyl2
cJnJpw8ERWbyeHJxhULurc2ekJVFeOS1sZ+sWU7P0LFt+J3dkOgZP1GJpXXhCWQa
GmgIm7vYYtAsVjDZ+FwH4ir/wSUeufscFEHs1oVhmIRh7jRCaJWycgLKZcK+rVRK
docrwE4wGrk=
=6eOr
-----END PGP SIGNATURE-----
Merge tag 'iio-for-4.17b' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next
Jonathan writes:
Second set of new device support, features and cleanup for IIO in the 4.17 cycle
The uptick in staging cleanup is partly due to GSoC Applications
process being underway and one of Daniel's tasks being to try
cleaning up an IIO driver to move out of staging.
Naturally there is some normal staging cleanup progress in here
as well.
New device support
* Microchip mcp4018
- New driver supproting MCP4017, MCP4018 and MCP4019 digital pots.
* On Semiconductor lv0104cs
- New driver to support this ambient light sensor.
Cleanup
* axp20x_adc
- remove a !! in favour of clear ternary operator.
* ad2s1210 (staging cleanup)
- Reorganise to avoid ending a line with [
- Remove some unnecessary defines.
- Remove unsed variable.
* ad5380
- Replace magic 0 with IIO_CHAN_INFO_RAW
* ad5764
- Replace magic 0 with IIO_CHA_INFO_RAW
* ad7150 (staging cleanup)
- Align arguements with parenthesis.
* ad7152 (staging cleanup)
- Align arguements.
* ad7746 (staging cleanup)
- Align arguements.
* ad7816
- Remove pointless void pointer cast.
* ade7753
- Replace IIO_DEV_ATTR_CH_OFF with equivalent to avoid confusing
checkpatch (this macro didn't really help anyway). Also drop the
macro from the meter.h header.
* ade7754 (staging cleanup)
- Add names to funciton definition arguements.
- Align arguements with open parenthesis where possible.
* ade7758 (staging cleanup)
- Remove __func__ from dev_dbg statements as provided by dynamic
debug anyway.
- Align arguements with open parenthesis where possible.
* ade7759 (staging cleanup)
- Replace IIO_DEV_ATTR_CH_OFF with equiavalent to avoid confusing
checkpatch.
* adis16201 (staging cleanup)
- Headers in alphabetical order.
- Blank lines before returns.
* adis16209 (staging cleanup)
- Headers in alphabetical order
- Change some definition names to make them more meaningful (2 rounds
of this).
- Add explicit _REG prefix to register names to make them
obviously different from fields within those registers.
- Remove some superflous comments and group definitions better.
- Use a switch statement to make it semantically obvious that we
only have two options (rather than an unlimited 'else').
- Use sign_extent32 instead of open coding.
* adt7316 (staging cleanup)
- Move an export next to symbol.
* bmc150
- drop redundant __func__ in dynamic debug.
* ccs811
- Rename varaible to better reflect what it does.
* cros_ec
- Reduce sampling frequency before suspending to avoid preventing
suspend.
* dummy
- Correct whitespace in Kconfig.
- Add extra description in Kconfig.
* ds1803
- Remove a VLA which we always know is 2 long.
* hid-sensor-accel
- Replace magic number 0 by IIO_CHAN_INFO_RAW.
* hid-sensor-gyro
- Replace magic number 0 by IIO_CHAN_INFO_RAW.
* hid-sensor-light
- Replace magic number 0 by IIO_CHAN_INFO_RAW.
* hid-sensor-magn
- Replace magic number 0 by IIO_CHAN_INFO_RAW.
* lm3533
- Replace magic number 0 by IIO_CHAN_INFO_RAW
* mlx90632
- Squash a smatch warning - no runtime effect.
* stm32_dfsdm:
- Cleanup the dt bindings.
* sx9500
- Add GPIO ACPI mapping table to behave correctly when firmware
doesn't provide the mapping.
* tsl2x7x (staging cleanup)
- Fix the proximity sensor functionality.
- Remove platform data provided power functions. There are much
better ways to do this these days.
- Introduce some common functions to avoid various repititions.
- Stop using mutex_trylock when mutex_lock and wait a bit is fine.
- Improve error handling in various places.
- Drop some 'Camel case' (which wasn't actually strickly camel case
but was a bit odd.
- Drop some _available sysfs attributes for things that don't exist
(for particular supported parts).
This commit is contained in:
commit
6761f0ac66
44 changed files with 1236 additions and 461 deletions
|
|
@ -336,8 +336,7 @@ static int bmc150_accel_update_slope(struct bmc150_accel_data *data)
|
|||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "%s: %x %x\n", __func__, data->slope_thres,
|
||||
data->slope_dur);
|
||||
dev_dbg(dev, "%x %x\n", data->slope_thres, data->slope_dur);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1716,7 +1715,6 @@ static int bmc150_accel_runtime_suspend(struct device *dev)
|
|||
struct bmc150_accel_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
dev_dbg(dev, __func__);
|
||||
ret = bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_SUSPEND, 0);
|
||||
if (ret < 0)
|
||||
return -EAGAIN;
|
||||
|
|
@ -1731,8 +1729,6 @@ static int bmc150_accel_runtime_resume(struct device *dev)
|
|||
int ret;
|
||||
int sleep_val;
|
||||
|
||||
dev_dbg(dev, __func__);
|
||||
|
||||
ret = bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_NORMAL, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ static int accel_3d_read_raw(struct iio_dev *indio_dev,
|
|||
*val = 0;
|
||||
*val2 = 0;
|
||||
switch (mask) {
|
||||
case 0:
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
hid_sensor_power_state(&accel_state->common_attributes, true);
|
||||
report_id = accel_state->accel[chan->scan_index].report_id;
|
||||
address = accel_3d_addresses[chan->scan_index];
|
||||
|
|
|
|||
|
|
@ -445,7 +445,7 @@ static int axp20x_adc_offset_voltage(struct iio_dev *indio_dev, int channel,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
*val = !!(*val) * 700000;
|
||||
*val = *val ? 700000 : 0;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
|
@ -542,15 +542,17 @@ static int axp20x_write_raw(struct iio_dev *indio_dev,
|
|||
if (val != 0 && val != 700000)
|
||||
return -EINVAL;
|
||||
|
||||
val = val ? 1 : 0;
|
||||
|
||||
switch (chan->channel) {
|
||||
case AXP20X_GPIO0_V:
|
||||
reg = AXP20X_GPIO10_IN_RANGE_GPIO0;
|
||||
regval = AXP20X_GPIO10_IN_RANGE_GPIO0_VAL(!!val);
|
||||
regval = AXP20X_GPIO10_IN_RANGE_GPIO0_VAL(val);
|
||||
break;
|
||||
|
||||
case AXP20X_GPIO1_V:
|
||||
reg = AXP20X_GPIO10_IN_RANGE_GPIO1;
|
||||
regval = AXP20X_GPIO10_IN_RANGE_GPIO1_VAL(!!val);
|
||||
regval = AXP20X_GPIO10_IN_RANGE_GPIO1_VAL(val);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ struct ccs811_reading {
|
|||
__be16 voc;
|
||||
u8 status;
|
||||
u8 error;
|
||||
__be16 resistance;
|
||||
__be16 raw_data;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct ccs811_data {
|
||||
|
|
@ -210,12 +210,12 @@ static int ccs811_read_raw(struct iio_dev *indio_dev,
|
|||
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
*val = be16_to_cpu(data->buffer.resistance) &
|
||||
*val = be16_to_cpu(data->buffer.raw_data) &
|
||||
CCS811_VOLTAGE_MASK;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CURRENT:
|
||||
*val = be16_to_cpu(data->buffer.resistance) >> 10;
|
||||
*val = be16_to_cpu(data->buffer.raw_data) >> 10;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CONCENTRATION:
|
||||
|
|
|
|||
|
|
@ -289,6 +289,7 @@ MODULE_DEVICE_TABLE(platform, cros_ec_sensors_ids);
|
|||
static struct platform_driver cros_ec_sensors_platform_driver = {
|
||||
.driver = {
|
||||
.name = "cros-ec-sensors",
|
||||
.pm = &cros_ec_sensors_pm_ops,
|
||||
},
|
||||
.probe = cros_ec_sensors_probe,
|
||||
.id_table = cros_ec_sensors_ids,
|
||||
|
|
|
|||
|
|
@ -446,5 +446,54 @@ int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(cros_ec_sensors_core_write);
|
||||
|
||||
static int __maybe_unused cros_ec_sensors_prepare(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (st->curr_sampl_freq == 0)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If the sensors are sampled at high frequency, we will not be able to
|
||||
* sleep. Set sampling to a long period if necessary.
|
||||
*/
|
||||
if (st->curr_sampl_freq < CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY) {
|
||||
mutex_lock(&st->cmd_lock);
|
||||
st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
|
||||
st->param.ec_rate.data = CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY;
|
||||
cros_ec_motion_send_host_cmd(st, 0);
|
||||
mutex_unlock(&st->cmd_lock);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __maybe_unused cros_ec_sensors_complete(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (st->curr_sampl_freq == 0)
|
||||
return;
|
||||
|
||||
if (st->curr_sampl_freq < CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY) {
|
||||
mutex_lock(&st->cmd_lock);
|
||||
st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
|
||||
st->param.ec_rate.data = st->curr_sampl_freq;
|
||||
cros_ec_motion_send_host_cmd(st, 0);
|
||||
mutex_unlock(&st->cmd_lock);
|
||||
}
|
||||
}
|
||||
|
||||
const struct dev_pm_ops cros_ec_sensors_pm_ops = {
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
.prepare = cros_ec_sensors_prepare,
|
||||
.complete = cros_ec_sensors_complete
|
||||
#endif
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(cros_ec_sensors_pm_ops);
|
||||
|
||||
MODULE_DESCRIPTION("ChromeOS EC sensor hub core functions");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
|||
|
|
@ -169,6 +169,8 @@ int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
|
|||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask);
|
||||
|
||||
extern const struct dev_pm_ops cros_ec_sensors_pm_ops;
|
||||
|
||||
/* List of extended channel specification for all sensors */
|
||||
extern const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[];
|
||||
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ static unsigned int ad5380_info_to_reg(struct iio_chan_spec const *chan,
|
|||
long info)
|
||||
{
|
||||
switch (info) {
|
||||
case 0:
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
return AD5380_REG_DATA(chan->address);
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
return AD5380_REG_OFFSET(chan->address);
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ static int ad5764_read(struct iio_dev *indio_dev, unsigned int reg,
|
|||
static int ad5764_chan_info_to_reg(struct iio_chan_spec const *chan, long info)
|
||||
{
|
||||
switch (info) {
|
||||
case 0:
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
return AD5764_REG_DATA(chan->address);
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
return AD5764_REG_OFFSET(chan->address);
|
||||
|
|
|
|||
|
|
@ -9,20 +9,24 @@ config IIO_DUMMY_EVGEN
|
|||
tristate
|
||||
|
||||
config IIO_SIMPLE_DUMMY
|
||||
tristate "An example driver with no hardware requirements"
|
||||
depends on IIO_SW_DEVICE
|
||||
help
|
||||
Driver intended mainly as documentation for how to write
|
||||
a driver. May also be useful for testing userspace code
|
||||
without hardware.
|
||||
tristate "An example driver with no hardware requirements"
|
||||
depends on IIO_SW_DEVICE
|
||||
help
|
||||
Driver intended mainly as documentation for how to write
|
||||
a driver. May also be useful for testing userspace code
|
||||
without hardware.
|
||||
|
||||
if IIO_SIMPLE_DUMMY
|
||||
|
||||
config IIO_SIMPLE_DUMMY_EVENTS
|
||||
bool "Event generation support"
|
||||
select IIO_DUMMY_EVGEN
|
||||
help
|
||||
Add some dummy events to the simple dummy driver.
|
||||
bool "Event generation support"
|
||||
select IIO_DUMMY_EVGEN
|
||||
help
|
||||
Add some dummy events to the simple dummy driver.
|
||||
|
||||
The purpose of this is to generate 'fake' event interrupts thus
|
||||
allowing that driver's code to be as close as possible to that
|
||||
a normal driver talking to hardware.
|
||||
|
||||
config IIO_SIMPLE_DUMMY_BUFFER
|
||||
bool "Buffered capture support"
|
||||
|
|
@ -32,6 +36,9 @@ config IIO_SIMPLE_DUMMY_BUFFER
|
|||
help
|
||||
Add buffered data capture to the simple dummy driver.
|
||||
|
||||
Buffer handling elements of industrial I/O reference driver.
|
||||
Uses the kfifo buffer.
|
||||
|
||||
endif # IIO_SIMPLE_DUMMY
|
||||
|
||||
endmenu
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ static int gyro_3d_read_raw(struct iio_dev *indio_dev,
|
|||
*val = 0;
|
||||
*val2 = 0;
|
||||
switch (mask) {
|
||||
case 0:
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
hid_sensor_power_state(&gyro_state->common_attributes, true);
|
||||
report_id = gyro_state->gyro[chan->scan_index].report_id;
|
||||
address = gyro_3d_addresses[chan->scan_index];
|
||||
|
|
|
|||
|
|
@ -275,6 +275,16 @@ config LTR501
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called ltr501.
|
||||
|
||||
config LV0104CS
|
||||
tristate "LV0104CS Ambient Light Sensor"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here if you want to build support for the On Semiconductor
|
||||
LV0104CS ambient light sensor.
|
||||
|
||||
To compile this driver as a module, choose M here:
|
||||
the module will be called lv0104cs.
|
||||
|
||||
config MAX44000
|
||||
tristate "MAX44000 Ambient and Infrared Proximity Sensor"
|
||||
depends on I2C
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ obj-$(CONFIG_ISL29125) += isl29125.o
|
|||
obj-$(CONFIG_JSA1212) += jsa1212.o
|
||||
obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o
|
||||
obj-$(CONFIG_LTR501) += ltr501.o
|
||||
obj-$(CONFIG_LV0104CS) += lv0104cs.o
|
||||
obj-$(CONFIG_MAX44000) += max44000.o
|
||||
obj-$(CONFIG_OPT3001) += opt3001.o
|
||||
obj-$(CONFIG_PA12203001) += pa12203001.o
|
||||
|
|
|
|||
|
|
@ -276,6 +276,7 @@ MODULE_DEVICE_TABLE(platform, cros_ec_light_prox_ids);
|
|||
static struct platform_driver cros_ec_light_prox_platform_driver = {
|
||||
.driver = {
|
||||
.name = "cros-ec-light-prox",
|
||||
.pm = &cros_ec_sensors_pm_ops,
|
||||
},
|
||||
.probe = cros_ec_light_prox_probe,
|
||||
.id_table = cros_ec_light_prox_ids,
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ static int als_read_raw(struct iio_dev *indio_dev,
|
|||
*val = 0;
|
||||
*val2 = 0;
|
||||
switch (mask) {
|
||||
case 0:
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
switch (chan->scan_index) {
|
||||
case CHANNEL_SCAN_INDEX_INTENSITY:
|
||||
case CHANNEL_SCAN_INDEX_ILLUM:
|
||||
|
|
|
|||
|
|
@ -199,7 +199,7 @@ static int lm3533_als_read_raw(struct iio_dev *indio_dev,
|
|||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case 0:
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
switch (chan->type) {
|
||||
case IIO_LIGHT:
|
||||
ret = lm3533_als_get_adc(indio_dev, false, val);
|
||||
|
|
|
|||
531
drivers/iio/light/lv0104cs.c
Normal file
531
drivers/iio/light/lv0104cs.c
Normal file
|
|
@ -0,0 +1,531 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* lv0104cs.c: LV0104CS Ambient Light Sensor Driver
|
||||
*
|
||||
* Copyright (C) 2018
|
||||
* Author: Jeff LaBundy <jeff@labundy.com>
|
||||
*
|
||||
* 7-bit I2C slave address: 0x13
|
||||
*
|
||||
* Link to data sheet: http://www.onsemi.com/pub/Collateral/LV0104CS-D.PDF
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define LV0104CS_REGVAL_MEASURE 0xE0
|
||||
#define LV0104CS_REGVAL_SLEEP 0x00
|
||||
|
||||
#define LV0104CS_SCALE_0_25X 0
|
||||
#define LV0104CS_SCALE_1X 1
|
||||
#define LV0104CS_SCALE_2X 2
|
||||
#define LV0104CS_SCALE_8X 3
|
||||
#define LV0104CS_SCALE_SHIFT 3
|
||||
|
||||
#define LV0104CS_INTEG_12_5MS 0
|
||||
#define LV0104CS_INTEG_100MS 1
|
||||
#define LV0104CS_INTEG_200MS 2
|
||||
#define LV0104CS_INTEG_SHIFT 1
|
||||
|
||||
#define LV0104CS_CALIBSCALE_UNITY 31
|
||||
|
||||
struct lv0104cs_private {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock;
|
||||
u8 calibscale;
|
||||
u8 scale;
|
||||
u8 int_time;
|
||||
};
|
||||
|
||||
struct lv0104cs_mapping {
|
||||
int val;
|
||||
int val2;
|
||||
u8 regval;
|
||||
};
|
||||
|
||||
static const struct lv0104cs_mapping lv0104cs_calibscales[] = {
|
||||
{ 0, 666666, 0x81 },
|
||||
{ 0, 800000, 0x82 },
|
||||
{ 0, 857142, 0x83 },
|
||||
{ 0, 888888, 0x84 },
|
||||
{ 0, 909090, 0x85 },
|
||||
{ 0, 923076, 0x86 },
|
||||
{ 0, 933333, 0x87 },
|
||||
{ 0, 941176, 0x88 },
|
||||
{ 0, 947368, 0x89 },
|
||||
{ 0, 952380, 0x8A },
|
||||
{ 0, 956521, 0x8B },
|
||||
{ 0, 960000, 0x8C },
|
||||
{ 0, 962962, 0x8D },
|
||||
{ 0, 965517, 0x8E },
|
||||
{ 0, 967741, 0x8F },
|
||||
{ 0, 969696, 0x90 },
|
||||
{ 0, 971428, 0x91 },
|
||||
{ 0, 972972, 0x92 },
|
||||
{ 0, 974358, 0x93 },
|
||||
{ 0, 975609, 0x94 },
|
||||
{ 0, 976744, 0x95 },
|
||||
{ 0, 977777, 0x96 },
|
||||
{ 0, 978723, 0x97 },
|
||||
{ 0, 979591, 0x98 },
|
||||
{ 0, 980392, 0x99 },
|
||||
{ 0, 981132, 0x9A },
|
||||
{ 0, 981818, 0x9B },
|
||||
{ 0, 982456, 0x9C },
|
||||
{ 0, 983050, 0x9D },
|
||||
{ 0, 983606, 0x9E },
|
||||
{ 0, 984126, 0x9F },
|
||||
{ 1, 0, 0x80 },
|
||||
{ 1, 16129, 0xBF },
|
||||
{ 1, 16666, 0xBE },
|
||||
{ 1, 17241, 0xBD },
|
||||
{ 1, 17857, 0xBC },
|
||||
{ 1, 18518, 0xBB },
|
||||
{ 1, 19230, 0xBA },
|
||||
{ 1, 20000, 0xB9 },
|
||||
{ 1, 20833, 0xB8 },
|
||||
{ 1, 21739, 0xB7 },
|
||||
{ 1, 22727, 0xB6 },
|
||||
{ 1, 23809, 0xB5 },
|
||||
{ 1, 24999, 0xB4 },
|
||||
{ 1, 26315, 0xB3 },
|
||||
{ 1, 27777, 0xB2 },
|
||||
{ 1, 29411, 0xB1 },
|
||||
{ 1, 31250, 0xB0 },
|
||||
{ 1, 33333, 0xAF },
|
||||
{ 1, 35714, 0xAE },
|
||||
{ 1, 38461, 0xAD },
|
||||
{ 1, 41666, 0xAC },
|
||||
{ 1, 45454, 0xAB },
|
||||
{ 1, 50000, 0xAA },
|
||||
{ 1, 55555, 0xA9 },
|
||||
{ 1, 62500, 0xA8 },
|
||||
{ 1, 71428, 0xA7 },
|
||||
{ 1, 83333, 0xA6 },
|
||||
{ 1, 100000, 0xA5 },
|
||||
{ 1, 125000, 0xA4 },
|
||||
{ 1, 166666, 0xA3 },
|
||||
{ 1, 250000, 0xA2 },
|
||||
{ 1, 500000, 0xA1 },
|
||||
};
|
||||
|
||||
static const struct lv0104cs_mapping lv0104cs_scales[] = {
|
||||
{ 0, 250000, LV0104CS_SCALE_0_25X << LV0104CS_SCALE_SHIFT },
|
||||
{ 1, 0, LV0104CS_SCALE_1X << LV0104CS_SCALE_SHIFT },
|
||||
{ 2, 0, LV0104CS_SCALE_2X << LV0104CS_SCALE_SHIFT },
|
||||
{ 8, 0, LV0104CS_SCALE_8X << LV0104CS_SCALE_SHIFT },
|
||||
};
|
||||
|
||||
static const struct lv0104cs_mapping lv0104cs_int_times[] = {
|
||||
{ 0, 12500, LV0104CS_INTEG_12_5MS << LV0104CS_INTEG_SHIFT },
|
||||
{ 0, 100000, LV0104CS_INTEG_100MS << LV0104CS_INTEG_SHIFT },
|
||||
{ 0, 200000, LV0104CS_INTEG_200MS << LV0104CS_INTEG_SHIFT },
|
||||
};
|
||||
|
||||
static int lv0104cs_write_reg(struct i2c_client *client, u8 regval)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_master_send(client, (char *)®val, sizeof(regval));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != sizeof(regval))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lv0104cs_read_adc(struct i2c_client *client, u16 *adc_output)
|
||||
{
|
||||
__be16 regval;
|
||||
int ret;
|
||||
|
||||
ret = i2c_master_recv(client, (char *)®val, sizeof(regval));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != sizeof(regval))
|
||||
return -EIO;
|
||||
|
||||
*adc_output = be16_to_cpu(regval);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lv0104cs_get_lux(struct lv0104cs_private *lv0104cs,
|
||||
int *val, int *val2)
|
||||
{
|
||||
u8 regval = LV0104CS_REGVAL_MEASURE;
|
||||
u16 adc_output;
|
||||
int ret;
|
||||
|
||||
regval |= lv0104cs_scales[lv0104cs->scale].regval;
|
||||
regval |= lv0104cs_int_times[lv0104cs->int_time].regval;
|
||||
ret = lv0104cs_write_reg(lv0104cs->client, regval);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* wait for integration time to pass (with margin) */
|
||||
switch (lv0104cs->int_time) {
|
||||
case LV0104CS_INTEG_12_5MS:
|
||||
msleep(50);
|
||||
break;
|
||||
|
||||
case LV0104CS_INTEG_100MS:
|
||||
msleep(150);
|
||||
break;
|
||||
|
||||
case LV0104CS_INTEG_200MS:
|
||||
msleep(250);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = lv0104cs_read_adc(lv0104cs->client, &adc_output);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = lv0104cs_write_reg(lv0104cs->client, LV0104CS_REGVAL_SLEEP);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* convert ADC output to lux */
|
||||
switch (lv0104cs->scale) {
|
||||
case LV0104CS_SCALE_0_25X:
|
||||
*val = adc_output * 4;
|
||||
*val2 = 0;
|
||||
return 0;
|
||||
|
||||
case LV0104CS_SCALE_1X:
|
||||
*val = adc_output;
|
||||
*val2 = 0;
|
||||
return 0;
|
||||
|
||||
case LV0104CS_SCALE_2X:
|
||||
*val = adc_output / 2;
|
||||
*val2 = (adc_output % 2) * 500000;
|
||||
return 0;
|
||||
|
||||
case LV0104CS_SCALE_8X:
|
||||
*val = adc_output / 8;
|
||||
*val2 = (adc_output % 8) * 125000;
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int lv0104cs_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct lv0104cs_private *lv0104cs = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
if (chan->type != IIO_LIGHT)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&lv0104cs->lock);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
ret = lv0104cs_get_lux(lv0104cs, val, val2);
|
||||
if (ret)
|
||||
goto err_mutex;
|
||||
ret = IIO_VAL_INT_PLUS_MICRO;
|
||||
break;
|
||||
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
*val = lv0104cs_calibscales[lv0104cs->calibscale].val;
|
||||
*val2 = lv0104cs_calibscales[lv0104cs->calibscale].val2;
|
||||
ret = IIO_VAL_INT_PLUS_MICRO;
|
||||
break;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = lv0104cs_scales[lv0104cs->scale].val;
|
||||
*val2 = lv0104cs_scales[lv0104cs->scale].val2;
|
||||
ret = IIO_VAL_INT_PLUS_MICRO;
|
||||
break;
|
||||
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
*val = lv0104cs_int_times[lv0104cs->int_time].val;
|
||||
*val2 = lv0104cs_int_times[lv0104cs->int_time].val2;
|
||||
ret = IIO_VAL_INT_PLUS_MICRO;
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
err_mutex:
|
||||
mutex_unlock(&lv0104cs->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lv0104cs_set_calibscale(struct lv0104cs_private *lv0104cs,
|
||||
int val, int val2)
|
||||
{
|
||||
int calibscale = val * 1000000 + val2;
|
||||
int floor, ceil, mid;
|
||||
int ret, i, index;
|
||||
|
||||
/* round to nearest quantized calibscale (sensitivity) */
|
||||
for (i = 0; i < ARRAY_SIZE(lv0104cs_calibscales) - 1; i++) {
|
||||
floor = lv0104cs_calibscales[i].val * 1000000
|
||||
+ lv0104cs_calibscales[i].val2;
|
||||
ceil = lv0104cs_calibscales[i + 1].val * 1000000
|
||||
+ lv0104cs_calibscales[i + 1].val2;
|
||||
mid = (floor + ceil) / 2;
|
||||
|
||||
/* round down */
|
||||
if (calibscale >= floor && calibscale < mid) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
|
||||
/* round up */
|
||||
if (calibscale >= mid && calibscale <= ceil) {
|
||||
index = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(lv0104cs_calibscales) - 1)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&lv0104cs->lock);
|
||||
|
||||
/* set calibscale (sensitivity) */
|
||||
ret = lv0104cs_write_reg(lv0104cs->client,
|
||||
lv0104cs_calibscales[index].regval);
|
||||
if (ret)
|
||||
goto err_mutex;
|
||||
|
||||
lv0104cs->calibscale = index;
|
||||
|
||||
err_mutex:
|
||||
mutex_unlock(&lv0104cs->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lv0104cs_set_scale(struct lv0104cs_private *lv0104cs,
|
||||
int val, int val2)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* hard matching */
|
||||
for (i = 0; i < ARRAY_SIZE(lv0104cs_scales); i++) {
|
||||
if (val != lv0104cs_scales[i].val)
|
||||
continue;
|
||||
|
||||
if (val2 == lv0104cs_scales[i].val2)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(lv0104cs_scales))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&lv0104cs->lock);
|
||||
lv0104cs->scale = i;
|
||||
mutex_unlock(&lv0104cs->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lv0104cs_set_int_time(struct lv0104cs_private *lv0104cs,
|
||||
int val, int val2)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* hard matching */
|
||||
for (i = 0; i < ARRAY_SIZE(lv0104cs_int_times); i++) {
|
||||
if (val != lv0104cs_int_times[i].val)
|
||||
continue;
|
||||
|
||||
if (val2 == lv0104cs_int_times[i].val2)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(lv0104cs_int_times))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&lv0104cs->lock);
|
||||
lv0104cs->int_time = i;
|
||||
mutex_unlock(&lv0104cs->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lv0104cs_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct lv0104cs_private *lv0104cs = iio_priv(indio_dev);
|
||||
|
||||
if (chan->type != IIO_LIGHT)
|
||||
return -EINVAL;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
return lv0104cs_set_calibscale(lv0104cs, val, val2);
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
return lv0104cs_set_scale(lv0104cs, val, val2);
|
||||
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
return lv0104cs_set_int_time(lv0104cs, val, val2);
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t lv0104cs_show_calibscale_avail(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
ssize_t len = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(lv0104cs_calibscales); i++) {
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06d ",
|
||||
lv0104cs_calibscales[i].val,
|
||||
lv0104cs_calibscales[i].val2);
|
||||
}
|
||||
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t lv0104cs_show_scale_avail(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
ssize_t len = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(lv0104cs_scales); i++) {
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06d ",
|
||||
lv0104cs_scales[i].val,
|
||||
lv0104cs_scales[i].val2);
|
||||
}
|
||||
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t lv0104cs_show_int_time_avail(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
ssize_t len = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(lv0104cs_int_times); i++) {
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06d ",
|
||||
lv0104cs_int_times[i].val,
|
||||
lv0104cs_int_times[i].val2);
|
||||
}
|
||||
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(calibscale_available, 0444,
|
||||
lv0104cs_show_calibscale_avail, NULL, 0);
|
||||
static IIO_DEVICE_ATTR(scale_available, 0444,
|
||||
lv0104cs_show_scale_avail, NULL, 0);
|
||||
static IIO_DEV_ATTR_INT_TIME_AVAIL(lv0104cs_show_int_time_avail);
|
||||
|
||||
static struct attribute *lv0104cs_attributes[] = {
|
||||
&iio_dev_attr_calibscale_available.dev_attr.attr,
|
||||
&iio_dev_attr_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_integration_time_available.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group lv0104cs_attribute_group = {
|
||||
.attrs = lv0104cs_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info lv0104cs_info = {
|
||||
.attrs = &lv0104cs_attribute_group,
|
||||
.read_raw = &lv0104cs_read_raw,
|
||||
.write_raw = &lv0104cs_write_raw,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec lv0104cs_channels[] = {
|
||||
{
|
||||
.type = IIO_LIGHT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_INT_TIME),
|
||||
},
|
||||
};
|
||||
|
||||
static int lv0104cs_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct lv0104cs_private *lv0104cs;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*lv0104cs));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
lv0104cs = iio_priv(indio_dev);
|
||||
|
||||
i2c_set_clientdata(client, lv0104cs);
|
||||
lv0104cs->client = client;
|
||||
|
||||
mutex_init(&lv0104cs->lock);
|
||||
|
||||
lv0104cs->calibscale = LV0104CS_CALIBSCALE_UNITY;
|
||||
lv0104cs->scale = LV0104CS_SCALE_1X;
|
||||
lv0104cs->int_time = LV0104CS_INTEG_200MS;
|
||||
|
||||
ret = lv0104cs_write_reg(lv0104cs->client,
|
||||
lv0104cs_calibscales[LV0104CS_CALIBSCALE_UNITY].regval);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->channels = lv0104cs_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(lv0104cs_channels);
|
||||
indio_dev->name = client->name;
|
||||
indio_dev->info = &lv0104cs_info;
|
||||
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id lv0104cs_id[] = {
|
||||
{ "lv0104cs", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, lv0104cs_id);
|
||||
|
||||
static struct i2c_driver lv0104cs_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "lv0104cs",
|
||||
},
|
||||
.id_table = lv0104cs_id,
|
||||
.probe = lv0104cs_probe,
|
||||
};
|
||||
module_i2c_driver(lv0104cs_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Jeff LaBundy <[email protected]>");
|
||||
MODULE_DESCRIPTION("LV0104CS Ambient Light Sensor Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -167,7 +167,7 @@ static int magn_3d_read_raw(struct iio_dev *indio_dev,
|
|||
*val = 0;
|
||||
*val2 = 0;
|
||||
switch (mask) {
|
||||
case 0:
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
hid_sensor_power_state(&magn_state->magn_flux_attributes, true);
|
||||
report_id =
|
||||
magn_state->magn[chan->address].report_id;
|
||||
|
|
|
|||
|
|
@ -47,6 +47,17 @@ config MAX5487
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called max5487.
|
||||
|
||||
config MCP4018
|
||||
tristate "Microchip MCP4017/18/19 Digital Potentiometer driver"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to build support for the Microchip
|
||||
MCP4017, MCP4018, MCP4019
|
||||
digital potentiometer chips.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called mcp4018.
|
||||
|
||||
config MCP4131
|
||||
tristate "Microchip MCP413X/414X/415X/416X/423X/424X/425X/426X Digital Potentiometer driver"
|
||||
depends on SPI
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ obj-$(CONFIG_AD5272) += ad5272.o
|
|||
obj-$(CONFIG_DS1803) += ds1803.o
|
||||
obj-$(CONFIG_MAX5481) += max5481.o
|
||||
obj-$(CONFIG_MAX5487) += max5487.o
|
||||
obj-$(CONFIG_MCP4018) += mcp4018.o
|
||||
obj-$(CONFIG_MCP4131) += mcp4131.o
|
||||
obj-$(CONFIG_MCP4531) += mcp4531.o
|
||||
obj-$(CONFIG_TPL0102) += tpl0102.o
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ static int ds1803_read_raw(struct iio_dev *indio_dev,
|
|||
struct ds1803_data *data = iio_priv(indio_dev);
|
||||
int pot = chan->channel;
|
||||
int ret;
|
||||
u8 result[indio_dev->num_channels];
|
||||
u8 result[ARRAY_SIZE(ds1803_channels)];
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
|
|
|
|||
194
drivers/iio/potentiometer/mcp4018.c
Normal file
194
drivers/iio/potentiometer/mcp4018.c
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Industrial I/O driver for Microchip digital potentiometers
|
||||
* Copyright (c) 2018 Axentia Technologies AB
|
||||
* Author: Peter Rosin <peda@axentia.se>
|
||||
*
|
||||
* Datasheet: http://www.microchip.com/downloads/en/DeviceDoc/22147a.pdf
|
||||
*
|
||||
* DEVID #Wipers #Positions Resistor Opts (kOhm)
|
||||
* mcp4017 1 128 5, 10, 50, 100
|
||||
* mcp4018 1 128 5, 10, 50, 100
|
||||
* mcp4019 1 128 5, 10, 50, 100
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#define MCP4018_WIPER_MAX 127
|
||||
|
||||
struct mcp4018_cfg {
|
||||
int kohms;
|
||||
};
|
||||
|
||||
enum mcp4018_type {
|
||||
MCP4018_502,
|
||||
MCP4018_103,
|
||||
MCP4018_503,
|
||||
MCP4018_104,
|
||||
};
|
||||
|
||||
static const struct mcp4018_cfg mcp4018_cfg[] = {
|
||||
[MCP4018_502] = { .kohms = 5, },
|
||||
[MCP4018_103] = { .kohms = 10, },
|
||||
[MCP4018_503] = { .kohms = 50, },
|
||||
[MCP4018_104] = { .kohms = 100, },
|
||||
};
|
||||
|
||||
struct mcp4018_data {
|
||||
struct i2c_client *client;
|
||||
const struct mcp4018_cfg *cfg;
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec mcp4018_channel = {
|
||||
.type = IIO_RESISTANCE,
|
||||
.indexed = 1,
|
||||
.output = 1,
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
};
|
||||
|
||||
static int mcp4018_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct mcp4018_data *data = iio_priv(indio_dev);
|
||||
s32 ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = i2c_smbus_read_byte(data->client);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 1000 * data->cfg->kohms;
|
||||
*val2 = MCP4018_WIPER_MAX;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int mcp4018_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct mcp4018_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (val > MCP4018_WIPER_MAX || val < 0)
|
||||
return -EINVAL;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return i2c_smbus_write_byte(data->client, val);
|
||||
}
|
||||
|
||||
static const struct iio_info mcp4018_info = {
|
||||
.read_raw = mcp4018_read_raw,
|
||||
.write_raw = mcp4018_write_raw,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
||||
#define MCP4018_COMPATIBLE(of_compatible, cfg) { \
|
||||
.compatible = of_compatible, \
|
||||
.data = &mcp4018_cfg[cfg], \
|
||||
}
|
||||
|
||||
static const struct of_device_id mcp4018_of_match[] = {
|
||||
MCP4018_COMPATIBLE("microchip,mcp4017-502", MCP4018_502),
|
||||
MCP4018_COMPATIBLE("microchip,mcp4017-103", MCP4018_103),
|
||||
MCP4018_COMPATIBLE("microchip,mcp4017-503", MCP4018_503),
|
||||
MCP4018_COMPATIBLE("microchip,mcp4017-104", MCP4018_104),
|
||||
MCP4018_COMPATIBLE("microchip,mcp4018-502", MCP4018_502),
|
||||
MCP4018_COMPATIBLE("microchip,mcp4018-103", MCP4018_103),
|
||||
MCP4018_COMPATIBLE("microchip,mcp4018-503", MCP4018_503),
|
||||
MCP4018_COMPATIBLE("microchip,mcp4018-104", MCP4018_104),
|
||||
MCP4018_COMPATIBLE("microchip,mcp4019-502", MCP4018_502),
|
||||
MCP4018_COMPATIBLE("microchip,mcp4019-103", MCP4018_103),
|
||||
MCP4018_COMPATIBLE("microchip,mcp4019-503", MCP4018_503),
|
||||
MCP4018_COMPATIBLE("microchip,mcp4019-104", MCP4018_104),
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mcp4018_of_match);
|
||||
|
||||
#endif
|
||||
|
||||
static int mcp4018_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct mcp4018_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
const struct of_device_id *match;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_BYTE)) {
|
||||
dev_err(dev, "SMBUS Byte transfers not supported\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
|
||||
match = of_match_device(of_match_ptr(mcp4018_of_match), dev);
|
||||
if (match)
|
||||
data->cfg = of_device_get_match_data(dev);
|
||||
else
|
||||
data->cfg = &mcp4018_cfg[id->driver_data];
|
||||
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->info = &mcp4018_info;
|
||||
indio_dev->channels = &mcp4018_channel;
|
||||
indio_dev->num_channels = 1;
|
||||
indio_dev->name = client->name;
|
||||
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id mcp4018_id[] = {
|
||||
{ "mcp4017-502", MCP4018_502 },
|
||||
{ "mcp4017-103", MCP4018_103 },
|
||||
{ "mcp4017-503", MCP4018_503 },
|
||||
{ "mcp4017-104", MCP4018_104 },
|
||||
{ "mcp4018-502", MCP4018_502 },
|
||||
{ "mcp4018-103", MCP4018_103 },
|
||||
{ "mcp4018-503", MCP4018_503 },
|
||||
{ "mcp4018-104", MCP4018_104 },
|
||||
{ "mcp4019-502", MCP4018_502 },
|
||||
{ "mcp4019-103", MCP4018_103 },
|
||||
{ "mcp4019-503", MCP4018_503 },
|
||||
{ "mcp4019-104", MCP4018_104 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mcp4018_id);
|
||||
|
||||
static struct i2c_driver mcp4018_driver = {
|
||||
.driver = {
|
||||
.name = "mcp4018",
|
||||
.of_match_table = of_match_ptr(mcp4018_of_match),
|
||||
},
|
||||
.probe = mcp4018_probe,
|
||||
.id_table = mcp4018_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(mcp4018_driver);
|
||||
|
||||
MODULE_AUTHOR("Peter Rosin <[email protected]>");
|
||||
MODULE_DESCRIPTION("MCP4018 digital potentiometer");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -32,9 +32,6 @@
|
|||
#define SX9500_DRIVER_NAME "sx9500"
|
||||
#define SX9500_IRQ_NAME "sx9500_event"
|
||||
|
||||
#define SX9500_GPIO_INT "interrupt"
|
||||
#define SX9500_GPIO_RESET "reset"
|
||||
|
||||
/* Register definitions. */
|
||||
#define SX9500_REG_IRQ_SRC 0x00
|
||||
#define SX9500_REG_STAT 0x01
|
||||
|
|
@ -866,26 +863,44 @@ static int sx9500_init_device(struct iio_dev *indio_dev)
|
|||
return sx9500_init_compensation(indio_dev);
|
||||
}
|
||||
|
||||
static const struct acpi_gpio_params reset_gpios = { 0, 0, false };
|
||||
static const struct acpi_gpio_params interrupt_gpios = { 2, 0, false };
|
||||
|
||||
static const struct acpi_gpio_mapping acpi_sx9500_gpios[] = {
|
||||
{ "reset-gpios", &reset_gpios, 1 },
|
||||
/*
|
||||
* Some platforms have a bug in ACPI GPIO description making IRQ
|
||||
* GPIO to be output only. Ask the GPIO core to ignore this limit.
|
||||
*/
|
||||
{ "interrupt-gpios", &interrupt_gpios, 1, ACPI_GPIO_QUIRK_NO_IO_RESTRICTION },
|
||||
{ },
|
||||
};
|
||||
|
||||
static void sx9500_gpio_probe(struct i2c_client *client,
|
||||
struct sx9500_data *data)
|
||||
{
|
||||
struct gpio_desc *gpiod_int;
|
||||
struct device *dev;
|
||||
int ret;
|
||||
|
||||
if (!client)
|
||||
return;
|
||||
|
||||
dev = &client->dev;
|
||||
|
||||
ret = devm_acpi_dev_add_driver_gpios(dev, acpi_sx9500_gpios);
|
||||
if (ret)
|
||||
dev_dbg(dev, "Unable to add GPIO mapping table\n");
|
||||
|
||||
if (client->irq <= 0) {
|
||||
gpiod_int = devm_gpiod_get(dev, SX9500_GPIO_INT, GPIOD_IN);
|
||||
gpiod_int = devm_gpiod_get(dev, "interrupt", GPIOD_IN);
|
||||
if (IS_ERR(gpiod_int))
|
||||
dev_err(dev, "gpio get irq failed\n");
|
||||
else
|
||||
client->irq = gpiod_to_irq(gpiod_int);
|
||||
}
|
||||
|
||||
data->gpiod_rst = devm_gpiod_get(dev, SX9500_GPIO_RESET, GPIOD_OUT_HIGH);
|
||||
data->gpiod_rst = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(data->gpiod_rst)) {
|
||||
dev_warn(dev, "gpio get reset pin failed\n");
|
||||
data->gpiod_rst = NULL;
|
||||
|
|
|
|||
|
|
@ -506,6 +506,8 @@ static int mlx90632_calc_ambient_dsp105(struct mlx90632_data *data, int *val)
|
|||
|
||||
ret = mlx90632_read_ambient_raw(data->regmap, &ambient_new_raw,
|
||||
&ambient_old_raw);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = mlx90632_calc_temp_ambient(ambient_new_raw, ambient_old_raw,
|
||||
PT, PR, PG, PO, Gb);
|
||||
return ret;
|
||||
|
|
|
|||
Loading…
Reference in a new issue