18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * tsl4531.c - Support for TAOS TSL4531 ambient light sensor
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2013 Peter Meerwald <pmeerw@pmeerw.net>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * IIO driver for the TSL4531x family
88c2ecf20Sopenharmony_ci *   TSL45311/TSL45313: 7-bit I2C slave address 0x39
98c2ecf20Sopenharmony_ci *   TSL45315/TSL45317: 7-bit I2C slave address 0x29
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * TODO: single cycle measurement
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/i2c.h>
168c2ecf20Sopenharmony_ci#include <linux/err.h>
178c2ecf20Sopenharmony_ci#include <linux/delay.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <linux/iio/iio.h>
208c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define TSL4531_DRV_NAME "tsl4531"
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define TSL4531_COMMAND BIT(7)
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define TSL4531_CONTROL (TSL4531_COMMAND | 0x00)
278c2ecf20Sopenharmony_ci#define TSL4531_CONFIG (TSL4531_COMMAND | 0x01)
288c2ecf20Sopenharmony_ci#define TSL4531_DATA (TSL4531_COMMAND | 0x04)
298c2ecf20Sopenharmony_ci#define TSL4531_ID (TSL4531_COMMAND | 0x0a)
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci/* operating modes in control register */
328c2ecf20Sopenharmony_ci#define TSL4531_MODE_POWERDOWN 0x00
338c2ecf20Sopenharmony_ci#define TSL4531_MODE_SINGLE_ADC 0x02
348c2ecf20Sopenharmony_ci#define TSL4531_MODE_NORMAL 0x03
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/* integration time control in config register */
378c2ecf20Sopenharmony_ci#define TSL4531_TCNTRL_400MS 0x00
388c2ecf20Sopenharmony_ci#define TSL4531_TCNTRL_200MS 0x01
398c2ecf20Sopenharmony_ci#define TSL4531_TCNTRL_100MS 0x02
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci/* part number in id register */
428c2ecf20Sopenharmony_ci#define TSL45311_ID 0x8
438c2ecf20Sopenharmony_ci#define TSL45313_ID 0x9
448c2ecf20Sopenharmony_ci#define TSL45315_ID 0xa
458c2ecf20Sopenharmony_ci#define TSL45317_ID 0xb
468c2ecf20Sopenharmony_ci#define TSL4531_ID_SHIFT 4
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistruct tsl4531_data {
498c2ecf20Sopenharmony_ci	struct i2c_client *client;
508c2ecf20Sopenharmony_ci	struct mutex lock;
518c2ecf20Sopenharmony_ci	int int_time;
528c2ecf20Sopenharmony_ci};
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic IIO_CONST_ATTR_INT_TIME_AVAIL("0.1 0.2 0.4");
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic struct attribute *tsl4531_attributes[] = {
578c2ecf20Sopenharmony_ci	&iio_const_attr_integration_time_available.dev_attr.attr,
588c2ecf20Sopenharmony_ci	NULL
598c2ecf20Sopenharmony_ci};
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic const struct attribute_group tsl4531_attribute_group = {
628c2ecf20Sopenharmony_ci	.attrs = tsl4531_attributes,
638c2ecf20Sopenharmony_ci};
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic const struct iio_chan_spec tsl4531_channels[] = {
668c2ecf20Sopenharmony_ci	{
678c2ecf20Sopenharmony_ci		.type = IIO_LIGHT,
688c2ecf20Sopenharmony_ci		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
698c2ecf20Sopenharmony_ci			BIT(IIO_CHAN_INFO_SCALE) |
708c2ecf20Sopenharmony_ci			BIT(IIO_CHAN_INFO_INT_TIME)
718c2ecf20Sopenharmony_ci	}
728c2ecf20Sopenharmony_ci};
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic int tsl4531_read_raw(struct iio_dev *indio_dev,
758c2ecf20Sopenharmony_ci				struct iio_chan_spec const *chan,
768c2ecf20Sopenharmony_ci				int *val, int *val2, long mask)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	struct tsl4531_data *data = iio_priv(indio_dev);
798c2ecf20Sopenharmony_ci	int ret;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	switch (mask) {
828c2ecf20Sopenharmony_ci	case IIO_CHAN_INFO_RAW:
838c2ecf20Sopenharmony_ci		ret = i2c_smbus_read_word_data(data->client,
848c2ecf20Sopenharmony_ci			TSL4531_DATA);
858c2ecf20Sopenharmony_ci		if (ret < 0)
868c2ecf20Sopenharmony_ci			return ret;
878c2ecf20Sopenharmony_ci		*val = ret;
888c2ecf20Sopenharmony_ci		return IIO_VAL_INT;
898c2ecf20Sopenharmony_ci	case IIO_CHAN_INFO_SCALE:
908c2ecf20Sopenharmony_ci		/* 0.. 1x, 1 .. 2x, 2 .. 4x */
918c2ecf20Sopenharmony_ci		*val = 1 << data->int_time;
928c2ecf20Sopenharmony_ci		return IIO_VAL_INT;
938c2ecf20Sopenharmony_ci	case IIO_CHAN_INFO_INT_TIME:
948c2ecf20Sopenharmony_ci		if (data->int_time == 0)
958c2ecf20Sopenharmony_ci			*val2 = 400000;
968c2ecf20Sopenharmony_ci		else if (data->int_time == 1)
978c2ecf20Sopenharmony_ci			*val2 = 200000;
988c2ecf20Sopenharmony_ci		else if (data->int_time == 2)
998c2ecf20Sopenharmony_ci			*val2 = 100000;
1008c2ecf20Sopenharmony_ci		else
1018c2ecf20Sopenharmony_ci			return -EINVAL;
1028c2ecf20Sopenharmony_ci		*val = 0;
1038c2ecf20Sopenharmony_ci		return IIO_VAL_INT_PLUS_MICRO;
1048c2ecf20Sopenharmony_ci	default:
1058c2ecf20Sopenharmony_ci		return -EINVAL;
1068c2ecf20Sopenharmony_ci	}
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic int tsl4531_write_raw(struct iio_dev *indio_dev,
1108c2ecf20Sopenharmony_ci			     struct iio_chan_spec const *chan,
1118c2ecf20Sopenharmony_ci			     int val, int val2, long mask)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	struct tsl4531_data *data = iio_priv(indio_dev);
1148c2ecf20Sopenharmony_ci	int int_time, ret;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	switch (mask) {
1178c2ecf20Sopenharmony_ci	case IIO_CHAN_INFO_INT_TIME:
1188c2ecf20Sopenharmony_ci		if (val != 0)
1198c2ecf20Sopenharmony_ci			return -EINVAL;
1208c2ecf20Sopenharmony_ci		if (val2 == 400000)
1218c2ecf20Sopenharmony_ci			int_time = 0;
1228c2ecf20Sopenharmony_ci		else if (val2 == 200000)
1238c2ecf20Sopenharmony_ci			int_time = 1;
1248c2ecf20Sopenharmony_ci		else if (val2 == 100000)
1258c2ecf20Sopenharmony_ci			int_time = 2;
1268c2ecf20Sopenharmony_ci		else
1278c2ecf20Sopenharmony_ci			return -EINVAL;
1288c2ecf20Sopenharmony_ci		mutex_lock(&data->lock);
1298c2ecf20Sopenharmony_ci		ret = i2c_smbus_write_byte_data(data->client,
1308c2ecf20Sopenharmony_ci			TSL4531_CONFIG, int_time);
1318c2ecf20Sopenharmony_ci		if (ret >= 0)
1328c2ecf20Sopenharmony_ci			data->int_time = int_time;
1338c2ecf20Sopenharmony_ci		mutex_unlock(&data->lock);
1348c2ecf20Sopenharmony_ci		return ret;
1358c2ecf20Sopenharmony_ci	default:
1368c2ecf20Sopenharmony_ci		return -EINVAL;
1378c2ecf20Sopenharmony_ci	}
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic const struct iio_info tsl4531_info = {
1418c2ecf20Sopenharmony_ci	.read_raw = tsl4531_read_raw,
1428c2ecf20Sopenharmony_ci	.write_raw = tsl4531_write_raw,
1438c2ecf20Sopenharmony_ci	.attrs = &tsl4531_attribute_group,
1448c2ecf20Sopenharmony_ci};
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic int tsl4531_check_id(struct i2c_client *client)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	int ret = i2c_smbus_read_byte_data(client, TSL4531_ID);
1498c2ecf20Sopenharmony_ci	if (ret < 0)
1508c2ecf20Sopenharmony_ci		return ret;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	switch (ret >> TSL4531_ID_SHIFT) {
1538c2ecf20Sopenharmony_ci	case TSL45311_ID:
1548c2ecf20Sopenharmony_ci	case TSL45313_ID:
1558c2ecf20Sopenharmony_ci	case TSL45315_ID:
1568c2ecf20Sopenharmony_ci	case TSL45317_ID:
1578c2ecf20Sopenharmony_ci		return 0;
1588c2ecf20Sopenharmony_ci	default:
1598c2ecf20Sopenharmony_ci		return -ENODEV;
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistatic int tsl4531_probe(struct i2c_client *client,
1648c2ecf20Sopenharmony_ci			  const struct i2c_device_id *id)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	struct tsl4531_data *data;
1678c2ecf20Sopenharmony_ci	struct iio_dev *indio_dev;
1688c2ecf20Sopenharmony_ci	int ret;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
1718c2ecf20Sopenharmony_ci	if (!indio_dev)
1728c2ecf20Sopenharmony_ci		return -ENOMEM;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	data = iio_priv(indio_dev);
1758c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, indio_dev);
1768c2ecf20Sopenharmony_ci	data->client = client;
1778c2ecf20Sopenharmony_ci	mutex_init(&data->lock);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	ret = tsl4531_check_id(client);
1808c2ecf20Sopenharmony_ci	if (ret) {
1818c2ecf20Sopenharmony_ci		dev_err(&client->dev, "no TSL4531 sensor\n");
1828c2ecf20Sopenharmony_ci		return ret;
1838c2ecf20Sopenharmony_ci	}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	ret = i2c_smbus_write_byte_data(data->client, TSL4531_CONTROL,
1868c2ecf20Sopenharmony_ci		TSL4531_MODE_NORMAL);
1878c2ecf20Sopenharmony_ci	if (ret < 0)
1888c2ecf20Sopenharmony_ci		return ret;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	ret = i2c_smbus_write_byte_data(data->client, TSL4531_CONFIG,
1918c2ecf20Sopenharmony_ci		TSL4531_TCNTRL_400MS);
1928c2ecf20Sopenharmony_ci	if (ret < 0)
1938c2ecf20Sopenharmony_ci		return ret;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	indio_dev->info = &tsl4531_info;
1968c2ecf20Sopenharmony_ci	indio_dev->channels = tsl4531_channels;
1978c2ecf20Sopenharmony_ci	indio_dev->num_channels = ARRAY_SIZE(tsl4531_channels);
1988c2ecf20Sopenharmony_ci	indio_dev->name = TSL4531_DRV_NAME;
1998c2ecf20Sopenharmony_ci	indio_dev->modes = INDIO_DIRECT_MODE;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	return iio_device_register(indio_dev);
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cistatic int tsl4531_powerdown(struct i2c_client *client)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	return i2c_smbus_write_byte_data(client, TSL4531_CONTROL,
2078c2ecf20Sopenharmony_ci		TSL4531_MODE_POWERDOWN);
2088c2ecf20Sopenharmony_ci}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_cistatic int tsl4531_remove(struct i2c_client *client)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	iio_device_unregister(i2c_get_clientdata(client));
2138c2ecf20Sopenharmony_ci	tsl4531_powerdown(client);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	return 0;
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
2198c2ecf20Sopenharmony_cistatic int tsl4531_suspend(struct device *dev)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	return tsl4531_powerdown(to_i2c_client(dev));
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistatic int tsl4531_resume(struct device *dev)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	return i2c_smbus_write_byte_data(to_i2c_client(dev), TSL4531_CONTROL,
2278c2ecf20Sopenharmony_ci		TSL4531_MODE_NORMAL);
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(tsl4531_pm_ops, tsl4531_suspend, tsl4531_resume);
2318c2ecf20Sopenharmony_ci#define TSL4531_PM_OPS (&tsl4531_pm_ops)
2328c2ecf20Sopenharmony_ci#else
2338c2ecf20Sopenharmony_ci#define TSL4531_PM_OPS NULL
2348c2ecf20Sopenharmony_ci#endif
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistatic const struct i2c_device_id tsl4531_id[] = {
2378c2ecf20Sopenharmony_ci	{ "tsl4531", 0 },
2388c2ecf20Sopenharmony_ci	{ }
2398c2ecf20Sopenharmony_ci};
2408c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, tsl4531_id);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_cistatic struct i2c_driver tsl4531_driver = {
2438c2ecf20Sopenharmony_ci	.driver = {
2448c2ecf20Sopenharmony_ci		.name   = TSL4531_DRV_NAME,
2458c2ecf20Sopenharmony_ci		.pm	= TSL4531_PM_OPS,
2468c2ecf20Sopenharmony_ci	},
2478c2ecf20Sopenharmony_ci	.probe  = tsl4531_probe,
2488c2ecf20Sopenharmony_ci	.remove = tsl4531_remove,
2498c2ecf20Sopenharmony_ci	.id_table = tsl4531_id,
2508c2ecf20Sopenharmony_ci};
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cimodule_i2c_driver(tsl4531_driver);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ciMODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
2558c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TAOS TSL4531 ambient light sensors driver");
2568c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
257