brintos

brintos / linux-shallow public Read only

0
0
Text · 17.0 KiB · 337a133 Raw
683 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * VCNL4035 Ambient Light and Proximity Sensor - 7-bit I2C slave address 0x604 *5 * Copyright (c) 2018, DENX Software Engineering GmbH6 * Author: Parthiban Nallathambi <pn@denx.de>7 *8 * TODO: Proximity9 */10#include <linux/bitops.h>11#include <linux/bitfield.h>12#include <linux/i2c.h>13#include <linux/module.h>14#include <linux/pm_runtime.h>15#include <linux/regmap.h>16 17#include <linux/iio/buffer.h>18#include <linux/iio/events.h>19#include <linux/iio/iio.h>20#include <linux/iio/sysfs.h>21#include <linux/iio/trigger.h>22#include <linux/iio/trigger_consumer.h>23#include <linux/iio/triggered_buffer.h>24 25#define VCNL4035_DRV_NAME	"vcnl4035"26#define VCNL4035_IRQ_NAME	"vcnl4035_event"27#define VCNL4035_REGMAP_NAME	"vcnl4035_regmap"28 29/* Device registers */30#define VCNL4035_ALS_CONF	0x0031#define VCNL4035_ALS_THDH	0x0132#define VCNL4035_ALS_THDL	0x0233#define VCNL4035_ALS_DATA	0x0B34#define VCNL4035_WHITE_DATA	0x0C35#define VCNL4035_INT_FLAG	0x0D36#define VCNL4035_DEV_ID		0x0E37 38/* Register masks */39#define VCNL4035_MODE_ALS_MASK		BIT(0)40#define VCNL4035_MODE_ALS_WHITE_CHAN	BIT(8)41#define VCNL4035_MODE_ALS_INT_MASK	BIT(1)42#define VCNL4035_ALS_IT_MASK		GENMASK(7, 5)43#define VCNL4035_ALS_PERS_MASK		GENMASK(3, 2)44#define VCNL4035_INT_ALS_IF_H_MASK	BIT(12)45#define VCNL4035_INT_ALS_IF_L_MASK	BIT(13)46#define VCNL4035_DEV_ID_MASK		GENMASK(7, 0)47 48/* Default values */49#define VCNL4035_MODE_ALS_ENABLE	BIT(0)50#define VCNL4035_MODE_ALS_DISABLE	0x0051#define VCNL4035_MODE_ALS_INT_ENABLE	BIT(1)52#define VCNL4035_MODE_ALS_INT_DISABLE	053#define VCNL4035_DEV_ID_VAL		0x8054#define VCNL4035_ALS_IT_DEFAULT		0x0155#define VCNL4035_ALS_PERS_DEFAULT	0x0056#define VCNL4035_ALS_THDH_DEFAULT	500057#define VCNL4035_ALS_THDL_DEFAULT	10058#define VCNL4035_SLEEP_DELAY_MS		200059 60struct vcnl4035_data {61	struct i2c_client *client;62	struct regmap *regmap;63	unsigned int als_it_val;64	unsigned int als_persistence;65	unsigned int als_thresh_low;66	unsigned int als_thresh_high;67	struct iio_trigger *drdy_trigger0;68};69 70static inline bool vcnl4035_is_triggered(struct vcnl4035_data *data)71{72	int ret;73	int reg;74 75	ret = regmap_read(data->regmap, VCNL4035_INT_FLAG, &reg);76	if (ret < 0)77		return false;78 79	return !!(reg &80		(VCNL4035_INT_ALS_IF_H_MASK | VCNL4035_INT_ALS_IF_L_MASK));81}82 83static irqreturn_t vcnl4035_drdy_irq_thread(int irq, void *private)84{85	struct iio_dev *indio_dev = private;86	struct vcnl4035_data *data = iio_priv(indio_dev);87 88	if (vcnl4035_is_triggered(data)) {89		iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_LIGHT,90							0,91							IIO_EV_TYPE_THRESH,92							IIO_EV_DIR_EITHER),93				iio_get_time_ns(indio_dev));94		iio_trigger_poll_nested(data->drdy_trigger0);95		return IRQ_HANDLED;96	}97 98	return IRQ_NONE;99}100 101/* Triggered buffer */102static irqreturn_t vcnl4035_trigger_consumer_handler(int irq, void *p)103{104	struct iio_poll_func *pf = p;105	struct iio_dev *indio_dev = pf->indio_dev;106	struct vcnl4035_data *data = iio_priv(indio_dev);107	/* Ensure naturally aligned timestamp */108	u8 buffer[ALIGN(sizeof(u16), sizeof(s64)) + sizeof(s64)]  __aligned(8);109	int ret;110 111	ret = regmap_read(data->regmap, VCNL4035_ALS_DATA, (int *)buffer);112	if (ret < 0) {113		dev_err(&data->client->dev,114			"Trigger consumer can't read from sensor.\n");115		goto fail_read;116	}117	iio_push_to_buffers_with_timestamp(indio_dev, buffer,118					iio_get_time_ns(indio_dev));119 120fail_read:121	iio_trigger_notify_done(indio_dev->trig);122 123	return IRQ_HANDLED;124}125 126static int vcnl4035_als_drdy_set_state(struct iio_trigger *trigger,127					bool enable_drdy)128{129	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trigger);130	struct vcnl4035_data *data = iio_priv(indio_dev);131	int val = enable_drdy ? VCNL4035_MODE_ALS_INT_ENABLE :132					VCNL4035_MODE_ALS_INT_DISABLE;133 134	return regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,135				 VCNL4035_MODE_ALS_INT_MASK,136				 val);137}138 139static const struct iio_trigger_ops vcnl4035_trigger_ops = {140	.validate_device = iio_trigger_validate_own_device,141	.set_trigger_state = vcnl4035_als_drdy_set_state,142};143 144static int vcnl4035_set_pm_runtime_state(struct vcnl4035_data *data, bool on)145{146	int ret;147	struct device *dev = &data->client->dev;148 149	if (on) {150		ret = pm_runtime_resume_and_get(dev);151	} else {152		pm_runtime_mark_last_busy(dev);153		ret = pm_runtime_put_autosuspend(dev);154	}155 156	return ret;157}158 159/*160 *	Device IT	INT Time (ms)	Scale (lux/step)161 *	000		50		0.064162 *	001		100		0.032163 *	010		200		0.016164 *	100		400		0.008165 *	101 - 111	800		0.004166 * Values are proportional, so ALS INT is selected for input due to167 * simplicity reason. Integration time value and scaling is168 * calculated based on device INT value169 *170 * Raw value needs to be scaled using ALS steps171 */172static int vcnl4035_read_raw(struct iio_dev *indio_dev,173			    struct iio_chan_spec const *chan, int *val,174			    int *val2, long mask)175{176	struct vcnl4035_data *data = iio_priv(indio_dev);177	int ret;178	int raw_data;179	unsigned int reg;180 181	switch (mask) {182	case IIO_CHAN_INFO_RAW:183		ret = vcnl4035_set_pm_runtime_state(data, true);184		if  (ret < 0)185			return ret;186 187		ret = iio_device_claim_direct_mode(indio_dev);188		if (!ret) {189			if (chan->channel)190				reg = VCNL4035_ALS_DATA;191			else192				reg = VCNL4035_WHITE_DATA;193			ret = regmap_read(data->regmap, reg, &raw_data);194			iio_device_release_direct_mode(indio_dev);195			if (!ret) {196				*val = raw_data;197				ret = IIO_VAL_INT;198			}199		}200		vcnl4035_set_pm_runtime_state(data, false);201		return ret;202	case IIO_CHAN_INFO_INT_TIME:203		*val = 50;204		if (data->als_it_val)205			*val = data->als_it_val * 100;206		return IIO_VAL_INT;207	case IIO_CHAN_INFO_SCALE:208		*val = 64;209		if (!data->als_it_val)210			*val2 = 1000;211		else212			*val2 = data->als_it_val * 2 * 1000;213		return IIO_VAL_FRACTIONAL;214	default:215		return -EINVAL;216	}217}218 219static int vcnl4035_write_raw(struct iio_dev *indio_dev,220				struct iio_chan_spec const *chan,221				int val, int val2, long mask)222{223	int ret;224	struct vcnl4035_data *data = iio_priv(indio_dev);225 226	switch (mask) {227	case IIO_CHAN_INFO_INT_TIME:228		if (val <= 0 || val > 800)229			return -EINVAL;230 231		ret = vcnl4035_set_pm_runtime_state(data, true);232		if  (ret < 0)233			return ret;234 235		ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,236					 VCNL4035_ALS_IT_MASK,237					 val / 100);238		if (!ret)239			data->als_it_val = val / 100;240 241		vcnl4035_set_pm_runtime_state(data, false);242		return ret;243	default:244		return -EINVAL;245	}246}247 248/* No direct ABI for persistence and threshold, so eventing */249static int vcnl4035_read_thresh(struct iio_dev *indio_dev,250		const struct iio_chan_spec *chan, enum iio_event_type type,251		enum iio_event_direction dir, enum iio_event_info info,252		int *val, int *val2)253{254	struct vcnl4035_data *data = iio_priv(indio_dev);255 256	switch (info) {257	case IIO_EV_INFO_VALUE:258		switch (dir) {259		case IIO_EV_DIR_RISING:260			*val = data->als_thresh_high;261			return IIO_VAL_INT;262		case IIO_EV_DIR_FALLING:263			*val = data->als_thresh_low;264			return IIO_VAL_INT;265		default:266			return -EINVAL;267		}268		break;269	case IIO_EV_INFO_PERIOD:270		*val = data->als_persistence;271		return IIO_VAL_INT;272	default:273		return -EINVAL;274	}275 276}277 278static int vcnl4035_write_thresh(struct iio_dev *indio_dev,279		const struct iio_chan_spec *chan, enum iio_event_type type,280		enum iio_event_direction dir, enum iio_event_info info, int val,281		int val2)282{283	struct vcnl4035_data *data = iio_priv(indio_dev);284	int ret;285 286	switch (info) {287	case IIO_EV_INFO_VALUE:288		/* 16 bit threshold range 0 - 65535 */289		if (val < 0 || val > 65535)290			return -EINVAL;291		if (dir == IIO_EV_DIR_RISING) {292			if (val < data->als_thresh_low)293				return -EINVAL;294			ret = regmap_write(data->regmap, VCNL4035_ALS_THDH,295					   val);296			if (ret)297				return ret;298			data->als_thresh_high = val;299		} else {300			if (val > data->als_thresh_high)301				return -EINVAL;302			ret = regmap_write(data->regmap, VCNL4035_ALS_THDL,303					   val);304			if (ret)305				return ret;306			data->als_thresh_low = val;307		}308		return ret;309	case IIO_EV_INFO_PERIOD:310		/* allow only 1 2 4 8 as persistence value */311		if (val < 0 || val > 8 || hweight8(val) != 1)312			return -EINVAL;313		ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,314					 VCNL4035_ALS_PERS_MASK, val);315		if (!ret)316			data->als_persistence = val;317		return ret;318	default:319		return -EINVAL;320	}321}322 323static IIO_CONST_ATTR_INT_TIME_AVAIL("50 100 200 400 800");324 325static struct attribute *vcnl4035_attributes[] = {326	&iio_const_attr_integration_time_available.dev_attr.attr,327	NULL,328};329 330static const struct attribute_group vcnl4035_attribute_group = {331	.attrs = vcnl4035_attributes,332};333 334static const struct iio_info vcnl4035_info = {335	.read_raw		= vcnl4035_read_raw,336	.write_raw		= vcnl4035_write_raw,337	.read_event_value	= vcnl4035_read_thresh,338	.write_event_value	= vcnl4035_write_thresh,339	.attrs			= &vcnl4035_attribute_group,340};341 342static const struct iio_event_spec vcnl4035_event_spec[] = {343	{344		.type = IIO_EV_TYPE_THRESH,345		.dir = IIO_EV_DIR_RISING,346		.mask_separate = BIT(IIO_EV_INFO_VALUE),347	}, {348		.type = IIO_EV_TYPE_THRESH,349		.dir = IIO_EV_DIR_FALLING,350		.mask_separate = BIT(IIO_EV_INFO_VALUE),351	}, {352		.type = IIO_EV_TYPE_THRESH,353		.dir = IIO_EV_DIR_EITHER,354		.mask_separate = BIT(IIO_EV_INFO_PERIOD),355	},356};357 358enum vcnl4035_scan_index_order {359	VCNL4035_CHAN_INDEX_LIGHT,360	VCNL4035_CHAN_INDEX_WHITE_LED,361};362 363static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {364	.validate_scan_mask = &iio_validate_scan_mask_onehot,365};366 367static const struct iio_chan_spec vcnl4035_channels[] = {368	{369		.type = IIO_LIGHT,370		.channel = 0,371		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |372				BIT(IIO_CHAN_INFO_INT_TIME) |373				BIT(IIO_CHAN_INFO_SCALE),374		.event_spec = vcnl4035_event_spec,375		.num_event_specs = ARRAY_SIZE(vcnl4035_event_spec),376		.scan_index = VCNL4035_CHAN_INDEX_LIGHT,377		.scan_type = {378			.sign = 'u',379			.realbits = 16,380			.storagebits = 16,381			.endianness = IIO_LE,382		},383	},384	{385		.type = IIO_INTENSITY,386		.channel = 1,387		.modified = 1,388		.channel2 = IIO_MOD_LIGHT_BOTH,389		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),390		.scan_index = VCNL4035_CHAN_INDEX_WHITE_LED,391		.scan_type = {392			.sign = 'u',393			.realbits = 16,394			.storagebits = 16,395			.endianness = IIO_LE,396		},397	},398};399 400static int vcnl4035_set_als_power_state(struct vcnl4035_data *data, u8 status)401{402	return regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,403					VCNL4035_MODE_ALS_MASK,404					status);405}406 407static int vcnl4035_init(struct vcnl4035_data *data)408{409	int ret;410	int id;411 412	ret = regmap_read(data->regmap, VCNL4035_DEV_ID, &id);413	if (ret < 0) {414		dev_err(&data->client->dev, "Failed to read DEV_ID register\n");415		return ret;416	}417 418	id = FIELD_GET(VCNL4035_DEV_ID_MASK, id);419	if (id != VCNL4035_DEV_ID_VAL) {420		dev_err(&data->client->dev, "Wrong id, got %x, expected %x\n",421			id, VCNL4035_DEV_ID_VAL);422		return -ENODEV;423	}424 425	ret = vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_ENABLE);426	if (ret < 0)427		return ret;428 429	/* ALS white channel enable */430	ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,431				 VCNL4035_MODE_ALS_WHITE_CHAN,432				 1);433	if (ret) {434		dev_err(&data->client->dev, "set white channel enable %d\n",435			ret);436		return ret;437	}438 439	/* set default integration time - 100 ms for ALS */440	ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,441				 VCNL4035_ALS_IT_MASK,442				 VCNL4035_ALS_IT_DEFAULT);443	if (ret) {444		dev_err(&data->client->dev, "set default ALS IT returned %d\n",445			ret);446		return ret;447	}448	data->als_it_val = VCNL4035_ALS_IT_DEFAULT;449 450	/* set default persistence time - 1 for ALS */451	ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,452				 VCNL4035_ALS_PERS_MASK,453				 VCNL4035_ALS_PERS_DEFAULT);454	if (ret) {455		dev_err(&data->client->dev, "set default PERS returned %d\n",456			ret);457		return ret;458	}459	data->als_persistence = VCNL4035_ALS_PERS_DEFAULT;460 461	/* set default HIGH threshold for ALS */462	ret = regmap_write(data->regmap, VCNL4035_ALS_THDH,463				VCNL4035_ALS_THDH_DEFAULT);464	if (ret) {465		dev_err(&data->client->dev, "set default THDH returned %d\n",466			ret);467		return ret;468	}469	data->als_thresh_high = VCNL4035_ALS_THDH_DEFAULT;470 471	/* set default LOW threshold for ALS */472	ret = regmap_write(data->regmap, VCNL4035_ALS_THDL,473				VCNL4035_ALS_THDL_DEFAULT);474	if (ret) {475		dev_err(&data->client->dev, "set default THDL returned %d\n",476			ret);477		return ret;478	}479	data->als_thresh_low = VCNL4035_ALS_THDL_DEFAULT;480 481	return 0;482}483 484static bool vcnl4035_is_volatile_reg(struct device *dev, unsigned int reg)485{486	switch (reg) {487	case VCNL4035_ALS_CONF:488	case VCNL4035_DEV_ID:489		return false;490	default:491		return true;492	}493}494 495static const struct regmap_config vcnl4035_regmap_config = {496	.name		= VCNL4035_REGMAP_NAME,497	.reg_bits	= 8,498	.val_bits	= 16,499	.max_register	= VCNL4035_DEV_ID,500	.cache_type	= REGCACHE_RBTREE,501	.volatile_reg	= vcnl4035_is_volatile_reg,502	.val_format_endian = REGMAP_ENDIAN_LITTLE,503};504 505static int vcnl4035_probe_trigger(struct iio_dev *indio_dev)506{507	int ret;508	struct vcnl4035_data *data = iio_priv(indio_dev);509 510	data->drdy_trigger0 = devm_iio_trigger_alloc(511			indio_dev->dev.parent,512			"%s-dev%d", indio_dev->name, iio_device_id(indio_dev));513	if (!data->drdy_trigger0)514		return -ENOMEM;515 516	data->drdy_trigger0->ops = &vcnl4035_trigger_ops;517	iio_trigger_set_drvdata(data->drdy_trigger0, indio_dev);518	ret = devm_iio_trigger_register(indio_dev->dev.parent,519					data->drdy_trigger0);520	if (ret) {521		dev_err(&data->client->dev, "iio trigger register failed\n");522		return ret;523	}524 525	/* Trigger setup */526	ret = devm_iio_triggered_buffer_setup(indio_dev->dev.parent, indio_dev,527					NULL, vcnl4035_trigger_consumer_handler,528					&iio_triggered_buffer_setup_ops);529	if (ret < 0) {530		dev_err(&data->client->dev, "iio triggered buffer setup failed\n");531		return ret;532	}533 534	/* IRQ to trigger mapping */535	ret = devm_request_threaded_irq(&data->client->dev, data->client->irq,536			NULL, vcnl4035_drdy_irq_thread,537			IRQF_TRIGGER_LOW | IRQF_ONESHOT,538			VCNL4035_IRQ_NAME, indio_dev);539	if (ret < 0)540		dev_err(&data->client->dev, "request irq %d for trigger0 failed\n",541				data->client->irq);542	return ret;543}544 545static int vcnl4035_probe(struct i2c_client *client)546{547	struct vcnl4035_data *data;548	struct iio_dev *indio_dev;549	struct regmap *regmap;550	int ret;551 552	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));553	if (!indio_dev)554		return -ENOMEM;555 556	regmap = devm_regmap_init_i2c(client, &vcnl4035_regmap_config);557	if (IS_ERR(regmap)) {558		dev_err(&client->dev, "regmap_init failed!\n");559		return PTR_ERR(regmap);560	}561 562	data = iio_priv(indio_dev);563	i2c_set_clientdata(client, indio_dev);564	data->client = client;565	data->regmap = regmap;566 567	indio_dev->info = &vcnl4035_info;568	indio_dev->name = VCNL4035_DRV_NAME;569	indio_dev->channels = vcnl4035_channels;570	indio_dev->num_channels = ARRAY_SIZE(vcnl4035_channels);571	indio_dev->modes = INDIO_DIRECT_MODE;572 573	ret = vcnl4035_init(data);574	if (ret < 0) {575		dev_err(&client->dev, "vcnl4035 chip init failed\n");576		return ret;577	}578 579	if (client->irq > 0) {580		ret = vcnl4035_probe_trigger(indio_dev);581		if (ret < 0) {582			dev_err(&client->dev, "vcnl4035 unable init trigger\n");583			goto fail_poweroff;584		}585	}586 587	ret = pm_runtime_set_active(&client->dev);588	if (ret < 0)589		goto fail_poweroff;590 591	ret = iio_device_register(indio_dev);592	if (ret < 0)593		goto fail_poweroff;594 595	pm_runtime_enable(&client->dev);596	pm_runtime_set_autosuspend_delay(&client->dev, VCNL4035_SLEEP_DELAY_MS);597	pm_runtime_use_autosuspend(&client->dev);598 599	return 0;600 601fail_poweroff:602	vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_DISABLE);603	return ret;604}605 606static void vcnl4035_remove(struct i2c_client *client)607{608	struct iio_dev *indio_dev = i2c_get_clientdata(client);609	int ret;610 611	pm_runtime_dont_use_autosuspend(&client->dev);612	pm_runtime_disable(&client->dev);613	iio_device_unregister(indio_dev);614	pm_runtime_set_suspended(&client->dev);615 616	ret = vcnl4035_set_als_power_state(iio_priv(indio_dev),617					   VCNL4035_MODE_ALS_DISABLE);618	if (ret)619		dev_warn(&client->dev, "Failed to put device into standby (%pe)\n",620			 ERR_PTR(ret));621}622 623static int vcnl4035_runtime_suspend(struct device *dev)624{625	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));626	struct vcnl4035_data *data = iio_priv(indio_dev);627	int ret;628 629	ret = vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_DISABLE);630	regcache_mark_dirty(data->regmap);631 632	return ret;633}634 635static int vcnl4035_runtime_resume(struct device *dev)636{637	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));638	struct vcnl4035_data *data = iio_priv(indio_dev);639	int ret;640 641	regcache_sync(data->regmap);642	ret = vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_ENABLE);643	if (ret < 0)644		return ret;645 646	/* wait for 1 ALS integration cycle */647	msleep(data->als_it_val * 100);648 649	return 0;650}651 652static DEFINE_RUNTIME_DEV_PM_OPS(vcnl4035_pm_ops, vcnl4035_runtime_suspend,653				 vcnl4035_runtime_resume, NULL);654 655static const struct i2c_device_id vcnl4035_id[] = {656	{ "vcnl4035" },657	{ }658};659MODULE_DEVICE_TABLE(i2c, vcnl4035_id);660 661static const struct of_device_id vcnl4035_of_match[] = {662	{ .compatible = "vishay,vcnl4035", },663	{ }664};665MODULE_DEVICE_TABLE(of, vcnl4035_of_match);666 667static struct i2c_driver vcnl4035_driver = {668	.driver = {669		.name   = VCNL4035_DRV_NAME,670		.pm	= pm_ptr(&vcnl4035_pm_ops),671		.of_match_table = vcnl4035_of_match,672	},673	.probe = vcnl4035_probe,674	.remove	= vcnl4035_remove,675	.id_table = vcnl4035_id,676};677 678module_i2c_driver(vcnl4035_driver);679 680MODULE_AUTHOR("Parthiban Nallathambi <pn@denx.de>");681MODULE_DESCRIPTION("VCNL4035 Ambient Light Sensor driver");682MODULE_LICENSE("GPL v2");683