brintos

brintos / linux-shallow public Read only

0
0
Text · 9.5 KiB · 4fecdf1 Raw
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