brintos

brintos / linux-shallow public Read only

0
0
Text · 8.2 KiB · cf6ddee Raw
346 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * IIO driver for the light sensor ISL76682.4 * ISL76682 is Ambient Light Sensor5 *6 * Copyright (c) 2023 Marek Vasut <marex@denx.de>7 */8 9#include <linux/array_size.h>10#include <linux/bits.h>11#include <linux/cleanup.h>12#include <linux/delay.h>13#include <linux/err.h>14#include <linux/i2c.h>15#include <linux/module.h>16#include <linux/mutex.h>17#include <linux/regmap.h>18#include <linux/types.h>19 20#include <linux/iio/iio.h>21 22#define ISL76682_REG_COMMAND			0x0023 24#define ISL76682_COMMAND_EN			BIT(7)25#define ISL76682_COMMAND_MODE_CONTINUOUS	BIT(6)26#define ISL76682_COMMAND_LIGHT_IR		BIT(5)27 28#define ISL76682_COMMAND_RANGE_LUX_1K		0x029#define ISL76682_COMMAND_RANGE_LUX_4K		0x130#define ISL76682_COMMAND_RANGE_LUX_16K		0x231#define ISL76682_COMMAND_RANGE_LUX_64K		0x332#define ISL76682_COMMAND_RANGE_LUX_MASK		GENMASK(1, 0)33 34#define ISL76682_REG_ALSIR_L			0x0135 36#define ISL76682_REG_ALSIR_U			0x0237 38#define ISL76682_NUM_REGS			(ISL76682_REG_ALSIR_U + 1)39 40#define ISL76682_CONV_TIME_MS			10041#define ISL76682_INT_TIME_US			9000042 43#define ISL76682_ADC_MAX			(BIT(16) - 1)44 45struct isl76682_chip {46	/*47	 * Lock to synchronize access to device command register48	 * and the content of range variable below.49	 */50	struct mutex			lock;51	struct regmap			*regmap;52	u8				range;53	u8				command;54};55 56struct isl76682_range {57	u8				range;58	u32				als;59	u32				ir;60};61 62static struct isl76682_range isl76682_range_table[] = {63	{ ISL76682_COMMAND_RANGE_LUX_1K, 15000, 10500 },64	{ ISL76682_COMMAND_RANGE_LUX_4K, 60000, 42000 },65	{ ISL76682_COMMAND_RANGE_LUX_16K, 240000, 168000 },66	{ ISL76682_COMMAND_RANGE_LUX_64K, 960000, 673000 }67};68 69static int isl76682_get(struct isl76682_chip *chip, bool mode_ir, int *data)70{71	u8 command;72	int ret;73 74	command = ISL76682_COMMAND_EN | ISL76682_COMMAND_MODE_CONTINUOUS |75		  chip->range;76 77	if (mode_ir)78		command |= ISL76682_COMMAND_LIGHT_IR;79 80	if (command != chip->command) {81		ret = regmap_write(chip->regmap, ISL76682_REG_COMMAND, command);82		if (ret)83			return ret;84 85		/* Need to wait for conversion time if ALS/IR mode enabled */86		msleep(ISL76682_CONV_TIME_MS);87 88		chip->command = command;89	}90 91	ret = regmap_bulk_read(chip->regmap, ISL76682_REG_ALSIR_L, data, 2);92	*data &= ISL76682_ADC_MAX;93	return ret;94}95 96static int isl76682_write_raw(struct iio_dev *indio_dev,97			      struct iio_chan_spec const *chan,98			      int val, int val2, long mask)99{100	struct isl76682_chip *chip = iio_priv(indio_dev);101	int i;102 103	if (mask != IIO_CHAN_INFO_SCALE)104		return -EINVAL;105 106	if (val != 0)107		return -EINVAL;108 109	for (i = 0; i < ARRAY_SIZE(isl76682_range_table); i++) {110		if (chan->type == IIO_LIGHT && val2 != isl76682_range_table[i].als)111			continue;112		if (chan->type == IIO_INTENSITY && val2 != isl76682_range_table[i].ir)113			continue;114 115		scoped_guard(mutex, &chip->lock)116			chip->range = isl76682_range_table[i].range;117		return 0;118	}119 120	return -EINVAL;121}122 123static int isl76682_read_raw(struct iio_dev *indio_dev,124			     struct iio_chan_spec const *chan,125			     int *val, int *val2, long mask)126{127	struct isl76682_chip *chip = iio_priv(indio_dev);128	int ret;129	int i;130 131	guard(mutex)(&chip->lock);132 133	switch (mask) {134	case IIO_CHAN_INFO_RAW:135		switch (chan->type) {136		case IIO_LIGHT:137			ret = isl76682_get(chip, false, val);138			return (ret < 0) ? ret : IIO_VAL_INT;139		case IIO_INTENSITY:140			ret = isl76682_get(chip, true, val);141			return (ret < 0) ? ret : IIO_VAL_INT;142		default:143			return -EINVAL;144		}145	case IIO_CHAN_INFO_SCALE:146		for (i = 0; i < ARRAY_SIZE(isl76682_range_table); i++) {147			if (chip->range != isl76682_range_table[i].range)148				continue;149 150			*val = 0;151			switch (chan->type) {152			case IIO_LIGHT:153				*val2 = isl76682_range_table[i].als;154				return IIO_VAL_INT_PLUS_MICRO;155			case IIO_INTENSITY:156				*val2 = isl76682_range_table[i].ir;157				return IIO_VAL_INT_PLUS_MICRO;158			default:159				return -EINVAL;160			}161		}162		return -EINVAL;163	case IIO_CHAN_INFO_INT_TIME:164		*val = 0;165		*val2 = ISL76682_INT_TIME_US;166		return IIO_VAL_INT_PLUS_MICRO;167	default:168		return -EINVAL;169	}170}171 172static int illuminance_scale_available[] = {173	0, 15000,174	0, 60000,175	0, 240000,176	0, 960000,177};178 179static int intensity_scale_available[] = {180	0, 10500,181	0, 42000,182	0, 168000,183	0, 673000,184};185 186static int isl76682_read_avail(struct iio_dev *indio_dev,187			       struct iio_chan_spec const *chan,188			       const int **vals, int *type,189			       int *length, long mask)190{191	switch (mask) {192	case IIO_CHAN_INFO_SCALE:193		switch (chan->type) {194		case IIO_LIGHT:195			*vals = illuminance_scale_available;196			*length = ARRAY_SIZE(illuminance_scale_available);197			*type = IIO_VAL_INT_PLUS_MICRO;198			return IIO_AVAIL_LIST;199		case IIO_INTENSITY:200			*vals = intensity_scale_available;201			*length = ARRAY_SIZE(intensity_scale_available);202			*type = IIO_VAL_INT_PLUS_MICRO;203			return IIO_AVAIL_LIST;204		default:205			return -EINVAL;206		}207	default:208		return -EINVAL;209	}210}211 212static const struct iio_chan_spec isl76682_channels[] = {213	{214		.type = IIO_LIGHT,215		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |216				      BIT(IIO_CHAN_INFO_SCALE),217		.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE),218		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),219	}, {220		.type = IIO_INTENSITY,221		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |222				      BIT(IIO_CHAN_INFO_SCALE),223		.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE),224		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),225	}226};227 228static const struct iio_info isl76682_info = {229	.read_avail	= isl76682_read_avail,230	.read_raw	= isl76682_read_raw,231	.write_raw	= isl76682_write_raw,232};233 234static int isl76682_clear_configure_reg(struct isl76682_chip *chip)235{236	struct device *dev = regmap_get_device(chip->regmap);237	int ret;238 239	ret = regmap_write(chip->regmap, ISL76682_REG_COMMAND, 0x0);240	if (ret < 0)241		dev_err(dev, "Error %d clearing the CONFIGURE register\n", ret);242 243	/*244	 * In the success case, the command register was zeroed out.245	 *246	 * In the error case, we do not know in which state the command247	 * register is, so we assume it is zeroed out, so that it would248	 * be reprogrammed at the next data read out, and at that time249	 * we hope it would be reprogrammed successfully. That is very250	 * much a best effort approach.251	 */252	chip->command = 0;253 254	return ret;255}256 257static void isl76682_reset_action(void *chip)258{259	isl76682_clear_configure_reg(chip);260}261 262static bool isl76682_is_volatile_reg(struct device *dev, unsigned int reg)263{264	switch (reg) {265	case ISL76682_REG_ALSIR_L:266	case ISL76682_REG_ALSIR_U:267		return true;268	default:269		return false;270	}271}272 273static const struct regmap_config isl76682_regmap_config = {274	.reg_bits		= 8,275	.val_bits		= 8,276	.volatile_reg		= isl76682_is_volatile_reg,277	.max_register		= ISL76682_NUM_REGS - 1,278	.num_reg_defaults_raw	= ISL76682_NUM_REGS,279	.cache_type		= REGCACHE_FLAT,280};281 282static int isl76682_probe(struct i2c_client *client)283{284	struct device *dev = &client->dev;285	struct isl76682_chip *chip;286	struct iio_dev *indio_dev;287	int ret;288 289	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));290	if (!indio_dev)291		return -ENOMEM;292 293	chip = iio_priv(indio_dev);294 295	mutex_init(&chip->lock);296 297	chip->regmap = devm_regmap_init_i2c(client, &isl76682_regmap_config);298	ret = PTR_ERR_OR_ZERO(chip->regmap);299	if (ret)300		return dev_err_probe(dev, ret, "Error initializing regmap\n");301 302	chip->range = ISL76682_COMMAND_RANGE_LUX_1K;303 304	ret = isl76682_clear_configure_reg(chip);305	if (ret < 0)306		return ret;307 308	ret = devm_add_action_or_reset(dev, isl76682_reset_action, chip);309	if (ret)310		return ret;311 312	indio_dev->info = &isl76682_info;313	indio_dev->channels = isl76682_channels;314	indio_dev->num_channels = ARRAY_SIZE(isl76682_channels);315	indio_dev->name = "isl76682";316	indio_dev->modes = INDIO_DIRECT_MODE;317 318	return devm_iio_device_register(dev, indio_dev);319}320 321static const struct i2c_device_id isl76682_id[] = {322	{ "isl76682" },323	{ }324};325MODULE_DEVICE_TABLE(i2c, isl76682_id);326 327static const struct of_device_id isl76682_of_match[] = {328	{ .compatible = "isil,isl76682" },329	{ }330};331MODULE_DEVICE_TABLE(of, isl76682_of_match);332 333static struct i2c_driver isl76682_driver = {334	.driver  = {335		.name		= "isl76682",336		.of_match_table	= isl76682_of_match,337	},338	.probe		= isl76682_probe,339	.id_table	= isl76682_id,340};341module_i2c_driver(isl76682_driver);342 343MODULE_DESCRIPTION("ISL76682 Ambient Light Sensor driver");344MODULE_LICENSE("GPL");345MODULE_AUTHOR("Marek Vasut <marex@denx.de>");346