mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-12 13:34:10 +08:00
First round of new drivers, functionality and cleanups for the 4.2 cycle
New drivers / device support * st sensors driver, lsm303dlh magnetometer support. * ltr501 - support ltr301 and ltr559 chips. New functionality * IIO_CHAN_INFO_CALIBEMISSIVITY for thermopile sensors. * kxcjk1013 - make driver operational with external trigger. * Add iio targets to the tools Makefile. Cleanups * st sensors - more helpful error message if device id wrong or irq request fails, explicitly make the Block Data Update optional rather than relying on writes to address 0 not doing anything, make interrupt support optional (Not always wired, and not all devices actually have an interrupt line.) * kxcjk-1013 white space additions for readability, add the KXCJ9000 ACPI id as seen in the wild. * sx9500 - GPIO reset support, refactor the GPIO interrupt code, add power management, optimize power usage by powering down when possible, rename the gpio interrupt pin to be more useful, trivial return path simplification, trivial formatting fixes. * isl29018 - move towards ABI compliance with a view to moving this driver out of staging, add some brackets to ensure code works as expected. Note there is no actual bug as the condition being tested is always true (with current devices). * ltr501 - add regmap support to get caching etc for later patches, fix a parameter sanity check that always fails (bug introduced earlier in this series), ACPI enumeration support, interrupt rate control support, interrupt support in general and integration time control support, code alignment cleanups. * mma9553 - a number of little cleanups following a review from Hartmut after I'd already applied the original driver patch. * tmp006 - prefix some defines with TMP006 for consistency. * tsl4531 - cleanup some wrong prefixes, presumably from copy and paste. * mlx90614 - check for errors in read values, add power management, add emissivity setting, add device tree binding documentation, fix a duplicate const warning. * ti_am335x_adc - refactor the DT parsing into a separate function. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJVSzO0AAoJEFSFNJnE9BaIf5wP/imKKOOISkT+0VdjxrR0GRfZ DfkxzlQ7wwDm0MUk76paSldDJPJUdzEstiqTFESIow1Ws/3gpiCsjWVEe+eg1Y/e H7/ezIAl/liBiaNyN0EI4MSizEJ8pHDmLSxB+HrzBOuJ08b12fTPKhNNSKvJn8+B AAs7Joq+O69qe9OrtVcb6xEM+Qrlvx7KhdVbceZv3xoW4DBaiZnzGsBoF2HCQHdA Bi/TgHNT7lbUGIuPAOPuf76X+tGbUhm1eUBh0+RdFRCO3V8zggAtu+ZolDvBIPp1 MlK41e6whNZKa3Y4yX3eC5Wh2+ka9EW7OjvtsbbU9rf3026hatorh9wIaJi1dru1 CNxOSE0nuUCbyUWy/P1/DRpS2ysEb6NrOP2suqTIOfPbMankVv8WX1uh+BqndE7p EZC8c00kXVyZFyXNEq04vAvz0bduM4Gh8acTYK0ogfGncAgQPu/4kEWzGUBFJNRq WaKIgDL5tbJUU0G7pjhXho5VNEonE/2RtoSRPOWzmLY2HCCgCFkXKKwfhgjR9KT1 j1isov6HeeINpAfvk/mAkx37R+87WOahaeqdC2iZskX5JyqRSbydYwajKpP8AI8E s+58uwCANmf1qL2yXwIO7or0gkDQxgwKOBGVRmnOSiDW3dx4MrKAPIPwrM1DdPOH K66EknSVZ1wgVGehZzK3 =wyz7 -----END PGP SIGNATURE----- Merge tag 'iio-for-v4.2a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next Jonathan writes: First round of new drivers, functionality and cleanups for the 4.2 cycle New drivers / device support * st sensors driver, lsm303dlh magnetometer support. * ltr501 - support ltr301 and ltr559 chips. New functionality * IIO_CHAN_INFO_CALIBEMISSIVITY for thermopile sensors. * kxcjk1013 - make driver operational with external trigger. * Add iio targets to the tools Makefile. Cleanups * st sensors - more helpful error message if device id wrong or irq request fails, explicitly make the Block Data Update optional rather than relying on writes to address 0 not doing anything, make interrupt support optional (Not always wired, and not all devices actually have an interrupt line.) * kxcjk-1013 white space additions for readability, add the KXCJ9000 ACPI id as seen in the wild. * sx9500 - GPIO reset support, refactor the GPIO interrupt code, add power management, optimize power usage by powering down when possible, rename the gpio interrupt pin to be more useful, trivial return path simplification, trivial formatting fixes. * isl29018 - move towards ABI compliance with a view to moving this driver out of staging, add some brackets to ensure code works as expected. Note there is no actual bug as the condition being tested is always true (with current devices). * ltr501 - add regmap support to get caching etc for later patches, fix a parameter sanity check that always fails (bug introduced earlier in this series), ACPI enumeration support, interrupt rate control support, interrupt support in general and integration time control support, code alignment cleanups. * mma9553 - a number of little cleanups following a review from Hartmut after I'd already applied the original driver patch. * tmp006 - prefix some defines with TMP006 for consistency. * tsl4531 - cleanup some wrong prefixes, presumably from copy and paste. * mlx90614 - check for errors in read values, add power management, add emissivity setting, add device tree binding documentation, fix a duplicate const warning. * ti_am335x_adc - refactor the DT parsing into a separate function.
This commit is contained in:
commit
5ccca15567
@ -1364,3 +1364,14 @@ Description:
|
|||||||
hwfifo_watermak_min but not equal to any of the values in this
|
hwfifo_watermak_min but not equal to any of the values in this
|
||||||
list, the driver will chose an appropriate value for the
|
list, the driver will chose an appropriate value for the
|
||||||
hardware fifo watermark level.
|
hardware fifo watermark level.
|
||||||
|
|
||||||
|
What: /sys/bus/iio/devices/iio:deviceX/in_temp_calibemissivity
|
||||||
|
What: /sys/bus/iio/devices/iio:deviceX/in_tempX_calibemissivity
|
||||||
|
What: /sys/bus/iio/devices/iio:deviceX/in_temp_object_calibemissivity
|
||||||
|
What: /sys/bus/iio/devices/iio:deviceX/in_tempX_object_calibemissivity
|
||||||
|
KernelVersion: 4.1
|
||||||
|
Contact: linux-iio@vger.kernel.org
|
||||||
|
Description:
|
||||||
|
The emissivity ratio of the surface in the field of view of the
|
||||||
|
contactless temperature sensor. Emissivity varies from 0 to 1,
|
||||||
|
with 1 being the emissivity of a black body.
|
||||||
|
@ -45,6 +45,7 @@ Gyroscopes:
|
|||||||
- st,lsm330-gyro
|
- st,lsm330-gyro
|
||||||
|
|
||||||
Magnetometers:
|
Magnetometers:
|
||||||
|
- st,lsm303dlh-magn
|
||||||
- st,lsm303dlhc-magn
|
- st,lsm303dlhc-magn
|
||||||
- st,lsm303dlm-magn
|
- st,lsm303dlm-magn
|
||||||
- st,lis3mdl-magn
|
- st,lis3mdl-magn
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
* Melexis MLX90614 contactless IR temperature sensor
|
||||||
|
|
||||||
|
http://melexis.com/Infrared-Thermometer-Sensors/Infrared-Thermometer-Sensors/MLX90614-615.aspx
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
|
||||||
|
- compatible: should be "melexis,mlx90614"
|
||||||
|
- reg: the I2C address of the sensor
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
|
||||||
|
- wakeup-gpios: device tree identifier of the GPIO connected to the SDA line
|
||||||
|
to hold low in order to wake up the device. In normal operation, the
|
||||||
|
GPIO is set as input and will not interfere in I2C communication. There
|
||||||
|
is no need for a GPIO driving the SCL line. If no GPIO is given, power
|
||||||
|
management is disabled.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
mlx90614@5a {
|
||||||
|
compatible = "melexis,mlx90614";
|
||||||
|
reg = <0x5a>;
|
||||||
|
wakeup-gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>;
|
||||||
|
};
|
@ -117,6 +117,7 @@ lltc Linear Technology Corporation
|
|||||||
marvell Marvell Technology Group Ltd.
|
marvell Marvell Technology Group Ltd.
|
||||||
maxim Maxim Integrated Products
|
maxim Maxim Integrated Products
|
||||||
mediatek MediaTek Inc.
|
mediatek MediaTek Inc.
|
||||||
|
melexis Melexis N.V.
|
||||||
merrii Merrii Technology Co., Ltd.
|
merrii Merrii Technology Co., Ltd.
|
||||||
micrel Micrel Inc.
|
micrel Micrel Inc.
|
||||||
microchip Microchip Technology Inc.
|
microchip Microchip Technology Inc.
|
||||||
|
@ -875,15 +875,18 @@ static int kxcjk1013_write_event_config(struct iio_dev *indio_dev,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int kxcjk1013_validate_trigger(struct iio_dev *indio_dev,
|
static int kxcjk1013_buffer_preenable(struct iio_dev *indio_dev)
|
||||||
struct iio_trigger *trig)
|
|
||||||
{
|
{
|
||||||
struct kxcjk1013_data *data = iio_priv(indio_dev);
|
struct kxcjk1013_data *data = iio_priv(indio_dev);
|
||||||
|
|
||||||
if (data->dready_trig != trig && data->motion_trig != trig)
|
return kxcjk1013_set_power_state(data, true);
|
||||||
return -EINVAL;
|
}
|
||||||
|
|
||||||
return 0;
|
static int kxcjk1013_buffer_postdisable(struct iio_dev *indio_dev)
|
||||||
|
{
|
||||||
|
struct kxcjk1013_data *data = iio_priv(indio_dev);
|
||||||
|
|
||||||
|
return kxcjk1013_set_power_state(data, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
|
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
|
||||||
@ -935,6 +938,13 @@ static const struct iio_chan_spec kxcjk1013_channels[] = {
|
|||||||
IIO_CHAN_SOFT_TIMESTAMP(3),
|
IIO_CHAN_SOFT_TIMESTAMP(3),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct iio_buffer_setup_ops kxcjk1013_buffer_setup_ops = {
|
||||||
|
.preenable = kxcjk1013_buffer_preenable,
|
||||||
|
.postenable = iio_triggered_buffer_postenable,
|
||||||
|
.postdisable = kxcjk1013_buffer_postdisable,
|
||||||
|
.predisable = iio_triggered_buffer_predisable,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct iio_info kxcjk1013_info = {
|
static const struct iio_info kxcjk1013_info = {
|
||||||
.attrs = &kxcjk1013_attrs_group,
|
.attrs = &kxcjk1013_attrs_group,
|
||||||
.read_raw = kxcjk1013_read_raw,
|
.read_raw = kxcjk1013_read_raw,
|
||||||
@ -943,7 +953,6 @@ static const struct iio_info kxcjk1013_info = {
|
|||||||
.write_event_value = kxcjk1013_write_event,
|
.write_event_value = kxcjk1013_write_event,
|
||||||
.write_event_config = kxcjk1013_write_event_config,
|
.write_event_config = kxcjk1013_write_event_config,
|
||||||
.read_event_config = kxcjk1013_read_event_config,
|
.read_event_config = kxcjk1013_read_event_config,
|
||||||
.validate_trigger = kxcjk1013_validate_trigger,
|
|
||||||
.driver_module = THIS_MODULE,
|
.driver_module = THIS_MODULE,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1147,8 +1156,10 @@ static const char *kxcjk1013_match_acpi_device(struct device *dev,
|
|||||||
id = acpi_match_device(dev->driver->acpi_match_table, dev);
|
id = acpi_match_device(dev->driver->acpi_match_table, dev);
|
||||||
if (!id)
|
if (!id)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (strcmp(id->id, "SMO8500") == 0)
|
if (strcmp(id->id, "SMO8500") == 0)
|
||||||
*is_smo8500_device = true;
|
*is_smo8500_device = true;
|
||||||
|
|
||||||
*chipset = (enum kx_chipset)id->driver_data;
|
*chipset = (enum kx_chipset)id->driver_data;
|
||||||
|
|
||||||
return dev_name(dev);
|
return dev_name(dev);
|
||||||
@ -1163,6 +1174,7 @@ static int kxcjk1013_gpio_probe(struct i2c_client *client,
|
|||||||
|
|
||||||
if (!client)
|
if (!client)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (data->is_smo8500_device)
|
if (data->is_smo8500_device)
|
||||||
return -ENOTSUPP;
|
return -ENOTSUPP;
|
||||||
|
|
||||||
@ -1276,17 +1288,16 @@ static int kxcjk1013_probe(struct i2c_client *client,
|
|||||||
data->motion_trig = NULL;
|
data->motion_trig = NULL;
|
||||||
goto err_trigger_unregister;
|
goto err_trigger_unregister;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ret = iio_triggered_buffer_setup(indio_dev,
|
ret = iio_triggered_buffer_setup(indio_dev,
|
||||||
&iio_pollfunc_store_time,
|
&iio_pollfunc_store_time,
|
||||||
kxcjk1013_trigger_handler,
|
kxcjk1013_trigger_handler,
|
||||||
NULL);
|
&kxcjk1013_buffer_setup_ops);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&client->dev,
|
dev_err(&client->dev, "iio triggered buffer setup failed\n");
|
||||||
"iio triggered buffer setup failed\n");
|
|
||||||
goto err_trigger_unregister;
|
goto err_trigger_unregister;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
ret = iio_device_register(indio_dev);
|
ret = iio_device_register(indio_dev);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@ -1418,6 +1429,7 @@ static const struct dev_pm_ops kxcjk1013_pm_ops = {
|
|||||||
static const struct acpi_device_id kx_acpi_match[] = {
|
static const struct acpi_device_id kx_acpi_match[] = {
|
||||||
{"KXCJ1013", KXCJK1013},
|
{"KXCJ1013", KXCJK1013},
|
||||||
{"KXCJ1008", KXCJ91008},
|
{"KXCJ1008", KXCJ91008},
|
||||||
|
{"KXCJ9000", KXCJ91008},
|
||||||
{"KXTJ1009", KXTJ21009},
|
{"KXTJ1009", KXTJ21009},
|
||||||
{"SMO8500", KXCJ91008},
|
{"SMO8500", KXCJ91008},
|
||||||
{ },
|
{ },
|
||||||
|
@ -374,7 +374,7 @@ EXPORT_SYMBOL(mma9551_read_status_word);
|
|||||||
* @app_id: Application ID
|
* @app_id: Application ID
|
||||||
* @reg: Application register
|
* @reg: Application register
|
||||||
* @len: Length of array to read in bytes
|
* @len: Length of array to read in bytes
|
||||||
* @val: Array of words to read
|
* @buf: Array of words to read
|
||||||
*
|
*
|
||||||
* Read multiple configuration registers (word-sized registers).
|
* Read multiple configuration registers (word-sized registers).
|
||||||
*
|
*
|
||||||
@ -409,7 +409,7 @@ EXPORT_SYMBOL(mma9551_read_config_words);
|
|||||||
* @app_id: Application ID
|
* @app_id: Application ID
|
||||||
* @reg: Application register
|
* @reg: Application register
|
||||||
* @len: Length of array to read in bytes
|
* @len: Length of array to read in bytes
|
||||||
* @val: Array of words to read
|
* @buf: Array of words to read
|
||||||
*
|
*
|
||||||
* Read multiple status registers (word-sized registers).
|
* Read multiple status registers (word-sized registers).
|
||||||
*
|
*
|
||||||
@ -444,7 +444,7 @@ EXPORT_SYMBOL(mma9551_read_status_words);
|
|||||||
* @app_id: Application ID
|
* @app_id: Application ID
|
||||||
* @reg: Application register
|
* @reg: Application register
|
||||||
* @len: Length of array to write in bytes
|
* @len: Length of array to write in bytes
|
||||||
* @val: Array of words to write
|
* @buf: Array of words to write
|
||||||
*
|
*
|
||||||
* Write multiple configuration registers (word-sized registers).
|
* Write multiple configuration registers (word-sized registers).
|
||||||
*
|
*
|
||||||
@ -785,7 +785,7 @@ EXPORT_SYMBOL(mma9551_read_accel_scale);
|
|||||||
*/
|
*/
|
||||||
int mma9551_app_reset(struct i2c_client *client, u32 app_mask)
|
int mma9551_app_reset(struct i2c_client *client, u32 app_mask)
|
||||||
{
|
{
|
||||||
return mma9551_write_config_byte(client, MMA9551_APPID_RCS,
|
return mma9551_write_config_byte(client, MMA9551_APPID_RSC,
|
||||||
MMA9551_RSC_RESET +
|
MMA9551_RSC_RESET +
|
||||||
MMA9551_RSC_OFFSET(app_mask),
|
MMA9551_RSC_OFFSET(app_mask),
|
||||||
MMA9551_RSC_VAL(app_mask));
|
MMA9551_RSC_VAL(app_mask));
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
#define MMA9551_APPID_TILT 0x0B
|
#define MMA9551_APPID_TILT 0x0B
|
||||||
#define MMA9551_APPID_SLEEP_WAKE 0x12
|
#define MMA9551_APPID_SLEEP_WAKE 0x12
|
||||||
#define MMA9551_APPID_PEDOMETER 0x15
|
#define MMA9551_APPID_PEDOMETER 0x15
|
||||||
#define MMA9551_APPID_RCS 0x17
|
#define MMA9551_APPID_RSC 0x17
|
||||||
#define MMA9551_APPID_NONE 0xff
|
#define MMA9551_APPID_NONE 0xff
|
||||||
|
|
||||||
/* Reset/Suspend/Clear application app masks */
|
/* Reset/Suspend/Clear application app masks */
|
||||||
|
@ -62,8 +62,8 @@
|
|||||||
#define MMA9553_MASK_STATUS_STEPCHG BIT(13)
|
#define MMA9553_MASK_STATUS_STEPCHG BIT(13)
|
||||||
#define MMA9553_MASK_STATUS_ACTCHG BIT(12)
|
#define MMA9553_MASK_STATUS_ACTCHG BIT(12)
|
||||||
#define MMA9553_MASK_STATUS_SUSP BIT(11)
|
#define MMA9553_MASK_STATUS_SUSP BIT(11)
|
||||||
#define MMA9553_MASK_STATUS_ACTIVITY (BIT(10) | BIT(9) | BIT(8))
|
#define MMA9553_MASK_STATUS_ACTIVITY GENMASK(10, 8)
|
||||||
#define MMA9553_MASK_STATUS_VERSION 0x00FF
|
#define MMA9553_MASK_STATUS_VERSION GENMASK(7, 0)
|
||||||
|
|
||||||
#define MMA9553_REG_STEPCNT 0x02
|
#define MMA9553_REG_STEPCNT 0x02
|
||||||
#define MMA9553_REG_DISTANCE 0x04
|
#define MMA9553_REG_DISTANCE 0x04
|
||||||
@ -75,8 +75,9 @@
|
|||||||
#define MMA9553_DEFAULT_GPIO_PIN mma9551_gpio6
|
#define MMA9553_DEFAULT_GPIO_PIN mma9551_gpio6
|
||||||
#define MMA9553_DEFAULT_GPIO_POLARITY 0
|
#define MMA9553_DEFAULT_GPIO_POLARITY 0
|
||||||
|
|
||||||
/* Bitnum used for gpio configuration = bit number in high status byte */
|
/* Bitnum used for GPIO configuration = bit number in high status byte */
|
||||||
#define STATUS_TO_BITNUM(bit) (ffs(bit) - 9)
|
#define MMA9553_STATUS_TO_BITNUM(bit) (ffs(bit) - 9)
|
||||||
|
#define MMA9553_MAX_BITNUM MMA9553_STATUS_TO_BITNUM(BIT(16))
|
||||||
|
|
||||||
#define MMA9553_DEFAULT_SAMPLE_RATE 30 /* Hz */
|
#define MMA9553_DEFAULT_SAMPLE_RATE 30 /* Hz */
|
||||||
|
|
||||||
@ -353,11 +354,11 @@ static int mma9553_conf_gpio(struct mma9553_data *data)
|
|||||||
* This bit is the logical OR of the SUSPCHG, STEPCHG, and ACTCHG flags.
|
* This bit is the logical OR of the SUSPCHG, STEPCHG, and ACTCHG flags.
|
||||||
*/
|
*/
|
||||||
if (activity_enabled && ev_step_detect->enabled)
|
if (activity_enabled && ev_step_detect->enabled)
|
||||||
bitnum = STATUS_TO_BITNUM(MMA9553_MASK_STATUS_MRGFL);
|
bitnum = MMA9553_STATUS_TO_BITNUM(MMA9553_MASK_STATUS_MRGFL);
|
||||||
else if (ev_step_detect->enabled)
|
else if (ev_step_detect->enabled)
|
||||||
bitnum = STATUS_TO_BITNUM(MMA9553_MASK_STATUS_STEPCHG);
|
bitnum = MMA9553_STATUS_TO_BITNUM(MMA9553_MASK_STATUS_STEPCHG);
|
||||||
else if (activity_enabled)
|
else if (activity_enabled)
|
||||||
bitnum = STATUS_TO_BITNUM(MMA9553_MASK_STATUS_ACTCHG);
|
bitnum = MMA9553_STATUS_TO_BITNUM(MMA9553_MASK_STATUS_ACTCHG);
|
||||||
else /* Reset */
|
else /* Reset */
|
||||||
appid = MMA9551_APPID_NONE;
|
appid = MMA9551_APPID_NONE;
|
||||||
|
|
||||||
@ -365,9 +366,12 @@ static int mma9553_conf_gpio(struct mma9553_data *data)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Save initial values for activity and stepcnt */
|
/* Save initial values for activity and stepcnt */
|
||||||
if (activity_enabled || ev_step_detect->enabled)
|
if (activity_enabled || ev_step_detect->enabled) {
|
||||||
mma9553_read_activity_stepcnt(data, &data->activity,
|
ret = mma9553_read_activity_stepcnt(data, &data->activity,
|
||||||
&data->stepcnt);
|
&data->stepcnt);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
ret = mma9551_gpio_config(data->client,
|
ret = mma9551_gpio_config(data->client,
|
||||||
MMA9553_DEFAULT_GPIO_PIN,
|
MMA9553_DEFAULT_GPIO_PIN,
|
||||||
@ -398,13 +402,13 @@ static int mma9553_init(struct mma9553_data *data)
|
|||||||
sizeof(data->conf), (u16 *) &data->conf);
|
sizeof(data->conf), (u16 *) &data->conf);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&data->client->dev,
|
dev_err(&data->client->dev,
|
||||||
"device is not MMA9553L: failed to read cfg regs\n");
|
"failed to read configuration registers\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Reset gpio */
|
/* Reset GPIO */
|
||||||
data->gpio_bitnum = -1;
|
data->gpio_bitnum = MMA9553_MAX_BITNUM;
|
||||||
ret = mma9553_conf_gpio(data);
|
ret = mma9553_conf_gpio(data);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
@ -438,6 +442,32 @@ static int mma9553_init(struct mma9553_data *data)
|
|||||||
return mma9551_set_device_state(data->client, true);
|
return mma9551_set_device_state(data->client, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mma9553_read_status_word(struct mma9553_data *data, u16 reg,
|
||||||
|
u16 *tmp)
|
||||||
|
{
|
||||||
|
bool powered_on;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The HW only counts steps and other dependent
|
||||||
|
* parameters (speed, distance, calories, activity)
|
||||||
|
* if power is on (from enabling an event or the
|
||||||
|
* step counter).
|
||||||
|
*/
|
||||||
|
powered_on = mma9553_is_any_event_enabled(data, false, 0) ||
|
||||||
|
data->stepcnt_enabled;
|
||||||
|
if (!powered_on) {
|
||||||
|
dev_err(&data->client->dev, "No channels enabled\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&data->mutex);
|
||||||
|
ret = mma9551_read_status_word(data->client, MMA9551_APPID_PEDOMETER,
|
||||||
|
reg, tmp);
|
||||||
|
mutex_unlock(&data->mutex);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int mma9553_read_raw(struct iio_dev *indio_dev,
|
static int mma9553_read_raw(struct iio_dev *indio_dev,
|
||||||
struct iio_chan_spec const *chan,
|
struct iio_chan_spec const *chan,
|
||||||
int *val, int *val2, long mask)
|
int *val, int *val2, long mask)
|
||||||
@ -446,69 +476,30 @@ static int mma9553_read_raw(struct iio_dev *indio_dev,
|
|||||||
int ret;
|
int ret;
|
||||||
u16 tmp;
|
u16 tmp;
|
||||||
u8 activity;
|
u8 activity;
|
||||||
bool powered_on;
|
|
||||||
|
|
||||||
switch (mask) {
|
switch (mask) {
|
||||||
case IIO_CHAN_INFO_PROCESSED:
|
case IIO_CHAN_INFO_PROCESSED:
|
||||||
switch (chan->type) {
|
switch (chan->type) {
|
||||||
case IIO_STEPS:
|
case IIO_STEPS:
|
||||||
/*
|
ret = mma9553_read_status_word(data,
|
||||||
* The HW only counts steps and other dependent
|
|
||||||
* parameters (speed, distance, calories, activity)
|
|
||||||
* if power is on (from enabling an event or the
|
|
||||||
* step counter */
|
|
||||||
powered_on =
|
|
||||||
mma9553_is_any_event_enabled(data, false, 0) ||
|
|
||||||
data->stepcnt_enabled;
|
|
||||||
if (!powered_on) {
|
|
||||||
dev_err(&data->client->dev,
|
|
||||||
"No channels enabled\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
mutex_lock(&data->mutex);
|
|
||||||
ret = mma9551_read_status_word(data->client,
|
|
||||||
MMA9551_APPID_PEDOMETER,
|
|
||||||
MMA9553_REG_STEPCNT,
|
MMA9553_REG_STEPCNT,
|
||||||
&tmp);
|
&tmp);
|
||||||
mutex_unlock(&data->mutex);
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
*val = tmp;
|
*val = tmp;
|
||||||
return IIO_VAL_INT;
|
return IIO_VAL_INT;
|
||||||
case IIO_DISTANCE:
|
case IIO_DISTANCE:
|
||||||
powered_on =
|
ret = mma9553_read_status_word(data,
|
||||||
mma9553_is_any_event_enabled(data, false, 0) ||
|
|
||||||
data->stepcnt_enabled;
|
|
||||||
if (!powered_on) {
|
|
||||||
dev_err(&data->client->dev,
|
|
||||||
"No channels enabled\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
mutex_lock(&data->mutex);
|
|
||||||
ret = mma9551_read_status_word(data->client,
|
|
||||||
MMA9551_APPID_PEDOMETER,
|
|
||||||
MMA9553_REG_DISTANCE,
|
MMA9553_REG_DISTANCE,
|
||||||
&tmp);
|
&tmp);
|
||||||
mutex_unlock(&data->mutex);
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
*val = tmp;
|
*val = tmp;
|
||||||
return IIO_VAL_INT;
|
return IIO_VAL_INT;
|
||||||
case IIO_ACTIVITY:
|
case IIO_ACTIVITY:
|
||||||
powered_on =
|
ret = mma9553_read_status_word(data,
|
||||||
mma9553_is_any_event_enabled(data, false, 0) ||
|
|
||||||
data->stepcnt_enabled;
|
|
||||||
if (!powered_on) {
|
|
||||||
dev_err(&data->client->dev,
|
|
||||||
"No channels enabled\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
mutex_lock(&data->mutex);
|
|
||||||
ret = mma9551_read_status_word(data->client,
|
|
||||||
MMA9551_APPID_PEDOMETER,
|
|
||||||
MMA9553_REG_STATUS,
|
MMA9553_REG_STATUS,
|
||||||
&tmp);
|
&tmp);
|
||||||
mutex_unlock(&data->mutex);
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
@ -533,38 +524,17 @@ static int mma9553_read_raw(struct iio_dev *indio_dev,
|
|||||||
case IIO_VELOCITY: /* m/h */
|
case IIO_VELOCITY: /* m/h */
|
||||||
if (chan->channel2 != IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
|
if (chan->channel2 != IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
powered_on =
|
ret = mma9553_read_status_word(data,
|
||||||
mma9553_is_any_event_enabled(data, false, 0) ||
|
MMA9553_REG_SPEED,
|
||||||
data->stepcnt_enabled;
|
&tmp);
|
||||||
if (!powered_on) {
|
|
||||||
dev_err(&data->client->dev,
|
|
||||||
"No channels enabled\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
mutex_lock(&data->mutex);
|
|
||||||
ret = mma9551_read_status_word(data->client,
|
|
||||||
MMA9551_APPID_PEDOMETER,
|
|
||||||
MMA9553_REG_SPEED, &tmp);
|
|
||||||
mutex_unlock(&data->mutex);
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
*val = tmp;
|
*val = tmp;
|
||||||
return IIO_VAL_INT;
|
return IIO_VAL_INT;
|
||||||
case IIO_ENERGY: /* Cal or kcal */
|
case IIO_ENERGY: /* Cal or kcal */
|
||||||
powered_on =
|
ret = mma9553_read_status_word(data,
|
||||||
mma9553_is_any_event_enabled(data, false, 0) ||
|
|
||||||
data->stepcnt_enabled;
|
|
||||||
if (!powered_on) {
|
|
||||||
dev_err(&data->client->dev,
|
|
||||||
"No channels enabled\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
mutex_lock(&data->mutex);
|
|
||||||
ret = mma9551_read_status_word(data->client,
|
|
||||||
MMA9551_APPID_PEDOMETER,
|
|
||||||
MMA9553_REG_CALORIES,
|
MMA9553_REG_CALORIES,
|
||||||
&tmp);
|
&tmp);
|
||||||
mutex_unlock(&data->mutex);
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
*val = tmp;
|
*val = tmp;
|
||||||
@ -791,7 +761,7 @@ static int mma9553_write_event_config(struct iio_dev *indio_dev,
|
|||||||
|
|
||||||
mutex_unlock(&data->mutex);
|
mutex_unlock(&data->mutex);
|
||||||
|
|
||||||
return ret;
|
return 0;
|
||||||
|
|
||||||
err_conf_gpio:
|
err_conf_gpio:
|
||||||
if (state) {
|
if (state) {
|
||||||
@ -896,7 +866,7 @@ static int mma9553_get_calibgender_mode(struct iio_dev *indio_dev,
|
|||||||
gender = mma9553_get_bits(data->conf.filter, MMA9553_MASK_CONF_MALE);
|
gender = mma9553_get_bits(data->conf.filter, MMA9553_MASK_CONF_MALE);
|
||||||
/*
|
/*
|
||||||
* HW expects 0 for female and 1 for male,
|
* HW expects 0 for female and 1 for male,
|
||||||
* while iio index is 0 for male and 1 for female
|
* while iio index is 0 for male and 1 for female.
|
||||||
*/
|
*/
|
||||||
return !gender;
|
return !gender;
|
||||||
}
|
}
|
||||||
@ -943,11 +913,11 @@ static const struct iio_event_spec mma9553_activity_events[] = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char * const calibgender_modes[] = { "male", "female" };
|
static const char * const mma9553_calibgender_modes[] = { "male", "female" };
|
||||||
|
|
||||||
static const struct iio_enum mma9553_calibgender_enum = {
|
static const struct iio_enum mma9553_calibgender_enum = {
|
||||||
.items = calibgender_modes,
|
.items = mma9553_calibgender_modes,
|
||||||
.num_items = ARRAY_SIZE(calibgender_modes),
|
.num_items = ARRAY_SIZE(mma9553_calibgender_modes),
|
||||||
.get = mma9553_get_calibgender_mode,
|
.get = mma9553_get_calibgender_mode,
|
||||||
.set = mma9553_set_calibgender_mode,
|
.set = mma9553_set_calibgender_mode,
|
||||||
};
|
};
|
||||||
@ -1108,16 +1078,16 @@ static int mma9553_gpio_probe(struct i2c_client *client)
|
|||||||
|
|
||||||
dev = &client->dev;
|
dev = &client->dev;
|
||||||
|
|
||||||
/* data ready gpio interrupt pin */
|
/* data ready GPIO interrupt pin */
|
||||||
gpio = devm_gpiod_get_index(dev, MMA9553_GPIO_NAME, 0, GPIOD_IN);
|
gpio = devm_gpiod_get_index(dev, MMA9553_GPIO_NAME, 0, GPIOD_IN);
|
||||||
if (IS_ERR(gpio)) {
|
if (IS_ERR(gpio)) {
|
||||||
dev_err(dev, "acpi gpio get index failed\n");
|
dev_err(dev, "ACPI GPIO get index failed\n");
|
||||||
return PTR_ERR(gpio);
|
return PTR_ERR(gpio);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = gpiod_to_irq(gpio);
|
ret = gpiod_to_irq(gpio);
|
||||||
|
|
||||||
dev_dbg(dev, "gpio resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
|
dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -395,16 +395,30 @@ static const struct iio_info tiadc_info = {
|
|||||||
.driver_module = THIS_MODULE,
|
.driver_module = THIS_MODULE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int tiadc_parse_dt(struct platform_device *pdev,
|
||||||
|
struct tiadc_device *adc_dev)
|
||||||
|
{
|
||||||
|
struct device_node *node = pdev->dev.of_node;
|
||||||
|
struct property *prop;
|
||||||
|
const __be32 *cur;
|
||||||
|
int channels = 0;
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
of_property_for_each_u32(node, "ti,adc-channels", prop, cur, val) {
|
||||||
|
adc_dev->channel_line[channels] = val;
|
||||||
|
channels++;
|
||||||
|
}
|
||||||
|
|
||||||
|
adc_dev->channels = channels;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int tiadc_probe(struct platform_device *pdev)
|
static int tiadc_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct iio_dev *indio_dev;
|
struct iio_dev *indio_dev;
|
||||||
struct tiadc_device *adc_dev;
|
struct tiadc_device *adc_dev;
|
||||||
struct device_node *node = pdev->dev.of_node;
|
struct device_node *node = pdev->dev.of_node;
|
||||||
struct property *prop;
|
|
||||||
const __be32 *cur;
|
|
||||||
int err;
|
int err;
|
||||||
u32 val;
|
|
||||||
int channels = 0;
|
|
||||||
|
|
||||||
if (!node) {
|
if (!node) {
|
||||||
dev_err(&pdev->dev, "Could not find valid DT data.\n");
|
dev_err(&pdev->dev, "Could not find valid DT data.\n");
|
||||||
@ -420,12 +434,7 @@ static int tiadc_probe(struct platform_device *pdev)
|
|||||||
adc_dev = iio_priv(indio_dev);
|
adc_dev = iio_priv(indio_dev);
|
||||||
|
|
||||||
adc_dev->mfd_tscadc = ti_tscadc_dev_get(pdev);
|
adc_dev->mfd_tscadc = ti_tscadc_dev_get(pdev);
|
||||||
|
tiadc_parse_dt(pdev, adc_dev);
|
||||||
of_property_for_each_u32(node, "ti,adc-channels", prop, cur, val) {
|
|
||||||
adc_dev->channel_line[channels] = val;
|
|
||||||
channels++;
|
|
||||||
}
|
|
||||||
adc_dev->channels = channels;
|
|
||||||
|
|
||||||
indio_dev->dev.parent = &pdev->dev;
|
indio_dev->dev.parent = &pdev->dev;
|
||||||
indio_dev->name = dev_name(&pdev->dev);
|
indio_dev->name = dev_name(&pdev->dev);
|
||||||
|
@ -245,6 +245,16 @@ static int st_sensors_set_drdy_int_pin(struct iio_dev *indio_dev,
|
|||||||
{
|
{
|
||||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||||
|
|
||||||
|
/* Sensor does not support interrupts */
|
||||||
|
if (sdata->sensor_settings->drdy_irq.addr == 0) {
|
||||||
|
if (pdata->drdy_int_pin)
|
||||||
|
dev_info(&indio_dev->dev,
|
||||||
|
"DRDY on pin INT%d specified, but sensor "
|
||||||
|
"does not support interrupts\n",
|
||||||
|
pdata->drdy_int_pin);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
switch (pdata->drdy_int_pin) {
|
switch (pdata->drdy_int_pin) {
|
||||||
case 1:
|
case 1:
|
||||||
if (sdata->sensor_settings->drdy_irq.mask_int1 == 0) {
|
if (sdata->sensor_settings->drdy_irq.mask_int1 == 0) {
|
||||||
@ -285,7 +295,7 @@ static struct st_sensors_platform_data *st_sensors_of_probe(struct device *dev,
|
|||||||
if (!of_property_read_u32(np, "st,drdy-int-pin", &val) && (val <= 2))
|
if (!of_property_read_u32(np, "st,drdy-int-pin", &val) && (val <= 2))
|
||||||
pdata->drdy_int_pin = (u8) val;
|
pdata->drdy_int_pin = (u8) val;
|
||||||
else
|
else
|
||||||
pdata->drdy_int_pin = defdata ? defdata->drdy_int_pin : 1;
|
pdata->drdy_int_pin = defdata ? defdata->drdy_int_pin : 0;
|
||||||
|
|
||||||
return pdata;
|
return pdata;
|
||||||
}
|
}
|
||||||
@ -334,11 +344,13 @@ int st_sensors_init_sensor(struct iio_dev *indio_dev,
|
|||||||
return err;
|
return err;
|
||||||
|
|
||||||
/* set BDU */
|
/* set BDU */
|
||||||
|
if (sdata->sensor_settings->bdu.addr) {
|
||||||
err = st_sensors_write_data_with_mask(indio_dev,
|
err = st_sensors_write_data_with_mask(indio_dev,
|
||||||
sdata->sensor_settings->bdu.addr,
|
sdata->sensor_settings->bdu.addr,
|
||||||
sdata->sensor_settings->bdu.mask, true);
|
sdata->sensor_settings->bdu.mask, true);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
|
err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
|
||||||
|
|
||||||
@ -491,7 +503,8 @@ int st_sensors_check_device_support(struct iio_dev *indio_dev,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (n == ARRAY_SIZE(sensor_settings[i].sensors_supported)) {
|
if (n == ARRAY_SIZE(sensor_settings[i].sensors_supported)) {
|
||||||
dev_err(&indio_dev->dev, "device name and WhoAmI mismatch.\n");
|
dev_err(&indio_dev->dev, "device name \"%s\" and WhoAmI (0x%02x) mismatch",
|
||||||
|
indio_dev->name, wai);
|
||||||
goto sensor_name_mismatch;
|
goto sensor_name_mismatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,8 +37,10 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
|
|||||||
IRQF_TRIGGER_RISING,
|
IRQF_TRIGGER_RISING,
|
||||||
sdata->trig->name,
|
sdata->trig->name,
|
||||||
sdata->trig);
|
sdata->trig);
|
||||||
if (err)
|
if (err) {
|
||||||
|
dev_err(&indio_dev->dev, "failed to request trigger IRQ.\n");
|
||||||
goto request_irq_error;
|
goto request_irq_error;
|
||||||
|
}
|
||||||
|
|
||||||
iio_trigger_set_drvdata(sdata->trig, indio_dev);
|
iio_trigger_set_drvdata(sdata->trig, indio_dev);
|
||||||
sdata->trig->ops = trigger_ops;
|
sdata->trig->ops = trigger_ops;
|
||||||
|
@ -128,6 +128,7 @@ static const char * const iio_chan_info_postfix[] = {
|
|||||||
[IIO_CHAN_INFO_CALIBWEIGHT] = "calibweight",
|
[IIO_CHAN_INFO_CALIBWEIGHT] = "calibweight",
|
||||||
[IIO_CHAN_INFO_DEBOUNCE_COUNT] = "debounce_count",
|
[IIO_CHAN_INFO_DEBOUNCE_COUNT] = "debounce_count",
|
||||||
[IIO_CHAN_INFO_DEBOUNCE_TIME] = "debounce_time",
|
[IIO_CHAN_INFO_DEBOUNCE_TIME] = "debounce_time",
|
||||||
|
[IIO_CHAN_INFO_CALIBEMISSIVITY] = "calibemissivity",
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -169,7 +169,8 @@ config LTR501
|
|||||||
select IIO_TRIGGERED_BUFFER
|
select IIO_TRIGGERED_BUFFER
|
||||||
help
|
help
|
||||||
If you say yes here you get support for the Lite-On LTR-501ALS-01
|
If you say yes here you get support for the Lite-On LTR-501ALS-01
|
||||||
ambient light and proximity sensor.
|
ambient light and proximity sensor. This driver also supports LTR-559
|
||||||
|
ALS/PS or LTR-301 ALS sensors.
|
||||||
|
|
||||||
This driver can also be built as a module. If so, the module
|
This driver can also be built as a module. If so, the module
|
||||||
will be called ltr501.
|
will be called ltr501.
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -240,7 +240,7 @@ static int tsl2563_read_id(struct tsl2563_chip *chip, u8 *id)
|
|||||||
* convert between normalized values and HW values obtained using given
|
* convert between normalized values and HW values obtained using given
|
||||||
* timing and gain settings.
|
* timing and gain settings.
|
||||||
*/
|
*/
|
||||||
static int adc_shiftbits(u8 timing)
|
static int tsl2563_adc_shiftbits(u8 timing)
|
||||||
{
|
{
|
||||||
int shift = 0;
|
int shift = 0;
|
||||||
|
|
||||||
@ -263,9 +263,9 @@ static int adc_shiftbits(u8 timing)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Convert a HW ADC value to normalized scale. */
|
/* Convert a HW ADC value to normalized scale. */
|
||||||
static u32 normalize_adc(u16 adc, u8 timing)
|
static u32 tsl2563_normalize_adc(u16 adc, u8 timing)
|
||||||
{
|
{
|
||||||
return adc << adc_shiftbits(timing);
|
return adc << tsl2563_adc_shiftbits(timing);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tsl2563_wait_adc(struct tsl2563_chip *chip)
|
static void tsl2563_wait_adc(struct tsl2563_chip *chip)
|
||||||
@ -350,8 +350,8 @@ static int tsl2563_get_adc(struct tsl2563_chip *chip)
|
|||||||
retry = tsl2563_adjust_gainlevel(chip, adc0);
|
retry = tsl2563_adjust_gainlevel(chip, adc0);
|
||||||
}
|
}
|
||||||
|
|
||||||
chip->data0 = normalize_adc(adc0, chip->gainlevel->gaintime);
|
chip->data0 = tsl2563_normalize_adc(adc0, chip->gainlevel->gaintime);
|
||||||
chip->data1 = normalize_adc(adc1, chip->gainlevel->gaintime);
|
chip->data1 = tsl2563_normalize_adc(adc1, chip->gainlevel->gaintime);
|
||||||
|
|
||||||
if (!chip->int_enabled)
|
if (!chip->int_enabled)
|
||||||
schedule_delayed_work(&chip->poweroff_work, 5 * HZ);
|
schedule_delayed_work(&chip->poweroff_work, 5 * HZ);
|
||||||
@ -361,13 +361,13 @@ out:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int calib_to_sysfs(u32 calib)
|
static inline int tsl2563_calib_to_sysfs(u32 calib)
|
||||||
{
|
{
|
||||||
return (int) (((calib * CALIB_BASE_SYSFS) +
|
return (int) (((calib * CALIB_BASE_SYSFS) +
|
||||||
CALIB_FRAC_HALF) >> CALIB_FRAC_BITS);
|
CALIB_FRAC_HALF) >> CALIB_FRAC_BITS);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u32 calib_from_sysfs(int value)
|
static inline u32 tsl2563_calib_from_sysfs(int value)
|
||||||
{
|
{
|
||||||
return (((u32) value) << CALIB_FRAC_BITS) / CALIB_BASE_SYSFS;
|
return (((u32) value) << CALIB_FRAC_BITS) / CALIB_BASE_SYSFS;
|
||||||
}
|
}
|
||||||
@ -426,7 +426,7 @@ static const struct tsl2563_lux_coeff lux_table[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* Convert normalized, scaled ADC values to lux. */
|
/* Convert normalized, scaled ADC values to lux. */
|
||||||
static unsigned int adc_to_lux(u32 adc0, u32 adc1)
|
static unsigned int tsl2563_adc_to_lux(u32 adc0, u32 adc1)
|
||||||
{
|
{
|
||||||
const struct tsl2563_lux_coeff *lp = lux_table;
|
const struct tsl2563_lux_coeff *lp = lux_table;
|
||||||
unsigned long ratio, lux, ch0 = adc0, ch1 = adc1;
|
unsigned long ratio, lux, ch0 = adc0, ch1 = adc1;
|
||||||
@ -442,7 +442,7 @@ static unsigned int adc_to_lux(u32 adc0, u32 adc1)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Apply calibration coefficient to ADC count. */
|
/* Apply calibration coefficient to ADC count. */
|
||||||
static u32 calib_adc(u32 adc, u32 calib)
|
static u32 tsl2563_calib_adc(u32 adc, u32 calib)
|
||||||
{
|
{
|
||||||
unsigned long scaled = adc;
|
unsigned long scaled = adc;
|
||||||
|
|
||||||
@ -463,9 +463,9 @@ static int tsl2563_write_raw(struct iio_dev *indio_dev,
|
|||||||
if (mask != IIO_CHAN_INFO_CALIBSCALE)
|
if (mask != IIO_CHAN_INFO_CALIBSCALE)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (chan->channel2 == IIO_MOD_LIGHT_BOTH)
|
if (chan->channel2 == IIO_MOD_LIGHT_BOTH)
|
||||||
chip->calib0 = calib_from_sysfs(val);
|
chip->calib0 = tsl2563_calib_from_sysfs(val);
|
||||||
else if (chan->channel2 == IIO_MOD_LIGHT_IR)
|
else if (chan->channel2 == IIO_MOD_LIGHT_IR)
|
||||||
chip->calib1 = calib_from_sysfs(val);
|
chip->calib1 = tsl2563_calib_from_sysfs(val);
|
||||||
else
|
else
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
@ -491,11 +491,11 @@ static int tsl2563_read_raw(struct iio_dev *indio_dev,
|
|||||||
ret = tsl2563_get_adc(chip);
|
ret = tsl2563_get_adc(chip);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto error_ret;
|
goto error_ret;
|
||||||
calib0 = calib_adc(chip->data0, chip->calib0) *
|
calib0 = tsl2563_calib_adc(chip->data0, chip->calib0) *
|
||||||
chip->cover_comp_gain;
|
chip->cover_comp_gain;
|
||||||
calib1 = calib_adc(chip->data1, chip->calib1) *
|
calib1 = tsl2563_calib_adc(chip->data1, chip->calib1) *
|
||||||
chip->cover_comp_gain;
|
chip->cover_comp_gain;
|
||||||
*val = adc_to_lux(calib0, calib1);
|
*val = tsl2563_adc_to_lux(calib0, calib1);
|
||||||
ret = IIO_VAL_INT;
|
ret = IIO_VAL_INT;
|
||||||
break;
|
break;
|
||||||
case IIO_INTENSITY:
|
case IIO_INTENSITY:
|
||||||
@ -515,9 +515,9 @@ static int tsl2563_read_raw(struct iio_dev *indio_dev,
|
|||||||
|
|
||||||
case IIO_CHAN_INFO_CALIBSCALE:
|
case IIO_CHAN_INFO_CALIBSCALE:
|
||||||
if (chan->channel2 == IIO_MOD_LIGHT_BOTH)
|
if (chan->channel2 == IIO_MOD_LIGHT_BOTH)
|
||||||
*val = calib_to_sysfs(chip->calib0);
|
*val = tsl2563_calib_to_sysfs(chip->calib0);
|
||||||
else
|
else
|
||||||
*val = calib_to_sysfs(chip->calib1);
|
*val = tsl2563_calib_to_sysfs(chip->calib1);
|
||||||
ret = IIO_VAL_INT;
|
ret = IIO_VAL_INT;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -750,8 +750,8 @@ static int tsl2563_probe(struct i2c_client *client,
|
|||||||
chip->high_thres = 0xffff;
|
chip->high_thres = 0xffff;
|
||||||
chip->gainlevel = tsl2563_gainlevel_table;
|
chip->gainlevel = tsl2563_gainlevel_table;
|
||||||
chip->intr = TSL2563_INT_PERSIST(4);
|
chip->intr = TSL2563_INT_PERSIST(4);
|
||||||
chip->calib0 = calib_from_sysfs(CALIB_BASE_SYSFS);
|
chip->calib0 = tsl2563_calib_from_sysfs(CALIB_BASE_SYSFS);
|
||||||
chip->calib1 = calib_from_sysfs(CALIB_BASE_SYSFS);
|
chip->calib1 = tsl2563_calib_from_sysfs(CALIB_BASE_SYSFS);
|
||||||
|
|
||||||
if (pdata)
|
if (pdata)
|
||||||
chip->cover_comp_gain = pdata->cover_comp_gain;
|
chip->cover_comp_gain = pdata->cover_comp_gain;
|
||||||
|
@ -24,12 +24,12 @@
|
|||||||
|
|
||||||
#define TSL4531_DRV_NAME "tsl4531"
|
#define TSL4531_DRV_NAME "tsl4531"
|
||||||
|
|
||||||
#define TCS3472_COMMAND BIT(7)
|
#define TSL4531_COMMAND BIT(7)
|
||||||
|
|
||||||
#define TSL4531_CONTROL (TCS3472_COMMAND | 0x00)
|
#define TSL4531_CONTROL (TSL4531_COMMAND | 0x00)
|
||||||
#define TSL4531_CONFIG (TCS3472_COMMAND | 0x01)
|
#define TSL4531_CONFIG (TSL4531_COMMAND | 0x01)
|
||||||
#define TSL4531_DATA (TCS3472_COMMAND | 0x04)
|
#define TSL4531_DATA (TSL4531_COMMAND | 0x04)
|
||||||
#define TSL4531_ID (TCS3472_COMMAND | 0x0a)
|
#define TSL4531_ID (TSL4531_COMMAND | 0x0a)
|
||||||
|
|
||||||
/* operating modes in control register */
|
/* operating modes in control register */
|
||||||
#define TSL4531_MODE_POWERDOWN 0x00
|
#define TSL4531_MODE_POWERDOWN 0x00
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/iio/common/st_sensors.h>
|
#include <linux/iio/common/st_sensors.h>
|
||||||
|
|
||||||
|
#define LSM303DLH_MAGN_DEV_NAME "lsm303dlh_magn"
|
||||||
#define LSM303DLHC_MAGN_DEV_NAME "lsm303dlhc_magn"
|
#define LSM303DLHC_MAGN_DEV_NAME "lsm303dlhc_magn"
|
||||||
#define LSM303DLM_MAGN_DEV_NAME "lsm303dlm_magn"
|
#define LSM303DLM_MAGN_DEV_NAME "lsm303dlm_magn"
|
||||||
#define LIS3MDL_MAGN_DEV_NAME "lis3mdl"
|
#define LIS3MDL_MAGN_DEV_NAME "lis3mdl"
|
||||||
|
@ -45,6 +45,46 @@
|
|||||||
#define ST_MAGN_FS_AVL_12000MG 12000
|
#define ST_MAGN_FS_AVL_12000MG 12000
|
||||||
#define ST_MAGN_FS_AVL_16000MG 16000
|
#define ST_MAGN_FS_AVL_16000MG 16000
|
||||||
|
|
||||||
|
/* CUSTOM VALUES FOR SENSOR 0 */
|
||||||
|
#define ST_MAGN_0_ODR_ADDR 0x00
|
||||||
|
#define ST_MAGN_0_ODR_MASK 0x1c
|
||||||
|
#define ST_MAGN_0_ODR_AVL_1HZ_VAL 0x00
|
||||||
|
#define ST_MAGN_0_ODR_AVL_2HZ_VAL 0x01
|
||||||
|
#define ST_MAGN_0_ODR_AVL_3HZ_VAL 0x02
|
||||||
|
#define ST_MAGN_0_ODR_AVL_8HZ_VAL 0x03
|
||||||
|
#define ST_MAGN_0_ODR_AVL_15HZ_VAL 0x04
|
||||||
|
#define ST_MAGN_0_ODR_AVL_30HZ_VAL 0x05
|
||||||
|
#define ST_MAGN_0_ODR_AVL_75HZ_VAL 0x06
|
||||||
|
#define ST_MAGN_0_ODR_AVL_220HZ_VAL 0x07
|
||||||
|
#define ST_MAGN_0_PW_ADDR 0x02
|
||||||
|
#define ST_MAGN_0_PW_MASK 0x03
|
||||||
|
#define ST_MAGN_0_PW_ON 0x00
|
||||||
|
#define ST_MAGN_0_PW_OFF 0x03
|
||||||
|
#define ST_MAGN_0_FS_ADDR 0x01
|
||||||
|
#define ST_MAGN_0_FS_MASK 0xe0
|
||||||
|
#define ST_MAGN_0_FS_AVL_1300_VAL 0x01
|
||||||
|
#define ST_MAGN_0_FS_AVL_1900_VAL 0x02
|
||||||
|
#define ST_MAGN_0_FS_AVL_2500_VAL 0x03
|
||||||
|
#define ST_MAGN_0_FS_AVL_4000_VAL 0x04
|
||||||
|
#define ST_MAGN_0_FS_AVL_4700_VAL 0x05
|
||||||
|
#define ST_MAGN_0_FS_AVL_5600_VAL 0x06
|
||||||
|
#define ST_MAGN_0_FS_AVL_8100_VAL 0x07
|
||||||
|
#define ST_MAGN_0_FS_AVL_1300_GAIN_XY 1100
|
||||||
|
#define ST_MAGN_0_FS_AVL_1900_GAIN_XY 855
|
||||||
|
#define ST_MAGN_0_FS_AVL_2500_GAIN_XY 670
|
||||||
|
#define ST_MAGN_0_FS_AVL_4000_GAIN_XY 450
|
||||||
|
#define ST_MAGN_0_FS_AVL_4700_GAIN_XY 400
|
||||||
|
#define ST_MAGN_0_FS_AVL_5600_GAIN_XY 330
|
||||||
|
#define ST_MAGN_0_FS_AVL_8100_GAIN_XY 230
|
||||||
|
#define ST_MAGN_0_FS_AVL_1300_GAIN_Z 980
|
||||||
|
#define ST_MAGN_0_FS_AVL_1900_GAIN_Z 760
|
||||||
|
#define ST_MAGN_0_FS_AVL_2500_GAIN_Z 600
|
||||||
|
#define ST_MAGN_0_FS_AVL_4000_GAIN_Z 400
|
||||||
|
#define ST_MAGN_0_FS_AVL_4700_GAIN_Z 355
|
||||||
|
#define ST_MAGN_0_FS_AVL_5600_GAIN_Z 295
|
||||||
|
#define ST_MAGN_0_FS_AVL_8100_GAIN_Z 205
|
||||||
|
#define ST_MAGN_0_MULTIREAD_BIT false
|
||||||
|
|
||||||
/* CUSTOM VALUES FOR SENSOR 1 */
|
/* CUSTOM VALUES FOR SENSOR 1 */
|
||||||
#define ST_MAGN_1_WAI_EXP 0x3c
|
#define ST_MAGN_1_WAI_EXP 0x3c
|
||||||
#define ST_MAGN_1_ODR_ADDR 0x00
|
#define ST_MAGN_1_ODR_ADDR 0x00
|
||||||
@ -150,6 +190,82 @@ static const struct iio_chan_spec st_magn_2_16bit_channels[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const struct st_sensor_settings st_magn_sensors_settings[] = {
|
static const struct st_sensor_settings st_magn_sensors_settings[] = {
|
||||||
|
{
|
||||||
|
.wai = 0, /* This sensor has no valid WhoAmI report 0 */
|
||||||
|
.sensors_supported = {
|
||||||
|
[0] = LSM303DLH_MAGN_DEV_NAME,
|
||||||
|
},
|
||||||
|
.ch = (struct iio_chan_spec *)st_magn_16bit_channels,
|
||||||
|
.odr = {
|
||||||
|
.addr = ST_MAGN_0_ODR_ADDR,
|
||||||
|
.mask = ST_MAGN_0_ODR_MASK,
|
||||||
|
.odr_avl = {
|
||||||
|
{ 1, ST_MAGN_0_ODR_AVL_1HZ_VAL, },
|
||||||
|
{ 2, ST_MAGN_0_ODR_AVL_2HZ_VAL, },
|
||||||
|
{ 3, ST_MAGN_0_ODR_AVL_3HZ_VAL, },
|
||||||
|
{ 8, ST_MAGN_0_ODR_AVL_8HZ_VAL, },
|
||||||
|
{ 15, ST_MAGN_0_ODR_AVL_15HZ_VAL, },
|
||||||
|
{ 30, ST_MAGN_0_ODR_AVL_30HZ_VAL, },
|
||||||
|
{ 75, ST_MAGN_0_ODR_AVL_75HZ_VAL, },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.pw = {
|
||||||
|
.addr = ST_MAGN_0_PW_ADDR,
|
||||||
|
.mask = ST_MAGN_0_PW_MASK,
|
||||||
|
.value_on = ST_MAGN_0_PW_ON,
|
||||||
|
.value_off = ST_MAGN_0_PW_OFF,
|
||||||
|
},
|
||||||
|
.fs = {
|
||||||
|
.addr = ST_MAGN_0_FS_ADDR,
|
||||||
|
.mask = ST_MAGN_0_FS_MASK,
|
||||||
|
.fs_avl = {
|
||||||
|
[0] = {
|
||||||
|
.num = ST_MAGN_FS_AVL_1300MG,
|
||||||
|
.value = ST_MAGN_0_FS_AVL_1300_VAL,
|
||||||
|
.gain = ST_MAGN_0_FS_AVL_1300_GAIN_XY,
|
||||||
|
.gain2 = ST_MAGN_0_FS_AVL_1300_GAIN_Z,
|
||||||
|
},
|
||||||
|
[1] = {
|
||||||
|
.num = ST_MAGN_FS_AVL_1900MG,
|
||||||
|
.value = ST_MAGN_0_FS_AVL_1900_VAL,
|
||||||
|
.gain = ST_MAGN_0_FS_AVL_1900_GAIN_XY,
|
||||||
|
.gain2 = ST_MAGN_0_FS_AVL_1900_GAIN_Z,
|
||||||
|
},
|
||||||
|
[2] = {
|
||||||
|
.num = ST_MAGN_FS_AVL_2500MG,
|
||||||
|
.value = ST_MAGN_0_FS_AVL_2500_VAL,
|
||||||
|
.gain = ST_MAGN_0_FS_AVL_2500_GAIN_XY,
|
||||||
|
.gain2 = ST_MAGN_0_FS_AVL_2500_GAIN_Z,
|
||||||
|
},
|
||||||
|
[3] = {
|
||||||
|
.num = ST_MAGN_FS_AVL_4000MG,
|
||||||
|
.value = ST_MAGN_0_FS_AVL_4000_VAL,
|
||||||
|
.gain = ST_MAGN_0_FS_AVL_4000_GAIN_XY,
|
||||||
|
.gain2 = ST_MAGN_0_FS_AVL_4000_GAIN_Z,
|
||||||
|
},
|
||||||
|
[4] = {
|
||||||
|
.num = ST_MAGN_FS_AVL_4700MG,
|
||||||
|
.value = ST_MAGN_0_FS_AVL_4700_VAL,
|
||||||
|
.gain = ST_MAGN_0_FS_AVL_4700_GAIN_XY,
|
||||||
|
.gain2 = ST_MAGN_0_FS_AVL_4700_GAIN_Z,
|
||||||
|
},
|
||||||
|
[5] = {
|
||||||
|
.num = ST_MAGN_FS_AVL_5600MG,
|
||||||
|
.value = ST_MAGN_0_FS_AVL_5600_VAL,
|
||||||
|
.gain = ST_MAGN_0_FS_AVL_5600_GAIN_XY,
|
||||||
|
.gain2 = ST_MAGN_0_FS_AVL_5600_GAIN_Z,
|
||||||
|
},
|
||||||
|
[6] = {
|
||||||
|
.num = ST_MAGN_FS_AVL_8100MG,
|
||||||
|
.value = ST_MAGN_0_FS_AVL_8100_VAL,
|
||||||
|
.gain = ST_MAGN_0_FS_AVL_8100_GAIN_XY,
|
||||||
|
.gain2 = ST_MAGN_0_FS_AVL_8100_GAIN_Z,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.multi_read_bit = ST_MAGN_0_MULTIREAD_BIT,
|
||||||
|
.bootime = 2,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.wai = ST_MAGN_1_WAI_EXP,
|
.wai = ST_MAGN_1_WAI_EXP,
|
||||||
.sensors_supported = {
|
.sensors_supported = {
|
||||||
|
@ -20,6 +20,10 @@
|
|||||||
|
|
||||||
#ifdef CONFIG_OF
|
#ifdef CONFIG_OF
|
||||||
static const struct of_device_id st_magn_of_match[] = {
|
static const struct of_device_id st_magn_of_match[] = {
|
||||||
|
{
|
||||||
|
.compatible = "st,lsm303dlh-magn",
|
||||||
|
.data = LSM303DLH_MAGN_DEV_NAME,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.compatible = "st,lsm303dlhc-magn",
|
.compatible = "st,lsm303dlhc-magn",
|
||||||
.data = LSM303DLHC_MAGN_DEV_NAME,
|
.data = LSM303DLHC_MAGN_DEV_NAME,
|
||||||
@ -71,6 +75,7 @@ static int st_magn_i2c_remove(struct i2c_client *client)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const struct i2c_device_id st_magn_id_table[] = {
|
static const struct i2c_device_id st_magn_id_table[] = {
|
||||||
|
{ LSM303DLH_MAGN_DEV_NAME },
|
||||||
{ LSM303DLHC_MAGN_DEV_NAME },
|
{ LSM303DLHC_MAGN_DEV_NAME },
|
||||||
{ LSM303DLM_MAGN_DEV_NAME },
|
{ LSM303DLM_MAGN_DEV_NAME },
|
||||||
{ LIS3MDL_MAGN_DEV_NAME },
|
{ LIS3MDL_MAGN_DEV_NAME },
|
||||||
|
@ -18,6 +18,8 @@
|
|||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
#include <linux/gpio/consumer.h>
|
#include <linux/gpio/consumer.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/pm.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
|
||||||
#include <linux/iio/iio.h>
|
#include <linux/iio/iio.h>
|
||||||
#include <linux/iio/buffer.h>
|
#include <linux/iio/buffer.h>
|
||||||
@ -29,7 +31,9 @@
|
|||||||
|
|
||||||
#define SX9500_DRIVER_NAME "sx9500"
|
#define SX9500_DRIVER_NAME "sx9500"
|
||||||
#define SX9500_IRQ_NAME "sx9500_event"
|
#define SX9500_IRQ_NAME "sx9500_event"
|
||||||
#define SX9500_GPIO_NAME "sx9500_gpio"
|
|
||||||
|
#define SX9500_GPIO_INT "interrupt"
|
||||||
|
#define SX9500_GPIO_RESET "reset"
|
||||||
|
|
||||||
/* Register definitions. */
|
/* Register definitions. */
|
||||||
#define SX9500_REG_IRQ_SRC 0x00
|
#define SX9500_REG_IRQ_SRC 0x00
|
||||||
@ -73,6 +77,7 @@
|
|||||||
#define SX9500_CONVDONE_IRQ BIT(3)
|
#define SX9500_CONVDONE_IRQ BIT(3)
|
||||||
|
|
||||||
#define SX9500_PROXSTAT_SHIFT 4
|
#define SX9500_PROXSTAT_SHIFT 4
|
||||||
|
#define SX9500_COMPSTAT_MASK GENMASK(3, 0)
|
||||||
|
|
||||||
#define SX9500_NUM_CHANNELS 4
|
#define SX9500_NUM_CHANNELS 4
|
||||||
|
|
||||||
@ -81,6 +86,7 @@ struct sx9500_data {
|
|||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
struct iio_trigger *trig;
|
struct iio_trigger *trig;
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
|
struct gpio_desc *gpiod_rst;
|
||||||
/*
|
/*
|
||||||
* Last reading of the proximity status for each channel. We
|
* Last reading of the proximity status for each channel. We
|
||||||
* only send an event to user space when this changes.
|
* only send an event to user space when this changes.
|
||||||
@ -89,6 +95,11 @@ struct sx9500_data {
|
|||||||
bool event_enabled[SX9500_NUM_CHANNELS];
|
bool event_enabled[SX9500_NUM_CHANNELS];
|
||||||
bool trigger_enabled;
|
bool trigger_enabled;
|
||||||
u16 *buffer;
|
u16 *buffer;
|
||||||
|
/* Remember enabled channels and sample rate during suspend. */
|
||||||
|
unsigned int suspend_ctrl0;
|
||||||
|
struct completion completion;
|
||||||
|
int data_rdy_users, close_far_users;
|
||||||
|
int channel_users[SX9500_NUM_CHANNELS];
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct iio_event_spec sx9500_events[] = {
|
static const struct iio_event_spec sx9500_events[] = {
|
||||||
@ -139,6 +150,10 @@ static const struct {
|
|||||||
{2, 500000},
|
{2, 500000},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const unsigned int sx9500_scan_period_table[] = {
|
||||||
|
30, 60, 90, 120, 150, 200, 300, 400,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct regmap_range sx9500_writable_reg_ranges[] = {
|
static const struct regmap_range sx9500_writable_reg_ranges[] = {
|
||||||
regmap_reg_range(SX9500_REG_IRQ_MSK, SX9500_REG_IRQ_MSK),
|
regmap_reg_range(SX9500_REG_IRQ_MSK, SX9500_REG_IRQ_MSK),
|
||||||
regmap_reg_range(SX9500_REG_PROX_CTRL0, SX9500_REG_PROX_CTRL8),
|
regmap_reg_range(SX9500_REG_PROX_CTRL0, SX9500_REG_PROX_CTRL8),
|
||||||
@ -191,7 +206,67 @@ static const struct regmap_config sx9500_regmap_config = {
|
|||||||
.volatile_table = &sx9500_volatile_regs,
|
.volatile_table = &sx9500_volatile_regs,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int sx9500_read_proximity(struct sx9500_data *data,
|
static int sx9500_inc_users(struct sx9500_data *data, int *counter,
|
||||||
|
unsigned int reg, unsigned int bitmask)
|
||||||
|
{
|
||||||
|
(*counter)++;
|
||||||
|
if (*counter != 1)
|
||||||
|
/* Bit is already active, nothing to do. */
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return regmap_update_bits(data->regmap, reg, bitmask, bitmask);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sx9500_dec_users(struct sx9500_data *data, int *counter,
|
||||||
|
unsigned int reg, unsigned int bitmask)
|
||||||
|
{
|
||||||
|
(*counter)--;
|
||||||
|
if (*counter != 0)
|
||||||
|
/* There are more users, do not deactivate. */
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return regmap_update_bits(data->regmap, reg, bitmask, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sx9500_inc_chan_users(struct sx9500_data *data, int chan)
|
||||||
|
{
|
||||||
|
return sx9500_inc_users(data, &data->channel_users[chan],
|
||||||
|
SX9500_REG_PROX_CTRL0, BIT(chan));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sx9500_dec_chan_users(struct sx9500_data *data, int chan)
|
||||||
|
{
|
||||||
|
return sx9500_dec_users(data, &data->channel_users[chan],
|
||||||
|
SX9500_REG_PROX_CTRL0, BIT(chan));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sx9500_inc_data_rdy_users(struct sx9500_data *data)
|
||||||
|
{
|
||||||
|
return sx9500_inc_users(data, &data->data_rdy_users,
|
||||||
|
SX9500_REG_IRQ_MSK, SX9500_CONVDONE_IRQ);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sx9500_dec_data_rdy_users(struct sx9500_data *data)
|
||||||
|
{
|
||||||
|
return sx9500_dec_users(data, &data->data_rdy_users,
|
||||||
|
SX9500_REG_IRQ_MSK, SX9500_CONVDONE_IRQ);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sx9500_inc_close_far_users(struct sx9500_data *data)
|
||||||
|
{
|
||||||
|
return sx9500_inc_users(data, &data->close_far_users,
|
||||||
|
SX9500_REG_IRQ_MSK,
|
||||||
|
SX9500_CLOSE_IRQ | SX9500_FAR_IRQ);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sx9500_dec_close_far_users(struct sx9500_data *data)
|
||||||
|
{
|
||||||
|
return sx9500_dec_users(data, &data->close_far_users,
|
||||||
|
SX9500_REG_IRQ_MSK,
|
||||||
|
SX9500_CLOSE_IRQ | SX9500_FAR_IRQ);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sx9500_read_prox_data(struct sx9500_data *data,
|
||||||
const struct iio_chan_spec *chan,
|
const struct iio_chan_spec *chan,
|
||||||
int *val)
|
int *val)
|
||||||
{
|
{
|
||||||
@ -211,6 +286,79 @@ static int sx9500_read_proximity(struct sx9500_data *data,
|
|||||||
return IIO_VAL_INT;
|
return IIO_VAL_INT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we have no interrupt support, we have to wait for a scan period
|
||||||
|
* after enabling a channel to get a result.
|
||||||
|
*/
|
||||||
|
static int sx9500_wait_for_sample(struct sx9500_data *data)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
unsigned int val;
|
||||||
|
|
||||||
|
ret = regmap_read(data->regmap, SX9500_REG_PROX_CTRL0, &val);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
val = (val & SX9500_SCAN_PERIOD_MASK) >> SX9500_SCAN_PERIOD_SHIFT;
|
||||||
|
|
||||||
|
msleep(sx9500_scan_period_table[val]);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sx9500_read_proximity(struct sx9500_data *data,
|
||||||
|
const struct iio_chan_spec *chan,
|
||||||
|
int *val)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mutex_lock(&data->mutex);
|
||||||
|
|
||||||
|
ret = sx9500_inc_chan_users(data, chan->channel);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ret = sx9500_inc_data_rdy_users(data);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out_dec_chan;
|
||||||
|
|
||||||
|
mutex_unlock(&data->mutex);
|
||||||
|
|
||||||
|
if (data->client->irq > 0)
|
||||||
|
ret = wait_for_completion_interruptible(&data->completion);
|
||||||
|
else
|
||||||
|
ret = sx9500_wait_for_sample(data);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
mutex_lock(&data->mutex);
|
||||||
|
|
||||||
|
ret = sx9500_read_prox_data(data, chan, val);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ret = sx9500_dec_chan_users(data, chan->channel);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ret = sx9500_dec_data_rdy_users(data);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ret = IIO_VAL_INT;
|
||||||
|
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
out_dec_chan:
|
||||||
|
sx9500_dec_chan_users(data, chan->channel);
|
||||||
|
out:
|
||||||
|
mutex_unlock(&data->mutex);
|
||||||
|
reinit_completion(&data->completion);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int sx9500_read_samp_freq(struct sx9500_data *data,
|
static int sx9500_read_samp_freq(struct sx9500_data *data,
|
||||||
int *val, int *val2)
|
int *val, int *val2)
|
||||||
{
|
{
|
||||||
@ -236,7 +384,6 @@ static int sx9500_read_raw(struct iio_dev *indio_dev,
|
|||||||
int *val, int *val2, long mask)
|
int *val, int *val2, long mask)
|
||||||
{
|
{
|
||||||
struct sx9500_data *data = iio_priv(indio_dev);
|
struct sx9500_data *data = iio_priv(indio_dev);
|
||||||
int ret;
|
|
||||||
|
|
||||||
switch (chan->type) {
|
switch (chan->type) {
|
||||||
case IIO_PROXIMITY:
|
case IIO_PROXIMITY:
|
||||||
@ -244,10 +391,7 @@ static int sx9500_read_raw(struct iio_dev *indio_dev,
|
|||||||
case IIO_CHAN_INFO_RAW:
|
case IIO_CHAN_INFO_RAW:
|
||||||
if (iio_buffer_enabled(indio_dev))
|
if (iio_buffer_enabled(indio_dev))
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
mutex_lock(&data->mutex);
|
return sx9500_read_proximity(data, chan, val);
|
||||||
ret = sx9500_read_proximity(data, chan, val);
|
|
||||||
mutex_unlock(&data->mutex);
|
|
||||||
return ret;
|
|
||||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||||
return sx9500_read_samp_freq(data, val, val2);
|
return sx9500_read_samp_freq(data, val, val2);
|
||||||
default:
|
default:
|
||||||
@ -318,28 +462,16 @@ static irqreturn_t sx9500_irq_handler(int irq, void *private)
|
|||||||
return IRQ_WAKE_THREAD;
|
return IRQ_WAKE_THREAD;
|
||||||
}
|
}
|
||||||
|
|
||||||
static irqreturn_t sx9500_irq_thread_handler(int irq, void *private)
|
static void sx9500_push_events(struct iio_dev *indio_dev)
|
||||||
{
|
{
|
||||||
struct iio_dev *indio_dev = private;
|
|
||||||
struct sx9500_data *data = iio_priv(indio_dev);
|
|
||||||
int ret;
|
int ret;
|
||||||
unsigned int val, chan;
|
unsigned int val, chan;
|
||||||
|
struct sx9500_data *data = iio_priv(indio_dev);
|
||||||
mutex_lock(&data->mutex);
|
|
||||||
|
|
||||||
ret = regmap_read(data->regmap, SX9500_REG_IRQ_SRC, &val);
|
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(&data->client->dev, "i2c transfer error in irq\n");
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(val & (SX9500_CLOSE_IRQ | SX9500_FAR_IRQ)))
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
ret = regmap_read(data->regmap, SX9500_REG_STAT, &val);
|
ret = regmap_read(data->regmap, SX9500_REG_STAT, &val);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(&data->client->dev, "i2c transfer error in irq\n");
|
dev_err(&data->client->dev, "i2c transfer error in irq\n");
|
||||||
goto out;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
val >>= SX9500_PROXSTAT_SHIFT;
|
val >>= SX9500_PROXSTAT_SHIFT;
|
||||||
@ -354,15 +486,34 @@ static irqreturn_t sx9500_irq_thread_handler(int irq, void *private)
|
|||||||
/* No change on this channel. */
|
/* No change on this channel. */
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
dir = new_prox ? IIO_EV_DIR_FALLING :
|
dir = new_prox ? IIO_EV_DIR_FALLING : IIO_EV_DIR_RISING;
|
||||||
IIO_EV_DIR_RISING;
|
ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, chan,
|
||||||
ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY,
|
IIO_EV_TYPE_THRESH, dir);
|
||||||
chan,
|
|
||||||
IIO_EV_TYPE_THRESH,
|
|
||||||
dir);
|
|
||||||
iio_push_event(indio_dev, ev, iio_get_time_ns());
|
iio_push_event(indio_dev, ev, iio_get_time_ns());
|
||||||
data->prox_stat[chan] = new_prox;
|
data->prox_stat[chan] = new_prox;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t sx9500_irq_thread_handler(int irq, void *private)
|
||||||
|
{
|
||||||
|
struct iio_dev *indio_dev = private;
|
||||||
|
struct sx9500_data *data = iio_priv(indio_dev);
|
||||||
|
int ret;
|
||||||
|
unsigned int val;
|
||||||
|
|
||||||
|
mutex_lock(&data->mutex);
|
||||||
|
|
||||||
|
ret = regmap_read(data->regmap, SX9500_REG_IRQ_SRC, &val);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&data->client->dev, "i2c transfer error in irq\n");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (val & (SX9500_CLOSE_IRQ | SX9500_FAR_IRQ))
|
||||||
|
sx9500_push_events(indio_dev);
|
||||||
|
|
||||||
|
if (val & SX9500_CONVDONE_IRQ)
|
||||||
|
complete_all(&data->completion);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
mutex_unlock(&data->mutex);
|
mutex_unlock(&data->mutex);
|
||||||
@ -391,9 +542,7 @@ static int sx9500_write_event_config(struct iio_dev *indio_dev,
|
|||||||
int state)
|
int state)
|
||||||
{
|
{
|
||||||
struct sx9500_data *data = iio_priv(indio_dev);
|
struct sx9500_data *data = iio_priv(indio_dev);
|
||||||
int ret, i;
|
int ret;
|
||||||
bool any_active = false;
|
|
||||||
unsigned int irqmask;
|
|
||||||
|
|
||||||
if (chan->type != IIO_PROXIMITY || type != IIO_EV_TYPE_THRESH ||
|
if (chan->type != IIO_PROXIMITY || type != IIO_EV_TYPE_THRESH ||
|
||||||
dir != IIO_EV_DIR_EITHER)
|
dir != IIO_EV_DIR_EITHER)
|
||||||
@ -401,24 +550,32 @@ static int sx9500_write_event_config(struct iio_dev *indio_dev,
|
|||||||
|
|
||||||
mutex_lock(&data->mutex);
|
mutex_lock(&data->mutex);
|
||||||
|
|
||||||
data->event_enabled[chan->channel] = state;
|
if (state == 1) {
|
||||||
|
ret = sx9500_inc_chan_users(data, chan->channel);
|
||||||
for (i = 0; i < SX9500_NUM_CHANNELS; i++)
|
if (ret < 0)
|
||||||
if (data->event_enabled[i]) {
|
goto out_unlock;
|
||||||
any_active = true;
|
ret = sx9500_inc_close_far_users(data);
|
||||||
break;
|
if (ret < 0)
|
||||||
|
goto out_undo_chan;
|
||||||
|
} else {
|
||||||
|
ret = sx9500_dec_chan_users(data, chan->channel);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out_unlock;
|
||||||
|
ret = sx9500_dec_close_far_users(data);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out_undo_chan;
|
||||||
}
|
}
|
||||||
|
|
||||||
irqmask = SX9500_CLOSE_IRQ | SX9500_FAR_IRQ;
|
data->event_enabled[chan->channel] = state;
|
||||||
if (any_active)
|
goto out_unlock;
|
||||||
ret = regmap_update_bits(data->regmap, SX9500_REG_IRQ_MSK,
|
|
||||||
irqmask, irqmask);
|
out_undo_chan:
|
||||||
|
if (state == 1)
|
||||||
|
sx9500_dec_chan_users(data, chan->channel);
|
||||||
else
|
else
|
||||||
ret = regmap_update_bits(data->regmap, SX9500_REG_IRQ_MSK,
|
sx9500_inc_chan_users(data, chan->channel);
|
||||||
irqmask, 0);
|
out_unlock:
|
||||||
|
|
||||||
mutex_unlock(&data->mutex);
|
mutex_unlock(&data->mutex);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -469,12 +626,16 @@ static int sx9500_set_trigger_state(struct iio_trigger *trig,
|
|||||||
|
|
||||||
mutex_lock(&data->mutex);
|
mutex_lock(&data->mutex);
|
||||||
|
|
||||||
ret = regmap_update_bits(data->regmap, SX9500_REG_IRQ_MSK,
|
if (state)
|
||||||
SX9500_CONVDONE_IRQ,
|
ret = sx9500_inc_data_rdy_users(data);
|
||||||
state ? SX9500_CONVDONE_IRQ : 0);
|
else
|
||||||
if (ret == 0)
|
ret = sx9500_dec_data_rdy_users(data);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
data->trigger_enabled = state;
|
data->trigger_enabled = state;
|
||||||
|
|
||||||
|
out:
|
||||||
mutex_unlock(&data->mutex);
|
mutex_unlock(&data->mutex);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -496,7 +657,7 @@ static irqreturn_t sx9500_trigger_handler(int irq, void *private)
|
|||||||
|
|
||||||
for_each_set_bit(bit, indio_dev->active_scan_mask,
|
for_each_set_bit(bit, indio_dev->active_scan_mask,
|
||||||
indio_dev->masklength) {
|
indio_dev->masklength) {
|
||||||
ret = sx9500_read_proximity(data, &indio_dev->channels[bit],
|
ret = sx9500_read_prox_data(data, &indio_dev->channels[bit],
|
||||||
&val);
|
&val);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out;
|
goto out;
|
||||||
@ -515,6 +676,62 @@ out:
|
|||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int sx9500_buffer_preenable(struct iio_dev *indio_dev)
|
||||||
|
{
|
||||||
|
struct sx9500_data *data = iio_priv(indio_dev);
|
||||||
|
int ret, i;
|
||||||
|
|
||||||
|
mutex_lock(&data->mutex);
|
||||||
|
|
||||||
|
for (i = 0; i < SX9500_NUM_CHANNELS; i++)
|
||||||
|
if (test_bit(i, indio_dev->active_scan_mask)) {
|
||||||
|
ret = sx9500_inc_chan_users(data, i);
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
for (i = i - 1; i >= 0; i--)
|
||||||
|
if (test_bit(i, indio_dev->active_scan_mask))
|
||||||
|
sx9500_dec_chan_users(data, i);
|
||||||
|
|
||||||
|
mutex_unlock(&data->mutex);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sx9500_buffer_predisable(struct iio_dev *indio_dev)
|
||||||
|
{
|
||||||
|
struct sx9500_data *data = iio_priv(indio_dev);
|
||||||
|
int ret, i;
|
||||||
|
|
||||||
|
iio_triggered_buffer_predisable(indio_dev);
|
||||||
|
|
||||||
|
mutex_lock(&data->mutex);
|
||||||
|
|
||||||
|
for (i = 0; i < SX9500_NUM_CHANNELS; i++)
|
||||||
|
if (test_bit(i, indio_dev->active_scan_mask)) {
|
||||||
|
ret = sx9500_dec_chan_users(data, i);
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
for (i = i - 1; i >= 0; i--)
|
||||||
|
if (test_bit(i, indio_dev->active_scan_mask))
|
||||||
|
sx9500_inc_chan_users(data, i);
|
||||||
|
|
||||||
|
mutex_unlock(&data->mutex);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct iio_buffer_setup_ops sx9500_buffer_setup_ops = {
|
||||||
|
.preenable = sx9500_buffer_preenable,
|
||||||
|
.postenable = iio_triggered_buffer_postenable,
|
||||||
|
.predisable = sx9500_buffer_predisable,
|
||||||
|
};
|
||||||
|
|
||||||
struct sx9500_reg_default {
|
struct sx9500_reg_default {
|
||||||
u8 reg;
|
u8 reg;
|
||||||
u8 def;
|
u8 def;
|
||||||
@ -570,17 +787,57 @@ static const struct sx9500_reg_default sx9500_default_regs[] = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
.reg = SX9500_REG_PROX_CTRL0,
|
.reg = SX9500_REG_PROX_CTRL0,
|
||||||
/* Scan period: 30ms, all sensors enabled. */
|
/* Scan period: 30ms, all sensors disabled. */
|
||||||
.def = 0x0f,
|
.def = 0x00,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Activate all channels and perform an initial compensation. */
|
||||||
|
static int sx9500_init_compensation(struct iio_dev *indio_dev)
|
||||||
|
{
|
||||||
|
struct sx9500_data *data = iio_priv(indio_dev);
|
||||||
|
int i, ret;
|
||||||
|
unsigned int val;
|
||||||
|
|
||||||
|
ret = regmap_update_bits(data->regmap, SX9500_REG_PROX_CTRL0,
|
||||||
|
GENMASK(SX9500_NUM_CHANNELS, 0),
|
||||||
|
GENMASK(SX9500_NUM_CHANNELS, 0));
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
for (i = 10; i >= 0; i--) {
|
||||||
|
usleep_range(10000, 20000);
|
||||||
|
ret = regmap_read(data->regmap, SX9500_REG_STAT, &val);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
if (!(val & SX9500_COMPSTAT_MASK))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < 0) {
|
||||||
|
dev_err(&data->client->dev, "initial compensation timed out");
|
||||||
|
ret = -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
regmap_update_bits(data->regmap, SX9500_REG_PROX_CTRL0,
|
||||||
|
GENMASK(SX9500_NUM_CHANNELS, 0), 0);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int sx9500_init_device(struct iio_dev *indio_dev)
|
static int sx9500_init_device(struct iio_dev *indio_dev)
|
||||||
{
|
{
|
||||||
struct sx9500_data *data = iio_priv(indio_dev);
|
struct sx9500_data *data = iio_priv(indio_dev);
|
||||||
int ret, i;
|
int ret, i;
|
||||||
unsigned int val;
|
unsigned int val;
|
||||||
|
|
||||||
|
if (data->gpiod_rst) {
|
||||||
|
gpiod_set_value_cansleep(data->gpiod_rst, 0);
|
||||||
|
usleep_range(1000, 2000);
|
||||||
|
gpiod_set_value_cansleep(data->gpiod_rst, 1);
|
||||||
|
usleep_range(1000, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
ret = regmap_write(data->regmap, SX9500_REG_IRQ_MSK, 0);
|
ret = regmap_write(data->regmap, SX9500_REG_IRQ_MSK, 0);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
@ -602,33 +859,34 @@ static int sx9500_init_device(struct iio_dev *indio_dev)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return sx9500_init_compensation(indio_dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sx9500_gpio_probe(struct i2c_client *client,
|
static void sx9500_gpio_probe(struct i2c_client *client,
|
||||||
struct sx9500_data *data)
|
struct sx9500_data *data)
|
||||||
{
|
{
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct gpio_desc *gpio;
|
struct gpio_desc *gpio;
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!client)
|
if (!client)
|
||||||
return -EINVAL;
|
return;
|
||||||
|
|
||||||
dev = &client->dev;
|
dev = &client->dev;
|
||||||
|
|
||||||
/* data ready gpio interrupt pin */
|
if (client->irq <= 0) {
|
||||||
gpio = devm_gpiod_get_index(dev, SX9500_GPIO_NAME, 0, GPIOD_IN);
|
gpio = devm_gpiod_get_index(dev, SX9500_GPIO_INT, 0, GPIOD_IN);
|
||||||
if (IS_ERR(gpio)) {
|
if (IS_ERR(gpio))
|
||||||
dev_err(dev, "acpi gpio get index failed\n");
|
dev_err(dev, "gpio get irq failed\n");
|
||||||
return PTR_ERR(gpio);
|
else
|
||||||
|
client->irq = gpiod_to_irq(gpio);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = gpiod_to_irq(gpio);
|
data->gpiod_rst = devm_gpiod_get_index(dev, SX9500_GPIO_RESET,
|
||||||
|
0, GPIOD_OUT_HIGH);
|
||||||
dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
|
if (IS_ERR(data->gpiod_rst)) {
|
||||||
|
dev_warn(dev, "gpio get reset pin failed\n");
|
||||||
return ret;
|
data->gpiod_rst = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sx9500_probe(struct i2c_client *client,
|
static int sx9500_probe(struct i2c_client *client,
|
||||||
@ -645,14 +903,13 @@ static int sx9500_probe(struct i2c_client *client,
|
|||||||
data = iio_priv(indio_dev);
|
data = iio_priv(indio_dev);
|
||||||
data->client = client;
|
data->client = client;
|
||||||
mutex_init(&data->mutex);
|
mutex_init(&data->mutex);
|
||||||
|
init_completion(&data->completion);
|
||||||
data->trigger_enabled = false;
|
data->trigger_enabled = false;
|
||||||
|
|
||||||
data->regmap = devm_regmap_init_i2c(client, &sx9500_regmap_config);
|
data->regmap = devm_regmap_init_i2c(client, &sx9500_regmap_config);
|
||||||
if (IS_ERR(data->regmap))
|
if (IS_ERR(data->regmap))
|
||||||
return PTR_ERR(data->regmap);
|
return PTR_ERR(data->regmap);
|
||||||
|
|
||||||
sx9500_init_device(indio_dev);
|
|
||||||
|
|
||||||
indio_dev->dev.parent = &client->dev;
|
indio_dev->dev.parent = &client->dev;
|
||||||
indio_dev->name = SX9500_DRIVER_NAME;
|
indio_dev->name = SX9500_DRIVER_NAME;
|
||||||
indio_dev->channels = sx9500_channels;
|
indio_dev->channels = sx9500_channels;
|
||||||
@ -661,10 +918,15 @@ static int sx9500_probe(struct i2c_client *client,
|
|||||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||||
i2c_set_clientdata(client, indio_dev);
|
i2c_set_clientdata(client, indio_dev);
|
||||||
|
|
||||||
if (client->irq <= 0)
|
sx9500_gpio_probe(client, data);
|
||||||
client->irq = sx9500_gpio_probe(client, data);
|
|
||||||
|
|
||||||
if (client->irq > 0) {
|
ret = sx9500_init_device(indio_dev);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (client->irq <= 0)
|
||||||
|
dev_warn(&client->dev, "no valid irq found\n");
|
||||||
|
else {
|
||||||
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
||||||
sx9500_irq_handler, sx9500_irq_thread_handler,
|
sx9500_irq_handler, sx9500_irq_thread_handler,
|
||||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||||
@ -687,7 +949,8 @@ static int sx9500_probe(struct i2c_client *client,
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||||
sx9500_trigger_handler, NULL);
|
sx9500_trigger_handler,
|
||||||
|
&sx9500_buffer_setup_ops);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out_trigger_unregister;
|
goto out_trigger_unregister;
|
||||||
|
|
||||||
@ -720,6 +983,49 @@ static int sx9500_remove(struct i2c_client *client)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_SLEEP
|
||||||
|
static int sx9500_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||||
|
struct sx9500_data *data = iio_priv(indio_dev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mutex_lock(&data->mutex);
|
||||||
|
ret = regmap_read(data->regmap, SX9500_REG_PROX_CTRL0,
|
||||||
|
&data->suspend_ctrl0);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Scan period doesn't matter because when all the sensors are
|
||||||
|
* deactivated the device is in sleep mode.
|
||||||
|
*/
|
||||||
|
ret = regmap_write(data->regmap, SX9500_REG_PROX_CTRL0, 0);
|
||||||
|
|
||||||
|
out:
|
||||||
|
mutex_unlock(&data->mutex);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sx9500_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||||
|
struct sx9500_data *data = iio_priv(indio_dev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mutex_lock(&data->mutex);
|
||||||
|
ret = regmap_write(data->regmap, SX9500_REG_PROX_CTRL0,
|
||||||
|
data->suspend_ctrl0);
|
||||||
|
mutex_unlock(&data->mutex);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_PM_SLEEP */
|
||||||
|
|
||||||
|
static const struct dev_pm_ops sx9500_pm_ops = {
|
||||||
|
SET_SYSTEM_SLEEP_PM_OPS(sx9500_suspend, sx9500_resume)
|
||||||
|
};
|
||||||
|
|
||||||
static const struct acpi_device_id sx9500_acpi_match[] = {
|
static const struct acpi_device_id sx9500_acpi_match[] = {
|
||||||
{"SSX9500", 0},
|
{"SSX9500", 0},
|
||||||
{ },
|
{ },
|
||||||
@ -728,7 +1034,7 @@ MODULE_DEVICE_TABLE(acpi, sx9500_acpi_match);
|
|||||||
|
|
||||||
static const struct i2c_device_id sx9500_id[] = {
|
static const struct i2c_device_id sx9500_id[] = {
|
||||||
{"sx9500", 0},
|
{"sx9500", 0},
|
||||||
{}
|
{ },
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(i2c, sx9500_id);
|
MODULE_DEVICE_TABLE(i2c, sx9500_id);
|
||||||
|
|
||||||
@ -736,6 +1042,7 @@ static struct i2c_driver sx9500_driver = {
|
|||||||
.driver = {
|
.driver = {
|
||||||
.name = SX9500_DRIVER_NAME,
|
.name = SX9500_DRIVER_NAME,
|
||||||
.acpi_match_table = ACPI_PTR(sx9500_acpi_match),
|
.acpi_match_table = ACPI_PTR(sx9500_acpi_match),
|
||||||
|
.pm = &sx9500_pm_ops,
|
||||||
},
|
},
|
||||||
.probe = sx9500_probe,
|
.probe = sx9500_probe,
|
||||||
.remove = sx9500_remove,
|
.remove = sx9500_remove,
|
||||||
|
@ -12,12 +12,24 @@
|
|||||||
*
|
*
|
||||||
* (7-bit I2C slave address 0x5a, 100KHz bus speed only!)
|
* (7-bit I2C slave address 0x5a, 100KHz bus speed only!)
|
||||||
*
|
*
|
||||||
* TODO: sleep mode, configuration EEPROM
|
* To wake up from sleep mode, the SDA line must be held low while SCL is high
|
||||||
|
* for at least 33ms. This is achieved with an extra GPIO that can be connected
|
||||||
|
* directly to the SDA line. In normal operation, the GPIO is set as input and
|
||||||
|
* will not interfere in I2C communication. While the GPIO is driven low, the
|
||||||
|
* i2c adapter is locked since it cannot be used by other clients. The SCL line
|
||||||
|
* always has a pull-up so we do not need an extra GPIO to drive it high. If
|
||||||
|
* the "wakeup" GPIO is not given, power management will be disabled.
|
||||||
|
*
|
||||||
|
* TODO: filter configuration
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/jiffies.h>
|
||||||
|
#include <linux/gpio/consumer.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
|
||||||
#include <linux/iio/iio.h>
|
#include <linux/iio/iio.h>
|
||||||
|
|
||||||
@ -51,10 +63,101 @@
|
|||||||
#define MLX90614_TIMING_WAKEUP 34 /* time to hold SDA low for wake-up */
|
#define MLX90614_TIMING_WAKEUP 34 /* time to hold SDA low for wake-up */
|
||||||
#define MLX90614_TIMING_STARTUP 250 /* time before first data after wake-up */
|
#define MLX90614_TIMING_STARTUP 250 /* time before first data after wake-up */
|
||||||
|
|
||||||
|
#define MLX90614_AUTOSLEEP_DELAY 5000 /* default autosleep delay */
|
||||||
|
|
||||||
struct mlx90614_data {
|
struct mlx90614_data {
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
|
struct mutex lock; /* for EEPROM access only */
|
||||||
|
struct gpio_desc *wakeup_gpio; /* NULL to disable sleep/wake-up */
|
||||||
|
unsigned long ready_timestamp; /* in jiffies */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Erase an address and write word.
|
||||||
|
* The mutex must be locked before calling.
|
||||||
|
*/
|
||||||
|
static s32 mlx90614_write_word(const struct i2c_client *client, u8 command,
|
||||||
|
u16 value)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Note: The mlx90614 requires a PEC on writing but does not send us a
|
||||||
|
* valid PEC on reading. Hence, we cannot set I2C_CLIENT_PEC in
|
||||||
|
* i2c_client.flags. As a workaround, we use i2c_smbus_xfer here.
|
||||||
|
*/
|
||||||
|
union i2c_smbus_data data;
|
||||||
|
s32 ret;
|
||||||
|
|
||||||
|
dev_dbg(&client->dev, "Writing 0x%x to address 0x%x", value, command);
|
||||||
|
|
||||||
|
data.word = 0x0000; /* erase command */
|
||||||
|
ret = i2c_smbus_xfer(client->adapter, client->addr,
|
||||||
|
client->flags | I2C_CLIENT_PEC,
|
||||||
|
I2C_SMBUS_WRITE, command,
|
||||||
|
I2C_SMBUS_WORD_DATA, &data);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
msleep(MLX90614_TIMING_EEPROM);
|
||||||
|
|
||||||
|
data.word = value; /* actual write */
|
||||||
|
ret = i2c_smbus_xfer(client->adapter, client->addr,
|
||||||
|
client->flags | I2C_CLIENT_PEC,
|
||||||
|
I2C_SMBUS_WRITE, command,
|
||||||
|
I2C_SMBUS_WORD_DATA, &data);
|
||||||
|
|
||||||
|
msleep(MLX90614_TIMING_EEPROM);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
/*
|
||||||
|
* If @startup is true, make sure MLX90614_TIMING_STARTUP ms have elapsed since
|
||||||
|
* the last wake-up. This is normally only needed to get a valid temperature
|
||||||
|
* reading. EEPROM access does not need such delay.
|
||||||
|
* Return 0 on success, <0 on error.
|
||||||
|
*/
|
||||||
|
static int mlx90614_power_get(struct mlx90614_data *data, bool startup)
|
||||||
|
{
|
||||||
|
unsigned long now;
|
||||||
|
|
||||||
|
if (!data->wakeup_gpio)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
pm_runtime_get_sync(&data->client->dev);
|
||||||
|
|
||||||
|
if (startup) {
|
||||||
|
now = jiffies;
|
||||||
|
if (time_before(now, data->ready_timestamp) &&
|
||||||
|
msleep_interruptible(jiffies_to_msecs(
|
||||||
|
data->ready_timestamp - now)) != 0) {
|
||||||
|
pm_runtime_put_autosuspend(&data->client->dev);
|
||||||
|
return -EINTR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mlx90614_power_put(struct mlx90614_data *data)
|
||||||
|
{
|
||||||
|
if (!data->wakeup_gpio)
|
||||||
|
return;
|
||||||
|
|
||||||
|
pm_runtime_mark_last_busy(&data->client->dev);
|
||||||
|
pm_runtime_put_autosuspend(&data->client->dev);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static inline int mlx90614_power_get(struct mlx90614_data *data, bool startup)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void mlx90614_power_put(struct mlx90614_data *data)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static int mlx90614_read_raw(struct iio_dev *indio_dev,
|
static int mlx90614_read_raw(struct iio_dev *indio_dev,
|
||||||
struct iio_chan_spec const *channel, int *val,
|
struct iio_chan_spec const *channel, int *val,
|
||||||
int *val2, long mask)
|
int *val2, long mask)
|
||||||
@ -85,9 +188,19 @@ static int mlx90614_read_raw(struct iio_dev *indio_dev,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = i2c_smbus_read_word_data(data->client, cmd);
|
ret = mlx90614_power_get(data, true);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
ret = i2c_smbus_read_word_data(data->client, cmd);
|
||||||
|
mlx90614_power_put(data);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* MSB is an error flag */
|
||||||
|
if (ret & 0x8000)
|
||||||
|
return -EIO;
|
||||||
|
|
||||||
*val = ret;
|
*val = ret;
|
||||||
return IIO_VAL_INT;
|
return IIO_VAL_INT;
|
||||||
case IIO_CHAN_INFO_OFFSET:
|
case IIO_CHAN_INFO_OFFSET:
|
||||||
@ -97,6 +210,65 @@ static int mlx90614_read_raw(struct iio_dev *indio_dev,
|
|||||||
case IIO_CHAN_INFO_SCALE:
|
case IIO_CHAN_INFO_SCALE:
|
||||||
*val = 20;
|
*val = 20;
|
||||||
return IIO_VAL_INT;
|
return IIO_VAL_INT;
|
||||||
|
case IIO_CHAN_INFO_CALIBEMISSIVITY: /* 1/65535 / LSB */
|
||||||
|
mlx90614_power_get(data, false);
|
||||||
|
mutex_lock(&data->lock);
|
||||||
|
ret = i2c_smbus_read_word_data(data->client,
|
||||||
|
MLX90614_EMISSIVITY);
|
||||||
|
mutex_unlock(&data->lock);
|
||||||
|
mlx90614_power_put(data);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (ret == 65535) {
|
||||||
|
*val = 1;
|
||||||
|
*val2 = 0;
|
||||||
|
} else {
|
||||||
|
*val = 0;
|
||||||
|
*val2 = ret * 15259; /* 1/65535 ~ 0.000015259 */
|
||||||
|
}
|
||||||
|
return IIO_VAL_INT_PLUS_NANO;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mlx90614_write_raw(struct iio_dev *indio_dev,
|
||||||
|
struct iio_chan_spec const *channel, int val,
|
||||||
|
int val2, long mask)
|
||||||
|
{
|
||||||
|
struct mlx90614_data *data = iio_priv(indio_dev);
|
||||||
|
s32 ret;
|
||||||
|
|
||||||
|
switch (mask) {
|
||||||
|
case IIO_CHAN_INFO_CALIBEMISSIVITY: /* 1/65535 / LSB */
|
||||||
|
if (val < 0 || val2 < 0 || val > 1 || (val == 1 && val2 != 0))
|
||||||
|
return -EINVAL;
|
||||||
|
val = val * 65535 + val2 / 15259; /* 1/65535 ~ 0.000015259 */
|
||||||
|
|
||||||
|
mlx90614_power_get(data, false);
|
||||||
|
mutex_lock(&data->lock);
|
||||||
|
ret = mlx90614_write_word(data->client, MLX90614_EMISSIVITY,
|
||||||
|
val);
|
||||||
|
mutex_unlock(&data->lock);
|
||||||
|
mlx90614_power_put(data);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mlx90614_write_raw_get_fmt(struct iio_dev *indio_dev,
|
||||||
|
struct iio_chan_spec const *channel,
|
||||||
|
long mask)
|
||||||
|
{
|
||||||
|
switch (mask) {
|
||||||
|
case IIO_CHAN_INFO_CALIBEMISSIVITY:
|
||||||
|
return IIO_VAL_INT_PLUS_NANO;
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
@ -115,7 +287,8 @@ static const struct iio_chan_spec mlx90614_channels[] = {
|
|||||||
.type = IIO_TEMP,
|
.type = IIO_TEMP,
|
||||||
.modified = 1,
|
.modified = 1,
|
||||||
.channel2 = IIO_MOD_TEMP_OBJECT,
|
.channel2 = IIO_MOD_TEMP_OBJECT,
|
||||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||||
|
BIT(IIO_CHAN_INFO_CALIBEMISSIVITY),
|
||||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||||
BIT(IIO_CHAN_INFO_SCALE),
|
BIT(IIO_CHAN_INFO_SCALE),
|
||||||
},
|
},
|
||||||
@ -125,7 +298,8 @@ static const struct iio_chan_spec mlx90614_channels[] = {
|
|||||||
.modified = 1,
|
.modified = 1,
|
||||||
.channel = 1,
|
.channel = 1,
|
||||||
.channel2 = IIO_MOD_TEMP_OBJECT,
|
.channel2 = IIO_MOD_TEMP_OBJECT,
|
||||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||||
|
BIT(IIO_CHAN_INFO_CALIBEMISSIVITY),
|
||||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||||
BIT(IIO_CHAN_INFO_SCALE),
|
BIT(IIO_CHAN_INFO_SCALE),
|
||||||
},
|
},
|
||||||
@ -133,9 +307,103 @@ static const struct iio_chan_spec mlx90614_channels[] = {
|
|||||||
|
|
||||||
static const struct iio_info mlx90614_info = {
|
static const struct iio_info mlx90614_info = {
|
||||||
.read_raw = mlx90614_read_raw,
|
.read_raw = mlx90614_read_raw,
|
||||||
|
.write_raw = mlx90614_write_raw,
|
||||||
|
.write_raw_get_fmt = mlx90614_write_raw_get_fmt,
|
||||||
.driver_module = THIS_MODULE,
|
.driver_module = THIS_MODULE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
static int mlx90614_sleep(struct mlx90614_data *data)
|
||||||
|
{
|
||||||
|
s32 ret;
|
||||||
|
|
||||||
|
if (!data->wakeup_gpio) {
|
||||||
|
dev_dbg(&data->client->dev, "Sleep disabled");
|
||||||
|
return -ENOSYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_dbg(&data->client->dev, "Requesting sleep");
|
||||||
|
|
||||||
|
mutex_lock(&data->lock);
|
||||||
|
ret = i2c_smbus_xfer(data->client->adapter, data->client->addr,
|
||||||
|
data->client->flags | I2C_CLIENT_PEC,
|
||||||
|
I2C_SMBUS_WRITE, MLX90614_OP_SLEEP,
|
||||||
|
I2C_SMBUS_BYTE, NULL);
|
||||||
|
mutex_unlock(&data->lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mlx90614_wakeup(struct mlx90614_data *data)
|
||||||
|
{
|
||||||
|
if (!data->wakeup_gpio) {
|
||||||
|
dev_dbg(&data->client->dev, "Wake-up disabled");
|
||||||
|
return -ENOSYS;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_dbg(&data->client->dev, "Requesting wake-up");
|
||||||
|
|
||||||
|
i2c_lock_adapter(data->client->adapter);
|
||||||
|
gpiod_direction_output(data->wakeup_gpio, 0);
|
||||||
|
msleep(MLX90614_TIMING_WAKEUP);
|
||||||
|
gpiod_direction_input(data->wakeup_gpio);
|
||||||
|
i2c_unlock_adapter(data->client->adapter);
|
||||||
|
|
||||||
|
data->ready_timestamp = jiffies +
|
||||||
|
msecs_to_jiffies(MLX90614_TIMING_STARTUP);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Quirk: the i2c controller may get confused right after the
|
||||||
|
* wake-up signal has been sent. As a workaround, do a dummy read.
|
||||||
|
* If the read fails, the controller will probably be reset so that
|
||||||
|
* further reads will work.
|
||||||
|
*/
|
||||||
|
i2c_smbus_read_word_data(data->client, MLX90614_CONFIG);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return wake-up GPIO or NULL if sleep functionality should be disabled. */
|
||||||
|
static struct gpio_desc *mlx90614_probe_wakeup(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
struct gpio_desc *gpio;
|
||||||
|
|
||||||
|
if (!i2c_check_functionality(client->adapter,
|
||||||
|
I2C_FUNC_SMBUS_WRITE_BYTE)) {
|
||||||
|
dev_info(&client->dev,
|
||||||
|
"i2c adapter does not support SMBUS_WRITE_BYTE, sleep disabled");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpio = devm_gpiod_get_optional(&client->dev, "wakeup", GPIOD_IN);
|
||||||
|
|
||||||
|
if (IS_ERR(gpio)) {
|
||||||
|
dev_warn(&client->dev,
|
||||||
|
"gpio acquisition failed with error %ld, sleep disabled",
|
||||||
|
PTR_ERR(gpio));
|
||||||
|
return NULL;
|
||||||
|
} else if (!gpio) {
|
||||||
|
dev_info(&client->dev,
|
||||||
|
"wakeup-gpio not found, sleep disabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
return gpio;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static inline int mlx90614_sleep(struct mlx90614_data *data)
|
||||||
|
{
|
||||||
|
return -ENOSYS;
|
||||||
|
}
|
||||||
|
static inline int mlx90614_wakeup(struct mlx90614_data *data)
|
||||||
|
{
|
||||||
|
return -ENOSYS;
|
||||||
|
}
|
||||||
|
static inline struct gpio_desc *mlx90614_probe_wakeup(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Return 0 for single sensor, 1 for dual sensor, <0 on error. */
|
/* Return 0 for single sensor, 1 for dual sensor, <0 on error. */
|
||||||
static int mlx90614_probe_num_ir_sensors(struct i2c_client *client)
|
static int mlx90614_probe_num_ir_sensors(struct i2c_client *client)
|
||||||
{
|
{
|
||||||
@ -166,6 +434,10 @@ static int mlx90614_probe(struct i2c_client *client,
|
|||||||
data = iio_priv(indio_dev);
|
data = iio_priv(indio_dev);
|
||||||
i2c_set_clientdata(client, indio_dev);
|
i2c_set_clientdata(client, indio_dev);
|
||||||
data->client = client;
|
data->client = client;
|
||||||
|
mutex_init(&data->lock);
|
||||||
|
data->wakeup_gpio = mlx90614_probe_wakeup(client);
|
||||||
|
|
||||||
|
mlx90614_wakeup(data);
|
||||||
|
|
||||||
indio_dev->dev.parent = &client->dev;
|
indio_dev->dev.parent = &client->dev;
|
||||||
indio_dev->name = id->name;
|
indio_dev->name = id->name;
|
||||||
@ -188,12 +460,30 @@ static int mlx90614_probe(struct i2c_client *client,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data->wakeup_gpio) {
|
||||||
|
pm_runtime_set_autosuspend_delay(&client->dev,
|
||||||
|
MLX90614_AUTOSLEEP_DELAY);
|
||||||
|
pm_runtime_use_autosuspend(&client->dev);
|
||||||
|
pm_runtime_set_active(&client->dev);
|
||||||
|
pm_runtime_enable(&client->dev);
|
||||||
|
}
|
||||||
|
|
||||||
return iio_device_register(indio_dev);
|
return iio_device_register(indio_dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mlx90614_remove(struct i2c_client *client)
|
static int mlx90614_remove(struct i2c_client *client)
|
||||||
{
|
{
|
||||||
iio_device_unregister(i2c_get_clientdata(client));
|
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||||
|
struct mlx90614_data *data = iio_priv(indio_dev);
|
||||||
|
|
||||||
|
iio_device_unregister(indio_dev);
|
||||||
|
|
||||||
|
if (data->wakeup_gpio) {
|
||||||
|
pm_runtime_disable(&client->dev);
|
||||||
|
if (!pm_runtime_status_suspended(&client->dev))
|
||||||
|
mlx90614_sleep(data);
|
||||||
|
pm_runtime_set_suspended(&client->dev);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -204,10 +494,67 @@ static const struct i2c_device_id mlx90614_id[] = {
|
|||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(i2c, mlx90614_id);
|
MODULE_DEVICE_TABLE(i2c, mlx90614_id);
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_SLEEP
|
||||||
|
static int mlx90614_pm_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||||
|
struct mlx90614_data *data = iio_priv(indio_dev);
|
||||||
|
|
||||||
|
if (data->wakeup_gpio && pm_runtime_active(dev))
|
||||||
|
return mlx90614_sleep(data);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mlx90614_pm_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||||
|
struct mlx90614_data *data = iio_priv(indio_dev);
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (data->wakeup_gpio) {
|
||||||
|
err = mlx90614_wakeup(data);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
pm_runtime_disable(dev);
|
||||||
|
pm_runtime_set_active(dev);
|
||||||
|
pm_runtime_enable(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
static int mlx90614_pm_runtime_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||||
|
struct mlx90614_data *data = iio_priv(indio_dev);
|
||||||
|
|
||||||
|
return mlx90614_sleep(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mlx90614_pm_runtime_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||||
|
struct mlx90614_data *data = iio_priv(indio_dev);
|
||||||
|
|
||||||
|
return mlx90614_wakeup(data);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const struct dev_pm_ops mlx90614_pm_ops = {
|
||||||
|
SET_SYSTEM_SLEEP_PM_OPS(mlx90614_pm_suspend, mlx90614_pm_resume)
|
||||||
|
SET_RUNTIME_PM_OPS(mlx90614_pm_runtime_suspend,
|
||||||
|
mlx90614_pm_runtime_resume, NULL)
|
||||||
|
};
|
||||||
|
|
||||||
static struct i2c_driver mlx90614_driver = {
|
static struct i2c_driver mlx90614_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "mlx90614",
|
.name = "mlx90614",
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
|
.pm = &mlx90614_pm_ops,
|
||||||
},
|
},
|
||||||
.probe = mlx90614_probe,
|
.probe = mlx90614_probe,
|
||||||
.remove = mlx90614_remove,
|
.remove = mlx90614_remove,
|
||||||
|
@ -41,8 +41,8 @@
|
|||||||
#define TMP006_CONFIG_CR_MASK 0x0e00
|
#define TMP006_CONFIG_CR_MASK 0x0e00
|
||||||
#define TMP006_CONFIG_CR_SHIFT 9
|
#define TMP006_CONFIG_CR_SHIFT 9
|
||||||
|
|
||||||
#define MANUFACTURER_MAGIC 0x5449
|
#define TMP006_MANUFACTURER_MAGIC 0x5449
|
||||||
#define DEVICE_MAGIC 0x0067
|
#define TMP006_DEVICE_MAGIC 0x0067
|
||||||
|
|
||||||
struct tmp006_data {
|
struct tmp006_data {
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
@ -191,7 +191,7 @@ static bool tmp006_check_identification(struct i2c_client *client)
|
|||||||
if (did < 0)
|
if (did < 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return mid == MANUFACTURER_MAGIC && did == DEVICE_MAGIC;
|
return mid == TMP006_MANUFACTURER_MAGIC && did == TMP006_DEVICE_MAGIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tmp006_probe(struct i2c_client *client,
|
static int tmp006_probe(struct i2c_client *client,
|
||||||
|
@ -66,58 +66,115 @@
|
|||||||
#define ISL29035_BOUT_SHIFT 0x07
|
#define ISL29035_BOUT_SHIFT 0x07
|
||||||
#define ISL29035_BOUT_MASK (0x01 << ISL29035_BOUT_SHIFT)
|
#define ISL29035_BOUT_MASK (0x01 << ISL29035_BOUT_SHIFT)
|
||||||
|
|
||||||
|
#define ISL29018_INT_TIME_AVAIL "0.090000 0.005630 0.000351 0.000021"
|
||||||
|
#define ISL29023_INT_TIME_AVAIL "0.090000 0.005600 0.000352 0.000022"
|
||||||
|
#define ISL29035_INT_TIME_AVAIL "0.105000 0.006500 0.000410 0.000025"
|
||||||
|
|
||||||
|
static const char * const int_time_avail[] = {
|
||||||
|
ISL29018_INT_TIME_AVAIL,
|
||||||
|
ISL29023_INT_TIME_AVAIL,
|
||||||
|
ISL29035_INT_TIME_AVAIL,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum isl29018_int_time {
|
||||||
|
ISL29018_INT_TIME_16,
|
||||||
|
ISL29018_INT_TIME_12,
|
||||||
|
ISL29018_INT_TIME_8,
|
||||||
|
ISL29018_INT_TIME_4,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned int isl29018_int_utimes[3][4] = {
|
||||||
|
{90000, 5630, 351, 21},
|
||||||
|
{90000, 5600, 352, 22},
|
||||||
|
{105000, 6500, 410, 25},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct isl29018_scale {
|
||||||
|
unsigned int scale;
|
||||||
|
unsigned int uscale;
|
||||||
|
} isl29018_scales[4][4] = {
|
||||||
|
{ {0, 15258}, {0, 61035}, {0, 244140}, {0, 976562} },
|
||||||
|
{ {0, 244140}, {0, 976562}, {3, 906250}, {15, 625000} },
|
||||||
|
{ {3, 906250}, {15, 625000}, {62, 500000}, {250, 0} },
|
||||||
|
{ {62, 500000}, {250, 0}, {1000, 0}, {4000, 0} }
|
||||||
|
};
|
||||||
|
|
||||||
struct isl29018_chip {
|
struct isl29018_chip {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
int type;
|
int type;
|
||||||
unsigned int lux_scale;
|
unsigned int calibscale;
|
||||||
unsigned int lux_uscale;
|
unsigned int ucalibscale;
|
||||||
unsigned int range;
|
unsigned int int_time;
|
||||||
unsigned int adc_bit;
|
struct isl29018_scale scale;
|
||||||
int prox_scheme;
|
int prox_scheme;
|
||||||
bool suspended;
|
bool suspended;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int isl29018_set_range(struct isl29018_chip *chip, unsigned long range,
|
static int isl29018_set_integration_time(struct isl29018_chip *chip,
|
||||||
unsigned int *new_range)
|
unsigned int utime)
|
||||||
{
|
{
|
||||||
static const unsigned long supp_ranges[] = {1000, 4000, 16000, 64000};
|
int i, ret;
|
||||||
int i;
|
unsigned int int_time, new_int_time;
|
||||||
|
struct isl29018_scale new_scale;
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(supp_ranges); ++i) {
|
for (i = 0; i < ARRAY_SIZE(isl29018_int_utimes[chip->type]); ++i) {
|
||||||
if (range <= supp_ranges[i]) {
|
if (utime == isl29018_int_utimes[chip->type][i]) {
|
||||||
*new_range = (unsigned int)supp_ranges[i];
|
new_int_time = i;
|
||||||
|
new_scale = isl29018_scales[new_int_time][0];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i >= ARRAY_SIZE(supp_ranges))
|
if (i >= ARRAY_SIZE(isl29018_int_utimes[chip->type]))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
return regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMANDII,
|
ret = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMANDII,
|
||||||
COMMANDII_RANGE_MASK, i << COMMANDII_RANGE_SHIFT);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int isl29018_set_resolution(struct isl29018_chip *chip,
|
|
||||||
unsigned long adcbit, unsigned int *conf_adc_bit)
|
|
||||||
{
|
|
||||||
static const unsigned long supp_adcbit[] = {16, 12, 8, 4};
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(supp_adcbit); ++i) {
|
|
||||||
if (adcbit >= supp_adcbit[i]) {
|
|
||||||
*conf_adc_bit = (unsigned int)supp_adcbit[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i >= ARRAY_SIZE(supp_adcbit))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
return regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMANDII,
|
|
||||||
COMMANDII_RESOLUTION_MASK,
|
COMMANDII_RESOLUTION_MASK,
|
||||||
i << COMMANDII_RESOLUTION_SHIFT);
|
i << COMMANDII_RESOLUTION_SHIFT);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* keep the same range when integration time changes */
|
||||||
|
int_time = chip->int_time;
|
||||||
|
for (i = 0; i < ARRAY_SIZE(isl29018_scales[int_time]); ++i) {
|
||||||
|
if (chip->scale.scale == isl29018_scales[int_time][i].scale &&
|
||||||
|
chip->scale.uscale == isl29018_scales[int_time][i].uscale) {
|
||||||
|
chip->scale = isl29018_scales[new_int_time][i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chip->int_time = new_int_time;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int isl29018_set_scale(struct isl29018_chip *chip, int scale, int uscale)
|
||||||
|
{
|
||||||
|
int i, ret;
|
||||||
|
struct isl29018_scale new_scale;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(isl29018_scales[chip->int_time]); ++i) {
|
||||||
|
if (scale == isl29018_scales[chip->int_time][i].scale &&
|
||||||
|
uscale == isl29018_scales[chip->int_time][i].uscale) {
|
||||||
|
new_scale = isl29018_scales[chip->int_time][i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i >= ARRAY_SIZE(isl29018_scales[chip->int_time]))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
ret = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMANDII,
|
||||||
|
COMMANDII_RANGE_MASK,
|
||||||
|
i << COMMANDII_RANGE_SHIFT);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
chip->scale = new_scale;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int isl29018_read_sensor_input(struct isl29018_chip *chip, int mode)
|
static int isl29018_read_sensor_input(struct isl29018_chip *chip, int mode)
|
||||||
@ -156,22 +213,17 @@ static int isl29018_read_sensor_input(struct isl29018_chip *chip, int mode)
|
|||||||
static int isl29018_read_lux(struct isl29018_chip *chip, int *lux)
|
static int isl29018_read_lux(struct isl29018_chip *chip, int *lux)
|
||||||
{
|
{
|
||||||
int lux_data;
|
int lux_data;
|
||||||
unsigned int data_x_range, lux_unshifted;
|
unsigned int data_x_range;
|
||||||
|
|
||||||
lux_data = isl29018_read_sensor_input(chip, COMMMAND1_OPMODE_ALS_ONCE);
|
lux_data = isl29018_read_sensor_input(chip, COMMMAND1_OPMODE_ALS_ONCE);
|
||||||
|
|
||||||
if (lux_data < 0)
|
if (lux_data < 0)
|
||||||
return lux_data;
|
return lux_data;
|
||||||
|
|
||||||
/* To support fractional scaling, separate the unshifted lux
|
data_x_range = lux_data * chip->scale.scale +
|
||||||
* into two calculations: int scaling and micro-scaling.
|
lux_data * chip->scale.uscale / 1000000;
|
||||||
* lux_uscale ranges from 0-999999, so about 20 bits. Split
|
*lux = data_x_range * chip->calibscale +
|
||||||
* the /1,000,000 in two to reduce the risk of over/underflow.
|
data_x_range * chip->ucalibscale / 1000000;
|
||||||
*/
|
|
||||||
data_x_range = lux_data * chip->range;
|
|
||||||
lux_unshifted = data_x_range * chip->lux_scale;
|
|
||||||
lux_unshifted += data_x_range / 1000 * chip->lux_uscale / 1000;
|
|
||||||
*lux = lux_unshifted >> chip->adc_bit;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -229,86 +281,37 @@ static int isl29018_read_proximity_ir(struct isl29018_chip *chip, int scheme,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sysfs interface */
|
static ssize_t show_scale_available(struct device *dev,
|
||||||
/* range */
|
|
||||||
static ssize_t show_range(struct device *dev,
|
|
||||||
struct device_attribute *attr, char *buf)
|
struct device_attribute *attr, char *buf)
|
||||||
{
|
{
|
||||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||||
struct isl29018_chip *chip = iio_priv(indio_dev);
|
struct isl29018_chip *chip = iio_priv(indio_dev);
|
||||||
|
int i, len = 0;
|
||||||
|
|
||||||
return sprintf(buf, "%u\n", chip->range);
|
for (i = 0; i < ARRAY_SIZE(isl29018_scales[chip->int_time]); ++i)
|
||||||
|
len += sprintf(buf + len, "%d.%06d ",
|
||||||
|
isl29018_scales[chip->int_time][i].scale,
|
||||||
|
isl29018_scales[chip->int_time][i].uscale);
|
||||||
|
|
||||||
|
buf[len - 1] = '\n';
|
||||||
|
|
||||||
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t store_range(struct device *dev,
|
static ssize_t show_int_time_available(struct device *dev,
|
||||||
struct device_attribute *attr, const char *buf, size_t count)
|
|
||||||
{
|
|
||||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
|
||||||
struct isl29018_chip *chip = iio_priv(indio_dev);
|
|
||||||
int status;
|
|
||||||
unsigned long lval;
|
|
||||||
unsigned int new_range;
|
|
||||||
|
|
||||||
if (kstrtoul(buf, 10, &lval))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (!(lval == 1000UL || lval == 4000UL ||
|
|
||||||
lval == 16000UL || lval == 64000UL)) {
|
|
||||||
dev_err(dev, "The range is not supported\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
mutex_lock(&chip->lock);
|
|
||||||
status = isl29018_set_range(chip, lval, &new_range);
|
|
||||||
if (status < 0) {
|
|
||||||
mutex_unlock(&chip->lock);
|
|
||||||
dev_err(dev,
|
|
||||||
"Error in setting max range with err %d\n", status);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
chip->range = new_range;
|
|
||||||
mutex_unlock(&chip->lock);
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* resolution */
|
|
||||||
static ssize_t show_resolution(struct device *dev,
|
|
||||||
struct device_attribute *attr, char *buf)
|
struct device_attribute *attr, char *buf)
|
||||||
{
|
{
|
||||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||||
struct isl29018_chip *chip = iio_priv(indio_dev);
|
struct isl29018_chip *chip = iio_priv(indio_dev);
|
||||||
|
int i, len = 0;
|
||||||
|
|
||||||
return sprintf(buf, "%u\n", chip->adc_bit);
|
for (i = 0; i < ARRAY_SIZE(isl29018_int_utimes[chip->type]); ++i)
|
||||||
}
|
len += sprintf(buf + len, "0.%06d ",
|
||||||
|
isl29018_int_utimes[chip->type][i]);
|
||||||
|
|
||||||
static ssize_t store_resolution(struct device *dev,
|
buf[len - 1] = '\n';
|
||||||
struct device_attribute *attr, const char *buf, size_t count)
|
|
||||||
{
|
|
||||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
|
||||||
struct isl29018_chip *chip = iio_priv(indio_dev);
|
|
||||||
int status;
|
|
||||||
unsigned int val;
|
|
||||||
unsigned int new_adc_bit;
|
|
||||||
|
|
||||||
if (kstrtouint(buf, 10, &val))
|
return len;
|
||||||
return -EINVAL;
|
|
||||||
if (!(val == 4 || val == 8 || val == 12 || val == 16)) {
|
|
||||||
dev_err(dev, "The resolution is not supported\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
mutex_lock(&chip->lock);
|
|
||||||
status = isl29018_set_resolution(chip, val, &new_adc_bit);
|
|
||||||
if (status < 0) {
|
|
||||||
mutex_unlock(&chip->lock);
|
|
||||||
dev_err(dev, "Error in setting resolution\n");
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
chip->adc_bit = new_adc_bit;
|
|
||||||
mutex_unlock(&chip->lock);
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* proximity scheme */
|
/* proximity scheme */
|
||||||
@ -357,12 +360,30 @@ static int isl29018_write_raw(struct iio_dev *indio_dev,
|
|||||||
int ret = -EINVAL;
|
int ret = -EINVAL;
|
||||||
|
|
||||||
mutex_lock(&chip->lock);
|
mutex_lock(&chip->lock);
|
||||||
if (mask == IIO_CHAN_INFO_CALIBSCALE && chan->type == IIO_LIGHT) {
|
switch (mask) {
|
||||||
chip->lux_scale = val;
|
case IIO_CHAN_INFO_CALIBSCALE:
|
||||||
/* With no write_raw_get_fmt(), val2 is a MICRO fraction. */
|
if (chan->type == IIO_LIGHT) {
|
||||||
chip->lux_uscale = val2;
|
chip->calibscale = val;
|
||||||
|
chip->ucalibscale = val2;
|
||||||
ret = 0;
|
ret = 0;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case IIO_CHAN_INFO_INT_TIME:
|
||||||
|
if (chan->type == IIO_LIGHT) {
|
||||||
|
if (val != 0) {
|
||||||
|
mutex_unlock(&chip->lock);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
ret = isl29018_set_integration_time(chip, val2);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case IIO_CHAN_INFO_SCALE:
|
||||||
|
if (chan->type == IIO_LIGHT)
|
||||||
|
ret = isl29018_set_scale(chip, val, val2);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
mutex_unlock(&chip->lock);
|
mutex_unlock(&chip->lock);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -402,10 +423,24 @@ static int isl29018_read_raw(struct iio_dev *indio_dev,
|
|||||||
if (!ret)
|
if (!ret)
|
||||||
ret = IIO_VAL_INT;
|
ret = IIO_VAL_INT;
|
||||||
break;
|
break;
|
||||||
|
case IIO_CHAN_INFO_INT_TIME:
|
||||||
|
if (chan->type == IIO_LIGHT) {
|
||||||
|
*val = 0;
|
||||||
|
*val2 = isl29018_int_utimes[chip->type][chip->int_time];
|
||||||
|
ret = IIO_VAL_INT_PLUS_MICRO;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case IIO_CHAN_INFO_SCALE:
|
||||||
|
if (chan->type == IIO_LIGHT) {
|
||||||
|
*val = chip->scale.scale;
|
||||||
|
*val2 = chip->scale.uscale;
|
||||||
|
ret = IIO_VAL_INT_PLUS_MICRO;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case IIO_CHAN_INFO_CALIBSCALE:
|
case IIO_CHAN_INFO_CALIBSCALE:
|
||||||
if (chan->type == IIO_LIGHT) {
|
if (chan->type == IIO_LIGHT) {
|
||||||
*val = chip->lux_scale;
|
*val = chip->calibscale;
|
||||||
*val2 = chip->lux_uscale;
|
*val2 = chip->ucalibscale;
|
||||||
ret = IIO_VAL_INT_PLUS_MICRO;
|
ret = IIO_VAL_INT_PLUS_MICRO;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -421,7 +456,9 @@ static int isl29018_read_raw(struct iio_dev *indio_dev,
|
|||||||
.indexed = 1, \
|
.indexed = 1, \
|
||||||
.channel = 0, \
|
.channel = 0, \
|
||||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | \
|
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | \
|
||||||
BIT(IIO_CHAN_INFO_CALIBSCALE), \
|
BIT(IIO_CHAN_INFO_CALIBSCALE) | \
|
||||||
|
BIT(IIO_CHAN_INFO_SCALE) | \
|
||||||
|
BIT(IIO_CHAN_INFO_INT_TIME), \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ISL29018_IR_CHANNEL { \
|
#define ISL29018_IR_CHANNEL { \
|
||||||
@ -447,32 +484,27 @@ static const struct iio_chan_spec isl29023_channels[] = {
|
|||||||
ISL29018_IR_CHANNEL,
|
ISL29018_IR_CHANNEL,
|
||||||
};
|
};
|
||||||
|
|
||||||
static IIO_DEVICE_ATTR(range, S_IRUGO | S_IWUSR, show_range, store_range, 0);
|
static IIO_DEVICE_ATTR(in_illuminance_integration_time_available, S_IRUGO,
|
||||||
static IIO_CONST_ATTR(range_available, "1000 4000 16000 64000");
|
show_int_time_available, NULL, 0);
|
||||||
static IIO_CONST_ATTR(adc_resolution_available, "4 8 12 16");
|
static IIO_DEVICE_ATTR(in_illuminance_scale_available, S_IRUGO,
|
||||||
static IIO_DEVICE_ATTR(adc_resolution, S_IRUGO | S_IWUSR,
|
show_scale_available, NULL, 0);
|
||||||
show_resolution, store_resolution, 0);
|
|
||||||
static IIO_DEVICE_ATTR(proximity_on_chip_ambient_infrared_suppression,
|
static IIO_DEVICE_ATTR(proximity_on_chip_ambient_infrared_suppression,
|
||||||
S_IRUGO | S_IWUSR,
|
S_IRUGO | S_IWUSR,
|
||||||
show_prox_infrared_suppression,
|
show_prox_infrared_suppression,
|
||||||
store_prox_infrared_suppression, 0);
|
store_prox_infrared_suppression, 0);
|
||||||
|
|
||||||
#define ISL29018_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr)
|
#define ISL29018_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr)
|
||||||
#define ISL29018_CONST_ATTR(name) (&iio_const_attr_##name.dev_attr.attr)
|
|
||||||
static struct attribute *isl29018_attributes[] = {
|
static struct attribute *isl29018_attributes[] = {
|
||||||
ISL29018_DEV_ATTR(range),
|
ISL29018_DEV_ATTR(in_illuminance_scale_available),
|
||||||
ISL29018_CONST_ATTR(range_available),
|
ISL29018_DEV_ATTR(in_illuminance_integration_time_available),
|
||||||
ISL29018_DEV_ATTR(adc_resolution),
|
|
||||||
ISL29018_CONST_ATTR(adc_resolution_available),
|
|
||||||
ISL29018_DEV_ATTR(proximity_on_chip_ambient_infrared_suppression),
|
ISL29018_DEV_ATTR(proximity_on_chip_ambient_infrared_suppression),
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct attribute *isl29023_attributes[] = {
|
static struct attribute *isl29023_attributes[] = {
|
||||||
ISL29018_DEV_ATTR(range),
|
ISL29018_DEV_ATTR(in_illuminance_scale_available),
|
||||||
ISL29018_CONST_ATTR(range_available),
|
ISL29018_DEV_ATTR(in_illuminance_integration_time_available),
|
||||||
ISL29018_DEV_ATTR(adc_resolution),
|
|
||||||
ISL29018_CONST_ATTR(adc_resolution_available),
|
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -516,8 +548,6 @@ enum {
|
|||||||
static int isl29018_chip_init(struct isl29018_chip *chip)
|
static int isl29018_chip_init(struct isl29018_chip *chip)
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
unsigned int new_adc_bit;
|
|
||||||
unsigned int new_range;
|
|
||||||
|
|
||||||
if (chip->type == isl29035) {
|
if (chip->type == isl29035) {
|
||||||
status = isl29035_detect(chip);
|
status = isl29035_detect(chip);
|
||||||
@ -566,14 +596,19 @@ static int isl29018_chip_init(struct isl29018_chip *chip)
|
|||||||
usleep_range(1000, 2000); /* per data sheet, page 10 */
|
usleep_range(1000, 2000); /* per data sheet, page 10 */
|
||||||
|
|
||||||
/* set defaults */
|
/* set defaults */
|
||||||
status = isl29018_set_range(chip, chip->range, &new_range);
|
status = isl29018_set_scale(chip, chip->scale.scale,
|
||||||
|
chip->scale.uscale);
|
||||||
if (status < 0) {
|
if (status < 0) {
|
||||||
dev_err(chip->dev, "Init of isl29018 fails\n");
|
dev_err(chip->dev, "Init of isl29018 fails\n");
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = isl29018_set_resolution(chip, chip->adc_bit,
|
status = isl29018_set_integration_time(chip,
|
||||||
&new_adc_bit);
|
isl29018_int_utimes[chip->type][chip->int_time]);
|
||||||
|
if (status < 0) {
|
||||||
|
dev_err(chip->dev, "Init of isl29018 fails\n");
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -701,10 +736,10 @@ static int isl29018_probe(struct i2c_client *client,
|
|||||||
mutex_init(&chip->lock);
|
mutex_init(&chip->lock);
|
||||||
|
|
||||||
chip->type = dev_id;
|
chip->type = dev_id;
|
||||||
chip->lux_scale = 1;
|
chip->calibscale = 1;
|
||||||
chip->lux_uscale = 0;
|
chip->ucalibscale = 0;
|
||||||
chip->range = 1000;
|
chip->int_time = ISL29018_INT_TIME_16;
|
||||||
chip->adc_bit = 16;
|
chip->scale = isl29018_scales[chip->int_time][0];
|
||||||
chip->suspended = false;
|
chip->suspended = false;
|
||||||
|
|
||||||
chip->regmap = devm_regmap_init_i2c(client,
|
chip->regmap = devm_regmap_init_i2c(client,
|
||||||
|
@ -43,6 +43,7 @@ enum iio_chan_info_enum {
|
|||||||
IIO_CHAN_INFO_CALIBWEIGHT,
|
IIO_CHAN_INFO_CALIBWEIGHT,
|
||||||
IIO_CHAN_INFO_DEBOUNCE_COUNT,
|
IIO_CHAN_INFO_DEBOUNCE_COUNT,
|
||||||
IIO_CHAN_INFO_DEBOUNCE_TIME,
|
IIO_CHAN_INFO_DEBOUNCE_TIME,
|
||||||
|
IIO_CHAN_INFO_CALIBEMISSIVITY,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum iio_shared_by {
|
enum iio_shared_by {
|
||||||
|
@ -8,6 +8,7 @@ help:
|
|||||||
@echo ' cpupower - a tool for all things x86 CPU power'
|
@echo ' cpupower - a tool for all things x86 CPU power'
|
||||||
@echo ' firewire - the userspace part of nosy, an IEEE-1394 traffic sniffer'
|
@echo ' firewire - the userspace part of nosy, an IEEE-1394 traffic sniffer'
|
||||||
@echo ' hv - tools used when in Hyper-V clients'
|
@echo ' hv - tools used when in Hyper-V clients'
|
||||||
|
@echo ' iio - IIO tools'
|
||||||
@echo ' lguest - a minimal 32-bit x86 hypervisor'
|
@echo ' lguest - a minimal 32-bit x86 hypervisor'
|
||||||
@echo ' perf - Linux performance measurement and analysis tool'
|
@echo ' perf - Linux performance measurement and analysis tool'
|
||||||
@echo ' selftests - various kernel selftests'
|
@echo ' selftests - various kernel selftests'
|
||||||
@ -41,7 +42,7 @@ acpi: FORCE
|
|||||||
cpupower: FORCE
|
cpupower: FORCE
|
||||||
$(call descend,power/$@)
|
$(call descend,power/$@)
|
||||||
|
|
||||||
cgroup firewire hv guest usb virtio vm net: FORCE
|
cgroup firewire hv guest usb virtio vm net iio: FORCE
|
||||||
$(call descend,$@)
|
$(call descend,$@)
|
||||||
|
|
||||||
liblockdep: FORCE
|
liblockdep: FORCE
|
||||||
@ -91,7 +92,7 @@ acpi_clean:
|
|||||||
cpupower_clean:
|
cpupower_clean:
|
||||||
$(call descend,power/cpupower,clean)
|
$(call descend,power/cpupower,clean)
|
||||||
|
|
||||||
cgroup_clean hv_clean firewire_clean lguest_clean usb_clean virtio_clean vm_clean net_clean:
|
cgroup_clean hv_clean firewire_clean lguest_clean usb_clean virtio_clean vm_clean net_clean iio_clean:
|
||||||
$(call descend,$(@:_clean=),clean)
|
$(call descend,$(@:_clean=),clean)
|
||||||
|
|
||||||
liblockdep_clean:
|
liblockdep_clean:
|
||||||
@ -114,6 +115,6 @@ tmon_clean:
|
|||||||
|
|
||||||
clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean lguest_clean \
|
clean: acpi_clean cgroup_clean cpupower_clean hv_clean firewire_clean lguest_clean \
|
||||||
perf_clean selftests_clean turbostat_clean usb_clean virtio_clean \
|
perf_clean selftests_clean turbostat_clean usb_clean virtio_clean \
|
||||||
vm_clean net_clean x86_energy_perf_policy_clean tmon_clean
|
vm_clean net_clean iio_clean x86_energy_perf_policy_clean tmon_clean
|
||||||
|
|
||||||
.PHONY: FORCE
|
.PHONY: FORCE
|
||||||
|
Loading…
Reference in New Issue
Block a user