brintos

brintos / linux-shallow public Read only

0
0
Text · 13.4 KiB · 604be60 Raw
566 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * zopt2201.c - Support for IDT ZOPT2201 ambient light and UV B sensor4 *5 * Copyright 2017 Peter Meerwald-Stadler <pmeerw@pmeerw.net>6 *7 * Datasheet: https://www.idt.com/document/dst/zopt2201-datasheet8 * 7-bit I2C slave addresses 0x53 (default) or 0x52 (programmed)9 *10 * TODO: interrupt support, ALS/UVB raw mode11 */12 13#include <linux/module.h>14#include <linux/i2c.h>15#include <linux/mutex.h>16#include <linux/err.h>17#include <linux/delay.h>18 19#include <linux/iio/iio.h>20#include <linux/iio/sysfs.h>21 22#include <linux/unaligned.h>23 24#define ZOPT2201_DRV_NAME "zopt2201"25 26/* Registers */27#define ZOPT2201_MAIN_CTRL		0x0028#define ZOPT2201_LS_MEAS_RATE		0x0429#define ZOPT2201_LS_GAIN		0x0530#define ZOPT2201_PART_ID		0x0631#define ZOPT2201_MAIN_STATUS		0x0732#define ZOPT2201_ALS_DATA		0x0d /* LSB first, 13 to 20 bits */33#define ZOPT2201_UVB_DATA		0x10 /* LSB first, 13 to 20 bits */34#define ZOPT2201_UV_COMP_DATA		0x13 /* LSB first, 13 to 20 bits */35#define ZOPT2201_COMP_DATA		0x16 /* LSB first, 13 to 20 bits */36#define ZOPT2201_INT_CFG		0x1937#define ZOPT2201_INT_PST		0x1a38 39#define ZOPT2201_MAIN_CTRL_LS_MODE	BIT(3) /* 0 .. ALS, 1 .. UV B */40#define ZOPT2201_MAIN_CTRL_LS_EN	BIT(1)41 42/* Values for ZOPT2201_LS_MEAS_RATE resolution / bit width */43#define ZOPT2201_MEAS_RES_20BIT		0 /* takes 400 ms */44#define ZOPT2201_MEAS_RES_19BIT		1 /* takes 200 ms */45#define ZOPT2201_MEAS_RES_18BIT		2 /* takes 100 ms, default */46#define ZOPT2201_MEAS_RES_17BIT		3 /* takes 50 ms */47#define ZOPT2201_MEAS_RES_16BIT		4 /* takes 25 ms */48#define ZOPT2201_MEAS_RES_13BIT		5 /* takes 3.125 ms */49#define ZOPT2201_MEAS_RES_SHIFT		450 51/* Values for ZOPT2201_LS_MEAS_RATE measurement rate */52#define ZOPT2201_MEAS_FREQ_25MS		053#define ZOPT2201_MEAS_FREQ_50MS		154#define ZOPT2201_MEAS_FREQ_100MS	2 /* default */55#define ZOPT2201_MEAS_FREQ_200MS	356#define ZOPT2201_MEAS_FREQ_500MS	457#define ZOPT2201_MEAS_FREQ_1000MS	558#define ZOPT2201_MEAS_FREQ_2000MS	659 60/* Values for ZOPT2201_LS_GAIN */61#define ZOPT2201_LS_GAIN_1		062#define ZOPT2201_LS_GAIN_3		163#define ZOPT2201_LS_GAIN_6		264#define ZOPT2201_LS_GAIN_9		365#define ZOPT2201_LS_GAIN_18		466 67/* Values for ZOPT2201_MAIN_STATUS */68#define ZOPT2201_MAIN_STATUS_POWERON	BIT(5)69#define ZOPT2201_MAIN_STATUS_INT	BIT(4)70#define ZOPT2201_MAIN_STATUS_DRDY	BIT(3)71 72#define ZOPT2201_PART_NUMBER		0xb273 74struct zopt2201_data {75	struct i2c_client *client;76	struct mutex lock;77	u8 gain;78	u8 res;79	u8 rate;80};81 82static const struct {83	unsigned int gain; /* gain factor */84	unsigned int scale; /* micro lux per count */85} zopt2201_gain_als[] = {86	{  1, 19200000 },87	{  3,  6400000 },88	{  6,  3200000 },89	{  9,  2133333 },90	{ 18,  1066666 },91};92 93static const struct {94	unsigned int gain; /* gain factor */95	unsigned int scale; /* micro W/m2 per count */96} zopt2201_gain_uvb[] = {97	{  1, 460800 },98	{  3, 153600 },99	{  6,  76800 },100	{  9,  51200 },101	{ 18,  25600 },102};103 104static const struct {105	unsigned int bits; /* sensor resolution in bits */106	unsigned long us; /* measurement time in micro seconds */107} zopt2201_resolution[] = {108	{ 20, 400000 },109	{ 19, 200000 },110	{ 18, 100000 },111	{ 17,  50000 },112	{ 16,  25000 },113	{ 13,   3125 },114};115 116static const struct {117	unsigned int scale, uscale; /* scale factor as integer + micro */118	u8 gain; /* gain register value */119	u8 res; /* resolution register value */120} zopt2201_scale_als[] = {121	{ 19, 200000, 0, 5 },122	{  6, 400000, 1, 5 },123	{  3, 200000, 2, 5 },124	{  2, 400000, 0, 4 },125	{  2, 133333, 3, 5 },126	{  1, 200000, 0, 3 },127	{  1,  66666, 4, 5 },128	{  0, 800000, 1, 4 },129	{  0, 600000, 0, 2 },130	{  0, 400000, 2, 4 },131	{  0, 300000, 0, 1 },132	{  0, 266666, 3, 4 },133	{  0, 200000, 2, 3 },134	{  0, 150000, 0, 0 },135	{  0, 133333, 4, 4 },136	{  0, 100000, 2, 2 },137	{  0,  66666, 4, 3 },138	{  0,  50000, 2, 1 },139	{  0,  33333, 4, 2 },140	{  0,  25000, 2, 0 },141	{  0,  16666, 4, 1 },142	{  0,   8333, 4, 0 },143};144 145static const struct {146	unsigned int scale, uscale; /* scale factor as integer + micro */147	u8 gain; /* gain register value */148	u8 res; /* resolution register value */149} zopt2201_scale_uvb[] = {150	{ 0, 460800, 0, 5 },151	{ 0, 153600, 1, 5 },152	{ 0,  76800, 2, 5 },153	{ 0,  57600, 0, 4 },154	{ 0,  51200, 3, 5 },155	{ 0,  28800, 0, 3 },156	{ 0,  25600, 4, 5 },157	{ 0,  19200, 1, 4 },158	{ 0,  14400, 0, 2 },159	{ 0,   9600, 2, 4 },160	{ 0,   7200, 0, 1 },161	{ 0,   6400, 3, 4 },162	{ 0,   4800, 2, 3 },163	{ 0,   3600, 0, 0 },164	{ 0,   3200, 4, 4 },165	{ 0,   2400, 2, 2 },166	{ 0,   1600, 4, 3 },167	{ 0,   1200, 2, 1 },168	{ 0,    800, 4, 2 },169	{ 0,    600, 2, 0 },170	{ 0,    400, 4, 1 },171	{ 0,    200, 4, 0 },172};173 174static int zopt2201_enable_mode(struct zopt2201_data *data, bool uvb_mode)175{176	u8 out = ZOPT2201_MAIN_CTRL_LS_EN;177 178	if (uvb_mode)179		out |= ZOPT2201_MAIN_CTRL_LS_MODE;180 181	return i2c_smbus_write_byte_data(data->client, ZOPT2201_MAIN_CTRL, out);182}183 184static int zopt2201_read(struct zopt2201_data *data, u8 reg)185{186	struct i2c_client *client = data->client;187	int tries = 10;188	u8 buf[3];189	int ret;190 191	mutex_lock(&data->lock);192	ret = zopt2201_enable_mode(data, reg == ZOPT2201_UVB_DATA);193	if (ret < 0)194		goto fail;195 196	while (tries--) {197		unsigned long t = zopt2201_resolution[data->res].us;198 199		if (t <= 20000)200			usleep_range(t, t + 1000);201		else202			msleep(t / 1000);203		ret = i2c_smbus_read_byte_data(client, ZOPT2201_MAIN_STATUS);204		if (ret < 0)205			goto fail;206		if (ret & ZOPT2201_MAIN_STATUS_DRDY)207			break;208	}209 210	if (tries < 0) {211		ret = -ETIMEDOUT;212		goto fail;213	}214 215	ret = i2c_smbus_read_i2c_block_data(client, reg, sizeof(buf), buf);216	if (ret < 0)217		goto fail;218 219	ret = i2c_smbus_write_byte_data(client, ZOPT2201_MAIN_CTRL, 0x00);220	if (ret < 0)221		goto fail;222	mutex_unlock(&data->lock);223 224	return get_unaligned_le24(&buf[0]);225 226fail:227	mutex_unlock(&data->lock);228	return ret;229}230 231static const struct iio_chan_spec zopt2201_channels[] = {232	{233		.type = IIO_LIGHT,234		.address = ZOPT2201_ALS_DATA,235		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |236				      BIT(IIO_CHAN_INFO_SCALE),237		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),238	},239	{240		.type = IIO_INTENSITY,241		.modified = 1,242		.channel2 = IIO_MOD_LIGHT_UV,243		.address = ZOPT2201_UVB_DATA,244		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |245				      BIT(IIO_CHAN_INFO_SCALE),246		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),247	},248	{249		.type = IIO_UVINDEX,250		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),251	},252};253 254static int zopt2201_read_raw(struct iio_dev *indio_dev,255				struct iio_chan_spec const *chan,256				int *val, int *val2, long mask)257{258	struct zopt2201_data *data = iio_priv(indio_dev);259	u64 tmp;260	int ret;261 262	switch (mask) {263	case IIO_CHAN_INFO_RAW:264		ret = zopt2201_read(data, chan->address);265		if (ret < 0)266			return ret;267		*val = ret;268		return IIO_VAL_INT;269	case IIO_CHAN_INFO_PROCESSED:270		ret = zopt2201_read(data, ZOPT2201_UVB_DATA);271		if (ret < 0)272			return ret;273		*val = ret * 18 *274			(1 << (20 - zopt2201_resolution[data->res].bits)) /275			zopt2201_gain_uvb[data->gain].gain;276		return IIO_VAL_INT;277	case IIO_CHAN_INFO_SCALE:278		switch (chan->address) {279		case ZOPT2201_ALS_DATA:280			*val = zopt2201_gain_als[data->gain].scale;281			break;282		case ZOPT2201_UVB_DATA:283			*val = zopt2201_gain_uvb[data->gain].scale;284			break;285		default:286			return -EINVAL;287		}288 289		*val2 = 1000000;290		*val2 *= (1 << (zopt2201_resolution[data->res].bits - 13));291		tmp = div_s64(*val * 1000000ULL, *val2);292		*val = div_s64_rem(tmp, 1000000, val2);293 294		return IIO_VAL_INT_PLUS_MICRO;295	case IIO_CHAN_INFO_INT_TIME:296		*val = 0;297		*val2 = zopt2201_resolution[data->res].us;298		return IIO_VAL_INT_PLUS_MICRO;299	default:300		return -EINVAL;301	}302}303 304static int zopt2201_set_resolution(struct zopt2201_data *data, u8 res)305{306	int ret;307 308	ret = i2c_smbus_write_byte_data(data->client, ZOPT2201_LS_MEAS_RATE,309					(res << ZOPT2201_MEAS_RES_SHIFT) |310					data->rate);311	if (ret < 0)312		return ret;313 314	data->res = res;315 316	return 0;317}318 319static int zopt2201_write_resolution(struct zopt2201_data *data,320				     int val, int val2)321{322	int i, ret;323 324	if (val != 0)325		return -EINVAL;326 327	for (i = 0; i < ARRAY_SIZE(zopt2201_resolution); i++)328		if (val2 == zopt2201_resolution[i].us) {329			mutex_lock(&data->lock);330			ret = zopt2201_set_resolution(data, i);331			mutex_unlock(&data->lock);332			return ret;333		}334 335	return -EINVAL;336}337 338static int zopt2201_set_gain(struct zopt2201_data *data, u8 gain)339{340	int ret;341 342	ret = i2c_smbus_write_byte_data(data->client, ZOPT2201_LS_GAIN, gain);343	if (ret < 0)344		return ret;345 346	data->gain = gain;347 348	return 0;349}350 351static int zopt2201_write_scale_als_by_idx(struct zopt2201_data *data, int idx)352{353	int ret;354 355	mutex_lock(&data->lock);356	ret = zopt2201_set_resolution(data, zopt2201_scale_als[idx].res);357	if (ret < 0)358		goto unlock;359 360	ret = zopt2201_set_gain(data, zopt2201_scale_als[idx].gain);361 362unlock:363	mutex_unlock(&data->lock);364	return ret;365}366 367static int zopt2201_write_scale_als(struct zopt2201_data *data,368				     int val, int val2)369{370	int i;371 372	for (i = 0; i < ARRAY_SIZE(zopt2201_scale_als); i++)373		if (val == zopt2201_scale_als[i].scale &&374		    val2 == zopt2201_scale_als[i].uscale) {375			return zopt2201_write_scale_als_by_idx(data, i);376		}377 378	return -EINVAL;379}380 381static int zopt2201_write_scale_uvb_by_idx(struct zopt2201_data *data, int idx)382{383	int ret;384 385	mutex_lock(&data->lock);386	ret = zopt2201_set_resolution(data, zopt2201_scale_als[idx].res);387	if (ret < 0)388		goto unlock;389 390	ret = zopt2201_set_gain(data, zopt2201_scale_als[idx].gain);391 392unlock:393	mutex_unlock(&data->lock);394	return ret;395}396 397static int zopt2201_write_scale_uvb(struct zopt2201_data *data,398				     int val, int val2)399{400	int i;401 402	for (i = 0; i < ARRAY_SIZE(zopt2201_scale_uvb); i++)403		if (val == zopt2201_scale_uvb[i].scale &&404		    val2 == zopt2201_scale_uvb[i].uscale)405			return zopt2201_write_scale_uvb_by_idx(data, i);406 407	return -EINVAL;408}409 410static int zopt2201_write_raw(struct iio_dev *indio_dev,411			      struct iio_chan_spec const *chan,412			      int val, int val2, long mask)413{414	struct zopt2201_data *data = iio_priv(indio_dev);415 416	switch (mask) {417	case IIO_CHAN_INFO_INT_TIME:418		return zopt2201_write_resolution(data, val, val2);419	case IIO_CHAN_INFO_SCALE:420		switch (chan->address) {421		case ZOPT2201_ALS_DATA:422			return zopt2201_write_scale_als(data, val, val2);423		case ZOPT2201_UVB_DATA:424			return zopt2201_write_scale_uvb(data, val, val2);425		default:426			return -EINVAL;427		}428	}429 430	return -EINVAL;431}432 433static ssize_t zopt2201_show_int_time_available(struct device *dev,434						struct device_attribute *attr,435						char *buf)436{437	size_t len = 0;438	int i;439 440	for (i = 0; i < ARRAY_SIZE(zopt2201_resolution); i++)441		len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06lu ",442				 zopt2201_resolution[i].us);443	buf[len - 1] = '\n';444 445	return len;446}447 448static IIO_DEV_ATTR_INT_TIME_AVAIL(zopt2201_show_int_time_available);449 450static ssize_t zopt2201_show_als_scale_avail(struct device *dev,451					     struct device_attribute *attr,452					     char *buf)453{454	ssize_t len = 0;455	int i;456 457	for (i = 0; i < ARRAY_SIZE(zopt2201_scale_als); i++)458		len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06u ",459				 zopt2201_scale_als[i].scale,460				 zopt2201_scale_als[i].uscale);461	buf[len - 1] = '\n';462 463	return len;464}465 466static ssize_t zopt2201_show_uvb_scale_avail(struct device *dev,467					     struct device_attribute *attr,468					     char *buf)469{470	ssize_t len = 0;471	int i;472 473	for (i = 0; i < ARRAY_SIZE(zopt2201_scale_uvb); i++)474		len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06u ",475				 zopt2201_scale_uvb[i].scale,476				 zopt2201_scale_uvb[i].uscale);477	buf[len - 1] = '\n';478 479	return len;480}481 482static IIO_DEVICE_ATTR(in_illuminance_scale_available, 0444,483		       zopt2201_show_als_scale_avail, NULL, 0);484static IIO_DEVICE_ATTR(in_intensity_uv_scale_available, 0444,485		       zopt2201_show_uvb_scale_avail, NULL, 0);486 487static struct attribute *zopt2201_attributes[] = {488	&iio_dev_attr_integration_time_available.dev_attr.attr,489	&iio_dev_attr_in_illuminance_scale_available.dev_attr.attr,490	&iio_dev_attr_in_intensity_uv_scale_available.dev_attr.attr,491	NULL492};493 494static const struct attribute_group zopt2201_attribute_group = {495	.attrs = zopt2201_attributes,496};497 498static const struct iio_info zopt2201_info = {499	.read_raw = zopt2201_read_raw,500	.write_raw = zopt2201_write_raw,501	.attrs = &zopt2201_attribute_group,502};503 504static int zopt2201_probe(struct i2c_client *client)505{506	struct zopt2201_data *data;507	struct iio_dev *indio_dev;508	int ret;509 510	if (!i2c_check_functionality(client->adapter,511				     I2C_FUNC_SMBUS_READ_I2C_BLOCK))512		return -EOPNOTSUPP;513 514	ret = i2c_smbus_read_byte_data(client, ZOPT2201_PART_ID);515	if (ret < 0)516		return ret;517	if (ret != ZOPT2201_PART_NUMBER)518		return -ENODEV;519 520	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));521	if (!indio_dev)522		return -ENOMEM;523 524	data = iio_priv(indio_dev);525	i2c_set_clientdata(client, indio_dev);526	data->client = client;527	mutex_init(&data->lock);528 529	indio_dev->info = &zopt2201_info;530	indio_dev->channels = zopt2201_channels;531	indio_dev->num_channels = ARRAY_SIZE(zopt2201_channels);532	indio_dev->name = ZOPT2201_DRV_NAME;533	indio_dev->modes = INDIO_DIRECT_MODE;534 535	data->rate = ZOPT2201_MEAS_FREQ_100MS;536	ret = zopt2201_set_resolution(data, ZOPT2201_MEAS_RES_18BIT);537	if (ret < 0)538		return ret;539 540	ret = zopt2201_set_gain(data, ZOPT2201_LS_GAIN_3);541	if (ret < 0)542		return ret;543 544	return devm_iio_device_register(&client->dev, indio_dev);545}546 547static const struct i2c_device_id zopt2201_id[] = {548	{ "zopt2201" },549	{ }550};551MODULE_DEVICE_TABLE(i2c, zopt2201_id);552 553static struct i2c_driver zopt2201_driver = {554	.driver = {555		.name   = ZOPT2201_DRV_NAME,556	},557	.probe = zopt2201_probe,558	.id_table = zopt2201_id,559};560 561module_i2c_driver(zopt2201_driver);562 563MODULE_AUTHOR("Peter Meerwald-Stadler <pmeerw@pmeerw.net>");564MODULE_DESCRIPTION("IDT ZOPT2201 ambient light and UV B sensor driver");565MODULE_LICENSE("GPL");566