brintos

brintos / linux-shallow public Read only

0
0
Text · 9.9 KiB · 1a7796a Raw
451 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *  tsl2550.c - Linux kernel modules for ambient light sensor4 *5 *  Copyright (C) 2007 Rodolfo Giometti <giometti@linux.it>6 *  Copyright (C) 2007 Eurotech S.p.A. <info@eurotech.it>7 */8 9#include <linux/module.h>10#include <linux/slab.h>11#include <linux/i2c.h>12#include <linux/mutex.h>13 14#define TSL2550_DRV_NAME	"tsl2550"15#define DRIVER_VERSION		"1.2"16 17/*18 * Defines19 */20 21#define TSL2550_POWER_DOWN		0x0022#define TSL2550_POWER_UP		0x0323#define TSL2550_STANDARD_RANGE		0x1824#define TSL2550_EXTENDED_RANGE		0x1d25#define TSL2550_READ_ADC0		0x4326#define TSL2550_READ_ADC1		0x8327 28/*29 * Structs30 */31 32struct tsl2550_data {33	struct i2c_client *client;34	struct mutex update_lock;35 36	unsigned int power_state:1;37	unsigned int operating_mode:1;38};39 40/*41 * Global data42 */43 44static const u8 TSL2550_MODE_RANGE[2] = {45	TSL2550_STANDARD_RANGE, TSL2550_EXTENDED_RANGE,46};47 48/*49 * Management functions50 */51 52static int tsl2550_set_operating_mode(struct i2c_client *client, int mode)53{54	struct tsl2550_data *data = i2c_get_clientdata(client);55 56	int ret = i2c_smbus_write_byte(client, TSL2550_MODE_RANGE[mode]);57 58	data->operating_mode = mode;59 60	return ret;61}62 63static int tsl2550_set_power_state(struct i2c_client *client, int state)64{65	struct tsl2550_data *data = i2c_get_clientdata(client);66	int ret;67 68	if (state == 0)69		ret = i2c_smbus_write_byte(client, TSL2550_POWER_DOWN);70	else {71		ret = i2c_smbus_write_byte(client, TSL2550_POWER_UP);72 73		/* On power up we should reset operating mode also... */74		tsl2550_set_operating_mode(client, data->operating_mode);75	}76 77	data->power_state = state;78 79	return ret;80}81 82static int tsl2550_get_adc_value(struct i2c_client *client, u8 cmd)83{84	int ret;85 86	ret = i2c_smbus_read_byte_data(client, cmd);87	if (ret < 0)88		return ret;89	if (!(ret & 0x80))90		return -EAGAIN;91	return ret & 0x7f;	/* remove the "valid" bit */92}93 94/*95 * LUX calculation96 */97 98#define	TSL2550_MAX_LUX		184699 100static const u8 ratio_lut[] = {101	100, 100, 100, 100, 100, 100, 100, 100,102	100, 100, 100, 100, 100, 100, 99, 99,103	99, 99, 99, 99, 99, 99, 99, 99,104	99, 99, 99, 98, 98, 98, 98, 98,105	98, 98, 97, 97, 97, 97, 97, 96,106	96, 96, 96, 95, 95, 95, 94, 94,107	93, 93, 93, 92, 92, 91, 91, 90,108	89, 89, 88, 87, 87, 86, 85, 84,109	83, 82, 81, 80, 79, 78, 77, 75,110	74, 73, 71, 69, 68, 66, 64, 62,111	60, 58, 56, 54, 52, 49, 47, 44,112	42, 41, 40, 40, 39, 39, 38, 38,113	37, 37, 37, 36, 36, 36, 35, 35,114	35, 35, 34, 34, 34, 34, 33, 33,115	33, 33, 32, 32, 32, 32, 32, 31,116	31, 31, 31, 31, 30, 30, 30, 30,117	30,118};119 120static const u16 count_lut[] = {121	0, 1, 2, 3, 4, 5, 6, 7,122	8, 9, 10, 11, 12, 13, 14, 15,123	16, 18, 20, 22, 24, 26, 28, 30,124	32, 34, 36, 38, 40, 42, 44, 46,125	49, 53, 57, 61, 65, 69, 73, 77,126	81, 85, 89, 93, 97, 101, 105, 109,127	115, 123, 131, 139, 147, 155, 163, 171,128	179, 187, 195, 203, 211, 219, 227, 235,129	247, 263, 279, 295, 311, 327, 343, 359,130	375, 391, 407, 423, 439, 455, 471, 487,131	511, 543, 575, 607, 639, 671, 703, 735,132	767, 799, 831, 863, 895, 927, 959, 991,133	1039, 1103, 1167, 1231, 1295, 1359, 1423, 1487,134	1551, 1615, 1679, 1743, 1807, 1871, 1935, 1999,135	2095, 2223, 2351, 2479, 2607, 2735, 2863, 2991,136	3119, 3247, 3375, 3503, 3631, 3759, 3887, 4015,137};138 139/*140 * This function is described into Taos TSL2550 Designer's Notebook141 * pages 2, 3.142 */143static int tsl2550_calculate_lux(u8 ch0, u8 ch1)144{145	unsigned int lux;146 147	/* Look up count from channel values */148	u16 c0 = count_lut[ch0];149	u16 c1 = count_lut[ch1];150 151	/* Avoid division by 0 and count 1 cannot be greater than count 0 */152	if (c1 <= c0)153		if (c0) {154			/*155			 * Calculate ratio.156			 * Note: the "128" is a scaling factor157			 */158			u8 r = c1 * 128 / c0;159 160			/* Calculate LUX */161			lux = ((c0 - c1) * ratio_lut[r]) / 256;162		} else163			lux = 0;164	else165		return 0;166 167	/* LUX range check */168	return lux > TSL2550_MAX_LUX ? TSL2550_MAX_LUX : lux;169}170 171/*172 * SysFS support173 */174 175static ssize_t tsl2550_show_power_state(struct device *dev,176		struct device_attribute *attr, char *buf)177{178	struct tsl2550_data *data = i2c_get_clientdata(to_i2c_client(dev));179 180	return sprintf(buf, "%u\n", data->power_state);181}182 183static ssize_t tsl2550_store_power_state(struct device *dev,184		struct device_attribute *attr, const char *buf, size_t count)185{186	struct i2c_client *client = to_i2c_client(dev);187	struct tsl2550_data *data = i2c_get_clientdata(client);188	unsigned long val;189	int ret;190 191	if (kstrtoul(buf, 10, &val) || val > 1)192		return -EINVAL;193 194	mutex_lock(&data->update_lock);195	ret = tsl2550_set_power_state(client, val);196	mutex_unlock(&data->update_lock);197 198	if (ret < 0)199		return ret;200 201	return count;202}203 204static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO,205		   tsl2550_show_power_state, tsl2550_store_power_state);206 207static ssize_t tsl2550_show_operating_mode(struct device *dev,208		struct device_attribute *attr, char *buf)209{210	struct tsl2550_data *data = i2c_get_clientdata(to_i2c_client(dev));211 212	return sprintf(buf, "%u\n", data->operating_mode);213}214 215static ssize_t tsl2550_store_operating_mode(struct device *dev,216		struct device_attribute *attr, const char *buf, size_t count)217{218	struct i2c_client *client = to_i2c_client(dev);219	struct tsl2550_data *data = i2c_get_clientdata(client);220	unsigned long val;221	int ret;222 223	if (kstrtoul(buf, 10, &val) || val > 1)224		return -EINVAL;225 226	if (data->power_state == 0)227		return -EBUSY;228 229	mutex_lock(&data->update_lock);230	ret = tsl2550_set_operating_mode(client, val);231	mutex_unlock(&data->update_lock);232 233	if (ret < 0)234		return ret;235 236	return count;237}238 239static DEVICE_ATTR(operating_mode, S_IWUSR | S_IRUGO,240		   tsl2550_show_operating_mode, tsl2550_store_operating_mode);241 242static ssize_t __tsl2550_show_lux(struct i2c_client *client, char *buf)243{244	struct tsl2550_data *data = i2c_get_clientdata(client);245	u8 ch0, ch1;246	int ret;247 248	ret = tsl2550_get_adc_value(client, TSL2550_READ_ADC0);249	if (ret < 0)250		return ret;251	ch0 = ret;252 253	ret = tsl2550_get_adc_value(client, TSL2550_READ_ADC1);254	if (ret < 0)255		return ret;256	ch1 = ret;257 258	/* Do the job */259	ret = tsl2550_calculate_lux(ch0, ch1);260	if (ret < 0)261		return ret;262	if (data->operating_mode == 1)263		ret *= 5;264 265	return sprintf(buf, "%d\n", ret);266}267 268static ssize_t tsl2550_show_lux1_input(struct device *dev,269			struct device_attribute *attr, char *buf)270{271	struct i2c_client *client = to_i2c_client(dev);272	struct tsl2550_data *data = i2c_get_clientdata(client);273	int ret;274 275	/* No LUX data if not operational */276	if (!data->power_state)277		return -EBUSY;278 279	mutex_lock(&data->update_lock);280	ret = __tsl2550_show_lux(client, buf);281	mutex_unlock(&data->update_lock);282 283	return ret;284}285 286static DEVICE_ATTR(lux1_input, S_IRUGO,287		   tsl2550_show_lux1_input, NULL);288 289static struct attribute *tsl2550_attributes[] = {290	&dev_attr_power_state.attr,291	&dev_attr_operating_mode.attr,292	&dev_attr_lux1_input.attr,293	NULL294};295 296static const struct attribute_group tsl2550_attr_group = {297	.attrs = tsl2550_attributes,298};299 300/*301 * Initialization function302 */303 304static int tsl2550_init_client(struct i2c_client *client)305{306	struct tsl2550_data *data = i2c_get_clientdata(client);307	int err;308 309	/*310	 * Probe the chip. To do so we try to power up the device and then to311	 * read back the 0x03 code312	 */313	err = i2c_smbus_read_byte_data(client, TSL2550_POWER_UP);314	if (err < 0)315		return err;316	if (err != TSL2550_POWER_UP)317		return -ENODEV;318	data->power_state = 1;319 320	/* Set the default operating mode */321	err = i2c_smbus_write_byte(client,322				   TSL2550_MODE_RANGE[data->operating_mode]);323	if (err < 0)324		return err;325 326	return 0;327}328 329/*330 * I2C init/probing/exit functions331 */332 333static struct i2c_driver tsl2550_driver;334static int tsl2550_probe(struct i2c_client *client)335{336	struct i2c_adapter *adapter = client->adapter;337	struct tsl2550_data *data;338	int *opmode, err = 0;339 340	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE341					    | I2C_FUNC_SMBUS_READ_BYTE_DATA)) {342		err = -EIO;343		goto exit;344	}345 346	data = kzalloc(sizeof(struct tsl2550_data), GFP_KERNEL);347	if (!data) {348		err = -ENOMEM;349		goto exit;350	}351	data->client = client;352	i2c_set_clientdata(client, data);353 354	/* Check platform data */355	opmode = client->dev.platform_data;356	if (opmode) {357		if (*opmode < 0 || *opmode > 1) {358			dev_err(&client->dev, "invalid operating_mode (%d)\n",359					*opmode);360			err = -EINVAL;361			goto exit_kfree;362		}363		data->operating_mode = *opmode;364	} else365		data->operating_mode = 0;	/* default mode is standard */366	dev_info(&client->dev, "%s operating mode\n",367			data->operating_mode ? "extended" : "standard");368 369	mutex_init(&data->update_lock);370 371	/* Initialize the TSL2550 chip */372	err = tsl2550_init_client(client);373	if (err)374		goto exit_kfree;375 376	/* Register sysfs hooks */377	err = sysfs_create_group(&client->dev.kobj, &tsl2550_attr_group);378	if (err)379		goto exit_kfree;380 381	dev_info(&client->dev, "support ver. %s enabled\n", DRIVER_VERSION);382 383	return 0;384 385exit_kfree:386	kfree(data);387exit:388	return err;389}390 391static void tsl2550_remove(struct i2c_client *client)392{393	sysfs_remove_group(&client->dev.kobj, &tsl2550_attr_group);394 395	/* Power down the device */396	tsl2550_set_power_state(client, 0);397 398	kfree(i2c_get_clientdata(client));399}400 401#ifdef CONFIG_PM_SLEEP402 403static int tsl2550_suspend(struct device *dev)404{405	return tsl2550_set_power_state(to_i2c_client(dev), 0);406}407 408static int tsl2550_resume(struct device *dev)409{410	return tsl2550_set_power_state(to_i2c_client(dev), 1);411}412 413static SIMPLE_DEV_PM_OPS(tsl2550_pm_ops, tsl2550_suspend, tsl2550_resume);414#define TSL2550_PM_OPS (&tsl2550_pm_ops)415 416#else417 418#define TSL2550_PM_OPS NULL419 420#endif /* CONFIG_PM_SLEEP */421 422static const struct i2c_device_id tsl2550_id[] = {423	{ "tsl2550" },424	{ }425};426MODULE_DEVICE_TABLE(i2c, tsl2550_id);427 428static const struct of_device_id tsl2550_of_match[] = {429	{ .compatible = "taos,tsl2550" },430	{ }431};432MODULE_DEVICE_TABLE(of, tsl2550_of_match);433 434static struct i2c_driver tsl2550_driver = {435	.driver = {436		.name	= TSL2550_DRV_NAME,437		.of_match_table = tsl2550_of_match,438		.pm	= TSL2550_PM_OPS,439	},440	.probe = tsl2550_probe,441	.remove	= tsl2550_remove,442	.id_table = tsl2550_id,443};444 445module_i2c_driver(tsl2550_driver);446 447MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");448MODULE_DESCRIPTION("TSL2550 ambient light sensor driver");449MODULE_LICENSE("GPL");450MODULE_VERSION(DRIVER_VERSION);451