18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * CM3323 - Capella Color Light Sensor
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2015, Intel Corporation.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * IIO driver for CM3323 (7-bit I2C slave address 0x10)
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * TODO: calibscale to correct the lens factor
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/init.h>
138c2ecf20Sopenharmony_ci#include <linux/i2c.h>
148c2ecf20Sopenharmony_ci#include <linux/mutex.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/iio/iio.h>
178c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define CM3323_DRV_NAME "cm3323"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define CM3323_CMD_CONF		0x00
228c2ecf20Sopenharmony_ci#define CM3323_CMD_RED_DATA	0x08
238c2ecf20Sopenharmony_ci#define CM3323_CMD_GREEN_DATA	0x09
248c2ecf20Sopenharmony_ci#define CM3323_CMD_BLUE_DATA	0x0A
258c2ecf20Sopenharmony_ci#define CM3323_CMD_CLEAR_DATA	0x0B
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define CM3323_CONF_SD_BIT	BIT(0) /* sensor disable */
288c2ecf20Sopenharmony_ci#define CM3323_CONF_AF_BIT	BIT(1) /* auto/manual force mode */
298c2ecf20Sopenharmony_ci#define CM3323_CONF_IT_MASK	GENMASK(6, 4)
308c2ecf20Sopenharmony_ci#define CM3323_CONF_IT_SHIFT	4
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define CM3323_INT_TIME_AVAILABLE "0.04 0.08 0.16 0.32 0.64 1.28"
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistatic const struct {
358c2ecf20Sopenharmony_ci	int val;
368c2ecf20Sopenharmony_ci	int val2;
378c2ecf20Sopenharmony_ci} cm3323_int_time[] = {
388c2ecf20Sopenharmony_ci	{0, 40000},  /* 40 ms */
398c2ecf20Sopenharmony_ci	{0, 80000},  /* 80 ms */
408c2ecf20Sopenharmony_ci	{0, 160000}, /* 160 ms */
418c2ecf20Sopenharmony_ci	{0, 320000}, /* 320 ms */
428c2ecf20Sopenharmony_ci	{0, 640000}, /* 640 ms */
438c2ecf20Sopenharmony_ci	{1, 280000}, /* 1280 ms */
448c2ecf20Sopenharmony_ci};
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistruct cm3323_data {
478c2ecf20Sopenharmony_ci	struct i2c_client *client;
488c2ecf20Sopenharmony_ci	u16 reg_conf;
498c2ecf20Sopenharmony_ci	struct mutex mutex;
508c2ecf20Sopenharmony_ci};
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci#define CM3323_COLOR_CHANNEL(_color, _addr) { \
538c2ecf20Sopenharmony_ci	.type = IIO_INTENSITY, \
548c2ecf20Sopenharmony_ci	.modified = 1, \
558c2ecf20Sopenharmony_ci	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
568c2ecf20Sopenharmony_ci	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), \
578c2ecf20Sopenharmony_ci	.channel2 = IIO_MOD_LIGHT_##_color, \
588c2ecf20Sopenharmony_ci	.address = _addr, \
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic const struct iio_chan_spec cm3323_channels[] = {
628c2ecf20Sopenharmony_ci	CM3323_COLOR_CHANNEL(RED, CM3323_CMD_RED_DATA),
638c2ecf20Sopenharmony_ci	CM3323_COLOR_CHANNEL(GREEN, CM3323_CMD_GREEN_DATA),
648c2ecf20Sopenharmony_ci	CM3323_COLOR_CHANNEL(BLUE, CM3323_CMD_BLUE_DATA),
658c2ecf20Sopenharmony_ci	CM3323_COLOR_CHANNEL(CLEAR, CM3323_CMD_CLEAR_DATA),
668c2ecf20Sopenharmony_ci};
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic IIO_CONST_ATTR_INT_TIME_AVAIL(CM3323_INT_TIME_AVAILABLE);
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic struct attribute *cm3323_attributes[] = {
718c2ecf20Sopenharmony_ci	&iio_const_attr_integration_time_available.dev_attr.attr,
728c2ecf20Sopenharmony_ci	NULL
738c2ecf20Sopenharmony_ci};
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic const struct attribute_group cm3323_attribute_group = {
768c2ecf20Sopenharmony_ci	.attrs = cm3323_attributes,
778c2ecf20Sopenharmony_ci};
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic int cm3323_init(struct iio_dev *indio_dev)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	int ret;
828c2ecf20Sopenharmony_ci	struct cm3323_data *data = iio_priv(indio_dev);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	ret = i2c_smbus_read_word_data(data->client, CM3323_CMD_CONF);
858c2ecf20Sopenharmony_ci	if (ret < 0) {
868c2ecf20Sopenharmony_ci		dev_err(&data->client->dev, "Error reading reg_conf\n");
878c2ecf20Sopenharmony_ci		return ret;
888c2ecf20Sopenharmony_ci	}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	/* enable sensor and set auto force mode */
918c2ecf20Sopenharmony_ci	ret &= ~(CM3323_CONF_SD_BIT | CM3323_CONF_AF_BIT);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	ret = i2c_smbus_write_word_data(data->client, CM3323_CMD_CONF, ret);
948c2ecf20Sopenharmony_ci	if (ret < 0) {
958c2ecf20Sopenharmony_ci		dev_err(&data->client->dev, "Error writing reg_conf\n");
968c2ecf20Sopenharmony_ci		return ret;
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	data->reg_conf = ret;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	return 0;
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic void cm3323_disable(void *data)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	int ret;
1078c2ecf20Sopenharmony_ci	struct iio_dev *indio_dev = data;
1088c2ecf20Sopenharmony_ci	struct cm3323_data *cm_data = iio_priv(indio_dev);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	ret = i2c_smbus_write_word_data(cm_data->client, CM3323_CMD_CONF,
1118c2ecf20Sopenharmony_ci					CM3323_CONF_SD_BIT);
1128c2ecf20Sopenharmony_ci	if (ret < 0)
1138c2ecf20Sopenharmony_ci		dev_err(&cm_data->client->dev, "Error writing reg_conf\n");
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic int cm3323_set_it_bits(struct cm3323_data *data, int val, int val2)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	int i, ret;
1198c2ecf20Sopenharmony_ci	u16 reg_conf;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(cm3323_int_time); i++) {
1228c2ecf20Sopenharmony_ci		if (val == cm3323_int_time[i].val &&
1238c2ecf20Sopenharmony_ci		    val2 == cm3323_int_time[i].val2) {
1248c2ecf20Sopenharmony_ci			reg_conf = data->reg_conf & ~CM3323_CONF_IT_MASK;
1258c2ecf20Sopenharmony_ci			reg_conf |= i << CM3323_CONF_IT_SHIFT;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci			ret = i2c_smbus_write_word_data(data->client,
1288c2ecf20Sopenharmony_ci							CM3323_CMD_CONF,
1298c2ecf20Sopenharmony_ci							reg_conf);
1308c2ecf20Sopenharmony_ci			if (ret < 0)
1318c2ecf20Sopenharmony_ci				return ret;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci			data->reg_conf = reg_conf;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci			return 0;
1368c2ecf20Sopenharmony_ci		}
1378c2ecf20Sopenharmony_ci	}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	return -EINVAL;
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic int cm3323_get_it_bits(struct cm3323_data *data)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	int bits;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	bits = (data->reg_conf & CM3323_CONF_IT_MASK) >>
1478c2ecf20Sopenharmony_ci		CM3323_CONF_IT_SHIFT;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	if (bits >= ARRAY_SIZE(cm3323_int_time))
1508c2ecf20Sopenharmony_ci		return -EINVAL;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	return bits;
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic int cm3323_read_raw(struct iio_dev *indio_dev,
1568c2ecf20Sopenharmony_ci			   struct iio_chan_spec const *chan, int *val,
1578c2ecf20Sopenharmony_ci			   int *val2, long mask)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	int ret;
1608c2ecf20Sopenharmony_ci	struct cm3323_data *data = iio_priv(indio_dev);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	switch (mask) {
1638c2ecf20Sopenharmony_ci	case IIO_CHAN_INFO_RAW:
1648c2ecf20Sopenharmony_ci		mutex_lock(&data->mutex);
1658c2ecf20Sopenharmony_ci		ret = i2c_smbus_read_word_data(data->client, chan->address);
1668c2ecf20Sopenharmony_ci		if (ret < 0) {
1678c2ecf20Sopenharmony_ci			mutex_unlock(&data->mutex);
1688c2ecf20Sopenharmony_ci			return ret;
1698c2ecf20Sopenharmony_ci		}
1708c2ecf20Sopenharmony_ci		*val = ret;
1718c2ecf20Sopenharmony_ci		mutex_unlock(&data->mutex);
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci		return IIO_VAL_INT;
1748c2ecf20Sopenharmony_ci	case IIO_CHAN_INFO_INT_TIME:
1758c2ecf20Sopenharmony_ci		mutex_lock(&data->mutex);
1768c2ecf20Sopenharmony_ci		ret = cm3323_get_it_bits(data);
1778c2ecf20Sopenharmony_ci		if (ret < 0) {
1788c2ecf20Sopenharmony_ci			mutex_unlock(&data->mutex);
1798c2ecf20Sopenharmony_ci			return ret;
1808c2ecf20Sopenharmony_ci		}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci		*val = cm3323_int_time[ret].val;
1838c2ecf20Sopenharmony_ci		*val2 = cm3323_int_time[ret].val2;
1848c2ecf20Sopenharmony_ci		mutex_unlock(&data->mutex);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci		return IIO_VAL_INT_PLUS_MICRO;
1878c2ecf20Sopenharmony_ci	default:
1888c2ecf20Sopenharmony_ci		return -EINVAL;
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic int cm3323_write_raw(struct iio_dev *indio_dev,
1938c2ecf20Sopenharmony_ci			    struct iio_chan_spec const *chan, int val,
1948c2ecf20Sopenharmony_ci			    int val2, long mask)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	struct cm3323_data *data = iio_priv(indio_dev);
1978c2ecf20Sopenharmony_ci	int ret;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	switch (mask) {
2008c2ecf20Sopenharmony_ci	case IIO_CHAN_INFO_INT_TIME:
2018c2ecf20Sopenharmony_ci		mutex_lock(&data->mutex);
2028c2ecf20Sopenharmony_ci		ret = cm3323_set_it_bits(data, val, val2);
2038c2ecf20Sopenharmony_ci		mutex_unlock(&data->mutex);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci		return ret;
2068c2ecf20Sopenharmony_ci	default:
2078c2ecf20Sopenharmony_ci		return -EINVAL;
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic const struct iio_info cm3323_info = {
2128c2ecf20Sopenharmony_ci	.read_raw	= cm3323_read_raw,
2138c2ecf20Sopenharmony_ci	.write_raw	= cm3323_write_raw,
2148c2ecf20Sopenharmony_ci	.attrs		= &cm3323_attribute_group,
2158c2ecf20Sopenharmony_ci};
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic int cm3323_probe(struct i2c_client *client,
2188c2ecf20Sopenharmony_ci			const struct i2c_device_id *id)
2198c2ecf20Sopenharmony_ci{
2208c2ecf20Sopenharmony_ci	struct cm3323_data *data;
2218c2ecf20Sopenharmony_ci	struct iio_dev *indio_dev;
2228c2ecf20Sopenharmony_ci	int ret;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
2258c2ecf20Sopenharmony_ci	if (!indio_dev)
2268c2ecf20Sopenharmony_ci		return -ENOMEM;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	data = iio_priv(indio_dev);
2298c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, indio_dev);
2308c2ecf20Sopenharmony_ci	data->client = client;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	mutex_init(&data->mutex);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	indio_dev->info = &cm3323_info;
2358c2ecf20Sopenharmony_ci	indio_dev->name = CM3323_DRV_NAME;
2368c2ecf20Sopenharmony_ci	indio_dev->channels = cm3323_channels;
2378c2ecf20Sopenharmony_ci	indio_dev->num_channels = ARRAY_SIZE(cm3323_channels);
2388c2ecf20Sopenharmony_ci	indio_dev->modes = INDIO_DIRECT_MODE;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	ret = cm3323_init(indio_dev);
2418c2ecf20Sopenharmony_ci	if (ret < 0) {
2428c2ecf20Sopenharmony_ci		dev_err(&client->dev, "cm3323 chip init failed\n");
2438c2ecf20Sopenharmony_ci		return ret;
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	ret = devm_add_action_or_reset(&client->dev, cm3323_disable, indio_dev);
2478c2ecf20Sopenharmony_ci	if (ret < 0)
2488c2ecf20Sopenharmony_ci		return ret;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	return devm_iio_device_register(&client->dev, indio_dev);
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistatic const struct i2c_device_id cm3323_id[] = {
2548c2ecf20Sopenharmony_ci	{"cm3323", 0},
2558c2ecf20Sopenharmony_ci	{}
2568c2ecf20Sopenharmony_ci};
2578c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, cm3323_id);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_cistatic struct i2c_driver cm3323_driver = {
2608c2ecf20Sopenharmony_ci	.driver = {
2618c2ecf20Sopenharmony_ci		.name = CM3323_DRV_NAME,
2628c2ecf20Sopenharmony_ci	},
2638c2ecf20Sopenharmony_ci	.probe		= cm3323_probe,
2648c2ecf20Sopenharmony_ci	.id_table	= cm3323_id,
2658c2ecf20Sopenharmony_ci};
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cimodule_i2c_driver(cm3323_driver);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ciMODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
2708c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Capella CM3323 Color Light Sensor driver");
2718c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
272