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