mirror of
https://github.com/torvalds/linux.git
synced 2024-12-01 16:41:39 +00:00
Second set of new drivers, functionality and cleanups for IIO in the 4.4 cycle.
Core stuff * adjust resistance documentation to allow for output devices. New device support: * bmc150 - split the i2c driver up into a core and i2c_regmap part including regmap conversion. - add spi support. * mcp4531 digitial potentiometer driver. * Measurement Specialties set of drivers with a core library module providing common functionality. Note that the htu21 has a driver in hwmon, but the view from that side was that, given the range of devices the same silicon turns up in are not all typical hwmon material, that driver would be deprecated in favour of this new support. - ms8607 temperature, pressure and humidty sensor - ms5637 temperature and pressure sensor - htu21 temperature and humidity sensor - tsys02d temperature sensor - tsys01 temperature sensor Cleanups * tree wide. - squish cases where irq 0 is still considered valid. * apds9960 - sparse endian warning cleanups by making endianness explicit. * ad5504 - leave group naming to the core. * ad7746 - cleanup comment style. - drop an unnecessary bit of dev_info - add some appropriate uses of the BIT macro. * ad799x - leave group naming to the core. * hdc100x - introduced this cycle,. - fix a wrong offset value. * lidar - add missing MODULE_DEVICE_TABLE for dt. * max1363 - leave sysfs group naming to the core. * m62332 got the Harmut treatment and as ever he found a 'few' bits the rest of us had missed! - Share scale and offset attributes across channels. - Shutdown the device on driver remove - Use ARRAY_SIZE rather than a hard coded count for channels. - Return more directly in the write_raw callback dropping a local variable along the way. - a few style issues - move to reading the regulator voltage for each use allowing for dynamic regulators. This is a common feature across drivers so we might end up with more fixes throughout the tree for this. * mlx96014 - introduced this cycle. - fixed up a spot of error handling. * vz89x - introduced this cycle. - work around a hardware quirk. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJWG/HnAAoJEFSFNJnE9BaI3skQAJa/x5iZKRfR15pbYcYrgfUl P0yOh2l5+PufKUQVqKla9wmYjoriXoKLvC2UtFyvnCj72H8zPt24N+Fx9kab/Wy0 YXnq/KgppQ9NFat3mYUxQoRFg7tlNkEj1jDF0iynNCSGUW6TJYxvHkto0oZOzi0O b9oejTci+1+8mreShiobALp12bappQVBWy3TUmcEnGr/RmOPi+YQaYO+YGcPUx6R YPCHw6/QpDukUJ82gnWtvarHFuf8LwmZ9lz+OzMY9Xy1ytglwHOnhnimWai0qEGz TSaFqM/HKFB8f9hY08srfqaAz1q+Gx56yQQsGlhGtwvVELKn5HhDpgiAJImc0ZG/ B7hUB9Gi7TWbGlE/VvCGRPjFkVVnDibtNd9OJaeQPZD8KwRR320bA+6o49RVAa92 0tFfybEe8I2y3577QkH7enYr+5CV/RtvWxBjwxzODAwWypzPWNTyfgsd9JKxnDYH cL9DejGQhGDgyM47Q8FEKTJovTc6ueP/1/5v4p6Qeg8WsaAC18bl3zKGJ3jywIiR Ec/8icxdxmT+UUDJAPKOMSBraOnUDa/q1blaNtMb9bqSlbMmE9vz3w71uBvfpzE5 /snq1j4yAyomE74x6lfb6Amzv1q6VVI72ewOtGng0kgYAV5t6+m2oVz8p+hYShZO Q7d1RqBZrEtsjYyg9qNn =LXV9 -----END PGP SIGNATURE----- Merge tag 'iio-for-4.4b' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next Jonathan writes: Second set of new drivers, functionality and cleanups for IIO in the 4.4 cycle. Core stuff * adjust resistance documentation to allow for output devices. New device support: * bmc150 - split the i2c driver up into a core and i2c_regmap part including regmap conversion. - add spi support. * mcp4531 digitial potentiometer driver. * Measurement Specialties set of drivers with a core library module providing common functionality. Note that the htu21 has a driver in hwmon, but the view from that side was that, given the range of devices the same silicon turns up in are not all typical hwmon material, that driver would be deprecated in favour of this new support. - ms8607 temperature, pressure and humidty sensor - ms5637 temperature and pressure sensor - htu21 temperature and humidity sensor - tsys02d temperature sensor - tsys01 temperature sensor Cleanups * tree wide. - squish cases where irq 0 is still considered valid. * apds9960 - sparse endian warning cleanups by making endianness explicit. * ad5504 - leave group naming to the core. * ad7746 - cleanup comment style. - drop an unnecessary bit of dev_info - add some appropriate uses of the BIT macro. * ad799x - leave group naming to the core. * hdc100x - introduced this cycle,. - fix a wrong offset value. * lidar - add missing MODULE_DEVICE_TABLE for dt. * max1363 - leave sysfs group naming to the core. * m62332 got the Harmut treatment and as ever he found a 'few' bits the rest of us had missed! - Share scale and offset attributes across channels. - Shutdown the device on driver remove - Use ARRAY_SIZE rather than a hard coded count for channels. - Return more directly in the write_raw callback dropping a local variable along the way. - a few style issues - move to reading the regulator voltage for each use allowing for dynamic regulators. This is a common feature across drivers so we might end up with more fixes throughout the tree for this. * mlx96014 - introduced this cycle. - fixed up a spot of error handling. * vz89x - introduced this cycle. - work around a hardware quirk.
This commit is contained in:
commit
5da654541b
@ -1474,8 +1474,20 @@ Description:
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_resistance_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_resistanceX_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/out_resistance_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/out_resistanceX_raw
|
||||
KernelVersion: 4.3
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Raw (unscaled no offset etc.) resistance reading that can be processed
|
||||
into an ohm value.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/heater_enable
|
||||
KernelVersion: 4.1.0
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
'1' (enable) or '0' (disable) specifying the enable
|
||||
of heater function. Same reading values apply
|
||||
This ABI is especially applicable for humidity sensors
|
||||
to heatup the device and get rid of any condensation
|
||||
in some humidity environment
|
||||
|
8
Documentation/ABI/testing/sysfs-bus-iio-meas-spec
Normal file
8
Documentation/ABI/testing/sysfs-bus-iio-meas-spec
Normal file
@ -0,0 +1,8 @@
|
||||
What: /sys/bus/iio/devices/iio:deviceX/battery_low
|
||||
KernelVersion: 4.1.0
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Reading returns either '1' or '0'. '1' means that the
|
||||
battery level supplied to sensor is below 2.25V.
|
||||
This ABI is available for tsys02d, htu21, ms8607
|
||||
This ABI is available for htu21, ms8607
|
@ -6682,6 +6682,12 @@ W: http://linuxtv.org
|
||||
S: Maintained
|
||||
F: drivers/media/radio/radio-maxiradio*
|
||||
|
||||
MCP4531 MICROCHIP DIGITAL POTENTIOMETER DRIVER
|
||||
M: Peter Rosin <peda@axentia.se>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/iio/potentiometer/mcp4531.c
|
||||
|
||||
MEDIA DRIVERS FOR RENESAS - VSP1
|
||||
M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
|
||||
L: linux-media@vger.kernel.org
|
||||
|
@ -60,6 +60,7 @@ source "drivers/iio/orientation/Kconfig"
|
||||
if IIO_TRIGGER
|
||||
source "drivers/iio/trigger/Kconfig"
|
||||
endif #IIO_TRIGGER
|
||||
source "drivers/iio/potentiometer/Kconfig"
|
||||
source "drivers/iio/pressure/Kconfig"
|
||||
source "drivers/iio/proximity/Kconfig"
|
||||
source "drivers/iio/temperature/Kconfig"
|
||||
|
@ -23,6 +23,7 @@ obj-y += imu/
|
||||
obj-y += light/
|
||||
obj-y += magnetometer/
|
||||
obj-y += orientation/
|
||||
obj-y += potentiometer/
|
||||
obj-y += pressure/
|
||||
obj-y += proximity/
|
||||
obj-y += temperature/
|
||||
|
@ -19,19 +19,27 @@ config BMA180
|
||||
|
||||
config BMC150_ACCEL
|
||||
tristate "Bosch BMC150 Accelerometer Driver"
|
||||
depends on I2C
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select REGMAP
|
||||
select BMC150_ACCEL_I2C if I2C
|
||||
select BMC150_ACCEL_SPI if SPI
|
||||
help
|
||||
Say yes here to build support for the following Bosch accelerometers:
|
||||
BMC150, BMI055, BMA250E, BMA222E, BMA255, BMA280.
|
||||
|
||||
Currently this only supports the device via an i2c interface.
|
||||
|
||||
This is a combo module with both accelerometer and magnetometer.
|
||||
This driver is only implementing accelerometer part, which has
|
||||
its own address and register map.
|
||||
|
||||
config BMC150_ACCEL_I2C
|
||||
tristate
|
||||
select REGMAP_I2C
|
||||
|
||||
config BMC150_ACCEL_SPI
|
||||
tristate
|
||||
select REGMAP_SPI
|
||||
|
||||
config HID_SENSOR_ACCEL_3D
|
||||
depends on HID_SENSOR_HUB
|
||||
select IIO_BUFFER
|
||||
|
@ -4,7 +4,9 @@
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_BMA180) += bma180.o
|
||||
obj-$(CONFIG_BMC150_ACCEL) += bmc150-accel.o
|
||||
obj-$(CONFIG_BMC150_ACCEL) += bmc150-accel-core.o
|
||||
obj-$(CONFIG_BMC150_ACCEL_I2C) += bmc150-accel-i2c.o
|
||||
obj-$(CONFIG_BMC150_ACCEL_SPI) += bmc150-accel-spi.o
|
||||
obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
|
||||
obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o
|
||||
obj-$(CONFIG_KXSD9) += kxsd9.o
|
||||
|
@ -35,10 +35,12 @@
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "bmc150-accel.h"
|
||||
|
||||
#define BMC150_ACCEL_DRV_NAME "bmc150_accel"
|
||||
#define BMC150_ACCEL_IRQ_NAME "bmc150_accel_event"
|
||||
#define BMC150_ACCEL_GPIO_NAME "bmc150_accel_int"
|
||||
|
||||
#define BMC150_ACCEL_REG_CHIP_ID 0x00
|
||||
|
||||
@ -185,7 +187,9 @@ enum bmc150_accel_trigger_id {
|
||||
};
|
||||
|
||||
struct bmc150_accel_data {
|
||||
struct i2c_client *client;
|
||||
struct regmap *regmap;
|
||||
struct device *dev;
|
||||
int irq;
|
||||
struct bmc150_accel_interrupt interrupts[BMC150_ACCEL_INTERRUPTS];
|
||||
atomic_t active_intr;
|
||||
struct bmc150_accel_trigger triggers[BMC150_ACCEL_TRIGGERS];
|
||||
@ -242,6 +246,12 @@ static const struct {
|
||||
{500000, BMC150_ACCEL_SLEEP_500_MS},
|
||||
{1000000, BMC150_ACCEL_SLEEP_1_SEC} };
|
||||
|
||||
static const struct regmap_config bmc150_i2c_regmap_conf = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = 0x3f,
|
||||
};
|
||||
|
||||
static int bmc150_accel_set_mode(struct bmc150_accel_data *data,
|
||||
enum bmc150_power_modes mode,
|
||||
int dur_us)
|
||||
@ -269,12 +279,11 @@ static int bmc150_accel_set_mode(struct bmc150_accel_data *data,
|
||||
lpw_bits = mode << BMC150_ACCEL_PMU_MODE_SHIFT;
|
||||
lpw_bits |= (dur_val << BMC150_ACCEL_PMU_BIT_SLEEP_DUR_SHIFT);
|
||||
|
||||
dev_dbg(&data->client->dev, "Set Mode bits %x\n", lpw_bits);
|
||||
dev_dbg(data->dev, "Set Mode bits %x\n", lpw_bits);
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
BMC150_ACCEL_REG_PMU_LPW, lpw_bits);
|
||||
ret = regmap_write(data->regmap, BMC150_ACCEL_REG_PMU_LPW, lpw_bits);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error writing reg_pmu_lpw\n");
|
||||
dev_err(data->dev, "Error writing reg_pmu_lpw\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -290,8 +299,7 @@ static int bmc150_accel_set_bw(struct bmc150_accel_data *data, int val,
|
||||
for (i = 0; i < ARRAY_SIZE(bmc150_accel_samp_freq_table); ++i) {
|
||||
if (bmc150_accel_samp_freq_table[i].val == val &&
|
||||
bmc150_accel_samp_freq_table[i].val2 == val2) {
|
||||
ret = i2c_smbus_write_byte_data(
|
||||
data->client,
|
||||
ret = regmap_write(data->regmap,
|
||||
BMC150_ACCEL_REG_PMU_BW,
|
||||
bmc150_accel_samp_freq_table[i].bw_bits);
|
||||
if (ret < 0)
|
||||
@ -308,30 +316,23 @@ static int bmc150_accel_set_bw(struct bmc150_accel_data *data, int val,
|
||||
|
||||
static int bmc150_accel_update_slope(struct bmc150_accel_data *data)
|
||||
{
|
||||
int ret, val;
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client, BMC150_ACCEL_REG_INT_6,
|
||||
ret = regmap_write(data->regmap, BMC150_ACCEL_REG_INT_6,
|
||||
data->slope_thres);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error writing reg_int_6\n");
|
||||
dev_err(data->dev, "Error writing reg_int_6\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_read_byte_data(data->client, BMC150_ACCEL_REG_INT_5);
|
||||
ret = regmap_update_bits(data->regmap, BMC150_ACCEL_REG_INT_5,
|
||||
BMC150_ACCEL_SLOPE_DUR_MASK, data->slope_dur);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error reading reg_int_5\n");
|
||||
dev_err(data->dev, "Error updating reg_int_5\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
val = (ret & ~BMC150_ACCEL_SLOPE_DUR_MASK) | data->slope_dur;
|
||||
ret = i2c_smbus_write_byte_data(data->client, BMC150_ACCEL_REG_INT_5,
|
||||
val);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error write reg_int_5\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(&data->client->dev, "%s: %x %x\n", __func__, data->slope_thres,
|
||||
dev_dbg(data->dev, "%s: %x %x\n", __func__, data->slope_thres,
|
||||
data->slope_dur);
|
||||
|
||||
return ret;
|
||||
@ -380,17 +381,17 @@ static int bmc150_accel_set_power_state(struct bmc150_accel_data *data, bool on)
|
||||
int ret;
|
||||
|
||||
if (on) {
|
||||
ret = pm_runtime_get_sync(&data->client->dev);
|
||||
ret = pm_runtime_get_sync(data->dev);
|
||||
} else {
|
||||
pm_runtime_mark_last_busy(&data->client->dev);
|
||||
ret = pm_runtime_put_autosuspend(&data->client->dev);
|
||||
pm_runtime_mark_last_busy(data->dev);
|
||||
ret = pm_runtime_put_autosuspend(data->dev);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev,
|
||||
dev_err(data->dev,
|
||||
"Failed: bmc150_accel_set_power_state for %d\n", on);
|
||||
if (on)
|
||||
pm_runtime_put_noidle(&data->client->dev);
|
||||
pm_runtime_put_noidle(data->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -470,38 +471,18 @@ static int bmc150_accel_set_interrupt(struct bmc150_accel_data *data, int i,
|
||||
return ret;
|
||||
|
||||
/* map the interrupt to the appropriate pins */
|
||||
ret = i2c_smbus_read_byte_data(data->client, info->map_reg);
|
||||
ret = regmap_update_bits(data->regmap, info->map_reg, info->map_bitmask,
|
||||
(state ? info->map_bitmask : 0));
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error reading reg_int_map\n");
|
||||
goto out_fix_power_state;
|
||||
}
|
||||
if (state)
|
||||
ret |= info->map_bitmask;
|
||||
else
|
||||
ret &= ~info->map_bitmask;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client, info->map_reg,
|
||||
ret);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error writing reg_int_map\n");
|
||||
dev_err(data->dev, "Error updating reg_int_map\n");
|
||||
goto out_fix_power_state;
|
||||
}
|
||||
|
||||
/* enable/disable the interrupt */
|
||||
ret = i2c_smbus_read_byte_data(data->client, info->en_reg);
|
||||
ret = regmap_update_bits(data->regmap, info->en_reg, info->en_bitmask,
|
||||
(state ? info->en_bitmask : 0));
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error reading reg_int_en\n");
|
||||
goto out_fix_power_state;
|
||||
}
|
||||
|
||||
if (state)
|
||||
ret |= info->en_bitmask;
|
||||
else
|
||||
ret &= ~info->en_bitmask;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client, info->en_reg, ret);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error writing reg_int_en\n");
|
||||
dev_err(data->dev, "Error updating reg_int_en\n");
|
||||
goto out_fix_power_state;
|
||||
}
|
||||
|
||||
@ -523,12 +504,11 @@ static int bmc150_accel_set_scale(struct bmc150_accel_data *data, int val)
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(data->chip_info->scale_table); ++i) {
|
||||
if (data->chip_info->scale_table[i].scale == val) {
|
||||
ret = i2c_smbus_write_byte_data(
|
||||
data->client,
|
||||
ret = regmap_write(data->regmap,
|
||||
BMC150_ACCEL_REG_PMU_RANGE,
|
||||
data->chip_info->scale_table[i].reg_range);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev,
|
||||
dev_err(data->dev,
|
||||
"Error writing pmu_range\n");
|
||||
return ret;
|
||||
}
|
||||
@ -544,16 +524,17 @@ static int bmc150_accel_set_scale(struct bmc150_accel_data *data, int val)
|
||||
static int bmc150_accel_get_temp(struct bmc150_accel_data *data, int *val)
|
||||
{
|
||||
int ret;
|
||||
unsigned int value;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
|
||||
ret = i2c_smbus_read_byte_data(data->client, BMC150_ACCEL_REG_TEMP);
|
||||
ret = regmap_read(data->regmap, BMC150_ACCEL_REG_TEMP, &value);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error reading reg_temp\n");
|
||||
dev_err(data->dev, "Error reading reg_temp\n");
|
||||
mutex_unlock(&data->mutex);
|
||||
return ret;
|
||||
}
|
||||
*val = sign_extend32(ret, 7);
|
||||
*val = sign_extend32(value, 7);
|
||||
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
@ -566,6 +547,7 @@ static int bmc150_accel_get_axis(struct bmc150_accel_data *data,
|
||||
{
|
||||
int ret;
|
||||
int axis = chan->scan_index;
|
||||
unsigned int raw_val;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
ret = bmc150_accel_set_power_state(data, true);
|
||||
@ -574,15 +556,15 @@ static int bmc150_accel_get_axis(struct bmc150_accel_data *data,
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_read_word_data(data->client,
|
||||
BMC150_ACCEL_AXIS_TO_REG(axis));
|
||||
ret = regmap_bulk_read(data->regmap, BMC150_ACCEL_AXIS_TO_REG(axis),
|
||||
&raw_val, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error reading axis %d\n", axis);
|
||||
dev_err(data->dev, "Error reading axis %d\n", axis);
|
||||
bmc150_accel_set_power_state(data, false);
|
||||
mutex_unlock(&data->mutex);
|
||||
return ret;
|
||||
}
|
||||
*val = sign_extend32(ret >> chan->scan_type.shift,
|
||||
*val = sign_extend32(raw_val >> chan->scan_type.shift,
|
||||
chan->scan_type.realbits - 1);
|
||||
ret = bmc150_accel_set_power_state(data, false);
|
||||
mutex_unlock(&data->mutex);
|
||||
@ -846,52 +828,34 @@ static int bmc150_accel_set_watermark(struct iio_dev *indio_dev, unsigned val)
|
||||
* We must read at least one full frame in one burst, otherwise the rest of the
|
||||
* frame data is discarded.
|
||||
*/
|
||||
static int bmc150_accel_fifo_transfer(const struct i2c_client *client,
|
||||
static int bmc150_accel_fifo_transfer(struct bmc150_accel_data *data,
|
||||
char *buffer, int samples)
|
||||
{
|
||||
int sample_length = 3 * 2;
|
||||
u8 reg_fifo_data = BMC150_ACCEL_REG_FIFO_DATA;
|
||||
int ret = -EIO;
|
||||
int ret;
|
||||
int total_length = samples * sample_length;
|
||||
int i;
|
||||
size_t step = regmap_get_raw_read_max(data->regmap);
|
||||
|
||||
if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
||||
struct i2c_msg msg[2] = {
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = 0,
|
||||
.buf = ®_fifo_data,
|
||||
.len = sizeof(reg_fifo_data),
|
||||
},
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.buf = (u8 *)buffer,
|
||||
.len = samples * sample_length,
|
||||
}
|
||||
};
|
||||
if (!step || step > total_length)
|
||||
step = total_length;
|
||||
else if (step < total_length)
|
||||
step = sample_length;
|
||||
|
||||
ret = i2c_transfer(client->adapter, msg, 2);
|
||||
if (ret != 2)
|
||||
ret = -EIO;
|
||||
else
|
||||
ret = 0;
|
||||
} else {
|
||||
int i, step = I2C_SMBUS_BLOCK_MAX / sample_length;
|
||||
|
||||
for (i = 0; i < samples * sample_length; i += step) {
|
||||
ret = i2c_smbus_read_i2c_block_data(client,
|
||||
reg_fifo_data, step,
|
||||
&buffer[i]);
|
||||
if (ret != step) {
|
||||
ret = -EIO;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
}
|
||||
/*
|
||||
* Seems we have a bus with size limitation so we have to execute
|
||||
* multiple reads
|
||||
*/
|
||||
for (i = 0; i < total_length; i += step) {
|
||||
ret = regmap_raw_read(data->regmap, BMC150_ACCEL_REG_FIFO_DATA,
|
||||
&buffer[i], step);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
dev_err(&client->dev, "Error transferring data from fifo\n");
|
||||
dev_err(data->dev, "Error transferring data from fifo in single steps of %zu\n",
|
||||
step);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -905,15 +869,15 @@ static int __bmc150_accel_fifo_flush(struct iio_dev *indio_dev,
|
||||
u16 buffer[BMC150_ACCEL_FIFO_LENGTH * 3];
|
||||
int64_t tstamp;
|
||||
uint64_t sample_period;
|
||||
unsigned int val;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(data->client,
|
||||
BMC150_ACCEL_REG_FIFO_STATUS);
|
||||
ret = regmap_read(data->regmap, BMC150_ACCEL_REG_FIFO_STATUS, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error reading reg_fifo_status\n");
|
||||
dev_err(data->dev, "Error reading reg_fifo_status\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
count = ret & 0x7F;
|
||||
count = val & 0x7F;
|
||||
|
||||
if (!count)
|
||||
return 0;
|
||||
@ -952,7 +916,7 @@ static int __bmc150_accel_fifo_flush(struct iio_dev *indio_dev,
|
||||
if (samples && count > samples)
|
||||
count = samples;
|
||||
|
||||
ret = bmc150_accel_fifo_transfer(data->client, (u8 *)buffer, count);
|
||||
ret = bmc150_accel_fifo_transfer(data, (u8 *)buffer, count);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -1052,15 +1016,6 @@ static const struct iio_chan_spec bmc150_accel_channels[] =
|
||||
static const struct iio_chan_spec bma280_accel_channels[] =
|
||||
BMC150_ACCEL_CHANNELS(14);
|
||||
|
||||
enum {
|
||||
bmc150,
|
||||
bmi055,
|
||||
bma255,
|
||||
bma250e,
|
||||
bma222e,
|
||||
bma280,
|
||||
};
|
||||
|
||||
static const struct bmc150_accel_chip_info bmc150_accel_chip_info_tbl[] = {
|
||||
[bmc150] = {
|
||||
.name = "BMC150A",
|
||||
@ -1155,17 +1110,19 @@ static irqreturn_t bmc150_accel_trigger_handler(int irq, void *p)
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct bmc150_accel_data *data = iio_priv(indio_dev);
|
||||
int bit, ret, i = 0;
|
||||
unsigned int raw_val;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
for_each_set_bit(bit, indio_dev->active_scan_mask,
|
||||
indio_dev->masklength) {
|
||||
ret = i2c_smbus_read_word_data(data->client,
|
||||
BMC150_ACCEL_AXIS_TO_REG(bit));
|
||||
ret = regmap_bulk_read(data->regmap,
|
||||
BMC150_ACCEL_AXIS_TO_REG(bit), &raw_val,
|
||||
2);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&data->mutex);
|
||||
goto err_read;
|
||||
}
|
||||
data->buffer[i++] = ret;
|
||||
data->buffer[i++] = raw_val;
|
||||
}
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
@ -1189,13 +1146,12 @@ static int bmc150_accel_trig_try_reen(struct iio_trigger *trig)
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
/* clear any latched interrupt */
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
BMC150_ACCEL_REG_INT_RST_LATCH,
|
||||
BMC150_ACCEL_INT_MODE_LATCH_INT |
|
||||
BMC150_ACCEL_INT_MODE_LATCH_RESET);
|
||||
ret = regmap_write(data->regmap, BMC150_ACCEL_REG_INT_RST_LATCH,
|
||||
BMC150_ACCEL_INT_MODE_LATCH_INT |
|
||||
BMC150_ACCEL_INT_MODE_LATCH_RESET);
|
||||
mutex_unlock(&data->mutex);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev,
|
||||
dev_err(data->dev,
|
||||
"Error writing reg_int_rst_latch\n");
|
||||
return ret;
|
||||
}
|
||||
@ -1249,20 +1205,20 @@ static int bmc150_accel_handle_roc_event(struct iio_dev *indio_dev)
|
||||
struct bmc150_accel_data *data = iio_priv(indio_dev);
|
||||
int dir;
|
||||
int ret;
|
||||
unsigned int val;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(data->client,
|
||||
BMC150_ACCEL_REG_INT_STATUS_2);
|
||||
ret = regmap_read(data->regmap, BMC150_ACCEL_REG_INT_STATUS_2, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error reading reg_int_status_2\n");
|
||||
dev_err(data->dev, "Error reading reg_int_status_2\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ret & BMC150_ACCEL_ANY_MOTION_BIT_SIGN)
|
||||
if (val & BMC150_ACCEL_ANY_MOTION_BIT_SIGN)
|
||||
dir = IIO_EV_DIR_FALLING;
|
||||
else
|
||||
dir = IIO_EV_DIR_RISING;
|
||||
|
||||
if (ret & BMC150_ACCEL_ANY_MOTION_BIT_X)
|
||||
if (val & BMC150_ACCEL_ANY_MOTION_BIT_X)
|
||||
iio_push_event(indio_dev,
|
||||
IIO_MOD_EVENT_CODE(IIO_ACCEL,
|
||||
0,
|
||||
@ -1271,7 +1227,7 @@ static int bmc150_accel_handle_roc_event(struct iio_dev *indio_dev)
|
||||
dir),
|
||||
data->timestamp);
|
||||
|
||||
if (ret & BMC150_ACCEL_ANY_MOTION_BIT_Y)
|
||||
if (val & BMC150_ACCEL_ANY_MOTION_BIT_Y)
|
||||
iio_push_event(indio_dev,
|
||||
IIO_MOD_EVENT_CODE(IIO_ACCEL,
|
||||
0,
|
||||
@ -1280,7 +1236,7 @@ static int bmc150_accel_handle_roc_event(struct iio_dev *indio_dev)
|
||||
dir),
|
||||
data->timestamp);
|
||||
|
||||
if (ret & BMC150_ACCEL_ANY_MOTION_BIT_Z)
|
||||
if (val & BMC150_ACCEL_ANY_MOTION_BIT_Z)
|
||||
iio_push_event(indio_dev,
|
||||
IIO_MOD_EVENT_CODE(IIO_ACCEL,
|
||||
0,
|
||||
@ -1315,13 +1271,11 @@ static irqreturn_t bmc150_accel_irq_thread_handler(int irq, void *private)
|
||||
}
|
||||
|
||||
if (ack) {
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
BMC150_ACCEL_REG_INT_RST_LATCH,
|
||||
BMC150_ACCEL_INT_MODE_LATCH_INT |
|
||||
BMC150_ACCEL_INT_MODE_LATCH_RESET);
|
||||
ret = regmap_write(data->regmap, BMC150_ACCEL_REG_INT_RST_LATCH,
|
||||
BMC150_ACCEL_INT_MODE_LATCH_INT |
|
||||
BMC150_ACCEL_INT_MODE_LATCH_RESET);
|
||||
if (ret)
|
||||
dev_err(&data->client->dev,
|
||||
"Error writing reg_int_rst_latch\n");
|
||||
dev_err(data->dev, "Error writing reg_int_rst_latch\n");
|
||||
|
||||
ret = IRQ_HANDLED;
|
||||
} else {
|
||||
@ -1360,32 +1314,6 @@ static irqreturn_t bmc150_accel_irq_handler(int irq, void *private)
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static int bmc150_accel_gpio_probe(struct i2c_client *client,
|
||||
struct bmc150_accel_data *data)
|
||||
{
|
||||
struct device *dev;
|
||||
struct gpio_desc *gpio;
|
||||
int ret;
|
||||
|
||||
if (!client)
|
||||
return -EINVAL;
|
||||
|
||||
dev = &client->dev;
|
||||
|
||||
/* data ready gpio interrupt pin */
|
||||
gpio = devm_gpiod_get_index(dev, BMC150_ACCEL_GPIO_NAME, 0, GPIOD_IN);
|
||||
if (IS_ERR(gpio)) {
|
||||
dev_err(dev, "Failed: gpio get index\n");
|
||||
return PTR_ERR(gpio);
|
||||
}
|
||||
|
||||
ret = gpiod_to_irq(gpio);
|
||||
|
||||
dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct {
|
||||
int intr;
|
||||
const char *name;
|
||||
@ -1423,7 +1351,7 @@ static int bmc150_accel_triggers_setup(struct iio_dev *indio_dev,
|
||||
for (i = 0; i < BMC150_ACCEL_TRIGGERS; i++) {
|
||||
struct bmc150_accel_trigger *t = &data->triggers[i];
|
||||
|
||||
t->indio_trig = devm_iio_trigger_alloc(&data->client->dev,
|
||||
t->indio_trig = devm_iio_trigger_alloc(data->dev,
|
||||
bmc150_accel_triggers[i].name,
|
||||
indio_dev->name,
|
||||
indio_dev->id);
|
||||
@ -1432,7 +1360,7 @@ static int bmc150_accel_triggers_setup(struct iio_dev *indio_dev,
|
||||
break;
|
||||
}
|
||||
|
||||
t->indio_trig->dev.parent = &data->client->dev;
|
||||
t->indio_trig->dev.parent = data->dev;
|
||||
t->indio_trig->ops = &bmc150_accel_trigger_ops;
|
||||
t->intr = bmc150_accel_triggers[i].intr;
|
||||
t->data = data;
|
||||
@ -1459,20 +1387,19 @@ static int bmc150_accel_fifo_set_mode(struct bmc150_accel_data *data)
|
||||
u8 reg = BMC150_ACCEL_REG_FIFO_CONFIG1;
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client, reg, data->fifo_mode);
|
||||
ret = regmap_write(data->regmap, reg, data->fifo_mode);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error writing reg_fifo_config1\n");
|
||||
dev_err(data->dev, "Error writing reg_fifo_config1\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!data->fifo_mode)
|
||||
return 0;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
BMC150_ACCEL_REG_FIFO_CONFIG0,
|
||||
data->watermark);
|
||||
ret = regmap_write(data->regmap, BMC150_ACCEL_REG_FIFO_CONFIG0,
|
||||
data->watermark);
|
||||
if (ret < 0)
|
||||
dev_err(&data->client->dev, "Error writing reg_fifo_config0\n");
|
||||
dev_err(data->dev, "Error writing reg_fifo_config0\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -1557,23 +1484,25 @@ static const struct iio_buffer_setup_ops bmc150_accel_buffer_ops = {
|
||||
static int bmc150_accel_chip_init(struct bmc150_accel_data *data)
|
||||
{
|
||||
int ret, i;
|
||||
unsigned int val;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(data->client, BMC150_ACCEL_REG_CHIP_ID);
|
||||
ret = regmap_read(data->regmap, BMC150_ACCEL_REG_CHIP_ID, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error: Reading chip id\n");
|
||||
dev_err(data->dev,
|
||||
"Error: Reading chip id\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(&data->client->dev, "Chip Id %x\n", ret);
|
||||
dev_dbg(data->dev, "Chip Id %x\n", val);
|
||||
for (i = 0; i < ARRAY_SIZE(bmc150_accel_chip_info_tbl); i++) {
|
||||
if (bmc150_accel_chip_info_tbl[i].chip_id == ret) {
|
||||
if (bmc150_accel_chip_info_tbl[i].chip_id == val) {
|
||||
data->chip_info = &bmc150_accel_chip_info_tbl[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!data->chip_info) {
|
||||
dev_err(&data->client->dev, "Unsupported chip %x\n", ret);
|
||||
dev_err(data->dev, "Invalid chip %x\n", val);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
@ -1587,11 +1516,11 @@ static int bmc150_accel_chip_init(struct bmc150_accel_data *data)
|
||||
return ret;
|
||||
|
||||
/* Set Default Range */
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
BMC150_ACCEL_REG_PMU_RANGE,
|
||||
BMC150_ACCEL_DEF_RANGE_4G);
|
||||
ret = regmap_write(data->regmap, BMC150_ACCEL_REG_PMU_RANGE,
|
||||
BMC150_ACCEL_DEF_RANGE_4G);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error writing reg_pmu_range\n");
|
||||
dev_err(data->dev,
|
||||
"Error writing reg_pmu_range\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1605,12 +1534,11 @@ static int bmc150_accel_chip_init(struct bmc150_accel_data *data)
|
||||
return ret;
|
||||
|
||||
/* Set default as latched interrupts */
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
BMC150_ACCEL_REG_INT_RST_LATCH,
|
||||
BMC150_ACCEL_INT_MODE_LATCH_INT |
|
||||
BMC150_ACCEL_INT_MODE_LATCH_RESET);
|
||||
ret = regmap_write(data->regmap, BMC150_ACCEL_REG_INT_RST_LATCH,
|
||||
BMC150_ACCEL_INT_MODE_LATCH_INT |
|
||||
BMC150_ACCEL_INT_MODE_LATCH_RESET);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev,
|
||||
dev_err(data->dev,
|
||||
"Error writing reg_int_rst_latch\n");
|
||||
return ret;
|
||||
}
|
||||
@ -1618,24 +1546,23 @@ static int bmc150_accel_chip_init(struct bmc150_accel_data *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bmc150_accel_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq,
|
||||
const char *name, bool block_supported)
|
||||
{
|
||||
struct bmc150_accel_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
const char *name = NULL;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
dev_set_drvdata(dev, indio_dev);
|
||||
data->dev = dev;
|
||||
data->irq = irq;
|
||||
|
||||
if (id)
|
||||
name = id->name;
|
||||
data->regmap = regmap;
|
||||
|
||||
ret = bmc150_accel_chip_init(data);
|
||||
if (ret < 0)
|
||||
@ -1643,7 +1570,7 @@ static int bmc150_accel_probe(struct i2c_client *client,
|
||||
|
||||
mutex_init(&data->mutex);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->channels = data->chip_info->channels;
|
||||
indio_dev->num_channels = data->chip_info->num_channels;
|
||||
indio_dev->name = name ? name : data->chip_info->name;
|
||||
@ -1655,16 +1582,13 @@ static int bmc150_accel_probe(struct i2c_client *client,
|
||||
bmc150_accel_trigger_handler,
|
||||
&bmc150_accel_buffer_ops);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Failed: iio triggered buffer setup\n");
|
||||
dev_err(data->dev, "Failed: iio triggered buffer setup\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (client->irq < 0)
|
||||
client->irq = bmc150_accel_gpio_probe(client, data);
|
||||
|
||||
if (client->irq > 0) {
|
||||
if (data->irq > 0) {
|
||||
ret = devm_request_threaded_irq(
|
||||
&client->dev, client->irq,
|
||||
data->dev, data->irq,
|
||||
bmc150_accel_irq_handler,
|
||||
bmc150_accel_irq_thread_handler,
|
||||
IRQF_TRIGGER_RISING,
|
||||
@ -1679,11 +1603,10 @@ static int bmc150_accel_probe(struct i2c_client *client,
|
||||
* want to use latch mode when we can to prevent interrupt
|
||||
* flooding.
|
||||
*/
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
BMC150_ACCEL_REG_INT_RST_LATCH,
|
||||
BMC150_ACCEL_INT_MODE_LATCH_RESET);
|
||||
ret = regmap_write(data->regmap, BMC150_ACCEL_REG_INT_RST_LATCH,
|
||||
BMC150_ACCEL_INT_MODE_LATCH_RESET);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error writing reg_int_rst_latch\n");
|
||||
dev_err(data->dev, "Error writing reg_int_rst_latch\n");
|
||||
goto err_buffer_cleanup;
|
||||
}
|
||||
|
||||
@ -1693,9 +1616,7 @@ static int bmc150_accel_probe(struct i2c_client *client,
|
||||
if (ret)
|
||||
goto err_buffer_cleanup;
|
||||
|
||||
if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) ||
|
||||
i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
|
||||
if (block_supported) {
|
||||
indio_dev->modes |= INDIO_BUFFER_SOFTWARE;
|
||||
indio_dev->info = &bmc150_accel_info_fifo;
|
||||
indio_dev->buffer->attrs = bmc150_accel_fifo_attributes;
|
||||
@ -1704,18 +1625,17 @@ static int bmc150_accel_probe(struct i2c_client *client,
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Unable to register iio device\n");
|
||||
dev_err(dev, "Unable to register iio device\n");
|
||||
goto err_trigger_unregister;
|
||||
}
|
||||
|
||||
ret = pm_runtime_set_active(&client->dev);
|
||||
ret = pm_runtime_set_active(dev);
|
||||
if (ret)
|
||||
goto err_iio_unregister;
|
||||
|
||||
pm_runtime_enable(&client->dev);
|
||||
pm_runtime_set_autosuspend_delay(&client->dev,
|
||||
BMC150_AUTO_SUSPEND_DELAY_MS);
|
||||
pm_runtime_use_autosuspend(&client->dev);
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_set_autosuspend_delay(dev, BMC150_AUTO_SUSPEND_DELAY_MS);
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
@ -1728,15 +1648,16 @@ err_buffer_cleanup:
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bmc150_accel_core_probe);
|
||||
|
||||
static int bmc150_accel_remove(struct i2c_client *client)
|
||||
int bmc150_accel_core_remove(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct bmc150_accel_data *data = iio_priv(indio_dev);
|
||||
|
||||
pm_runtime_disable(&client->dev);
|
||||
pm_runtime_set_suspended(&client->dev);
|
||||
pm_runtime_put_noidle(&client->dev);
|
||||
pm_runtime_disable(data->dev);
|
||||
pm_runtime_set_suspended(data->dev);
|
||||
pm_runtime_put_noidle(data->dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
@ -1750,11 +1671,12 @@ static int bmc150_accel_remove(struct i2c_client *client)
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bmc150_accel_core_remove);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int bmc150_accel_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct bmc150_accel_data *data = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
@ -1766,7 +1688,7 @@ static int bmc150_accel_suspend(struct device *dev)
|
||||
|
||||
static int bmc150_accel_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct bmc150_accel_data *data = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
@ -1782,11 +1704,11 @@ static int bmc150_accel_resume(struct device *dev)
|
||||
#ifdef CONFIG_PM
|
||||
static int bmc150_accel_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct bmc150_accel_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
dev_dbg(&data->client->dev, __func__);
|
||||
dev_dbg(data->dev, __func__);
|
||||
ret = bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_SUSPEND, 0);
|
||||
if (ret < 0)
|
||||
return -EAGAIN;
|
||||
@ -1796,12 +1718,12 @@ static int bmc150_accel_runtime_suspend(struct device *dev)
|
||||
|
||||
static int bmc150_accel_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct bmc150_accel_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
int sleep_val;
|
||||
|
||||
dev_dbg(&data->client->dev, __func__);
|
||||
dev_dbg(data->dev, __func__);
|
||||
|
||||
ret = bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_NORMAL, 0);
|
||||
if (ret < 0)
|
||||
@ -1820,47 +1742,12 @@ static int bmc150_accel_runtime_resume(struct device *dev)
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops bmc150_accel_pm_ops = {
|
||||
const struct dev_pm_ops bmc150_accel_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(bmc150_accel_suspend, bmc150_accel_resume)
|
||||
SET_RUNTIME_PM_OPS(bmc150_accel_runtime_suspend,
|
||||
bmc150_accel_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct acpi_device_id bmc150_accel_acpi_match[] = {
|
||||
{"BSBA0150", bmc150},
|
||||
{"BMC150A", bmc150},
|
||||
{"BMI055A", bmi055},
|
||||
{"BMA0255", bma255},
|
||||
{"BMA250E", bma250e},
|
||||
{"BMA222E", bma222e},
|
||||
{"BMA0280", bma280},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, bmc150_accel_acpi_match);
|
||||
|
||||
static const struct i2c_device_id bmc150_accel_id[] = {
|
||||
{"bmc150_accel", bmc150},
|
||||
{"bmi055_accel", bmi055},
|
||||
{"bma255", bma255},
|
||||
{"bma250e", bma250e},
|
||||
{"bma222e", bma222e},
|
||||
{"bma280", bma280},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, bmc150_accel_id);
|
||||
|
||||
static struct i2c_driver bmc150_accel_driver = {
|
||||
.driver = {
|
||||
.name = BMC150_ACCEL_DRV_NAME,
|
||||
.acpi_match_table = ACPI_PTR(bmc150_accel_acpi_match),
|
||||
.pm = &bmc150_accel_pm_ops,
|
||||
},
|
||||
.probe = bmc150_accel_probe,
|
||||
.remove = bmc150_accel_remove,
|
||||
.id_table = bmc150_accel_id,
|
||||
};
|
||||
module_i2c_driver(bmc150_accel_driver);
|
||||
EXPORT_SYMBOL_GPL(bmc150_accel_pm_ops);
|
||||
|
||||
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
102
drivers/iio/accel/bmc150-accel-i2c.c
Normal file
102
drivers/iio/accel/bmc150-accel-i2c.c
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 3-axis accelerometer driver supporting following I2C Bosch-Sensortec chips:
|
||||
* - BMC150
|
||||
* - BMI055
|
||||
* - BMA255
|
||||
* - BMA250E
|
||||
* - BMA222E
|
||||
* - BMA280
|
||||
*
|
||||
* Copyright (c) 2014, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "bmc150-accel.h"
|
||||
|
||||
static const struct regmap_config bmc150_i2c_regmap_conf = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
|
||||
static int bmc150_accel_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
const char *name = NULL;
|
||||
bool block_supported =
|
||||
i2c_check_functionality(client->adapter, I2C_FUNC_I2C) ||
|
||||
i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_READ_I2C_BLOCK);
|
||||
|
||||
regmap = devm_regmap_init_i2c(client, &bmc150_i2c_regmap_conf);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&client->dev, "Failed to initialize i2c regmap\n");
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
if (id)
|
||||
name = id->name;
|
||||
|
||||
return bmc150_accel_core_probe(&client->dev, regmap, client->irq, name,
|
||||
block_supported);
|
||||
}
|
||||
|
||||
static int bmc150_accel_remove(struct i2c_client *client)
|
||||
{
|
||||
return bmc150_accel_core_remove(&client->dev);
|
||||
}
|
||||
|
||||
static const struct acpi_device_id bmc150_accel_acpi_match[] = {
|
||||
{"BSBA0150", bmc150},
|
||||
{"BMC150A", bmc150},
|
||||
{"BMI055A", bmi055},
|
||||
{"BMA0255", bma255},
|
||||
{"BMA250E", bma250e},
|
||||
{"BMA222E", bma222e},
|
||||
{"BMA0280", bma280},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, bmc150_accel_acpi_match);
|
||||
|
||||
static const struct i2c_device_id bmc150_accel_id[] = {
|
||||
{"bmc150_accel", bmc150},
|
||||
{"bmi055_accel", bmi055},
|
||||
{"bma255", bma255},
|
||||
{"bma250e", bma250e},
|
||||
{"bma222e", bma222e},
|
||||
{"bma280", bma280},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, bmc150_accel_id);
|
||||
|
||||
static struct i2c_driver bmc150_accel_driver = {
|
||||
.driver = {
|
||||
.name = "bmc150_accel_i2c",
|
||||
.acpi_match_table = ACPI_PTR(bmc150_accel_acpi_match),
|
||||
.pm = &bmc150_accel_pm_ops,
|
||||
},
|
||||
.probe = bmc150_accel_probe,
|
||||
.remove = bmc150_accel_remove,
|
||||
.id_table = bmc150_accel_id,
|
||||
};
|
||||
module_i2c_driver(bmc150_accel_driver);
|
||||
|
||||
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("BMC150 I2C accelerometer driver");
|
91
drivers/iio/accel/bmc150-accel-spi.c
Normal file
91
drivers/iio/accel/bmc150-accel-spi.c
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 3-axis accelerometer driver supporting SPI Bosch-Sensortec accelerometer chip
|
||||
* Copyright © 2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include "bmc150-accel.h"
|
||||
|
||||
static const struct regmap_config bmc150_spi_regmap_conf = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = 0x3f,
|
||||
};
|
||||
|
||||
static int bmc150_accel_probe(struct spi_device *spi)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
|
||||
regmap = devm_regmap_init_spi(spi, &bmc150_spi_regmap_conf);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&spi->dev, "Failed to initialize spi regmap\n");
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
return bmc150_accel_core_probe(&spi->dev, regmap, spi->irq, id->name,
|
||||
true);
|
||||
}
|
||||
|
||||
static int bmc150_accel_remove(struct spi_device *spi)
|
||||
{
|
||||
return bmc150_accel_core_remove(&spi->dev);
|
||||
}
|
||||
|
||||
static const struct acpi_device_id bmc150_accel_acpi_match[] = {
|
||||
{"BSBA0150", bmc150},
|
||||
{"BMC150A", bmc150},
|
||||
{"BMI055A", bmi055},
|
||||
{"BMA0255", bma255},
|
||||
{"BMA250E", bma250e},
|
||||
{"BMA222E", bma222e},
|
||||
{"BMA0280", bma280},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, bmc150_accel_acpi_match);
|
||||
|
||||
static const struct spi_device_id bmc150_accel_id[] = {
|
||||
{"bmc150_accel", bmc150},
|
||||
{"bmi055_accel", bmi055},
|
||||
{"bma255", bma255},
|
||||
{"bma250e", bma250e},
|
||||
{"bma222e", bma222e},
|
||||
{"bma280", bma280},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, bmc150_accel_id);
|
||||
|
||||
static struct spi_driver bmc150_accel_driver = {
|
||||
.driver = {
|
||||
.name = "bmc150_accel_spi",
|
||||
.acpi_match_table = ACPI_PTR(bmc150_accel_acpi_match),
|
||||
.pm = &bmc150_accel_pm_ops,
|
||||
},
|
||||
.probe = bmc150_accel_probe,
|
||||
.remove = bmc150_accel_remove,
|
||||
.id_table = bmc150_accel_id,
|
||||
};
|
||||
module_spi_driver(bmc150_accel_driver);
|
||||
|
||||
MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("BMC150 SPI accelerometer driver");
|
20
drivers/iio/accel/bmc150-accel.h
Normal file
20
drivers/iio/accel/bmc150-accel.h
Normal file
@ -0,0 +1,20 @@
|
||||
#ifndef _BMC150_ACCEL_H_
|
||||
#define _BMC150_ACCEL_H_
|
||||
|
||||
struct regmap;
|
||||
|
||||
enum {
|
||||
bmc150,
|
||||
bmi055,
|
||||
bma255,
|
||||
bma250e,
|
||||
bma222e,
|
||||
bma280,
|
||||
};
|
||||
|
||||
int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq,
|
||||
const char *name, bool block_supported);
|
||||
int bmc150_accel_core_remove(struct device *dev);
|
||||
extern const struct dev_pm_ops bmc150_accel_pm_ops;
|
||||
|
||||
#endif /* _BMC150_ACCEL_H_ */
|
@ -1162,35 +1162,6 @@ static const char *kxcjk1013_match_acpi_device(struct device *dev,
|
||||
return dev_name(dev);
|
||||
}
|
||||
|
||||
static int kxcjk1013_gpio_probe(struct i2c_client *client,
|
||||
struct kxcjk1013_data *data)
|
||||
{
|
||||
struct device *dev;
|
||||
struct gpio_desc *gpio;
|
||||
int ret;
|
||||
|
||||
if (!client)
|
||||
return -EINVAL;
|
||||
|
||||
if (data->is_smo8500_device)
|
||||
return -ENOTSUPP;
|
||||
|
||||
dev = &client->dev;
|
||||
|
||||
/* data ready gpio interrupt pin */
|
||||
gpio = devm_gpiod_get_index(dev, "kxcjk1013_int", 0, GPIOD_IN);
|
||||
if (IS_ERR(gpio)) {
|
||||
dev_err(dev, "acpi gpio get index failed\n");
|
||||
return PTR_ERR(gpio);
|
||||
}
|
||||
|
||||
ret = gpiod_to_irq(gpio);
|
||||
|
||||
dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int kxcjk1013_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
@ -1237,10 +1208,7 @@ static int kxcjk1013_probe(struct i2c_client *client,
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &kxcjk1013_info;
|
||||
|
||||
if (client->irq < 0)
|
||||
client->irq = kxcjk1013_gpio_probe(client, data);
|
||||
|
||||
if (client->irq > 0) {
|
||||
if (client->irq > 0 && !data->is_smo8500_device) {
|
||||
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
kxcjk1013_data_rdy_trig_poll,
|
||||
kxcjk1013_event_handler,
|
||||
|
@ -26,7 +26,6 @@
|
||||
|
||||
#define MMA9553_DRV_NAME "mma9553"
|
||||
#define MMA9553_IRQ_NAME "mma9553_event"
|
||||
#define MMA9553_GPIO_NAME "mma9553_int"
|
||||
|
||||
/* Pedometer configuration registers (R/W) */
|
||||
#define MMA9553_REG_CONF_SLEEPMIN 0x00
|
||||
@ -1073,31 +1072,6 @@ static irqreturn_t mma9553_event_handler(int irq, void *private)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int mma9553_gpio_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev;
|
||||
struct gpio_desc *gpio;
|
||||
int ret;
|
||||
|
||||
if (!client)
|
||||
return -EINVAL;
|
||||
|
||||
dev = &client->dev;
|
||||
|
||||
/* data ready GPIO interrupt pin */
|
||||
gpio = devm_gpiod_get_index(dev, MMA9553_GPIO_NAME, 0, GPIOD_IN);
|
||||
if (IS_ERR(gpio)) {
|
||||
dev_err(dev, "ACPI GPIO get index failed\n");
|
||||
return PTR_ERR(gpio);
|
||||
}
|
||||
|
||||
ret = gpiod_to_irq(gpio);
|
||||
|
||||
dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char *mma9553_match_acpi_device(struct device *dev)
|
||||
{
|
||||
const struct acpi_device_id *id;
|
||||
@ -1146,9 +1120,6 @@ static int mma9553_probe(struct i2c_client *client,
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &mma9553_info;
|
||||
|
||||
if (client->irq < 0)
|
||||
client->irq = mma9553_gpio_probe(client);
|
||||
|
||||
if (client->irq > 0) {
|
||||
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
mma9553_irq_handler,
|
||||
|
@ -50,7 +50,6 @@
|
||||
#define STK8312_ALL_CHANNEL_SIZE 3
|
||||
|
||||
#define STK8312_DRIVER_NAME "stk8312"
|
||||
#define STK8312_GPIO "stk8312_gpio"
|
||||
#define STK8312_IRQ_NAME "stk8312_event"
|
||||
|
||||
/*
|
||||
@ -504,30 +503,6 @@ static const struct iio_buffer_setup_ops stk8312_buffer_setup_ops = {
|
||||
.postdisable = stk8312_buffer_postdisable,
|
||||
};
|
||||
|
||||
static int stk8312_gpio_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev;
|
||||
struct gpio_desc *gpio;
|
||||
int ret;
|
||||
|
||||
if (!client)
|
||||
return -EINVAL;
|
||||
|
||||
dev = &client->dev;
|
||||
|
||||
/* data ready gpio interrupt pin */
|
||||
gpio = devm_gpiod_get_index(dev, STK8312_GPIO, 0, GPIOD_IN);
|
||||
if (IS_ERR(gpio)) {
|
||||
dev_err(dev, "acpi gpio get index failed\n");
|
||||
return PTR_ERR(gpio);
|
||||
}
|
||||
|
||||
ret = gpiod_to_irq(gpio);
|
||||
dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stk8312_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
@ -569,10 +544,7 @@ static int stk8312_probe(struct i2c_client *client,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (client->irq < 0)
|
||||
client->irq = stk8312_gpio_probe(client);
|
||||
|
||||
if (client->irq >= 0) {
|
||||
if (client->irq > 0) {
|
||||
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
stk8312_data_rdy_trig_poll,
|
||||
NULL,
|
||||
|
@ -45,7 +45,6 @@
|
||||
#define STK8BA50_ALL_CHANNEL_SIZE 6
|
||||
|
||||
#define STK8BA50_DRIVER_NAME "stk8ba50"
|
||||
#define STK8BA50_GPIO "stk8ba50_gpio"
|
||||
#define STK8BA50_IRQ_NAME "stk8ba50_event"
|
||||
|
||||
#define STK8BA50_SCALE_AVAIL "0.0384 0.0767 0.1534 0.3069"
|
||||
@ -388,30 +387,6 @@ static const struct iio_buffer_setup_ops stk8ba50_buffer_setup_ops = {
|
||||
.postdisable = stk8ba50_buffer_postdisable,
|
||||
};
|
||||
|
||||
static int stk8ba50_gpio_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev;
|
||||
struct gpio_desc *gpio;
|
||||
int ret;
|
||||
|
||||
if (!client)
|
||||
return -EINVAL;
|
||||
|
||||
dev = &client->dev;
|
||||
|
||||
/* data ready gpio interrupt pin */
|
||||
gpio = devm_gpiod_get_index(dev, STK8BA50_GPIO, 0, GPIOD_IN);
|
||||
if (IS_ERR(gpio)) {
|
||||
dev_err(dev, "acpi gpio get index failed\n");
|
||||
return PTR_ERR(gpio);
|
||||
}
|
||||
|
||||
ret = gpiod_to_irq(gpio);
|
||||
dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stk8ba50_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
@ -465,10 +440,7 @@ static int stk8ba50_probe(struct i2c_client *client,
|
||||
goto err_power_off;
|
||||
}
|
||||
|
||||
if (client->irq < 0)
|
||||
client->irq = stk8ba50_gpio_probe(client);
|
||||
|
||||
if (client->irq >= 0) {
|
||||
if (client->irq > 0) {
|
||||
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
stk8ba50_data_rdy_trig_poll,
|
||||
NULL,
|
||||
|
@ -528,7 +528,6 @@ static struct attribute *ad799x_event_attributes[] = {
|
||||
|
||||
static struct attribute_group ad799x_event_attrs_group = {
|
||||
.attrs = ad799x_event_attributes,
|
||||
.name = "events",
|
||||
};
|
||||
|
||||
static const struct iio_info ad7991_info = {
|
||||
|
@ -1007,7 +1007,6 @@ static struct attribute *max1363_event_attributes[] = {
|
||||
|
||||
static struct attribute_group max1363_event_attribute_group = {
|
||||
.attrs = max1363_event_attributes,
|
||||
.name = "events",
|
||||
};
|
||||
|
||||
static int max1363_update_scan_mode(struct iio_dev *indio_dev,
|
||||
|
@ -85,6 +85,21 @@ static const struct attribute_group vz89x_attrs_group = {
|
||||
.attrs = vz89x_attributes,
|
||||
};
|
||||
|
||||
/*
|
||||
* Chipset sometime updates in the middle of a reading causing it to reset the
|
||||
* data pointer, and causing invalid reading of previous data.
|
||||
* We can check for this by reading MSB of the resistance reading that is
|
||||
* always zero, and by also confirming the VOC_short isn't zero.
|
||||
*/
|
||||
|
||||
static int vz89x_measurement_is_valid(struct vz89x_data *data)
|
||||
{
|
||||
if (data->buffer[VZ89X_VOC_SHORT_IDX] == 0)
|
||||
return 1;
|
||||
|
||||
return !!(data->buffer[VZ89X_REG_MEASUREMENT_SIZE - 1] > 0);
|
||||
}
|
||||
|
||||
static int vz89x_get_measurement(struct vz89x_data *data)
|
||||
{
|
||||
int ret;
|
||||
@ -106,6 +121,10 @@ static int vz89x_get_measurement(struct vz89x_data *data)
|
||||
data->buffer[i] = ret;
|
||||
}
|
||||
|
||||
ret = vz89x_measurement_is_valid(data);
|
||||
if (ret)
|
||||
return -EAGAIN;
|
||||
|
||||
data->last_update = jiffies;
|
||||
|
||||
return 0;
|
||||
@ -113,9 +132,9 @@ static int vz89x_get_measurement(struct vz89x_data *data)
|
||||
|
||||
static int vz89x_get_resistance_reading(struct vz89x_data *data)
|
||||
{
|
||||
u8 *buf = &data->buffer[VZ89X_VOC_TVOC_IDX];
|
||||
u8 *buf = &data->buffer[VZ89X_VOC_RESISTANCE_IDX];
|
||||
|
||||
return buf[0] | (buf[1] << 8) | (buf[2] << 16);
|
||||
return buf[0] | (buf[1] << 8);
|
||||
}
|
||||
|
||||
static int vz89x_read_raw(struct iio_dev *indio_dev,
|
||||
|
@ -3,5 +3,6 @@
|
||||
#
|
||||
|
||||
source "drivers/iio/common/hid-sensors/Kconfig"
|
||||
source "drivers/iio/common/ms_sensors/Kconfig"
|
||||
source "drivers/iio/common/ssp_sensors/Kconfig"
|
||||
source "drivers/iio/common/st_sensors/Kconfig"
|
||||
|
@ -8,5 +8,6 @@
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-y += hid-sensors/
|
||||
obj-y += ms_sensors/
|
||||
obj-y += ssp_sensors/
|
||||
obj-y += st_sensors/
|
||||
|
6
drivers/iio/common/ms_sensors/Kconfig
Normal file
6
drivers/iio/common/ms_sensors/Kconfig
Normal file
@ -0,0 +1,6 @@
|
||||
#
|
||||
# Measurements Specialties sensors common library
|
||||
#
|
||||
|
||||
config IIO_MS_SENSORS_I2C
|
||||
tristate
|
5
drivers/iio/common/ms_sensors/Makefile
Normal file
5
drivers/iio/common/ms_sensors/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
#
|
||||
# Makefile for the Measurement Specialties sensor common modules.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_IIO_MS_SENSORS_I2C) += ms_sensors_i2c.o
|
652
drivers/iio/common/ms_sensors/ms_sensors_i2c.c
Normal file
652
drivers/iio/common/ms_sensors/ms_sensors_i2c.c
Normal file
@ -0,0 +1,652 @@
|
||||
/*
|
||||
* Measurements Specialties driver common i2c functions
|
||||
*
|
||||
* Copyright (c) 2015 Measurement-Specialties
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "ms_sensors_i2c.h"
|
||||
|
||||
/* Conversion times in us */
|
||||
static const u16 ms_sensors_ht_t_conversion_time[] = { 50000, 25000,
|
||||
13000, 7000 };
|
||||
static const u16 ms_sensors_ht_h_conversion_time[] = { 16000, 3000,
|
||||
5000, 8000 };
|
||||
static const u16 ms_sensors_tp_conversion_time[] = { 500, 1100, 2100,
|
||||
4100, 8220, 16440 };
|
||||
|
||||
#define MS_SENSORS_SERIAL_READ_MSB 0xFA0F
|
||||
#define MS_SENSORS_SERIAL_READ_LSB 0xFCC9
|
||||
#define MS_SENSORS_CONFIG_REG_WRITE 0xE6
|
||||
#define MS_SENSORS_CONFIG_REG_READ 0xE7
|
||||
#define MS_SENSORS_HT_T_CONVERSION_START 0xF3
|
||||
#define MS_SENSORS_HT_H_CONVERSION_START 0xF5
|
||||
|
||||
#define MS_SENSORS_TP_PROM_READ 0xA0
|
||||
#define MS_SENSORS_TP_T_CONVERSION_START 0x50
|
||||
#define MS_SENSORS_TP_P_CONVERSION_START 0x40
|
||||
#define MS_SENSORS_TP_ADC_READ 0x00
|
||||
|
||||
#define MS_SENSORS_NO_READ_CMD 0xFF
|
||||
|
||||
/**
|
||||
* ms_sensors_reset() - Reset function
|
||||
* @cli: pointer to device client
|
||||
* @cmd: reset cmd. Depends on device in use
|
||||
* @delay: usleep minimal delay after reset command is issued
|
||||
*
|
||||
* Generic I2C reset function for Measurement Specialties devices.
|
||||
*
|
||||
* Return: 0 on success, negative errno otherwise.
|
||||
*/
|
||||
int ms_sensors_reset(void *cli, u8 cmd, unsigned int delay)
|
||||
{
|
||||
int ret;
|
||||
struct i2c_client *client = cli;
|
||||
|
||||
ret = i2c_smbus_write_byte(client, cmd);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "Failed to reset device\n");
|
||||
return ret;
|
||||
}
|
||||
usleep_range(delay, delay + 1000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ms_sensors_reset);
|
||||
|
||||
/**
|
||||
* ms_sensors_read_prom_word() - PROM word read function
|
||||
* @cli: pointer to device client
|
||||
* @cmd: PROM read cmd. Depends on device and prom id
|
||||
* @word: pointer to word destination value
|
||||
*
|
||||
* Generic i2c prom word read function for Measurement Specialties devices.
|
||||
*
|
||||
* Return: 0 on success, negative errno otherwise.
|
||||
*/
|
||||
int ms_sensors_read_prom_word(void *cli, int cmd, u16 *word)
|
||||
{
|
||||
int ret;
|
||||
struct i2c_client *client = (struct i2c_client *)cli;
|
||||
|
||||
ret = i2c_smbus_read_word_swapped(client, cmd);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Failed to read prom word\n");
|
||||
return ret;
|
||||
}
|
||||
*word = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ms_sensors_read_prom_word);
|
||||
|
||||
/**
|
||||
* ms_sensors_convert_and_read() - ADC conversion & read function
|
||||
* @cli: pointer to device client
|
||||
* @conv: ADC conversion command. Depends on device in use
|
||||
* @rd: ADC read command. Depends on device in use
|
||||
* @delay: usleep minimal delay after conversion command is issued
|
||||
* @adc: pointer to ADC destination value
|
||||
*
|
||||
* Generic ADC conversion & read function for Measurement Specialties
|
||||
* devices.
|
||||
* The function will issue conversion command, sleep appopriate delay, and
|
||||
* issue command to read ADC.
|
||||
*
|
||||
* Return: 0 on success, negative errno otherwise.
|
||||
*/
|
||||
int ms_sensors_convert_and_read(void *cli, u8 conv, u8 rd,
|
||||
unsigned int delay, u32 *adc)
|
||||
{
|
||||
int ret;
|
||||
__be32 buf = 0;
|
||||
struct i2c_client *client = (struct i2c_client *)cli;
|
||||
|
||||
/* Trigger conversion */
|
||||
ret = i2c_smbus_write_byte(client, conv);
|
||||
if (ret)
|
||||
goto err;
|
||||
usleep_range(delay, delay + 1000);
|
||||
|
||||
/* Retrieve ADC value */
|
||||
if (rd != MS_SENSORS_NO_READ_CMD)
|
||||
ret = i2c_smbus_read_i2c_block_data(client, rd, 3, (u8 *)&buf);
|
||||
else
|
||||
ret = i2c_master_recv(client, (u8 *)&buf, 3);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
dev_dbg(&client->dev, "ADC raw value : %x\n", be32_to_cpu(buf) >> 8);
|
||||
*adc = be32_to_cpu(buf) >> 8;
|
||||
|
||||
return 0;
|
||||
err:
|
||||
dev_err(&client->dev, "Unable to make sensor adc conversion\n");
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(ms_sensors_convert_and_read);
|
||||
|
||||
/**
|
||||
* ms_sensors_crc_valid() - CRC check function
|
||||
* @value: input and CRC compare value
|
||||
*
|
||||
* Cyclic Redundancy Check function used in TSYS02D, HTU21, MS8607.
|
||||
* This function performs a x^8 + x^5 + x^4 + 1 polynomial CRC.
|
||||
* The argument contains CRC value in LSB byte while the bytes 1 and 2
|
||||
* are used for CRC computation.
|
||||
*
|
||||
* Return: 1 if CRC is valid, 0 otherwise.
|
||||
*/
|
||||
static bool ms_sensors_crc_valid(u32 value)
|
||||
{
|
||||
u32 polynom = 0x988000; /* x^8 + x^5 + x^4 + 1 */
|
||||
u32 msb = 0x800000;
|
||||
u32 mask = 0xFF8000;
|
||||
u32 result = value & 0xFFFF00;
|
||||
u8 crc = value & 0xFF;
|
||||
|
||||
while (msb != 0x80) {
|
||||
if (result & msb)
|
||||
result = ((result ^ polynom) & mask)
|
||||
| (result & ~mask);
|
||||
msb >>= 1;
|
||||
mask >>= 1;
|
||||
polynom >>= 1;
|
||||
}
|
||||
|
||||
return result == crc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ms_sensors_read_serial() - Serial number read function
|
||||
* @cli: pointer to i2c client
|
||||
* @sn: pointer to 64-bits destination value
|
||||
*
|
||||
* Generic i2c serial number read function for Measurement Specialties devices.
|
||||
* This function is used for TSYS02d, HTU21, MS8607 chipset.
|
||||
* Refer to datasheet:
|
||||
* http://www.meas-spec.com/downloads/HTU2X_Serial_Number_Reading.pdf
|
||||
*
|
||||
* Sensor raw MSB serial number format is the following :
|
||||
* [ SNB3, CRC, SNB2, CRC, SNB1, CRC, SNB0, CRC]
|
||||
* Sensor raw LSB serial number format is the following :
|
||||
* [ X, X, SNC1, SNC0, CRC, SNA1, SNA0, CRC]
|
||||
* The resulting serial number is following :
|
||||
* [ SNA1, SNA0, SNB3, SNB2, SNB1, SNB0, SNC1, SNC0]
|
||||
*
|
||||
* Return: 0 on success, negative errno otherwise.
|
||||
*/
|
||||
int ms_sensors_read_serial(struct i2c_client *client, u64 *sn)
|
||||
{
|
||||
u8 i;
|
||||
__be64 rcv_buf = 0;
|
||||
u64 rcv_val;
|
||||
__be16 send_buf;
|
||||
int ret;
|
||||
|
||||
struct i2c_msg msg[2] = {
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = client->flags,
|
||||
.len = 2,
|
||||
.buf = (__u8 *)&send_buf,
|
||||
},
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = client->flags | I2C_M_RD,
|
||||
.buf = (__u8 *)&rcv_buf,
|
||||
},
|
||||
};
|
||||
|
||||
/* Read MSB part of serial number */
|
||||
send_buf = cpu_to_be16(MS_SENSORS_SERIAL_READ_MSB);
|
||||
msg[1].len = 8;
|
||||
ret = i2c_transfer(client->adapter, msg, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Unable to read device serial number");
|
||||
return ret;
|
||||
}
|
||||
|
||||
rcv_val = be64_to_cpu(rcv_buf);
|
||||
dev_dbg(&client->dev, "Serial MSB raw : %llx\n", rcv_val);
|
||||
|
||||
for (i = 0; i < 64; i += 16) {
|
||||
if (!ms_sensors_crc_valid((rcv_val >> i) & 0xFFFF))
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
*sn = (((rcv_val >> 32) & 0xFF000000) |
|
||||
((rcv_val >> 24) & 0x00FF0000) |
|
||||
((rcv_val >> 16) & 0x0000FF00) |
|
||||
((rcv_val >> 8) & 0x000000FF)) << 16;
|
||||
|
||||
/* Read LSB part of serial number */
|
||||
send_buf = cpu_to_be16(MS_SENSORS_SERIAL_READ_LSB);
|
||||
msg[1].len = 6;
|
||||
rcv_buf = 0;
|
||||
ret = i2c_transfer(client->adapter, msg, 2);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Unable to read device serial number");
|
||||
return ret;
|
||||
}
|
||||
|
||||
rcv_val = be64_to_cpu(rcv_buf) >> 16;
|
||||
dev_dbg(&client->dev, "Serial MSB raw : %llx\n", rcv_val);
|
||||
|
||||
for (i = 0; i < 48; i += 24) {
|
||||
if (!ms_sensors_crc_valid((rcv_val >> i) & 0xFFFFFF))
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
*sn |= (rcv_val & 0xFFFF00) << 40 | (rcv_val >> 32);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ms_sensors_read_serial);
|
||||
|
||||
static int ms_sensors_read_config_reg(struct i2c_client *client,
|
||||
u8 *config_reg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_byte(client, MS_SENSORS_CONFIG_REG_READ);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "Unable to read config register");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = i2c_master_recv(client, config_reg, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Unable to read config register");
|
||||
return ret;
|
||||
}
|
||||
dev_dbg(&client->dev, "Config register :%x\n", *config_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ms_sensors_write_resolution() - Set resolution function
|
||||
* @dev_data: pointer to temperature/humidity device data
|
||||
* @i: resolution index to set
|
||||
*
|
||||
* This function will program the appropriate resolution based on the index
|
||||
* provided when user space will set samp_freq channel.
|
||||
* This function is used for TSYS02D, HTU21 and MS8607 chipsets.
|
||||
*
|
||||
* Return: 0 on success, negative errno otherwise.
|
||||
*/
|
||||
ssize_t ms_sensors_write_resolution(struct ms_ht_dev *dev_data,
|
||||
u8 i)
|
||||
{
|
||||
u8 config_reg;
|
||||
int ret;
|
||||
|
||||
ret = ms_sensors_read_config_reg(dev_data->client, &config_reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
config_reg &= 0x7E;
|
||||
config_reg |= ((i & 1) << 7) + ((i & 2) >> 1);
|
||||
|
||||
return i2c_smbus_write_byte_data(dev_data->client,
|
||||
MS_SENSORS_CONFIG_REG_WRITE,
|
||||
config_reg);
|
||||
}
|
||||
EXPORT_SYMBOL(ms_sensors_write_resolution);
|
||||
|
||||
/**
|
||||
* ms_sensors_show_battery_low() - Show device battery low indicator
|
||||
* @dev_data: pointer to temperature/humidity device data
|
||||
* @buf: pointer to char buffer to write result
|
||||
*
|
||||
* This function will read battery indicator value in the device and
|
||||
* return 1 if the device voltage is below 2.25V.
|
||||
* This function is used for TSYS02D, HTU21 and MS8607 chipsets.
|
||||
*
|
||||
* Return: length of sprintf on success, negative errno otherwise.
|
||||
*/
|
||||
ssize_t ms_sensors_show_battery_low(struct ms_ht_dev *dev_data,
|
||||
char *buf)
|
||||
{
|
||||
int ret;
|
||||
u8 config_reg;
|
||||
|
||||
mutex_lock(&dev_data->lock);
|
||||
ret = ms_sensors_read_config_reg(dev_data->client, &config_reg);
|
||||
mutex_unlock(&dev_data->lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%d\n", (config_reg & 0x40) >> 6);
|
||||
}
|
||||
EXPORT_SYMBOL(ms_sensors_show_battery_low);
|
||||
|
||||
/**
|
||||
* ms_sensors_show_heater() - Show device heater
|
||||
* @dev_data: pointer to temperature/humidity device data
|
||||
* @buf: pointer to char buffer to write result
|
||||
*
|
||||
* This function will read heater enable value in the device and
|
||||
* return 1 if the heater is enabled.
|
||||
* This function is used for HTU21 and MS8607 chipsets.
|
||||
*
|
||||
* Return: length of sprintf on success, negative errno otherwise.
|
||||
*/
|
||||
ssize_t ms_sensors_show_heater(struct ms_ht_dev *dev_data,
|
||||
char *buf)
|
||||
{
|
||||
u8 config_reg;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&dev_data->lock);
|
||||
ret = ms_sensors_read_config_reg(dev_data->client, &config_reg);
|
||||
mutex_unlock(&dev_data->lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%d\n", (config_reg & 0x4) >> 2);
|
||||
}
|
||||
EXPORT_SYMBOL(ms_sensors_show_heater);
|
||||
|
||||
/**
|
||||
* ms_sensors_write_heater() - Write device heater
|
||||
* @dev_data: pointer to temperature/humidity device data
|
||||
* @buf: pointer to char buffer from user space
|
||||
* @len: length of buf
|
||||
*
|
||||
* This function will write 1 or 0 value in the device
|
||||
* to enable or disable heater.
|
||||
* This function is used for HTU21 and MS8607 chipsets.
|
||||
*
|
||||
* Return: length of buffer, negative errno otherwise.
|
||||
*/
|
||||
ssize_t ms_sensors_write_heater(struct ms_ht_dev *dev_data,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
u8 val, config_reg;
|
||||
int ret;
|
||||
|
||||
ret = kstrtou8(buf, 10, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (val > 1)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&dev_data->lock);
|
||||
ret = ms_sensors_read_config_reg(dev_data->client, &config_reg);
|
||||
if (ret) {
|
||||
mutex_unlock(&dev_data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
config_reg &= 0xFB;
|
||||
config_reg |= val << 2;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(dev_data->client,
|
||||
MS_SENSORS_CONFIG_REG_WRITE,
|
||||
config_reg);
|
||||
mutex_unlock(&dev_data->lock);
|
||||
if (ret) {
|
||||
dev_err(&dev_data->client->dev, "Unable to write config register\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
EXPORT_SYMBOL(ms_sensors_write_heater);
|
||||
|
||||
/**
|
||||
* ms_sensors_ht_read_temperature() - Read temperature
|
||||
* @dev_data: pointer to temperature/humidity device data
|
||||
* @temperature:pointer to temperature destination value
|
||||
*
|
||||
* This function will get temperature ADC value from the device,
|
||||
* check the CRC and compute the temperature value.
|
||||
* This function is used for TSYS02D, HTU21 and MS8607 chipsets.
|
||||
*
|
||||
* Return: 0 on success, negative errno otherwise.
|
||||
*/
|
||||
int ms_sensors_ht_read_temperature(struct ms_ht_dev *dev_data,
|
||||
s32 *temperature)
|
||||
{
|
||||
int ret;
|
||||
u32 adc;
|
||||
u16 delay;
|
||||
|
||||
mutex_lock(&dev_data->lock);
|
||||
delay = ms_sensors_ht_t_conversion_time[dev_data->res_index];
|
||||
ret = ms_sensors_convert_and_read(dev_data->client,
|
||||
MS_SENSORS_HT_T_CONVERSION_START,
|
||||
MS_SENSORS_NO_READ_CMD,
|
||||
delay, &adc);
|
||||
mutex_unlock(&dev_data->lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!ms_sensors_crc_valid(adc)) {
|
||||
dev_err(&dev_data->client->dev,
|
||||
"Temperature read crc check error\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Temperature algorithm */
|
||||
*temperature = (((s64)(adc >> 8) * 175720) >> 16) - 46850;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ms_sensors_ht_read_temperature);
|
||||
|
||||
/**
|
||||
* ms_sensors_ht_read_humidity() - Read humidity
|
||||
* @dev_data: pointer to temperature/humidity device data
|
||||
* @humidity: pointer to humidity destination value
|
||||
*
|
||||
* This function will get humidity ADC value from the device,
|
||||
* check the CRC and compute the temperature value.
|
||||
* This function is used for HTU21 and MS8607 chipsets.
|
||||
*
|
||||
* Return: 0 on success, negative errno otherwise.
|
||||
*/
|
||||
int ms_sensors_ht_read_humidity(struct ms_ht_dev *dev_data,
|
||||
u32 *humidity)
|
||||
{
|
||||
int ret;
|
||||
u32 adc;
|
||||
u16 delay;
|
||||
|
||||
mutex_lock(&dev_data->lock);
|
||||
delay = ms_sensors_ht_h_conversion_time[dev_data->res_index];
|
||||
ret = ms_sensors_convert_and_read(dev_data->client,
|
||||
MS_SENSORS_HT_H_CONVERSION_START,
|
||||
MS_SENSORS_NO_READ_CMD,
|
||||
delay, &adc);
|
||||
mutex_unlock(&dev_data->lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!ms_sensors_crc_valid(adc)) {
|
||||
dev_err(&dev_data->client->dev,
|
||||
"Humidity read crc check error\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Humidity algorithm */
|
||||
*humidity = (((s32)(adc >> 8) * 12500) >> 16) * 10 - 6000;
|
||||
if (*humidity >= 100000)
|
||||
*humidity = 100000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ms_sensors_ht_read_humidity);
|
||||
|
||||
/**
|
||||
* ms_sensors_tp_crc_valid() - CRC check function for
|
||||
* Temperature and pressure devices.
|
||||
* This function is only used when reading PROM coefficients
|
||||
*
|
||||
* @prom: pointer to PROM coefficients array
|
||||
* @len: length of PROM coefficients array
|
||||
*
|
||||
* Return: True if CRC is ok.
|
||||
*/
|
||||
static bool ms_sensors_tp_crc_valid(u16 *prom, u8 len)
|
||||
{
|
||||
unsigned int cnt, n_bit;
|
||||
u16 n_rem = 0x0000, crc_read = prom[0], crc = (*prom & 0xF000) >> 12;
|
||||
|
||||
prom[len - 1] = 0;
|
||||
prom[0] &= 0x0FFF; /* Clear the CRC computation part */
|
||||
|
||||
for (cnt = 0; cnt < len * 2; cnt++) {
|
||||
if (cnt % 2 == 1)
|
||||
n_rem ^= prom[cnt >> 1] & 0x00FF;
|
||||
else
|
||||
n_rem ^= prom[cnt >> 1] >> 8;
|
||||
|
||||
for (n_bit = 8; n_bit > 0; n_bit--) {
|
||||
if (n_rem & 0x8000)
|
||||
n_rem = (n_rem << 1) ^ 0x3000;
|
||||
else
|
||||
n_rem <<= 1;
|
||||
}
|
||||
}
|
||||
n_rem >>= 12;
|
||||
prom[0] = crc_read;
|
||||
|
||||
return n_rem == crc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ms_sensors_tp_read_prom() - prom coeff read function
|
||||
* @dev_data: pointer to temperature/pressure device data
|
||||
*
|
||||
* This function will read prom coefficients and check CRC.
|
||||
* This function is used for MS5637 and MS8607 chipsets.
|
||||
*
|
||||
* Return: 0 on success, negative errno otherwise.
|
||||
*/
|
||||
int ms_sensors_tp_read_prom(struct ms_tp_dev *dev_data)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < MS_SENSORS_TP_PROM_WORDS_NB; i++) {
|
||||
ret = ms_sensors_read_prom_word(
|
||||
dev_data->client,
|
||||
MS_SENSORS_TP_PROM_READ + (i << 1),
|
||||
&dev_data->prom[i]);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!ms_sensors_tp_crc_valid(dev_data->prom,
|
||||
MS_SENSORS_TP_PROM_WORDS_NB + 1)) {
|
||||
dev_err(&dev_data->client->dev,
|
||||
"Calibration coefficients crc check error\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ms_sensors_tp_read_prom);
|
||||
|
||||
/**
|
||||
* ms_sensors_read_temp_and_pressure() - read temp and pressure
|
||||
* @dev_data: pointer to temperature/pressure device data
|
||||
* @temperature:pointer to temperature destination value
|
||||
* @pressure: pointer to pressure destination value
|
||||
*
|
||||
* This function will read ADC and compute pressure and temperature value.
|
||||
* This function is used for MS5637 and MS8607 chipsets.
|
||||
*
|
||||
* Return: 0 on success, negative errno otherwise.
|
||||
*/
|
||||
int ms_sensors_read_temp_and_pressure(struct ms_tp_dev *dev_data,
|
||||
int *temperature,
|
||||
unsigned int *pressure)
|
||||
{
|
||||
int ret;
|
||||
u32 t_adc, p_adc;
|
||||
s32 dt, temp;
|
||||
s64 off, sens, t2, off2, sens2;
|
||||
u16 *prom = dev_data->prom, delay;
|
||||
|
||||
mutex_lock(&dev_data->lock);
|
||||
delay = ms_sensors_tp_conversion_time[dev_data->res_index];
|
||||
|
||||
ret = ms_sensors_convert_and_read(
|
||||
dev_data->client,
|
||||
MS_SENSORS_TP_T_CONVERSION_START +
|
||||
dev_data->res_index * 2,
|
||||
MS_SENSORS_TP_ADC_READ,
|
||||
delay, &t_adc);
|
||||
if (ret) {
|
||||
mutex_unlock(&dev_data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ms_sensors_convert_and_read(
|
||||
dev_data->client,
|
||||
MS_SENSORS_TP_P_CONVERSION_START +
|
||||
dev_data->res_index * 2,
|
||||
MS_SENSORS_TP_ADC_READ,
|
||||
delay, &p_adc);
|
||||
mutex_unlock(&dev_data->lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dt = (s32)t_adc - (prom[5] << 8);
|
||||
|
||||
/* Actual temperature = 2000 + dT * TEMPSENS */
|
||||
temp = 2000 + (((s64)dt * prom[6]) >> 23);
|
||||
|
||||
/* Second order temperature compensation */
|
||||
if (temp < 2000) {
|
||||
s64 tmp = (s64)temp - 2000;
|
||||
|
||||
t2 = (3 * ((s64)dt * (s64)dt)) >> 33;
|
||||
off2 = (61 * tmp * tmp) >> 4;
|
||||
sens2 = (29 * tmp * tmp) >> 4;
|
||||
|
||||
if (temp < -1500) {
|
||||
s64 tmp = (s64)temp + 1500;
|
||||
|
||||
off2 += 17 * tmp * tmp;
|
||||
sens2 += 9 * tmp * tmp;
|
||||
}
|
||||
} else {
|
||||
t2 = (5 * ((s64)dt * (s64)dt)) >> 38;
|
||||
off2 = 0;
|
||||
sens2 = 0;
|
||||
}
|
||||
|
||||
/* OFF = OFF_T1 + TCO * dT */
|
||||
off = (((s64)prom[2]) << 17) + ((((s64)prom[4]) * (s64)dt) >> 6);
|
||||
off -= off2;
|
||||
|
||||
/* Sensitivity at actual temperature = SENS_T1 + TCS * dT */
|
||||
sens = (((s64)prom[1]) << 16) + (((s64)prom[3] * dt) >> 7);
|
||||
sens -= sens2;
|
||||
|
||||
/* Temperature compensated pressure = D1 * SENS - OFF */
|
||||
*temperature = (temp - t2) * 10;
|
||||
*pressure = (u32)(((((s64)p_adc * sens) >> 21) - off) >> 15);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ms_sensors_read_temp_and_pressure);
|
||||
|
||||
MODULE_DESCRIPTION("Measurement-Specialties common i2c driver");
|
||||
MODULE_AUTHOR("William Markezana <william.markezana@meas-spec.com>");
|
||||
MODULE_AUTHOR("Ludovic Tancerel <ludovic.tancerel@maplehightech.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
66
drivers/iio/common/ms_sensors/ms_sensors_i2c.h
Normal file
66
drivers/iio/common/ms_sensors/ms_sensors_i2c.h
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Measurements Specialties common sensor driver
|
||||
*
|
||||
* Copyright (c) 2015 Measurement-Specialties
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef _MS_SENSORS_I2C_H
|
||||
#define _MS_SENSORS_I2C_H
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#define MS_SENSORS_TP_PROM_WORDS_NB 7
|
||||
|
||||
/**
|
||||
* struct ms_ht_dev - Humidity/Temperature sensor device structure
|
||||
* @client: i2c client
|
||||
* @lock: lock protecting the i2c conversion
|
||||
* @res_index: index to selected sensor resolution
|
||||
*/
|
||||
struct ms_ht_dev {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock;
|
||||
u8 res_index;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ms_tp_dev - Temperature/Pressure sensor device structure
|
||||
* @client: i2c client
|
||||
* @lock: lock protecting the i2c conversion
|
||||
* @prom: array of PROM coefficients used for conversion. Added element
|
||||
* for CRC computation
|
||||
* @res_index: index to selected sensor resolution
|
||||
*/
|
||||
struct ms_tp_dev {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock;
|
||||
u16 prom[MS_SENSORS_TP_PROM_WORDS_NB + 1];
|
||||
u8 res_index;
|
||||
};
|
||||
|
||||
int ms_sensors_reset(void *cli, u8 cmd, unsigned int delay);
|
||||
int ms_sensors_read_prom_word(void *cli, int cmd, u16 *word);
|
||||
int ms_sensors_convert_and_read(void *cli, u8 conv, u8 rd,
|
||||
unsigned int delay, u32 *adc);
|
||||
int ms_sensors_read_serial(struct i2c_client *client, u64 *sn);
|
||||
ssize_t ms_sensors_show_serial(struct ms_ht_dev *dev_data, char *buf);
|
||||
ssize_t ms_sensors_write_resolution(struct ms_ht_dev *dev_data, u8 i);
|
||||
ssize_t ms_sensors_show_battery_low(struct ms_ht_dev *dev_data, char *buf);
|
||||
ssize_t ms_sensors_show_heater(struct ms_ht_dev *dev_data, char *buf);
|
||||
ssize_t ms_sensors_write_heater(struct ms_ht_dev *dev_data,
|
||||
const char *buf, size_t len);
|
||||
int ms_sensors_ht_read_temperature(struct ms_ht_dev *dev_data,
|
||||
s32 *temperature);
|
||||
int ms_sensors_ht_read_humidity(struct ms_ht_dev *dev_data,
|
||||
u32 *humidity);
|
||||
int ms_sensors_tp_read_prom(struct ms_tp_dev *dev_data);
|
||||
int ms_sensors_read_temp_and_pressure(struct ms_tp_dev *dev_data,
|
||||
int *temperature,
|
||||
unsigned int *pressure);
|
||||
|
||||
#endif /* _MS_SENSORS_I2C_H */
|
@ -214,7 +214,6 @@ static struct attribute *ad5504_ev_attributes[] = {
|
||||
|
||||
static struct attribute_group ad5504_ev_attribute_group = {
|
||||
.attrs = ad5504_ev_attributes,
|
||||
.name = "events",
|
||||
};
|
||||
|
||||
static irqreturn_t ad5504_event_handler(int irq, void *private)
|
||||
|
@ -31,7 +31,6 @@
|
||||
|
||||
struct m62332_data {
|
||||
struct i2c_client *client;
|
||||
u16 vref_mv;
|
||||
struct regulator *vcc;
|
||||
struct mutex mutex;
|
||||
u8 raw[M62332_CHANNELS];
|
||||
@ -40,8 +39,7 @@ struct m62332_data {
|
||||
#endif
|
||||
};
|
||||
|
||||
static int m62332_set_value(struct iio_dev *indio_dev,
|
||||
u8 val, int channel)
|
||||
static int m62332_set_value(struct iio_dev *indio_dev, u8 val, int channel)
|
||||
{
|
||||
struct m62332_data *data = iio_priv(indio_dev);
|
||||
struct i2c_client *client = data->client;
|
||||
@ -62,8 +60,8 @@ static int m62332_set_value(struct iio_dev *indio_dev,
|
||||
goto out;
|
||||
}
|
||||
|
||||
res = i2c_master_send(client, outbuf, 2);
|
||||
if (res >= 0 && res != 2)
|
||||
res = i2c_master_send(client, outbuf, ARRAY_SIZE(outbuf));
|
||||
if (res >= 0 && res != ARRAY_SIZE(outbuf))
|
||||
res = -EIO;
|
||||
if (res < 0)
|
||||
goto out;
|
||||
@ -87,46 +85,52 @@ static int m62332_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long m)
|
||||
long mask)
|
||||
{
|
||||
struct m62332_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (m) {
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
/* Corresponds to Vref / 2^(bits) */
|
||||
*val = data->vref_mv;
|
||||
ret = regulator_get_voltage(data->vcc);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = ret / 1000; /* mV */
|
||||
*val2 = 8;
|
||||
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
*val = data->raw[chan->channel];
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = 1;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int m62332_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2, long mask)
|
||||
struct iio_chan_spec const *chan, int val, int val2,
|
||||
long mask)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (val < 0 || val > 255)
|
||||
return -EINVAL;
|
||||
|
||||
ret = m62332_set_value(indio_dev, val, chan->channel);
|
||||
break;
|
||||
return m62332_set_value(indio_dev, val, chan->channel);
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
@ -173,15 +177,15 @@ static const struct iio_info m62332_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
#define M62332_CHANNEL(chan) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = (chan), \
|
||||
.datasheet_name = "CH" #chan, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_OFFSET), \
|
||||
#define M62332_CHANNEL(chan) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = (chan), \
|
||||
.datasheet_name = "CH" #chan, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_OFFSET), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec m62332_channels[M62332_CHANNELS] = {
|
||||
@ -199,6 +203,7 @@ static int m62332_probe(struct i2c_client *client,
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
@ -212,16 +217,11 @@ static int m62332_probe(struct i2c_client *client,
|
||||
/* establish that the iio_dev is a child of the i2c device */
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
|
||||
indio_dev->num_channels = M62332_CHANNELS;
|
||||
indio_dev->num_channels = ARRAY_SIZE(m62332_channels);
|
||||
indio_dev->channels = m62332_channels;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &m62332_info;
|
||||
|
||||
ret = regulator_get_voltage(data->vcc);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->vref_mv = ret / 1000; /* mV */
|
||||
|
||||
ret = iio_map_array_register(indio_dev, client->dev.platform_data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@ -234,6 +234,7 @@ static int m62332_probe(struct i2c_client *client,
|
||||
|
||||
err:
|
||||
iio_map_array_unregister(indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -243,6 +244,8 @@ static int m62332_remove(struct i2c_client *client)
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_map_array_unregister(indio_dev);
|
||||
m62332_set_value(indio_dev, 0, 0);
|
||||
m62332_set_value(indio_dev, 0, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -22,6 +22,19 @@ config HDC100X
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called hdc100x.
|
||||
|
||||
config HTU21
|
||||
tristate "Measurement Specialties HTU21 humidity & temperature sensor"
|
||||
depends on I2C
|
||||
select IIO_MS_SENSORS_I2C
|
||||
help
|
||||
If you say yes here you get support for the Measurement Specialties
|
||||
HTU21 humidity and temperature sensor.
|
||||
This driver is also used for MS8607 temperature, pressure & humidity
|
||||
sensor
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called htu21.
|
||||
|
||||
config SI7005
|
||||
tristate "SI7005 relative humidity and temperature sensor"
|
||||
depends on I2C
|
||||
|
@ -4,5 +4,6 @@
|
||||
|
||||
obj-$(CONFIG_DHT11) += dht11.o
|
||||
obj-$(CONFIG_HDC100X) += hdc100x.o
|
||||
obj-$(CONFIG_HTU21) += htu21.o
|
||||
obj-$(CONFIG_SI7005) += si7005.o
|
||||
obj-$(CONFIG_SI7020) += si7020.o
|
||||
|
@ -221,8 +221,9 @@ static int hdc100x_read_raw(struct iio_dev *indio_dev,
|
||||
}
|
||||
break;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = -40;
|
||||
return IIO_VAL_INT;
|
||||
*val = -3971;
|
||||
*val2 = 879096;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
253
drivers/iio/humidity/htu21.c
Normal file
253
drivers/iio/humidity/htu21.c
Normal file
@ -0,0 +1,253 @@
|
||||
/*
|
||||
* htu21.c - Support for Measurement-Specialties
|
||||
* htu21 temperature & humidity sensor
|
||||
* and humidity part of MS8607 sensor
|
||||
*
|
||||
* Copyright (c) 2014 Measurement-Specialties
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*
|
||||
* (7-bit I2C slave address 0x40)
|
||||
*
|
||||
* Datasheet:
|
||||
* http://www.meas-spec.com/downloads/HTU21D.pdf
|
||||
* Datasheet:
|
||||
* http://www.meas-spec.com/downloads/MS8607-02BA01.pdf
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#include "../common/ms_sensors/ms_sensors_i2c.h"
|
||||
|
||||
#define HTU21_RESET 0xFE
|
||||
|
||||
enum {
|
||||
HTU21,
|
||||
MS8607
|
||||
};
|
||||
|
||||
static const int htu21_samp_freq[4] = { 20, 40, 70, 120 };
|
||||
/* String copy of the above const for readability purpose */
|
||||
static const char htu21_show_samp_freq[] = "20 40 70 120";
|
||||
|
||||
static int htu21_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *channel, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
int ret, temperature;
|
||||
unsigned int humidity;
|
||||
struct ms_ht_dev *dev_data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
switch (channel->type) {
|
||||
case IIO_TEMP: /* in milli °C */
|
||||
ret = ms_sensors_ht_read_temperature(dev_data,
|
||||
&temperature);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val = temperature;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_HUMIDITYRELATIVE: /* in milli %RH */
|
||||
ret = ms_sensors_ht_read_humidity(dev_data,
|
||||
&humidity);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val = humidity;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val = htu21_samp_freq[dev_data->res_index];
|
||||
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int htu21_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct ms_ht_dev *dev_data = iio_priv(indio_dev);
|
||||
int i, ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
i = ARRAY_SIZE(htu21_samp_freq);
|
||||
while (i-- > 0)
|
||||
if (val == htu21_samp_freq[i])
|
||||
break;
|
||||
if (i < 0)
|
||||
return -EINVAL;
|
||||
mutex_lock(&dev_data->lock);
|
||||
dev_data->res_index = i;
|
||||
ret = ms_sensors_write_resolution(dev_data, i);
|
||||
mutex_unlock(&dev_data->lock);
|
||||
|
||||
return ret;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec htu21_channels[] = {
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
},
|
||||
{
|
||||
.type = IIO_HUMIDITYRELATIVE,
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Meas Spec recommendation is to not read temperature
|
||||
* on this driver part for MS8607
|
||||
*/
|
||||
static const struct iio_chan_spec ms8607_channels[] = {
|
||||
{
|
||||
.type = IIO_HUMIDITYRELATIVE,
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
}
|
||||
};
|
||||
|
||||
static ssize_t htu21_show_battery_low(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ms_ht_dev *dev_data = iio_priv(indio_dev);
|
||||
|
||||
return ms_sensors_show_battery_low(dev_data, buf);
|
||||
}
|
||||
|
||||
static ssize_t htu21_show_heater(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ms_ht_dev *dev_data = iio_priv(indio_dev);
|
||||
|
||||
return ms_sensors_show_heater(dev_data, buf);
|
||||
}
|
||||
|
||||
static ssize_t htu21_write_heater(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ms_ht_dev *dev_data = iio_priv(indio_dev);
|
||||
|
||||
return ms_sensors_write_heater(dev_data, buf, len);
|
||||
}
|
||||
|
||||
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(htu21_show_samp_freq);
|
||||
static IIO_DEVICE_ATTR(battery_low, S_IRUGO,
|
||||
htu21_show_battery_low, NULL, 0);
|
||||
static IIO_DEVICE_ATTR(heater_enable, S_IRUGO | S_IWUSR,
|
||||
htu21_show_heater, htu21_write_heater, 0);
|
||||
|
||||
static struct attribute *htu21_attributes[] = {
|
||||
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
||||
&iio_dev_attr_battery_low.dev_attr.attr,
|
||||
&iio_dev_attr_heater_enable.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group htu21_attribute_group = {
|
||||
.attrs = htu21_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info htu21_info = {
|
||||
.read_raw = htu21_read_raw,
|
||||
.write_raw = htu21_write_raw,
|
||||
.attrs = &htu21_attribute_group,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int htu21_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct ms_ht_dev *dev_data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
u64 serial_number;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_WRITE_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_WRITE_BYTE |
|
||||
I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
|
||||
dev_err(&client->dev,
|
||||
"Adapter does not support some i2c transaction\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*dev_data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_data = iio_priv(indio_dev);
|
||||
dev_data->client = client;
|
||||
dev_data->res_index = 0;
|
||||
mutex_init(&dev_data->lock);
|
||||
|
||||
indio_dev->info = &htu21_info;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
if (id->driver_data == MS8607) {
|
||||
indio_dev->channels = ms8607_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ms8607_channels);
|
||||
} else {
|
||||
indio_dev->channels = htu21_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(htu21_channels);
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
ret = ms_sensors_reset(client, HTU21_RESET, 15000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ms_sensors_read_serial(client, &serial_number);
|
||||
if (ret)
|
||||
return ret;
|
||||
dev_info(&client->dev, "Serial number : %llx", serial_number);
|
||||
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id htu21_id[] = {
|
||||
{"htu21", HTU21},
|
||||
{"ms8607-humidity", MS8607},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct i2c_driver htu21_driver = {
|
||||
.probe = htu21_probe,
|
||||
.id_table = htu21_id,
|
||||
.driver = {
|
||||
.name = "htu21",
|
||||
},
|
||||
};
|
||||
|
||||
module_i2c_driver(htu21_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Measurement-Specialties htu21 temperature and humidity driver");
|
||||
MODULE_AUTHOR("William Markezana <william.markezana@meas-spec.com>");
|
||||
MODULE_AUTHOR("Ludovic Tancerel <ludovic.tancerel@maplehightech.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -27,7 +27,6 @@
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
|
||||
#define KMX61_DRV_NAME "kmx61"
|
||||
#define KMX61_GPIO_NAME "kmx61_int"
|
||||
#define KMX61_IRQ_NAME "kmx61_event"
|
||||
|
||||
#define KMX61_REG_WHO_AM_I 0x00
|
||||
@ -1243,30 +1242,6 @@ static const char *kmx61_match_acpi_device(struct device *dev)
|
||||
return dev_name(dev);
|
||||
}
|
||||
|
||||
static int kmx61_gpio_probe(struct i2c_client *client, struct kmx61_data *data)
|
||||
{
|
||||
struct device *dev;
|
||||
struct gpio_desc *gpio;
|
||||
int ret;
|
||||
|
||||
if (!client)
|
||||
return -EINVAL;
|
||||
|
||||
dev = &client->dev;
|
||||
|
||||
/* data ready gpio interrupt pin */
|
||||
gpio = devm_gpiod_get_index(dev, KMX61_GPIO_NAME, 0, GPIOD_IN);
|
||||
if (IS_ERR(gpio)) {
|
||||
dev_err(dev, "acpi gpio get index failed\n");
|
||||
return PTR_ERR(gpio);
|
||||
}
|
||||
|
||||
ret = gpiod_to_irq(gpio);
|
||||
|
||||
dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct iio_dev *kmx61_indiodev_setup(struct kmx61_data *data,
|
||||
const struct iio_info *info,
|
||||
const struct iio_chan_spec *chan,
|
||||
@ -1360,9 +1335,6 @@ static int kmx61_probe(struct i2c_client *client,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (client->irq < 0)
|
||||
client->irq = kmx61_gpio_probe(client, data);
|
||||
|
||||
if (client->irq > 0) {
|
||||
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
kmx61_data_rdy_trig_poll,
|
||||
|
@ -472,7 +472,7 @@ static int apds9960_read_raw(struct iio_dev *indio_dev,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct apds9960_data *data = iio_priv(indio_dev);
|
||||
u16 buf;
|
||||
__le16 buf;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (data->gesture_mode_running)
|
||||
@ -613,7 +613,7 @@ static int apds9960_read_event(struct iio_dev *indio_dev,
|
||||
int *val, int *val2)
|
||||
{
|
||||
u8 reg;
|
||||
u16 buf;
|
||||
__le16 buf;
|
||||
int ret = 0;
|
||||
struct apds9960_data *data = iio_priv(indio_dev);
|
||||
|
||||
@ -649,7 +649,7 @@ static int apds9960_write_event(struct iio_dev *indio_dev,
|
||||
int val, int val2)
|
||||
{
|
||||
u8 reg;
|
||||
u16 buf;
|
||||
__le16 buf;
|
||||
int ret = 0;
|
||||
struct apds9960_data *data = iio_priv(indio_dev);
|
||||
|
||||
|
@ -47,7 +47,6 @@
|
||||
#define STK3310_DRIVER_NAME "stk3310"
|
||||
#define STK3310_REGMAP_NAME "stk3310_regmap"
|
||||
#define STK3310_EVENT "stk3310_event"
|
||||
#define STK3310_GPIO "stk3310_gpio"
|
||||
|
||||
#define STK3310_SCALE_AVAILABLE "6.4 1.6 0.4 0.1"
|
||||
|
||||
@ -477,30 +476,6 @@ static int stk3310_init(struct iio_dev *indio_dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stk3310_gpio_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev;
|
||||
struct gpio_desc *gpio;
|
||||
int ret;
|
||||
|
||||
if (!client)
|
||||
return -EINVAL;
|
||||
|
||||
dev = &client->dev;
|
||||
|
||||
/* gpio interrupt pin */
|
||||
gpio = devm_gpiod_get_index(dev, STK3310_GPIO, 0, GPIOD_IN);
|
||||
if (IS_ERR(gpio)) {
|
||||
dev_err(dev, "acpi gpio get index failed\n");
|
||||
return PTR_ERR(gpio);
|
||||
}
|
||||
|
||||
ret = gpiod_to_irq(gpio);
|
||||
dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool stk3310_is_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
@ -624,15 +599,7 @@ static int stk3310_probe(struct i2c_client *client,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (client->irq < 0) {
|
||||
client->irq = stk3310_gpio_probe(client);
|
||||
if (client->irq < 0) {
|
||||
ret = client->irq;
|
||||
goto err_standby;
|
||||
}
|
||||
}
|
||||
|
||||
if (client->irq >= 0) {
|
||||
if (client->irq > 0) {
|
||||
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
stk3310_irq_handler,
|
||||
stk3310_irq_event_handler,
|
||||
|
@ -37,7 +37,6 @@
|
||||
|
||||
#define BMC150_MAGN_DRV_NAME "bmc150_magn"
|
||||
#define BMC150_MAGN_IRQ_NAME "bmc150_magn_event"
|
||||
#define BMC150_MAGN_GPIO_INT "interrupt"
|
||||
|
||||
#define BMC150_MAGN_REG_CHIP_ID 0x40
|
||||
#define BMC150_MAGN_CHIP_ID_VAL 0x32
|
||||
@ -833,31 +832,6 @@ static const struct iio_buffer_setup_ops bmc150_magn_buffer_setup_ops = {
|
||||
.postdisable = bmc150_magn_buffer_postdisable,
|
||||
};
|
||||
|
||||
static int bmc150_magn_gpio_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev;
|
||||
struct gpio_desc *gpio;
|
||||
int ret;
|
||||
|
||||
if (!client)
|
||||
return -EINVAL;
|
||||
|
||||
dev = &client->dev;
|
||||
|
||||
/* data ready GPIO interrupt pin */
|
||||
gpio = devm_gpiod_get_index(dev, BMC150_MAGN_GPIO_INT, 0, GPIOD_IN);
|
||||
if (IS_ERR(gpio)) {
|
||||
dev_err(dev, "ACPI GPIO get index failed\n");
|
||||
return PTR_ERR(gpio);
|
||||
}
|
||||
|
||||
ret = gpiod_to_irq(gpio);
|
||||
|
||||
dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char *bmc150_magn_match_acpi_device(struct device *dev)
|
||||
{
|
||||
const struct acpi_device_id *id;
|
||||
@ -911,9 +885,6 @@ static int bmc150_magn_probe(struct i2c_client *client,
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &bmc150_magn_info;
|
||||
|
||||
if (client->irq <= 0)
|
||||
client->irq = bmc150_magn_gpio_probe(client);
|
||||
|
||||
if (client->irq > 0) {
|
||||
data->dready_trig = devm_iio_trigger_alloc(&client->dev,
|
||||
"%s-dev%d",
|
||||
|
20
drivers/iio/potentiometer/Kconfig
Normal file
20
drivers/iio/potentiometer/Kconfig
Normal file
@ -0,0 +1,20 @@
|
||||
#
|
||||
# Potentiometer drivers
|
||||
#
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
|
||||
menu "Digital potentiometers"
|
||||
|
||||
config MCP4531
|
||||
tristate "Microchip MCP45xx/MCP46xx Digital Potentiometer driver"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to build support for the Microchip
|
||||
MCP4531, MCP4532, MCP4551, MCP4552,
|
||||
MCP4631, MCP4632, MCP4651, MCP4652
|
||||
digital potentiomenter chips.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called mcp4531.
|
||||
|
||||
endmenu
|
6
drivers/iio/potentiometer/Makefile
Normal file
6
drivers/iio/potentiometer/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
#
|
||||
# Makefile for industrial I/O potentiometer drivers
|
||||
#
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_MCP4531) += mcp4531.o
|
231
drivers/iio/potentiometer/mcp4531.c
Normal file
231
drivers/iio/potentiometer/mcp4531.c
Normal file
@ -0,0 +1,231 @@
|
||||
/*
|
||||
* Industrial I/O driver for Microchip digital potentiometers
|
||||
* Copyright (c) 2015 Axentia Technologies AB
|
||||
* Author: Peter Rosin <peda@axentia.se>
|
||||
*
|
||||
* Datasheet: http://www.microchip.com/downloads/en/DeviceDoc/22096b.pdf
|
||||
*
|
||||
* DEVID #Wipers #Positions Resistor Opts (kOhm) i2c address
|
||||
* mcp4531 1 129 5, 10, 50, 100 010111x
|
||||
* mcp4532 1 129 5, 10, 50, 100 01011xx
|
||||
* mcp4551 1 257 5, 10, 50, 100 010111x
|
||||
* mcp4552 1 257 5, 10, 50, 100 01011xx
|
||||
* mcp4631 2 129 5, 10, 50, 100 0101xxx
|
||||
* mcp4632 2 129 5, 10, 50, 100 01011xx
|
||||
* mcp4651 2 257 5, 10, 50, 100 0101xxx
|
||||
* mcp4652 2 257 5, 10, 50, 100 01011xx
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
struct mcp4531_cfg {
|
||||
int wipers;
|
||||
int max_pos;
|
||||
int kohms;
|
||||
};
|
||||
|
||||
enum mcp4531_type {
|
||||
MCP453x_502,
|
||||
MCP453x_103,
|
||||
MCP453x_503,
|
||||
MCP453x_104,
|
||||
MCP455x_502,
|
||||
MCP455x_103,
|
||||
MCP455x_503,
|
||||
MCP455x_104,
|
||||
MCP463x_502,
|
||||
MCP463x_103,
|
||||
MCP463x_503,
|
||||
MCP463x_104,
|
||||
MCP465x_502,
|
||||
MCP465x_103,
|
||||
MCP465x_503,
|
||||
MCP465x_104,
|
||||
};
|
||||
|
||||
static const struct mcp4531_cfg mcp4531_cfg[] = {
|
||||
[MCP453x_502] = { .wipers = 1, .max_pos = 128, .kohms = 5, },
|
||||
[MCP453x_103] = { .wipers = 1, .max_pos = 128, .kohms = 10, },
|
||||
[MCP453x_503] = { .wipers = 1, .max_pos = 128, .kohms = 50, },
|
||||
[MCP453x_104] = { .wipers = 1, .max_pos = 128, .kohms = 100, },
|
||||
[MCP455x_502] = { .wipers = 1, .max_pos = 256, .kohms = 5, },
|
||||
[MCP455x_103] = { .wipers = 1, .max_pos = 256, .kohms = 10, },
|
||||
[MCP455x_503] = { .wipers = 1, .max_pos = 256, .kohms = 50, },
|
||||
[MCP455x_104] = { .wipers = 1, .max_pos = 256, .kohms = 100, },
|
||||
[MCP463x_502] = { .wipers = 2, .max_pos = 128, .kohms = 5, },
|
||||
[MCP463x_103] = { .wipers = 2, .max_pos = 128, .kohms = 10, },
|
||||
[MCP463x_503] = { .wipers = 2, .max_pos = 128, .kohms = 50, },
|
||||
[MCP463x_104] = { .wipers = 2, .max_pos = 128, .kohms = 100, },
|
||||
[MCP465x_502] = { .wipers = 2, .max_pos = 256, .kohms = 5, },
|
||||
[MCP465x_103] = { .wipers = 2, .max_pos = 256, .kohms = 10, },
|
||||
[MCP465x_503] = { .wipers = 2, .max_pos = 256, .kohms = 50, },
|
||||
[MCP465x_104] = { .wipers = 2, .max_pos = 256, .kohms = 100, },
|
||||
};
|
||||
|
||||
#define MCP4531_WRITE (0 << 2)
|
||||
#define MCP4531_INCR (1 << 2)
|
||||
#define MCP4531_DECR (2 << 2)
|
||||
#define MCP4531_READ (3 << 2)
|
||||
|
||||
#define MCP4531_WIPER_SHIFT (4)
|
||||
|
||||
struct mcp4531_data {
|
||||
struct i2c_client *client;
|
||||
unsigned long devid;
|
||||
};
|
||||
|
||||
#define MCP4531_CHANNEL(ch) { \
|
||||
.type = IIO_RESISTANCE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = (ch), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec mcp4531_channels[] = {
|
||||
MCP4531_CHANNEL(0),
|
||||
MCP4531_CHANNEL(1),
|
||||
};
|
||||
|
||||
static int mcp4531_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct mcp4531_data *data = iio_priv(indio_dev);
|
||||
int address = chan->channel << MCP4531_WIPER_SHIFT;
|
||||
s32 ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = i2c_smbus_read_word_swapped(data->client,
|
||||
MCP4531_READ | address);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 1000 * mcp4531_cfg[data->devid].kohms;
|
||||
*val2 = mcp4531_cfg[data->devid].max_pos;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int mcp4531_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct mcp4531_data *data = iio_priv(indio_dev);
|
||||
int address = chan->channel << MCP4531_WIPER_SHIFT;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (val > mcp4531_cfg[data->devid].max_pos || val < 0)
|
||||
return -EINVAL;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return i2c_smbus_write_byte_data(data->client,
|
||||
MCP4531_WRITE | address | (val >> 8),
|
||||
val & 0xff);
|
||||
}
|
||||
|
||||
static const struct iio_info mcp4531_info = {
|
||||
.read_raw = mcp4531_read_raw,
|
||||
.write_raw = mcp4531_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int mcp4531_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
unsigned long devid = id->driver_data;
|
||||
struct mcp4531_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_WORD_DATA)) {
|
||||
dev_err(dev, "SMBUS Word Data not supported\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
data->devid = devid;
|
||||
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->info = &mcp4531_info;
|
||||
indio_dev->channels = mcp4531_channels;
|
||||
indio_dev->num_channels = mcp4531_cfg[devid].wipers;
|
||||
indio_dev->name = client->name;
|
||||
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id mcp4531_id[] = {
|
||||
{ "mcp4531-502", MCP453x_502 },
|
||||
{ "mcp4531-103", MCP453x_103 },
|
||||
{ "mcp4531-503", MCP453x_503 },
|
||||
{ "mcp4531-104", MCP453x_104 },
|
||||
{ "mcp4532-502", MCP453x_502 },
|
||||
{ "mcp4532-103", MCP453x_103 },
|
||||
{ "mcp4532-503", MCP453x_503 },
|
||||
{ "mcp4532-104", MCP453x_104 },
|
||||
{ "mcp4551-502", MCP455x_502 },
|
||||
{ "mcp4551-103", MCP455x_103 },
|
||||
{ "mcp4551-503", MCP455x_503 },
|
||||
{ "mcp4551-104", MCP455x_104 },
|
||||
{ "mcp4552-502", MCP455x_502 },
|
||||
{ "mcp4552-103", MCP455x_103 },
|
||||
{ "mcp4552-503", MCP455x_503 },
|
||||
{ "mcp4552-104", MCP455x_104 },
|
||||
{ "mcp4631-502", MCP463x_502 },
|
||||
{ "mcp4631-103", MCP463x_103 },
|
||||
{ "mcp4631-503", MCP463x_503 },
|
||||
{ "mcp4631-104", MCP463x_104 },
|
||||
{ "mcp4632-502", MCP463x_502 },
|
||||
{ "mcp4632-103", MCP463x_103 },
|
||||
{ "mcp4632-503", MCP463x_503 },
|
||||
{ "mcp4632-104", MCP463x_104 },
|
||||
{ "mcp4651-502", MCP465x_502 },
|
||||
{ "mcp4651-103", MCP465x_103 },
|
||||
{ "mcp4651-503", MCP465x_503 },
|
||||
{ "mcp4651-104", MCP465x_104 },
|
||||
{ "mcp4652-502", MCP465x_502 },
|
||||
{ "mcp4652-103", MCP465x_103 },
|
||||
{ "mcp4652-503", MCP465x_503 },
|
||||
{ "mcp4652-104", MCP465x_104 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mcp4531_id);
|
||||
|
||||
static struct i2c_driver mcp4531_driver = {
|
||||
.driver = {
|
||||
.name = "mcp4531",
|
||||
},
|
||||
.probe = mcp4531_probe,
|
||||
.id_table = mcp4531_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(mcp4531_driver);
|
||||
|
||||
MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
|
||||
MODULE_DESCRIPTION("MCP4531 digital potentiometer");
|
||||
MODULE_LICENSE("GPL");
|
@ -79,6 +79,19 @@ config MS5611_SPI
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called ms5611_spi.
|
||||
|
||||
config MS5637
|
||||
tristate "Measurement Specialties MS5637 pressure & temperature sensor"
|
||||
depends on I2C
|
||||
select IIO_MS_SENSORS_I2C
|
||||
help
|
||||
If you say yes here you get support for the Measurement Specialties
|
||||
MS5637 pressure and temperature sensor.
|
||||
This driver is also used for MS8607 temperature, pressure & humidity
|
||||
sensor
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called ms5637.
|
||||
|
||||
config IIO_ST_PRESS
|
||||
tristate "STMicroelectronics pressure sensor Driver"
|
||||
depends on (I2C || SPI_MASTER) && SYSFS
|
||||
|
@ -10,6 +10,7 @@ obj-$(CONFIG_MPL3115) += mpl3115.o
|
||||
obj-$(CONFIG_MS5611) += ms5611_core.o
|
||||
obj-$(CONFIG_MS5611_I2C) += ms5611_i2c.o
|
||||
obj-$(CONFIG_MS5611_SPI) += ms5611_spi.o
|
||||
obj-$(CONFIG_MS5637) += ms5637.o
|
||||
obj-$(CONFIG_IIO_ST_PRESS) += st_pressure.o
|
||||
st_pressure-y := st_pressure_core.o
|
||||
st_pressure-$(CONFIG_IIO_BUFFER) += st_pressure_buffer.o
|
||||
|
190
drivers/iio/pressure/ms5637.c
Normal file
190
drivers/iio/pressure/ms5637.c
Normal file
@ -0,0 +1,190 @@
|
||||
/*
|
||||
* ms5637.c - Support for Measurement-Specialties ms5637 and ms8607
|
||||
* pressure & temperature sensor
|
||||
*
|
||||
* Copyright (c) 2015 Measurement-Specialties
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*
|
||||
* (7-bit I2C slave address 0x76)
|
||||
*
|
||||
* Datasheet:
|
||||
* http://www.meas-spec.com/downloads/MS5637-02BA03.pdf
|
||||
* Datasheet:
|
||||
* http://www.meas-spec.com/downloads/MS8607-02BA01.pdf
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#include "../common/ms_sensors/ms_sensors_i2c.h"
|
||||
|
||||
static const int ms5637_samp_freq[6] = { 960, 480, 240, 120, 60, 30 };
|
||||
/* String copy of the above const for readability purpose */
|
||||
static const char ms5637_show_samp_freq[] = "960 480 240 120 60 30";
|
||||
|
||||
static int ms5637_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *channel, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
int ret;
|
||||
int temperature;
|
||||
unsigned int pressure;
|
||||
struct ms_tp_dev *dev_data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
ret = ms_sensors_read_temp_and_pressure(dev_data,
|
||||
&temperature,
|
||||
&pressure);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (channel->type) {
|
||||
case IIO_TEMP: /* in milli °C */
|
||||
*val = temperature;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_PRESSURE: /* in kPa */
|
||||
*val = pressure / 1000;
|
||||
*val2 = (pressure % 1000) * 1000;
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val = ms5637_samp_freq[dev_data->res_index];
|
||||
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int ms5637_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct ms_tp_dev *dev_data = iio_priv(indio_dev);
|
||||
int i;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
i = ARRAY_SIZE(ms5637_samp_freq);
|
||||
while (i-- > 0)
|
||||
if (val == ms5637_samp_freq[i])
|
||||
break;
|
||||
if (i < 0)
|
||||
return -EINVAL;
|
||||
dev_data->res_index = i;
|
||||
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ms5637_channels[] = {
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
},
|
||||
{
|
||||
.type = IIO_PRESSURE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
}
|
||||
};
|
||||
|
||||
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(ms5637_show_samp_freq);
|
||||
|
||||
static struct attribute *ms5637_attributes[] = {
|
||||
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group ms5637_attribute_group = {
|
||||
.attrs = ms5637_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info ms5637_info = {
|
||||
.read_raw = ms5637_read_raw,
|
||||
.write_raw = ms5637_write_raw,
|
||||
.attrs = &ms5637_attribute_group,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int ms5637_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct ms_tp_dev *dev_data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_READ_WORD_DATA |
|
||||
I2C_FUNC_SMBUS_WRITE_BYTE |
|
||||
I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
|
||||
dev_err(&client->dev,
|
||||
"Adapter does not support some i2c transaction\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*dev_data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_data = iio_priv(indio_dev);
|
||||
dev_data->client = client;
|
||||
dev_data->res_index = 5;
|
||||
mutex_init(&dev_data->lock);
|
||||
|
||||
indio_dev->info = &ms5637_info;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = ms5637_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ms5637_channels);
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
ret = ms_sensors_reset(client, 0x1E, 3000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ms_sensors_tp_read_prom(dev_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ms5637_id[] = {
|
||||
{"ms5637", 0},
|
||||
{"ms8607-temppressure", 1},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct i2c_driver ms5637_driver = {
|
||||
.probe = ms5637_probe,
|
||||
.id_table = ms5637_id,
|
||||
.driver = {
|
||||
.name = "ms5637"
|
||||
},
|
||||
};
|
||||
|
||||
module_i2c_driver(ms5637_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Measurement-Specialties ms5637 temperature & pressure driver");
|
||||
MODULE_AUTHOR("William Markezana <william.markezana@meas-spec.com>");
|
||||
MODULE_AUTHOR("Ludovic Tancerel <ludovic.tancerel@maplehightech.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -271,6 +271,7 @@ static const struct of_device_id lidar_dt_ids[] = {
|
||||
{ .compatible = "pulsedlight,lidar-lite-v2" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, lidar_dt_ids);
|
||||
|
||||
static struct i2c_driver lidar_driver = {
|
||||
.driver = {
|
||||
|
@ -868,21 +868,12 @@ static void sx9500_gpio_probe(struct i2c_client *client,
|
||||
struct sx9500_data *data)
|
||||
{
|
||||
struct device *dev;
|
||||
struct gpio_desc *gpio;
|
||||
|
||||
if (!client)
|
||||
return;
|
||||
|
||||
dev = &client->dev;
|
||||
|
||||
if (client->irq <= 0) {
|
||||
gpio = devm_gpiod_get_index(dev, SX9500_GPIO_INT, 0, GPIOD_IN);
|
||||
if (IS_ERR(gpio))
|
||||
dev_err(dev, "gpio get irq failed\n");
|
||||
else
|
||||
client->irq = gpiod_to_irq(gpio);
|
||||
}
|
||||
|
||||
data->gpiod_rst = devm_gpiod_get_index(dev, SX9500_GPIO_RESET,
|
||||
0, GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(data->gpiod_rst)) {
|
||||
|
@ -23,4 +23,26 @@ config TMP006
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called tmp006.
|
||||
|
||||
config TSYS01
|
||||
tristate "Measurement Specialties TSYS01 temperature sensor using I2C bus connection"
|
||||
depends on I2C
|
||||
select IIO_MS_SENSORS_I2C
|
||||
help
|
||||
If you say yes here you get support for the Measurement Specialties
|
||||
TSYS01 I2C temperature sensor.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called tsys01.
|
||||
|
||||
config TSYS02D
|
||||
tristate "Measurement Specialties TSYS02D temperature sensor"
|
||||
depends on I2C
|
||||
select IIO_MS_SENSORS_I2C
|
||||
help
|
||||
If you say yes here you get support for the Measurement Specialties
|
||||
TSYS02D temperature sensor.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called tsys02d.
|
||||
|
||||
endmenu
|
||||
|
@ -4,3 +4,5 @@
|
||||
|
||||
obj-$(CONFIG_MLX90614) += mlx90614.o
|
||||
obj-$(CONFIG_TMP006) += tmp006.o
|
||||
obj-$(CONFIG_TSYS01) += tsys01.o
|
||||
obj-$(CONFIG_TSYS02D) += tsys02d.o
|
||||
|
@ -72,6 +72,7 @@
|
||||
#define MLX90614_CONST_SCALE 20 /* Scale in milliKelvin (0.02 * 1000) */
|
||||
#define MLX90614_CONST_RAW_EMISSIVITY_MAX 65535 /* max value for emissivity */
|
||||
#define MLX90614_CONST_EMISSIVITY_RESOLUTION 15259 /* 1/65535 ~ 0.000015259 */
|
||||
#define MLX90614_CONST_FIR 0x7 /* Fixed value for FIR part of low pass filter */
|
||||
|
||||
struct mlx90614_data {
|
||||
struct i2c_client *client;
|
||||
@ -156,15 +157,16 @@ static inline s32 mlx90614_iir_search(const struct i2c_client *client,
|
||||
* changes
|
||||
*/
|
||||
ret = i2c_smbus_read_word_data(client, MLX90614_CONFIG);
|
||||
if (ret > 0)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret &= ~MLX90614_CONFIG_FIR_MASK;
|
||||
ret |= MLX90614_CONST_FIR << MLX90614_CONFIG_FIR_SHIFT;
|
||||
ret &= ~MLX90614_CONFIG_IIR_MASK;
|
||||
ret |= i << MLX90614_CONFIG_IIR_SHIFT;
|
||||
|
||||
/* Write changed values */
|
||||
ret = mlx90614_write_word(client, MLX90614_CONFIG,
|
||||
(i << MLX90614_CONFIG_IIR_SHIFT) |
|
||||
(((u16) ((0x7 << MLX90614_CONFIG_FIR_SHIFT) |
|
||||
((u16) ret & (~((u16) MLX90614_CONFIG_FIR_MASK))))) &
|
||||
(~(u16) MLX90614_CONFIG_IIR_MASK)));
|
||||
ret = mlx90614_write_word(client, MLX90614_CONFIG, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
230
drivers/iio/temperature/tsys01.c
Normal file
230
drivers/iio/temperature/tsys01.c
Normal file
@ -0,0 +1,230 @@
|
||||
/*
|
||||
* tsys01.c - Support for Measurement-Specialties tsys01 temperature sensor
|
||||
*
|
||||
* Copyright (c) 2015 Measurement-Specialties
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*
|
||||
* Datasheet:
|
||||
* http://www.meas-spec.com/downloads/TSYS01_Digital_Temperature_Sensor.pdf
|
||||
*/
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/stat.h>
|
||||
#include "../common/ms_sensors/ms_sensors_i2c.h"
|
||||
|
||||
/* TSYS01 Commands */
|
||||
#define TSYS01_RESET 0x1E
|
||||
#define TSYS01_CONVERSION_START 0x48
|
||||
#define TSYS01_ADC_READ 0x00
|
||||
#define TSYS01_PROM_READ 0xA0
|
||||
|
||||
#define TSYS01_PROM_WORDS_NB 8
|
||||
|
||||
struct tsys01_dev {
|
||||
void *client;
|
||||
struct mutex lock; /* lock during conversion */
|
||||
|
||||
int (*reset)(void *cli, u8 cmd, unsigned int delay);
|
||||
int (*convert_and_read)(void *cli, u8 conv, u8 rd,
|
||||
unsigned int delay, u32 *adc);
|
||||
int (*read_prom_word)(void *cli, int cmd, u16 *word);
|
||||
|
||||
u16 prom[TSYS01_PROM_WORDS_NB];
|
||||
};
|
||||
|
||||
/* Multiplication coefficients for temperature computation */
|
||||
static const int coeff_mul[] = { -1500000, 1000000, -2000000,
|
||||
4000000, -2000000 };
|
||||
|
||||
static int tsys01_read_temperature(struct iio_dev *indio_dev,
|
||||
s32 *temperature)
|
||||
{
|
||||
int ret, i;
|
||||
u32 adc;
|
||||
s64 temp = 0;
|
||||
struct tsys01_dev *dev_data = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&dev_data->lock);
|
||||
ret = dev_data->convert_and_read(dev_data->client,
|
||||
TSYS01_CONVERSION_START,
|
||||
TSYS01_ADC_READ, 9000, &adc);
|
||||
mutex_unlock(&dev_data->lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
adc >>= 8;
|
||||
|
||||
/* Temperature algorithm */
|
||||
for (i = 4; i > 0; i--) {
|
||||
temp += coeff_mul[i] *
|
||||
(s64)dev_data->prom[5 - i];
|
||||
temp *= (s64)adc;
|
||||
temp = div64_s64(temp, 100000);
|
||||
}
|
||||
temp *= 10;
|
||||
temp += coeff_mul[0] * (s64)dev_data->prom[5];
|
||||
temp = div64_s64(temp, 100000);
|
||||
|
||||
*temperature = temp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tsys01_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *channel, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
int ret;
|
||||
s32 temperature;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
switch (channel->type) {
|
||||
case IIO_TEMP: /* in milli °C */
|
||||
ret = tsys01_read_temperature(indio_dev, &temperature);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val = temperature;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec tsys01_channels[] = {
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||
}
|
||||
};
|
||||
|
||||
static const struct iio_info tsys01_info = {
|
||||
.read_raw = tsys01_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static bool tsys01_crc_valid(u16 *n_prom)
|
||||
{
|
||||
u8 cnt;
|
||||
u8 sum = 0;
|
||||
|
||||
for (cnt = 0; cnt < TSYS01_PROM_WORDS_NB; cnt++)
|
||||
sum += ((n_prom[0] >> 8) + (n_prom[0] & 0xFF));
|
||||
|
||||
return (sum == 0);
|
||||
}
|
||||
|
||||
static int tsys01_read_prom(struct iio_dev *indio_dev)
|
||||
{
|
||||
int i, ret;
|
||||
struct tsys01_dev *dev_data = iio_priv(indio_dev);
|
||||
char buf[7 * TSYS01_PROM_WORDS_NB + 1];
|
||||
char *ptr = buf;
|
||||
|
||||
for (i = 0; i < TSYS01_PROM_WORDS_NB; i++) {
|
||||
ret = dev_data->read_prom_word(dev_data->client,
|
||||
TSYS01_PROM_READ + (i << 1),
|
||||
&dev_data->prom[i]);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = sprintf(ptr, "0x%04x ", dev_data->prom[i]);
|
||||
ptr += ret;
|
||||
}
|
||||
|
||||
if (!tsys01_crc_valid(dev_data->prom)) {
|
||||
dev_err(&indio_dev->dev, "prom crc check error\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
*ptr = 0;
|
||||
dev_info(&indio_dev->dev, "PROM coefficients : %s\n", buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tsys01_probe(struct iio_dev *indio_dev, struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct tsys01_dev *dev_data = iio_priv(indio_dev);
|
||||
|
||||
mutex_init(&dev_data->lock);
|
||||
|
||||
indio_dev->info = &tsys01_info;
|
||||
indio_dev->name = dev->driver->name;
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = tsys01_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(tsys01_channels);
|
||||
|
||||
ret = dev_data->reset(dev_data->client, TSYS01_RESET, 3000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = tsys01_read_prom(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
|
||||
static int tsys01_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct tsys01_dev *dev_data;
|
||||
struct iio_dev *indio_dev;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_WORD_DATA |
|
||||
I2C_FUNC_SMBUS_WRITE_BYTE |
|
||||
I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
|
||||
dev_err(&client->dev,
|
||||
"Adapter does not support some i2c transaction\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*dev_data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_data = iio_priv(indio_dev);
|
||||
dev_data->client = client;
|
||||
dev_data->reset = ms_sensors_reset;
|
||||
dev_data->read_prom_word = ms_sensors_read_prom_word;
|
||||
dev_data->convert_and_read = ms_sensors_convert_and_read;
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
return tsys01_probe(indio_dev, &client->dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id tsys01_id[] = {
|
||||
{"tsys01", 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, tsys01_id);
|
||||
|
||||
static struct i2c_driver tsys01_driver = {
|
||||
.probe = tsys01_i2c_probe,
|
||||
.id_table = tsys01_id,
|
||||
.driver = {
|
||||
.name = "tsys01",
|
||||
},
|
||||
};
|
||||
|
||||
module_i2c_driver(tsys01_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Measurement-Specialties tsys01 temperature driver");
|
||||
MODULE_AUTHOR("William Markezana <william.markezana@meas-spec.com>");
|
||||
MODULE_AUTHOR("Ludovic Tancerel <ludovic.tancerel@maplehightech.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
191
drivers/iio/temperature/tsys02d.c
Normal file
191
drivers/iio/temperature/tsys02d.c
Normal file
@ -0,0 +1,191 @@
|
||||
/*
|
||||
* tsys02d.c - Support for Measurement-Specialties tsys02d temperature sensor
|
||||
*
|
||||
* Copyright (c) 2015 Measurement-Specialties
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*
|
||||
* (7-bit I2C slave address 0x40)
|
||||
*
|
||||
* Datasheet:
|
||||
* http://www.meas-spec.com/downloads/Digital_Sensor_TSYS02D.pdf
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#include "../common/ms_sensors/ms_sensors_i2c.h"
|
||||
|
||||
#define TSYS02D_RESET 0xFE
|
||||
|
||||
static const int tsys02d_samp_freq[4] = { 20, 40, 70, 140 };
|
||||
/* String copy of the above const for readability purpose */
|
||||
static const char tsys02d_show_samp_freq[] = "20 40 70 140";
|
||||
|
||||
static int tsys02d_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *channel, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
int ret;
|
||||
s32 temperature;
|
||||
struct ms_ht_dev *dev_data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
switch (channel->type) {
|
||||
case IIO_TEMP: /* in milli °C */
|
||||
ret = ms_sensors_ht_read_temperature(dev_data,
|
||||
&temperature);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val = temperature;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val = tsys02d_samp_freq[dev_data->res_index];
|
||||
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int tsys02d_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct ms_ht_dev *dev_data = iio_priv(indio_dev);
|
||||
int i, ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
i = ARRAY_SIZE(tsys02d_samp_freq);
|
||||
while (i-- > 0)
|
||||
if (val == tsys02d_samp_freq[i])
|
||||
break;
|
||||
if (i < 0)
|
||||
return -EINVAL;
|
||||
mutex_lock(&dev_data->lock);
|
||||
dev_data->res_index = i;
|
||||
ret = ms_sensors_write_resolution(dev_data, i);
|
||||
mutex_unlock(&dev_data->lock);
|
||||
|
||||
return ret;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec tsys02d_channels[] = {
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
}
|
||||
};
|
||||
|
||||
static ssize_t tsys02_read_battery_low(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ms_ht_dev *dev_data = iio_priv(indio_dev);
|
||||
|
||||
return ms_sensors_show_battery_low(dev_data, buf);
|
||||
}
|
||||
|
||||
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(tsys02d_show_samp_freq);
|
||||
static IIO_DEVICE_ATTR(battery_low, S_IRUGO,
|
||||
tsys02_read_battery_low, NULL, 0);
|
||||
|
||||
static struct attribute *tsys02d_attributes[] = {
|
||||
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
||||
&iio_dev_attr_battery_low.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group tsys02d_attribute_group = {
|
||||
.attrs = tsys02d_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info tsys02d_info = {
|
||||
.read_raw = tsys02d_read_raw,
|
||||
.write_raw = tsys02d_write_raw,
|
||||
.attrs = &tsys02d_attribute_group,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int tsys02d_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct ms_ht_dev *dev_data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
u64 serial_number;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_WRITE_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_WRITE_BYTE |
|
||||
I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
|
||||
dev_err(&client->dev,
|
||||
"Adapter does not support some i2c transaction\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*dev_data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_data = iio_priv(indio_dev);
|
||||
dev_data->client = client;
|
||||
dev_data->res_index = 0;
|
||||
mutex_init(&dev_data->lock);
|
||||
|
||||
indio_dev->info = &tsys02d_info;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = tsys02d_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(tsys02d_channels);
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
ret = ms_sensors_reset(client, TSYS02D_RESET, 15000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ms_sensors_read_serial(client, &serial_number);
|
||||
if (ret)
|
||||
return ret;
|
||||
dev_info(&client->dev, "Serial number : %llx", serial_number);
|
||||
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id tsys02d_id[] = {
|
||||
{"tsys02d", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct i2c_driver tsys02d_driver = {
|
||||
.probe = tsys02d_probe,
|
||||
.id_table = tsys02d_id,
|
||||
.driver = {
|
||||
.name = "tsys02d",
|
||||
},
|
||||
};
|
||||
|
||||
module_i2c_driver(tsys02d_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Measurement-Specialties tsys02d temperature driver");
|
||||
MODULE_AUTHOR("William Markezana <william.markezana@meas-spec.com>");
|
||||
MODULE_AUTHOR("Ludovic Tancerel <ludovic.tancerel@maplehightech.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -68,12 +68,12 @@
|
||||
#define AD7746_VTSETUP_VTCHOP (1 << 0)
|
||||
|
||||
/* Excitation Setup Register Bit Designations (AD7746_REG_EXC_SETUP) */
|
||||
#define AD7746_EXCSETUP_CLKCTRL (1 << 7)
|
||||
#define AD7746_EXCSETUP_EXCON (1 << 6)
|
||||
#define AD7746_EXCSETUP_EXCB (1 << 5)
|
||||
#define AD7746_EXCSETUP_NEXCB (1 << 4)
|
||||
#define AD7746_EXCSETUP_EXCA (1 << 3)
|
||||
#define AD7746_EXCSETUP_NEXCA (1 << 2)
|
||||
#define AD7746_EXCSETUP_CLKCTRL BIT(7)
|
||||
#define AD7746_EXCSETUP_EXCON BIT(6)
|
||||
#define AD7746_EXCSETUP_EXCB BIT(5)
|
||||
#define AD7746_EXCSETUP_NEXCB BIT(4)
|
||||
#define AD7746_EXCSETUP_EXCA BIT(3)
|
||||
#define AD7746_EXCSETUP_NEXCA BIT(2)
|
||||
#define AD7746_EXCSETUP_EXCLVL(x) (((x) & 0x3) << 0)
|
||||
|
||||
/* Config Register Bit Designations (AD7746_REG_CFG) */
|
||||
@ -528,7 +528,8 @@ static int ad7746_write_raw(struct iio_dev *indio_dev,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* CAPDAC Scale = 21pF_typ / 127
|
||||
/*
|
||||
* CAPDAC Scale = 21pF_typ / 127
|
||||
* CIN Scale = 8.192pF / 2^24
|
||||
* Offset Scale = CAPDAC Scale / CIN Scale = 338646
|
||||
*/
|
||||
@ -600,7 +601,8 @@ static int ad7746_read_raw(struct iio_dev *indio_dev,
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_TEMP:
|
||||
/* temperature in milli degrees Celsius
|
||||
/*
|
||||
* temperature in milli degrees Celsius
|
||||
* T = ((*val / 2048) - 4096) * 1000
|
||||
*/
|
||||
*val = (*val * 125) / 256;
|
||||
@ -753,8 +755,6 @@ static int ad7746_probe(struct i2c_client *client,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_info(&client->dev, "%s capacitive sensor registered\n", id->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user