3ffdea3fec
A number of new drivers and some new functionality + a lot of cleanups all over IIO. New Core Elements 1) New INT_TIME info_mask element for integration time, which may have different effects on measurement noise and similar, than an amplifier and hence is different from existing SCALE. Already existed in some drivers as a custom attribute. 2) Introduce a iio_push_buffers_with_timestamp helper to cover the common case of filling the last 64 bits of data to be passed to the buffer with a timestamp. Applied to lots of drivers. Cuts down on repeated code and moves a slightly fiddly bit of logic into a single location. 3) Introduce info_mask_[shared_by_dir/shared_by_all] elements to allow support of elements such as sampling_frequency which is typically shared by all input channels on a device. This reduces code and makes these controls available from in kernel consumers of IIO devices. New drivers 1) MCP3422/3/4 ADC 2) TSL4531 ambient light sensor 3) TCS3472/5 color light sensor 4) GP2AP020A00F ambient light / proximity sensor 5) LPS001WP support added to ST pressure sensor driver. New driver functionality 1) ti_am335x_adc Add buffered sampling support. This device has a hardware fifo that is fed directly into an IIO kfifo buffer based on a watershed interrupt. Note this will act as an example of how to handle this increasingly common type of device. The only previous example - sca3000 - take a less than optimal approach which is largely why it is still in staging. A couple of little cleanups for that new functionality followed later. Core cleanups: 1) MAINTAINERS - Sachin actually brought my email address up to date because I said I'd do it and never got around to it :) 2) Assign buffer list elements as single element lists to simplify the iio_buffer_is_active logic. 3) wake_up_interruptible_poll instead of wake_up_interruptible to only wake up threads waiting for poll notifications. 4) Add O_CLOEXEC flag to anon_inode_get_fd call for IIO event interface. 5) Change iio_push_to_buffers to take a void * pointer so as to avoid some annoying and unnecessary type casts. 6) iio_compute_scan_bytes incorrectly took a long rather than unsigned long. 7) Various minor tidy ups. Driver cleanups (in no particular order) 1) Another set of devm_ allocations patches from Sachin Kamat. 2) tsl2x7x - 0 to NULL cleanup. 3) hmc5843 - fix missing > in MODULE_AUTHOR 4) Set of strict_strto* to kstrto* conversions. 5) mxs-lradc - fix ordering of resource removal to match creation 6) mxs-lradc - add MODULE_ALIAS 7) adc7606 - drop a work pending test duplicated in core functions. 8) hmc5843 - devm_ allocation patch 9) Series of redundant breaks removed. 10) ad2s1200 - pr_err -> dev_err 11) adjd_s311 - use INT_TIME 12) ST sensors - large set of cleanups from Lee Jones and removed restriction to using only triggers provided by the st_sensors themselves from Dennis Ciocca. 13) dummy and tmp006 provide sampling_frequency via info_mask_shared_by_all. 14) tcs3472 - fix incorrect buffer size and wrong device pointer used in suspend / resume functions. 15) max1363 - use defaults for buffer setup ops as provided by the triggered buffer helpers as they are the same as were specified in max1363 driver. 16) Trivial tidy ups in a number of other drivers. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.21 (GNU/Linux) iQIcBAABAgAGBQJSP0HUAAoJEFSFNJnE9BaIwiQQAKuJaoPrdMezm1TDaqgrzQWQ U95mSJ19xPYVSQVNHFLFidcajhADRMFhMUGOJF64VZObEdOtFWI0UkrJjFhYtJTt n1B6qAqPjatmruj434+n5PW32XtareOPThso5EDCAW0X+CNgSOgda+TVj+9g1Ilg Onltb3wugMcs27FakZpKv1YuGyKAKE6uT/33qr++cuynR89JZOlp0QmLgIXobVRR WdjuiH8OXFA4LsP7dWQhoSejs6+JPMn992qkACUc5fztQfFfCk0eJsgQIsOXkz1e U6MFvab0LtdPKDRyzT1kIpK/Jxf1OVNiOYaQNIGuNMipa+5WRz2lF1sZyERQTJWR HOZehkikBdL73WaaKwyaLTsYyDMbYM9ZkpLrBEFRr7ocZpg/0LA84BWYYDWu1Nok 9Ib9xNAxcAgFwQMJpiz9J3ap/IzV2qJT9rv78q1chVwhNhVDs2CbwcuZKAB4UvWs Oz7C0Xx5DA/K7DlpJMLaVB1+BRJ3C1I9Jbr84mnu0clgOqFE+nrdKZcUTrOTFXdy 2yTp7Bkc2JiRtOYhI40UL79N08KCGNTUfigmUDQseF2dsaNlz5rTOiMifYQCRw9+ C1kxY00emzlGTvfUDdPwkiQTtz8tWf9Ahvjx/ufGfed68KWDMs1VuGNcqEzgqKNI SMP0VTEXbCiLeWYMqGep =mMgm -----END PGP SIGNATURE----- Merge tag 'iio-for-3.13a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next Jonathan writes: First round of new drivers, functionality and cleanups for IIO in the 3.13 cycle A number of new drivers and some new functionality + a lot of cleanups all over IIO. New Core Elements 1) New INT_TIME info_mask element for integration time, which may have different effects on measurement noise and similar, than an amplifier and hence is different from existing SCALE. Already existed in some drivers as a custom attribute. 2) Introduce a iio_push_buffers_with_timestamp helper to cover the common case of filling the last 64 bits of data to be passed to the buffer with a timestamp. Applied to lots of drivers. Cuts down on repeated code and moves a slightly fiddly bit of logic into a single location. 3) Introduce info_mask_[shared_by_dir/shared_by_all] elements to allow support of elements such as sampling_frequency which is typically shared by all input channels on a device. This reduces code and makes these controls available from in kernel consumers of IIO devices. New drivers 1) MCP3422/3/4 ADC 2) TSL4531 ambient light sensor 3) TCS3472/5 color light sensor 4) GP2AP020A00F ambient light / proximity sensor 5) LPS001WP support added to ST pressure sensor driver. New driver functionality 1) ti_am335x_adc Add buffered sampling support. This device has a hardware fifo that is fed directly into an IIO kfifo buffer based on a watershed interrupt. Note this will act as an example of how to handle this increasingly common type of device. The only previous example - sca3000 - take a less than optimal approach which is largely why it is still in staging. A couple of little cleanups for that new functionality followed later. Core cleanups: 1) MAINTAINERS - Sachin actually brought my email address up to date because I said I'd do it and never got around to it :) 2) Assign buffer list elements as single element lists to simplify the iio_buffer_is_active logic. 3) wake_up_interruptible_poll instead of wake_up_interruptible to only wake up threads waiting for poll notifications. 4) Add O_CLOEXEC flag to anon_inode_get_fd call for IIO event interface. 5) Change iio_push_to_buffers to take a void * pointer so as to avoid some annoying and unnecessary type casts. 6) iio_compute_scan_bytes incorrectly took a long rather than unsigned long. 7) Various minor tidy ups. Driver cleanups (in no particular order) 1) Another set of devm_ allocations patches from Sachin Kamat. 2) tsl2x7x - 0 to NULL cleanup. 3) hmc5843 - fix missing > in MODULE_AUTHOR 4) Set of strict_strto* to kstrto* conversions. 5) mxs-lradc - fix ordering of resource removal to match creation 6) mxs-lradc - add MODULE_ALIAS 7) adc7606 - drop a work pending test duplicated in core functions. 8) hmc5843 - devm_ allocation patch 9) Series of redundant breaks removed. 10) ad2s1200 - pr_err -> dev_err 11) adjd_s311 - use INT_TIME 12) ST sensors - large set of cleanups from Lee Jones and removed restriction to using only triggers provided by the st_sensors themselves from Dennis Ciocca. 13) dummy and tmp006 provide sampling_frequency via info_mask_shared_by_all. 14) tcs3472 - fix incorrect buffer size and wrong device pointer used in suspend / resume functions. 15) max1363 - use defaults for buffer setup ops as provided by the triggered buffer helpers as they are the same as were specified in max1363 driver. 16) Trivial tidy ups in a number of other drivers.
320 lines
7.8 KiB
C
320 lines
7.8 KiB
C
/*
|
|
* adjd_s311.c - Support for ADJD-S311-CR999 digital color sensor
|
|
*
|
|
* Copyright (C) 2012 Peter Meerwald <pmeerw@pmeerw.net>
|
|
*
|
|
* 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.
|
|
*
|
|
* driver for ADJD-S311-CR999 digital color sensor (10-bit channels for
|
|
* red, green, blue, clear); 7-bit I2C slave address 0x74
|
|
*
|
|
* limitations: no calibration, no offset mode, no sleep mode
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/bitmap.h>
|
|
#include <linux/err.h>
|
|
#include <linux/irq.h>
|
|
|
|
#include <linux/iio/iio.h>
|
|
#include <linux/iio/sysfs.h>
|
|
#include <linux/iio/trigger_consumer.h>
|
|
#include <linux/iio/buffer.h>
|
|
#include <linux/iio/triggered_buffer.h>
|
|
|
|
#define ADJD_S311_DRV_NAME "adjd_s311"
|
|
|
|
#define ADJD_S311_CTRL 0x00
|
|
#define ADJD_S311_CONFIG 0x01
|
|
#define ADJD_S311_CAP_RED 0x06
|
|
#define ADJD_S311_CAP_GREEN 0x07
|
|
#define ADJD_S311_CAP_BLUE 0x08
|
|
#define ADJD_S311_CAP_CLEAR 0x09
|
|
#define ADJD_S311_INT_RED 0x0a
|
|
#define ADJD_S311_INT_GREEN 0x0c
|
|
#define ADJD_S311_INT_BLUE 0x0e
|
|
#define ADJD_S311_INT_CLEAR 0x10
|
|
#define ADJD_S311_DATA_RED 0x40
|
|
#define ADJD_S311_DATA_GREEN 0x42
|
|
#define ADJD_S311_DATA_BLUE 0x44
|
|
#define ADJD_S311_DATA_CLEAR 0x46
|
|
#define ADJD_S311_OFFSET_RED 0x48
|
|
#define ADJD_S311_OFFSET_GREEN 0x49
|
|
#define ADJD_S311_OFFSET_BLUE 0x4a
|
|
#define ADJD_S311_OFFSET_CLEAR 0x4b
|
|
|
|
#define ADJD_S311_CTRL_GOFS 0x02
|
|
#define ADJD_S311_CTRL_GSSR 0x01
|
|
#define ADJD_S311_CAP_MASK 0x0f
|
|
#define ADJD_S311_INT_MASK 0x0fff
|
|
#define ADJD_S311_DATA_MASK 0x03ff
|
|
|
|
struct adjd_s311_data {
|
|
struct i2c_client *client;
|
|
u16 *buffer;
|
|
};
|
|
|
|
enum adjd_s311_channel_idx {
|
|
IDX_RED, IDX_GREEN, IDX_BLUE, IDX_CLEAR
|
|
};
|
|
|
|
#define ADJD_S311_DATA_REG(chan) (ADJD_S311_DATA_RED + (chan) * 2)
|
|
#define ADJD_S311_INT_REG(chan) (ADJD_S311_INT_RED + (chan) * 2)
|
|
#define ADJD_S311_CAP_REG(chan) (ADJD_S311_CAP_RED + (chan))
|
|
|
|
static int adjd_s311_req_data(struct iio_dev *indio_dev)
|
|
{
|
|
struct adjd_s311_data *data = iio_priv(indio_dev);
|
|
int tries = 10;
|
|
|
|
int ret = i2c_smbus_write_byte_data(data->client, ADJD_S311_CTRL,
|
|
ADJD_S311_CTRL_GSSR);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
while (tries--) {
|
|
ret = i2c_smbus_read_byte_data(data->client, ADJD_S311_CTRL);
|
|
if (ret < 0)
|
|
return ret;
|
|
if (!(ret & ADJD_S311_CTRL_GSSR))
|
|
break;
|
|
msleep(20);
|
|
}
|
|
|
|
if (tries < 0) {
|
|
dev_err(&data->client->dev,
|
|
"adjd_s311_req_data() failed, data not ready\n");
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int adjd_s311_read_data(struct iio_dev *indio_dev, u8 reg, int *val)
|
|
{
|
|
struct adjd_s311_data *data = iio_priv(indio_dev);
|
|
|
|
int ret = adjd_s311_req_data(indio_dev);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = i2c_smbus_read_word_data(data->client, reg);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
*val = ret & ADJD_S311_DATA_MASK;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static irqreturn_t adjd_s311_trigger_handler(int irq, void *p)
|
|
{
|
|
struct iio_poll_func *pf = p;
|
|
struct iio_dev *indio_dev = pf->indio_dev;
|
|
struct adjd_s311_data *data = iio_priv(indio_dev);
|
|
s64 time_ns = iio_get_time_ns();
|
|
int len = 0;
|
|
int i, j = 0;
|
|
|
|
int ret = adjd_s311_req_data(indio_dev);
|
|
if (ret < 0)
|
|
goto done;
|
|
|
|
for_each_set_bit(i, indio_dev->active_scan_mask,
|
|
indio_dev->masklength) {
|
|
ret = i2c_smbus_read_word_data(data->client,
|
|
ADJD_S311_DATA_REG(i));
|
|
if (ret < 0)
|
|
goto done;
|
|
|
|
data->buffer[j++] = ret & ADJD_S311_DATA_MASK;
|
|
len += 2;
|
|
}
|
|
|
|
iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, time_ns);
|
|
|
|
done:
|
|
iio_trigger_notify_done(indio_dev->trig);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
#define ADJD_S311_CHANNEL(_color, _scan_idx) { \
|
|
.type = IIO_INTENSITY, \
|
|
.modified = 1, \
|
|
.address = (IDX_##_color), \
|
|
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
|
BIT(IIO_CHAN_INFO_HARDWAREGAIN) | \
|
|
BIT(IIO_CHAN_INFO_INT_TIME), \
|
|
.channel2 = (IIO_MOD_LIGHT_##_color), \
|
|
.scan_index = (_scan_idx), \
|
|
.scan_type = IIO_ST('u', 10, 16, 0), \
|
|
}
|
|
|
|
static const struct iio_chan_spec adjd_s311_channels[] = {
|
|
ADJD_S311_CHANNEL(RED, 0),
|
|
ADJD_S311_CHANNEL(GREEN, 1),
|
|
ADJD_S311_CHANNEL(BLUE, 2),
|
|
ADJD_S311_CHANNEL(CLEAR, 3),
|
|
IIO_CHAN_SOFT_TIMESTAMP(4),
|
|
};
|
|
|
|
static int adjd_s311_read_raw(struct iio_dev *indio_dev,
|
|
struct iio_chan_spec const *chan,
|
|
int *val, int *val2, long mask)
|
|
{
|
|
struct adjd_s311_data *data = iio_priv(indio_dev);
|
|
int ret;
|
|
|
|
switch (mask) {
|
|
case IIO_CHAN_INFO_RAW:
|
|
ret = adjd_s311_read_data(indio_dev,
|
|
ADJD_S311_DATA_REG(chan->address), val);
|
|
if (ret < 0)
|
|
return ret;
|
|
return IIO_VAL_INT;
|
|
case IIO_CHAN_INFO_HARDWAREGAIN:
|
|
ret = i2c_smbus_read_byte_data(data->client,
|
|
ADJD_S311_CAP_REG(chan->address));
|
|
if (ret < 0)
|
|
return ret;
|
|
*val = ret & ADJD_S311_CAP_MASK;
|
|
return IIO_VAL_INT;
|
|
case IIO_CHAN_INFO_INT_TIME:
|
|
ret = i2c_smbus_read_word_data(data->client,
|
|
ADJD_S311_INT_REG(chan->address));
|
|
if (ret < 0)
|
|
return ret;
|
|
*val = 0;
|
|
/*
|
|
* not documented, based on measurement:
|
|
* 4095 LSBs correspond to roughly 4 ms
|
|
*/
|
|
*val2 = ret & ADJD_S311_INT_MASK;
|
|
return IIO_VAL_INT_PLUS_MICRO;
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int adjd_s311_write_raw(struct iio_dev *indio_dev,
|
|
struct iio_chan_spec const *chan,
|
|
int val, int val2, long mask)
|
|
{
|
|
struct adjd_s311_data *data = iio_priv(indio_dev);
|
|
|
|
switch (mask) {
|
|
case IIO_CHAN_INFO_HARDWAREGAIN:
|
|
if (val < 0 || val > ADJD_S311_CAP_MASK)
|
|
return -EINVAL;
|
|
|
|
return i2c_smbus_write_byte_data(data->client,
|
|
ADJD_S311_CAP_REG(chan->address), val);
|
|
case IIO_CHAN_INFO_INT_TIME:
|
|
if (val != 0 || val2 < 0 || val2 > ADJD_S311_INT_MASK)
|
|
return -EINVAL;
|
|
|
|
return i2c_smbus_write_word_data(data->client,
|
|
ADJD_S311_INT_REG(chan->address), val2);
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int adjd_s311_update_scan_mode(struct iio_dev *indio_dev,
|
|
const unsigned long *scan_mask)
|
|
{
|
|
struct adjd_s311_data *data = iio_priv(indio_dev);
|
|
|
|
kfree(data->buffer);
|
|
data->buffer = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
|
|
if (data->buffer == NULL)
|
|
return -ENOMEM;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct iio_info adjd_s311_info = {
|
|
.read_raw = adjd_s311_read_raw,
|
|
.write_raw = adjd_s311_write_raw,
|
|
.update_scan_mode = adjd_s311_update_scan_mode,
|
|
.driver_module = THIS_MODULE,
|
|
};
|
|
|
|
static int adjd_s311_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id)
|
|
{
|
|
struct adjd_s311_data *data;
|
|
struct iio_dev *indio_dev;
|
|
int err;
|
|
|
|
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
|
if (indio_dev == NULL)
|
|
return -ENOMEM;
|
|
|
|
data = iio_priv(indio_dev);
|
|
i2c_set_clientdata(client, indio_dev);
|
|
data->client = client;
|
|
|
|
indio_dev->dev.parent = &client->dev;
|
|
indio_dev->info = &adjd_s311_info;
|
|
indio_dev->name = ADJD_S311_DRV_NAME;
|
|
indio_dev->channels = adjd_s311_channels;
|
|
indio_dev->num_channels = ARRAY_SIZE(adjd_s311_channels);
|
|
indio_dev->modes = INDIO_DIRECT_MODE;
|
|
|
|
err = iio_triggered_buffer_setup(indio_dev, NULL,
|
|
adjd_s311_trigger_handler, NULL);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
err = iio_device_register(indio_dev);
|
|
if (err)
|
|
goto exit_unreg_buffer;
|
|
|
|
dev_info(&client->dev, "ADJD-S311 color sensor registered\n");
|
|
|
|
return 0;
|
|
|
|
exit_unreg_buffer:
|
|
iio_triggered_buffer_cleanup(indio_dev);
|
|
return err;
|
|
}
|
|
|
|
static int adjd_s311_remove(struct i2c_client *client)
|
|
{
|
|
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
|
struct adjd_s311_data *data = iio_priv(indio_dev);
|
|
|
|
iio_device_unregister(indio_dev);
|
|
iio_triggered_buffer_cleanup(indio_dev);
|
|
kfree(data->buffer);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct i2c_device_id adjd_s311_id[] = {
|
|
{ "adjd_s311", 0 },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(i2c, adjd_s311_id);
|
|
|
|
static struct i2c_driver adjd_s311_driver = {
|
|
.driver = {
|
|
.name = ADJD_S311_DRV_NAME,
|
|
},
|
|
.probe = adjd_s311_probe,
|
|
.remove = adjd_s311_remove,
|
|
.id_table = adjd_s311_id,
|
|
};
|
|
module_i2c_driver(adjd_s311_driver);
|
|
|
|
MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
|
|
MODULE_DESCRIPTION("ADJD-S311 color sensor");
|
|
MODULE_LICENSE("GPL");
|