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

isl29003.c (11159B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *  isl29003.c - Linux kernel module for
      4 * 	Intersil ISL29003 ambient light sensor
      5 *
      6 *  See file:Documentation/misc-devices/isl29003.rst
      7 *
      8 *  Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
      9 *
     10 *  Based on code written by
     11 *  	Rodolfo Giometti <giometti@linux.it>
     12 *  	Eurotech S.p.A. <info@eurotech.it>
     13 */
     14
     15#include <linux/module.h>
     16#include <linux/slab.h>
     17#include <linux/i2c.h>
     18#include <linux/mutex.h>
     19#include <linux/delay.h>
     20
     21#define ISL29003_DRV_NAME	"isl29003"
     22#define DRIVER_VERSION		"1.0"
     23
     24#define ISL29003_REG_COMMAND		0x00
     25#define ISL29003_ADC_ENABLED		(1 << 7)
     26#define ISL29003_ADC_PD			(1 << 6)
     27#define ISL29003_TIMING_INT		(1 << 5)
     28#define ISL29003_MODE_SHIFT		(2)
     29#define ISL29003_MODE_MASK		(0x3 << ISL29003_MODE_SHIFT)
     30#define ISL29003_RES_SHIFT		(0)
     31#define ISL29003_RES_MASK		(0x3 << ISL29003_RES_SHIFT)
     32
     33#define ISL29003_REG_CONTROL		0x01
     34#define ISL29003_INT_FLG		(1 << 5)
     35#define ISL29003_RANGE_SHIFT		(2)
     36#define ISL29003_RANGE_MASK		(0x3 << ISL29003_RANGE_SHIFT)
     37#define ISL29003_INT_PERSISTS_SHIFT	(0)
     38#define ISL29003_INT_PERSISTS_MASK	(0xf << ISL29003_INT_PERSISTS_SHIFT)
     39
     40#define ISL29003_REG_IRQ_THRESH_HI	0x02
     41#define ISL29003_REG_IRQ_THRESH_LO	0x03
     42#define ISL29003_REG_LSB_SENSOR		0x04
     43#define ISL29003_REG_MSB_SENSOR		0x05
     44#define ISL29003_REG_LSB_TIMER		0x06
     45#define ISL29003_REG_MSB_TIMER		0x07
     46
     47#define ISL29003_NUM_CACHABLE_REGS	4
     48
     49struct isl29003_data {
     50	struct i2c_client *client;
     51	struct mutex lock;
     52	u8 reg_cache[ISL29003_NUM_CACHABLE_REGS];
     53	u8 power_state_before_suspend;
     54};
     55
     56static int gain_range[] = {
     57	1000, 4000, 16000, 64000
     58};
     59
     60/*
     61 * register access helpers
     62 */
     63
     64static int __isl29003_read_reg(struct i2c_client *client,
     65			       u32 reg, u8 mask, u8 shift)
     66{
     67	struct isl29003_data *data = i2c_get_clientdata(client);
     68
     69	return (data->reg_cache[reg] & mask) >> shift;
     70}
     71
     72static int __isl29003_write_reg(struct i2c_client *client,
     73				u32 reg, u8 mask, u8 shift, u8 val)
     74{
     75	struct isl29003_data *data = i2c_get_clientdata(client);
     76	int ret = 0;
     77	u8 tmp;
     78
     79	if (reg >= ISL29003_NUM_CACHABLE_REGS)
     80		return -EINVAL;
     81
     82	mutex_lock(&data->lock);
     83
     84	tmp = data->reg_cache[reg];
     85	tmp &= ~mask;
     86	tmp |= val << shift;
     87
     88	ret = i2c_smbus_write_byte_data(client, reg, tmp);
     89	if (!ret)
     90		data->reg_cache[reg] = tmp;
     91
     92	mutex_unlock(&data->lock);
     93	return ret;
     94}
     95
     96/*
     97 * internally used functions
     98 */
     99
    100/* range */
    101static int isl29003_get_range(struct i2c_client *client)
    102{
    103	return __isl29003_read_reg(client, ISL29003_REG_CONTROL,
    104		ISL29003_RANGE_MASK, ISL29003_RANGE_SHIFT);
    105}
    106
    107static int isl29003_set_range(struct i2c_client *client, int range)
    108{
    109	return __isl29003_write_reg(client, ISL29003_REG_CONTROL,
    110		ISL29003_RANGE_MASK, ISL29003_RANGE_SHIFT, range);
    111}
    112
    113/* resolution */
    114static int isl29003_get_resolution(struct i2c_client *client)
    115{
    116	return __isl29003_read_reg(client, ISL29003_REG_COMMAND,
    117		ISL29003_RES_MASK, ISL29003_RES_SHIFT);
    118}
    119
    120static int isl29003_set_resolution(struct i2c_client *client, int res)
    121{
    122	return __isl29003_write_reg(client, ISL29003_REG_COMMAND,
    123		ISL29003_RES_MASK, ISL29003_RES_SHIFT, res);
    124}
    125
    126/* mode */
    127static int isl29003_get_mode(struct i2c_client *client)
    128{
    129	return __isl29003_read_reg(client, ISL29003_REG_COMMAND,
    130		ISL29003_MODE_MASK, ISL29003_MODE_SHIFT);
    131}
    132
    133static int isl29003_set_mode(struct i2c_client *client, int mode)
    134{
    135	return __isl29003_write_reg(client, ISL29003_REG_COMMAND,
    136		ISL29003_MODE_MASK, ISL29003_MODE_SHIFT, mode);
    137}
    138
    139/* power_state */
    140static int isl29003_set_power_state(struct i2c_client *client, int state)
    141{
    142	return __isl29003_write_reg(client, ISL29003_REG_COMMAND,
    143				ISL29003_ADC_ENABLED | ISL29003_ADC_PD, 0,
    144				state ? ISL29003_ADC_ENABLED : ISL29003_ADC_PD);
    145}
    146
    147static int isl29003_get_power_state(struct i2c_client *client)
    148{
    149	struct isl29003_data *data = i2c_get_clientdata(client);
    150	u8 cmdreg = data->reg_cache[ISL29003_REG_COMMAND];
    151
    152	return ~cmdreg & ISL29003_ADC_PD;
    153}
    154
    155static int isl29003_get_adc_value(struct i2c_client *client)
    156{
    157	struct isl29003_data *data = i2c_get_clientdata(client);
    158	int lsb, msb, range, bitdepth;
    159
    160	mutex_lock(&data->lock);
    161	lsb = i2c_smbus_read_byte_data(client, ISL29003_REG_LSB_SENSOR);
    162
    163	if (lsb < 0) {
    164		mutex_unlock(&data->lock);
    165		return lsb;
    166	}
    167
    168	msb = i2c_smbus_read_byte_data(client, ISL29003_REG_MSB_SENSOR);
    169	mutex_unlock(&data->lock);
    170
    171	if (msb < 0)
    172		return msb;
    173
    174	range = isl29003_get_range(client);
    175	bitdepth = (4 - isl29003_get_resolution(client)) * 4;
    176	return (((msb << 8) | lsb) * gain_range[range]) >> bitdepth;
    177}
    178
    179/*
    180 * sysfs layer
    181 */
    182
    183/* range */
    184static ssize_t isl29003_show_range(struct device *dev,
    185				   struct device_attribute *attr, char *buf)
    186{
    187	struct i2c_client *client = to_i2c_client(dev);
    188
    189	return sprintf(buf, "%i\n", isl29003_get_range(client));
    190}
    191
    192static ssize_t isl29003_store_range(struct device *dev,
    193				    struct device_attribute *attr,
    194				    const char *buf, size_t count)
    195{
    196	struct i2c_client *client = to_i2c_client(dev);
    197	unsigned long val;
    198	int ret;
    199
    200	ret = kstrtoul(buf, 10, &val);
    201	if (ret)
    202		return ret;
    203
    204	if (val > 3)
    205		return -EINVAL;
    206
    207	ret = isl29003_set_range(client, val);
    208	if (ret < 0)
    209		return ret;
    210
    211	return count;
    212}
    213
    214static DEVICE_ATTR(range, S_IWUSR | S_IRUGO,
    215		   isl29003_show_range, isl29003_store_range);
    216
    217
    218/* resolution */
    219static ssize_t isl29003_show_resolution(struct device *dev,
    220					struct device_attribute *attr,
    221					char *buf)
    222{
    223	struct i2c_client *client = to_i2c_client(dev);
    224
    225	return sprintf(buf, "%d\n", isl29003_get_resolution(client));
    226}
    227
    228static ssize_t isl29003_store_resolution(struct device *dev,
    229					 struct device_attribute *attr,
    230					 const char *buf, size_t count)
    231{
    232	struct i2c_client *client = to_i2c_client(dev);
    233	unsigned long val;
    234	int ret;
    235
    236	ret = kstrtoul(buf, 10, &val);
    237	if (ret)
    238		return ret;
    239
    240	if (val > 3)
    241		return -EINVAL;
    242
    243	ret = isl29003_set_resolution(client, val);
    244	if (ret < 0)
    245		return ret;
    246
    247	return count;
    248}
    249
    250static DEVICE_ATTR(resolution, S_IWUSR | S_IRUGO,
    251		   isl29003_show_resolution, isl29003_store_resolution);
    252
    253/* mode */
    254static ssize_t isl29003_show_mode(struct device *dev,
    255				  struct device_attribute *attr, char *buf)
    256{
    257	struct i2c_client *client = to_i2c_client(dev);
    258
    259	return sprintf(buf, "%d\n", isl29003_get_mode(client));
    260}
    261
    262static ssize_t isl29003_store_mode(struct device *dev,
    263		struct device_attribute *attr, const char *buf, size_t count)
    264{
    265	struct i2c_client *client = to_i2c_client(dev);
    266	unsigned long val;
    267	int ret;
    268
    269	ret = kstrtoul(buf, 10, &val);
    270	if (ret)
    271		return ret;
    272
    273	if (val > 2)
    274		return -EINVAL;
    275
    276	ret = isl29003_set_mode(client, val);
    277	if (ret < 0)
    278		return ret;
    279
    280	return count;
    281}
    282
    283static DEVICE_ATTR(mode, S_IWUSR | S_IRUGO,
    284		   isl29003_show_mode, isl29003_store_mode);
    285
    286
    287/* power state */
    288static ssize_t isl29003_show_power_state(struct device *dev,
    289					 struct device_attribute *attr,
    290					 char *buf)
    291{
    292	struct i2c_client *client = to_i2c_client(dev);
    293
    294	return sprintf(buf, "%d\n", isl29003_get_power_state(client));
    295}
    296
    297static ssize_t isl29003_store_power_state(struct device *dev,
    298					  struct device_attribute *attr,
    299					  const char *buf, size_t count)
    300{
    301	struct i2c_client *client = to_i2c_client(dev);
    302	unsigned long val;
    303	int ret;
    304
    305	ret = kstrtoul(buf, 10, &val);
    306	if (ret)
    307		return ret;
    308
    309	if (val > 1)
    310		return -EINVAL;
    311
    312	ret = isl29003_set_power_state(client, val);
    313	return ret ? ret : count;
    314}
    315
    316static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO,
    317		   isl29003_show_power_state, isl29003_store_power_state);
    318
    319
    320/* lux */
    321static ssize_t isl29003_show_lux(struct device *dev,
    322				 struct device_attribute *attr, char *buf)
    323{
    324	struct i2c_client *client = to_i2c_client(dev);
    325
    326	/* No LUX data if not operational */
    327	if (!isl29003_get_power_state(client))
    328		return -EBUSY;
    329
    330	return sprintf(buf, "%d\n", isl29003_get_adc_value(client));
    331}
    332
    333static DEVICE_ATTR(lux, S_IRUGO, isl29003_show_lux, NULL);
    334
    335static struct attribute *isl29003_attributes[] = {
    336	&dev_attr_range.attr,
    337	&dev_attr_resolution.attr,
    338	&dev_attr_mode.attr,
    339	&dev_attr_power_state.attr,
    340	&dev_attr_lux.attr,
    341	NULL
    342};
    343
    344static const struct attribute_group isl29003_attr_group = {
    345	.attrs = isl29003_attributes,
    346};
    347
    348static int isl29003_init_client(struct i2c_client *client)
    349{
    350	struct isl29003_data *data = i2c_get_clientdata(client);
    351	int i;
    352
    353	/* read all the registers once to fill the cache.
    354	 * if one of the reads fails, we consider the init failed */
    355	for (i = 0; i < ARRAY_SIZE(data->reg_cache); i++) {
    356		int v = i2c_smbus_read_byte_data(client, i);
    357
    358		if (v < 0)
    359			return -ENODEV;
    360
    361		data->reg_cache[i] = v;
    362	}
    363
    364	/* set defaults */
    365	isl29003_set_range(client, 0);
    366	isl29003_set_resolution(client, 0);
    367	isl29003_set_mode(client, 0);
    368	isl29003_set_power_state(client, 0);
    369
    370	return 0;
    371}
    372
    373/*
    374 * I2C layer
    375 */
    376
    377static int isl29003_probe(struct i2c_client *client,
    378				    const struct i2c_device_id *id)
    379{
    380	struct i2c_adapter *adapter = client->adapter;
    381	struct isl29003_data *data;
    382	int err = 0;
    383
    384	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
    385		return -EIO;
    386
    387	data = kzalloc(sizeof(struct isl29003_data), GFP_KERNEL);
    388	if (!data)
    389		return -ENOMEM;
    390
    391	data->client = client;
    392	i2c_set_clientdata(client, data);
    393	mutex_init(&data->lock);
    394
    395	/* initialize the ISL29003 chip */
    396	err = isl29003_init_client(client);
    397	if (err)
    398		goto exit_kfree;
    399
    400	/* register sysfs hooks */
    401	err = sysfs_create_group(&client->dev.kobj, &isl29003_attr_group);
    402	if (err)
    403		goto exit_kfree;
    404
    405	dev_info(&client->dev, "driver version %s enabled\n", DRIVER_VERSION);
    406	return 0;
    407
    408exit_kfree:
    409	kfree(data);
    410	return err;
    411}
    412
    413static int isl29003_remove(struct i2c_client *client)
    414{
    415	sysfs_remove_group(&client->dev.kobj, &isl29003_attr_group);
    416	isl29003_set_power_state(client, 0);
    417	kfree(i2c_get_clientdata(client));
    418	return 0;
    419}
    420
    421#ifdef CONFIG_PM_SLEEP
    422static int isl29003_suspend(struct device *dev)
    423{
    424	struct i2c_client *client = to_i2c_client(dev);
    425	struct isl29003_data *data = i2c_get_clientdata(client);
    426
    427	data->power_state_before_suspend = isl29003_get_power_state(client);
    428	return isl29003_set_power_state(client, 0);
    429}
    430
    431static int isl29003_resume(struct device *dev)
    432{
    433	int i;
    434	struct i2c_client *client = to_i2c_client(dev);
    435	struct isl29003_data *data = i2c_get_clientdata(client);
    436
    437	/* restore registers from cache */
    438	for (i = 0; i < ARRAY_SIZE(data->reg_cache); i++)
    439		if (i2c_smbus_write_byte_data(client, i, data->reg_cache[i]))
    440			return -EIO;
    441
    442	return isl29003_set_power_state(client,
    443		data->power_state_before_suspend);
    444}
    445
    446static SIMPLE_DEV_PM_OPS(isl29003_pm_ops, isl29003_suspend, isl29003_resume);
    447#define ISL29003_PM_OPS (&isl29003_pm_ops)
    448
    449#else
    450#define ISL29003_PM_OPS NULL
    451#endif /* CONFIG_PM_SLEEP */
    452
    453static const struct i2c_device_id isl29003_id[] = {
    454	{ "isl29003", 0 },
    455	{}
    456};
    457MODULE_DEVICE_TABLE(i2c, isl29003_id);
    458
    459static struct i2c_driver isl29003_driver = {
    460	.driver = {
    461		.name	= ISL29003_DRV_NAME,
    462		.pm	= ISL29003_PM_OPS,
    463	},
    464	.probe	= isl29003_probe,
    465	.remove	= isl29003_remove,
    466	.id_table = isl29003_id,
    467};
    468
    469module_i2c_driver(isl29003_driver);
    470
    471MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
    472MODULE_DESCRIPTION("ISL29003 ambient light sensor driver");
    473MODULE_LICENSE("GPL v2");
    474MODULE_VERSION(DRIVER_VERSION);