cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

cm3323.c (6449B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * CM3323 - Capella Color Light Sensor
      4 *
      5 * Copyright (c) 2015, Intel Corporation.
      6 *
      7 * IIO driver for CM3323 (7-bit I2C slave address 0x10)
      8 *
      9 * TODO: calibscale to correct the lens factor
     10 */
     11#include <linux/module.h>
     12#include <linux/init.h>
     13#include <linux/i2c.h>
     14#include <linux/mutex.h>
     15
     16#include <linux/iio/iio.h>
     17#include <linux/iio/sysfs.h>
     18
     19#define CM3323_DRV_NAME "cm3323"
     20
     21#define CM3323_CMD_CONF		0x00
     22#define CM3323_CMD_RED_DATA	0x08
     23#define CM3323_CMD_GREEN_DATA	0x09
     24#define CM3323_CMD_BLUE_DATA	0x0A
     25#define CM3323_CMD_CLEAR_DATA	0x0B
     26
     27#define CM3323_CONF_SD_BIT	BIT(0) /* sensor disable */
     28#define CM3323_CONF_AF_BIT	BIT(1) /* auto/manual force mode */
     29#define CM3323_CONF_IT_MASK	GENMASK(6, 4)
     30#define CM3323_CONF_IT_SHIFT	4
     31
     32#define CM3323_INT_TIME_AVAILABLE "0.04 0.08 0.16 0.32 0.64 1.28"
     33
     34static const struct {
     35	int val;
     36	int val2;
     37} cm3323_int_time[] = {
     38	{0, 40000},  /* 40 ms */
     39	{0, 80000},  /* 80 ms */
     40	{0, 160000}, /* 160 ms */
     41	{0, 320000}, /* 320 ms */
     42	{0, 640000}, /* 640 ms */
     43	{1, 280000}, /* 1280 ms */
     44};
     45
     46struct cm3323_data {
     47	struct i2c_client *client;
     48	u16 reg_conf;
     49	struct mutex mutex;
     50};
     51
     52#define CM3323_COLOR_CHANNEL(_color, _addr) { \
     53	.type = IIO_INTENSITY, \
     54	.modified = 1, \
     55	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
     56	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), \
     57	.channel2 = IIO_MOD_LIGHT_##_color, \
     58	.address = _addr, \
     59}
     60
     61static const struct iio_chan_spec cm3323_channels[] = {
     62	CM3323_COLOR_CHANNEL(RED, CM3323_CMD_RED_DATA),
     63	CM3323_COLOR_CHANNEL(GREEN, CM3323_CMD_GREEN_DATA),
     64	CM3323_COLOR_CHANNEL(BLUE, CM3323_CMD_BLUE_DATA),
     65	CM3323_COLOR_CHANNEL(CLEAR, CM3323_CMD_CLEAR_DATA),
     66};
     67
     68static IIO_CONST_ATTR_INT_TIME_AVAIL(CM3323_INT_TIME_AVAILABLE);
     69
     70static struct attribute *cm3323_attributes[] = {
     71	&iio_const_attr_integration_time_available.dev_attr.attr,
     72	NULL
     73};
     74
     75static const struct attribute_group cm3323_attribute_group = {
     76	.attrs = cm3323_attributes,
     77};
     78
     79static int cm3323_init(struct iio_dev *indio_dev)
     80{
     81	int ret;
     82	struct cm3323_data *data = iio_priv(indio_dev);
     83
     84	ret = i2c_smbus_read_word_data(data->client, CM3323_CMD_CONF);
     85	if (ret < 0) {
     86		dev_err(&data->client->dev, "Error reading reg_conf\n");
     87		return ret;
     88	}
     89
     90	/* enable sensor and set auto force mode */
     91	ret &= ~(CM3323_CONF_SD_BIT | CM3323_CONF_AF_BIT);
     92
     93	ret = i2c_smbus_write_word_data(data->client, CM3323_CMD_CONF, ret);
     94	if (ret < 0) {
     95		dev_err(&data->client->dev, "Error writing reg_conf\n");
     96		return ret;
     97	}
     98
     99	data->reg_conf = ret;
    100
    101	return 0;
    102}
    103
    104static void cm3323_disable(void *data)
    105{
    106	int ret;
    107	struct iio_dev *indio_dev = data;
    108	struct cm3323_data *cm_data = iio_priv(indio_dev);
    109
    110	ret = i2c_smbus_write_word_data(cm_data->client, CM3323_CMD_CONF,
    111					CM3323_CONF_SD_BIT);
    112	if (ret < 0)
    113		dev_err(&cm_data->client->dev, "Error writing reg_conf\n");
    114}
    115
    116static int cm3323_set_it_bits(struct cm3323_data *data, int val, int val2)
    117{
    118	int i, ret;
    119	u16 reg_conf;
    120
    121	for (i = 0; i < ARRAY_SIZE(cm3323_int_time); i++) {
    122		if (val == cm3323_int_time[i].val &&
    123		    val2 == cm3323_int_time[i].val2) {
    124			reg_conf = data->reg_conf & ~CM3323_CONF_IT_MASK;
    125			reg_conf |= i << CM3323_CONF_IT_SHIFT;
    126
    127			ret = i2c_smbus_write_word_data(data->client,
    128							CM3323_CMD_CONF,
    129							reg_conf);
    130			if (ret < 0)
    131				return ret;
    132
    133			data->reg_conf = reg_conf;
    134
    135			return 0;
    136		}
    137	}
    138
    139	return -EINVAL;
    140}
    141
    142static int cm3323_get_it_bits(struct cm3323_data *data)
    143{
    144	int bits;
    145
    146	bits = (data->reg_conf & CM3323_CONF_IT_MASK) >>
    147		CM3323_CONF_IT_SHIFT;
    148
    149	if (bits >= ARRAY_SIZE(cm3323_int_time))
    150		return -EINVAL;
    151
    152	return bits;
    153}
    154
    155static int cm3323_read_raw(struct iio_dev *indio_dev,
    156			   struct iio_chan_spec const *chan, int *val,
    157			   int *val2, long mask)
    158{
    159	int ret;
    160	struct cm3323_data *data = iio_priv(indio_dev);
    161
    162	switch (mask) {
    163	case IIO_CHAN_INFO_RAW:
    164		mutex_lock(&data->mutex);
    165		ret = i2c_smbus_read_word_data(data->client, chan->address);
    166		if (ret < 0) {
    167			mutex_unlock(&data->mutex);
    168			return ret;
    169		}
    170		*val = ret;
    171		mutex_unlock(&data->mutex);
    172
    173		return IIO_VAL_INT;
    174	case IIO_CHAN_INFO_INT_TIME:
    175		mutex_lock(&data->mutex);
    176		ret = cm3323_get_it_bits(data);
    177		if (ret < 0) {
    178			mutex_unlock(&data->mutex);
    179			return ret;
    180		}
    181
    182		*val = cm3323_int_time[ret].val;
    183		*val2 = cm3323_int_time[ret].val2;
    184		mutex_unlock(&data->mutex);
    185
    186		return IIO_VAL_INT_PLUS_MICRO;
    187	default:
    188		return -EINVAL;
    189	}
    190}
    191
    192static int cm3323_write_raw(struct iio_dev *indio_dev,
    193			    struct iio_chan_spec const *chan, int val,
    194			    int val2, long mask)
    195{
    196	struct cm3323_data *data = iio_priv(indio_dev);
    197	int ret;
    198
    199	switch (mask) {
    200	case IIO_CHAN_INFO_INT_TIME:
    201		mutex_lock(&data->mutex);
    202		ret = cm3323_set_it_bits(data, val, val2);
    203		mutex_unlock(&data->mutex);
    204
    205		return ret;
    206	default:
    207		return -EINVAL;
    208	}
    209}
    210
    211static const struct iio_info cm3323_info = {
    212	.read_raw	= cm3323_read_raw,
    213	.write_raw	= cm3323_write_raw,
    214	.attrs		= &cm3323_attribute_group,
    215};
    216
    217static int cm3323_probe(struct i2c_client *client,
    218			const struct i2c_device_id *id)
    219{
    220	struct cm3323_data *data;
    221	struct iio_dev *indio_dev;
    222	int ret;
    223
    224	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
    225	if (!indio_dev)
    226		return -ENOMEM;
    227
    228	data = iio_priv(indio_dev);
    229	i2c_set_clientdata(client, indio_dev);
    230	data->client = client;
    231
    232	mutex_init(&data->mutex);
    233
    234	indio_dev->info = &cm3323_info;
    235	indio_dev->name = CM3323_DRV_NAME;
    236	indio_dev->channels = cm3323_channels;
    237	indio_dev->num_channels = ARRAY_SIZE(cm3323_channels);
    238	indio_dev->modes = INDIO_DIRECT_MODE;
    239
    240	ret = cm3323_init(indio_dev);
    241	if (ret < 0) {
    242		dev_err(&client->dev, "cm3323 chip init failed\n");
    243		return ret;
    244	}
    245
    246	ret = devm_add_action_or_reset(&client->dev, cm3323_disable, indio_dev);
    247	if (ret < 0)
    248		return ret;
    249
    250	return devm_iio_device_register(&client->dev, indio_dev);
    251}
    252
    253static const struct i2c_device_id cm3323_id[] = {
    254	{"cm3323", 0},
    255	{}
    256};
    257MODULE_DEVICE_TABLE(i2c, cm3323_id);
    258
    259static const struct of_device_id cm3323_of_match[] = {
    260	{ .compatible = "capella,cm3323", },
    261	{ /* sentinel */ }
    262};
    263MODULE_DEVICE_TABLE(of, cm3323_of_match);
    264
    265static struct i2c_driver cm3323_driver = {
    266	.driver = {
    267		.name = CM3323_DRV_NAME,
    268		.of_match_table = cm3323_of_match,
    269	},
    270	.probe		= cm3323_probe,
    271	.id_table	= cm3323_id,
    272};
    273
    274module_i2c_driver(cm3323_driver);
    275
    276MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
    277MODULE_DESCRIPTION("Capella CM3323 Color Light Sensor driver");
    278MODULE_LICENSE("GPL v2");