brintos

brintos / linux-shallow public Read only

0
0
Text · 11.3 KiB · 05d4c0e Raw
475 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Vishay VEML6075 UVA and UVB light sensor4 *5 * Copyright 2023 Javier Carrasco <javier.carrasco.cruz@gmail.com>6 *7 * 7-bit I2C slave, address 0x108 */9 10#include <linux/bitfield.h>11#include <linux/delay.h>12#include <linux/err.h>13#include <linux/i2c.h>14#include <linux/module.h>15#include <linux/mutex.h>16#include <linux/regmap.h>17#include <linux/units.h>18 19#include <linux/iio/iio.h>20 21#define VEML6075_CMD_CONF	0x00 /* configuration register */22#define VEML6075_CMD_UVA	0x07 /* UVA channel */23#define VEML6075_CMD_UVB	0x09 /* UVB channel */24#define VEML6075_CMD_COMP1	0x0A /* visible light compensation */25#define VEML6075_CMD_COMP2	0x0B /* infrarred light compensation */26#define VEML6075_CMD_ID		0x0C /* device ID */27 28#define VEML6075_CONF_IT	GENMASK(6, 4) /* intregration time */29#define VEML6075_CONF_HD	BIT(3) /* dynamic setting */30#define VEML6075_CONF_TRIG	BIT(2) /* trigger */31#define VEML6075_CONF_AF	BIT(1) /* active force enable */32#define VEML6075_CONF_SD	BIT(0) /* shutdown */33 34#define VEML6075_IT_50_MS	0x0035#define VEML6075_IT_100_MS	0x0136#define VEML6075_IT_200_MS	0x0237#define VEML6075_IT_400_MS	0x0338#define VEML6075_IT_800_MS	0x0439 40#define VEML6075_AF_DISABLE	0x0041#define VEML6075_AF_ENABLE	0x0142 43#define VEML6075_SD_DISABLE	0x0044#define VEML6075_SD_ENABLE	0x0145 46/* Open-air coefficients and responsivity */47#define VEML6075_A_COEF		222048#define VEML6075_B_COEF		133049#define VEML6075_C_COEF		295050#define VEML6075_D_COEF		174051#define VEML6075_UVA_RESP	146152#define VEML6075_UVB_RESP	259153 54static const int veml6075_it_ms[] = { 50, 100, 200, 400, 800 };55 56struct veml6075_data {57	struct i2c_client *client;58	struct regmap *regmap;59	/*60	 * prevent integration time modification and triggering61	 * measurements while a measurement is underway.62	 */63	struct mutex lock;64};65 66/* channel number */67enum veml6075_chan {68	CH_UVA,69	CH_UVB,70};71 72static const struct iio_chan_spec veml6075_channels[] = {73	{74		.type = IIO_INTENSITY,75		.channel = CH_UVA,76		.modified = 1,77		.channel2 = IIO_MOD_LIGHT_UVA,78		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |79			BIT(IIO_CHAN_INFO_SCALE),80		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),81		.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),82	},83	{84		.type = IIO_INTENSITY,85		.channel = CH_UVB,86		.modified = 1,87		.channel2 = IIO_MOD_LIGHT_UVB,88		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |89			BIT(IIO_CHAN_INFO_SCALE),90		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),91		.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),92	},93	{94		.type = IIO_UVINDEX,95		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),96		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),97		.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),98	},99};100 101static int veml6075_request_measurement(struct veml6075_data *data)102{103	int ret, conf, int_time;104 105	ret = regmap_read(data->regmap, VEML6075_CMD_CONF, &conf);106	if (ret < 0)107		return ret;108 109	/* disable shutdown and trigger measurement */110	ret = regmap_write(data->regmap, VEML6075_CMD_CONF,111			   (conf | VEML6075_CONF_TRIG) & ~VEML6075_CONF_SD);112	if (ret < 0)113		return ret;114 115	/*116	 * A measurement requires between 1.30 and 1.40 times the integration117	 * time for all possible configurations. Using a 1.50 factor simplifies118	 * operations and ensures reliability under all circumstances.119	 */120	int_time = veml6075_it_ms[FIELD_GET(VEML6075_CONF_IT, conf)];121	msleep(int_time + (int_time / 2));122 123	/* shutdown again, data registers are still accessible */124	return regmap_update_bits(data->regmap, VEML6075_CMD_CONF,125				  VEML6075_CONF_SD, VEML6075_CONF_SD);126}127 128static int veml6075_uva_comp(int raw_uva, int comp1, int comp2)129{130	int comp1a_c, comp2a_c, uva_comp;131 132	comp1a_c = (comp1 * VEML6075_A_COEF) / 1000U;133	comp2a_c = (comp2 * VEML6075_B_COEF) / 1000U;134	uva_comp = raw_uva - comp1a_c - comp2a_c;135 136	return clamp_val(uva_comp, 0, U16_MAX);137}138 139static int veml6075_uvb_comp(int raw_uvb, int comp1, int comp2)140{141	int comp1b_c, comp2b_c, uvb_comp;142 143	comp1b_c = (comp1 * VEML6075_C_COEF) / 1000U;144	comp2b_c = (comp2 * VEML6075_D_COEF) / 1000U;145	uvb_comp = raw_uvb - comp1b_c - comp2b_c;146 147	return clamp_val(uvb_comp, 0, U16_MAX);148}149 150static int veml6075_read_comp(struct veml6075_data *data, int *c1, int *c2)151{152	int ret;153 154	ret = regmap_read(data->regmap, VEML6075_CMD_COMP1, c1);155	if (ret < 0)156		return ret;157 158	return regmap_read(data->regmap, VEML6075_CMD_COMP2, c2);159}160 161static int veml6075_read_uv_direct(struct veml6075_data *data, int chan,162				   int *val)163{164	int c1, c2, ret;165 166	guard(mutex)(&data->lock);167 168	ret = veml6075_request_measurement(data);169	if (ret < 0)170		return ret;171 172	ret = veml6075_read_comp(data, &c1, &c2);173	if (ret < 0)174		return ret;175 176	switch (chan) {177	case CH_UVA:178		ret = regmap_read(data->regmap, VEML6075_CMD_UVA, val);179		if (ret < 0)180			return ret;181 182		*val = veml6075_uva_comp(*val, c1, c2);183		return IIO_VAL_INT;184	case CH_UVB:185		ret = regmap_read(data->regmap, VEML6075_CMD_UVB, val);186		if (ret < 0)187			return ret;188 189		*val = veml6075_uvb_comp(*val, c1, c2);190		return IIO_VAL_INT;191	default:192		return -EINVAL;193	}194}195 196static int veml6075_read_int_time_index(struct veml6075_data *data)197{198	int ret, conf;199 200	ret = regmap_read(data->regmap, VEML6075_CMD_CONF, &conf);201	if (ret < 0)202		return ret;203 204	return FIELD_GET(VEML6075_CONF_IT, conf);205}206 207static int veml6075_read_int_time_ms(struct veml6075_data *data, int *val)208{209	int int_index;210 211	guard(mutex)(&data->lock);212	int_index = veml6075_read_int_time_index(data);213	if (int_index < 0)214		return int_index;215 216	*val = veml6075_it_ms[int_index];217 218	return IIO_VAL_INT;219}220 221static int veml6075_get_uvi_micro(struct veml6075_data *data, int uva_comp,222				  int uvb_comp)223{224	int uvia_micro = uva_comp * VEML6075_UVA_RESP;225	int uvib_micro = uvb_comp * VEML6075_UVB_RESP;226	int int_index;227 228	int_index = veml6075_read_int_time_index(data);229	if (int_index < 0)230		return int_index;231 232	switch (int_index) {233	case VEML6075_IT_50_MS:234		return uvia_micro + uvib_micro;235	case VEML6075_IT_100_MS:236	case VEML6075_IT_200_MS:237	case VEML6075_IT_400_MS:238	case VEML6075_IT_800_MS:239		return (uvia_micro + uvib_micro) / (2 << int_index);240	default:241		return -EINVAL;242	}243}244 245static int veml6075_read_uvi(struct veml6075_data *data, int *val, int *val2)246{247	int ret, c1, c2, uva, uvb, uvi_micro;248 249	guard(mutex)(&data->lock);250 251	ret = veml6075_request_measurement(data);252	if (ret < 0)253		return ret;254 255	ret = veml6075_read_comp(data, &c1, &c2);256	if (ret < 0)257		return ret;258 259	ret = regmap_read(data->regmap, VEML6075_CMD_UVA, &uva);260	if (ret < 0)261		return ret;262 263	ret = regmap_read(data->regmap, VEML6075_CMD_UVB, &uvb);264	if (ret < 0)265		return ret;266 267	uvi_micro = veml6075_get_uvi_micro(data, veml6075_uva_comp(uva, c1, c2),268					   veml6075_uvb_comp(uvb, c1, c2));269	if (uvi_micro < 0)270		return uvi_micro;271 272	*val = uvi_micro / MICRO;273	*val2 = uvi_micro % MICRO;274 275	return IIO_VAL_INT_PLUS_MICRO;276}277 278static int veml6075_read_responsivity(int chan, int *val, int *val2)279{280	/* scale = 1 / resp */281	switch (chan) {282	case CH_UVA:283		/* resp = 0.93 c/uW/cm2: scale = 1.75268817 */284		*val = 1;285		*val2 = 75268817;286		return IIO_VAL_INT_PLUS_NANO;287	case CH_UVB:288		/* resp = 2.1 c/uW/cm2: scale = 0.476190476 */289		*val = 0;290		*val2 = 476190476;291		return IIO_VAL_INT_PLUS_NANO;292	default:293		return -EINVAL;294	}295}296 297static int veml6075_read_avail(struct iio_dev *indio_dev,298			       struct iio_chan_spec const *chan,299			       const int **vals, int *type, int *length,300			       long mask)301{302	switch (mask) {303	case IIO_CHAN_INFO_INT_TIME:304		*length = ARRAY_SIZE(veml6075_it_ms);305		*vals = veml6075_it_ms;306		*type = IIO_VAL_INT;307		return IIO_AVAIL_LIST;308 309	default:310		return -EINVAL;311	}312}313 314static int veml6075_read_raw(struct iio_dev *indio_dev,315			     struct iio_chan_spec const *chan,316			     int *val, int *val2, long mask)317{318	struct veml6075_data *data = iio_priv(indio_dev);319 320	switch (mask) {321	case IIO_CHAN_INFO_RAW:322		return veml6075_read_uv_direct(data, chan->channel, val);323	case IIO_CHAN_INFO_PROCESSED:324		return veml6075_read_uvi(data, val, val2);325	case IIO_CHAN_INFO_INT_TIME:326		return veml6075_read_int_time_ms(data, val);327	case IIO_CHAN_INFO_SCALE:328		return veml6075_read_responsivity(chan->channel, val, val2);329	default:330		return -EINVAL;331	}332}333 334static int veml6075_write_int_time_ms(struct veml6075_data *data, int val)335{336	int i = ARRAY_SIZE(veml6075_it_ms);337 338	guard(mutex)(&data->lock);339 340	while (i-- > 0) {341		if (val == veml6075_it_ms[i])342			break;343	}344	if (i < 0)345		return -EINVAL;346 347	return regmap_update_bits(data->regmap, VEML6075_CMD_CONF,348				  VEML6075_CONF_IT,349				  FIELD_PREP(VEML6075_CONF_IT, i));350}351 352static int veml6075_write_raw(struct iio_dev *indio_dev,353			      struct iio_chan_spec const *chan,354			      int val, int val2, long mask)355{356	struct veml6075_data *data = iio_priv(indio_dev);357 358	switch (mask) {359	case IIO_CHAN_INFO_INT_TIME:360		return veml6075_write_int_time_ms(data, val);361	default:362		return -EINVAL;363	}364}365 366static const struct iio_info veml6075_info = {367	.read_avail = veml6075_read_avail,368	.read_raw = veml6075_read_raw,369	.write_raw = veml6075_write_raw,370};371 372static bool veml6075_readable_reg(struct device *dev, unsigned int reg)373{374	switch (reg) {375	case VEML6075_CMD_CONF:376	case VEML6075_CMD_UVA:377	case VEML6075_CMD_UVB:378	case VEML6075_CMD_COMP1:379	case VEML6075_CMD_COMP2:380	case VEML6075_CMD_ID:381		return true;382	default:383		return false;384	}385}386 387static bool veml6075_writable_reg(struct device *dev, unsigned int reg)388{389	switch (reg) {390	case VEML6075_CMD_CONF:391		return true;392	default:393		return false;394	}395}396 397static const struct regmap_config veml6075_regmap_config = {398	.name = "veml6075",399	.reg_bits = 8,400	.val_bits = 16,401	.max_register = VEML6075_CMD_ID,402	.readable_reg = veml6075_readable_reg,403	.writeable_reg = veml6075_writable_reg,404	.val_format_endian = REGMAP_ENDIAN_LITTLE,405};406 407static int veml6075_probe(struct i2c_client *client)408{409	struct veml6075_data *data;410	struct iio_dev *indio_dev;411	struct regmap *regmap;412	int config, ret;413 414	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));415	if (!indio_dev)416		return -ENOMEM;417 418	regmap = devm_regmap_init_i2c(client, &veml6075_regmap_config);419	if (IS_ERR(regmap))420		return PTR_ERR(regmap);421 422	data = iio_priv(indio_dev);423	data->client = client;424	data->regmap = regmap;425 426	mutex_init(&data->lock);427 428	indio_dev->name = "veml6075";429	indio_dev->info = &veml6075_info;430	indio_dev->channels = veml6075_channels;431	indio_dev->num_channels = ARRAY_SIZE(veml6075_channels);432	indio_dev->modes = INDIO_DIRECT_MODE;433 434	ret = devm_regulator_get_enable(&client->dev, "vdd");435	if (ret < 0)436		return ret;437 438	/* default: 100ms integration time, active force enable, shutdown */439	config = FIELD_PREP(VEML6075_CONF_IT, VEML6075_IT_100_MS) |440		FIELD_PREP(VEML6075_CONF_AF, VEML6075_AF_ENABLE) |441		FIELD_PREP(VEML6075_CONF_SD, VEML6075_SD_ENABLE);442	ret = regmap_write(data->regmap, VEML6075_CMD_CONF, config);443	if (ret < 0)444		return ret;445 446	return devm_iio_device_register(&client->dev, indio_dev);447}448 449static const struct i2c_device_id veml6075_id[] = {450	{ "veml6075" },451	{ }452};453MODULE_DEVICE_TABLE(i2c, veml6075_id);454 455static const struct of_device_id veml6075_of_match[] = {456	{ .compatible = "vishay,veml6075" },457	{}458};459MODULE_DEVICE_TABLE(of, veml6075_of_match);460 461static struct i2c_driver veml6075_driver = {462	.driver = {463		.name   = "veml6075",464		.of_match_table = veml6075_of_match,465	},466	.probe = veml6075_probe,467	.id_table = veml6075_id,468};469 470module_i2c_driver(veml6075_driver);471 472MODULE_AUTHOR("Javier Carrasco <javier.carrasco.cruz@gmail.com>");473MODULE_DESCRIPTION("Vishay VEML6075 UVA and UVB light sensor driver");474MODULE_LICENSE("GPL");475