brintos

brintos / linux-shallow public Read only

0
0
Text · 10.9 KiB · 9f26db4 Raw
473 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *  isl29003.c - Linux kernel module for4 * 	Intersil ISL29003 ambient light sensor5 *6 *  See file:Documentation/misc-devices/isl29003.rst7 *8 *  Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>9 *10 *  Based on code written by11 *  	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		0x0025#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		0x0134#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	0x0241#define ISL29003_REG_IRQ_THRESH_LO	0x0342#define ISL29003_REG_LSB_SENSOR		0x0443#define ISL29003_REG_MSB_SENSOR		0x0544#define ISL29003_REG_LSB_TIMER		0x0645#define ISL29003_REG_MSB_TIMER		0x0746 47#define ISL29003_NUM_CACHABLE_REGS	448 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, 6400058};59 60/*61 * register access helpers62 */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 functions98 */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 layer181 */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 sysfs_emit(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 sysfs_emit(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 sysfs_emit(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 sysfs_emit(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 sysfs_emit(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	NULL342};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 layer375 */376 377static int isl29003_probe(struct i2c_client *client)378{379	struct i2c_adapter *adapter = client->adapter;380	struct isl29003_data *data;381	int err = 0;382 383	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))384		return -EIO;385 386	data = kzalloc(sizeof(struct isl29003_data), GFP_KERNEL);387	if (!data)388		return -ENOMEM;389 390	data->client = client;391	i2c_set_clientdata(client, data);392	mutex_init(&data->lock);393 394	/* initialize the ISL29003 chip */395	err = isl29003_init_client(client);396	if (err)397		goto exit_kfree;398 399	/* register sysfs hooks */400	err = sysfs_create_group(&client->dev.kobj, &isl29003_attr_group);401	if (err)402		goto exit_kfree;403 404	dev_info(&client->dev, "driver version %s enabled\n", DRIVER_VERSION);405	return 0;406 407exit_kfree:408	kfree(data);409	return err;410}411 412static void isl29003_remove(struct i2c_client *client)413{414	sysfs_remove_group(&client->dev.kobj, &isl29003_attr_group);415	isl29003_set_power_state(client, 0);416	kfree(i2c_get_clientdata(client));417}418 419#ifdef CONFIG_PM_SLEEP420static int isl29003_suspend(struct device *dev)421{422	struct i2c_client *client = to_i2c_client(dev);423	struct isl29003_data *data = i2c_get_clientdata(client);424 425	data->power_state_before_suspend = isl29003_get_power_state(client);426	return isl29003_set_power_state(client, 0);427}428 429static int isl29003_resume(struct device *dev)430{431	int i;432	struct i2c_client *client = to_i2c_client(dev);433	struct isl29003_data *data = i2c_get_clientdata(client);434 435	/* restore registers from cache */436	for (i = 0; i < ARRAY_SIZE(data->reg_cache); i++)437		if (i2c_smbus_write_byte_data(client, i, data->reg_cache[i]))438			return -EIO;439 440	return isl29003_set_power_state(client,441		data->power_state_before_suspend);442}443 444static SIMPLE_DEV_PM_OPS(isl29003_pm_ops, isl29003_suspend, isl29003_resume);445#define ISL29003_PM_OPS (&isl29003_pm_ops)446 447#else448#define ISL29003_PM_OPS NULL449#endif /* CONFIG_PM_SLEEP */450 451static const struct i2c_device_id isl29003_id[] = {452	{ "isl29003" },453	{}454};455MODULE_DEVICE_TABLE(i2c, isl29003_id);456 457static struct i2c_driver isl29003_driver = {458	.driver = {459		.name	= ISL29003_DRV_NAME,460		.pm	= ISL29003_PM_OPS,461	},462	.probe = isl29003_probe,463	.remove	= isl29003_remove,464	.id_table = isl29003_id,465};466 467module_i2c_driver(isl29003_driver);468 469MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");470MODULE_DESCRIPTION("ISL29003 ambient light sensor driver");471MODULE_LICENSE("GPL v2");472MODULE_VERSION(DRIVER_VERSION);473