First round of new devices, features and cleanups for IIO in the 4.17 cycle.
Outside of IIO * Strongly typed 64bit int_sqrt function needed by the mlx90632 New device support * adc081s - New driver supporting adc081s, adc101s and adc121s TI ADCs. * ad5272 - New driver supproting the ad5272 and ad5274 ADI digital potentiometers with DT bindings. * axp20x_adc - support the AXP813 ADC - includes rework patches to prepare for this. * mlx90632 - New driver with dt bindings for this IR temperature sensor. Features * axp20x_adc - Add DT bindings and probing. * dht11 - The sensor has a wider range than advertised in the datasheet - support it. * st_lsm6dsx - Add hardware timestamp su9pport. Cleanups * ABI docs - Update email contact for Matt Ranostay * SPDX changes - Matt Ranostay has moved his drivers over to SPDX. Currently we are making this an author choice in IIO. * ad7192 - Disable burnout current on misconfiguration. No actually effect as they simply won't work otherwise. * ad7476 - Drop a license definition that was replicating information in SPDX tag. * ade7758 - Expand buf_lock to cover both buffer and state protection allowing unintented uses of mlock in the core to be removed. * ade7759 - Align parameters to opening parenthesis. * at91_adc - Depend on sysfs instead of selecting it - for try wide consistency. * ccs811 - trivial naming type for a define. * ep93xx - Drop a redundant return as a result checking platform_get_resource. * hts221 - Regmap conversion which simplifies the driver somewhat. - Clean up some restricted endian cast warnings. - Drop a trailing whitespace from a comment - Drop an unnecessary get_unaligned by changing to the right 16bit data type. * ms5611 - Fix coding style in the probe function (whitespace) * st_accel - Use strlcpy instead of strncpy to avoid potentially truncating a string. -----BEGIN PGP SIGNATURE----- iQJFBAABCAAvFiEEbilms4eEBlKRJoGxVIU0mcT0FogFAlqL3RIRHGppYzIzQGtl cm5lbC5vcmcACgkQVIU0mcT0Foiq7w//eKBOgTy0osSkLjSXACsJ0oizUoEGDV3t NxQ3D3el+1ATUd2tJL33gDvCzYw5DPcNcy3Hh/tl5svnwdV0WvBwIDIgdPfPBJQu LpBjT6bI2MwYdi1WLHZJBeoIXxg0PMF4GNZ2DIk0GL0WJg87n6SkZCmslW8Rv8BD NE8m9Qv72vhhZ9QjcBPoQYzRAY16moaX+9a36TBgtgGs7c5vHYNGNxoMn6sZ3sQk VbUtqWv9By3gp0mUg7T2Qb+62d04MbDk3Sa/K2B0COBOCSP7kWjg9h7KP902R6rU WNsTK+rVRHxV5Jm6PBPcit4HeSK3/9308EKkv2axQrPqCLM50GoxoqAhNq7UpAEu YXLi4f0te7sEQdt2QFDCdDMk1HjI3eCA7a8enBpex3gt7bdGTMQH9qDKdsWE+z4G Gm9xbZn+z7wNfowf5wAE16Y+yUS4UvVXawWD8FSIpKi3HvvyYS1I0nTnzvTvS/C3 EhubRf+nguYsFjHB+LpA3II6b/NlrkKGO8PDzCr3kvfPUVErTWPgVzx6+5vPG2Gd 6qhoFVanV7SuHD0lSq9tKQfsh0DFjAh6DQYdLpoFaxgIJi6cLCGbwOMY49ihYfSO mTMz5c52mJACOpq7W5e9xzvLwx6pxiQobeun3fq7kDdq5vUweKSGK4iggEZ+sr2N luFxzN7VLNA= =CH1K -----END PGP SIGNATURE----- Merge tag 'iio-for-4.17a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next Jonathan writes: First round of new devices, features and cleanups for IIO in the 4.17 cycle. Outside of IIO * Strongly typed 64bit int_sqrt function needed by the mlx90632 New device support * adc081s - New driver supporting adc081s, adc101s and adc121s TI ADCs. * ad5272 - New driver supproting the ad5272 and ad5274 ADI digital potentiometers with DT bindings. * axp20x_adc - support the AXP813 ADC - includes rework patches to prepare for this. * mlx90632 - New driver with dt bindings for this IR temperature sensor. Features * axp20x_adc - Add DT bindings and probing. * dht11 - The sensor has a wider range than advertised in the datasheet - support it. * st_lsm6dsx - Add hardware timestamp su9pport. Cleanups * ABI docs - Update email contact for Matt Ranostay * SPDX changes - Matt Ranostay has moved his drivers over to SPDX. Currently we are making this an author choice in IIO. * ad7192 - Disable burnout current on misconfiguration. No actually effect as they simply won't work otherwise. * ad7476 - Drop a license definition that was replicating information in SPDX tag. * ade7758 - Expand buf_lock to cover both buffer and state protection allowing unintented uses of mlock in the core to be removed. * ade7759 - Align parameters to opening parenthesis. * at91_adc - Depend on sysfs instead of selecting it - for try wide consistency. * ccs811 - trivial naming type for a define. * ep93xx - Drop a redundant return as a result checking platform_get_resource. * hts221 - Regmap conversion which simplifies the driver somewhat. - Clean up some restricted endian cast warnings. - Drop a trailing whitespace from a comment - Drop an unnecessary get_unaligned by changing to the right 16bit data type. * ms5611 - Fix coding style in the probe function (whitespace) * st_accel - Use strlcpy instead of strncpy to avoid potentially truncating a string.
This commit is contained in:
Коммит
59142f808a
|
@ -1,7 +1,7 @@
|
|||
What: /sys/bus/iio/devices/iio:deviceX/in_concentration_VOC_short_raw
|
||||
Date: September 2015
|
||||
KernelVersion: 4.3
|
||||
Contact: Matt Ranostay <mranostay@gmail.com>
|
||||
Contact: Matt Ranostay <matt.ranostay@konsulko.com>
|
||||
Description:
|
||||
Get the raw calibration VOC value from the sensor.
|
||||
This value has little application outside of calibration.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
What /sys/bus/iio/devices/iio:deviceX/in_proximity_input
|
||||
Date: March 2014
|
||||
KernelVersion: 3.15
|
||||
Contact: Matt Ranostay <mranostay@gmail.com>
|
||||
Contact: Matt Ranostay <matt.ranostay@konsulko.com>
|
||||
Description:
|
||||
Get the current distance in meters of storm (1km steps)
|
||||
1000-40000 = distance in meters
|
||||
|
@ -9,7 +9,7 @@ Description:
|
|||
What /sys/bus/iio/devices/iio:deviceX/sensor_sensitivity
|
||||
Date: March 2014
|
||||
KernelVersion: 3.15
|
||||
Contact: Matt Ranostay <mranostay@gmail.com>
|
||||
Contact: Matt Ranostay <matt.ranostay@konsulko.com>
|
||||
Description:
|
||||
Show or set the gain boost of the amp, from 0-31 range.
|
||||
18 = indoors (default)
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
* X-Powers AXP ADC bindings
|
||||
|
||||
Required properties:
|
||||
- compatible: should be one of:
|
||||
- "x-powers,axp209-adc",
|
||||
- "x-powers,axp221-adc",
|
||||
- "x-powers,axp813-adc",
|
||||
- #io-channel-cells: should be 1,
|
||||
|
||||
Example:
|
||||
|
||||
&axp22x {
|
||||
adc {
|
||||
compatible = "x-powers,axp221-adc";
|
||||
#io-channel-cells = <1>;
|
||||
};
|
||||
};
|
||||
|
||||
ADC channels and their indexes per variant:
|
||||
|
||||
AXP209
|
||||
------
|
||||
0 | acin_v
|
||||
1 | acin_i
|
||||
2 | vbus_v
|
||||
3 | vbus_i
|
||||
4 | pmic_temp
|
||||
5 | gpio0_v
|
||||
6 | gpio1_v
|
||||
7 | ipsout_v
|
||||
8 | batt_v
|
||||
9 | batt_chrg_i
|
||||
10 | batt_dischrg_i
|
||||
|
||||
AXP22x
|
||||
------
|
||||
0 | pmic_temp
|
||||
1 | batt_v
|
||||
2 | batt_chrg_i
|
||||
3 | batt_dischrg_i
|
||||
|
||||
AXP813
|
||||
------
|
||||
0 | pmic_temp
|
||||
1 | gpio0_v
|
||||
2 | batt_v
|
||||
3 | batt_chrg_i
|
||||
4 | batt_dischrg_i
|
|
@ -0,0 +1,27 @@
|
|||
* Analog Devices AD5272 digital potentiometer
|
||||
|
||||
The node for this device must be a child node of a I2C controller, hence
|
||||
all mandatory properties for your controller must be specified. See directory:
|
||||
|
||||
Documentation/devicetree/bindings/i2c
|
||||
|
||||
for more details.
|
||||
|
||||
Required properties:
|
||||
- compatible: Must be one of the following, depending on the model:
|
||||
adi,ad5272-020
|
||||
adi,ad5272-050
|
||||
adi,ad5272-100
|
||||
adi,ad5274-020
|
||||
adi,ad5274-100
|
||||
|
||||
Optional properties:
|
||||
- reset-gpios: GPIO specification for the RESET input. This is an
|
||||
active low signal to the AD5272.
|
||||
|
||||
Example:
|
||||
ad5272: potentiometer@2f {
|
||||
reg = <0x2F>;
|
||||
compatible = "adi,ad5272-020";
|
||||
reset-gpios = <&gpio3 6 GPIO_ACTIVE_HIGH>;
|
||||
};
|
|
@ -0,0 +1,28 @@
|
|||
* Melexis MLX90632 contactless Infra Red temperature sensor
|
||||
|
||||
Link to datasheet: https://www.melexis.com/en/documents/documentation/datasheets/datasheet-mlx90632
|
||||
|
||||
There are various applications for the Infra Red contactless temperature sensor
|
||||
and MLX90632 is most suitable for consumer applications where measured object
|
||||
temperature is in range between -20 to 200 degrees Celsius with relative error
|
||||
of measurement below 1 degree Celsius in object temperature range for
|
||||
industrial applications. Since it can operate and measure ambient temperature
|
||||
in range of -20 to 85 degrees Celsius it is suitable also for outdoor use.
|
||||
|
||||
Be aware that electronics surrounding the sensor can increase ambient
|
||||
temperature. MLX90632 can be calibrated to reduce the housing effect via
|
||||
already existing EEPROM parameters.
|
||||
|
||||
Since measured object emissivity effects Infra Red energy emitted, emissivity
|
||||
should be set before requesting the object temperature.
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "melexis,mlx90632"
|
||||
- reg: the I2C address of the sensor (default 0x3a)
|
||||
|
||||
Example:
|
||||
|
||||
mlx90632@3a {
|
||||
compatible = "melexis,mlx90632";
|
||||
reg = <0x3a>;
|
||||
};
|
|
@ -8878,6 +8878,13 @@ W: http://www.melexis.com
|
|||
S: Supported
|
||||
F: drivers/iio/temperature/mlx90614.c
|
||||
|
||||
MELEXIS MLX90632 DRIVER
|
||||
M: Crt Mori <cmo@melexis.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
W: http://www.melexis.com
|
||||
S: Supported
|
||||
F: drivers/iio/temperature/mlx90632.c
|
||||
|
||||
MELFAS MIP4 TOUCHSCREEN DRIVER
|
||||
M: Sangwon Jee <jeesw@melfas.com>
|
||||
W: http://www.melfas.com
|
||||
|
|
|
@ -159,9 +159,8 @@ static int st_accel_i2c_probe(struct i2c_client *client,
|
|||
if ((ret < 0) || (ret >= ST_ACCEL_MAX))
|
||||
return -ENODEV;
|
||||
|
||||
strncpy(client->name, st_accel_id_table[ret].name,
|
||||
strlcpy(client->name, st_accel_id_table[ret].name,
|
||||
sizeof(client->name));
|
||||
client->name[sizeof(client->name) - 1] = '\0';
|
||||
} else if (!id)
|
||||
return -ENODEV;
|
||||
|
||||
|
|
|
@ -144,10 +144,9 @@ config ASPEED_ADC
|
|||
config AT91_ADC
|
||||
tristate "Atmel AT91 ADC"
|
||||
depends on ARCH_AT91
|
||||
depends on INPUT
|
||||
depends on INPUT && SYSFS
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select SYSFS
|
||||
help
|
||||
Say yes here to build support for Atmel AT91 ADC.
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* AD7466/7/8 AD7476/5/7/8 (A) SPI ADC driver
|
||||
* Analog Devices AD7466/7/8 AD7476/5/7/8 (A) SPI ADC driver
|
||||
* TI ADC081S/ADC101S/ADC121S 8/10/12-bit SPI ADC driver
|
||||
*
|
||||
* Copyright 2010 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
|
@ -56,6 +56,9 @@ enum ad7476_supported_device_ids {
|
|||
ID_AD7468,
|
||||
ID_AD7495,
|
||||
ID_AD7940,
|
||||
ID_ADC081S,
|
||||
ID_ADC101S,
|
||||
ID_ADC121S,
|
||||
};
|
||||
|
||||
static irqreturn_t ad7476_trigger_handler(int irq, void *p)
|
||||
|
@ -147,6 +150,8 @@ static int ad7476_read_raw(struct iio_dev *indio_dev,
|
|||
}, \
|
||||
}
|
||||
|
||||
#define ADC081S_CHAN(bits) _AD7476_CHAN((bits), 12 - (bits), \
|
||||
BIT(IIO_CHAN_INFO_RAW))
|
||||
#define AD7476_CHAN(bits) _AD7476_CHAN((bits), 13 - (bits), \
|
||||
BIT(IIO_CHAN_INFO_RAW))
|
||||
#define AD7940_CHAN(bits) _AD7476_CHAN((bits), 15 - (bits), \
|
||||
|
@ -192,6 +197,18 @@ static const struct ad7476_chip_info ad7476_chip_info_tbl[] = {
|
|||
.channel[0] = AD7940_CHAN(14),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_ADC081S] = {
|
||||
.channel[0] = ADC081S_CHAN(8),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_ADC101S] = {
|
||||
.channel[0] = ADC081S_CHAN(10),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_ADC121S] = {
|
||||
.channel[0] = ADC081S_CHAN(12),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_info ad7476_info = {
|
||||
|
@ -294,6 +311,9 @@ static const struct spi_device_id ad7476_id[] = {
|
|||
{"ad7910", ID_AD7467},
|
||||
{"ad7920", ID_AD7466},
|
||||
{"ad7940", ID_AD7940},
|
||||
{"adc081s", ID_ADC081S},
|
||||
{"adc101s", ID_ADC101S},
|
||||
{"adc121s", ID_ADC121S},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad7476_id);
|
||||
|
|
|
@ -35,8 +35,13 @@
|
|||
#define AXP20X_GPIO10_IN_RANGE_GPIO1_VAL(x) (((x) & BIT(0)) << 1)
|
||||
|
||||
#define AXP20X_ADC_RATE_MASK GENMASK(7, 6)
|
||||
#define AXP813_V_I_ADC_RATE_MASK GENMASK(5, 4)
|
||||
#define AXP813_ADC_RATE_MASK (AXP20X_ADC_RATE_MASK | AXP813_V_I_ADC_RATE_MASK)
|
||||
#define AXP20X_ADC_RATE_HZ(x) ((ilog2((x) / 25) << 6) & AXP20X_ADC_RATE_MASK)
|
||||
#define AXP22X_ADC_RATE_HZ(x) ((ilog2((x) / 100) << 6) & AXP20X_ADC_RATE_MASK)
|
||||
#define AXP813_TS_GPIO0_ADC_RATE_HZ(x) AXP20X_ADC_RATE_HZ(x)
|
||||
#define AXP813_V_I_ADC_RATE_HZ(x) ((ilog2((x) / 100) << 4) & AXP813_V_I_ADC_RATE_MASK)
|
||||
#define AXP813_ADC_RATE_HZ(x) (AXP20X_ADC_RATE_HZ(x) | AXP813_V_I_ADC_RATE_HZ(x))
|
||||
|
||||
#define AXP20X_ADC_CHANNEL(_channel, _name, _type, _reg) \
|
||||
{ \
|
||||
|
@ -95,6 +100,12 @@ enum axp22x_adc_channel_i {
|
|||
AXP22X_BATT_DISCHRG_I,
|
||||
};
|
||||
|
||||
enum axp813_adc_channel_v {
|
||||
AXP813_TS_IN = 0,
|
||||
AXP813_GPIO0_V,
|
||||
AXP813_BATT_V,
|
||||
};
|
||||
|
||||
static struct iio_map axp20x_maps[] = {
|
||||
{
|
||||
.consumer_dev_name = "axp20x-usb-power-supply",
|
||||
|
@ -197,6 +208,25 @@ static const struct iio_chan_spec axp22x_adc_channels[] = {
|
|||
AXP20X_BATT_DISCHRG_I_H),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec axp813_adc_channels[] = {
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.address = AXP22X_PMIC_TEMP_H,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.datasheet_name = "pmic_temp",
|
||||
},
|
||||
AXP20X_ADC_CHANNEL(AXP813_GPIO0_V, "gpio0_v", IIO_VOLTAGE,
|
||||
AXP288_GP_ADC_H),
|
||||
AXP20X_ADC_CHANNEL(AXP813_BATT_V, "batt_v", IIO_VOLTAGE,
|
||||
AXP20X_BATT_V_H),
|
||||
AXP20X_ADC_CHANNEL(AXP22X_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT,
|
||||
AXP20X_BATT_CHRG_I_H),
|
||||
AXP20X_ADC_CHANNEL(AXP22X_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT,
|
||||
AXP20X_BATT_DISCHRG_I_H),
|
||||
};
|
||||
|
||||
static int axp20x_adc_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val)
|
||||
{
|
||||
|
@ -243,6 +273,18 @@ static int axp22x_adc_raw(struct iio_dev *indio_dev,
|
|||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int axp813_adc_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val)
|
||||
{
|
||||
struct axp20x_adc_iio *info = iio_priv(indio_dev);
|
||||
|
||||
*val = axp20x_read_variable_width(info->regmap, chan->address, 12);
|
||||
if (*val < 0)
|
||||
return *val;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int axp20x_adc_scale_voltage(int channel, int *val, int *val2)
|
||||
{
|
||||
switch (channel) {
|
||||
|
@ -273,6 +315,24 @@ static int axp20x_adc_scale_voltage(int channel, int *val, int *val2)
|
|||
}
|
||||
}
|
||||
|
||||
static int axp813_adc_scale_voltage(int channel, int *val, int *val2)
|
||||
{
|
||||
switch (channel) {
|
||||
case AXP813_GPIO0_V:
|
||||
*val = 0;
|
||||
*val2 = 800000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
|
||||
case AXP813_BATT_V:
|
||||
*val = 1;
|
||||
*val2 = 100000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int axp20x_adc_scale_current(int channel, int *val, int *val2)
|
||||
{
|
||||
switch (channel) {
|
||||
|
@ -342,6 +402,26 @@ static int axp22x_adc_scale(struct iio_chan_spec const *chan, int *val,
|
|||
}
|
||||
}
|
||||
|
||||
static int axp813_adc_scale(struct iio_chan_spec const *chan, int *val,
|
||||
int *val2)
|
||||
{
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
return axp813_adc_scale_voltage(chan->channel, val, val2);
|
||||
|
||||
case IIO_CURRENT:
|
||||
*val = 1;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_TEMP:
|
||||
*val = 100;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int axp20x_adc_offset_voltage(struct iio_dev *indio_dev, int channel,
|
||||
int *val)
|
||||
{
|
||||
|
@ -425,6 +505,26 @@ static int axp22x_read_raw(struct iio_dev *indio_dev,
|
|||
}
|
||||
}
|
||||
|
||||
static int axp813_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = -2667;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
return axp813_adc_scale(chan, val, val2);
|
||||
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
return axp813_adc_raw(indio_dev, chan, val);
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int axp20x_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2,
|
||||
long mask)
|
||||
|
@ -470,14 +570,29 @@ static const struct iio_info axp22x_adc_iio_info = {
|
|||
.read_raw = axp22x_read_raw,
|
||||
};
|
||||
|
||||
static int axp20x_adc_rate(int rate)
|
||||
static const struct iio_info axp813_adc_iio_info = {
|
||||
.read_raw = axp813_read_raw,
|
||||
};
|
||||
|
||||
static int axp20x_adc_rate(struct axp20x_adc_iio *info, int rate)
|
||||
{
|
||||
return AXP20X_ADC_RATE_HZ(rate);
|
||||
return regmap_update_bits(info->regmap, AXP20X_ADC_RATE,
|
||||
AXP20X_ADC_RATE_MASK,
|
||||
AXP20X_ADC_RATE_HZ(rate));
|
||||
}
|
||||
|
||||
static int axp22x_adc_rate(int rate)
|
||||
static int axp22x_adc_rate(struct axp20x_adc_iio *info, int rate)
|
||||
{
|
||||
return AXP22X_ADC_RATE_HZ(rate);
|
||||
return regmap_update_bits(info->regmap, AXP20X_ADC_RATE,
|
||||
AXP20X_ADC_RATE_MASK,
|
||||
AXP22X_ADC_RATE_HZ(rate));
|
||||
}
|
||||
|
||||
static int axp813_adc_rate(struct axp20x_adc_iio *info, int rate)
|
||||
{
|
||||
return regmap_update_bits(info->regmap, AXP813_ADC_RATE,
|
||||
AXP813_ADC_RATE_MASK,
|
||||
AXP813_ADC_RATE_HZ(rate));
|
||||
}
|
||||
|
||||
struct axp_data {
|
||||
|
@ -485,7 +600,8 @@ struct axp_data {
|
|||
int num_channels;
|
||||
struct iio_chan_spec const *channels;
|
||||
unsigned long adc_en1_mask;
|
||||
int (*adc_rate)(int rate);
|
||||
int (*adc_rate)(struct axp20x_adc_iio *info,
|
||||
int rate);
|
||||
bool adc_en2;
|
||||
struct iio_map *maps;
|
||||
};
|
||||
|
@ -510,9 +626,28 @@ static const struct axp_data axp22x_data = {
|
|||
.maps = axp22x_maps,
|
||||
};
|
||||
|
||||
static const struct axp_data axp813_data = {
|
||||
.iio_info = &axp813_adc_iio_info,
|
||||
.num_channels = ARRAY_SIZE(axp813_adc_channels),
|
||||
.channels = axp813_adc_channels,
|
||||
.adc_en1_mask = AXP22X_ADC_EN1_MASK,
|
||||
.adc_rate = axp813_adc_rate,
|
||||
.adc_en2 = false,
|
||||
.maps = axp22x_maps,
|
||||
};
|
||||
|
||||
static const struct of_device_id axp20x_adc_of_match[] = {
|
||||
{ .compatible = "x-powers,axp209-adc", .data = (void *)&axp20x_data, },
|
||||
{ .compatible = "x-powers,axp221-adc", .data = (void *)&axp22x_data, },
|
||||
{ .compatible = "x-powers,axp813-adc", .data = (void *)&axp813_data, },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, axp20x_adc_of_match);
|
||||
|
||||
static const struct platform_device_id axp20x_adc_id_match[] = {
|
||||
{ .name = "axp20x-adc", .driver_data = (kernel_ulong_t)&axp20x_data, },
|
||||
{ .name = "axp22x-adc", .driver_data = (kernel_ulong_t)&axp22x_data, },
|
||||
{ .name = "axp813-adc", .driver_data = (kernel_ulong_t)&axp813_data, },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, axp20x_adc_id_match);
|
||||
|
@ -538,7 +673,16 @@ static int axp20x_probe(struct platform_device *pdev)
|
|||
indio_dev->dev.of_node = pdev->dev.of_node;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
info->data = (struct axp_data *)platform_get_device_id(pdev)->driver_data;
|
||||
if (!pdev->dev.of_node) {
|
||||
const struct platform_device_id *id;
|
||||
|
||||
id = platform_get_device_id(pdev);
|
||||
info->data = (struct axp_data *)id->driver_data;
|
||||
} else {
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
info->data = (struct axp_data *)of_device_get_match_data(dev);
|
||||
}
|
||||
|
||||
indio_dev->name = platform_get_device_id(pdev)->name;
|
||||
indio_dev->info = info->data->iio_info;
|
||||
|
@ -554,8 +698,7 @@ static int axp20x_probe(struct platform_device *pdev)
|
|||
AXP20X_ADC_EN2_MASK, AXP20X_ADC_EN2_MASK);
|
||||
|
||||
/* Configure ADCs rate */
|
||||
regmap_update_bits(info->regmap, AXP20X_ADC_RATE, AXP20X_ADC_RATE_MASK,
|
||||
info->data->adc_rate(100));
|
||||
info->data->adc_rate(info, 100);
|
||||
|
||||
ret = iio_map_array_register(indio_dev, info->data->maps);
|
||||
if (ret < 0) {
|
||||
|
@ -602,6 +745,7 @@ static int axp20x_remove(struct platform_device *pdev)
|
|||
static struct platform_driver axp20x_adc_driver = {
|
||||
.driver = {
|
||||
.name = "axp20x-adc",
|
||||
.of_match_table = of_match_ptr(axp20x_adc_of_match),
|
||||
},
|
||||
.id_table = axp20x_adc_id_match,
|
||||
.probe = axp20x_probe,
|
||||
|
|
|
@ -167,10 +167,6 @@ static int ep93xx_adc_probe(struct platform_device *pdev)
|
|||
priv = iio_priv(iiodev);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Cannot obtain memory resource\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
priv->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(priv->base)) {
|
||||
dev_err(&pdev->dev, "Cannot map memory resource\n");
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* ti-adc161s626.c - Texas Instruments ADC161S626 1-channel differential ADC
|
||||
*
|
||||
|
@ -5,17 +6,8 @@
|
|||
* adc141s626 - 14-bit ADC
|
||||
* adc161s626 - 16-bit ADC
|
||||
*
|
||||
* Copyright (C) 2016 Matt Ranostay <mranostay@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* Copyright (C) 2016-2018
|
||||
* Author: Matt Ranostay <matt.ranostay@konsulko.com>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
@ -275,6 +267,6 @@ static struct spi_driver ti_adc_driver = {
|
|||
};
|
||||
module_spi_driver(ti_adc_driver);
|
||||
|
||||
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
|
||||
MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
|
||||
MODULE_DESCRIPTION("Texas Instruments ADC1x1S 1-channel differential ADC");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -1,18 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* ams-iaq-core.c - Support for AMS iAQ-Core VOC sensors
|
||||
*
|
||||
* Copyright (C) 2015 Matt Ranostay <mranostay@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* Copyright (C) 2015, 2018
|
||||
* Author: Matt Ranostay <matt.ranostay@konsulko.com>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
@ -194,6 +185,6 @@ static struct i2c_driver ams_iaqcore_driver = {
|
|||
};
|
||||
module_i2c_driver(ams_iaqcore_driver);
|
||||
|
||||
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
|
||||
MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
|
||||
MODULE_DESCRIPTION("AMS iAQ-Core VOC sensors");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -1,17 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* atlas-ph-sensor.c - Support for Atlas Scientific OEM pH-SM sensor
|
||||
*
|
||||
* Copyright (C) 2015 Matt Ranostay <mranostay@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* Copyright (C) 2015-2018 Matt Ranostay
|
||||
* Author: Matt Ranostay <matt.ranostay@konsulko.com>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
@ -689,6 +681,6 @@ static struct i2c_driver atlas_driver = {
|
|||
};
|
||||
module_i2c_driver(atlas_driver);
|
||||
|
||||
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
|
||||
MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
|
||||
MODULE_DESCRIPTION("Atlas Scientific pH-SM sensor");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
#define CCS811_ALG_RESULT_DATA 0x02
|
||||
#define CCS811_RAW_DATA 0x03
|
||||
#define CCS811_HW_ID 0x20
|
||||
#define CCS881_HW_ID_VALUE 0x81
|
||||
#define CCS811_HW_ID_VALUE 0x81
|
||||
#define CCS811_HW_VERSION 0x21
|
||||
#define CCS811_HW_VERSION_VALUE 0x10
|
||||
#define CCS811_HW_VERSION_MASK 0xF0
|
||||
|
@ -353,7 +353,7 @@ static int ccs811_probe(struct i2c_client *client,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret != CCS881_HW_ID_VALUE) {
|
||||
if (ret != CCS811_HW_ID_VALUE) {
|
||||
dev_err(&client->dev, "hardware id doesn't match CCS81x\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
|
|
@ -1,18 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* vz89x.c - Support for SGX Sensortech MiCS VZ89X VOC sensors
|
||||
*
|
||||
* Copyright (C) 2015 Matt Ranostay <mranostay@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* Copyright (C) 2015-2018
|
||||
* Author: Matt Ranostay <matt.ranostay@konsulko.com>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
@ -419,6 +410,6 @@ static struct i2c_driver vz89x_driver = {
|
|||
};
|
||||
module_i2c_driver(vz89x_driver);
|
||||
|
||||
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
|
||||
MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
|
||||
MODULE_DESCRIPTION("SGX Sensortech MiCS VZ89X VOC sensors");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -1,17 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* max30100.c - Support for MAX30100 heart rate and pulse oximeter sensor
|
||||
*
|
||||
* Copyright (C) 2015 Matt Ranostay <mranostay@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* Copyright (C) 2015, 2018
|
||||
* Author: Matt Ranostay <matt.ranostay@konsulko.com>
|
||||
*
|
||||
* TODO: enable pulse length controls via device tree properties
|
||||
*/
|
||||
|
@ -518,6 +510,6 @@ static struct i2c_driver max30100_driver = {
|
|||
};
|
||||
module_i2c_driver(max30100_driver);
|
||||
|
||||
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
|
||||
MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
|
||||
MODULE_DESCRIPTION("MAX30100 heart rate and pulse oximeter sensor");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -68,10 +68,12 @@ config HTS221
|
|||
config HTS221_I2C
|
||||
tristate
|
||||
depends on HTS221
|
||||
select REGMAP_I2C
|
||||
|
||||
config HTS221_SPI
|
||||
tristate
|
||||
depends on HTS221
|
||||
select REGMAP_SPI
|
||||
|
||||
config HTU21
|
||||
tristate "Measurement Specialties HTU21 humidity & temperature sensor"
|
||||
|
|
|
@ -159,7 +159,7 @@ static int dht11_decode(struct dht11 *dht11, int offset)
|
|||
}
|
||||
|
||||
dht11->timestamp = ktime_get_boot_ns();
|
||||
if (hum_int < 20) { /* DHT22 */
|
||||
if (hum_int < 4) { /* DHT22: 100000 = (3*256+232)*100 */
|
||||
dht11->temperature = (((temp_int & 0x7f) << 8) + temp_dec) *
|
||||
((temp_int & 0x80) ? -100 : 100);
|
||||
dht11->humidity = ((hum_int << 8) + hum_dec) * 100;
|
||||
|
|
|
@ -1,17 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* hdc100x.c - Support for the TI HDC100x temperature + humidity sensors
|
||||
*
|
||||
* Copyright (C) 2015 Matt Ranostay <mranostay@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* Copyright (C) 2015, 2018
|
||||
* Author: Matt Ranostay <matt.ranostay@konsulko.com>
|
||||
*
|
||||
* Datasheets:
|
||||
* http://www.ti.com/product/HDC1000/datasheet
|
||||
|
@ -449,6 +441,6 @@ static struct i2c_driver hdc100x_driver = {
|
|||
};
|
||||
module_i2c_driver(hdc100x_driver);
|
||||
|
||||
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
|
||||
MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
|
||||
MODULE_DESCRIPTION("TI HDC100x humidity and temperature sensor driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -15,21 +15,8 @@
|
|||
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#define HTS221_RX_MAX_LENGTH 8
|
||||
#define HTS221_TX_MAX_LENGTH 8
|
||||
|
||||
#define HTS221_DATA_SIZE 2
|
||||
|
||||
struct hts221_transfer_buffer {
|
||||
u8 rx_buf[HTS221_RX_MAX_LENGTH];
|
||||
u8 tx_buf[HTS221_TX_MAX_LENGTH] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
struct hts221_transfer_function {
|
||||
int (*read)(struct device *dev, u8 addr, int len, u8 *data);
|
||||
int (*write)(struct device *dev, u8 addr, int len, u8 *data);
|
||||
};
|
||||
|
||||
enum hts221_sensor_type {
|
||||
HTS221_SENSOR_H,
|
||||
HTS221_SENSOR_T,
|
||||
|
@ -44,8 +31,8 @@ struct hts221_sensor {
|
|||
struct hts221_hw {
|
||||
const char *name;
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
|
||||
struct mutex lock;
|
||||
struct iio_trigger *trig;
|
||||
int irq;
|
||||
|
||||
|
@ -53,16 +40,12 @@ struct hts221_hw {
|
|||
|
||||
bool enabled;
|
||||
u8 odr;
|
||||
|
||||
const struct hts221_transfer_function *tf;
|
||||
struct hts221_transfer_buffer tb;
|
||||
};
|
||||
|
||||
extern const struct dev_pm_ops hts221_pm_ops;
|
||||
|
||||
int hts221_write_with_mask(struct hts221_hw *hw, u8 addr, u8 mask, u8 val);
|
||||
int hts221_probe(struct device *dev, int irq, const char *name,
|
||||
const struct hts221_transfer_function *tf_ops);
|
||||
struct regmap *regmap);
|
||||
int hts221_set_enable(struct hts221_hw *hw, bool enable);
|
||||
int hts221_allocate_buffers(struct hts221_hw *hw);
|
||||
int hts221_allocate_trigger(struct hts221_hw *hw);
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqreturn.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/bitfield.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
|
@ -38,12 +40,10 @@ static int hts221_trig_set_state(struct iio_trigger *trig, bool state)
|
|||
{
|
||||
struct iio_dev *iio_dev = iio_trigger_get_drvdata(trig);
|
||||
struct hts221_hw *hw = iio_priv(iio_dev);
|
||||
int err;
|
||||
|
||||
err = hts221_write_with_mask(hw, HTS221_REG_DRDY_EN_ADDR,
|
||||
HTS221_REG_DRDY_EN_MASK, state);
|
||||
|
||||
return err < 0 ? err : 0;
|
||||
return regmap_update_bits(hw->regmap, HTS221_REG_DRDY_EN_ADDR,
|
||||
HTS221_REG_DRDY_EN_MASK,
|
||||
FIELD_PREP(HTS221_REG_DRDY_EN_MASK, state));
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops hts221_trigger_ops = {
|
||||
|
@ -53,15 +53,13 @@ static const struct iio_trigger_ops hts221_trigger_ops = {
|
|||
static irqreturn_t hts221_trigger_handler_thread(int irq, void *private)
|
||||
{
|
||||
struct hts221_hw *hw = private;
|
||||
u8 status;
|
||||
int err;
|
||||
int err, status;
|
||||
|
||||
err = hw->tf->read(hw->dev, HTS221_REG_STATUS_ADDR, sizeof(status),
|
||||
&status);
|
||||
err = regmap_read(hw->regmap, HTS221_REG_STATUS_ADDR, &status);
|
||||
if (err < 0)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
/*
|
||||
/*
|
||||
* H_DA bit (humidity data available) is routed to DRDY line.
|
||||
* Humidity sample is computed after temperature one.
|
||||
* Here we can assume data channels are both available if H_DA bit
|
||||
|
@ -102,8 +100,10 @@ int hts221_allocate_trigger(struct hts221_hw *hw)
|
|||
break;
|
||||
}
|
||||
|
||||
err = hts221_write_with_mask(hw, HTS221_REG_DRDY_HL_ADDR,
|
||||
HTS221_REG_DRDY_HL_MASK, irq_active_low);
|
||||
err = regmap_update_bits(hw->regmap, HTS221_REG_DRDY_HL_ADDR,
|
||||
HTS221_REG_DRDY_HL_MASK,
|
||||
FIELD_PREP(HTS221_REG_DRDY_HL_MASK,
|
||||
irq_active_low));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -114,9 +114,10 @@ int hts221_allocate_trigger(struct hts221_hw *hw)
|
|||
open_drain = true;
|
||||
}
|
||||
|
||||
err = hts221_write_with_mask(hw, HTS221_REG_DRDY_PP_OD_ADDR,
|
||||
HTS221_REG_DRDY_PP_OD_MASK,
|
||||
open_drain);
|
||||
err = regmap_update_bits(hw->regmap, HTS221_REG_DRDY_PP_OD_ADDR,
|
||||
HTS221_REG_DRDY_PP_OD_MASK,
|
||||
FIELD_PREP(HTS221_REG_DRDY_PP_OD_MASK,
|
||||
open_drain));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -171,15 +172,15 @@ static irqreturn_t hts221_buffer_handler_thread(int irq, void *p)
|
|||
|
||||
/* humidity data */
|
||||
ch = &iio_dev->channels[HTS221_SENSOR_H];
|
||||
err = hw->tf->read(hw->dev, ch->address, HTS221_DATA_SIZE,
|
||||
buffer);
|
||||
err = regmap_bulk_read(hw->regmap, ch->address,
|
||||
buffer, HTS221_DATA_SIZE);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
/* temperature data */
|
||||
ch = &iio_dev->channels[HTS221_SENSOR_T];
|
||||
err = hw->tf->read(hw->dev, ch->address, HTS221_DATA_SIZE,
|
||||
buffer + HTS221_DATA_SIZE);
|
||||
err = regmap_bulk_read(hw->regmap, ch->address,
|
||||
buffer + HTS221_DATA_SIZE, HTS221_DATA_SIZE);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pm.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/bitfield.h>
|
||||
|
||||
#include "hts221.h"
|
||||
|
||||
|
@ -131,38 +132,11 @@ static const struct iio_chan_spec hts221_channels[] = {
|
|||
IIO_CHAN_SOFT_TIMESTAMP(2),
|
||||
};
|
||||
|
||||
int hts221_write_with_mask(struct hts221_hw *hw, u8 addr, u8 mask, u8 val)
|
||||
{
|
||||
u8 data;
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->lock);
|
||||
|
||||
err = hw->tf->read(hw->dev, addr, sizeof(data), &data);
|
||||
if (err < 0) {
|
||||
dev_err(hw->dev, "failed to read %02x register\n", addr);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
data = (data & ~mask) | ((val << __ffs(mask)) & mask);
|
||||
|
||||
err = hw->tf->write(hw->dev, addr, sizeof(data), &data);
|
||||
if (err < 0)
|
||||
dev_err(hw->dev, "failed to write %02x register\n", addr);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&hw->lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int hts221_check_whoami(struct hts221_hw *hw)
|
||||
{
|
||||
u8 data;
|
||||
int err;
|
||||
int err, data;
|
||||
|
||||
err = hw->tf->read(hw->dev, HTS221_REG_WHOAMI_ADDR, sizeof(data),
|
||||
&data);
|
||||
err = regmap_read(hw->regmap, HTS221_REG_WHOAMI_ADDR, &data);
|
||||
if (err < 0) {
|
||||
dev_err(hw->dev, "failed to read whoami register\n");
|
||||
return err;
|
||||
|
@ -188,8 +162,10 @@ static int hts221_update_odr(struct hts221_hw *hw, u8 odr)
|
|||
if (i == ARRAY_SIZE(hts221_odr_table))
|
||||
return -EINVAL;
|
||||
|
||||
err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR,
|
||||
HTS221_ODR_MASK, hts221_odr_table[i].val);
|
||||
err = regmap_update_bits(hw->regmap, HTS221_REG_CNTRL1_ADDR,
|
||||
HTS221_ODR_MASK,
|
||||
FIELD_PREP(HTS221_ODR_MASK,
|
||||
hts221_odr_table[i].val));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -202,8 +178,8 @@ static int hts221_update_avg(struct hts221_hw *hw,
|
|||
enum hts221_sensor_type type,
|
||||
u16 val)
|
||||
{
|
||||
int i, err;
|
||||
const struct hts221_avg *avg = &hts221_avg_list[type];
|
||||
int i, err, data;
|
||||
|
||||
for (i = 0; i < HTS221_AVG_DEPTH; i++)
|
||||
if (avg->avg_avl[i] == val)
|
||||
|
@ -212,7 +188,9 @@ static int hts221_update_avg(struct hts221_hw *hw,
|
|||
if (i == HTS221_AVG_DEPTH)
|
||||
return -EINVAL;
|
||||
|
||||
err = hts221_write_with_mask(hw, avg->addr, avg->mask, i);
|
||||
data = ((i << __ffs(avg->mask)) & avg->mask);
|
||||
err = regmap_update_bits(hw->regmap, avg->addr,
|
||||
avg->mask, data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -274,8 +252,9 @@ int hts221_set_enable(struct hts221_hw *hw, bool enable)
|
|||
{
|
||||
int err;
|
||||
|
||||
err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR,
|
||||
HTS221_ENABLE_MASK, enable);
|
||||
err = regmap_update_bits(hw->regmap, HTS221_REG_CNTRL1_ADDR,
|
||||
HTS221_ENABLE_MASK,
|
||||
FIELD_PREP(HTS221_ENABLE_MASK, enable));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -286,38 +265,35 @@ int hts221_set_enable(struct hts221_hw *hw, bool enable)
|
|||
|
||||
static int hts221_parse_temp_caldata(struct hts221_hw *hw)
|
||||
{
|
||||
int err, *slope, *b_gen;
|
||||
int err, *slope, *b_gen, cal0, cal1;
|
||||
s16 cal_x0, cal_x1, cal_y0, cal_y1;
|
||||
u8 cal0, cal1;
|
||||
__le16 val;
|
||||
|
||||
err = hw->tf->read(hw->dev, HTS221_REG_0T_CAL_Y_H,
|
||||
sizeof(cal0), &cal0);
|
||||
err = regmap_read(hw->regmap, HTS221_REG_0T_CAL_Y_H, &cal0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = hw->tf->read(hw->dev, HTS221_REG_T1_T0_CAL_Y_H,
|
||||
sizeof(cal1), &cal1);
|
||||
err = regmap_read(hw->regmap, HTS221_REG_T1_T0_CAL_Y_H, &cal1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
cal_y0 = (le16_to_cpu(cal1 & 0x3) << 8) | cal0;
|
||||
cal_y0 = ((cal1 & 0x3) << 8) | cal0;
|
||||
|
||||
err = hw->tf->read(hw->dev, HTS221_REG_1T_CAL_Y_H,
|
||||
sizeof(cal0), &cal0);
|
||||
err = regmap_read(hw->regmap, HTS221_REG_1T_CAL_Y_H, &cal0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
cal_y1 = (((cal1 & 0xc) >> 2) << 8) | cal0;
|
||||
|
||||
err = hw->tf->read(hw->dev, HTS221_REG_0T_CAL_X_L, sizeof(cal_x0),
|
||||
(u8 *)&cal_x0);
|
||||
err = regmap_bulk_read(hw->regmap, HTS221_REG_0T_CAL_X_L,
|
||||
&val, sizeof(val));
|
||||
if (err < 0)
|
||||
return err;
|
||||
cal_x0 = le16_to_cpu(cal_x0);
|
||||
cal_x0 = le16_to_cpu(val);
|
||||
|
||||
err = hw->tf->read(hw->dev, HTS221_REG_1T_CAL_X_L, sizeof(cal_x1),
|
||||
(u8 *)&cal_x1);
|
||||
err = regmap_bulk_read(hw->regmap, HTS221_REG_1T_CAL_X_L,
|
||||
&val, sizeof(val));
|
||||
if (err < 0)
|
||||
return err;
|
||||
cal_x1 = le16_to_cpu(cal_x1);
|
||||
cal_x1 = le16_to_cpu(val);
|
||||
|
||||
slope = &hw->sensors[HTS221_SENSOR_T].slope;
|
||||
b_gen = &hw->sensors[HTS221_SENSOR_T].b_gen;
|
||||
|
@ -332,33 +308,31 @@ static int hts221_parse_temp_caldata(struct hts221_hw *hw)
|
|||
|
||||
static int hts221_parse_rh_caldata(struct hts221_hw *hw)
|
||||
{
|
||||
int err, *slope, *b_gen;
|
||||
int err, *slope, *b_gen, data;
|
||||
s16 cal_x0, cal_x1, cal_y0, cal_y1;
|
||||
u8 data;
|
||||
__le16 val;
|
||||
|
||||
err = hw->tf->read(hw->dev, HTS221_REG_0RH_CAL_Y_H, sizeof(data),
|
||||
&data);
|
||||
err = regmap_read(hw->regmap, HTS221_REG_0RH_CAL_Y_H, &data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
cal_y0 = data;
|
||||
|
||||
err = hw->tf->read(hw->dev, HTS221_REG_1RH_CAL_Y_H, sizeof(data),
|
||||
&data);
|
||||
err = regmap_read(hw->regmap, HTS221_REG_1RH_CAL_Y_H, &data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
cal_y1 = data;
|
||||
|
||||
err = hw->tf->read(hw->dev, HTS221_REG_0RH_CAL_X_H, sizeof(cal_x0),
|
||||
(u8 *)&cal_x0);
|
||||
err = regmap_bulk_read(hw->regmap, HTS221_REG_0RH_CAL_X_H,
|
||||
&val, sizeof(val));
|
||||
if (err < 0)
|
||||
return err;
|
||||
cal_x0 = le16_to_cpu(cal_x0);
|
||||
cal_x0 = le16_to_cpu(val);
|
||||
|
||||
err = hw->tf->read(hw->dev, HTS221_REG_1RH_CAL_X_H, sizeof(cal_x1),
|
||||
(u8 *)&cal_x1);
|
||||
err = regmap_bulk_read(hw->regmap, HTS221_REG_1RH_CAL_X_H,
|
||||
&val, sizeof(val));
|
||||
if (err < 0)
|
||||
return err;
|
||||
cal_x1 = le16_to_cpu(cal_x1);
|
||||
cal_x1 = le16_to_cpu(val);
|
||||
|
||||
slope = &hw->sensors[HTS221_SENSOR_H].slope;
|
||||
b_gen = &hw->sensors[HTS221_SENSOR_H].b_gen;
|
||||
|
@ -431,7 +405,7 @@ static int hts221_get_sensor_offset(struct hts221_hw *hw,
|
|||
|
||||
static int hts221_read_oneshot(struct hts221_hw *hw, u8 addr, int *val)
|
||||
{
|
||||
u8 data[HTS221_DATA_SIZE];
|
||||
__le16 data;
|
||||
int err;
|
||||
|
||||
err = hts221_set_enable(hw, true);
|
||||
|
@ -440,13 +414,13 @@ static int hts221_read_oneshot(struct hts221_hw *hw, u8 addr, int *val)
|
|||
|
||||
msleep(50);
|
||||
|
||||
err = hw->tf->read(hw->dev, addr, sizeof(data), data);
|
||||
err = regmap_bulk_read(hw->regmap, addr, &data, sizeof(data));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
hts221_set_enable(hw, false);
|
||||
|
||||
*val = (s16)get_unaligned_le16(data);
|
||||
*val = (s16)le16_to_cpu(data);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
@ -582,7 +556,7 @@ static const struct iio_info hts221_info = {
|
|||
static const unsigned long hts221_scan_masks[] = {0x3, 0x0};
|
||||
|
||||
int hts221_probe(struct device *dev, int irq, const char *name,
|
||||
const struct hts221_transfer_function *tf_ops)
|
||||
struct regmap *regmap)
|
||||
{
|
||||
struct iio_dev *iio_dev;
|
||||
struct hts221_hw *hw;
|
||||
|
@ -599,9 +573,7 @@ int hts221_probe(struct device *dev, int irq, const char *name,
|
|||
hw->name = name;
|
||||
hw->dev = dev;
|
||||
hw->irq = irq;
|
||||
hw->tf = tf_ops;
|
||||
|
||||
mutex_init(&hw->lock);
|
||||
hw->regmap = regmap;
|
||||
|
||||
err = hts221_check_whoami(hw);
|
||||
if (err < 0)
|
||||
|
@ -616,8 +588,9 @@ int hts221_probe(struct device *dev, int irq, const char *name,
|
|||
iio_dev->info = &hts221_info;
|
||||
|
||||
/* enable Block Data Update */
|
||||
err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR,
|
||||
HTS221_BDU_MASK, 1);
|
||||
err = regmap_update_bits(hw->regmap, HTS221_REG_CNTRL1_ADDR,
|
||||
HTS221_BDU_MASK,
|
||||
FIELD_PREP(HTS221_BDU_MASK, 1));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -673,12 +646,10 @@ static int __maybe_unused hts221_suspend(struct device *dev)
|
|||
{
|
||||
struct iio_dev *iio_dev = dev_get_drvdata(dev);
|
||||
struct hts221_hw *hw = iio_priv(iio_dev);
|
||||
int err;
|
||||
|
||||
err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR,
|
||||
HTS221_ENABLE_MASK, false);
|
||||
|
||||
return err < 0 ? err : 0;
|
||||
return regmap_update_bits(hw->regmap, HTS221_REG_CNTRL1_ADDR,
|
||||
HTS221_ENABLE_MASK,
|
||||
FIELD_PREP(HTS221_ENABLE_MASK, false));
|
||||
}
|
||||
|
||||
static int __maybe_unused hts221_resume(struct device *dev)
|
||||
|
@ -688,9 +659,10 @@ static int __maybe_unused hts221_resume(struct device *dev)
|
|||
int err = 0;
|
||||
|
||||
if (hw->enabled)
|
||||
err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR,
|
||||
HTS221_ENABLE_MASK, true);
|
||||
|
||||
err = regmap_update_bits(hw->regmap, HTS221_REG_CNTRL1_ADDR,
|
||||
HTS221_ENABLE_MASK,
|
||||
FIELD_PREP(HTS221_ENABLE_MASK,
|
||||
true));
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,61 +13,33 @@
|
|||
#include <linux/acpi.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "hts221.h"
|
||||
|
||||
#define I2C_AUTO_INCREMENT 0x80
|
||||
#define HTS221_I2C_AUTO_INCREMENT BIT(7)
|
||||
|
||||
static int hts221_i2c_read(struct device *dev, u8 addr, int len, u8 *data)
|
||||
{
|
||||
struct i2c_msg msg[2];
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
||||
if (len > 1)
|
||||
addr |= I2C_AUTO_INCREMENT;
|
||||
|
||||
msg[0].addr = client->addr;
|
||||
msg[0].flags = client->flags;
|
||||
msg[0].len = 1;
|
||||
msg[0].buf = &addr;
|
||||
|
||||
msg[1].addr = client->addr;
|
||||
msg[1].flags = client->flags | I2C_M_RD;
|
||||
msg[1].len = len;
|
||||
msg[1].buf = data;
|
||||
|
||||
return i2c_transfer(client->adapter, msg, 2);
|
||||
}
|
||||
|
||||
static int hts221_i2c_write(struct device *dev, u8 addr, int len, u8 *data)
|
||||
{
|
||||
u8 send[len + 1];
|
||||
struct i2c_msg msg;
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
||||
if (len > 1)
|
||||
addr |= I2C_AUTO_INCREMENT;
|
||||
|
||||
send[0] = addr;
|
||||
memcpy(&send[1], data, len * sizeof(u8));
|
||||
|
||||
msg.addr = client->addr;
|
||||
msg.flags = client->flags;
|
||||
msg.len = len + 1;
|
||||
msg.buf = send;
|
||||
|
||||
return i2c_transfer(client->adapter, &msg, 1);
|
||||
}
|
||||
|
||||
static const struct hts221_transfer_function hts221_transfer_fn = {
|
||||
.read = hts221_i2c_read,
|
||||
.write = hts221_i2c_write,
|
||||
static const struct regmap_config hts221_i2c_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.write_flag_mask = HTS221_I2C_AUTO_INCREMENT,
|
||||
.read_flag_mask = HTS221_I2C_AUTO_INCREMENT,
|
||||
};
|
||||
|
||||
static int hts221_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = devm_regmap_init_i2c(client, &hts221_i2c_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&client->dev, "Failed to register i2c regmap %d\n",
|
||||
(int)PTR_ERR(regmap));
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
return hts221_probe(&client->dev, client->irq,
|
||||
client->name, &hts221_transfer_fn);
|
||||
client->name, regmap);
|
||||
}
|
||||
|
||||
static const struct acpi_device_id hts221_acpi_match[] = {
|
||||
|
|
|
@ -12,76 +12,33 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "hts221.h"
|
||||
|
||||
#define SENSORS_SPI_READ 0x80
|
||||
#define SPI_AUTO_INCREMENT 0x40
|
||||
#define HTS221_SPI_READ BIT(7)
|
||||
#define HTS221_SPI_AUTO_INCREMENT BIT(6)
|
||||
|
||||
static int hts221_spi_read(struct device *dev, u8 addr, int len, u8 *data)
|
||||
{
|
||||
int err;
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct iio_dev *iio_dev = spi_get_drvdata(spi);
|
||||
struct hts221_hw *hw = iio_priv(iio_dev);
|
||||
|
||||
struct spi_transfer xfers[] = {
|
||||
{
|
||||
.tx_buf = hw->tb.tx_buf,
|
||||
.bits_per_word = 8,
|
||||
.len = 1,
|
||||
},
|
||||
{
|
||||
.rx_buf = hw->tb.rx_buf,
|
||||
.bits_per_word = 8,
|
||||
.len = len,
|
||||
}
|
||||
};
|
||||
|
||||
if (len > 1)
|
||||
addr |= SPI_AUTO_INCREMENT;
|
||||
hw->tb.tx_buf[0] = addr | SENSORS_SPI_READ;
|
||||
|
||||
err = spi_sync_transfer(spi, xfers, ARRAY_SIZE(xfers));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
memcpy(data, hw->tb.rx_buf, len * sizeof(u8));
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int hts221_spi_write(struct device *dev, u8 addr, int len, u8 *data)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct iio_dev *iio_dev = spi_get_drvdata(spi);
|
||||
struct hts221_hw *hw = iio_priv(iio_dev);
|
||||
|
||||
struct spi_transfer xfers = {
|
||||
.tx_buf = hw->tb.tx_buf,
|
||||
.bits_per_word = 8,
|
||||
.len = len + 1,
|
||||
};
|
||||
|
||||
if (len >= HTS221_TX_MAX_LENGTH)
|
||||
return -ENOMEM;
|
||||
|
||||
if (len > 1)
|
||||
addr |= SPI_AUTO_INCREMENT;
|
||||
hw->tb.tx_buf[0] = addr;
|
||||
memcpy(&hw->tb.tx_buf[1], data, len);
|
||||
|
||||
return spi_sync_transfer(spi, &xfers, 1);
|
||||
}
|
||||
|
||||
static const struct hts221_transfer_function hts221_transfer_fn = {
|
||||
.read = hts221_spi_read,
|
||||
.write = hts221_spi_write,
|
||||
static const struct regmap_config hts221_spi_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.write_flag_mask = HTS221_SPI_AUTO_INCREMENT,
|
||||
.read_flag_mask = HTS221_SPI_READ | HTS221_SPI_AUTO_INCREMENT,
|
||||
};
|
||||
|
||||
static int hts221_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = devm_regmap_init_spi(spi, &hts221_spi_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&spi->dev, "Failed to register spi regmap %d\n",
|
||||
(int)PTR_ERR(regmap));
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
return hts221_probe(&spi->dev, spi->irq,
|
||||
spi->modalias, &hts221_transfer_fn);
|
||||
spi->modalias, regmap);
|
||||
}
|
||||
|
||||
static const struct of_device_id hts221_spi_of_match[] = {
|
||||
|
|
|
@ -27,7 +27,7 @@ enum st_lsm6dsx_hw_id {
|
|||
ST_LSM6DSX_MAX_ID,
|
||||
};
|
||||
|
||||
#define ST_LSM6DSX_BUFF_SIZE 256
|
||||
#define ST_LSM6DSX_BUFF_SIZE 400
|
||||
#define ST_LSM6DSX_CHAN_SIZE 2
|
||||
#define ST_LSM6DSX_SAMPLE_SIZE 6
|
||||
#define ST_LSM6DSX_MAX_WORD_LEN ((32 / ST_LSM6DSX_SAMPLE_SIZE) * \
|
||||
|
@ -57,6 +57,20 @@ struct st_lsm6dsx_fifo_ops {
|
|||
u8 th_wl;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_lsm6dsx_hw_ts_settings - ST IMU hw timer settings
|
||||
* @timer_en: Hw timer enable register info (addr + mask).
|
||||
* @hr_timer: Hw timer resolution register info (addr + mask).
|
||||
* @fifo_en: Hw timer FIFO enable register info (addr + mask).
|
||||
* @decimator: Hw timer FIFO decimator register info (addr + mask).
|
||||
*/
|
||||
struct st_lsm6dsx_hw_ts_settings {
|
||||
struct st_lsm6dsx_reg timer_en;
|
||||
struct st_lsm6dsx_reg hr_timer;
|
||||
struct st_lsm6dsx_reg fifo_en;
|
||||
struct st_lsm6dsx_reg decimator;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct st_lsm6dsx_settings - ST IMU sensor settings
|
||||
* @wai: Sensor WhoAmI default value.
|
||||
|
@ -64,6 +78,7 @@ struct st_lsm6dsx_fifo_ops {
|
|||
* @id: List of hw id supported by the driver configuration.
|
||||
* @decimator: List of decimator register info (addr + mask).
|
||||
* @fifo_ops: Sensor hw FIFO parameters.
|
||||
* @ts_settings: Hw timer related settings.
|
||||
*/
|
||||
struct st_lsm6dsx_settings {
|
||||
u8 wai;
|
||||
|
@ -71,6 +86,7 @@ struct st_lsm6dsx_settings {
|
|||
enum st_lsm6dsx_hw_id id[ST_LSM6DSX_MAX_ID];
|
||||
struct st_lsm6dsx_reg decimator[ST_LSM6DSX_MAX_ID];
|
||||
struct st_lsm6dsx_fifo_ops fifo_ops;
|
||||
struct st_lsm6dsx_hw_ts_settings ts_settings;
|
||||
};
|
||||
|
||||
enum st_lsm6dsx_sensor_id {
|
||||
|
@ -94,8 +110,7 @@ enum st_lsm6dsx_fifo_mode {
|
|||
* @watermark: Sensor watermark level.
|
||||
* @sip: Number of samples in a given pattern.
|
||||
* @decimator: FIFO decimation factor.
|
||||
* @delta_ts: Delta time between two consecutive interrupts.
|
||||
* @ts: Latest timestamp from the interrupt handler.
|
||||
* @ts_ref: Sensor timestamp reference for hw one.
|
||||
*/
|
||||
struct st_lsm6dsx_sensor {
|
||||
char name[32];
|
||||
|
@ -108,9 +123,7 @@ struct st_lsm6dsx_sensor {
|
|||
u16 watermark;
|
||||
u8 sip;
|
||||
u8 decimator;
|
||||
|
||||
s64 delta_ts;
|
||||
s64 ts;
|
||||
s64 ts_ref;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -122,7 +135,8 @@ struct st_lsm6dsx_sensor {
|
|||
* @conf_lock: Mutex to prevent concurrent FIFO configuration update.
|
||||
* @fifo_mode: FIFO operating mode supported by the device.
|
||||
* @enable_mask: Enabled sensor bitmask.
|
||||
* @sip: Total number of samples (acc/gyro) in a given pattern.
|
||||
* @ts_sip: Total number of timestamp samples in a given pattern.
|
||||
* @sip: Total number of samples (acc/gyro/ts) in a given pattern.
|
||||
* @buff: Device read buffer.
|
||||
* @iio_devs: Pointers to acc/gyro iio_dev instances.
|
||||
* @settings: Pointer to the specific sensor settings in use.
|
||||
|
@ -137,6 +151,7 @@ struct st_lsm6dsx_hw {
|
|||
|
||||
enum st_lsm6dsx_fifo_mode fifo_mode;
|
||||
u8 enable_mask;
|
||||
u8 ts_sip;
|
||||
u8 sip;
|
||||
|
||||
u8 *buff;
|
||||
|
|
|
@ -46,9 +46,13 @@
|
|||
#define ST_LSM6DSX_FIFO_ODR_MASK GENMASK(6, 3)
|
||||
#define ST_LSM6DSX_FIFO_EMPTY_MASK BIT(12)
|
||||
#define ST_LSM6DSX_REG_FIFO_OUTL_ADDR 0x3e
|
||||
#define ST_LSM6DSX_REG_TS_RESET_ADDR 0x42
|
||||
|
||||
#define ST_LSM6DSX_MAX_FIFO_ODR_VAL 0x08
|
||||
|
||||
#define ST_LSM6DSX_TS_SENSITIVITY 25000UL /* 25us */
|
||||
#define ST_LSM6DSX_TS_RESET_VAL 0xaa
|
||||
|
||||
struct st_lsm6dsx_decimator_entry {
|
||||
u8 decimator;
|
||||
u8 val;
|
||||
|
@ -98,9 +102,10 @@ static void st_lsm6dsx_get_max_min_odr(struct st_lsm6dsx_hw *hw,
|
|||
|
||||
static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw)
|
||||
{
|
||||
u16 max_odr, min_odr, sip = 0, ts_sip = 0;
|
||||
const struct st_lsm6dsx_reg *ts_dec_reg;
|
||||
struct st_lsm6dsx_sensor *sensor;
|
||||
u16 max_odr, min_odr, sip = 0;
|
||||
int err, i;
|
||||
int err = 0, i;
|
||||
u8 data;
|
||||
|
||||
st_lsm6dsx_get_max_min_odr(hw, &max_odr, &min_odr);
|
||||
|
@ -119,6 +124,7 @@ static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw)
|
|||
sensor->decimator = 0;
|
||||
data = 0;
|
||||
}
|
||||
ts_sip = max_t(u16, ts_sip, sensor->sip);
|
||||
|
||||
dec_reg = &hw->settings->decimator[sensor->id];
|
||||
if (dec_reg->addr) {
|
||||
|
@ -131,9 +137,23 @@ static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw)
|
|||
}
|
||||
sip += sensor->sip;
|
||||
}
|
||||
hw->sip = sip;
|
||||
hw->sip = sip + ts_sip;
|
||||
hw->ts_sip = ts_sip;
|
||||
|
||||
return 0;
|
||||
/*
|
||||
* update hw ts decimator if necessary. Decimator for hw timestamp
|
||||
* is always 1 or 0 in order to have a ts sample for each data
|
||||
* sample in FIFO
|
||||
*/
|
||||
ts_dec_reg = &hw->settings->ts_settings.decimator;
|
||||
if (ts_dec_reg->addr) {
|
||||
int val, ts_dec = !!hw->ts_sip;
|
||||
|
||||
val = ST_LSM6DSX_SHIFT_VAL(ts_dec, ts_dec_reg->mask);
|
||||
err = regmap_update_bits(hw->regmap, ts_dec_reg->addr,
|
||||
ts_dec_reg->mask, val);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int st_lsm6dsx_set_fifo_mode(struct st_lsm6dsx_hw *hw,
|
||||
|
@ -208,6 +228,28 @@ int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, u16 watermark)
|
|||
&wdata, sizeof(wdata));
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_reset_hw_ts(struct st_lsm6dsx_hw *hw)
|
||||
{
|
||||
struct st_lsm6dsx_sensor *sensor;
|
||||
int i, err;
|
||||
|
||||
/* reset hw ts counter */
|
||||
err = regmap_write(hw->regmap, ST_LSM6DSX_REG_TS_RESET_ADDR,
|
||||
ST_LSM6DSX_TS_RESET_VAL);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
|
||||
sensor = iio_priv(hw->iio_devs[i]);
|
||||
/*
|
||||
* store enable buffer timestamp as reference for
|
||||
* hw timestamp
|
||||
*/
|
||||
sensor->ts_ref = iio_get_time_ns(hw->iio_devs[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set max bulk read to ST_LSM6DSX_MAX_WORD_LEN in order to avoid
|
||||
* a kmalloc for each bus access
|
||||
|
@ -231,6 +273,8 @@ static inline int st_lsm6dsx_read_block(struct st_lsm6dsx_hw *hw, u8 *data,
|
|||
return 0;
|
||||
}
|
||||
|
||||
#define ST_LSM6DSX_IIO_BUFF_SIZE (ALIGN(ST_LSM6DSX_SAMPLE_SIZE, \
|
||||
sizeof(s64)) + sizeof(s64))
|
||||
/**
|
||||
* st_lsm6dsx_read_fifo() - LSM6DS3-LSM6DS3H-LSM6DSL-LSM6DSM read FIFO routine
|
||||
* @hw: Pointer to instance of struct st_lsm6dsx_hw.
|
||||
|
@ -243,11 +287,13 @@ static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
|
|||
{
|
||||
u16 fifo_len, pattern_len = hw->sip * ST_LSM6DSX_SAMPLE_SIZE;
|
||||
u16 fifo_diff_mask = hw->settings->fifo_ops.fifo_diff.mask;
|
||||
int err, acc_sip, gyro_sip, read_len, samples, offset;
|
||||
int err, acc_sip, gyro_sip, ts_sip, read_len, offset;
|
||||
struct st_lsm6dsx_sensor *acc_sensor, *gyro_sensor;
|
||||
s64 acc_ts, acc_delta_ts, gyro_ts, gyro_delta_ts;
|
||||
u8 iio_buff[ALIGN(ST_LSM6DSX_SAMPLE_SIZE, sizeof(s64)) + sizeof(s64)];
|
||||
u8 gyro_buff[ST_LSM6DSX_IIO_BUFF_SIZE];
|
||||
u8 acc_buff[ST_LSM6DSX_IIO_BUFF_SIZE];
|
||||
bool reset_ts = false;
|
||||
__le16 fifo_status;
|
||||
s64 ts = 0;
|
||||
|
||||
err = regmap_bulk_read(hw->regmap,
|
||||
hw->settings->fifo_ops.fifo_diff.addr,
|
||||
|
@ -260,23 +306,10 @@ static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
|
|||
|
||||
fifo_len = (le16_to_cpu(fifo_status) & fifo_diff_mask) *
|
||||
ST_LSM6DSX_CHAN_SIZE;
|
||||
samples = fifo_len / ST_LSM6DSX_SAMPLE_SIZE;
|
||||
fifo_len = (fifo_len / pattern_len) * pattern_len;
|
||||
|
||||
/*
|
||||
* compute delta timestamp between two consecutive samples
|
||||
* in order to estimate queueing time of data generated
|
||||
* by the sensor
|
||||
*/
|
||||
acc_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
|
||||
acc_ts = acc_sensor->ts - acc_sensor->delta_ts;
|
||||
acc_delta_ts = div_s64(acc_sensor->delta_ts * acc_sensor->decimator,
|
||||
samples);
|
||||
|
||||
gyro_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_GYRO]);
|
||||
gyro_ts = gyro_sensor->ts - gyro_sensor->delta_ts;
|
||||
gyro_delta_ts = div_s64(gyro_sensor->delta_ts * gyro_sensor->decimator,
|
||||
samples);
|
||||
|
||||
for (read_len = 0; read_len < fifo_len; read_len += pattern_len) {
|
||||
err = st_lsm6dsx_read_block(hw, hw->buff, pattern_len);
|
||||
|
@ -287,7 +320,7 @@ static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
|
|||
* Data are written to the FIFO with a specific pattern
|
||||
* depending on the configured ODRs. The first sequence of data
|
||||
* stored in FIFO contains the data of all enabled sensors
|
||||
* (e.g. Gx, Gy, Gz, Ax, Ay, Az), then data are repeated
|
||||
* (e.g. Gx, Gy, Gz, Ax, Ay, Az, Ts), then data are repeated
|
||||
* depending on the value of the decimation factor set for each
|
||||
* sensor.
|
||||
*
|
||||
|
@ -296,35 +329,65 @@ static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
|
|||
* - gyroscope ODR = 208Hz, accelerometer ODR = 104Hz
|
||||
* Since the gyroscope ODR is twice the accelerometer one, the
|
||||
* following pattern is repeated every 9 samples:
|
||||
* - Gx, Gy, Gz, Ax, Ay, Az, Gx, Gy, Gz
|
||||
* - Gx, Gy, Gz, Ax, Ay, Az, Ts, Gx, Gy, Gz, Ts, Gx, ..
|
||||
*/
|
||||
gyro_sip = gyro_sensor->sip;
|
||||
acc_sip = acc_sensor->sip;
|
||||
ts_sip = hw->ts_sip;
|
||||
offset = 0;
|
||||
|
||||
while (acc_sip > 0 || gyro_sip > 0) {
|
||||
if (gyro_sip-- > 0) {
|
||||
memcpy(iio_buff, &hw->buff[offset],
|
||||
if (gyro_sip > 0) {
|
||||
memcpy(gyro_buff, &hw->buff[offset],
|
||||
ST_LSM6DSX_SAMPLE_SIZE);
|
||||
offset += ST_LSM6DSX_SAMPLE_SIZE;
|
||||
}
|
||||
if (acc_sip > 0) {
|
||||
memcpy(acc_buff, &hw->buff[offset],
|
||||
ST_LSM6DSX_SAMPLE_SIZE);
|
||||
iio_push_to_buffers_with_timestamp(
|
||||
hw->iio_devs[ST_LSM6DSX_ID_GYRO],
|
||||
iio_buff, gyro_ts);
|
||||
offset += ST_LSM6DSX_SAMPLE_SIZE;
|
||||
gyro_ts += gyro_delta_ts;
|
||||
}
|
||||
|
||||
if (acc_sip-- > 0) {
|
||||
memcpy(iio_buff, &hw->buff[offset],
|
||||
ST_LSM6DSX_SAMPLE_SIZE);
|
||||
if (ts_sip-- > 0) {
|
||||
u8 data[ST_LSM6DSX_SAMPLE_SIZE];
|
||||
|
||||
memcpy(data, &hw->buff[offset], sizeof(data));
|
||||
/*
|
||||
* hw timestamp is 3B long and it is stored
|
||||
* in FIFO using 6B as 4th FIFO data set
|
||||
* according to this schema:
|
||||
* B0 = ts[15:8], B1 = ts[23:16], B3 = ts[7:0]
|
||||
*/
|
||||
ts = data[1] << 16 | data[0] << 8 | data[3];
|
||||
/*
|
||||
* check if hw timestamp engine is going to
|
||||
* reset (the sensor generates an interrupt
|
||||
* to signal the hw timestamp will reset in
|
||||
* 1.638s)
|
||||
*/
|
||||
if (!reset_ts && ts >= 0xff0000)
|
||||
reset_ts = true;
|
||||
ts *= ST_LSM6DSX_TS_SENSITIVITY;
|
||||
|
||||
offset += ST_LSM6DSX_SAMPLE_SIZE;
|
||||
}
|
||||
|
||||
if (gyro_sip-- > 0)
|
||||
iio_push_to_buffers_with_timestamp(
|
||||
hw->iio_devs[ST_LSM6DSX_ID_GYRO],
|
||||
gyro_buff, gyro_sensor->ts_ref + ts);
|
||||
if (acc_sip-- > 0)
|
||||
iio_push_to_buffers_with_timestamp(
|
||||
hw->iio_devs[ST_LSM6DSX_ID_ACC],
|
||||
iio_buff, acc_ts);
|
||||
offset += ST_LSM6DSX_SAMPLE_SIZE;
|
||||
acc_ts += acc_delta_ts;
|
||||
}
|
||||
acc_buff, acc_sensor->ts_ref + ts);
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(reset_ts)) {
|
||||
err = st_lsm6dsx_reset_hw_ts(hw);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
return read_len;
|
||||
}
|
||||
|
||||
|
@ -379,15 +442,12 @@ static int st_lsm6dsx_update_fifo(struct iio_dev *iio_dev, bool enable)
|
|||
goto out;
|
||||
|
||||
if (hw->enable_mask) {
|
||||
err = st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_CONT);
|
||||
/* reset hw ts counter */
|
||||
err = st_lsm6dsx_reset_hw_ts(hw);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* store enable buffer timestamp as reference to compute
|
||||
* first delta timestamp
|
||||
*/
|
||||
sensor->ts = iio_get_time_ns(iio_dev);
|
||||
err = st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_CONT);
|
||||
}
|
||||
|
||||
out:
|
||||
|
@ -399,25 +459,8 @@ out:
|
|||
static irqreturn_t st_lsm6dsx_handler_irq(int irq, void *private)
|
||||
{
|
||||
struct st_lsm6dsx_hw *hw = private;
|
||||
struct st_lsm6dsx_sensor *sensor;
|
||||
int i;
|
||||
|
||||
if (!hw->sip)
|
||||
return IRQ_NONE;
|
||||
|
||||
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
|
||||
sensor = iio_priv(hw->iio_devs[i]);
|
||||
|
||||
if (sensor->sip > 0) {
|
||||
s64 timestamp;
|
||||
|
||||
timestamp = iio_get_time_ns(hw->iio_devs[i]);
|
||||
sensor->delta_ts = timestamp - sensor->ts;
|
||||
sensor->ts = timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_WAKE_THREAD;
|
||||
return hw->sip > 0 ? IRQ_WAKE_THREAD : IRQ_NONE;
|
||||
}
|
||||
|
||||
static irqreturn_t st_lsm6dsx_handler_thread(int irq, void *private)
|
||||
|
|
|
@ -181,6 +181,24 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
|
|||
},
|
||||
.th_wl = 3, /* 1LSB = 2B */
|
||||
},
|
||||
.ts_settings = {
|
||||
.timer_en = {
|
||||
.addr = 0x58,
|
||||
.mask = BIT(7),
|
||||
},
|
||||
.hr_timer = {
|
||||
.addr = 0x5c,
|
||||
.mask = BIT(4),
|
||||
},
|
||||
.fifo_en = {
|
||||
.addr = 0x07,
|
||||
.mask = BIT(7),
|
||||
},
|
||||
.decimator = {
|
||||
.addr = 0x09,
|
||||
.mask = GENMASK(5, 3),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.wai = 0x69,
|
||||
|
@ -209,6 +227,24 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
|
|||
},
|
||||
.th_wl = 3, /* 1LSB = 2B */
|
||||
},
|
||||
.ts_settings = {
|
||||
.timer_en = {
|
||||
.addr = 0x58,
|
||||
.mask = BIT(7),
|
||||
},
|
||||
.hr_timer = {
|
||||
.addr = 0x5c,
|
||||
.mask = BIT(4),
|
||||
},
|
||||
.fifo_en = {
|
||||
.addr = 0x07,
|
||||
.mask = BIT(7),
|
||||
},
|
||||
.decimator = {
|
||||
.addr = 0x09,
|
||||
.mask = GENMASK(5, 3),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.wai = 0x6a,
|
||||
|
@ -238,6 +274,24 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
|
|||
},
|
||||
.th_wl = 3, /* 1LSB = 2B */
|
||||
},
|
||||
.ts_settings = {
|
||||
.timer_en = {
|
||||
.addr = 0x19,
|
||||
.mask = BIT(5),
|
||||
},
|
||||
.hr_timer = {
|
||||
.addr = 0x5c,
|
||||
.mask = BIT(4),
|
||||
},
|
||||
.fifo_en = {
|
||||
.addr = 0x07,
|
||||
.mask = BIT(7),
|
||||
},
|
||||
.decimator = {
|
||||
.addr = 0x09,
|
||||
.mask = GENMASK(5, 3),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -630,6 +684,44 @@ static int st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, u8 *drdy_reg)
|
|||
return err;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_init_hw_timer(struct st_lsm6dsx_hw *hw)
|
||||
{
|
||||
const struct st_lsm6dsx_hw_ts_settings *ts_settings;
|
||||
int err, val;
|
||||
|
||||
ts_settings = &hw->settings->ts_settings;
|
||||
/* enable hw timestamp generation if necessary */
|
||||
if (ts_settings->timer_en.addr) {
|
||||
val = ST_LSM6DSX_SHIFT_VAL(1, ts_settings->timer_en.mask);
|
||||
err = regmap_update_bits(hw->regmap,
|
||||
ts_settings->timer_en.addr,
|
||||
ts_settings->timer_en.mask, val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* enable high resolution for hw ts timer if necessary */
|
||||
if (ts_settings->hr_timer.addr) {
|
||||
val = ST_LSM6DSX_SHIFT_VAL(1, ts_settings->hr_timer.mask);
|
||||
err = regmap_update_bits(hw->regmap,
|
||||
ts_settings->hr_timer.addr,
|
||||
ts_settings->hr_timer.mask, val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* enable ts queueing in FIFO if necessary */
|
||||
if (ts_settings->fifo_en.addr) {
|
||||
val = ST_LSM6DSX_SHIFT_VAL(1, ts_settings->fifo_en.mask);
|
||||
err = regmap_update_bits(hw->regmap,
|
||||
ts_settings->fifo_en.addr,
|
||||
ts_settings->fifo_en.mask, val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw)
|
||||
{
|
||||
u8 drdy_int_reg;
|
||||
|
@ -654,10 +746,14 @@ static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw)
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return regmap_update_bits(hw->regmap, drdy_int_reg,
|
||||
ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK,
|
||||
FIELD_PREP(ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK,
|
||||
1));
|
||||
err = regmap_update_bits(hw->regmap, drdy_int_reg,
|
||||
ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK,
|
||||
FIELD_PREP(ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK,
|
||||
1));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return st_lsm6dsx_init_hw_timer(hw);
|
||||
}
|
||||
|
||||
static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw,
|
||||
|
|
|
@ -1,17 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* apds9960.c - Support for Avago APDS9960 gesture/RGB/ALS/proximity sensor
|
||||
*
|
||||
* Copyright (C) 2015 Matt Ranostay <mranostay@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* Copyright (C) 2015, 2018
|
||||
* Author: Matt Ranostay <matt.ranostay@konsulko.com>
|
||||
*
|
||||
* TODO: gesture + proximity calib offsets
|
||||
*/
|
||||
|
@ -1141,6 +1133,6 @@ static struct i2c_driver apds9960_driver = {
|
|||
};
|
||||
module_i2c_driver(apds9960_driver);
|
||||
|
||||
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
|
||||
MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
|
||||
MODULE_DESCRIPTION("ADPS9960 Gesture/RGB/ALS/Proximity sensor");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -5,6 +5,16 @@
|
|||
|
||||
menu "Digital potentiometers"
|
||||
|
||||
config AD5272
|
||||
tristate "Analog Devices AD5272 and similar Digital Potentiometer driver"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to build support for the Analog Devices AD5272 and AD5274
|
||||
digital potentiometer chip.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad5272.
|
||||
|
||||
config DS1803
|
||||
tristate "Maxim Integrated DS1803 Digital Potentiometer driver"
|
||||
depends on I2C
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_AD5272) += ad5272.o
|
||||
obj-$(CONFIG_DS1803) += ds1803.o
|
||||
obj-$(CONFIG_MAX5481) += max5481.o
|
||||
obj-$(CONFIG_MAX5487) += max5487.o
|
||||
|
|
|
@ -0,0 +1,231 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Analog Devices AD5272 digital potentiometer driver
|
||||
* Copyright (C) 2018 Phil Reid <preid@electromag.com.au>
|
||||
*
|
||||
* Datasheet: http://www.analog.com/media/en/technical-documentation/data-sheets/AD5272_5274.pdf
|
||||
*
|
||||
* DEVID #Wipers #Positions Resistor Opts (kOhm) i2c address
|
||||
* ad5272 1 1024 20, 50, 100 01011xx
|
||||
* ad5274 1 256 20, 100 01011xx
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#define AD5272_RDAC_WR 1
|
||||
#define AD5272_RDAC_RD 2
|
||||
#define AD5272_RESET 4
|
||||
#define AD5272_CTL 7
|
||||
|
||||
#define AD5272_RDAC_WR_EN BIT(1)
|
||||
|
||||
struct ad5272_cfg {
|
||||
int max_pos;
|
||||
int kohms;
|
||||
int shift;
|
||||
};
|
||||
|
||||
enum ad5272_type {
|
||||
AD5272_020,
|
||||
AD5272_050,
|
||||
AD5272_100,
|
||||
AD5274_020,
|
||||
AD5274_100,
|
||||
};
|
||||
|
||||
static const struct ad5272_cfg ad5272_cfg[] = {
|
||||
[AD5272_020] = { .max_pos = 1024, .kohms = 20 },
|
||||
[AD5272_050] = { .max_pos = 1024, .kohms = 50 },
|
||||
[AD5272_100] = { .max_pos = 1024, .kohms = 100 },
|
||||
[AD5274_020] = { .max_pos = 256, .kohms = 20, .shift = 2 },
|
||||
[AD5274_100] = { .max_pos = 256, .kohms = 100, .shift = 2 },
|
||||
};
|
||||
|
||||
struct ad5272_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock;
|
||||
const struct ad5272_cfg *cfg;
|
||||
u8 buf[2] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec ad5272_channel = {
|
||||
.type = IIO_RESISTANCE,
|
||||
.output = 1,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
};
|
||||
|
||||
static int ad5272_write(struct ad5272_data *data, int reg, int val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
data->buf[0] = (reg << 2) | ((val >> 8) & 0x3);
|
||||
data->buf[1] = (u8)val;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
ret = i2c_master_send(data->client, data->buf, sizeof(data->buf));
|
||||
mutex_unlock(&data->lock);
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
|
||||
static int ad5272_read(struct ad5272_data *data, int reg, int *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
data->buf[0] = reg << 2;
|
||||
data->buf[1] = 0;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
ret = i2c_master_send(data->client, data->buf, sizeof(data->buf));
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
ret = i2c_master_recv(data->client, data->buf, sizeof(data->buf));
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
*val = ((data->buf[0] & 0x3) << 8) | data->buf[1];
|
||||
ret = 0;
|
||||
error:
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5272_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct ad5272_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW: {
|
||||
ret = ad5272_read(data, AD5272_RDAC_RD, val);
|
||||
*val = *val >> data->cfg->shift;
|
||||
return ret ? ret : IIO_VAL_INT;
|
||||
}
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 1000 * data->cfg->kohms;
|
||||
*val2 = data->cfg->max_pos;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad5272_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct ad5272_data *data = iio_priv(indio_dev);
|
||||
|
||||
if (mask != IIO_CHAN_INFO_RAW)
|
||||
return -EINVAL;
|
||||
|
||||
if (val >= data->cfg->max_pos || val < 0 || val2)
|
||||
return -EINVAL;
|
||||
|
||||
return ad5272_write(data, AD5272_RDAC_WR, val << data->cfg->shift);
|
||||
}
|
||||
|
||||
static const struct iio_info ad5272_info = {
|
||||
.read_raw = ad5272_read_raw,
|
||||
.write_raw = ad5272_write_raw,
|
||||
};
|
||||
|
||||
static int ad5272_reset(struct ad5272_data *data)
|
||||
{
|
||||
struct gpio_desc *reset_gpio;
|
||||
|
||||
reset_gpio = devm_gpiod_get_optional(&data->client->dev, "reset",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(reset_gpio))
|
||||
return PTR_ERR(reset_gpio);
|
||||
|
||||
if (reset_gpio) {
|
||||
udelay(1);
|
||||
gpiod_set_value(reset_gpio, 1);
|
||||
} else {
|
||||
ad5272_write(data, AD5272_RESET, 0);
|
||||
}
|
||||
usleep_range(1000, 2000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad5272_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad5272_data *data;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->client = client;
|
||||
mutex_init(&data->lock);
|
||||
data->cfg = &ad5272_cfg[id->driver_data];
|
||||
|
||||
ret = ad5272_reset(data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ad5272_write(data, AD5272_CTL, AD5272_RDAC_WR_EN);
|
||||
if (ret < 0)
|
||||
return -ENODEV;
|
||||
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->info = &ad5272_info;
|
||||
indio_dev->channels = &ad5272_channel;
|
||||
indio_dev->num_channels = 1;
|
||||
indio_dev->name = client->name;
|
||||
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
static const struct of_device_id ad5272_dt_ids[] = {
|
||||
{ .compatible = "adi,ad5272-020", .data = (void *)AD5272_020 },
|
||||
{ .compatible = "adi,ad5272-050", .data = (void *)AD5272_050 },
|
||||
{ .compatible = "adi,ad5272-100", .data = (void *)AD5272_100 },
|
||||
{ .compatible = "adi,ad5274-020", .data = (void *)AD5274_020 },
|
||||
{ .compatible = "adi,ad5274-100", .data = (void *)AD5274_100 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ad5272_dt_ids);
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
static const struct i2c_device_id ad5272_id[] = {
|
||||
{ "ad5272-020", AD5272_020 },
|
||||
{ "ad5272-050", AD5272_050 },
|
||||
{ "ad5272-100", AD5272_100 },
|
||||
{ "ad5274-020", AD5274_020 },
|
||||
{ "ad5274-100", AD5274_100 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ad5272_id);
|
||||
|
||||
static struct i2c_driver ad5272_driver = {
|
||||
.driver = {
|
||||
.name = "ad5272",
|
||||
.of_match_table = of_match_ptr(ad5272_dt_ids),
|
||||
},
|
||||
.probe = ad5272_probe,
|
||||
.id_table = ad5272_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(ad5272_driver);
|
||||
|
||||
MODULE_AUTHOR("Phil Reid <preid@eletromag.com.au>");
|
||||
MODULE_DESCRIPTION("AD5272 digital potentiometer");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -1,17 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* tpl0102.c - Support for Texas Instruments digital potentiometers
|
||||
*
|
||||
* Copyright (C) 2016 Matt Ranostay <mranostay@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* Copyright (C) 2016, 2018
|
||||
* Author: Matt Ranostay <matt.ranostay@konsulko.com>
|
||||
*
|
||||
* TODO: enable/disable hi-z output control
|
||||
*/
|
||||
|
@ -156,6 +148,6 @@ static struct i2c_driver tpl0102_driver = {
|
|||
|
||||
module_i2c_driver(tpl0102_driver);
|
||||
|
||||
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
|
||||
MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
|
||||
MODULE_DESCRIPTION("TPL0102 digital potentiometer");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -1,17 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* lmp91000.c - Support for Texas Instruments digital potentiostats
|
||||
*
|
||||
* Copyright (C) 2016 Matt Ranostay <mranostay@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* Copyright (C) 2016, 2018
|
||||
* Author: Matt Ranostay <matt.ranostay@konsulko.com>
|
||||
*
|
||||
* TODO: bias voltage + polarity control, and multiple chip support
|
||||
*/
|
||||
|
@ -440,6 +432,6 @@ static struct i2c_driver lmp91000_driver = {
|
|||
};
|
||||
module_i2c_driver(lmp91000_driver);
|
||||
|
||||
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
|
||||
MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
|
||||
MODULE_DESCRIPTION("LMP91000 digital potentiostat");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -63,7 +63,7 @@ struct ms5611_state {
|
|||
};
|
||||
|
||||
int ms5611_probe(struct iio_dev *indio_dev, struct device *dev,
|
||||
const char* name, int type);
|
||||
const char *name, int type);
|
||||
int ms5611_remove(struct iio_dev *indio_dev);
|
||||
|
||||
#endif /* _MS5611_H */
|
||||
|
|
|
@ -1,18 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* as3935.c - Support for AS3935 Franklin lightning sensor
|
||||
*
|
||||
* Copyright (C) 2014 Matt Ranostay <mranostay@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* Copyright (C) 2014, 2017-2018
|
||||
* Author: Matt Ranostay <matt.ranostay@konsulko.com>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
@ -502,6 +493,6 @@ static struct spi_driver as3935_driver = {
|
|||
};
|
||||
module_spi_driver(as3935_driver);
|
||||
|
||||
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
|
||||
MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
|
||||
MODULE_DESCRIPTION("AS3935 lightning sensor");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -1,17 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* pulsedlight-lidar-lite-v2.c - Support for PulsedLight LIDAR sensor
|
||||
*
|
||||
* Copyright (C) 2015 Matt Ranostay <mranostay@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* Copyright (C) 2015, 2017-2018
|
||||
* Author: Matt Ranostay <matt.ranostay@konsulko.com>
|
||||
*
|
||||
* TODO: interrupt mode, and signal strength reporting
|
||||
*/
|
||||
|
@ -377,6 +369,6 @@ static struct i2c_driver lidar_driver = {
|
|||
};
|
||||
module_i2c_driver(lidar_driver);
|
||||
|
||||
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
|
||||
MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
|
||||
MODULE_DESCRIPTION("PulsedLight LIDAR sensor");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -43,6 +43,18 @@ config MLX90614
|
|||
This driver can also be built as a module. If so, the module will
|
||||
be called mlx90614.
|
||||
|
||||
config MLX90632
|
||||
tristate "MLX90632 contact-less infrared sensor with medical accuracy"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
If you say yes here you get support for the Melexis
|
||||
MLX90632 contact-less infrared sensor with medical accuracy
|
||||
connected with I2C.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called mlx90632.
|
||||
|
||||
config TMP006
|
||||
tristate "TMP006 infrared thermopile sensor"
|
||||
depends on I2C
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
obj-$(CONFIG_HID_SENSOR_TEMP) += hid-sensor-temperature.o
|
||||
obj-$(CONFIG_MAXIM_THERMOCOUPLE) += maxim_thermocouple.o
|
||||
obj-$(CONFIG_MLX90614) += mlx90614.o
|
||||
obj-$(CONFIG_MLX90632) += mlx90632.o
|
||||
obj-$(CONFIG_TMP006) += tmp006.o
|
||||
obj-$(CONFIG_TMP007) += tmp007.o
|
||||
obj-$(CONFIG_TSYS01) += tsys01.o
|
||||
|
|
|
@ -1,17 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* maxim_thermocouple.c - Support for Maxim thermocouple chips
|
||||
*
|
||||
* Copyright (C) 2016 Matt Ranostay <mranostay@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* Copyright (C) 2016-2018 Matt Ranostay
|
||||
* Author: <matt.ranostay@konsulko.com>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
@ -281,6 +273,6 @@ static struct spi_driver maxim_thermocouple_driver = {
|
|||
};
|
||||
module_spi_driver(maxim_thermocouple_driver);
|
||||
|
||||
MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
|
||||
MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
|
||||
MODULE_DESCRIPTION("Maxim thermocouple sensors");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -0,0 +1,750 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* mlx90632.c - Melexis MLX90632 contactless IR temperature sensor
|
||||
*
|
||||
* Copyright (c) 2017 Melexis <cmo@melexis.com>
|
||||
*
|
||||
* Driver for the Melexis MLX90632 I2C 16-bit IR thermopile sensor
|
||||
*/
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
/* Memory sections addresses */
|
||||
#define MLX90632_ADDR_RAM 0x4000 /* Start address of ram */
|
||||
#define MLX90632_ADDR_EEPROM 0x2480 /* Start address of user eeprom */
|
||||
|
||||
/* EEPROM addresses - used at startup */
|
||||
#define MLX90632_EE_CTRL 0x24d4 /* Control register initial value */
|
||||
#define MLX90632_EE_I2C_ADDR 0x24d5 /* I2C address register initial value */
|
||||
#define MLX90632_EE_VERSION 0x240b /* EEPROM version reg address */
|
||||
#define MLX90632_EE_P_R 0x240c /* P_R calibration register 32bit */
|
||||
#define MLX90632_EE_P_G 0x240e /* P_G calibration register 32bit */
|
||||
#define MLX90632_EE_P_T 0x2410 /* P_T calibration register 32bit */
|
||||
#define MLX90632_EE_P_O 0x2412 /* P_O calibration register 32bit */
|
||||
#define MLX90632_EE_Aa 0x2414 /* Aa calibration register 32bit */
|
||||
#define MLX90632_EE_Ab 0x2416 /* Ab calibration register 32bit */
|
||||
#define MLX90632_EE_Ba 0x2418 /* Ba calibration register 32bit */
|
||||
#define MLX90632_EE_Bb 0x241a /* Bb calibration register 32bit */
|
||||
#define MLX90632_EE_Ca 0x241c /* Ca calibration register 32bit */
|
||||
#define MLX90632_EE_Cb 0x241e /* Cb calibration register 32bit */
|
||||
#define MLX90632_EE_Da 0x2420 /* Da calibration register 32bit */
|
||||
#define MLX90632_EE_Db 0x2422 /* Db calibration register 32bit */
|
||||
#define MLX90632_EE_Ea 0x2424 /* Ea calibration register 32bit */
|
||||
#define MLX90632_EE_Eb 0x2426 /* Eb calibration register 32bit */
|
||||
#define MLX90632_EE_Fa 0x2428 /* Fa calibration register 32bit */
|
||||
#define MLX90632_EE_Fb 0x242a /* Fb calibration register 32bit */
|
||||
#define MLX90632_EE_Ga 0x242c /* Ga calibration register 32bit */
|
||||
|
||||
#define MLX90632_EE_Gb 0x242e /* Gb calibration register 16bit */
|
||||
#define MLX90632_EE_Ka 0x242f /* Ka calibration register 16bit */
|
||||
|
||||
#define MLX90632_EE_Ha 0x2481 /* Ha customer calib value reg 16bit */
|
||||
#define MLX90632_EE_Hb 0x2482 /* Hb customer calib value reg 16bit */
|
||||
|
||||
/* Register addresses - volatile */
|
||||
#define MLX90632_REG_I2C_ADDR 0x3000 /* Chip I2C address register */
|
||||
|
||||
/* Control register address - volatile */
|
||||
#define MLX90632_REG_CONTROL 0x3001 /* Control Register address */
|
||||
#define MLX90632_CFG_PWR_MASK GENMASK(2, 1) /* PowerMode Mask */
|
||||
/* PowerModes statuses */
|
||||
#define MLX90632_PWR_STATUS(ctrl_val) (ctrl_val << 1)
|
||||
#define MLX90632_PWR_STATUS_HALT MLX90632_PWR_STATUS(0) /* hold */
|
||||
#define MLX90632_PWR_STATUS_SLEEP_STEP MLX90632_PWR_STATUS(1) /* sleep step*/
|
||||
#define MLX90632_PWR_STATUS_STEP MLX90632_PWR_STATUS(2) /* step */
|
||||
#define MLX90632_PWR_STATUS_CONTINUOUS MLX90632_PWR_STATUS(3) /* continuous*/
|
||||
|
||||
/* Device status register - volatile */
|
||||
#define MLX90632_REG_STATUS 0x3fff /* Device status register */
|
||||
#define MLX90632_STAT_BUSY BIT(10) /* Device busy indicator */
|
||||
#define MLX90632_STAT_EE_BUSY BIT(9) /* EEPROM busy indicator */
|
||||
#define MLX90632_STAT_BRST BIT(8) /* Brown out reset indicator */
|
||||
#define MLX90632_STAT_CYCLE_POS GENMASK(6, 2) /* Data position */
|
||||
#define MLX90632_STAT_DATA_RDY BIT(0) /* Data ready indicator */
|
||||
|
||||
/* RAM_MEAS address-es for each channel */
|
||||
#define MLX90632_RAM_1(meas_num) (MLX90632_ADDR_RAM + 3 * meas_num)
|
||||
#define MLX90632_RAM_2(meas_num) (MLX90632_ADDR_RAM + 3 * meas_num + 1)
|
||||
#define MLX90632_RAM_3(meas_num) (MLX90632_ADDR_RAM + 3 * meas_num + 2)
|
||||
|
||||
/* Magic constants */
|
||||
#define MLX90632_ID_MEDICAL 0x0105 /* EEPROM DSPv5 Medical device id */
|
||||
#define MLX90632_ID_CONSUMER 0x0205 /* EEPROM DSPv5 Consumer device id */
|
||||
#define MLX90632_RESET_CMD 0x0006 /* Reset sensor (address or global) */
|
||||
#define MLX90632_REF_12 12LL /**< ResCtrlRef value of Ch 1 or Ch 2 */
|
||||
#define MLX90632_REF_3 12LL /**< ResCtrlRef value of Channel 3 */
|
||||
#define MLX90632_MAX_MEAS_NUM 31 /**< Maximum measurements in list */
|
||||
#define MLX90632_SLEEP_DELAY_MS 3000 /**< Autosleep delay */
|
||||
|
||||
struct mlx90632_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock; /* Multiple reads for single measurement */
|
||||
struct regmap *regmap;
|
||||
u16 emissivity;
|
||||
};
|
||||
|
||||
static const struct regmap_range mlx90632_volatile_reg_range[] = {
|
||||
regmap_reg_range(MLX90632_REG_I2C_ADDR, MLX90632_REG_CONTROL),
|
||||
regmap_reg_range(MLX90632_REG_STATUS, MLX90632_REG_STATUS),
|
||||
regmap_reg_range(MLX90632_RAM_1(0),
|
||||
MLX90632_RAM_3(MLX90632_MAX_MEAS_NUM)),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table mlx90632_volatile_regs_tbl = {
|
||||
.yes_ranges = mlx90632_volatile_reg_range,
|
||||
.n_yes_ranges = ARRAY_SIZE(mlx90632_volatile_reg_range),
|
||||
};
|
||||
|
||||
static const struct regmap_range mlx90632_read_reg_range[] = {
|
||||
regmap_reg_range(MLX90632_EE_VERSION, MLX90632_EE_Ka),
|
||||
regmap_reg_range(MLX90632_EE_CTRL, MLX90632_EE_I2C_ADDR),
|
||||
regmap_reg_range(MLX90632_EE_Ha, MLX90632_EE_Hb),
|
||||
regmap_reg_range(MLX90632_REG_I2C_ADDR, MLX90632_REG_CONTROL),
|
||||
regmap_reg_range(MLX90632_REG_STATUS, MLX90632_REG_STATUS),
|
||||
regmap_reg_range(MLX90632_RAM_1(0),
|
||||
MLX90632_RAM_3(MLX90632_MAX_MEAS_NUM)),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table mlx90632_readable_regs_tbl = {
|
||||
.yes_ranges = mlx90632_read_reg_range,
|
||||
.n_yes_ranges = ARRAY_SIZE(mlx90632_read_reg_range),
|
||||
};
|
||||
|
||||
static const struct regmap_range mlx90632_no_write_reg_range[] = {
|
||||
regmap_reg_range(MLX90632_EE_VERSION, MLX90632_EE_Ka),
|
||||
regmap_reg_range(MLX90632_RAM_1(0),
|
||||
MLX90632_RAM_3(MLX90632_MAX_MEAS_NUM)),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table mlx90632_writeable_regs_tbl = {
|
||||
.no_ranges = mlx90632_no_write_reg_range,
|
||||
.n_no_ranges = ARRAY_SIZE(mlx90632_no_write_reg_range),
|
||||
};
|
||||
|
||||
static const struct regmap_config mlx90632_regmap = {
|
||||
.reg_bits = 16,
|
||||
.val_bits = 16,
|
||||
|
||||
.volatile_table = &mlx90632_volatile_regs_tbl,
|
||||
.rd_table = &mlx90632_readable_regs_tbl,
|
||||
.wr_table = &mlx90632_writeable_regs_tbl,
|
||||
|
||||
.use_single_rw = true,
|
||||
.reg_format_endian = REGMAP_ENDIAN_BIG,
|
||||
.val_format_endian = REGMAP_ENDIAN_BIG,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
|
||||
static s32 mlx90632_pwr_set_sleep_step(struct regmap *regmap)
|
||||
{
|
||||
return regmap_update_bits(regmap, MLX90632_REG_CONTROL,
|
||||
MLX90632_CFG_PWR_MASK,
|
||||
MLX90632_PWR_STATUS_SLEEP_STEP);
|
||||
}
|
||||
|
||||
static s32 mlx90632_pwr_continuous(struct regmap *regmap)
|
||||
{
|
||||
return regmap_update_bits(regmap, MLX90632_REG_CONTROL,
|
||||
MLX90632_CFG_PWR_MASK,
|
||||
MLX90632_PWR_STATUS_CONTINUOUS);
|
||||
}
|
||||
|
||||
/**
|
||||
* mlx90632_perform_measurement - Trigger and retrieve current measurement cycle
|
||||
* @*data: pointer to mlx90632_data object containing regmap information
|
||||
*
|
||||
* Perform a measurement and return latest measurement cycle position reported
|
||||
* by sensor. This is a blocking function for 500ms, as that is default sensor
|
||||
* refresh rate.
|
||||
*/
|
||||
static int mlx90632_perform_measurement(struct mlx90632_data *data)
|
||||
{
|
||||
int ret, tries = 100;
|
||||
unsigned int reg_status;
|
||||
|
||||
ret = regmap_update_bits(data->regmap, MLX90632_REG_STATUS,
|
||||
MLX90632_STAT_DATA_RDY, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
while (tries-- > 0) {
|
||||
ret = regmap_read(data->regmap, MLX90632_REG_STATUS,
|
||||
®_status);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (reg_status & MLX90632_STAT_DATA_RDY)
|
||||
break;
|
||||
usleep_range(10000, 11000);
|
||||
}
|
||||
|
||||
if (tries < 0) {
|
||||
dev_err(&data->client->dev, "data not ready");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return (reg_status & MLX90632_STAT_CYCLE_POS) >> 2;
|
||||
}
|
||||
|
||||
static int mlx90632_channel_new_select(int perform_ret, uint8_t *channel_new,
|
||||
uint8_t *channel_old)
|
||||
{
|
||||
switch (perform_ret) {
|
||||
case 1:
|
||||
*channel_new = 1;
|
||||
*channel_old = 2;
|
||||
break;
|
||||
case 2:
|
||||
*channel_new = 2;
|
||||
*channel_old = 1;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mlx90632_read_ambient_raw(struct regmap *regmap,
|
||||
s16 *ambient_new_raw, s16 *ambient_old_raw)
|
||||
{
|
||||
int ret;
|
||||
unsigned int read_tmp;
|
||||
|
||||
ret = regmap_read(regmap, MLX90632_RAM_3(1), &read_tmp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*ambient_new_raw = (s16)read_tmp;
|
||||
|
||||
ret = regmap_read(regmap, MLX90632_RAM_3(2), &read_tmp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*ambient_old_raw = (s16)read_tmp;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mlx90632_read_object_raw(struct regmap *regmap,
|
||||
int perform_measurement_ret,
|
||||
s16 *object_new_raw, s16 *object_old_raw)
|
||||
{
|
||||
int ret;
|
||||
unsigned int read_tmp;
|
||||
s16 read;
|
||||
u8 channel = 0;
|
||||
u8 channel_old = 0;
|
||||
|
||||
ret = mlx90632_channel_new_select(perform_measurement_ret, &channel,
|
||||
&channel_old);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_read(regmap, MLX90632_RAM_2(channel), &read_tmp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
read = (s16)read_tmp;
|
||||
|
||||
ret = regmap_read(regmap, MLX90632_RAM_1(channel), &read_tmp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*object_new_raw = (read + (s16)read_tmp) / 2;
|
||||
|
||||
ret = regmap_read(regmap, MLX90632_RAM_2(channel_old), &read_tmp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
read = (s16)read_tmp;
|
||||
|
||||
ret = regmap_read(regmap, MLX90632_RAM_1(channel_old), &read_tmp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*object_old_raw = (read + (s16)read_tmp) / 2;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mlx90632_read_all_channel(struct mlx90632_data *data,
|
||||
s16 *ambient_new_raw, s16 *ambient_old_raw,
|
||||
s16 *object_new_raw, s16 *object_old_raw)
|
||||
{
|
||||
s32 ret, measurement;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
measurement = mlx90632_perform_measurement(data);
|
||||
if (measurement < 0) {
|
||||
ret = measurement;
|
||||
goto read_unlock;
|
||||
}
|
||||
ret = mlx90632_read_ambient_raw(data->regmap, ambient_new_raw,
|
||||
ambient_old_raw);
|
||||
if (ret < 0)
|
||||
goto read_unlock;
|
||||
|
||||
ret = mlx90632_read_object_raw(data->regmap, measurement,
|
||||
object_new_raw, object_old_raw);
|
||||
read_unlock:
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mlx90632_read_ee_register(struct regmap *regmap, u16 reg_lsb,
|
||||
s32 *reg_value)
|
||||
{
|
||||
s32 ret;
|
||||
unsigned int read;
|
||||
u32 value;
|
||||
|
||||
ret = regmap_read(regmap, reg_lsb, &read);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
value = read;
|
||||
|
||||
ret = regmap_read(regmap, reg_lsb + 1, &read);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*reg_value = (read << 16) | (value & 0xffff);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static s64 mlx90632_preprocess_temp_amb(s16 ambient_new_raw,
|
||||
s16 ambient_old_raw, s16 Gb)
|
||||
{
|
||||
s64 VR_Ta, kGb, tmp;
|
||||
|
||||
kGb = ((s64)Gb * 1000LL) >> 10ULL;
|
||||
VR_Ta = (s64)ambient_old_raw * 1000000LL +
|
||||
kGb * div64_s64(((s64)ambient_new_raw * 1000LL),
|
||||
(MLX90632_REF_3));
|
||||
tmp = div64_s64(
|
||||
div64_s64(((s64)ambient_new_raw * 1000000000000LL),
|
||||
(MLX90632_REF_3)), VR_Ta);
|
||||
return div64_s64(tmp << 19ULL, 1000LL);
|
||||
}
|
||||
|
||||
static s64 mlx90632_preprocess_temp_obj(s16 object_new_raw, s16 object_old_raw,
|
||||
s16 ambient_new_raw,
|
||||
s16 ambient_old_raw, s16 Ka)
|
||||
{
|
||||
s64 VR_IR, kKa, tmp;
|
||||
|
||||
kKa = ((s64)Ka * 1000LL) >> 10ULL;
|
||||
VR_IR = (s64)ambient_old_raw * 1000000LL +
|
||||
kKa * div64_s64(((s64)ambient_new_raw * 1000LL),
|
||||
(MLX90632_REF_3));
|
||||
tmp = div64_s64(
|
||||
div64_s64(((s64)((object_new_raw + object_old_raw) / 2)
|
||||
* 1000000000000LL), (MLX90632_REF_12)),
|
||||
VR_IR);
|
||||
return div64_s64((tmp << 19ULL), 1000LL);
|
||||
}
|
||||
|
||||
static s32 mlx90632_calc_temp_ambient(s16 ambient_new_raw, s16 ambient_old_raw,
|
||||
s32 P_T, s32 P_R, s32 P_G, s32 P_O,
|
||||
s16 Gb)
|
||||
{
|
||||
s64 Asub, Bsub, Ablock, Bblock, Cblock, AMB, sum;
|
||||
|
||||
AMB = mlx90632_preprocess_temp_amb(ambient_new_raw, ambient_old_raw,
|
||||
Gb);
|
||||
Asub = ((s64)P_T * 10000000000LL) >> 44ULL;
|
||||
Bsub = AMB - (((s64)P_R * 1000LL) >> 8ULL);
|
||||
Ablock = Asub * (Bsub * Bsub);
|
||||
Bblock = (div64_s64(Bsub * 10000000LL, P_G)) << 20ULL;
|
||||
Cblock = ((s64)P_O * 10000000000LL) >> 8ULL;
|
||||
|
||||
sum = div64_s64(Ablock, 1000000LL) + Bblock + Cblock;
|
||||
|
||||
return div64_s64(sum, 10000000LL);
|
||||
}
|
||||
|
||||
static s32 mlx90632_calc_temp_object_iteration(s32 prev_object_temp, s64 object,
|
||||
s64 TAdut, s32 Fa, s32 Fb,
|
||||
s32 Ga, s16 Ha, s16 Hb,
|
||||
u16 emissivity)
|
||||
{
|
||||
s64 calcedKsTO, calcedKsTA, ir_Alpha, TAdut4, Alpha_corr;
|
||||
s64 Ha_customer, Hb_customer;
|
||||
|
||||
Ha_customer = ((s64)Ha * 1000000LL) >> 14ULL;
|
||||
Hb_customer = ((s64)Hb * 100) >> 10ULL;
|
||||
|
||||
calcedKsTO = ((s64)((s64)Ga * (prev_object_temp - 25 * 1000LL)
|
||||
* 1000LL)) >> 36LL;
|
||||
calcedKsTA = ((s64)(Fb * (TAdut - 25 * 1000000LL))) >> 36LL;
|
||||
Alpha_corr = div64_s64((((s64)(Fa * 10000000000LL) >> 46LL)
|
||||
* Ha_customer), 1000LL);
|
||||
Alpha_corr *= ((s64)(1 * 1000000LL + calcedKsTO + calcedKsTA));
|
||||
Alpha_corr = emissivity * div64_s64(Alpha_corr, 100000LL);
|
||||
Alpha_corr = div64_s64(Alpha_corr, 1000LL);
|
||||
ir_Alpha = div64_s64((s64)object * 10000000LL, Alpha_corr);
|
||||
TAdut4 = (div64_s64(TAdut, 10000LL) + 27315) *
|
||||
(div64_s64(TAdut, 10000LL) + 27315) *
|
||||
(div64_s64(TAdut, 10000LL) + 27315) *
|
||||
(div64_s64(TAdut, 10000LL) + 27315);
|
||||
|
||||
return (int_sqrt64(int_sqrt64(ir_Alpha * 1000000000000LL + TAdut4))
|
||||
- 27315 - Hb_customer) * 10;
|
||||
}
|
||||
|
||||
static s32 mlx90632_calc_temp_object(s64 object, s64 ambient, s32 Ea, s32 Eb,
|
||||
s32 Fa, s32 Fb, s32 Ga, s16 Ha, s16 Hb,
|
||||
u16 tmp_emi)
|
||||
{
|
||||
s64 kTA, kTA0, TAdut;
|
||||
s64 temp = 25000;
|
||||
s8 i;
|
||||
|
||||
kTA = (Ea * 1000LL) >> 16LL;
|
||||
kTA0 = (Eb * 1000LL) >> 8LL;
|
||||
TAdut = div64_s64(((ambient - kTA0) * 1000000LL), kTA) + 25 * 1000000LL;
|
||||
|
||||
/* Iterations of calculation as described in datasheet */
|
||||
for (i = 0; i < 5; ++i) {
|
||||
temp = mlx90632_calc_temp_object_iteration(temp, object, TAdut,
|
||||
Fa, Fb, Ga, Ha, Hb,
|
||||
tmp_emi);
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
static int mlx90632_calc_object_dsp105(struct mlx90632_data *data, int *val)
|
||||
{
|
||||
s32 ret;
|
||||
s32 Ea, Eb, Fa, Fb, Ga;
|
||||
unsigned int read_tmp;
|
||||
s16 Ha, Hb, Gb, Ka;
|
||||
s16 ambient_new_raw, ambient_old_raw, object_new_raw, object_old_raw;
|
||||
s64 object, ambient;
|
||||
|
||||
ret = mlx90632_read_ee_register(data->regmap, MLX90632_EE_Ea, &Ea);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = mlx90632_read_ee_register(data->regmap, MLX90632_EE_Eb, &Eb);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = mlx90632_read_ee_register(data->regmap, MLX90632_EE_Fa, &Fa);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = mlx90632_read_ee_register(data->regmap, MLX90632_EE_Fb, &Fb);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = mlx90632_read_ee_register(data->regmap, MLX90632_EE_Ga, &Ga);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = regmap_read(data->regmap, MLX90632_EE_Ha, &read_tmp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
Ha = (s16)read_tmp;
|
||||
ret = regmap_read(data->regmap, MLX90632_EE_Hb, &read_tmp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
Hb = (s16)read_tmp;
|
||||
ret = regmap_read(data->regmap, MLX90632_EE_Gb, &read_tmp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
Gb = (s16)read_tmp;
|
||||
ret = regmap_read(data->regmap, MLX90632_EE_Ka, &read_tmp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
Ka = (s16)read_tmp;
|
||||
|
||||
ret = mlx90632_read_all_channel(data,
|
||||
&ambient_new_raw, &ambient_old_raw,
|
||||
&object_new_raw, &object_old_raw);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ambient = mlx90632_preprocess_temp_amb(ambient_new_raw,
|
||||
ambient_old_raw, Gb);
|
||||
object = mlx90632_preprocess_temp_obj(object_new_raw,
|
||||
object_old_raw,
|
||||
ambient_new_raw,
|
||||
ambient_old_raw, Ka);
|
||||
|
||||
*val = mlx90632_calc_temp_object(object, ambient, Ea, Eb, Fa, Fb, Ga,
|
||||
Ha, Hb, data->emissivity);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mlx90632_calc_ambient_dsp105(struct mlx90632_data *data, int *val)
|
||||
{
|
||||
s32 ret;
|
||||
unsigned int read_tmp;
|
||||
s32 PT, PR, PG, PO;
|
||||
s16 Gb;
|
||||
s16 ambient_new_raw, ambient_old_raw;
|
||||
|
||||
ret = mlx90632_read_ee_register(data->regmap, MLX90632_EE_P_R, &PR);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = mlx90632_read_ee_register(data->regmap, MLX90632_EE_P_G, &PG);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = mlx90632_read_ee_register(data->regmap, MLX90632_EE_P_T, &PT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = mlx90632_read_ee_register(data->regmap, MLX90632_EE_P_O, &PO);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = regmap_read(data->regmap, MLX90632_EE_Gb, &read_tmp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
Gb = (s16)read_tmp;
|
||||
|
||||
ret = mlx90632_read_ambient_raw(data->regmap, &ambient_new_raw,
|
||||
&ambient_old_raw);
|
||||
*val = mlx90632_calc_temp_ambient(ambient_new_raw, ambient_old_raw,
|
||||
PT, PR, PG, PO, Gb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mlx90632_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *channel, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct mlx90632_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
switch (channel->channel2) {
|
||||
case IIO_MOD_TEMP_AMBIENT:
|
||||
ret = mlx90632_calc_ambient_dsp105(data, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_MOD_TEMP_OBJECT:
|
||||
ret = mlx90632_calc_object_dsp105(data, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_CALIBEMISSIVITY:
|
||||
if (data->emissivity == 1000) {
|
||||
*val = 1;
|
||||
*val2 = 0;
|
||||
} else {
|
||||
*val = 0;
|
||||
*val2 = data->emissivity * 1000;
|
||||
}
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int mlx90632_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *channel, int val,
|
||||
int val2, long mask)
|
||||
{
|
||||
struct mlx90632_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_CALIBEMISSIVITY:
|
||||
/* Confirm we are within 0 and 1.0 */
|
||||
if (val < 0 || val2 < 0 || val > 1 ||
|
||||
(val == 1 && val2 != 0))
|
||||
return -EINVAL;
|
||||
data->emissivity = val * 1000 + val2 / 1000;
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec mlx90632_channels[] = {
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_TEMP_AMBIENT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||
},
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_TEMP_OBJECT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
|
||||
BIT(IIO_CHAN_INFO_CALIBEMISSIVITY),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_info mlx90632_info = {
|
||||
.read_raw = mlx90632_read_raw,
|
||||
.write_raw = mlx90632_write_raw,
|
||||
};
|
||||
|
||||
static int mlx90632_sleep(struct mlx90632_data *data)
|
||||
{
|
||||
regcache_mark_dirty(data->regmap);
|
||||
|
||||
dev_dbg(&data->client->dev, "Requesting sleep");
|
||||
return mlx90632_pwr_set_sleep_step(data->regmap);
|
||||
}
|
||||
|
||||
static int mlx90632_wakeup(struct mlx90632_data *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regcache_sync(data->regmap);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev,
|
||||
"Failed to sync regmap registers: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(&data->client->dev, "Requesting wake-up\n");
|
||||
return mlx90632_pwr_continuous(data->regmap);
|
||||
}
|
||||
|
||||
static int mlx90632_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct mlx90632_data *mlx90632;
|
||||
struct regmap *regmap;
|
||||
int ret;
|
||||
unsigned int read;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*mlx90632));
|
||||
if (!indio_dev) {
|
||||
dev_err(&client->dev, "Failed to allocate device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
regmap = devm_regmap_init_i2c(client, &mlx90632_regmap);
|
||||
if (IS_ERR(regmap)) {
|
||||
ret = PTR_ERR(regmap);
|
||||
dev_err(&client->dev, "Failed to allocate regmap: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
mlx90632 = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
mlx90632->client = client;
|
||||
mlx90632->regmap = regmap;
|
||||
|
||||
mutex_init(&mlx90632->lock);
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &mlx90632_info;
|
||||
indio_dev->channels = mlx90632_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(mlx90632_channels);
|
||||
|
||||
ret = mlx90632_wakeup(mlx90632);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Wakeup failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_read(mlx90632->regmap, MLX90632_EE_VERSION, &read);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "read of version failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
if (read == MLX90632_ID_MEDICAL) {
|
||||
dev_dbg(&client->dev,
|
||||
"Detected Medical EEPROM calibration %x\n", read);
|
||||
} else if (read == MLX90632_ID_CONSUMER) {
|
||||
dev_dbg(&client->dev,
|
||||
"Detected Consumer EEPROM calibration %x\n", read);
|
||||
} else {
|
||||
dev_err(&client->dev,
|
||||
"EEPROM version mismatch %x (expected %x or %x)\n",
|
||||
read, MLX90632_ID_CONSUMER, MLX90632_ID_MEDICAL);
|
||||
return -EPROTONOSUPPORT;
|
||||
}
|
||||
|
||||
mlx90632->emissivity = 1000;
|
||||
|
||||
pm_runtime_disable(&client->dev);
|
||||
ret = pm_runtime_set_active(&client->dev);
|
||||
if (ret < 0) {
|
||||
mlx90632_sleep(mlx90632);
|
||||
return ret;
|
||||
}
|
||||
pm_runtime_enable(&client->dev);
|
||||
pm_runtime_set_autosuspend_delay(&client->dev, MLX90632_SLEEP_DELAY_MS);
|
||||
pm_runtime_use_autosuspend(&client->dev);
|
||||
|
||||
return iio_device_register(indio_dev);
|
||||
}
|
||||
|
||||
static int mlx90632_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct mlx90632_data *data = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
pm_runtime_disable(&client->dev);
|
||||
pm_runtime_set_suspended(&client->dev);
|
||||
pm_runtime_put_noidle(&client->dev);
|
||||
|
||||
mlx90632_sleep(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id mlx90632_id[] = {
|
||||
{ "mlx90632", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mlx90632_id);
|
||||
|
||||
static const struct of_device_id mlx90632_of_match[] = {
|
||||
{ .compatible = "melexis,mlx90632" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mlx90632_of_match);
|
||||
|
||||
static int __maybe_unused mlx90632_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct mlx90632_data *data = iio_priv(indio_dev);
|
||||
|
||||
return mlx90632_sleep(data);
|
||||
}
|
||||
|
||||
static int __maybe_unused mlx90632_pm_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct mlx90632_data *data = iio_priv(indio_dev);
|
||||
|
||||
return mlx90632_wakeup(data);
|
||||
}
|
||||
|
||||
static UNIVERSAL_DEV_PM_OPS(mlx90632_pm_ops, mlx90632_pm_suspend,
|
||||
mlx90632_pm_resume, NULL);
|
||||
|
||||
static struct i2c_driver mlx90632_driver = {
|
||||
.driver = {
|
||||
.name = "mlx90632",
|
||||
.of_match_table = mlx90632_of_match,
|
||||
.pm = &mlx90632_pm_ops,
|
||||
},
|
||||
.probe = mlx90632_probe,
|
||||
.remove = mlx90632_remove,
|
||||
.id_table = mlx90632_id,
|
||||
};
|
||||
module_i2c_driver(mlx90632_driver);
|
||||
|
||||
MODULE_AUTHOR("Crt Mori <cmo@melexis.com>");
|
||||
MODULE_DESCRIPTION("Melexis MLX90632 contactless Infra Red temperature sensor driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -290,8 +290,12 @@ static int ad7192_setup(struct ad7192_state *st,
|
|||
if (pdata->unipolar_en)
|
||||
st->conf |= AD7192_CONF_UNIPOLAR;
|
||||
|
||||
if (pdata->burnout_curr_en)
|
||||
if (pdata->burnout_curr_en && pdata->buf_en && !pdata->chop_en) {
|
||||
st->conf |= AD7192_CONF_BURN;
|
||||
} else if (pdata->burnout_curr_en) {
|
||||
dev_warn(&st->sd.spi->dev,
|
||||
"Can't enable burnout currents: see CHOP or buffer\n");
|
||||
}
|
||||
|
||||
ret = ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode);
|
||||
if (ret)
|
||||
|
|
|
@ -111,7 +111,7 @@
|
|||
* @trig: data ready trigger registered with iio
|
||||
* @tx: transmit buffer
|
||||
* @rx: receive buffer
|
||||
* @buf_lock: mutex to protect tx and rx
|
||||
* @buf_lock: mutex to protect tx, rx, read and write frequency
|
||||
**/
|
||||
struct ade7758_state {
|
||||
struct spi_device *us;
|
||||
|
|
|
@ -24,6 +24,17 @@
|
|||
#include "meter.h"
|
||||
#include "ade7758.h"
|
||||
|
||||
static int __ade7758_spi_write_reg_8(struct device *dev, u8 reg_address, u8 val)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ade7758_state *st = iio_priv(indio_dev);
|
||||
|
||||
st->tx[0] = ADE7758_WRITE_REG(reg_address);
|
||||
st->tx[1] = val;
|
||||
|
||||
return spi_write(st->us, st->tx, 2);
|
||||
}
|
||||
|
||||
int ade7758_spi_write_reg_8(struct device *dev, u8 reg_address, u8 val)
|
||||
{
|
||||
int ret;
|
||||
|
@ -31,10 +42,7 @@ int ade7758_spi_write_reg_8(struct device *dev, u8 reg_address, u8 val)
|
|||
struct ade7758_state *st = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
st->tx[0] = ADE7758_WRITE_REG(reg_address);
|
||||
st->tx[1] = val;
|
||||
|
||||
ret = spi_write(st->us, st->tx, 2);
|
||||
ret = __ade7758_spi_write_reg_8(dev, reg_address, val);
|
||||
mutex_unlock(&st->buf_lock);
|
||||
|
||||
return ret;
|
||||
|
@ -91,7 +99,7 @@ static int ade7758_spi_write_reg_24(struct device *dev, u8 reg_address,
|
|||
return ret;
|
||||
}
|
||||
|
||||
int ade7758_spi_read_reg_8(struct device *dev, u8 reg_address, u8 *val)
|
||||
static int __ade7758_spi_read_reg_8(struct device *dev, u8 reg_address, u8 *val)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ade7758_state *st = iio_priv(indio_dev);
|
||||
|
@ -111,7 +119,6 @@ int ade7758_spi_read_reg_8(struct device *dev, u8 reg_address, u8 *val)
|
|||
},
|
||||
};
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
st->tx[0] = ADE7758_READ_REG(reg_address);
|
||||
st->tx[1] = 0;
|
||||
|
||||
|
@ -124,7 +131,19 @@ int ade7758_spi_read_reg_8(struct device *dev, u8 reg_address, u8 *val)
|
|||
*val = st->rx[0];
|
||||
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ade7758_spi_read_reg_8(struct device *dev, u8 reg_address, u8 *val)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ade7758_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
ret = __ade7758_spi_read_reg_8(dev, reg_address, val);
|
||||
mutex_unlock(&st->buf_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -484,6 +503,8 @@ static int ade7758_write_samp_freq(struct device *dev, int val)
|
|||
{
|
||||
int ret;
|
||||
u8 reg, t;
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ade7758_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (val) {
|
||||
case 26040:
|
||||
|
@ -499,20 +520,23 @@ static int ade7758_write_samp_freq(struct device *dev, int val)
|
|||
t = 3;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = ade7758_spi_read_reg_8(dev, ADE7758_WAVMODE, ®);
|
||||
mutex_lock(&st->buf_lock);
|
||||
|
||||
ret = __ade7758_spi_read_reg_8(dev, ADE7758_WAVMODE, ®);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
reg &= ~(5 << 3);
|
||||
reg |= t << 5;
|
||||
|
||||
ret = ade7758_spi_write_reg_8(dev, ADE7758_WAVMODE, reg);
|
||||
ret = __ade7758_spi_write_reg_8(dev, ADE7758_WAVMODE, reg);
|
||||
|
||||
out:
|
||||
mutex_unlock(&st->buf_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -526,9 +550,9 @@ static int ade7758_read_raw(struct iio_dev *indio_dev,
|
|||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
ret = ade7758_read_samp_freq(&indio_dev->dev, val);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
@ -547,9 +571,9 @@ static int ade7758_write_raw(struct iio_dev *indio_dev,
|
|||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
if (val2)
|
||||
return -EINVAL;
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
ret = ade7758_write_samp_freq(&indio_dev->dev, val);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
|
|
@ -72,8 +72,8 @@ struct ade7759_state {
|
|||
};
|
||||
|
||||
static int ade7759_spi_write_reg_8(struct device *dev,
|
||||
u8 reg_address,
|
||||
u8 val)
|
||||
u8 reg_address,
|
||||
u8 val)
|
||||
{
|
||||
int ret;
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
|
@ -91,8 +91,8 @@ static int ade7759_spi_write_reg_8(struct device *dev,
|
|||
|
||||
/*Unlocked version of ade7759_spi_write_reg_16 function */
|
||||
static int __ade7759_spi_write_reg_16(struct device *dev,
|
||||
u8 reg_address,
|
||||
u16 value)
|
||||
u8 reg_address,
|
||||
u16 value)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ade7759_state *st = iio_priv(indio_dev);
|
||||
|
@ -104,8 +104,8 @@ static int __ade7759_spi_write_reg_16(struct device *dev,
|
|||
}
|
||||
|
||||
static int ade7759_spi_write_reg_16(struct device *dev,
|
||||
u8 reg_address,
|
||||
u16 value)
|
||||
u8 reg_address,
|
||||
u16 value)
|
||||
{
|
||||
int ret;
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
|
@ -119,8 +119,8 @@ static int ade7759_spi_write_reg_16(struct device *dev,
|
|||
}
|
||||
|
||||
static int ade7759_spi_read_reg_8(struct device *dev,
|
||||
u8 reg_address,
|
||||
u8 *val)
|
||||
u8 reg_address,
|
||||
u8 *val)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ade7759_state *st = iio_priv(indio_dev);
|
||||
|
@ -128,8 +128,9 @@ static int ade7759_spi_read_reg_8(struct device *dev,
|
|||
|
||||
ret = spi_w8r8(st->us, ADE7759_READ_REG(reg_address));
|
||||
if (ret < 0) {
|
||||
dev_err(&st->us->dev, "problem when reading 8 bit register 0x%02X",
|
||||
reg_address);
|
||||
dev_err(&st->us->dev,
|
||||
"problem when reading 8 bit register 0x%02X",
|
||||
reg_address);
|
||||
return ret;
|
||||
}
|
||||
*val = ret;
|
||||
|
@ -138,8 +139,8 @@ static int ade7759_spi_read_reg_8(struct device *dev,
|
|||
}
|
||||
|
||||
static int ade7759_spi_read_reg_16(struct device *dev,
|
||||
u8 reg_address,
|
||||
u16 *val)
|
||||
u8 reg_address,
|
||||
u16 *val)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ade7759_state *st = iio_priv(indio_dev);
|
||||
|
@ -158,8 +159,8 @@ static int ade7759_spi_read_reg_16(struct device *dev,
|
|||
}
|
||||
|
||||
static int ade7759_spi_read_reg_40(struct device *dev,
|
||||
u8 reg_address,
|
||||
u64 *val)
|
||||
u8 reg_address,
|
||||
u64 *val)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ade7759_state *st = iio_priv(indio_dev);
|
||||
|
@ -179,8 +180,9 @@ static int ade7759_spi_read_reg_40(struct device *dev,
|
|||
|
||||
ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers));
|
||||
if (ret) {
|
||||
dev_err(&st->us->dev, "problem when reading 40 bit register 0x%02X",
|
||||
reg_address);
|
||||
dev_err(&st->us->dev,
|
||||
"problem when reading 40 bit register 0x%02X",
|
||||
reg_address);
|
||||
goto error_ret;
|
||||
}
|
||||
*val = ((u64)st->rx[1] << 32) | ((u64)st->rx[2] << 24) |
|
||||
|
@ -192,8 +194,8 @@ error_ret:
|
|||
}
|
||||
|
||||
static ssize_t ade7759_read_8bit(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int ret;
|
||||
u8 val = 0;
|
||||
|
@ -207,8 +209,8 @@ static ssize_t ade7759_read_8bit(struct device *dev,
|
|||
}
|
||||
|
||||
static ssize_t ade7759_read_16bit(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int ret;
|
||||
u16 val = 0;
|
||||
|
@ -222,8 +224,8 @@ static ssize_t ade7759_read_16bit(struct device *dev,
|
|||
}
|
||||
|
||||
static ssize_t ade7759_read_40bit(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int ret;
|
||||
u64 val = 0;
|
||||
|
@ -237,9 +239,9 @@ static ssize_t ade7759_read_40bit(struct device *dev,
|
|||
}
|
||||
|
||||
static ssize_t ade7759_write_8bit(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t len)
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
||||
int ret;
|
||||
|
@ -255,9 +257,9 @@ error_ret:
|
|||
}
|
||||
|
||||
static ssize_t ade7759_write_16bit(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t len)
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
||||
int ret;
|
||||
|
@ -277,9 +279,7 @@ static int ade7759_reset(struct device *dev)
|
|||
int ret;
|
||||
u16 val;
|
||||
|
||||
ret = ade7759_spi_read_reg_16(dev,
|
||||
ADE7759_MODE,
|
||||
&val);
|
||||
ret = ade7759_spi_read_reg_16(dev, ADE7759_MODE, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -365,9 +365,7 @@ static int ade7759_stop_device(struct device *dev)
|
|||
int ret;
|
||||
u16 val;
|
||||
|
||||
ret = ade7759_spi_read_reg_16(dev,
|
||||
ADE7759_MODE,
|
||||
&val);
|
||||
ret = ade7759_spi_read_reg_16(dev, ADE7759_MODE, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "unable to power down the device, error: %d\n",
|
||||
ret);
|
||||
|
@ -404,16 +402,14 @@ err_ret:
|
|||
}
|
||||
|
||||
static ssize_t ade7759_read_frequency(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int ret;
|
||||
u16 t;
|
||||
int sps;
|
||||
|
||||
ret = ade7759_spi_read_reg_16(dev,
|
||||
ADE7759_MODE,
|
||||
&t);
|
||||
ret = ade7759_spi_read_reg_16(dev, ADE7759_MODE, &t);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -424,9 +420,9 @@ static ssize_t ade7759_read_frequency(struct device *dev,
|
|||
}
|
||||
|
||||
static ssize_t ade7759_write_frequency(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t len)
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ade7759_state *st = iio_priv(indio_dev);
|
||||
|
|
|
@ -479,6 +479,15 @@ extern int func_ptr_is_kernel_text(void *ptr);
|
|||
|
||||
unsigned long int_sqrt(unsigned long);
|
||||
|
||||
#if BITS_PER_LONG < 64
|
||||
u32 int_sqrt64(u64 x);
|
||||
#else
|
||||
static inline u32 int_sqrt64(u64 x)
|
||||
{
|
||||
return (u32)int_sqrt(x);
|
||||
}
|
||||
#endif
|
||||
|
||||
extern void bust_spinlocks(int yes);
|
||||
extern int oops_in_progress; /* If set, an oops, panic(), BUG() or die() is in progress */
|
||||
extern int panic_timeout;
|
||||
|
|
|
@ -266,6 +266,8 @@ enum axp20x_variants {
|
|||
#define AXP288_RT_BATT_V_H 0xa0
|
||||
#define AXP288_RT_BATT_V_L 0xa1
|
||||
|
||||
#define AXP813_ADC_RATE 0x85
|
||||
|
||||
/* Fuel Gauge */
|
||||
#define AXP288_FG_RDC1_REG 0xba
|
||||
#define AXP288_FG_RDC0_REG 0xbb
|
||||
|
|
|
@ -38,3 +38,33 @@ unsigned long int_sqrt(unsigned long x)
|
|||
return y;
|
||||
}
|
||||
EXPORT_SYMBOL(int_sqrt);
|
||||
|
||||
#if BITS_PER_LONG < 64
|
||||
/**
|
||||
* int_sqrt64 - strongly typed int_sqrt function when minimum 64 bit input
|
||||
* is expected.
|
||||
* @x: 64bit integer of which to calculate the sqrt
|
||||
*/
|
||||
u32 int_sqrt64(u64 x)
|
||||
{
|
||||
u64 b, m, y = 0;
|
||||
|
||||
if (x <= ULONG_MAX)
|
||||
return int_sqrt((unsigned long) x);
|
||||
|
||||
m = 1ULL << (fls64(x) & ~1ULL);
|
||||
while (m != 0) {
|
||||
b = y + m;
|
||||
y >>= 1;
|
||||
|
||||
if (x >= b) {
|
||||
x -= b;
|
||||
y += m;
|
||||
}
|
||||
m >>= 2;
|
||||
}
|
||||
|
||||
return y;
|
||||
}
|
||||
EXPORT_SYMBOL(int_sqrt64);
|
||||
#endif
|
||||
|
|
Загрузка…
Ссылка в новой задаче