Third round of new drivers, features and cleanups for IIO in the 3.14 cycle.
One new driver a spot of new functionality and a cleanup. * New driver for the Capella CM32181 ambient light sensor. This is the first driver directly from Kevin at Capella with more already in the pipeline. * Support for configurable predividers on the mxs lradc * Convert a field to a bitmap in mxs lradc instead of using an array of unsigned longs to store boolean values. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQIcBAABAgAGBQJSzaveAAoJEFSFNJnE9BaInqIQAKZeBFS64HkWCU+fPF62gRCT VLkfPvzOSIYctKuTQCVggAeJUt6lDVZT23r/Oy2GSenkcvL/xVy+GRSFM6ZZ7vhN cyOHKwq1sSZV+ZSQNKdHxRAEla41qy8tvCKe1XseYKERi8JJnhxwB4Hcb1gTvmqo LN9wh1zfV5XZTq7i/vcBVVM/SYrDxJmapbNW1wa3Ytx65VzCYsb3iIHDzxaZh33Y WtjLRnyCDavTY19gg4K4/c6+IPgLi+VM2oN7GDdKSaT4jGAq5eVzrqWQ+s+WjAGj 0HZ0dt6QDo21P0moO1RIhgE7uN3TpJeEHL1oB5SBV/0VpBcDcZDmaVz2VMQuL1Iv tmY0Gf4sz2z3VnbA7DfKWIUSpF8j2pnPPzUEC+wUrkP50QEwzbH5jsop8CzUa9PT Rm88DXKr7oUB3y8apC1HsadS1dnIr7wloZCj5gbQ70TnpJat625Y+mhKawRUUGms fSyY/C+O+5VrTw0+usCLB1EtMty2aDM/2pQcTiMdRvG58F9hIcpZTGybwViPQPta Lf1a82n5LF8HKrdq10mpCJnuB9VDoQqvdtPJDQ8QNFr6aGRih0HFliZ4OpGOD7mo bOj68VEI9u4Hjc7dUoZ6HLiA/v6mMUme01422dN5rRQmWjzeaEyQibAq3XZKnr4i gBnUKoGHFHil6KxOrYU3 =VaZB -----END PGP SIGNATURE----- Merge tag 'iio-for-3.14c' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next Jonathan writes: Third round of new drivers, features and cleanups for IIO in the 3.14 cycle. One new driver a spot of new functionality and a cleanup. * New driver for the Capella CM32181 ambient light sensor. This is the first driver directly from Kevin at Capella with more already in the pipeline. * Support for configurable predividers on the mxs lradc * Convert a field to a bitmap in mxs lradc instead of using an array of unsigned longs to store boolean values.
This commit is contained in:
commit
8a3cecf503
5 changed files with 611 additions and 3 deletions
|
@ -16,6 +16,7 @@ adt7461 +/-1C TDM Extended Temp Range I.C
|
|||
at,24c08 i2c serial eeprom (24cxx)
|
||||
atmel,24c02 i2c serial eeprom (24cxx)
|
||||
atmel,at97sc3204t i2c trusted platform module (TPM)
|
||||
capella,cm32181 CM32181: Ambient Light Sensor
|
||||
catalyst,24c32 i2c serial eeprom
|
||||
dallas,ds1307 64 x 8, Serial, I2C Real-Time Clock
|
||||
dallas,ds1338 I2C RTC with 56-Byte NV RAM
|
||||
|
|
|
@ -27,6 +27,17 @@ config APDS9300
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called apds9300.
|
||||
|
||||
config CM32181
|
||||
depends on I2C
|
||||
tristate "CM32181 driver"
|
||||
help
|
||||
Say Y here if you use cm32181.
|
||||
This option enables ambient light sensor using
|
||||
Capella cm32181 device driver.
|
||||
|
||||
To compile this driver as a module, choose M here:
|
||||
the module will be called cm32181.
|
||||
|
||||
config CM36651
|
||||
depends on I2C
|
||||
tristate "CM36651 driver"
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_ADJD_S311) += adjd_s311.o
|
||||
obj-$(CONFIG_APDS9300) += apds9300.o
|
||||
obj-$(CONFIG_CM32181) += cm32181.o
|
||||
obj-$(CONFIG_CM36651) += cm36651.o
|
||||
obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o
|
||||
obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o
|
||||
|
|
379
drivers/iio/light/cm32181.c
Normal file
379
drivers/iio/light/cm32181.c
Normal file
|
@ -0,0 +1,379 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Capella Microsystems Inc.
|
||||
* Author: Kevin Tsai <ktsai@capellamicro.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/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
/* Registers Address */
|
||||
#define CM32181_REG_ADDR_CMD 0x00
|
||||
#define CM32181_REG_ADDR_ALS 0x04
|
||||
#define CM32181_REG_ADDR_STATUS 0x06
|
||||
#define CM32181_REG_ADDR_ID 0x07
|
||||
|
||||
/* Number of Configurable Registers */
|
||||
#define CM32181_CONF_REG_NUM 0x01
|
||||
|
||||
/* CMD register */
|
||||
#define CM32181_CMD_ALS_ENABLE 0x00
|
||||
#define CM32181_CMD_ALS_DISABLE 0x01
|
||||
#define CM32181_CMD_ALS_INT_EN 0x02
|
||||
|
||||
#define CM32181_CMD_ALS_IT_SHIFT 6
|
||||
#define CM32181_CMD_ALS_IT_MASK (0x0F << CM32181_CMD_ALS_IT_SHIFT)
|
||||
#define CM32181_CMD_ALS_IT_DEFAULT (0x00 << CM32181_CMD_ALS_IT_SHIFT)
|
||||
|
||||
#define CM32181_CMD_ALS_SM_SHIFT 11
|
||||
#define CM32181_CMD_ALS_SM_MASK (0x03 << CM32181_CMD_ALS_SM_SHIFT)
|
||||
#define CM32181_CMD_ALS_SM_DEFAULT (0x01 << CM32181_CMD_ALS_SM_SHIFT)
|
||||
|
||||
#define CM32181_MLUX_PER_BIT 5 /* ALS_SM=01 IT=800ms */
|
||||
#define CM32181_MLUX_PER_BIT_BASE_IT 800000 /* Based on IT=800ms */
|
||||
#define CM32181_CALIBSCALE_DEFAULT 1000
|
||||
#define CM32181_CALIBSCALE_RESOLUTION 1000
|
||||
#define MLUX_PER_LUX 1000
|
||||
|
||||
static const u8 cm32181_reg[CM32181_CONF_REG_NUM] = {
|
||||
CM32181_REG_ADDR_CMD,
|
||||
};
|
||||
|
||||
static const int als_it_bits[] = {12, 8, 0, 1, 2, 3};
|
||||
static const int als_it_value[] = {25000, 50000, 100000, 200000, 400000,
|
||||
800000};
|
||||
|
||||
struct cm32181_chip {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock;
|
||||
u16 conf_regs[CM32181_CONF_REG_NUM];
|
||||
int calibscale;
|
||||
};
|
||||
|
||||
/**
|
||||
* cm32181_reg_init() - Initialize CM32181 registers
|
||||
* @cm32181: pointer of struct cm32181.
|
||||
*
|
||||
* Initialize CM32181 ambient light sensor register to default values.
|
||||
*
|
||||
* Return: 0 for success; otherwise for error code.
|
||||
*/
|
||||
static int cm32181_reg_init(struct cm32181_chip *cm32181)
|
||||
{
|
||||
struct i2c_client *client = cm32181->client;
|
||||
int i;
|
||||
s32 ret;
|
||||
|
||||
ret = i2c_smbus_read_word_data(client, CM32181_REG_ADDR_ID);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* check device ID */
|
||||
if ((ret & 0xFF) != 0x81)
|
||||
return -ENODEV;
|
||||
|
||||
/* Default Values */
|
||||
cm32181->conf_regs[CM32181_REG_ADDR_CMD] = CM32181_CMD_ALS_ENABLE |
|
||||
CM32181_CMD_ALS_IT_DEFAULT | CM32181_CMD_ALS_SM_DEFAULT;
|
||||
cm32181->calibscale = CM32181_CALIBSCALE_DEFAULT;
|
||||
|
||||
/* Initialize registers*/
|
||||
for (i = 0; i < CM32181_CONF_REG_NUM; i++) {
|
||||
ret = i2c_smbus_write_word_data(client, cm32181_reg[i],
|
||||
cm32181->conf_regs[i]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cm32181_read_als_it() - Get sensor integration time (ms)
|
||||
* @cm32181: pointer of struct cm32181
|
||||
* @val: pointer of int to load the als_it value.
|
||||
*
|
||||
* Report the current integartion time by millisecond.
|
||||
*
|
||||
* Return: IIO_VAL_INT for success, otherwise -EINVAL.
|
||||
*/
|
||||
static int cm32181_read_als_it(struct cm32181_chip *cm32181, int *val)
|
||||
{
|
||||
u16 als_it;
|
||||
int i;
|
||||
|
||||
als_it = cm32181->conf_regs[CM32181_REG_ADDR_CMD];
|
||||
als_it &= CM32181_CMD_ALS_IT_MASK;
|
||||
als_it >>= CM32181_CMD_ALS_IT_SHIFT;
|
||||
for (i = 0; i < ARRAY_SIZE(als_it_bits); i++) {
|
||||
if (als_it == als_it_bits[i]) {
|
||||
*val = als_it_value[i];
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* cm32181_write_als_it() - Write sensor integration time
|
||||
* @cm32181: pointer of struct cm32181.
|
||||
* @val: integration time by millisecond.
|
||||
*
|
||||
* Convert integration time (ms) to sensor value.
|
||||
*
|
||||
* Return: i2c_smbus_write_word_data command return value.
|
||||
*/
|
||||
static int cm32181_write_als_it(struct cm32181_chip *cm32181, int val)
|
||||
{
|
||||
struct i2c_client *client = cm32181->client;
|
||||
u16 als_it;
|
||||
int ret, i, n;
|
||||
|
||||
n = ARRAY_SIZE(als_it_value);
|
||||
for (i = 0; i < n; i++)
|
||||
if (val <= als_it_value[i])
|
||||
break;
|
||||
if (i >= n)
|
||||
i = n - 1;
|
||||
|
||||
als_it = als_it_bits[i];
|
||||
als_it <<= CM32181_CMD_ALS_IT_SHIFT;
|
||||
|
||||
mutex_lock(&cm32181->lock);
|
||||
cm32181->conf_regs[CM32181_REG_ADDR_CMD] &=
|
||||
~CM32181_CMD_ALS_IT_MASK;
|
||||
cm32181->conf_regs[CM32181_REG_ADDR_CMD] |=
|
||||
als_it;
|
||||
ret = i2c_smbus_write_word_data(client, CM32181_REG_ADDR_CMD,
|
||||
cm32181->conf_regs[CM32181_REG_ADDR_CMD]);
|
||||
mutex_unlock(&cm32181->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* cm32181_get_lux() - report current lux value
|
||||
* @cm32181: pointer of struct cm32181.
|
||||
*
|
||||
* Convert sensor raw data to lux. It depends on integration
|
||||
* time and claibscale variable.
|
||||
*
|
||||
* Return: Positive value is lux, otherwise is error code.
|
||||
*/
|
||||
static int cm32181_get_lux(struct cm32181_chip *cm32181)
|
||||
{
|
||||
struct i2c_client *client = cm32181->client;
|
||||
int ret;
|
||||
int als_it;
|
||||
unsigned long lux;
|
||||
|
||||
ret = cm32181_read_als_it(cm32181, &als_it);
|
||||
if (ret < 0)
|
||||
return -EINVAL;
|
||||
|
||||
lux = CM32181_MLUX_PER_BIT;
|
||||
lux *= CM32181_MLUX_PER_BIT_BASE_IT;
|
||||
lux /= als_it;
|
||||
|
||||
ret = i2c_smbus_read_word_data(client, CM32181_REG_ADDR_ALS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
lux *= ret;
|
||||
lux *= cm32181->calibscale;
|
||||
lux /= CM32181_CALIBSCALE_RESOLUTION;
|
||||
lux /= MLUX_PER_LUX;
|
||||
|
||||
if (lux > 0xFFFF)
|
||||
lux = 0xFFFF;
|
||||
|
||||
return lux;
|
||||
}
|
||||
|
||||
static int cm32181_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct cm32181_chip *cm32181 = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
ret = cm32181_get_lux(cm32181);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
*val = cm32181->calibscale;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
ret = cm32181_read_als_it(cm32181, val);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int cm32181_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct cm32181_chip *cm32181 = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
cm32181->calibscale = val;
|
||||
return val;
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
ret = cm32181_write_als_it(cm32181, val);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* cm32181_get_it_available() - Get available ALS IT value
|
||||
* @dev: pointer of struct device.
|
||||
* @attr: pointer of struct device_attribute.
|
||||
* @buf: pointer of return string buffer.
|
||||
*
|
||||
* Display the available integration time values by millisecond.
|
||||
*
|
||||
* Return: string length.
|
||||
*/
|
||||
static ssize_t cm32181_get_it_available(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int i, n, len;
|
||||
|
||||
n = ARRAY_SIZE(als_it_value);
|
||||
for (i = 0, len = 0; i < n; i++)
|
||||
len += sprintf(buf + len, "%d ", als_it_value[i]);
|
||||
return len + sprintf(buf + len, "\n");
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec cm32181_channels[] = {
|
||||
{
|
||||
.type = IIO_LIGHT,
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_PROCESSED) |
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE) |
|
||||
BIT(IIO_CHAN_INFO_INT_TIME),
|
||||
}
|
||||
};
|
||||
|
||||
static IIO_DEVICE_ATTR(in_illuminance_integration_time_available,
|
||||
S_IRUGO, cm32181_get_it_available, NULL, 0);
|
||||
|
||||
static struct attribute *cm32181_attributes[] = {
|
||||
&iio_dev_attr_in_illuminance_integration_time_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group cm32181_attribute_group = {
|
||||
.attrs = cm32181_attributes
|
||||
};
|
||||
|
||||
static const struct iio_info cm32181_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &cm32181_read_raw,
|
||||
.write_raw = &cm32181_write_raw,
|
||||
.attrs = &cm32181_attribute_group,
|
||||
};
|
||||
|
||||
static int cm32181_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct cm32181_chip *cm32181;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*cm32181));
|
||||
if (!indio_dev) {
|
||||
dev_err(&client->dev, "devm_iio_device_alloc failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
cm32181 = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
cm32181->client = client;
|
||||
|
||||
mutex_init(&cm32181->lock);
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->channels = cm32181_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(cm32181_channels);
|
||||
indio_dev->info = &cm32181_info;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = cm32181_reg_init(cm32181);
|
||||
if (ret) {
|
||||
dev_err(&client->dev,
|
||||
"%s: register init failed\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&client->dev,
|
||||
"%s: regist device failed\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cm32181_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id cm32181_id[] = {
|
||||
{ "cm32181", 0 },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, cm32181_id);
|
||||
|
||||
static const struct of_device_id cm32181_of_match[] = {
|
||||
{ .compatible = "capella,cm32181" },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct i2c_driver cm32181_driver = {
|
||||
.driver = {
|
||||
.name = "cm32181",
|
||||
.of_match_table = of_match_ptr(cm32181_of_match),
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.id_table = cm32181_id,
|
||||
.probe = cm32181_probe,
|
||||
.remove = cm32181_remove,
|
||||
};
|
||||
|
||||
module_i2c_driver(cm32181_driver);
|
||||
|
||||
MODULE_AUTHOR("Kevin Tsai <ktsai@capellamicro.com>");
|
||||
MODULE_DESCRIPTION("CM32181 ambient light sensor driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -38,6 +38,7 @@
|
|||
#include <linux/clk.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
|
@ -111,16 +112,59 @@ static const char * const mx28_lradc_irq_names[] = {
|
|||
struct mxs_lradc_of_config {
|
||||
const int irq_count;
|
||||
const char * const *irq_name;
|
||||
const uint32_t *vref_mv;
|
||||
};
|
||||
|
||||
#define VREF_MV_BASE 1850
|
||||
|
||||
static const uint32_t mx23_vref_mv[LRADC_MAX_TOTAL_CHANS] = {
|
||||
VREF_MV_BASE, /* CH0 */
|
||||
VREF_MV_BASE, /* CH1 */
|
||||
VREF_MV_BASE, /* CH2 */
|
||||
VREF_MV_BASE, /* CH3 */
|
||||
VREF_MV_BASE, /* CH4 */
|
||||
VREF_MV_BASE, /* CH5 */
|
||||
VREF_MV_BASE * 2, /* CH6 VDDIO */
|
||||
VREF_MV_BASE * 4, /* CH7 VBATT */
|
||||
VREF_MV_BASE, /* CH8 Temp sense 0 */
|
||||
VREF_MV_BASE, /* CH9 Temp sense 1 */
|
||||
VREF_MV_BASE, /* CH10 */
|
||||
VREF_MV_BASE, /* CH11 */
|
||||
VREF_MV_BASE, /* CH12 USB_DP */
|
||||
VREF_MV_BASE, /* CH13 USB_DN */
|
||||
VREF_MV_BASE, /* CH14 VBG */
|
||||
VREF_MV_BASE * 4, /* CH15 VDD5V */
|
||||
};
|
||||
|
||||
static const uint32_t mx28_vref_mv[LRADC_MAX_TOTAL_CHANS] = {
|
||||
VREF_MV_BASE, /* CH0 */
|
||||
VREF_MV_BASE, /* CH1 */
|
||||
VREF_MV_BASE, /* CH2 */
|
||||
VREF_MV_BASE, /* CH3 */
|
||||
VREF_MV_BASE, /* CH4 */
|
||||
VREF_MV_BASE, /* CH5 */
|
||||
VREF_MV_BASE, /* CH6 */
|
||||
VREF_MV_BASE * 4, /* CH7 VBATT */
|
||||
VREF_MV_BASE, /* CH8 Temp sense 0 */
|
||||
VREF_MV_BASE, /* CH9 Temp sense 1 */
|
||||
VREF_MV_BASE * 2, /* CH10 VDDIO */
|
||||
VREF_MV_BASE, /* CH11 VTH */
|
||||
VREF_MV_BASE * 2, /* CH12 VDDA */
|
||||
VREF_MV_BASE, /* CH13 VDDD */
|
||||
VREF_MV_BASE, /* CH14 VBG */
|
||||
VREF_MV_BASE * 4, /* CH15 VDD5V */
|
||||
};
|
||||
|
||||
static const struct mxs_lradc_of_config mxs_lradc_of_config[] = {
|
||||
[IMX23_LRADC] = {
|
||||
.irq_count = ARRAY_SIZE(mx23_lradc_irq_names),
|
||||
.irq_name = mx23_lradc_irq_names,
|
||||
.vref_mv = mx23_vref_mv,
|
||||
},
|
||||
[IMX28_LRADC] = {
|
||||
.irq_count = ARRAY_SIZE(mx28_lradc_irq_names),
|
||||
.irq_name = mx28_lradc_irq_names,
|
||||
.vref_mv = mx28_vref_mv,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -141,6 +185,16 @@ enum lradc_ts_plate {
|
|||
LRADC_SAMPLE_VALID,
|
||||
};
|
||||
|
||||
enum mxs_lradc_divbytwo {
|
||||
MXS_LRADC_DIV_DISABLED = 0,
|
||||
MXS_LRADC_DIV_ENABLED,
|
||||
};
|
||||
|
||||
struct mxs_lradc_scale {
|
||||
unsigned int integer;
|
||||
unsigned int nano;
|
||||
};
|
||||
|
||||
struct mxs_lradc {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
|
@ -155,6 +209,10 @@ struct mxs_lradc {
|
|||
|
||||
struct completion completion;
|
||||
|
||||
const uint32_t *vref_mv;
|
||||
struct mxs_lradc_scale scale_avail[LRADC_MAX_TOTAL_CHANS][2];
|
||||
unsigned long is_divided;
|
||||
|
||||
/*
|
||||
* Touchscreen LRADC channels receives a private slot in the CTRL4
|
||||
* register, the slot #7. Therefore only 7 slots instead of 8 in the
|
||||
|
@ -243,6 +301,7 @@ struct mxs_lradc {
|
|||
#define LRADC_CTRL1_LRADC_IRQ_OFFSET 0
|
||||
|
||||
#define LRADC_CTRL2 0x20
|
||||
#define LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET 24
|
||||
#define LRADC_CTRL2_TEMPSENSE_PWD (1 << 15)
|
||||
|
||||
#define LRADC_STATUS 0x40
|
||||
|
@ -836,6 +895,8 @@ static int mxs_lradc_read_raw(struct iio_dev *iio_dev,
|
|||
const struct iio_chan_spec *chan,
|
||||
int *val, int *val2, long m)
|
||||
{
|
||||
struct mxs_lradc *lradc = iio_priv(iio_dev);
|
||||
|
||||
/* Check for invalid channel */
|
||||
if (chan->channel > LRADC_MAX_TOTAL_CHANS)
|
||||
return -EINVAL;
|
||||
|
@ -857,7 +918,10 @@ static int mxs_lradc_read_raw(struct iio_dev *iio_dev,
|
|||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
*val = lradc->vref_mv[chan->channel];
|
||||
*val2 = chan->scan_type.realbits -
|
||||
test_bit(chan->channel, &lradc->is_divided);
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
if (chan->type == IIO_TEMP) {
|
||||
|
@ -880,9 +944,136 @@ static int mxs_lradc_read_raw(struct iio_dev *iio_dev,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int mxs_lradc_write_raw(struct iio_dev *iio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
int val, int val2, long m)
|
||||
{
|
||||
struct mxs_lradc *lradc = iio_priv(iio_dev);
|
||||
struct mxs_lradc_scale *scale_avail =
|
||||
lradc->scale_avail[chan->channel];
|
||||
int ret;
|
||||
|
||||
ret = mutex_trylock(&lradc->lock);
|
||||
if (!ret)
|
||||
return -EBUSY;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = -EINVAL;
|
||||
if (val == scale_avail[MXS_LRADC_DIV_DISABLED].integer &&
|
||||
val2 == scale_avail[MXS_LRADC_DIV_DISABLED].nano) {
|
||||
/* divider by two disabled */
|
||||
writel(1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET,
|
||||
lradc->base + LRADC_CTRL2 + STMP_OFFSET_REG_CLR);
|
||||
clear_bit(chan->channel, &lradc->is_divided);
|
||||
ret = 0;
|
||||
} else if (val == scale_avail[MXS_LRADC_DIV_ENABLED].integer &&
|
||||
val2 == scale_avail[MXS_LRADC_DIV_ENABLED].nano) {
|
||||
/* divider by two enabled */
|
||||
writel(1 << LRADC_CTRL2_DIVIDE_BY_TWO_OFFSET,
|
||||
lradc->base + LRADC_CTRL2 + STMP_OFFSET_REG_SET);
|
||||
set_bit(chan->channel, &lradc->is_divided);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&lradc->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mxs_lradc_write_raw_get_fmt(struct iio_dev *iio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
long m)
|
||||
{
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
}
|
||||
|
||||
static ssize_t mxs_lradc_show_scale_available_ch(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf,
|
||||
int ch)
|
||||
{
|
||||
struct iio_dev *iio = dev_to_iio_dev(dev);
|
||||
struct mxs_lradc *lradc = iio_priv(iio);
|
||||
int i, len = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(lradc->scale_avail[ch]); i++)
|
||||
len += sprintf(buf + len, "%d.%09u ",
|
||||
lradc->scale_avail[ch][i].integer,
|
||||
lradc->scale_avail[ch][i].nano);
|
||||
|
||||
len += sprintf(buf + len, "\n");
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t mxs_lradc_show_scale_available(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev_attr *iio_attr = to_iio_dev_attr(attr);
|
||||
|
||||
return mxs_lradc_show_scale_available_ch(dev, attr, buf,
|
||||
iio_attr->address);
|
||||
}
|
||||
|
||||
#define SHOW_SCALE_AVAILABLE_ATTR(ch) \
|
||||
static IIO_DEVICE_ATTR(in_voltage##ch##_scale_available, S_IRUGO, \
|
||||
mxs_lradc_show_scale_available, NULL, ch)
|
||||
|
||||
SHOW_SCALE_AVAILABLE_ATTR(0);
|
||||
SHOW_SCALE_AVAILABLE_ATTR(1);
|
||||
SHOW_SCALE_AVAILABLE_ATTR(2);
|
||||
SHOW_SCALE_AVAILABLE_ATTR(3);
|
||||
SHOW_SCALE_AVAILABLE_ATTR(4);
|
||||
SHOW_SCALE_AVAILABLE_ATTR(5);
|
||||
SHOW_SCALE_AVAILABLE_ATTR(6);
|
||||
SHOW_SCALE_AVAILABLE_ATTR(7);
|
||||
SHOW_SCALE_AVAILABLE_ATTR(8);
|
||||
SHOW_SCALE_AVAILABLE_ATTR(9);
|
||||
SHOW_SCALE_AVAILABLE_ATTR(10);
|
||||
SHOW_SCALE_AVAILABLE_ATTR(11);
|
||||
SHOW_SCALE_AVAILABLE_ATTR(12);
|
||||
SHOW_SCALE_AVAILABLE_ATTR(13);
|
||||
SHOW_SCALE_AVAILABLE_ATTR(14);
|
||||
SHOW_SCALE_AVAILABLE_ATTR(15);
|
||||
|
||||
static struct attribute *mxs_lradc_attributes[] = {
|
||||
&iio_dev_attr_in_voltage0_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage1_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage2_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage3_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage4_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage5_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage6_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage7_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage8_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage9_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage10_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage11_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage12_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage13_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage14_scale_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage15_scale_available.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group mxs_lradc_attribute_group = {
|
||||
.attrs = mxs_lradc_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info mxs_lradc_iio_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = mxs_lradc_read_raw,
|
||||
.write_raw = mxs_lradc_write_raw,
|
||||
.write_raw_get_fmt = mxs_lradc_write_raw_get_fmt,
|
||||
.attrs = &mxs_lradc_attribute_group,
|
||||
};
|
||||
|
||||
static int mxs_lradc_ts_open(struct input_dev *dev)
|
||||
|
@ -1189,8 +1380,10 @@ static const struct iio_buffer_setup_ops mxs_lradc_buffer_ops = {
|
|||
.type = (chan_type), \
|
||||
.indexed = 1, \
|
||||
.scan_index = (idx), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.channel = (idx), \
|
||||
.address = (idx), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = LRADC_RESOLUTION, \
|
||||
|
@ -1336,7 +1529,8 @@ static int mxs_lradc_probe(struct platform_device *pdev)
|
|||
struct iio_dev *iio;
|
||||
struct resource *iores;
|
||||
int ret = 0, touch_ret;
|
||||
int i;
|
||||
int i, s;
|
||||
unsigned int scale_uv;
|
||||
|
||||
/* Allocate the IIO device. */
|
||||
iio = devm_iio_device_alloc(dev, sizeof(*lradc));
|
||||
|
@ -1381,6 +1575,8 @@ static int mxs_lradc_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
lradc->vref_mv = of_cfg->vref_mv;
|
||||
|
||||
platform_set_drvdata(pdev, iio);
|
||||
|
||||
init_completion(&lradc->completion);
|
||||
|
@ -1404,6 +1600,26 @@ static int mxs_lradc_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
goto err_trig;
|
||||
|
||||
/* Populate available ADC input ranges */
|
||||
for (i = 0; i < LRADC_MAX_TOTAL_CHANS; i++) {
|
||||
for (s = 0; s < ARRAY_SIZE(lradc->scale_avail[i]); s++) {
|
||||
/*
|
||||
* [s=0] = optional divider by two disabled (default)
|
||||
* [s=1] = optional divider by two enabled
|
||||
*
|
||||
* The scale is calculated by doing:
|
||||
* Vref >> (realbits - s)
|
||||
* which multiplies by two on the second component
|
||||
* of the array.
|
||||
*/
|
||||
scale_uv = ((u64)lradc->vref_mv[i] * 100000000) >>
|
||||
(iio->channels[i].scan_type.realbits - s);
|
||||
lradc->scale_avail[i][s].nano =
|
||||
do_div(scale_uv, 100000000) * 10;
|
||||
lradc->scale_avail[i][s].integer = scale_uv;
|
||||
}
|
||||
}
|
||||
|
||||
/* Configure the hardware. */
|
||||
ret = mxs_lradc_hw_init(lradc);
|
||||
if (ret)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue