mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-30 11:41:38 -04:00
Staging driver patches for 4.1-rc1
Here's the big staging driver patchset for 4.1-rc1. There's a lot of patches here, the Outreachy application period happened during this development cycle, so that means that there was a lot of cleanup patches accepted. Other than the normal coding style and sparse fixes here, there are some driver updates and work toward making some of the drivers into "mergable" shape (like the Unisys drivers.) All of these have been in linux-next for a while. Signed-off-by: Greg Kroah-Hartman <[email protected]> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iEYEABECAAYFAlUsIA0ACgkQMUfUDdst+ylHuACgm8QZ9S6lk45/Qd9YeR3NSDaS upoAn3gVAaHnuxkW3anivuwOcirgCp3l =7VHU -----END PGP SIGNATURE----- Merge tag 'staging-4.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging Pull staging driver updates from Greg KH: "Here's the big staging driver patchset for 4.1-rc1. There's a lot of patches here, the Outreachy application period happened during this development cycle, so that means that there was a lot of cleanup patches accepted. Other than the normal coding style and sparse fixes here, there are some driver updates and work toward making some of the drivers into "mergable" shape (like the Unisys drivers.) All of these have been in linux-next for a while" * tag 'staging-4.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (1214 commits) staging: lustre: orthography & coding style staging: lustre: lnet: lnet: fix error return code staging: lustre: fix sparse warning Revert "Staging: sm750fb: Fix C99 Comments" Staging: rtl8192u: use correct array for debug output staging: rtl8192e: Remove dead code staging: rtl8192e: Comment cleanup (style/format) staging: rtl8192e: Fix indentation in rtllib_rx_auth_resp() staging: rtl8192e: Decrease nesting of rtllib_rx_auth_resp() staging: rtl8192e: Divide rtllib_rx_auth() staging: rtl8192e: Fix PRINTK_WITHOUT_KERN_LEVEL warnings staging: rtl8192e: Fix DO_WHILE_MACRO_WITH_TRAILING_SEMICOLON warning staging: rtl8192e: Fix BRACES warning staging: rtl8192e: Fix LINE_CONTINUATIONS warning staging: rtl8192e: Fix UNNECESSARY_PARENTHESES warnings staging: rtl8192e: remove unused EXPORT_SYMBOL_RSL macro staging: rtl8192e: Fix RETURN_VOID warnings staging: rtl8192e: Fix UNNECESSARY_ELSE warning staging: rtl8723au: Remove unneeded comments staging: rtl8723au: Use __func__ in trace logs ...
This commit is contained in:
commit
b79013b244
824 changed files with 38863 additions and 22325 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -1169,16 +1169,12 @@ static int kxcjk1013_gpio_probe(struct i2c_client *client,
|
|||
dev = &client->dev;
|
||||
|
||||
/* data ready gpio interrupt pin */
|
||||
gpio = devm_gpiod_get_index(dev, "kxcjk1013_int", 0);
|
||||
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_direction_input(gpio);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = gpiod_to_irq(gpio);
|
||||
|
||||
dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
|
||||
|
|
|
|||
|
|
@ -418,17 +418,18 @@ static int mma9551_gpio_probe(struct iio_dev *indio_dev)
|
|||
struct device *dev = &data->client->dev;
|
||||
|
||||
for (i = 0; i < MMA9551_GPIO_COUNT; i++) {
|
||||
gpio = devm_gpiod_get_index(dev, MMA9551_GPIO_NAME, i);
|
||||
gpio = devm_gpiod_get_index(dev, MMA9551_GPIO_NAME, i,
|
||||
GPIOD_IN);
|
||||
if (IS_ERR(gpio)) {
|
||||
dev_err(dev, "acpi gpio get index failed\n");
|
||||
return PTR_ERR(gpio);
|
||||
}
|
||||
|
||||
ret = gpiod_direction_input(gpio);
|
||||
if (ret)
|
||||
ret = gpiod_to_irq(gpio);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data->irqs[i] = gpiod_to_irq(gpio);
|
||||
data->irqs[i] = ret;
|
||||
ret = devm_request_threaded_irq(dev, data->irqs[i],
|
||||
NULL, mma9551_event_handler,
|
||||
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
||||
|
|
|
|||
|
|
@ -1109,16 +1109,12 @@ static int mma9553_gpio_probe(struct i2c_client *client)
|
|||
dev = &client->dev;
|
||||
|
||||
/* data ready gpio interrupt pin */
|
||||
gpio = devm_gpiod_get_index(dev, MMA9553_GPIO_NAME, 0);
|
||||
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_direction_input(gpio);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = gpiod_to_irq(gpio);
|
||||
|
||||
dev_dbg(dev, "gpio resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
|
||||
#define LIS3LV02DL_ACCEL_DEV_NAME "lis3lv02dl_accel"
|
||||
#define LSM303DLHC_ACCEL_DEV_NAME "lsm303dlhc_accel"
|
||||
#define LIS3DH_ACCEL_DEV_NAME "lis3dh"
|
||||
#define LSM330D_ACCEL_DEV_NAME "lsm330d_accel"
|
||||
|
|
|
|||
|
|
@ -129,6 +129,30 @@
|
|||
#define ST_ACCEL_3_IG1_EN_MASK 0x08
|
||||
#define ST_ACCEL_3_MULTIREAD_BIT false
|
||||
|
||||
/* CUSTOM VALUES FOR SENSOR 4 */
|
||||
#define ST_ACCEL_4_WAI_EXP 0x3a
|
||||
#define ST_ACCEL_4_ODR_ADDR 0x20
|
||||
#define ST_ACCEL_4_ODR_MASK 0x30 /* DF1 and DF0 */
|
||||
#define ST_ACCEL_4_ODR_AVL_40HZ_VAL 0x00
|
||||
#define ST_ACCEL_4_ODR_AVL_160HZ_VAL 0x01
|
||||
#define ST_ACCEL_4_ODR_AVL_640HZ_VAL 0x02
|
||||
#define ST_ACCEL_4_ODR_AVL_2560HZ_VAL 0x03
|
||||
#define ST_ACCEL_4_PW_ADDR 0x20
|
||||
#define ST_ACCEL_4_PW_MASK 0xc0
|
||||
#define ST_ACCEL_4_FS_ADDR 0x21
|
||||
#define ST_ACCEL_4_FS_MASK 0x80
|
||||
#define ST_ACCEL_4_FS_AVL_2_VAL 0X00
|
||||
#define ST_ACCEL_4_FS_AVL_6_VAL 0X01
|
||||
#define ST_ACCEL_4_FS_AVL_2_GAIN IIO_G_TO_M_S_2(1024)
|
||||
#define ST_ACCEL_4_FS_AVL_6_GAIN IIO_G_TO_M_S_2(340)
|
||||
#define ST_ACCEL_4_BDU_ADDR 0x21
|
||||
#define ST_ACCEL_4_BDU_MASK 0x40
|
||||
#define ST_ACCEL_4_DRDY_IRQ_ADDR 0x21
|
||||
#define ST_ACCEL_4_DRDY_IRQ_INT1_MASK 0x04
|
||||
#define ST_ACCEL_4_IG1_EN_ADDR 0x21
|
||||
#define ST_ACCEL_4_IG1_EN_MASK 0x08
|
||||
#define ST_ACCEL_4_MULTIREAD_BIT true
|
||||
|
||||
static const struct iio_chan_spec st_accel_12bit_channels[] = {
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
|
|
@ -373,6 +397,63 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
|
|||
.multi_read_bit = ST_ACCEL_3_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
},
|
||||
{
|
||||
.wai = ST_ACCEL_4_WAI_EXP,
|
||||
.sensors_supported = {
|
||||
[0] = LIS3LV02DL_ACCEL_DEV_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
|
||||
.odr = {
|
||||
.addr = ST_ACCEL_4_ODR_ADDR,
|
||||
.mask = ST_ACCEL_4_ODR_MASK,
|
||||
.odr_avl = {
|
||||
{ 40, ST_ACCEL_4_ODR_AVL_40HZ_VAL },
|
||||
{ 160, ST_ACCEL_4_ODR_AVL_160HZ_VAL, },
|
||||
{ 640, ST_ACCEL_4_ODR_AVL_640HZ_VAL, },
|
||||
{ 2560, ST_ACCEL_4_ODR_AVL_2560HZ_VAL, },
|
||||
},
|
||||
},
|
||||
.pw = {
|
||||
.addr = ST_ACCEL_4_PW_ADDR,
|
||||
.mask = ST_ACCEL_4_PW_MASK,
|
||||
.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
|
||||
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
|
||||
},
|
||||
.enable_axis = {
|
||||
.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
|
||||
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
|
||||
},
|
||||
.fs = {
|
||||
.addr = ST_ACCEL_4_FS_ADDR,
|
||||
.mask = ST_ACCEL_4_FS_MASK,
|
||||
.fs_avl = {
|
||||
[0] = {
|
||||
.num = ST_ACCEL_FS_AVL_2G,
|
||||
.value = ST_ACCEL_4_FS_AVL_2_VAL,
|
||||
.gain = ST_ACCEL_4_FS_AVL_2_GAIN,
|
||||
},
|
||||
[1] = {
|
||||
.num = ST_ACCEL_FS_AVL_6G,
|
||||
.value = ST_ACCEL_4_FS_AVL_6_VAL,
|
||||
.gain = ST_ACCEL_4_FS_AVL_6_GAIN,
|
||||
},
|
||||
},
|
||||
},
|
||||
.bdu = {
|
||||
.addr = ST_ACCEL_4_BDU_ADDR,
|
||||
.mask = ST_ACCEL_4_BDU_MASK,
|
||||
},
|
||||
.drdy_irq = {
|
||||
.addr = ST_ACCEL_4_DRDY_IRQ_ADDR,
|
||||
.mask_int1 = ST_ACCEL_4_DRDY_IRQ_INT1_MASK,
|
||||
.ig1 = {
|
||||
.en_addr = ST_ACCEL_4_IG1_EN_ADDR,
|
||||
.en_mask = ST_ACCEL_4_IG1_EN_MASK,
|
||||
},
|
||||
},
|
||||
.multi_read_bit = ST_ACCEL_4_MULTIREAD_BIT,
|
||||
.bootime = 2, /* guess */
|
||||
},
|
||||
};
|
||||
|
||||
static int st_accel_read_raw(struct iio_dev *indio_dev,
|
||||
|
|
|
|||
|
|
@ -20,6 +20,10 @@
|
|||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id st_accel_of_match[] = {
|
||||
{
|
||||
.compatible = "st,lis3lv02dl-accel",
|
||||
.data = LIS3LV02DL_ACCEL_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm303dlh-accel",
|
||||
.data = LSM303DLH_ACCEL_DEV_NAME,
|
||||
|
|
|
|||
|
|
@ -196,10 +196,11 @@ config MAX1363
|
|||
data via the iio dev interface.
|
||||
|
||||
config MCP320X
|
||||
tristate "Microchip Technology MCP3204/08"
|
||||
tristate "Microchip Technology MCP3x01/02/04/08"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Microchip Technology's MCP3204 or
|
||||
Say yes here to build support for Microchip Technology's
|
||||
MCP3001, MCP3002, MCP3004, MCP3008, MCP3201, MCP3202, MCP3204 or
|
||||
MCP3208 analog to digital converter.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
|
|
|
|||
|
|
@ -861,5 +861,5 @@ static struct spi_driver ad7793_driver = {
|
|||
module_spi_driver(ad7793_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <[email protected]>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7793 and simialr ADCs");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7793 and similar ADCs");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
|||
|
|
@ -275,7 +275,6 @@ static void vf610_adc_cfg_post_set(struct vf610_adc *info)
|
|||
static void vf610_adc_calibration(struct vf610_adc *info)
|
||||
{
|
||||
int adc_gc, hc_cfg;
|
||||
int timeout;
|
||||
|
||||
if (!info->adc_feature.calibration)
|
||||
return;
|
||||
|
|
@ -287,9 +286,7 @@ static void vf610_adc_calibration(struct vf610_adc *info)
|
|||
adc_gc = readl(info->regs + VF610_REG_ADC_GC);
|
||||
writel(adc_gc | VF610_ADC_CAL, info->regs + VF610_REG_ADC_GC);
|
||||
|
||||
timeout = wait_for_completion_timeout
|
||||
(&info->completion, VF610_ADC_TIMEOUT);
|
||||
if (timeout == 0)
|
||||
if (!wait_for_completion_timeout(&info->completion, VF610_ADC_TIMEOUT))
|
||||
dev_err(info->dev, "Timeout for adc calibration\n");
|
||||
|
||||
adc_gc = readl(info->regs + VF610_REG_ADC_GS);
|
||||
|
|
|
|||
|
|
@ -437,7 +437,7 @@ int ssp_queue_ssp_refresh_task(struct ssp_data *data, unsigned int delay)
|
|||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct of_device_id ssp_of_match[] = {
|
||||
static const struct of_device_id ssp_of_match[] = {
|
||||
{
|
||||
.compatible = "samsung,sensorhub-rinato",
|
||||
.data = &ssp_rinato_info,
|
||||
|
|
|
|||
|
|
@ -143,11 +143,16 @@ config AD7303
|
|||
ad7303.
|
||||
|
||||
config MAX517
|
||||
tristate "Maxim MAX517/518/519 DAC driver"
|
||||
tristate "Maxim MAX517/518/519/520/521 DAC driver"
|
||||
depends on I2C
|
||||
help
|
||||
If you say yes here you get support for the Maxim chips MAX517,
|
||||
MAX518 and MAX519 (I2C 8-Bit DACs with rail-to-rail outputs).
|
||||
If you say yes here you get support for the following Maxim chips
|
||||
(I2C 8-Bit DACs with rail-to-rail outputs):
|
||||
MAX517 - Single channel, single reference
|
||||
MAX518 - Dual channel, ref=Vdd
|
||||
MAX519 - Dual channel, dual reference
|
||||
MAX520 - Quad channel, quad reference
|
||||
MAX521 - Octal channel, independent ref for ch0-3, shared ref for ch4-7
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called max517.
|
||||
|
|
|
|||
|
|
@ -39,11 +39,13 @@ enum max517_device_ids {
|
|||
ID_MAX517,
|
||||
ID_MAX518,
|
||||
ID_MAX519,
|
||||
ID_MAX520,
|
||||
ID_MAX521,
|
||||
};
|
||||
|
||||
struct max517_data {
|
||||
struct i2c_client *client;
|
||||
unsigned short vref_mv[2];
|
||||
unsigned short vref_mv[8];
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -149,7 +151,13 @@ static const struct iio_info max517_info = {
|
|||
|
||||
static const struct iio_chan_spec max517_channels[] = {
|
||||
MAX517_CHANNEL(0),
|
||||
MAX517_CHANNEL(1)
|
||||
MAX517_CHANNEL(1),
|
||||
MAX517_CHANNEL(2),
|
||||
MAX517_CHANNEL(3),
|
||||
MAX517_CHANNEL(4),
|
||||
MAX517_CHANNEL(5),
|
||||
MAX517_CHANNEL(6),
|
||||
MAX517_CHANNEL(7),
|
||||
};
|
||||
|
||||
static int max517_probe(struct i2c_client *client,
|
||||
|
|
@ -158,6 +166,7 @@ static int max517_probe(struct i2c_client *client,
|
|||
struct max517_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
struct max517_platform_data *platform_data = client->dev.platform_data;
|
||||
int chan;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
|
|
@ -169,11 +178,21 @@ static int max517_probe(struct i2c_client *client,
|
|||
/* establish that the iio_dev is a child of the i2c device */
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
|
||||
/* reduced channel set for MAX517 */
|
||||
if (id->driver_data == ID_MAX517)
|
||||
indio_dev->num_channels = 1;
|
||||
else
|
||||
switch (id->driver_data) {
|
||||
case ID_MAX521:
|
||||
indio_dev->num_channels = 8;
|
||||
break;
|
||||
case ID_MAX520:
|
||||
indio_dev->num_channels = 4;
|
||||
break;
|
||||
case ID_MAX519:
|
||||
case ID_MAX518:
|
||||
indio_dev->num_channels = 2;
|
||||
break;
|
||||
default: /* single channel for MAX517 */
|
||||
indio_dev->num_channels = 1;
|
||||
break;
|
||||
}
|
||||
indio_dev->channels = max517_channels;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &max517_info;
|
||||
|
|
@ -182,11 +201,11 @@ static int max517_probe(struct i2c_client *client,
|
|||
* Reference voltage on MAX518 and default is 5V, else take vref_mv
|
||||
* from platform_data
|
||||
*/
|
||||
if (id->driver_data == ID_MAX518 || !platform_data) {
|
||||
data->vref_mv[0] = data->vref_mv[1] = 5000; /* mV */
|
||||
} else {
|
||||
data->vref_mv[0] = platform_data->vref_mv[0];
|
||||
data->vref_mv[1] = platform_data->vref_mv[1];
|
||||
for (chan = 0; chan < indio_dev->num_channels; chan++) {
|
||||
if (id->driver_data == ID_MAX518 || !platform_data)
|
||||
data->vref_mv[chan] = 5000; /* mV */
|
||||
else
|
||||
data->vref_mv[chan] = platform_data->vref_mv[chan];
|
||||
}
|
||||
|
||||
return iio_device_register(indio_dev);
|
||||
|
|
@ -202,6 +221,8 @@ static const struct i2c_device_id max517_id[] = {
|
|||
{ "max517", ID_MAX517 },
|
||||
{ "max518", ID_MAX518 },
|
||||
{ "max519", ID_MAX519 },
|
||||
{ "max520", ID_MAX520 },
|
||||
{ "max521", ID_MAX521 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, max517_id);
|
||||
|
|
@ -218,5 +239,5 @@ static struct i2c_driver max517_driver = {
|
|||
module_i2c_driver(max517_driver);
|
||||
|
||||
MODULE_AUTHOR("Roland Stigge <[email protected]>");
|
||||
MODULE_DESCRIPTION("MAX517/MAX518/MAX519 8-bit DAC");
|
||||
MODULE_DESCRIPTION("MAX517/518/519/520/521 8-bit DAC");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
|||
|
|
@ -1001,16 +1001,12 @@ static int bmg160_gpio_probe(struct i2c_client *client,
|
|||
dev = &client->dev;
|
||||
|
||||
/* data ready gpio interrupt pin */
|
||||
gpio = devm_gpiod_get_index(dev, BMG160_GPIO_NAME, 0);
|
||||
gpio = devm_gpiod_get_index(dev, BMG160_GPIO_NAME, 0, GPIOD_IN);
|
||||
if (IS_ERR(gpio)) {
|
||||
dev_err(dev, "acpi gpio get index failed\n");
|
||||
return PTR_ERR(gpio);
|
||||
}
|
||||
|
||||
ret = gpiod_direction_input(gpio);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = gpiod_to_irq(gpio);
|
||||
|
||||
dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
|
||||
|
|
|
|||
|
|
@ -223,6 +223,10 @@ static int itg3200_initial_setup(struct iio_dev *indio_dev)
|
|||
int ret;
|
||||
u8 val;
|
||||
|
||||
ret = itg3200_reset(indio_dev);
|
||||
if (ret)
|
||||
goto err_ret;
|
||||
|
||||
ret = itg3200_read_reg_8(indio_dev, ITG3200_REG_ADDRESS, &val);
|
||||
if (ret)
|
||||
goto err_ret;
|
||||
|
|
@ -233,10 +237,6 @@ static int itg3200_initial_setup(struct iio_dev *indio_dev)
|
|||
goto err_ret;
|
||||
}
|
||||
|
||||
ret = itg3200_reset(indio_dev);
|
||||
if (ret)
|
||||
goto err_ret;
|
||||
|
||||
ret = itg3200_enable_full_scale(indio_dev);
|
||||
err_ret:
|
||||
return ret;
|
||||
|
|
@ -351,6 +351,26 @@ static int itg3200_remove(struct i2c_client *client)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused itg3200_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct itg3200 *st = iio_priv(indio_dev);
|
||||
|
||||
dev_dbg(&st->i2c->dev, "suspend device");
|
||||
|
||||
return itg3200_write_reg_8(indio_dev, ITG3200_REG_POWER_MANAGEMENT,
|
||||
ITG3200_SLEEP);
|
||||
}
|
||||
|
||||
static int __maybe_unused itg3200_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
|
||||
return itg3200_initial_setup(indio_dev);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(itg3200_pm_ops, itg3200_suspend, itg3200_resume);
|
||||
|
||||
static const struct i2c_device_id itg3200_id[] = {
|
||||
{ "itg3200", 0 },
|
||||
{ }
|
||||
|
|
@ -361,6 +381,7 @@ static struct i2c_driver itg3200_driver = {
|
|||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "itg3200",
|
||||
.pm = &itg3200_pm_ops,
|
||||
},
|
||||
.id_table = itg3200_id,
|
||||
.probe = itg3200_probe,
|
||||
|
|
|
|||
|
|
@ -87,6 +87,31 @@
|
|||
#define ST_GYRO_2_DRDY_IRQ_INT2_MASK 0x08
|
||||
#define ST_GYRO_2_MULTIREAD_BIT true
|
||||
|
||||
/* CUSTOM VALUES FOR SENSOR 3 */
|
||||
#define ST_GYRO_3_WAI_EXP 0xd7
|
||||
#define ST_GYRO_3_ODR_ADDR 0x20
|
||||
#define ST_GYRO_3_ODR_MASK 0xc0
|
||||
#define ST_GYRO_3_ODR_AVL_95HZ_VAL 0x00
|
||||
#define ST_GYRO_3_ODR_AVL_190HZ_VAL 0x01
|
||||
#define ST_GYRO_3_ODR_AVL_380HZ_VAL 0x02
|
||||
#define ST_GYRO_3_ODR_AVL_760HZ_VAL 0x03
|
||||
#define ST_GYRO_3_PW_ADDR 0x20
|
||||
#define ST_GYRO_3_PW_MASK 0x08
|
||||
#define ST_GYRO_3_FS_ADDR 0x23
|
||||
#define ST_GYRO_3_FS_MASK 0x30
|
||||
#define ST_GYRO_3_FS_AVL_250_VAL 0x00
|
||||
#define ST_GYRO_3_FS_AVL_500_VAL 0x01
|
||||
#define ST_GYRO_3_FS_AVL_2000_VAL 0x02
|
||||
#define ST_GYRO_3_FS_AVL_250_GAIN IIO_DEGREE_TO_RAD(8750)
|
||||
#define ST_GYRO_3_FS_AVL_500_GAIN IIO_DEGREE_TO_RAD(17500)
|
||||
#define ST_GYRO_3_FS_AVL_2000_GAIN IIO_DEGREE_TO_RAD(70000)
|
||||
#define ST_GYRO_3_BDU_ADDR 0x23
|
||||
#define ST_GYRO_3_BDU_MASK 0x80
|
||||
#define ST_GYRO_3_DRDY_IRQ_ADDR 0x22
|
||||
#define ST_GYRO_3_DRDY_IRQ_INT2_MASK 0x08
|
||||
#define ST_GYRO_3_MULTIREAD_BIT true
|
||||
|
||||
|
||||
static const struct iio_chan_spec st_gyro_16bit_channels[] = {
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL,
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
|
|
@ -225,6 +250,64 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = {
|
|||
.multi_read_bit = ST_GYRO_2_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
},
|
||||
{
|
||||
.wai = ST_GYRO_3_WAI_EXP,
|
||||
.sensors_supported = {
|
||||
[0] = L3GD20_GYRO_DEV_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_gyro_16bit_channels,
|
||||
.odr = {
|
||||
.addr = ST_GYRO_3_ODR_ADDR,
|
||||
.mask = ST_GYRO_3_ODR_MASK,
|
||||
.odr_avl = {
|
||||
{ 95, ST_GYRO_3_ODR_AVL_95HZ_VAL, },
|
||||
{ 190, ST_GYRO_3_ODR_AVL_190HZ_VAL, },
|
||||
{ 380, ST_GYRO_3_ODR_AVL_380HZ_VAL, },
|
||||
{ 760, ST_GYRO_3_ODR_AVL_760HZ_VAL, },
|
||||
},
|
||||
},
|
||||
.pw = {
|
||||
.addr = ST_GYRO_3_PW_ADDR,
|
||||
.mask = ST_GYRO_3_PW_MASK,
|
||||
.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
|
||||
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
|
||||
},
|
||||
.enable_axis = {
|
||||
.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
|
||||
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
|
||||
},
|
||||
.fs = {
|
||||
.addr = ST_GYRO_3_FS_ADDR,
|
||||
.mask = ST_GYRO_3_FS_MASK,
|
||||
.fs_avl = {
|
||||
[0] = {
|
||||
.num = ST_GYRO_FS_AVL_250DPS,
|
||||
.value = ST_GYRO_3_FS_AVL_250_VAL,
|
||||
.gain = ST_GYRO_3_FS_AVL_250_GAIN,
|
||||
},
|
||||
[1] = {
|
||||
.num = ST_GYRO_FS_AVL_500DPS,
|
||||
.value = ST_GYRO_3_FS_AVL_500_VAL,
|
||||
.gain = ST_GYRO_3_FS_AVL_500_GAIN,
|
||||
},
|
||||
[2] = {
|
||||
.num = ST_GYRO_FS_AVL_2000DPS,
|
||||
.value = ST_GYRO_3_FS_AVL_2000_VAL,
|
||||
.gain = ST_GYRO_3_FS_AVL_2000_GAIN,
|
||||
},
|
||||
},
|
||||
},
|
||||
.bdu = {
|
||||
.addr = ST_GYRO_3_BDU_ADDR,
|
||||
.mask = ST_GYRO_3_BDU_MASK,
|
||||
},
|
||||
.drdy_irq = {
|
||||
.addr = ST_GYRO_3_DRDY_IRQ_ADDR,
|
||||
.mask_int2 = ST_GYRO_3_DRDY_IRQ_INT2_MASK,
|
||||
},
|
||||
.multi_read_bit = ST_GYRO_3_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
},
|
||||
};
|
||||
|
||||
static int st_gyro_read_raw(struct iio_dev *indio_dev,
|
||||
|
|
|
|||
|
|
@ -3,4 +3,4 @@
|
|||
#
|
||||
|
||||
obj-$(CONFIG_INV_MPU6050_IIO) += inv-mpu6050.o
|
||||
inv-mpu6050-objs := inv_mpu_core.o inv_mpu_ring.o inv_mpu_trigger.o
|
||||
inv-mpu6050-objs := inv_mpu_core.o inv_mpu_ring.o inv_mpu_trigger.o inv_mpu_acpi.o
|
||||
|
|
|
|||
211
drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c
Normal file
211
drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
* inv_mpu_acpi: ACPI processing for creating client devices
|
||||
* Copyright (c) 2015, 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.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/acpi.h>
|
||||
#include "inv_mpu_iio.h"
|
||||
|
||||
enum inv_mpu_product_name {
|
||||
INV_MPU_NOT_MATCHED,
|
||||
INV_MPU_ASUS_T100TA,
|
||||
};
|
||||
|
||||
static enum inv_mpu_product_name matched_product_name;
|
||||
|
||||
static int __init asus_t100_matched(const struct dmi_system_id *d)
|
||||
{
|
||||
matched_product_name = INV_MPU_ASUS_T100TA;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dmi_system_id inv_mpu_dev_list[] = {
|
||||
{
|
||||
.callback = asus_t100_matched,
|
||||
.ident = "Asus Transformer Book T100",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "T100TA"),
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, "1.0"),
|
||||
},
|
||||
},
|
||||
/* Add more matching tables here..*/
|
||||
{}
|
||||
};
|
||||
|
||||
static int asus_acpi_get_sensor_info(struct acpi_device *adev,
|
||||
struct i2c_client *client,
|
||||
struct i2c_board_info *info)
|
||||
{
|
||||
struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
|
||||
int i;
|
||||
acpi_status status;
|
||||
union acpi_object *cpm;
|
||||
|
||||
status = acpi_evaluate_object(adev->handle, "CNF0", NULL, &buffer);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
|
||||
cpm = buffer.pointer;
|
||||
for (i = 0; i < cpm->package.count; ++i) {
|
||||
union acpi_object *elem;
|
||||
int j;
|
||||
|
||||
elem = &(cpm->package.elements[i]);
|
||||
for (j = 0; j < elem->package.count; ++j) {
|
||||
union acpi_object *sub_elem;
|
||||
|
||||
sub_elem = &(elem->package.elements[j]);
|
||||
if (sub_elem->type == ACPI_TYPE_STRING)
|
||||
strlcpy(info->type, sub_elem->string.pointer,
|
||||
sizeof(info->type));
|
||||
else if (sub_elem->type == ACPI_TYPE_INTEGER) {
|
||||
if (sub_elem->integer.value != client->addr) {
|
||||
info->addr = sub_elem->integer.value;
|
||||
break; /* Not a MPU6500 primary */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kfree(buffer.pointer);
|
||||
|
||||
return cpm->package.count;
|
||||
}
|
||||
|
||||
static int acpi_i2c_check_resource(struct acpi_resource *ares, void *data)
|
||||
{
|
||||
u32 *addr = data;
|
||||
|
||||
if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) {
|
||||
struct acpi_resource_i2c_serialbus *sb;
|
||||
|
||||
sb = &ares->data.i2c_serial_bus;
|
||||
if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_I2C) {
|
||||
if (*addr)
|
||||
*addr |= (sb->slave_address << 16);
|
||||
else
|
||||
*addr = sb->slave_address;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tell the ACPI core that we already copied this address */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int inv_mpu_process_acpi_config(struct i2c_client *client,
|
||||
unsigned short *primary_addr,
|
||||
unsigned short *secondary_addr)
|
||||
{
|
||||
const struct acpi_device_id *id;
|
||||
struct acpi_device *adev;
|
||||
u32 i2c_addr = 0;
|
||||
LIST_HEAD(resources);
|
||||
int ret;
|
||||
|
||||
id = acpi_match_device(client->dev.driver->acpi_match_table,
|
||||
&client->dev);
|
||||
if (!id)
|
||||
return -ENODEV;
|
||||
|
||||
adev = ACPI_COMPANION(&client->dev);
|
||||
if (!adev)
|
||||
return -ENODEV;
|
||||
|
||||
ret = acpi_dev_get_resources(adev, &resources,
|
||||
acpi_i2c_check_resource, &i2c_addr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
acpi_dev_free_resource_list(&resources);
|
||||
*primary_addr = i2c_addr & 0x0000ffff;
|
||||
*secondary_addr = (i2c_addr & 0xffff0000) >> 16;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int inv_mpu_acpi_create_mux_client(struct inv_mpu6050_state *st)
|
||||
{
|
||||
|
||||
st->mux_client = NULL;
|
||||
if (ACPI_HANDLE(&st->client->dev)) {
|
||||
struct i2c_board_info info;
|
||||
struct acpi_device *adev;
|
||||
int ret = -1;
|
||||
|
||||
adev = ACPI_COMPANION(&st->client->dev);
|
||||
memset(&info, 0, sizeof(info));
|
||||
|
||||
dmi_check_system(inv_mpu_dev_list);
|
||||
switch (matched_product_name) {
|
||||
case INV_MPU_ASUS_T100TA:
|
||||
ret = asus_acpi_get_sensor_info(adev, st->client,
|
||||
&info);
|
||||
break;
|
||||
/* Add more matched product processing here */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
/* No matching DMI, so create device on INV6XX type */
|
||||
unsigned short primary, secondary;
|
||||
|
||||
ret = inv_mpu_process_acpi_config(st->client, &primary,
|
||||
&secondary);
|
||||
if (!ret && secondary) {
|
||||
char *name;
|
||||
|
||||
info.addr = secondary;
|
||||
strlcpy(info.type, dev_name(&adev->dev),
|
||||
sizeof(info.type));
|
||||
name = strchr(info.type, ':');
|
||||
if (name)
|
||||
*name = '\0';
|
||||
strlcat(info.type, "-client",
|
||||
sizeof(info.type));
|
||||
} else
|
||||
return 0; /* no secondary addr, which is OK */
|
||||
}
|
||||
st->mux_client = i2c_new_device(st->mux_adapter, &info);
|
||||
if (!st->mux_client)
|
||||
return -ENODEV;
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void inv_mpu_acpi_delete_mux_client(struct inv_mpu6050_state *st)
|
||||
{
|
||||
if (st->mux_client)
|
||||
i2c_unregister_device(st->mux_client);
|
||||
}
|
||||
#else
|
||||
|
||||
#include "inv_mpu_iio.h"
|
||||
|
||||
int inv_mpu_acpi_create_mux_client(struct inv_mpu6050_state *st)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void inv_mpu_acpi_delete_mux_client(struct inv_mpu6050_state *st)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
|
@ -829,8 +829,14 @@ static int inv_mpu_probe(struct i2c_client *client,
|
|||
goto out_unreg_device;
|
||||
}
|
||||
|
||||
result = inv_mpu_acpi_create_mux_client(st);
|
||||
if (result)
|
||||
goto out_del_mux;
|
||||
|
||||
return 0;
|
||||
|
||||
out_del_mux:
|
||||
i2c_del_mux_adapter(st->mux_adapter);
|
||||
out_unreg_device:
|
||||
iio_device_unregister(indio_dev);
|
||||
out_remove_trigger:
|
||||
|
|
@ -845,6 +851,7 @@ static int inv_mpu_remove(struct i2c_client *client)
|
|||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
|
||||
inv_mpu_acpi_delete_mux_client(st);
|
||||
i2c_del_mux_adapter(st->mux_adapter);
|
||||
iio_device_unregister(indio_dev);
|
||||
inv_mpu6050_remove_trigger(st);
|
||||
|
|
|
|||
|
|
@ -121,6 +121,7 @@ struct inv_mpu6050_state {
|
|||
spinlock_t time_stamp_lock;
|
||||
struct i2c_client *client;
|
||||
struct i2c_adapter *mux_adapter;
|
||||
struct i2c_client *mux_client;
|
||||
unsigned int powerup_count;
|
||||
struct inv_mpu6050_platform_data plat_data;
|
||||
DECLARE_KFIFO(timestamps, long long, TIMESTAMP_FIFO_SIZE);
|
||||
|
|
@ -251,3 +252,5 @@ int inv_reset_fifo(struct iio_dev *indio_dev);
|
|||
int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask);
|
||||
int inv_mpu6050_write_reg(struct inv_mpu6050_state *st, int reg, u8 val);
|
||||
int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on);
|
||||
int inv_mpu_acpi_create_mux_client(struct inv_mpu6050_state *st);
|
||||
void inv_mpu_acpi_delete_mux_client(struct inv_mpu6050_state *st);
|
||||
|
|
|
|||
|
|
@ -169,19 +169,18 @@ static const u16 kmx61_uscale_table[] = {9582, 19163, 38326};
|
|||
static const struct {
|
||||
int val;
|
||||
int val2;
|
||||
u8 odr_bits;
|
||||
} kmx61_samp_freq_table[] = { {12, 500000, 0x00},
|
||||
{25, 0, 0x01},
|
||||
{50, 0, 0x02},
|
||||
{100, 0, 0x03},
|
||||
{200, 0, 0x04},
|
||||
{400, 0, 0x05},
|
||||
{800, 0, 0x06},
|
||||
{1600, 0, 0x07},
|
||||
{0, 781000, 0x08},
|
||||
{1, 563000, 0x09},
|
||||
{3, 125000, 0x0A},
|
||||
{6, 250000, 0x0B} };
|
||||
} kmx61_samp_freq_table[] = { {12, 500000},
|
||||
{25, 0},
|
||||
{50, 0},
|
||||
{100, 0},
|
||||
{200, 0},
|
||||
{400, 0},
|
||||
{800, 0},
|
||||
{1600, 0},
|
||||
{0, 781000},
|
||||
{1, 563000},
|
||||
{3, 125000},
|
||||
{6, 250000} };
|
||||
|
||||
static const struct {
|
||||
int val;
|
||||
|
|
@ -302,24 +301,10 @@ static int kmx61_convert_freq_to_bit(int val, int val2)
|
|||
for (i = 0; i < ARRAY_SIZE(kmx61_samp_freq_table); i++)
|
||||
if (val == kmx61_samp_freq_table[i].val &&
|
||||
val2 == kmx61_samp_freq_table[i].val2)
|
||||
return kmx61_samp_freq_table[i].odr_bits;
|
||||
return i;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int kmx61_convert_bit_to_freq(u8 odr_bits, int *val, int *val2)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(kmx61_samp_freq_table); i++)
|
||||
if (odr_bits == kmx61_samp_freq_table[i].odr_bits) {
|
||||
*val = kmx61_samp_freq_table[i].val;
|
||||
*val2 = kmx61_samp_freq_table[i].val2;
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
static int kmx61_convert_wake_up_odr_to_bit(int val, int val2)
|
||||
{
|
||||
int i;
|
||||
|
|
@ -478,7 +463,7 @@ static int kmx61_set_odr(struct kmx61_data *data, int val, int val2, u8 device)
|
|||
|
||||
static int kmx61_get_odr(struct kmx61_data *data, int *val, int *val2,
|
||||
u8 device)
|
||||
{ int i;
|
||||
{
|
||||
u8 lodr_bits;
|
||||
|
||||
if (device & KMX61_ACC)
|
||||
|
|
@ -490,13 +475,13 @@ static int kmx61_get_odr(struct kmx61_data *data, int *val, int *val2,
|
|||
else
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(kmx61_samp_freq_table); i++)
|
||||
if (lodr_bits == kmx61_samp_freq_table[i].odr_bits) {
|
||||
*val = kmx61_samp_freq_table[i].val;
|
||||
*val2 = kmx61_samp_freq_table[i].val2;
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
if (lodr_bits >= ARRAY_SIZE(kmx61_samp_freq_table))
|
||||
return -EINVAL;
|
||||
|
||||
*val = kmx61_samp_freq_table[lodr_bits].val;
|
||||
*val2 = kmx61_samp_freq_table[lodr_bits].val2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kmx61_set_range(struct kmx61_data *data, u8 range)
|
||||
|
|
@ -580,8 +565,11 @@ static int kmx61_chip_init(struct kmx61_data *data)
|
|||
}
|
||||
data->odr_bits = ret;
|
||||
|
||||
/* set output data rate for wake up (motion detection) function */
|
||||
ret = kmx61_convert_bit_to_freq(data->odr_bits, &val, &val2);
|
||||
/*
|
||||
* set output data rate for wake up (motion detection) function
|
||||
* to match data rate for accelerometer sampling
|
||||
*/
|
||||
ret = kmx61_get_odr(data, &val, &val2, KMX61_ACC);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
@ -1267,16 +1255,12 @@ static int kmx61_gpio_probe(struct i2c_client *client, struct kmx61_data *data)
|
|||
dev = &client->dev;
|
||||
|
||||
/* data ready gpio interrupt pin */
|
||||
gpio = devm_gpiod_get_index(dev, KMX61_GPIO_NAME, 0);
|
||||
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_direction_input(gpio);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = gpiod_to_irq(gpio);
|
||||
|
||||
dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
|
||||
|
|
|
|||
|
|
@ -37,11 +37,57 @@ static bool iio_buffer_is_active(struct iio_buffer *buf)
|
|||
return !list_empty(&buf->buffer_list);
|
||||
}
|
||||
|
||||
static bool iio_buffer_data_available(struct iio_buffer *buf)
|
||||
static size_t iio_buffer_data_available(struct iio_buffer *buf)
|
||||
{
|
||||
return buf->access->data_available(buf);
|
||||
}
|
||||
|
||||
static int iio_buffer_flush_hwfifo(struct iio_dev *indio_dev,
|
||||
struct iio_buffer *buf, size_t required)
|
||||
{
|
||||
if (!indio_dev->info->hwfifo_flush_to_buffer)
|
||||
return -ENODEV;
|
||||
|
||||
return indio_dev->info->hwfifo_flush_to_buffer(indio_dev, required);
|
||||
}
|
||||
|
||||
static bool iio_buffer_ready(struct iio_dev *indio_dev, struct iio_buffer *buf,
|
||||
size_t to_wait, int to_flush)
|
||||
{
|
||||
size_t avail;
|
||||
int flushed = 0;
|
||||
|
||||
/* wakeup if the device was unregistered */
|
||||
if (!indio_dev->info)
|
||||
return true;
|
||||
|
||||
/* drain the buffer if it was disabled */
|
||||
if (!iio_buffer_is_active(buf)) {
|
||||
to_wait = min_t(size_t, to_wait, 1);
|
||||
to_flush = 0;
|
||||
}
|
||||
|
||||
avail = iio_buffer_data_available(buf);
|
||||
|
||||
if (avail >= to_wait) {
|
||||
/* force a flush for non-blocking reads */
|
||||
if (!to_wait && !avail && to_flush)
|
||||
iio_buffer_flush_hwfifo(indio_dev, buf, to_flush);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (to_flush)
|
||||
flushed = iio_buffer_flush_hwfifo(indio_dev, buf,
|
||||
to_wait - avail);
|
||||
if (flushed <= 0)
|
||||
return false;
|
||||
|
||||
if (avail + flushed >= to_wait)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* iio_buffer_read_first_n_outer() - chrdev read for buffer access
|
||||
*
|
||||
|
|
@ -53,6 +99,9 @@ ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf,
|
|||
{
|
||||
struct iio_dev *indio_dev = filp->private_data;
|
||||
struct iio_buffer *rb = indio_dev->buffer;
|
||||
size_t datum_size;
|
||||
size_t to_wait = 0;
|
||||
size_t to_read;
|
||||
int ret;
|
||||
|
||||
if (!indio_dev->info)
|
||||
|
|
@ -61,19 +110,28 @@ ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf,
|
|||
if (!rb || !rb->access->read_first_n)
|
||||
return -EINVAL;
|
||||
|
||||
do {
|
||||
if (!iio_buffer_data_available(rb)) {
|
||||
if (filp->f_flags & O_NONBLOCK)
|
||||
return -EAGAIN;
|
||||
datum_size = rb->bytes_per_datum;
|
||||
|
||||
ret = wait_event_interruptible(rb->pollq,
|
||||
iio_buffer_data_available(rb) ||
|
||||
indio_dev->info == NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (indio_dev->info == NULL)
|
||||
return -ENODEV;
|
||||
}
|
||||
/*
|
||||
* If datum_size is 0 there will never be anything to read from the
|
||||
* buffer, so signal end of file now.
|
||||
*/
|
||||
if (!datum_size)
|
||||
return 0;
|
||||
|
||||
to_read = min_t(size_t, n / datum_size, rb->watermark);
|
||||
|
||||
if (!(filp->f_flags & O_NONBLOCK))
|
||||
to_wait = to_read;
|
||||
|
||||
do {
|
||||
ret = wait_event_interruptible(rb->pollq,
|
||||
iio_buffer_ready(indio_dev, rb, to_wait, to_read));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!indio_dev->info)
|
||||
return -ENODEV;
|
||||
|
||||
ret = rb->access->read_first_n(rb, n, buf);
|
||||
if (ret == 0 && (filp->f_flags & O_NONBLOCK))
|
||||
|
|
@ -96,9 +154,8 @@ unsigned int iio_buffer_poll(struct file *filp,
|
|||
return -ENODEV;
|
||||
|
||||
poll_wait(filp, &rb->pollq, wait);
|
||||
if (iio_buffer_data_available(rb))
|
||||
if (iio_buffer_ready(indio_dev, rb, rb->watermark, 0))
|
||||
return POLLIN | POLLRDNORM;
|
||||
/* need a way of knowing if there may be enough data... */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -123,6 +180,7 @@ void iio_buffer_init(struct iio_buffer *buffer)
|
|||
INIT_LIST_HEAD(&buffer->buffer_list);
|
||||
init_waitqueue_head(&buffer->pollq);
|
||||
kref_init(&buffer->ref);
|
||||
buffer->watermark = 1;
|
||||
}
|
||||
EXPORT_SYMBOL(iio_buffer_init);
|
||||
|
||||
|
|
@ -416,6 +474,11 @@ static ssize_t iio_buffer_write_length(struct device *dev,
|
|||
buffer->access->set_length(buffer, val);
|
||||
ret = 0;
|
||||
}
|
||||
if (ret)
|
||||
goto out;
|
||||
if (buffer->length && buffer->length < buffer->watermark)
|
||||
buffer->watermark = buffer->length;
|
||||
out:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret ? ret : len;
|
||||
|
|
@ -472,6 +535,7 @@ static void iio_buffer_activate(struct iio_dev *indio_dev,
|
|||
static void iio_buffer_deactivate(struct iio_buffer *buffer)
|
||||
{
|
||||
list_del_init(&buffer->buffer_list);
|
||||
wake_up_interruptible(&buffer->pollq);
|
||||
iio_buffer_put(buffer);
|
||||
}
|
||||
|
||||
|
|
@ -629,19 +693,16 @@ static int __iio_update_buffers(struct iio_dev *indio_dev,
|
|||
}
|
||||
}
|
||||
/* Definitely possible for devices to support both of these. */
|
||||
if (indio_dev->modes & INDIO_BUFFER_TRIGGERED) {
|
||||
if (!indio_dev->trig) {
|
||||
printk(KERN_INFO "Buffer not started: no trigger\n");
|
||||
ret = -EINVAL;
|
||||
/* Can only occur on first buffer */
|
||||
goto error_run_postdisable;
|
||||
}
|
||||
if ((indio_dev->modes & INDIO_BUFFER_TRIGGERED) && indio_dev->trig) {
|
||||
indio_dev->currentmode = INDIO_BUFFER_TRIGGERED;
|
||||
} else if (indio_dev->modes & INDIO_BUFFER_HARDWARE) {
|
||||
indio_dev->currentmode = INDIO_BUFFER_HARDWARE;
|
||||
} else if (indio_dev->modes & INDIO_BUFFER_SOFTWARE) {
|
||||
indio_dev->currentmode = INDIO_BUFFER_SOFTWARE;
|
||||
} else { /* Should never be reached */
|
||||
/* Can only occur on first buffer */
|
||||
if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
|
||||
pr_info("Buffer not started: no trigger\n");
|
||||
ret = -EINVAL;
|
||||
goto error_run_postdisable;
|
||||
}
|
||||
|
|
@ -754,12 +815,68 @@ done:
|
|||
|
||||
static const char * const iio_scan_elements_group_name = "scan_elements";
|
||||
|
||||
static ssize_t iio_buffer_show_watermark(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct iio_buffer *buffer = indio_dev->buffer;
|
||||
|
||||
return sprintf(buf, "%u\n", buffer->watermark);
|
||||
}
|
||||
|
||||
static ssize_t iio_buffer_store_watermark(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct iio_buffer *buffer = indio_dev->buffer;
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtouint(buf, 10, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (!val)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
if (val > buffer->length) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (iio_buffer_is_active(indio_dev->buffer)) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
buffer->watermark = val;
|
||||
|
||||
if (indio_dev->info->hwfifo_set_watermark)
|
||||
indio_dev->info->hwfifo_set_watermark(indio_dev, val);
|
||||
out:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(length, S_IRUGO | S_IWUSR, iio_buffer_read_length,
|
||||
iio_buffer_write_length);
|
||||
static struct device_attribute dev_attr_length_ro = __ATTR(length,
|
||||
S_IRUGO, iio_buffer_read_length, NULL);
|
||||
static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR,
|
||||
iio_buffer_show_enable, iio_buffer_store_enable);
|
||||
static DEVICE_ATTR(watermark, S_IRUGO | S_IWUSR,
|
||||
iio_buffer_show_watermark, iio_buffer_store_watermark);
|
||||
|
||||
static struct attribute *iio_buffer_attrs[] = {
|
||||
&dev_attr_length.attr,
|
||||
&dev_attr_enable.attr,
|
||||
&dev_attr_watermark.attr,
|
||||
};
|
||||
|
||||
int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
|
||||
{
|
||||
|
|
@ -778,21 +895,23 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
|
|||
attrcount++;
|
||||
}
|
||||
|
||||
buffer->buffer_group.name = "buffer";
|
||||
buffer->buffer_group.attrs = kcalloc(attrcount + 3,
|
||||
sizeof(*buffer->buffer_group.attrs), GFP_KERNEL);
|
||||
if (!buffer->buffer_group.attrs)
|
||||
attr = kcalloc(attrcount + ARRAY_SIZE(iio_buffer_attrs) + 1,
|
||||
sizeof(struct attribute *), GFP_KERNEL);
|
||||
if (!attr)
|
||||
return -ENOMEM;
|
||||
|
||||
if (buffer->access->set_length)
|
||||
buffer->buffer_group.attrs[0] = &dev_attr_length.attr;
|
||||
else
|
||||
buffer->buffer_group.attrs[0] = &dev_attr_length_ro.attr;
|
||||
buffer->buffer_group.attrs[1] = &dev_attr_enable.attr;
|
||||
memcpy(attr, iio_buffer_attrs, sizeof(iio_buffer_attrs));
|
||||
if (!buffer->access->set_length)
|
||||
attr[0] = &dev_attr_length_ro.attr;
|
||||
|
||||
if (buffer->attrs)
|
||||
memcpy(&buffer->buffer_group.attrs[2], buffer->attrs,
|
||||
sizeof(*&buffer->buffer_group.attrs) * attrcount);
|
||||
buffer->buffer_group.attrs[attrcount+2] = NULL;
|
||||
memcpy(&attr[ARRAY_SIZE(iio_buffer_attrs)], buffer->attrs,
|
||||
sizeof(struct attribute *) * attrcount);
|
||||
|
||||
attr[attrcount + ARRAY_SIZE(iio_buffer_attrs)] = NULL;
|
||||
|
||||
buffer->buffer_group.name = "buffer";
|
||||
buffer->buffer_group.attrs = attr;
|
||||
|
||||
indio_dev->groups[indio_dev->groupcounter++] = &buffer->buffer_group;
|
||||
|
||||
|
|
@ -937,8 +1056,18 @@ static const void *iio_demux(struct iio_buffer *buffer,
|
|||
static int iio_push_to_buffer(struct iio_buffer *buffer, const void *data)
|
||||
{
|
||||
const void *dataout = iio_demux(buffer, data);
|
||||
int ret;
|
||||
|
||||
return buffer->access->store_to(buffer, dataout);
|
||||
ret = buffer->access->store_to(buffer, dataout);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* We can't just test for watermark to decide if we wake the poll queue
|
||||
* because read may request less samples than the watermark.
|
||||
*/
|
||||
wake_up_interruptible_poll(&buffer->pollq, POLLIN | POLLRDNORM);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void iio_buffer_demux_free(struct iio_buffer *buffer)
|
||||
|
|
|
|||
|
|
@ -83,9 +83,6 @@ static int iio_store_to_kfifo(struct iio_buffer *r,
|
|||
ret = kfifo_in(&kf->kf, data, 1);
|
||||
if (ret != 1)
|
||||
return -EBUSY;
|
||||
|
||||
wake_up_interruptible_poll(&r->pollq, POLLIN | POLLRDNORM);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -109,16 +106,16 @@ static int iio_read_first_n_kfifo(struct iio_buffer *r,
|
|||
return copied;
|
||||
}
|
||||
|
||||
static bool iio_kfifo_buf_data_available(struct iio_buffer *r)
|
||||
static size_t iio_kfifo_buf_data_available(struct iio_buffer *r)
|
||||
{
|
||||
struct iio_kfifo *kf = iio_to_kfifo(r);
|
||||
bool empty;
|
||||
size_t samples;
|
||||
|
||||
mutex_lock(&kf->user_lock);
|
||||
empty = kfifo_is_empty(&kf->kf);
|
||||
samples = kfifo_len(&kf->kf);
|
||||
mutex_unlock(&kf->user_lock);
|
||||
|
||||
return !empty;
|
||||
return samples;
|
||||
}
|
||||
|
||||
static void iio_kfifo_buffer_release(struct iio_buffer *buffer)
|
||||
|
|
|
|||
|
|
@ -59,6 +59,16 @@ config CM3232
|
|||
To compile this driver as a module, choose M here:
|
||||
the module will be called cm3232.
|
||||
|
||||
config CM3323
|
||||
depends on I2C
|
||||
tristate "Capella CM3323 color light sensor"
|
||||
help
|
||||
Say Y here if you want to build a driver for Capela CM3323
|
||||
color sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called cm3323.
|
||||
|
||||
config CM36651
|
||||
depends on I2C
|
||||
tristate "CM36651 driver"
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ obj-$(CONFIG_AL3320A) += al3320a.o
|
|||
obj-$(CONFIG_APDS9300) += apds9300.o
|
||||
obj-$(CONFIG_CM32181) += cm32181.o
|
||||
obj-$(CONFIG_CM3232) += cm3232.o
|
||||
obj-$(CONFIG_CM3323) += cm3323.o
|
||||
obj-$(CONFIG_CM36651) += cm36651.o
|
||||
obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o
|
||||
obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o
|
||||
|
|
|
|||
|
|
@ -378,6 +378,39 @@ static const struct i2c_device_id cm3232_id[] = {
|
|||
{}
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int cm3232_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct cm3232_chip *chip = iio_priv(indio_dev);
|
||||
struct i2c_client *client = chip->client;
|
||||
int ret;
|
||||
|
||||
chip->regs_cmd |= CM3232_CMD_ALS_DISABLE;
|
||||
ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
|
||||
chip->regs_cmd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cm3232_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct cm3232_chip *chip = iio_priv(indio_dev);
|
||||
struct i2c_client *client = chip->client;
|
||||
int ret;
|
||||
|
||||
chip->regs_cmd &= ~CM3232_CMD_ALS_DISABLE;
|
||||
ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
|
||||
chip->regs_cmd | CM3232_CMD_ALS_RESET);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops cm3232_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(cm3232_suspend, cm3232_resume)};
|
||||
#endif
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, cm3232_id);
|
||||
|
||||
static const struct of_device_id cm3232_of_match[] = {
|
||||
|
|
@ -390,6 +423,9 @@ static struct i2c_driver cm3232_driver = {
|
|||
.name = "cm3232",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(cm3232_of_match),
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
.pm = &cm3232_pm_ops,
|
||||
#endif
|
||||
},
|
||||
.id_table = cm3232_id,
|
||||
.probe = cm3232_probe,
|
||||
|
|
|
|||
286
drivers/iio/light/cm3323.c
Normal file
286
drivers/iio/light/cm3323.c
Normal file
|
|
@ -0,0 +1,286 @@
|
|||
/*
|
||||
* CM3323 - Capella Color Light Sensor
|
||||
*
|
||||
* Copyright (c) 2015, Intel Corporation.
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* IIO driver for CM3323 (7-bit I2C slave address 0x10)
|
||||
*
|
||||
* TODO: calibscale to correct the lens factor
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define CM3323_DRV_NAME "cm3323"
|
||||
|
||||
#define CM3323_CMD_CONF 0x00
|
||||
#define CM3323_CMD_RED_DATA 0x08
|
||||
#define CM3323_CMD_GREEN_DATA 0x09
|
||||
#define CM3323_CMD_BLUE_DATA 0x0A
|
||||
#define CM3323_CMD_CLEAR_DATA 0x0B
|
||||
|
||||
#define CM3323_CONF_SD_BIT BIT(0) /* sensor disable */
|
||||
#define CM3323_CONF_AF_BIT BIT(1) /* auto/manual force mode */
|
||||
#define CM3323_CONF_IT_MASK (BIT(4) | BIT(5) | BIT(6))
|
||||
#define CM3323_CONF_IT_SHIFT 4
|
||||
|
||||
#define CM3323_INT_TIME_AVAILABLE "0.04 0.08 0.16 0.32 0.64 1.28"
|
||||
|
||||
static const struct {
|
||||
int val;
|
||||
int val2;
|
||||
} cm3323_int_time[] = {
|
||||
{0, 40000}, /* 40 ms */
|
||||
{0, 80000}, /* 80 ms */
|
||||
{0, 160000}, /* 160 ms */
|
||||
{0, 320000}, /* 320 ms */
|
||||
{0, 640000}, /* 640 ms */
|
||||
{1, 280000}, /* 1280 ms */
|
||||
};
|
||||
|
||||
struct cm3323_data {
|
||||
struct i2c_client *client;
|
||||
u16 reg_conf;
|
||||
struct mutex mutex;
|
||||
};
|
||||
|
||||
#define CM3323_COLOR_CHANNEL(_color, _addr) { \
|
||||
.type = IIO_INTENSITY, \
|
||||
.modified = 1, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), \
|
||||
.channel2 = IIO_MOD_LIGHT_##_color, \
|
||||
.address = _addr, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec cm3323_channels[] = {
|
||||
CM3323_COLOR_CHANNEL(RED, CM3323_CMD_RED_DATA),
|
||||
CM3323_COLOR_CHANNEL(GREEN, CM3323_CMD_GREEN_DATA),
|
||||
CM3323_COLOR_CHANNEL(BLUE, CM3323_CMD_BLUE_DATA),
|
||||
CM3323_COLOR_CHANNEL(CLEAR, CM3323_CMD_CLEAR_DATA),
|
||||
};
|
||||
|
||||
static IIO_CONST_ATTR_INT_TIME_AVAIL(CM3323_INT_TIME_AVAILABLE);
|
||||
|
||||
static struct attribute *cm3323_attributes[] = {
|
||||
&iio_const_attr_integration_time_available.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group cm3323_attribute_group = {
|
||||
.attrs = cm3323_attributes,
|
||||
};
|
||||
|
||||
static int cm3323_init(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
struct cm3323_data *data = iio_priv(indio_dev);
|
||||
|
||||
ret = i2c_smbus_read_word_data(data->client, CM3323_CMD_CONF);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error reading reg_conf\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* enable sensor and set auto force mode */
|
||||
ret &= ~(CM3323_CONF_SD_BIT | CM3323_CONF_AF_BIT);
|
||||
|
||||
ret = i2c_smbus_write_word_data(data->client, CM3323_CMD_CONF, ret);
|
||||
if (ret < 0) {
|
||||
dev_err(&data->client->dev, "Error writing reg_conf\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->reg_conf = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cm3323_disable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
struct cm3323_data *data = iio_priv(indio_dev);
|
||||
|
||||
ret = i2c_smbus_write_word_data(data->client, CM3323_CMD_CONF,
|
||||
CM3323_CONF_SD_BIT);
|
||||
if (ret < 0)
|
||||
dev_err(&data->client->dev, "Error writing reg_conf\n");
|
||||
}
|
||||
|
||||
static int cm3323_set_it_bits(struct cm3323_data *data, int val, int val2)
|
||||
{
|
||||
int i, ret;
|
||||
u16 reg_conf;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(cm3323_int_time); i++) {
|
||||
if (val == cm3323_int_time[i].val &&
|
||||
val2 == cm3323_int_time[i].val2) {
|
||||
reg_conf = data->reg_conf;
|
||||
reg_conf |= i << CM3323_CONF_IT_SHIFT;
|
||||
|
||||
ret = i2c_smbus_write_word_data(data->client,
|
||||
CM3323_CMD_CONF,
|
||||
reg_conf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data->reg_conf = reg_conf;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int cm3323_get_it_bits(struct cm3323_data *data)
|
||||
{
|
||||
int bits;
|
||||
|
||||
bits = (data->reg_conf & CM3323_CONF_IT_MASK) >>
|
||||
CM3323_CONF_IT_SHIFT;
|
||||
|
||||
if (bits >= ARRAY_SIZE(cm3323_int_time))
|
||||
return -EINVAL;
|
||||
return bits;
|
||||
}
|
||||
|
||||
static int cm3323_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
int i, ret;
|
||||
struct cm3323_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&data->mutex);
|
||||
ret = i2c_smbus_read_word_data(data->client, chan->address);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&data->mutex);
|
||||
return ret;
|
||||
}
|
||||
*val = ret;
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
mutex_lock(&data->mutex);
|
||||
i = cm3323_get_it_bits(data);
|
||||
if (i < 0) {
|
||||
mutex_unlock(&data->mutex);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*val = cm3323_int_time[i].val;
|
||||
*val2 = cm3323_int_time[i].val2;
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int cm3323_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val,
|
||||
int val2, long mask)
|
||||
{
|
||||
struct cm3323_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
mutex_lock(&data->mutex);
|
||||
ret = cm3323_set_it_bits(data, val, val2);
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
return ret;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info cm3323_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = cm3323_read_raw,
|
||||
.write_raw = cm3323_write_raw,
|
||||
.attrs = &cm3323_attribute_group,
|
||||
};
|
||||
|
||||
static int cm3323_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct cm3323_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
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;
|
||||
|
||||
mutex_init(&data->mutex);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &cm3323_info;
|
||||
indio_dev->name = CM3323_DRV_NAME;
|
||||
indio_dev->channels = cm3323_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(cm3323_channels);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = cm3323_init(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "cm3323 chip init failed\n");
|
||||
return ret;
|
||||
}
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "failed to register iio dev\n");
|
||||
goto err_init;
|
||||
}
|
||||
return 0;
|
||||
err_init:
|
||||
cm3323_disable(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cm3323_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
cm3323_disable(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id cm3323_id[] = {
|
||||
{"cm3323", 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, cm3323_id);
|
||||
|
||||
static struct i2c_driver cm3323_driver = {
|
||||
.driver = {
|
||||
.name = CM3323_DRV_NAME,
|
||||
},
|
||||
.probe = cm3323_probe,
|
||||
.remove = cm3323_remove,
|
||||
.id_table = cm3323_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(cm3323_driver);
|
||||
|
||||
MODULE_AUTHOR("Daniel Baluta <[email protected]>");
|
||||
MODULE_DESCRIPTION("Capella CM3323 Color Light Sensor driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
@ -46,6 +46,7 @@
|
|||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
|
@ -966,7 +967,6 @@ static irqreturn_t gp2ap020a00f_trigger_handler(int irq, void *data)
|
|||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct gp2ap020a00f_data *priv = iio_priv(indio_dev);
|
||||
size_t d_size = 0;
|
||||
__le32 light_lux;
|
||||
int i, out_val, ret;
|
||||
|
||||
for_each_set_bit(i, indio_dev->active_scan_mask,
|
||||
|
|
@ -981,8 +981,8 @@ static irqreturn_t gp2ap020a00f_trigger_handler(int irq, void *data)
|
|||
i == GP2AP020A00F_SCAN_MODE_LIGHT_IR) {
|
||||
out_val = le16_to_cpup((__le16 *)&priv->buffer[d_size]);
|
||||
gp2ap020a00f_output_to_lux(priv, &out_val);
|
||||
light_lux = cpu_to_le32(out_val);
|
||||
memcpy(&priv->buffer[d_size], (u8 *)&light_lux, 4);
|
||||
|
||||
put_unaligned_le32(out_val, &priv->buffer[d_size]);
|
||||
d_size += 4;
|
||||
} else {
|
||||
d_size += 2;
|
||||
|
|
|
|||
|
|
@ -308,7 +308,7 @@ static bool jsa1212_is_volatile_reg(struct device *dev, unsigned int reg)
|
|||
}
|
||||
}
|
||||
|
||||
static struct regmap_config jsa1212_regmap_config = {
|
||||
static const struct regmap_config jsa1212_regmap_config = {
|
||||
.name = JSA1212_REGMAP_NAME,
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
|
|
|
|||
|
|
@ -333,6 +333,13 @@ static int ltr501_init(struct ltr501_data *data)
|
|||
data->ps_contr);
|
||||
}
|
||||
|
||||
static int ltr501_powerdown(struct ltr501_data *data)
|
||||
{
|
||||
return ltr501_write_contr(data->client,
|
||||
data->als_contr & ~LTR501_CONTR_ACTIVE,
|
||||
data->ps_contr & ~LTR501_CONTR_ACTIVE);
|
||||
}
|
||||
|
||||
static int ltr501_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
|
|
@ -370,7 +377,7 @@ static int ltr501_probe(struct i2c_client *client,
|
|||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
ltr501_trigger_handler, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto powerdown_on_error;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
|
|
@ -380,16 +387,11 @@ static int ltr501_probe(struct i2c_client *client,
|
|||
|
||||
error_unreg_buffer:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
powerdown_on_error:
|
||||
ltr501_powerdown(data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ltr501_powerdown(struct ltr501_data *data)
|
||||
{
|
||||
return ltr501_write_contr(data->client,
|
||||
data->als_contr & ~LTR501_CONTR_ACTIVE,
|
||||
data->ps_contr & ~LTR501_CONTR_ACTIVE);
|
||||
}
|
||||
|
||||
static int ltr501_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
|
|
|||
|
|
@ -321,6 +321,12 @@ static const struct iio_info mag3110_info = {
|
|||
|
||||
static const unsigned long mag3110_scan_masks[] = {0x7, 0xf, 0};
|
||||
|
||||
static int mag3110_standby(struct mag3110_data *data)
|
||||
{
|
||||
return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1,
|
||||
data->ctrl_reg1 & ~MAG3110_CTRL_AC);
|
||||
}
|
||||
|
||||
static int mag3110_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
|
|
@ -360,12 +366,12 @@ static int mag3110_probe(struct i2c_client *client,
|
|||
ret = i2c_smbus_write_byte_data(client, MAG3110_CTRL_REG2,
|
||||
MAG3110_CTRL_AUTO_MRST_EN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto standby_on_error;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
mag3110_trigger_handler, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto standby_on_error;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
|
|
@ -374,15 +380,11 @@ static int mag3110_probe(struct i2c_client *client,
|
|||
|
||||
buffer_cleanup:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
standby_on_error:
|
||||
mag3110_standby(iio_priv(indio_dev));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mag3110_standby(struct mag3110_data *data)
|
||||
{
|
||||
return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1,
|
||||
data->ctrl_reg1 & ~MAG3110_CTRL_AC);
|
||||
}
|
||||
|
||||
static int mag3110_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
|
|
|||
|
|
@ -52,6 +52,33 @@ config MPL3115
|
|||
To compile this driver as a module, choose M here: the module
|
||||
will be called mpl3115.
|
||||
|
||||
config MS5611
|
||||
tristate "Measurement Specialities MS5611 pressure sensor driver"
|
||||
help
|
||||
Say Y here to build support for the Measurement Specialities
|
||||
MS5611 pressure and temperature sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called ms5611_core.
|
||||
|
||||
config MS5611_I2C
|
||||
tristate "support I2C bus connection"
|
||||
depends on I2C && MS5611
|
||||
help
|
||||
Say Y here to build I2C bus support for MS5611.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called ms5611_i2c.
|
||||
|
||||
config MS5611_SPI
|
||||
tristate "support SPI bus connection"
|
||||
depends on SPI_MASTER && MS5611
|
||||
help
|
||||
Say Y here to build SPI bus support for MS5611.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called ms5611_spi.
|
||||
|
||||
config IIO_ST_PRESS
|
||||
tristate "STMicroelectronics pressure sensor Driver"
|
||||
depends on (I2C || SPI_MASTER) && SYSFS
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ obj-$(CONFIG_BMP280) += bmp280.o
|
|||
obj-$(CONFIG_HID_SENSOR_PRESS) += hid-sensor-press.o
|
||||
obj-$(CONFIG_MPL115) += mpl115.o
|
||||
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_IIO_ST_PRESS) += st_pressure.o
|
||||
st_pressure-y := st_pressure_core.o
|
||||
st_pressure-$(CONFIG_IIO_BUFFER) += st_pressure_buffer.o
|
||||
|
|
|
|||
44
drivers/iio/pressure/ms5611.h
Normal file
44
drivers/iio/pressure/ms5611.h
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* MS5611 pressure and temperature sensor driver
|
||||
*
|
||||
* Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
|
||||
*
|
||||
* 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 _MS5611_H
|
||||
#define _MS5611_H
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#define MS5611_RESET 0x1e
|
||||
#define MS5611_READ_ADC 0x00
|
||||
#define MS5611_READ_PROM_WORD 0xA0
|
||||
#define MS5611_START_TEMP_CONV 0x58
|
||||
#define MS5611_START_PRESSURE_CONV 0x48
|
||||
|
||||
#define MS5611_CONV_TIME_MIN 9040
|
||||
#define MS5611_CONV_TIME_MAX 10000
|
||||
|
||||
#define MS5611_PROM_WORDS_NB 8
|
||||
|
||||
struct ms5611_state {
|
||||
void *client;
|
||||
struct mutex lock;
|
||||
|
||||
int (*reset)(struct device *dev);
|
||||
int (*read_prom_word)(struct device *dev, int index, u16 *word);
|
||||
int (*read_adc_temp_and_pressure)(struct device *dev,
|
||||
s32 *temp, s32 *pressure);
|
||||
|
||||
u16 prom[MS5611_PROM_WORDS_NB];
|
||||
};
|
||||
|
||||
int ms5611_probe(struct iio_dev *indio_dev, struct device *dev);
|
||||
|
||||
#endif /* _MS5611_H */
|
||||
215
drivers/iio/pressure/ms5611_core.c
Normal file
215
drivers/iio/pressure/ms5611_core.c
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
/*
|
||||
* MS5611 pressure and temperature sensor driver
|
||||
*
|
||||
* Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Data sheet:
|
||||
* http://www.meas-spec.com/downloads/MS5611-01BA03.pdf
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "ms5611.h"
|
||||
|
||||
static bool ms5611_prom_is_valid(u16 *prom, size_t len)
|
||||
{
|
||||
int i, j;
|
||||
uint16_t crc = 0, crc_orig = prom[7] & 0x000F;
|
||||
|
||||
prom[7] &= 0xFF00;
|
||||
|
||||
for (i = 0; i < len * 2; i++) {
|
||||
if (i % 2 == 1)
|
||||
crc ^= prom[i >> 1] & 0x00FF;
|
||||
else
|
||||
crc ^= prom[i >> 1] >> 8;
|
||||
|
||||
for (j = 0; j < 8; j++) {
|
||||
if (crc & 0x8000)
|
||||
crc = (crc << 1) ^ 0x3000;
|
||||
else
|
||||
crc <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
crc = (crc >> 12) & 0x000F;
|
||||
|
||||
return crc_orig != 0x0000 && crc == crc_orig;
|
||||
}
|
||||
|
||||
static int ms5611_read_prom(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret, i;
|
||||
struct ms5611_state *st = iio_priv(indio_dev);
|
||||
|
||||
for (i = 0; i < MS5611_PROM_WORDS_NB; i++) {
|
||||
ret = st->read_prom_word(&indio_dev->dev, i, &st->prom[i]);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev,
|
||||
"failed to read prom at %d\n", i);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ms5611_prom_is_valid(st->prom, MS5611_PROM_WORDS_NB)) {
|
||||
dev_err(&indio_dev->dev, "PROM integrity check failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ms5611_read_temp_and_pressure(struct iio_dev *indio_dev,
|
||||
s32 *temp, s32 *pressure)
|
||||
{
|
||||
int ret;
|
||||
s32 t, p;
|
||||
s64 off, sens, dt;
|
||||
struct ms5611_state *st = iio_priv(indio_dev);
|
||||
|
||||
ret = st->read_adc_temp_and_pressure(&indio_dev->dev, &t, &p);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev,
|
||||
"failed to read temperature and pressure\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dt = t - (st->prom[5] << 8);
|
||||
off = ((s64)st->prom[2] << 16) + ((st->prom[4] * dt) >> 7);
|
||||
sens = ((s64)st->prom[1] << 15) + ((st->prom[3] * dt) >> 8);
|
||||
|
||||
t = 2000 + ((st->prom[6] * dt) >> 23);
|
||||
if (t < 2000) {
|
||||
s64 off2, sens2, t2;
|
||||
|
||||
t2 = (dt * dt) >> 31;
|
||||
off2 = (5 * (t - 2000) * (t - 2000)) >> 1;
|
||||
sens2 = off2 >> 1;
|
||||
|
||||
if (t < -1500) {
|
||||
s64 tmp = (t + 1500) * (t + 1500);
|
||||
|
||||
off2 += 7 * tmp;
|
||||
sens2 += (11 * tmp) >> 1;
|
||||
}
|
||||
|
||||
t -= t2;
|
||||
off -= off2;
|
||||
sens -= sens2;
|
||||
}
|
||||
|
||||
*temp = t;
|
||||
*pressure = (((p * sens) >> 21) - off) >> 15;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ms5611_reset(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
struct ms5611_state *st = iio_priv(indio_dev);
|
||||
|
||||
ret = st->reset(&indio_dev->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev, "failed to reset device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
usleep_range(3000, 4000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ms5611_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
int ret;
|
||||
s32 temp, pressure;
|
||||
struct ms5611_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
mutex_lock(&st->lock);
|
||||
ret = ms5611_read_temp_and_pressure(indio_dev,
|
||||
&temp, &pressure);
|
||||
mutex_unlock(&st->lock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_TEMP:
|
||||
*val = temp * 10;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_PRESSURE:
|
||||
*val = pressure / 1000;
|
||||
*val2 = (pressure % 1000) * 1000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ms5611_channels[] = {
|
||||
{
|
||||
.type = IIO_PRESSURE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
|
||||
BIT(IIO_CHAN_INFO_SCALE)
|
||||
},
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
|
||||
BIT(IIO_CHAN_INFO_SCALE)
|
||||
}
|
||||
};
|
||||
|
||||
static const struct iio_info ms5611_info = {
|
||||
.read_raw = &ms5611_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int ms5611_init(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ms5611_reset(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return ms5611_read_prom(indio_dev);
|
||||
}
|
||||
|
||||
int ms5611_probe(struct iio_dev *indio_dev, struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct ms5611_state *st = iio_priv(indio_dev);
|
||||
|
||||
mutex_init(&st->lock);
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->name = dev->driver->name;
|
||||
indio_dev->info = &ms5611_info;
|
||||
indio_dev->channels = ms5611_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ms5611_channels);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = ms5611_init(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
EXPORT_SYMBOL(ms5611_probe);
|
||||
|
||||
MODULE_AUTHOR("Tomasz Duszynski <[email protected]>");
|
||||
MODULE_DESCRIPTION("MS5611 core driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
128
drivers/iio/pressure/ms5611_i2c.c
Normal file
128
drivers/iio/pressure/ms5611_i2c.c
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* MS5611 pressure and temperature sensor driver (I2C bus)
|
||||
*
|
||||
* Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 7-bit I2C slave addresses:
|
||||
*
|
||||
* 0x77 (CSB pin low)
|
||||
* 0x76 (CSB pin high)
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "ms5611.h"
|
||||
|
||||
static int ms5611_i2c_reset(struct device *dev)
|
||||
{
|
||||
struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev));
|
||||
|
||||
return i2c_smbus_write_byte(st->client, MS5611_RESET);
|
||||
}
|
||||
|
||||
static int ms5611_i2c_read_prom_word(struct device *dev, int index, u16 *word)
|
||||
{
|
||||
int ret;
|
||||
struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev));
|
||||
|
||||
ret = i2c_smbus_read_word_swapped(st->client,
|
||||
MS5611_READ_PROM_WORD + (index << 1));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*word = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ms5611_i2c_read_adc(struct ms5611_state *st, s32 *val)
|
||||
{
|
||||
int ret;
|
||||
u8 buf[3];
|
||||
|
||||
ret = i2c_smbus_read_i2c_block_data(st->client, MS5611_READ_ADC,
|
||||
3, buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = (buf[0] << 16) | (buf[1] << 8) | buf[2];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ms5611_i2c_read_adc_temp_and_pressure(struct device *dev,
|
||||
s32 *temp, s32 *pressure)
|
||||
{
|
||||
int ret;
|
||||
struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev));
|
||||
|
||||
ret = i2c_smbus_write_byte(st->client, MS5611_START_TEMP_CONV);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
usleep_range(MS5611_CONV_TIME_MIN, MS5611_CONV_TIME_MAX);
|
||||
|
||||
ret = ms5611_i2c_read_adc(st, temp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = i2c_smbus_write_byte(st->client, MS5611_START_PRESSURE_CONV);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
usleep_range(MS5611_CONV_TIME_MIN, MS5611_CONV_TIME_MAX);
|
||||
|
||||
return ms5611_i2c_read_adc(st, pressure);
|
||||
}
|
||||
|
||||
static int ms5611_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct ms5611_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_WRITE_BYTE |
|
||||
I2C_FUNC_SMBUS_READ_WORD_DATA |
|
||||
I2C_FUNC_SMBUS_READ_I2C_BLOCK))
|
||||
return -ENODEV;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
st->reset = ms5611_i2c_reset;
|
||||
st->read_prom_word = ms5611_i2c_read_prom_word;
|
||||
st->read_adc_temp_and_pressure = ms5611_i2c_read_adc_temp_and_pressure;
|
||||
st->client = client;
|
||||
|
||||
return ms5611_probe(indio_dev, &client->dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ms5611_id[] = {
|
||||
{ "ms5611", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ms5611_id);
|
||||
|
||||
static struct i2c_driver ms5611_driver = {
|
||||
.driver = {
|
||||
.name = "ms5611",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.id_table = ms5611_id,
|
||||
.probe = ms5611_i2c_probe,
|
||||
};
|
||||
module_i2c_driver(ms5611_driver);
|
||||
|
||||
MODULE_AUTHOR("Tomasz Duszynski <[email protected]>");
|
||||
MODULE_DESCRIPTION("MS5611 i2c driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
127
drivers/iio/pressure/ms5611_spi.c
Normal file
127
drivers/iio/pressure/ms5611_spi.c
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* MS5611 pressure and temperature sensor driver (SPI bus)
|
||||
*
|
||||
* Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
|
||||
*
|
||||
* 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/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include "ms5611.h"
|
||||
|
||||
static int ms5611_spi_reset(struct device *dev)
|
||||
{
|
||||
u8 cmd = MS5611_RESET;
|
||||
struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev));
|
||||
|
||||
return spi_write_then_read(st->client, &cmd, 1, NULL, 0);
|
||||
}
|
||||
|
||||
static int ms5611_spi_read_prom_word(struct device *dev, int index, u16 *word)
|
||||
{
|
||||
int ret;
|
||||
struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev));
|
||||
|
||||
ret = spi_w8r16be(st->client, MS5611_READ_PROM_WORD + (index << 1));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*word = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ms5611_spi_read_adc(struct device *dev, s32 *val)
|
||||
{
|
||||
int ret;
|
||||
u8 buf[3] = { MS5611_READ_ADC };
|
||||
struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev));
|
||||
|
||||
ret = spi_write_then_read(st->client, buf, 1, buf, 3);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = (buf[0] << 16) | (buf[1] << 8) | buf[2];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ms5611_spi_read_adc_temp_and_pressure(struct device *dev,
|
||||
s32 *temp, s32 *pressure)
|
||||
{
|
||||
u8 cmd;
|
||||
int ret;
|
||||
struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev));
|
||||
|
||||
cmd = MS5611_START_TEMP_CONV;
|
||||
ret = spi_write_then_read(st->client, &cmd, 1, NULL, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
usleep_range(MS5611_CONV_TIME_MIN, MS5611_CONV_TIME_MAX);
|
||||
|
||||
ret = ms5611_spi_read_adc(dev, temp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
cmd = MS5611_START_PRESSURE_CONV;
|
||||
ret = spi_write_then_read(st->client, &cmd, 1, NULL, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
usleep_range(MS5611_CONV_TIME_MIN, MS5611_CONV_TIME_MAX);
|
||||
|
||||
return ms5611_spi_read_adc(dev, pressure);
|
||||
}
|
||||
|
||||
static int ms5611_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
int ret;
|
||||
struct ms5611_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
spi->mode = SPI_MODE_0;
|
||||
spi->max_speed_hz = 20000000;
|
||||
spi->bits_per_word = 8;
|
||||
ret = spi_setup(spi);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
st->reset = ms5611_spi_reset;
|
||||
st->read_prom_word = ms5611_spi_read_prom_word;
|
||||
st->read_adc_temp_and_pressure = ms5611_spi_read_adc_temp_and_pressure;
|
||||
st->client = spi;
|
||||
|
||||
return ms5611_probe(indio_dev, &spi->dev);
|
||||
}
|
||||
|
||||
static const struct spi_device_id ms5611_id[] = {
|
||||
{ "ms5611", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ms5611_id);
|
||||
|
||||
static struct spi_driver ms5611_driver = {
|
||||
.driver = {
|
||||
.name = "ms5611",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.id_table = ms5611_id,
|
||||
.probe = ms5611_spi_probe,
|
||||
};
|
||||
module_spi_driver(ms5611_driver);
|
||||
|
||||
MODULE_AUTHOR("Tomasz Duszynski <[email protected]>");
|
||||
MODULE_DESCRIPTION("MS5611 spi driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
@ -618,16 +618,12 @@ static int sx9500_gpio_probe(struct i2c_client *client,
|
|||
dev = &client->dev;
|
||||
|
||||
/* data ready gpio interrupt pin */
|
||||
gpio = devm_gpiod_get_index(dev, SX9500_GPIO_NAME, 0);
|
||||
gpio = devm_gpiod_get_index(dev, SX9500_GPIO_NAME, 0, GPIOD_IN);
|
||||
if (IS_ERR(gpio)) {
|
||||
dev_err(dev, "acpi gpio get index failed\n");
|
||||
return PTR_ERR(gpio);
|
||||
}
|
||||
|
||||
ret = gpiod_direction_input(gpio);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = gpiod_to_irq(gpio);
|
||||
|
||||
dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
* mlx90614.c - Support for Melexis MLX90614 contactless IR temperature sensor
|
||||
*
|
||||
* Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
|
||||
* Copyright (c) 2015 Essensium NV
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
|
|
@ -20,11 +21,35 @@
|
|||
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#define MLX90614_OP_RAM 0x00
|
||||
#define MLX90614_OP_RAM 0x00
|
||||
#define MLX90614_OP_EEPROM 0x20
|
||||
#define MLX90614_OP_SLEEP 0xff
|
||||
|
||||
/* RAM offsets with 16-bit data, MSB first */
|
||||
#define MLX90614_TA 0x06 /* ambient temperature */
|
||||
#define MLX90614_TOBJ1 0x07 /* object temperature */
|
||||
#define MLX90614_RAW1 (MLX90614_OP_RAM | 0x04) /* raw data IR channel 1 */
|
||||
#define MLX90614_RAW2 (MLX90614_OP_RAM | 0x05) /* raw data IR channel 2 */
|
||||
#define MLX90614_TA (MLX90614_OP_RAM | 0x06) /* ambient temperature */
|
||||
#define MLX90614_TOBJ1 (MLX90614_OP_RAM | 0x07) /* object 1 temperature */
|
||||
#define MLX90614_TOBJ2 (MLX90614_OP_RAM | 0x08) /* object 2 temperature */
|
||||
|
||||
/* EEPROM offsets with 16-bit data, MSB first */
|
||||
#define MLX90614_EMISSIVITY (MLX90614_OP_EEPROM | 0x04) /* emissivity correction coefficient */
|
||||
#define MLX90614_CONFIG (MLX90614_OP_EEPROM | 0x05) /* configuration register */
|
||||
|
||||
/* Control bits in configuration register */
|
||||
#define MLX90614_CONFIG_IIR_SHIFT 0 /* IIR coefficient */
|
||||
#define MLX90614_CONFIG_IIR_MASK (0x7 << MLX90614_CONFIG_IIR_SHIFT)
|
||||
#define MLX90614_CONFIG_DUAL_SHIFT 6 /* single (0) or dual (1) IR sensor */
|
||||
#define MLX90614_CONFIG_DUAL_MASK (1 << MLX90614_CONFIG_DUAL_SHIFT)
|
||||
#define MLX90614_CONFIG_FIR_SHIFT 8 /* FIR coefficient */
|
||||
#define MLX90614_CONFIG_FIR_MASK (0x7 << MLX90614_CONFIG_FIR_SHIFT)
|
||||
#define MLX90614_CONFIG_GAIN_SHIFT 11 /* gain */
|
||||
#define MLX90614_CONFIG_GAIN_MASK (0x7 << MLX90614_CONFIG_GAIN_SHIFT)
|
||||
|
||||
/* Timings (in ms) */
|
||||
#define MLX90614_TIMING_EEPROM 20 /* time for EEPROM write/erase to complete */
|
||||
#define MLX90614_TIMING_WAKEUP 34 /* time to hold SDA low for wake-up */
|
||||
#define MLX90614_TIMING_STARTUP 250 /* time before first data after wake-up */
|
||||
|
||||
struct mlx90614_data {
|
||||
struct i2c_client *client;
|
||||
|
|
@ -35,26 +60,34 @@ static int mlx90614_read_raw(struct iio_dev *indio_dev,
|
|||
int *val2, long mask)
|
||||
{
|
||||
struct mlx90614_data *data = iio_priv(indio_dev);
|
||||
u8 cmd;
|
||||
s32 ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW: /* 0.02K / LSB */
|
||||
switch (channel->channel2) {
|
||||
case IIO_MOD_TEMP_AMBIENT:
|
||||
ret = i2c_smbus_read_word_data(data->client,
|
||||
MLX90614_OP_RAM | MLX90614_TA);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
cmd = MLX90614_TA;
|
||||
break;
|
||||
case IIO_MOD_TEMP_OBJECT:
|
||||
ret = i2c_smbus_read_word_data(data->client,
|
||||
MLX90614_OP_RAM | MLX90614_TOBJ1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
switch (channel->channel) {
|
||||
case 0:
|
||||
cmd = MLX90614_TOBJ1;
|
||||
break;
|
||||
case 1:
|
||||
cmd = MLX90614_TOBJ2;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_read_word_data(data->client, cmd);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
|
|
@ -86,6 +119,16 @@ static const struct iio_chan_spec mlx90614_channels[] = {
|
|||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
},
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.indexed = 1,
|
||||
.modified = 1,
|
||||
.channel = 1,
|
||||
.channel2 = IIO_MOD_TEMP_OBJECT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_info mlx90614_info = {
|
||||
|
|
@ -93,11 +136,25 @@ static const struct iio_info mlx90614_info = {
|
|||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
/* Return 0 for single sensor, 1 for dual sensor, <0 on error. */
|
||||
static int mlx90614_probe_num_ir_sensors(struct i2c_client *client)
|
||||
{
|
||||
s32 ret;
|
||||
|
||||
ret = i2c_smbus_read_word_data(client, MLX90614_CONFIG);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return (ret & MLX90614_CONFIG_DUAL_MASK) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int mlx90614_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct mlx90614_data *data;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
|
||||
return -ENODEV;
|
||||
|
|
@ -115,8 +172,21 @@ static int mlx90614_probe(struct i2c_client *client,
|
|||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &mlx90614_info;
|
||||
|
||||
indio_dev->channels = mlx90614_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(mlx90614_channels);
|
||||
ret = mlx90614_probe_num_ir_sensors(client);
|
||||
switch (ret) {
|
||||
case 0:
|
||||
dev_dbg(&client->dev, "Found single sensor");
|
||||
indio_dev->channels = mlx90614_channels;
|
||||
indio_dev->num_channels = 2;
|
||||
break;
|
||||
case 1:
|
||||
dev_dbg(&client->dev, "Found dual sensor");
|
||||
indio_dev->channels = mlx90614_channels;
|
||||
indio_dev->num_channels = 3;
|
||||
break;
|
||||
default:
|
||||
return ret;
|
||||
}
|
||||
|
||||
return iio_device_register(indio_dev);
|
||||
}
|
||||
|
|
@ -146,5 +216,6 @@ static struct i2c_driver mlx90614_driver = {
|
|||
module_i2c_driver(mlx90614_driver);
|
||||
|
||||
MODULE_AUTHOR("Peter Meerwald <[email protected]>");
|
||||
MODULE_AUTHOR("Vianney le Clément de Saint-Marcq <[email protected]>");
|
||||
MODULE_DESCRIPTION("Melexis MLX90614 contactless IR temperature sensor driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
|||
Loading…
Reference in a new issue