brintos

brintos / linux-shallow public Read only

0
0
Text · 12.1 KiB · 11f2ab4 Raw
518 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * apds9300.c - IIO driver for Avago APDS9300 ambient light sensor4 *5 * Copyright 2013 Oleksandr Kravchenko <o.v.kravchenko@globallogic.com>6 */7 8#include <linux/module.h>9#include <linux/slab.h>10#include <linux/pm.h>11#include <linux/i2c.h>12#include <linux/err.h>13#include <linux/mutex.h>14#include <linux/interrupt.h>15#include <linux/iio/iio.h>16#include <linux/iio/sysfs.h>17#include <linux/iio/events.h>18 19#define APDS9300_DRV_NAME "apds9300"20#define APDS9300_IRQ_NAME "apds9300_event"21 22/* Command register bits */23#define APDS9300_CMD	BIT(7) /* Select command register. Must write as 1 */24#define APDS9300_WORD	BIT(5) /* I2C write/read: if 1 word, if 0 byte */25#define APDS9300_CLEAR	BIT(6) /* Interrupt clear. Clears pending interrupt */26 27/* Register set */28#define APDS9300_CONTROL	0x00 /* Control of basic functions */29#define APDS9300_THRESHLOWLOW	0x02 /* Low byte of low interrupt threshold */30#define APDS9300_THRESHHIGHLOW	0x04 /* Low byte of high interrupt threshold */31#define APDS9300_INTERRUPT	0x06 /* Interrupt control */32#define APDS9300_DATA0LOW	0x0c /* Low byte of ADC channel 0 */33#define APDS9300_DATA1LOW	0x0e /* Low byte of ADC channel 1 */34 35/* Power on/off value for APDS9300_CONTROL register */36#define APDS9300_POWER_ON	0x0337#define APDS9300_POWER_OFF	0x0038 39/* Interrupts */40#define APDS9300_INTR_ENABLE	0x1041/* Interrupt Persist Function: Any value outside of threshold range */42#define APDS9300_THRESH_INTR	0x0143 44#define APDS9300_THRESH_MAX	0xffff /* Max threshold value */45 46struct apds9300_data {47	struct i2c_client *client;48	struct mutex mutex;49	int power_state;50	int thresh_low;51	int thresh_hi;52	int intr_en;53};54 55/* Lux calculation */56 57/* Calculated values 1000 * (CH1/CH0)^1.4 for CH1/CH0 from 0 to 0.52 */58static const u16 apds9300_lux_ratio[] = {59	0, 2, 4, 7, 11, 15, 19, 24, 29, 34, 40, 45, 51, 57, 64, 70, 77, 84, 91,60	98, 105, 112, 120, 128, 136, 144, 152, 160, 168, 177, 185, 194, 203,61	212, 221, 230, 239, 249, 258, 268, 277, 287, 297, 307, 317, 327, 337,62	347, 358, 368, 379, 390, 400,63};64 65static unsigned long apds9300_calculate_lux(u16 ch0, u16 ch1)66{67	unsigned long lux, tmp;68 69	/* avoid division by zero */70	if (ch0 == 0)71		return 0;72 73	tmp = DIV_ROUND_UP(ch1 * 100, ch0);74	if (tmp <= 52) {75		lux = 3150 * ch0 - (unsigned long)DIV_ROUND_UP_ULL(ch076				* apds9300_lux_ratio[tmp] * 5930ull, 1000);77	} else if (tmp <= 65) {78		lux = 2290 * ch0 - 2910 * ch1;79	} else if (tmp <= 80) {80		lux = 1570 * ch0 - 1800 * ch1;81	} else if (tmp <= 130) {82		lux = 338 * ch0 - 260 * ch1;83	} else {84		lux = 0;85	}86 87	return lux / 100000;88}89 90static int apds9300_get_adc_val(struct apds9300_data *data, int adc_number)91{92	int ret;93	u8 flags = APDS9300_CMD | APDS9300_WORD;94 95	if (!data->power_state)96		return -EBUSY;97 98	/* Select ADC0 or ADC1 data register */99	flags |= adc_number ? APDS9300_DATA1LOW : APDS9300_DATA0LOW;100 101	ret = i2c_smbus_read_word_data(data->client, flags);102	if (ret < 0)103		dev_err(&data->client->dev,104			"failed to read ADC%d value\n", adc_number);105 106	return ret;107}108 109static int apds9300_set_thresh_low(struct apds9300_data *data, int value)110{111	int ret;112 113	if (!data->power_state)114		return -EBUSY;115 116	if (value > APDS9300_THRESH_MAX)117		return -EINVAL;118 119	ret = i2c_smbus_write_word_data(data->client, APDS9300_THRESHLOWLOW120			| APDS9300_CMD | APDS9300_WORD, value);121	if (ret) {122		dev_err(&data->client->dev, "failed to set thresh_low\n");123		return ret;124	}125	data->thresh_low = value;126 127	return 0;128}129 130static int apds9300_set_thresh_hi(struct apds9300_data *data, int value)131{132	int ret;133 134	if (!data->power_state)135		return -EBUSY;136 137	if (value > APDS9300_THRESH_MAX)138		return -EINVAL;139 140	ret = i2c_smbus_write_word_data(data->client, APDS9300_THRESHHIGHLOW141			| APDS9300_CMD | APDS9300_WORD, value);142	if (ret) {143		dev_err(&data->client->dev, "failed to set thresh_hi\n");144		return ret;145	}146	data->thresh_hi = value;147 148	return 0;149}150 151static int apds9300_set_intr_state(struct apds9300_data *data, int state)152{153	int ret;154	u8 cmd;155 156	if (!data->power_state)157		return -EBUSY;158 159	cmd = state ? APDS9300_INTR_ENABLE | APDS9300_THRESH_INTR : 0x00;160	ret = i2c_smbus_write_byte_data(data->client,161			APDS9300_INTERRUPT | APDS9300_CMD, cmd);162	if (ret) {163		dev_err(&data->client->dev,164			"failed to set interrupt state %d\n", state);165		return ret;166	}167	data->intr_en = state;168 169	return 0;170}171 172static int apds9300_set_power_state(struct apds9300_data *data, int state)173{174	int ret;175	u8 cmd;176 177	cmd = state ? APDS9300_POWER_ON : APDS9300_POWER_OFF;178	ret = i2c_smbus_write_byte_data(data->client,179			APDS9300_CONTROL | APDS9300_CMD, cmd);180	if (ret) {181		dev_err(&data->client->dev,182			"failed to set power state %d\n", state);183		return ret;184	}185	data->power_state = state;186 187	return 0;188}189 190static void apds9300_clear_intr(struct apds9300_data *data)191{192	int ret;193 194	ret = i2c_smbus_write_byte(data->client, APDS9300_CLEAR | APDS9300_CMD);195	if (ret < 0)196		dev_err(&data->client->dev, "failed to clear interrupt\n");197}198 199static int apds9300_chip_init(struct apds9300_data *data)200{201	int ret;202 203	/* Need to set power off to ensure that the chip is off */204	ret = apds9300_set_power_state(data, 0);205	if (ret < 0)206		goto err;207	/*208	 * Probe the chip. To do so we try to power up the device and then to209	 * read back the 0x03 code210	 */211	ret = apds9300_set_power_state(data, 1);212	if (ret < 0)213		goto err;214	ret = i2c_smbus_read_byte_data(data->client,215			APDS9300_CONTROL | APDS9300_CMD);216	if (ret != APDS9300_POWER_ON) {217		ret = -ENODEV;218		goto err;219	}220	/*221	 * Disable interrupt to ensure thai it is doesn't enable222	 * i.e. after device soft reset223	 */224	ret = apds9300_set_intr_state(data, 0);225	if (ret < 0)226		goto err;227 228	return 0;229 230err:231	dev_err(&data->client->dev, "failed to init the chip\n");232	return ret;233}234 235static int apds9300_read_raw(struct iio_dev *indio_dev,236		struct iio_chan_spec const *chan, int *val, int *val2,237		long mask)238{239	int ch0, ch1, ret = -EINVAL;240	struct apds9300_data *data = iio_priv(indio_dev);241 242	mutex_lock(&data->mutex);243	switch (chan->type) {244	case IIO_LIGHT:245		ch0 = apds9300_get_adc_val(data, 0);246		if (ch0 < 0) {247			ret = ch0;248			break;249		}250		ch1 = apds9300_get_adc_val(data, 1);251		if (ch1 < 0) {252			ret = ch1;253			break;254		}255		*val = apds9300_calculate_lux(ch0, ch1);256		ret = IIO_VAL_INT;257		break;258	case IIO_INTENSITY:259		ret = apds9300_get_adc_val(data, chan->channel);260		if (ret < 0)261			break;262		*val = ret;263		ret = IIO_VAL_INT;264		break;265	default:266		break;267	}268	mutex_unlock(&data->mutex);269 270	return ret;271}272 273static int apds9300_read_thresh(struct iio_dev *indio_dev,274		const struct iio_chan_spec *chan, enum iio_event_type type,275		enum iio_event_direction dir, enum iio_event_info info,276		int *val, int *val2)277{278	struct apds9300_data *data = iio_priv(indio_dev);279 280	switch (dir) {281	case IIO_EV_DIR_RISING:282		*val = data->thresh_hi;283		break;284	case IIO_EV_DIR_FALLING:285		*val = data->thresh_low;286		break;287	default:288		return -EINVAL;289	}290 291	return IIO_VAL_INT;292}293 294static int apds9300_write_thresh(struct iio_dev *indio_dev,295		const struct iio_chan_spec *chan, enum iio_event_type type,296		enum iio_event_direction dir, enum iio_event_info info, int val,297		int val2)298{299	struct apds9300_data *data = iio_priv(indio_dev);300	int ret;301 302	mutex_lock(&data->mutex);303	if (dir == IIO_EV_DIR_RISING)304		ret = apds9300_set_thresh_hi(data, val);305	else306		ret = apds9300_set_thresh_low(data, val);307	mutex_unlock(&data->mutex);308 309	return ret;310}311 312static int apds9300_read_interrupt_config(struct iio_dev *indio_dev,313		const struct iio_chan_spec *chan,314		enum iio_event_type type,315		enum iio_event_direction dir)316{317	struct apds9300_data *data = iio_priv(indio_dev);318 319	return data->intr_en;320}321 322static int apds9300_write_interrupt_config(struct iio_dev *indio_dev,323		const struct iio_chan_spec *chan, enum iio_event_type type,324		enum iio_event_direction dir, int state)325{326	struct apds9300_data *data = iio_priv(indio_dev);327	int ret;328 329	mutex_lock(&data->mutex);330	ret = apds9300_set_intr_state(data, state);331	mutex_unlock(&data->mutex);332 333	return ret;334}335 336static const struct iio_info apds9300_info_no_irq = {337	.read_raw	= apds9300_read_raw,338};339 340static const struct iio_info apds9300_info = {341	.read_raw		= apds9300_read_raw,342	.read_event_value	= apds9300_read_thresh,343	.write_event_value	= apds9300_write_thresh,344	.read_event_config	= apds9300_read_interrupt_config,345	.write_event_config	= apds9300_write_interrupt_config,346};347 348static const struct iio_event_spec apds9300_event_spec[] = {349	{350		.type = IIO_EV_TYPE_THRESH,351		.dir = IIO_EV_DIR_RISING,352		.mask_separate = BIT(IIO_EV_INFO_VALUE) |353			BIT(IIO_EV_INFO_ENABLE),354	}, {355		.type = IIO_EV_TYPE_THRESH,356		.dir = IIO_EV_DIR_FALLING,357		.mask_separate = BIT(IIO_EV_INFO_VALUE) |358			BIT(IIO_EV_INFO_ENABLE),359	},360};361 362static const struct iio_chan_spec apds9300_channels[] = {363	{364		.type = IIO_LIGHT,365		.channel = 0,366		.indexed = true,367		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),368	}, {369		.type = IIO_INTENSITY,370		.channel = 0,371		.channel2 = IIO_MOD_LIGHT_BOTH,372		.indexed = true,373		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),374		.event_spec = apds9300_event_spec,375		.num_event_specs = ARRAY_SIZE(apds9300_event_spec),376	}, {377		.type = IIO_INTENSITY,378		.channel = 1,379		.channel2 = IIO_MOD_LIGHT_IR,380		.indexed = true,381		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),382	},383};384 385static irqreturn_t apds9300_interrupt_handler(int irq, void *private)386{387	struct iio_dev *dev_info = private;388	struct apds9300_data *data = iio_priv(dev_info);389 390	iio_push_event(dev_info,391		       IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, 0,392					    IIO_EV_TYPE_THRESH,393					    IIO_EV_DIR_EITHER),394		       iio_get_time_ns(dev_info));395 396	apds9300_clear_intr(data);397 398	return IRQ_HANDLED;399}400 401static int apds9300_probe(struct i2c_client *client)402{403	struct apds9300_data *data;404	struct iio_dev *indio_dev;405	int ret;406 407	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));408	if (!indio_dev)409		return -ENOMEM;410 411	data = iio_priv(indio_dev);412	i2c_set_clientdata(client, indio_dev);413	data->client = client;414 415	ret = apds9300_chip_init(data);416	if (ret < 0)417		goto err;418 419	mutex_init(&data->mutex);420 421	indio_dev->channels = apds9300_channels;422	indio_dev->num_channels = ARRAY_SIZE(apds9300_channels);423	indio_dev->name = APDS9300_DRV_NAME;424	indio_dev->modes = INDIO_DIRECT_MODE;425 426	if (client->irq)427		indio_dev->info = &apds9300_info;428	else429		indio_dev->info = &apds9300_info_no_irq;430 431	if (client->irq) {432		ret = devm_request_threaded_irq(&client->dev, client->irq,433				NULL, apds9300_interrupt_handler,434				IRQF_TRIGGER_FALLING | IRQF_ONESHOT,435				APDS9300_IRQ_NAME, indio_dev);436		if (ret) {437			dev_err(&client->dev, "irq request error %d\n", -ret);438			goto err;439		}440	}441 442	ret = iio_device_register(indio_dev);443	if (ret < 0)444		goto err;445 446	return 0;447 448err:449	/* Ensure that power off in case of error */450	apds9300_set_power_state(data, 0);451	return ret;452}453 454static void apds9300_remove(struct i2c_client *client)455{456	struct iio_dev *indio_dev = i2c_get_clientdata(client);457	struct apds9300_data *data = iio_priv(indio_dev);458 459	iio_device_unregister(indio_dev);460 461	/* Ensure that power off and interrupts are disabled */462	apds9300_set_intr_state(data, 0);463	apds9300_set_power_state(data, 0);464}465 466static int apds9300_suspend(struct device *dev)467{468	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));469	struct apds9300_data *data = iio_priv(indio_dev);470	int ret;471 472	mutex_lock(&data->mutex);473	ret = apds9300_set_power_state(data, 0);474	mutex_unlock(&data->mutex);475 476	return ret;477}478 479static int apds9300_resume(struct device *dev)480{481	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));482	struct apds9300_data *data = iio_priv(indio_dev);483	int ret;484 485	mutex_lock(&data->mutex);486	ret = apds9300_set_power_state(data, 1);487	mutex_unlock(&data->mutex);488 489	return ret;490}491 492static DEFINE_SIMPLE_DEV_PM_OPS(apds9300_pm_ops, apds9300_suspend,493				apds9300_resume);494 495static const struct i2c_device_id apds9300_id[] = {496	{ APDS9300_DRV_NAME },497	{ }498};499 500MODULE_DEVICE_TABLE(i2c, apds9300_id);501 502static struct i2c_driver apds9300_driver = {503	.driver = {504		.name	= APDS9300_DRV_NAME,505		.pm	= pm_sleep_ptr(&apds9300_pm_ops),506	},507	.probe		= apds9300_probe,508	.remove		= apds9300_remove,509	.id_table	= apds9300_id,510};511 512module_i2c_driver(apds9300_driver);513 514MODULE_AUTHOR("Kravchenko Oleksandr <o.v.kravchenko@globallogic.com>");515MODULE_AUTHOR("GlobalLogic inc.");516MODULE_DESCRIPTION("APDS9300 ambient light photo sensor driver");517MODULE_LICENSE("GPL");518