brintos

brintos / linux-shallow public Read only

0
0
Text · 7.0 KiB · 216e271 Raw
282 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Vishay VEML6040 RGBW light sensor driver4 *5 * Copyright (C) 2024 Sentec AG6 * Author: Arthur Becker <arthur.becker@sentec.com>7 *8 */9 10#include <linux/bitfield.h>11#include <linux/err.h>12#include <linux/i2c.h>13#include <linux/iio/iio.h>14#include <linux/iio/sysfs.h>15#include <linux/module.h>16#include <linux/regmap.h>17 18/* VEML6040 Configuration Registers19 *20 * SD: Shutdown21 * AF: Auto / Force Mode (Auto Measurements On:0, Off:1)22 * TR: Trigger Measurement (when AF Bit is set)23 * IT: Integration Time24 */25#define VEML6040_CONF_REG 0x00026#define VEML6040_CONF_SD_MSK BIT(0)27#define VEML6040_CONF_AF_MSK BIT(1)28#define VEML6040_CONF_TR_MSK BIT(2)29#define VEML6040_CONF_IT_MSK GENMASK(6, 4)30#define VEML6040_CONF_IT_40_MS 031#define VEML6040_CONF_IT_80_MS 132#define VEML6040_CONF_IT_160_MS 233#define VEML6040_CONF_IT_320_MS 334#define VEML6040_CONF_IT_640_MS 435#define VEML6040_CONF_IT_1280_MS 536 37/* VEML6040 Read Only Registers */38#define VEML6040_REG_R 0x0839#define VEML6040_REG_G 0x0940#define VEML6040_REG_B 0x0A41#define VEML6040_REG_W 0x0B42 43static const int veml6040_it_ms[] = { 40, 80, 160, 320, 640, 1280 };44 45enum veml6040_chan {46	CH_RED,47	CH_GREEN,48	CH_BLUE,49	CH_WHITE,50};51 52struct veml6040_data {53	struct i2c_client *client;54	struct regmap *regmap;55};56 57static const struct regmap_config veml6040_regmap_config = {58	.name = "veml6040_regmap",59	.reg_bits = 8,60	.val_bits = 16,61	.max_register = VEML6040_REG_W,62	.val_format_endian = REGMAP_ENDIAN_LITTLE,63};64 65static int veml6040_read_raw(struct iio_dev *indio_dev,66			     struct iio_chan_spec const *chan, int *val,67			     int *val2, long mask)68{69	int ret, reg, it_index;70	struct veml6040_data *data = iio_priv(indio_dev);71	struct regmap *regmap = data->regmap;72	struct device *dev = &data->client->dev;73 74	switch (mask) {75	case IIO_CHAN_INFO_RAW:76		ret = regmap_read(regmap, chan->address, &reg);77		if (ret) {78			dev_err(dev, "Data read failed: %d\n", ret);79			return ret;80		}81		*val = reg;82		return IIO_VAL_INT;83 84	case IIO_CHAN_INFO_INT_TIME:85		ret = regmap_read(regmap, VEML6040_CONF_REG, &reg);86		if (ret) {87			dev_err(dev, "Data read failed: %d\n", ret);88			return ret;89		}90		it_index = FIELD_GET(VEML6040_CONF_IT_MSK, reg);91		if (it_index >= ARRAY_SIZE(veml6040_it_ms)) {92			dev_err(dev, "Invalid Integration Time Set");93			return -EINVAL;94		}95		*val = veml6040_it_ms[it_index];96		return IIO_VAL_INT;97 98	default:99		return -EINVAL;100	}101}102 103static int veml6040_write_raw(struct iio_dev *indio_dev,104			      struct iio_chan_spec const *chan, int val,105			      int val2, long mask)106{107	struct veml6040_data *data = iio_priv(indio_dev);108 109	switch (mask) {110	case IIO_CHAN_INFO_INT_TIME:111		for (int i = 0; i < ARRAY_SIZE(veml6040_it_ms); i++) {112			if (veml6040_it_ms[i] != val)113				continue;114 115			return regmap_update_bits(data->regmap,116					VEML6040_CONF_REG,117					VEML6040_CONF_IT_MSK,118					FIELD_PREP(VEML6040_CONF_IT_MSK, i));119		}120		return -EINVAL;121	default:122		return -EINVAL;123	}124}125 126static int veml6040_read_avail(struct iio_dev *indio_dev,127			       struct iio_chan_spec const *chan,128			       const int **vals, int *type, int *length,129			       long mask)130{131	switch (mask) {132	case IIO_CHAN_INFO_INT_TIME:133		*length = ARRAY_SIZE(veml6040_it_ms);134		*vals = veml6040_it_ms;135		*type = IIO_VAL_INT;136		return IIO_AVAIL_LIST;137 138	default:139		return -EINVAL;140	}141}142 143static const struct iio_info veml6040_info = {144	.read_raw = veml6040_read_raw,145	.write_raw = veml6040_write_raw,146	.read_avail = veml6040_read_avail,147};148 149static const struct iio_chan_spec veml6040_channels[] = {150	{151		.type = IIO_INTENSITY,152		.address = VEML6040_REG_R,153		.channel = CH_RED,154		.channel2 = IIO_MOD_LIGHT_RED,155		.modified = 1,156		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),157		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_INT_TIME),158		.info_mask_shared_by_type_available =159			BIT(IIO_CHAN_INFO_INT_TIME),160	},161	{162		.type = IIO_INTENSITY,163		.address = VEML6040_REG_G,164		.channel = CH_GREEN,165		.channel2 = IIO_MOD_LIGHT_GREEN,166		.modified = 1,167		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),168		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_INT_TIME),169		.info_mask_shared_by_type_available =170			BIT(IIO_CHAN_INFO_INT_TIME),171	},172	{173		.type = IIO_INTENSITY,174		.address = VEML6040_REG_B,175		.channel = CH_BLUE,176		.channel2 = IIO_MOD_LIGHT_BLUE,177		.modified = 1,178		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),179		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_INT_TIME),180		.info_mask_shared_by_type_available =181			BIT(IIO_CHAN_INFO_INT_TIME),182	},183	{184		.type = IIO_INTENSITY,185		.address = VEML6040_REG_W,186		.channel = CH_WHITE,187		.channel2 = IIO_MOD_LIGHT_CLEAR,188		.modified = 1,189		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),190		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_INT_TIME),191		.info_mask_shared_by_type_available =192			BIT(IIO_CHAN_INFO_INT_TIME),193	}194};195 196static void veml6040_shutdown_action(void *data)197{198	struct veml6040_data *veml6040_data = data;199 200	regmap_update_bits(veml6040_data->regmap, VEML6040_CONF_REG,201			   VEML6040_CONF_SD_MSK, VEML6040_CONF_SD_MSK);202}203 204static int veml6040_probe(struct i2c_client *client)205{206	struct device *dev = &client->dev;207	struct veml6040_data *data;208	struct iio_dev *indio_dev;209	struct regmap *regmap;210	const int init_config =211		FIELD_PREP(VEML6040_CONF_IT_MSK, VEML6040_CONF_IT_40_MS) |212		FIELD_PREP(VEML6040_CONF_AF_MSK, 0) |213		FIELD_PREP(VEML6040_CONF_SD_MSK, 0);214	int ret;215 216	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))217		return dev_err_probe(dev, -EOPNOTSUPP,218				     "I2C adapter doesn't support plain I2C\n");219 220	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));221	if (!indio_dev)222		return dev_err_probe(dev, -ENOMEM,223				     "IIO device allocation failed\n");224 225	regmap = devm_regmap_init_i2c(client, &veml6040_regmap_config);226	if (IS_ERR(regmap))227		return dev_err_probe(dev, PTR_ERR(regmap),228				     "Regmap setup failed\n");229 230	data = iio_priv(indio_dev);231	i2c_set_clientdata(client, indio_dev);232	data->client = client;233	data->regmap = regmap;234 235	indio_dev->name = "veml6040";236	indio_dev->info = &veml6040_info;237	indio_dev->channels = veml6040_channels;238	indio_dev->num_channels = ARRAY_SIZE(veml6040_channels);239	indio_dev->modes = INDIO_DIRECT_MODE;240 241	ret = devm_regulator_get_enable(dev, "vdd");242	if (ret)243		return ret;244 245	ret = regmap_write(regmap, VEML6040_CONF_REG, init_config);246	if (ret)247		return dev_err_probe(dev, ret,248				     "Could not set initial config\n");249 250	ret = devm_add_action_or_reset(dev, veml6040_shutdown_action, data);251	if (ret)252		return ret;253 254	return devm_iio_device_register(dev, indio_dev);255}256 257static const struct i2c_device_id veml6040_id_table[] = {258	{"veml6040"},259	{}260};261MODULE_DEVICE_TABLE(i2c, veml6040_id_table);262 263static const struct of_device_id veml6040_of_match[] = {264	{.compatible = "vishay,veml6040"},265	{}266};267MODULE_DEVICE_TABLE(of, veml6040_of_match);268 269static struct i2c_driver veml6040_driver = {270	.probe = veml6040_probe,271	.id_table = veml6040_id_table,272	.driver = {273		.name = "veml6040",274		.of_match_table = veml6040_of_match,275	},276};277module_i2c_driver(veml6040_driver);278 279MODULE_DESCRIPTION("veml6040 RGBW light sensor driver");280MODULE_AUTHOR("Arthur Becker <arthur.becker@sentec.com>");281MODULE_LICENSE("GPL");282