383 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * tcs3414.c - Support for TAOS TCS3414 digital color sensor4 *5 * Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>6 *7 * Digital color sensor with 16-bit channels for red, green, blue, clear);8 * 7-bit I2C slave address 0x39 (TCS3414) or 0x29, 0x49, 0x59 (TCS3413,9 * TCS3415, TCS3416, resp.)10 *11 * TODO: sync, interrupt support, thresholds, prescaler12 */13 14#include <linux/module.h>15#include <linux/i2c.h>16#include <linux/delay.h>17#include <linux/pm.h>18 19#include <linux/iio/iio.h>20#include <linux/iio/sysfs.h>21#include <linux/iio/trigger_consumer.h>22#include <linux/iio/buffer.h>23#include <linux/iio/triggered_buffer.h>24 25#define TCS3414_DRV_NAME "tcs3414"26 27#define TCS3414_COMMAND BIT(7)28#define TCS3414_COMMAND_WORD (TCS3414_COMMAND | BIT(5))29 30#define TCS3414_CONTROL (TCS3414_COMMAND | 0x00)31#define TCS3414_TIMING (TCS3414_COMMAND | 0x01)32#define TCS3414_ID (TCS3414_COMMAND | 0x04)33#define TCS3414_GAIN (TCS3414_COMMAND | 0x07)34#define TCS3414_DATA_GREEN (TCS3414_COMMAND_WORD | 0x10)35#define TCS3414_DATA_RED (TCS3414_COMMAND_WORD | 0x12)36#define TCS3414_DATA_BLUE (TCS3414_COMMAND_WORD | 0x14)37#define TCS3414_DATA_CLEAR (TCS3414_COMMAND_WORD | 0x16)38 39#define TCS3414_CONTROL_ADC_VALID BIT(4)40#define TCS3414_CONTROL_ADC_EN BIT(1)41#define TCS3414_CONTROL_POWER BIT(0)42 43#define TCS3414_INTEG_MASK GENMASK(1, 0)44#define TCS3414_INTEG_12MS 0x045#define TCS3414_INTEG_100MS 0x146#define TCS3414_INTEG_400MS 0x247 48#define TCS3414_GAIN_MASK GENMASK(5, 4)49#define TCS3414_GAIN_SHIFT 450 51struct tcs3414_data {52 struct i2c_client *client;53 u8 control;54 u8 gain;55 u8 timing;56 /* Ensure timestamp is naturally aligned */57 struct {58 u16 chans[4];59 s64 timestamp __aligned(8);60 } scan;61};62 63#define TCS3414_CHANNEL(_color, _si, _addr) { \64 .type = IIO_INTENSITY, \65 .modified = 1, \66 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \67 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \68 BIT(IIO_CHAN_INFO_INT_TIME), \69 .channel2 = IIO_MOD_LIGHT_##_color, \70 .address = _addr, \71 .scan_index = _si, \72 .scan_type = { \73 .sign = 'u', \74 .realbits = 16, \75 .storagebits = 16, \76 .endianness = IIO_CPU, \77 }, \78}79 80/* scale factors: 1/gain */81static const int tcs3414_scales[][2] = {82 {1, 0}, {0, 250000}, {0, 62500}, {0, 15625}83};84 85/* integration time in ms */86static const int tcs3414_times[] = { 12, 100, 400 };87 88static const struct iio_chan_spec tcs3414_channels[] = {89 TCS3414_CHANNEL(GREEN, 0, TCS3414_DATA_GREEN),90 TCS3414_CHANNEL(RED, 1, TCS3414_DATA_RED),91 TCS3414_CHANNEL(BLUE, 2, TCS3414_DATA_BLUE),92 TCS3414_CHANNEL(CLEAR, 3, TCS3414_DATA_CLEAR),93 IIO_CHAN_SOFT_TIMESTAMP(4),94};95 96static int tcs3414_req_data(struct tcs3414_data *data)97{98 int tries = 25;99 int ret;100 101 ret = i2c_smbus_write_byte_data(data->client, TCS3414_CONTROL,102 data->control | TCS3414_CONTROL_ADC_EN);103 if (ret < 0)104 return ret;105 106 while (tries--) {107 ret = i2c_smbus_read_byte_data(data->client, TCS3414_CONTROL);108 if (ret < 0)109 return ret;110 if (ret & TCS3414_CONTROL_ADC_VALID)111 break;112 msleep(20);113 }114 115 ret = i2c_smbus_write_byte_data(data->client, TCS3414_CONTROL,116 data->control);117 if (ret < 0)118 return ret;119 120 if (tries < 0) {121 dev_err(&data->client->dev, "data not ready\n");122 return -EIO;123 }124 125 return 0;126}127 128static int tcs3414_read_raw(struct iio_dev *indio_dev,129 struct iio_chan_spec const *chan,130 int *val, int *val2, long mask)131{132 struct tcs3414_data *data = iio_priv(indio_dev);133 int i, ret;134 135 switch (mask) {136 case IIO_CHAN_INFO_RAW:137 ret = iio_device_claim_direct_mode(indio_dev);138 if (ret)139 return ret;140 ret = tcs3414_req_data(data);141 if (ret < 0) {142 iio_device_release_direct_mode(indio_dev);143 return ret;144 }145 ret = i2c_smbus_read_word_data(data->client, chan->address);146 iio_device_release_direct_mode(indio_dev);147 if (ret < 0)148 return ret;149 *val = ret;150 return IIO_VAL_INT;151 case IIO_CHAN_INFO_SCALE:152 i = (data->gain & TCS3414_GAIN_MASK) >> TCS3414_GAIN_SHIFT;153 *val = tcs3414_scales[i][0];154 *val2 = tcs3414_scales[i][1];155 return IIO_VAL_INT_PLUS_MICRO;156 case IIO_CHAN_INFO_INT_TIME:157 *val = 0;158 *val2 = tcs3414_times[data->timing & TCS3414_INTEG_MASK] * 1000;159 return IIO_VAL_INT_PLUS_MICRO;160 }161 return -EINVAL;162}163 164static int tcs3414_write_raw(struct iio_dev *indio_dev,165 struct iio_chan_spec const *chan,166 int val, int val2, long mask)167{168 struct tcs3414_data *data = iio_priv(indio_dev);169 int i;170 171 switch (mask) {172 case IIO_CHAN_INFO_SCALE:173 for (i = 0; i < ARRAY_SIZE(tcs3414_scales); i++) {174 if (val == tcs3414_scales[i][0] &&175 val2 == tcs3414_scales[i][1]) {176 data->gain &= ~TCS3414_GAIN_MASK;177 data->gain |= i << TCS3414_GAIN_SHIFT;178 return i2c_smbus_write_byte_data(179 data->client, TCS3414_GAIN,180 data->gain);181 }182 }183 return -EINVAL;184 case IIO_CHAN_INFO_INT_TIME:185 if (val != 0)186 return -EINVAL;187 for (i = 0; i < ARRAY_SIZE(tcs3414_times); i++) {188 if (val2 == tcs3414_times[i] * 1000) {189 data->timing &= ~TCS3414_INTEG_MASK;190 data->timing |= i;191 return i2c_smbus_write_byte_data(192 data->client, TCS3414_TIMING,193 data->timing);194 }195 }196 return -EINVAL;197 default:198 return -EINVAL;199 }200}201 202static irqreturn_t tcs3414_trigger_handler(int irq, void *p)203{204 struct iio_poll_func *pf = p;205 struct iio_dev *indio_dev = pf->indio_dev;206 struct tcs3414_data *data = iio_priv(indio_dev);207 int i, j = 0;208 209 iio_for_each_active_channel(indio_dev, i) {210 int ret = i2c_smbus_read_word_data(data->client,211 TCS3414_DATA_GREEN + 2*i);212 if (ret < 0)213 goto done;214 215 data->scan.chans[j++] = ret;216 }217 218 iio_push_to_buffers_with_timestamp(indio_dev, &data->scan,219 iio_get_time_ns(indio_dev));220 221done:222 iio_trigger_notify_done(indio_dev->trig);223 224 return IRQ_HANDLED;225}226 227static IIO_CONST_ATTR(scale_available, "1 0.25 0.0625 0.015625");228static IIO_CONST_ATTR_INT_TIME_AVAIL("0.012 0.1 0.4");229 230static struct attribute *tcs3414_attributes[] = {231 &iio_const_attr_scale_available.dev_attr.attr,232 &iio_const_attr_integration_time_available.dev_attr.attr,233 NULL234};235 236static const struct attribute_group tcs3414_attribute_group = {237 .attrs = tcs3414_attributes,238};239 240static const struct iio_info tcs3414_info = {241 .read_raw = tcs3414_read_raw,242 .write_raw = tcs3414_write_raw,243 .attrs = &tcs3414_attribute_group,244};245 246static int tcs3414_buffer_postenable(struct iio_dev *indio_dev)247{248 struct tcs3414_data *data = iio_priv(indio_dev);249 250 data->control |= TCS3414_CONTROL_ADC_EN;251 return i2c_smbus_write_byte_data(data->client, TCS3414_CONTROL,252 data->control);253}254 255static int tcs3414_buffer_predisable(struct iio_dev *indio_dev)256{257 struct tcs3414_data *data = iio_priv(indio_dev);258 259 data->control &= ~TCS3414_CONTROL_ADC_EN;260 return i2c_smbus_write_byte_data(data->client, TCS3414_CONTROL,261 data->control);262}263 264static const struct iio_buffer_setup_ops tcs3414_buffer_setup_ops = {265 .postenable = tcs3414_buffer_postenable,266 .predisable = tcs3414_buffer_predisable,267};268 269static int tcs3414_powerdown(struct tcs3414_data *data)270{271 return i2c_smbus_write_byte_data(data->client, TCS3414_CONTROL,272 data->control & ~(TCS3414_CONTROL_POWER |273 TCS3414_CONTROL_ADC_EN));274}275 276static void tcs3414_powerdown_cleanup(void *data)277{278 tcs3414_powerdown(data);279}280 281static int tcs3414_probe(struct i2c_client *client)282{283 struct tcs3414_data *data;284 struct iio_dev *indio_dev;285 int ret;286 287 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));288 if (indio_dev == NULL)289 return -ENOMEM;290 291 data = iio_priv(indio_dev);292 i2c_set_clientdata(client, indio_dev);293 data->client = client;294 295 indio_dev->info = &tcs3414_info;296 indio_dev->name = TCS3414_DRV_NAME;297 indio_dev->channels = tcs3414_channels;298 indio_dev->num_channels = ARRAY_SIZE(tcs3414_channels);299 indio_dev->modes = INDIO_DIRECT_MODE;300 301 ret = i2c_smbus_read_byte_data(data->client, TCS3414_ID);302 if (ret < 0)303 return ret;304 305 switch (ret & 0xf0) {306 case 0x00:307 dev_info(&client->dev, "TCS3404 found\n");308 break;309 case 0x10:310 dev_info(&client->dev, "TCS3413/14/15/16 found\n");311 break;312 default:313 return -ENODEV;314 }315 316 data->control = TCS3414_CONTROL_POWER;317 ret = i2c_smbus_write_byte_data(data->client, TCS3414_CONTROL,318 data->control);319 if (ret < 0)320 return ret;321 322 ret = devm_add_action_or_reset(&client->dev, tcs3414_powerdown_cleanup,323 data);324 if (ret < 0)325 return ret;326 327 data->timing = TCS3414_INTEG_12MS; /* free running */328 ret = i2c_smbus_write_byte_data(data->client, TCS3414_TIMING,329 data->timing);330 if (ret < 0)331 return ret;332 333 ret = i2c_smbus_read_byte_data(data->client, TCS3414_GAIN);334 if (ret < 0)335 return ret;336 data->gain = ret;337 338 ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, NULL,339 tcs3414_trigger_handler, &tcs3414_buffer_setup_ops);340 if (ret < 0)341 return ret;342 343 return devm_iio_device_register(&client->dev, indio_dev);344}345 346static int tcs3414_suspend(struct device *dev)347{348 struct tcs3414_data *data = iio_priv(i2c_get_clientdata(349 to_i2c_client(dev)));350 return tcs3414_powerdown(data);351}352 353static int tcs3414_resume(struct device *dev)354{355 struct tcs3414_data *data = iio_priv(i2c_get_clientdata(356 to_i2c_client(dev)));357 return i2c_smbus_write_byte_data(data->client, TCS3414_CONTROL,358 data->control);359}360 361static DEFINE_SIMPLE_DEV_PM_OPS(tcs3414_pm_ops, tcs3414_suspend,362 tcs3414_resume);363 364static const struct i2c_device_id tcs3414_id[] = {365 { "tcs3414" },366 { }367};368MODULE_DEVICE_TABLE(i2c, tcs3414_id);369 370static struct i2c_driver tcs3414_driver = {371 .driver = {372 .name = TCS3414_DRV_NAME,373 .pm = pm_sleep_ptr(&tcs3414_pm_ops),374 },375 .probe = tcs3414_probe,376 .id_table = tcs3414_id,377};378module_i2c_driver(tcs3414_driver);379 380MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");381MODULE_DESCRIPTION("TCS3414 digital color sensors driver");382MODULE_LICENSE("GPL");383