18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * adjd_s311.c - Support for ADJD-S311-CR999 digital color sensor 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012 Peter Meerwald <pmeerw@pmeerw.net> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * driver for ADJD-S311-CR999 digital color sensor (10-bit channels for 88c2ecf20Sopenharmony_ci * red, green, blue, clear); 7-bit I2C slave address 0x74 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * limitations: no calibration, no offset mode, no sleep mode 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/i2c.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/delay.h> 188c2ecf20Sopenharmony_ci#include <linux/bitmap.h> 198c2ecf20Sopenharmony_ci#include <linux/err.h> 208c2ecf20Sopenharmony_ci#include <linux/irq.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 238c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h> 248c2ecf20Sopenharmony_ci#include <linux/iio/trigger_consumer.h> 258c2ecf20Sopenharmony_ci#include <linux/iio/buffer.h> 268c2ecf20Sopenharmony_ci#include <linux/iio/triggered_buffer.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define ADJD_S311_DRV_NAME "adjd_s311" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define ADJD_S311_CTRL 0x00 318c2ecf20Sopenharmony_ci#define ADJD_S311_CONFIG 0x01 328c2ecf20Sopenharmony_ci#define ADJD_S311_CAP_RED 0x06 338c2ecf20Sopenharmony_ci#define ADJD_S311_CAP_GREEN 0x07 348c2ecf20Sopenharmony_ci#define ADJD_S311_CAP_BLUE 0x08 358c2ecf20Sopenharmony_ci#define ADJD_S311_CAP_CLEAR 0x09 368c2ecf20Sopenharmony_ci#define ADJD_S311_INT_RED 0x0a 378c2ecf20Sopenharmony_ci#define ADJD_S311_INT_GREEN 0x0c 388c2ecf20Sopenharmony_ci#define ADJD_S311_INT_BLUE 0x0e 398c2ecf20Sopenharmony_ci#define ADJD_S311_INT_CLEAR 0x10 408c2ecf20Sopenharmony_ci#define ADJD_S311_DATA_RED 0x40 418c2ecf20Sopenharmony_ci#define ADJD_S311_DATA_GREEN 0x42 428c2ecf20Sopenharmony_ci#define ADJD_S311_DATA_BLUE 0x44 438c2ecf20Sopenharmony_ci#define ADJD_S311_DATA_CLEAR 0x46 448c2ecf20Sopenharmony_ci#define ADJD_S311_OFFSET_RED 0x48 458c2ecf20Sopenharmony_ci#define ADJD_S311_OFFSET_GREEN 0x49 468c2ecf20Sopenharmony_ci#define ADJD_S311_OFFSET_BLUE 0x4a 478c2ecf20Sopenharmony_ci#define ADJD_S311_OFFSET_CLEAR 0x4b 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#define ADJD_S311_CTRL_GOFS 0x02 508c2ecf20Sopenharmony_ci#define ADJD_S311_CTRL_GSSR 0x01 518c2ecf20Sopenharmony_ci#define ADJD_S311_CAP_MASK 0x0f 528c2ecf20Sopenharmony_ci#define ADJD_S311_INT_MASK 0x0fff 538c2ecf20Sopenharmony_ci#define ADJD_S311_DATA_MASK 0x03ff 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistruct adjd_s311_data { 568c2ecf20Sopenharmony_ci struct i2c_client *client; 578c2ecf20Sopenharmony_ci u16 *buffer; 588c2ecf20Sopenharmony_ci}; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cienum adjd_s311_channel_idx { 618c2ecf20Sopenharmony_ci IDX_RED, IDX_GREEN, IDX_BLUE, IDX_CLEAR 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define ADJD_S311_DATA_REG(chan) (ADJD_S311_DATA_RED + (chan) * 2) 658c2ecf20Sopenharmony_ci#define ADJD_S311_INT_REG(chan) (ADJD_S311_INT_RED + (chan) * 2) 668c2ecf20Sopenharmony_ci#define ADJD_S311_CAP_REG(chan) (ADJD_S311_CAP_RED + (chan)) 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic int adjd_s311_req_data(struct iio_dev *indio_dev) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct adjd_s311_data *data = iio_priv(indio_dev); 718c2ecf20Sopenharmony_ci int tries = 10; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci int ret = i2c_smbus_write_byte_data(data->client, ADJD_S311_CTRL, 748c2ecf20Sopenharmony_ci ADJD_S311_CTRL_GSSR); 758c2ecf20Sopenharmony_ci if (ret < 0) 768c2ecf20Sopenharmony_ci return ret; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci while (tries--) { 798c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, ADJD_S311_CTRL); 808c2ecf20Sopenharmony_ci if (ret < 0) 818c2ecf20Sopenharmony_ci return ret; 828c2ecf20Sopenharmony_ci if (!(ret & ADJD_S311_CTRL_GSSR)) 838c2ecf20Sopenharmony_ci break; 848c2ecf20Sopenharmony_ci msleep(20); 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (tries < 0) { 888c2ecf20Sopenharmony_ci dev_err(&data->client->dev, 898c2ecf20Sopenharmony_ci "adjd_s311_req_data() failed, data not ready\n"); 908c2ecf20Sopenharmony_ci return -EIO; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci return 0; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic int adjd_s311_read_data(struct iio_dev *indio_dev, u8 reg, int *val) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct adjd_s311_data *data = iio_priv(indio_dev); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci int ret = adjd_s311_req_data(indio_dev); 1018c2ecf20Sopenharmony_ci if (ret < 0) 1028c2ecf20Sopenharmony_ci return ret; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_data(data->client, reg); 1058c2ecf20Sopenharmony_ci if (ret < 0) 1068c2ecf20Sopenharmony_ci return ret; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci *val = ret & ADJD_S311_DATA_MASK; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci return 0; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic irqreturn_t adjd_s311_trigger_handler(int irq, void *p) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci struct iio_poll_func *pf = p; 1168c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = pf->indio_dev; 1178c2ecf20Sopenharmony_ci struct adjd_s311_data *data = iio_priv(indio_dev); 1188c2ecf20Sopenharmony_ci s64 time_ns = iio_get_time_ns(indio_dev); 1198c2ecf20Sopenharmony_ci int i, j = 0; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci int ret = adjd_s311_req_data(indio_dev); 1228c2ecf20Sopenharmony_ci if (ret < 0) 1238c2ecf20Sopenharmony_ci goto done; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci for_each_set_bit(i, indio_dev->active_scan_mask, 1268c2ecf20Sopenharmony_ci indio_dev->masklength) { 1278c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_data(data->client, 1288c2ecf20Sopenharmony_ci ADJD_S311_DATA_REG(i)); 1298c2ecf20Sopenharmony_ci if (ret < 0) 1308c2ecf20Sopenharmony_ci goto done; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci data->buffer[j++] = ret & ADJD_S311_DATA_MASK; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, time_ns); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cidone: 1388c2ecf20Sopenharmony_ci iio_trigger_notify_done(indio_dev->trig); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci#define ADJD_S311_CHANNEL(_color, _scan_idx) { \ 1448c2ecf20Sopenharmony_ci .type = IIO_INTENSITY, \ 1458c2ecf20Sopenharmony_ci .modified = 1, \ 1468c2ecf20Sopenharmony_ci .address = (IDX_##_color), \ 1478c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ 1488c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_HARDWAREGAIN) | \ 1498c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME), \ 1508c2ecf20Sopenharmony_ci .channel2 = (IIO_MOD_LIGHT_##_color), \ 1518c2ecf20Sopenharmony_ci .scan_index = (_scan_idx), \ 1528c2ecf20Sopenharmony_ci .scan_type = { \ 1538c2ecf20Sopenharmony_ci .sign = 'u', \ 1548c2ecf20Sopenharmony_ci .realbits = 10, \ 1558c2ecf20Sopenharmony_ci .storagebits = 16, \ 1568c2ecf20Sopenharmony_ci .endianness = IIO_CPU, \ 1578c2ecf20Sopenharmony_ci }, \ 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic const struct iio_chan_spec adjd_s311_channels[] = { 1618c2ecf20Sopenharmony_ci ADJD_S311_CHANNEL(RED, 0), 1628c2ecf20Sopenharmony_ci ADJD_S311_CHANNEL(GREEN, 1), 1638c2ecf20Sopenharmony_ci ADJD_S311_CHANNEL(BLUE, 2), 1648c2ecf20Sopenharmony_ci ADJD_S311_CHANNEL(CLEAR, 3), 1658c2ecf20Sopenharmony_ci IIO_CHAN_SOFT_TIMESTAMP(4), 1668c2ecf20Sopenharmony_ci}; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_cistatic int adjd_s311_read_raw(struct iio_dev *indio_dev, 1698c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 1708c2ecf20Sopenharmony_ci int *val, int *val2, long mask) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct adjd_s311_data *data = iio_priv(indio_dev); 1738c2ecf20Sopenharmony_ci int ret; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci switch (mask) { 1768c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 1778c2ecf20Sopenharmony_ci ret = adjd_s311_read_data(indio_dev, 1788c2ecf20Sopenharmony_ci ADJD_S311_DATA_REG(chan->address), val); 1798c2ecf20Sopenharmony_ci if (ret < 0) 1808c2ecf20Sopenharmony_ci return ret; 1818c2ecf20Sopenharmony_ci return IIO_VAL_INT; 1828c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_HARDWAREGAIN: 1838c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, 1848c2ecf20Sopenharmony_ci ADJD_S311_CAP_REG(chan->address)); 1858c2ecf20Sopenharmony_ci if (ret < 0) 1868c2ecf20Sopenharmony_ci return ret; 1878c2ecf20Sopenharmony_ci *val = ret & ADJD_S311_CAP_MASK; 1888c2ecf20Sopenharmony_ci return IIO_VAL_INT; 1898c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_INT_TIME: 1908c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_data(data->client, 1918c2ecf20Sopenharmony_ci ADJD_S311_INT_REG(chan->address)); 1928c2ecf20Sopenharmony_ci if (ret < 0) 1938c2ecf20Sopenharmony_ci return ret; 1948c2ecf20Sopenharmony_ci *val = 0; 1958c2ecf20Sopenharmony_ci /* 1968c2ecf20Sopenharmony_ci * not documented, based on measurement: 1978c2ecf20Sopenharmony_ci * 4095 LSBs correspond to roughly 4 ms 1988c2ecf20Sopenharmony_ci */ 1998c2ecf20Sopenharmony_ci *val2 = ret & ADJD_S311_INT_MASK; 2008c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci return -EINVAL; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic int adjd_s311_write_raw(struct iio_dev *indio_dev, 2068c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 2078c2ecf20Sopenharmony_ci int val, int val2, long mask) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci struct adjd_s311_data *data = iio_priv(indio_dev); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci switch (mask) { 2128c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_HARDWAREGAIN: 2138c2ecf20Sopenharmony_ci if (val < 0 || val > ADJD_S311_CAP_MASK) 2148c2ecf20Sopenharmony_ci return -EINVAL; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci return i2c_smbus_write_byte_data(data->client, 2178c2ecf20Sopenharmony_ci ADJD_S311_CAP_REG(chan->address), val); 2188c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_INT_TIME: 2198c2ecf20Sopenharmony_ci if (val != 0 || val2 < 0 || val2 > ADJD_S311_INT_MASK) 2208c2ecf20Sopenharmony_ci return -EINVAL; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci return i2c_smbus_write_word_data(data->client, 2238c2ecf20Sopenharmony_ci ADJD_S311_INT_REG(chan->address), val2); 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci return -EINVAL; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic int adjd_s311_update_scan_mode(struct iio_dev *indio_dev, 2298c2ecf20Sopenharmony_ci const unsigned long *scan_mask) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct adjd_s311_data *data = iio_priv(indio_dev); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci kfree(data->buffer); 2348c2ecf20Sopenharmony_ci data->buffer = kmalloc(indio_dev->scan_bytes, GFP_KERNEL); 2358c2ecf20Sopenharmony_ci if (data->buffer == NULL) 2368c2ecf20Sopenharmony_ci return -ENOMEM; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci return 0; 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic const struct iio_info adjd_s311_info = { 2428c2ecf20Sopenharmony_ci .read_raw = adjd_s311_read_raw, 2438c2ecf20Sopenharmony_ci .write_raw = adjd_s311_write_raw, 2448c2ecf20Sopenharmony_ci .update_scan_mode = adjd_s311_update_scan_mode, 2458c2ecf20Sopenharmony_ci}; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic int adjd_s311_probe(struct i2c_client *client, 2488c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci struct adjd_s311_data *data; 2518c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 2528c2ecf20Sopenharmony_ci int err; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 2558c2ecf20Sopenharmony_ci if (indio_dev == NULL) 2568c2ecf20Sopenharmony_ci return -ENOMEM; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci data = iio_priv(indio_dev); 2598c2ecf20Sopenharmony_ci i2c_set_clientdata(client, indio_dev); 2608c2ecf20Sopenharmony_ci data->client = client; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci indio_dev->info = &adjd_s311_info; 2638c2ecf20Sopenharmony_ci indio_dev->name = ADJD_S311_DRV_NAME; 2648c2ecf20Sopenharmony_ci indio_dev->channels = adjd_s311_channels; 2658c2ecf20Sopenharmony_ci indio_dev->num_channels = ARRAY_SIZE(adjd_s311_channels); 2668c2ecf20Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci err = iio_triggered_buffer_setup(indio_dev, NULL, 2698c2ecf20Sopenharmony_ci adjd_s311_trigger_handler, NULL); 2708c2ecf20Sopenharmony_ci if (err < 0) 2718c2ecf20Sopenharmony_ci return err; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci err = iio_device_register(indio_dev); 2748c2ecf20Sopenharmony_ci if (err) 2758c2ecf20Sopenharmony_ci goto exit_unreg_buffer; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci dev_info(&client->dev, "ADJD-S311 color sensor registered\n"); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci return 0; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ciexit_unreg_buffer: 2828c2ecf20Sopenharmony_ci iio_triggered_buffer_cleanup(indio_dev); 2838c2ecf20Sopenharmony_ci return err; 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic int adjd_s311_remove(struct i2c_client *client) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = i2c_get_clientdata(client); 2898c2ecf20Sopenharmony_ci struct adjd_s311_data *data = iio_priv(indio_dev); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci iio_device_unregister(indio_dev); 2928c2ecf20Sopenharmony_ci iio_triggered_buffer_cleanup(indio_dev); 2938c2ecf20Sopenharmony_ci kfree(data->buffer); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci return 0; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic const struct i2c_device_id adjd_s311_id[] = { 2998c2ecf20Sopenharmony_ci { "adjd_s311", 0 }, 3008c2ecf20Sopenharmony_ci { } 3018c2ecf20Sopenharmony_ci}; 3028c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, adjd_s311_id); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic struct i2c_driver adjd_s311_driver = { 3058c2ecf20Sopenharmony_ci .driver = { 3068c2ecf20Sopenharmony_ci .name = ADJD_S311_DRV_NAME, 3078c2ecf20Sopenharmony_ci }, 3088c2ecf20Sopenharmony_ci .probe = adjd_s311_probe, 3098c2ecf20Sopenharmony_ci .remove = adjd_s311_remove, 3108c2ecf20Sopenharmony_ci .id_table = adjd_s311_id, 3118c2ecf20Sopenharmony_ci}; 3128c2ecf20Sopenharmony_cimodule_i2c_driver(adjd_s311_driver); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ciMODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); 3158c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ADJD-S311 color sensor"); 3168c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 317