brintos

brintos / linux-shallow public Read only

0
0
Text · 8.4 KiB · 25f63da Raw
338 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Support for ON Semiconductor NOA1305 ambient light sensor4 *5 * Copyright (C) 2016 Emcraft Systems6 * Copyright (C) 2019 Collabora Ltd.7 */8 9#include <linux/delay.h>10#include <linux/err.h>11#include <linux/i2c.h>12#include <linux/iio/iio.h>13#include <linux/iio/sysfs.h>14#include <linux/module.h>15#include <linux/regmap.h>16#include <linux/regulator/consumer.h>17 18#define NOA1305_REG_POWER_CONTROL	0x019#define   NOA1305_POWER_CONTROL_DOWN	0x0020#define   NOA1305_POWER_CONTROL_ON	0x0821#define NOA1305_REG_RESET		0x122#define   NOA1305_RESET_RESET		0x1023#define NOA1305_REG_INTEGRATION_TIME	0x224#define   NOA1305_INTEGR_TIME_800MS	0x0025#define   NOA1305_INTEGR_TIME_400MS	0x0126#define   NOA1305_INTEGR_TIME_200MS	0x0227#define   NOA1305_INTEGR_TIME_100MS	0x0328#define   NOA1305_INTEGR_TIME_50MS	0x0429#define   NOA1305_INTEGR_TIME_25MS	0x0530#define   NOA1305_INTEGR_TIME_12_5MS	0x0631#define   NOA1305_INTEGR_TIME_6_25MS	0x0732#define   NOA1305_INTEGR_TIME_MASK	0x0733#define NOA1305_REG_INT_SELECT		0x334#define   NOA1305_INT_SEL_ACTIVE_HIGH	0x0135#define   NOA1305_INT_SEL_ACTIVE_LOW	0x0236#define   NOA1305_INT_SEL_INACTIVE	0x0337#define NOA1305_REG_INT_THRESH_LSB	0x438#define NOA1305_REG_INT_THRESH_MSB	0x539#define NOA1305_REG_ALS_DATA_LSB	0x640#define NOA1305_REG_ALS_DATA_MSB	0x741#define NOA1305_REG_DEVICE_ID_LSB	0x842#define NOA1305_REG_DEVICE_ID_MSB	0x943 44#define NOA1305_DEVICE_ID	0x051945#define NOA1305_DRIVER_NAME	"noa1305"46 47static int noa1305_scale_available[] = {48	100, 8 * 77,		/* 800 ms */49	100, 4 * 77,		/* 400 ms */50	100, 2 * 77,		/* 200 ms */51	100, 1 * 77,		/* 100 ms */52	1000, 5 * 77,		/* 50 ms */53	10000, 25 * 77,		/* 25 ms */54	100000, 125 * 77,	/* 12.5 ms */55	1000000, 625 * 77,	/* 6.25 ms */56};57 58static int noa1305_int_time_available[] = {59	0, 800000,		/* 800 ms */60	0, 400000,		/* 400 ms */61	0, 200000,		/* 200 ms */62	0, 100000,		/* 100 ms */63	0, 50000,		/* 50 ms */64	0, 25000,		/* 25 ms */65	0, 12500,		/* 12.5 ms */66	0, 6250,		/* 6.25 ms */67};68 69struct noa1305_priv {70	struct i2c_client *client;71	struct regmap *regmap;72};73 74static int noa1305_measure(struct noa1305_priv *priv, int *val)75{76	__le16 data;77	int ret;78 79	ret = regmap_bulk_read(priv->regmap, NOA1305_REG_ALS_DATA_LSB, &data,80			       2);81	if (ret < 0)82		return ret;83 84	*val = le16_to_cpu(data);85 86	return IIO_VAL_INT;87}88 89static int noa1305_scale(struct noa1305_priv *priv, int *val, int *val2)90{91	int data;92	int ret;93 94	ret = regmap_read(priv->regmap, NOA1305_REG_INTEGRATION_TIME, &data);95	if (ret < 0)96		return ret;97 98	/*99	 * Lux = count / (<Integration Constant> * <Integration Time>)100	 *101	 * Integration Constant = 7.7102	 * Integration Time in Seconds103	 */104	data &= NOA1305_INTEGR_TIME_MASK;105	*val = noa1305_scale_available[2 * data + 0];106	*val2 = noa1305_scale_available[2 * data + 1];107 108	return IIO_VAL_FRACTIONAL;109}110 111static int noa1305_int_time(struct noa1305_priv *priv, int *val, int *val2)112{113	int data;114	int ret;115 116	ret = regmap_read(priv->regmap, NOA1305_REG_INTEGRATION_TIME, &data);117	if (ret < 0)118		return ret;119 120	data &= NOA1305_INTEGR_TIME_MASK;121	*val = noa1305_int_time_available[2 * data + 0];122	*val2 = noa1305_int_time_available[2 * data + 1];123 124	return IIO_VAL_INT_PLUS_MICRO;125}126 127static const struct iio_chan_spec noa1305_channels[] = {128	{129		.type = IIO_LIGHT,130		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),131		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),132		.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE),133		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),134		.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),135	}136};137 138static int noa1305_read_avail(struct iio_dev *indio_dev,139			      struct iio_chan_spec const *chan,140			      const int **vals, int *type,141			      int *length, long mask)142{143	if (chan->type != IIO_LIGHT)144		return -EINVAL;145 146	switch (mask) {147	case IIO_CHAN_INFO_SCALE:148		*vals = noa1305_scale_available;149		*length = ARRAY_SIZE(noa1305_scale_available);150		*type = IIO_VAL_FRACTIONAL;151		return IIO_AVAIL_LIST;152	case IIO_CHAN_INFO_INT_TIME:153		*vals = noa1305_int_time_available;154		*length = ARRAY_SIZE(noa1305_int_time_available);155		*type = IIO_VAL_INT_PLUS_MICRO;156		return IIO_AVAIL_LIST;157	default:158		return -EINVAL;159	}160}161 162static int noa1305_read_raw(struct iio_dev *indio_dev,163			    struct iio_chan_spec const *chan,164			    int *val, int *val2, long mask)165{166	struct noa1305_priv *priv = iio_priv(indio_dev);167 168	if (chan->type != IIO_LIGHT)169		return -EINVAL;170 171	switch (mask) {172	case IIO_CHAN_INFO_RAW:173		return noa1305_measure(priv, val);174	case IIO_CHAN_INFO_SCALE:175		return noa1305_scale(priv, val, val2);176	case IIO_CHAN_INFO_INT_TIME:177		return noa1305_int_time(priv, val, val2);178	default:179		return -EINVAL;180	}181}182 183static int noa1305_write_raw(struct iio_dev *indio_dev,184			     struct iio_chan_spec const *chan,185			     int val, int val2, long mask)186{187	struct noa1305_priv *priv = iio_priv(indio_dev);188	int i;189 190	if (chan->type != IIO_LIGHT)191		return -EINVAL;192 193	if (mask != IIO_CHAN_INFO_INT_TIME)194		return -EINVAL;195 196	if (val)	/* >= 1s integration time not supported */197		return -EINVAL;198 199	/* Look up integration time register settings and write it if found. */200	for (i = 0; i < ARRAY_SIZE(noa1305_int_time_available) / 2; i++)201		if (noa1305_int_time_available[2 * i + 1] == val2)202			return regmap_write(priv->regmap, NOA1305_REG_INTEGRATION_TIME, i);203 204	return -EINVAL;205}206 207static const struct iio_info noa1305_info = {208	.read_avail = noa1305_read_avail,209	.read_raw = noa1305_read_raw,210	.write_raw = noa1305_write_raw,211};212 213static bool noa1305_writable_reg(struct device *dev, unsigned int reg)214{215	switch (reg) {216	case NOA1305_REG_POWER_CONTROL:217	case NOA1305_REG_RESET:218	case NOA1305_REG_INTEGRATION_TIME:219	case NOA1305_REG_INT_SELECT:220	case NOA1305_REG_INT_THRESH_LSB:221	case NOA1305_REG_INT_THRESH_MSB:222		return true;223	default:224		return false;225	}226}227 228static const struct regmap_config noa1305_regmap_config = {229	.name = NOA1305_DRIVER_NAME,230	.reg_bits = 8,231	.val_bits = 8,232	.max_register = NOA1305_REG_DEVICE_ID_MSB,233	.writeable_reg = noa1305_writable_reg,234};235 236static int noa1305_probe(struct i2c_client *client)237{238	struct noa1305_priv *priv;239	struct iio_dev *indio_dev;240	struct regmap *regmap;241	__le16 data;242	unsigned int dev_id;243	int ret;244 245	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*priv));246	if (!indio_dev)247		return -ENOMEM;248 249	regmap = devm_regmap_init_i2c(client, &noa1305_regmap_config);250	if (IS_ERR(regmap)) {251		dev_err(&client->dev, "Regmap initialization failed.\n");252		return PTR_ERR(regmap);253	}254 255	priv = iio_priv(indio_dev);256 257	ret = devm_regulator_get_enable(&client->dev, "vin");258	if (ret)259		return dev_err_probe(&client->dev, ret,260				     "get regulator vin failed\n");261 262	i2c_set_clientdata(client, indio_dev);263	priv->client = client;264	priv->regmap = regmap;265 266	ret = regmap_bulk_read(regmap, NOA1305_REG_DEVICE_ID_LSB, &data, 2);267	if (ret < 0) {268		dev_err(&client->dev, "ID reading failed: %d\n", ret);269		return ret;270	}271 272	dev_id = le16_to_cpu(data);273	if (dev_id != NOA1305_DEVICE_ID) {274		dev_err(&client->dev, "Unknown device ID: 0x%x\n", dev_id);275		return -ENODEV;276	}277 278	ret = regmap_write(regmap, NOA1305_REG_POWER_CONTROL,279			   NOA1305_POWER_CONTROL_ON);280	if (ret < 0) {281		dev_err(&client->dev, "Enabling power control failed\n");282		return ret;283	}284 285	ret = regmap_write(regmap, NOA1305_REG_RESET, NOA1305_RESET_RESET);286	if (ret < 0) {287		dev_err(&client->dev, "Device reset failed\n");288		return ret;289	}290 291	ret = regmap_write(regmap, NOA1305_REG_INTEGRATION_TIME,292			   NOA1305_INTEGR_TIME_800MS);293	if (ret < 0) {294		dev_err(&client->dev, "Setting integration time failed\n");295		return ret;296	}297 298	indio_dev->info = &noa1305_info;299	indio_dev->channels = noa1305_channels;300	indio_dev->num_channels = ARRAY_SIZE(noa1305_channels);301	indio_dev->name = NOA1305_DRIVER_NAME;302	indio_dev->modes = INDIO_DIRECT_MODE;303 304	ret = devm_iio_device_register(&client->dev, indio_dev);305	if (ret)306		dev_err(&client->dev, "registering device failed\n");307 308	return ret;309}310 311static const struct of_device_id noa1305_of_match[] = {312	{ .compatible = "onnn,noa1305" },313	{ }314};315MODULE_DEVICE_TABLE(of, noa1305_of_match);316 317static const struct i2c_device_id noa1305_ids[] = {318	{ "noa1305" },319	{ }320};321MODULE_DEVICE_TABLE(i2c, noa1305_ids);322 323static struct i2c_driver noa1305_driver = {324	.driver = {325		.name		= NOA1305_DRIVER_NAME,326		.of_match_table	= noa1305_of_match,327	},328	.probe		= noa1305_probe,329	.id_table	= noa1305_ids,330};331 332module_i2c_driver(noa1305_driver);333 334MODULE_AUTHOR("Sergei Miroshnichenko <sergeimir@emcraft.com>");335MODULE_AUTHOR("Martyn Welch <martyn.welch@collabora.com");336MODULE_DESCRIPTION("ON Semiconductor NOA1305 ambient light sensor");337MODULE_LICENSE("GPL");338