cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

noa1305.c (7287B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Support for ON Semiconductor NOA1305 ambient light sensor
      4 *
      5 * Copyright (C) 2016 Emcraft Systems
      6 * 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	0x0
     19#define   NOA1305_POWER_CONTROL_DOWN	0x00
     20#define   NOA1305_POWER_CONTROL_ON	0x08
     21#define NOA1305_REG_RESET		0x1
     22#define   NOA1305_RESET_RESET		0x10
     23#define NOA1305_REG_INTEGRATION_TIME	0x2
     24#define   NOA1305_INTEGR_TIME_800MS	0x00
     25#define   NOA1305_INTEGR_TIME_400MS	0x01
     26#define   NOA1305_INTEGR_TIME_200MS	0x02
     27#define   NOA1305_INTEGR_TIME_100MS	0x03
     28#define   NOA1305_INTEGR_TIME_50MS	0x04
     29#define   NOA1305_INTEGR_TIME_25MS	0x05
     30#define   NOA1305_INTEGR_TIME_12_5MS	0x06
     31#define   NOA1305_INTEGR_TIME_6_25MS	0x07
     32#define NOA1305_REG_INT_SELECT		0x3
     33#define   NOA1305_INT_SEL_ACTIVE_HIGH	0x01
     34#define   NOA1305_INT_SEL_ACTIVE_LOW	0x02
     35#define   NOA1305_INT_SEL_INACTIVE	0x03
     36#define NOA1305_REG_INT_THRESH_LSB	0x4
     37#define NOA1305_REG_INT_THRESH_MSB	0x5
     38#define NOA1305_REG_ALS_DATA_LSB	0x6
     39#define NOA1305_REG_ALS_DATA_MSB	0x7
     40#define NOA1305_REG_DEVICE_ID_LSB	0x8
     41#define NOA1305_REG_DEVICE_ID_MSB	0x9
     42
     43#define NOA1305_DEVICE_ID	0x0519
     44#define NOA1305_DRIVER_NAME	"noa1305"
     45
     46struct noa1305_priv {
     47	struct i2c_client *client;
     48	struct regmap *regmap;
     49	struct regulator *vin_reg;
     50};
     51
     52static int noa1305_measure(struct noa1305_priv *priv)
     53{
     54	__le16 data;
     55	int ret;
     56
     57	ret = regmap_bulk_read(priv->regmap, NOA1305_REG_ALS_DATA_LSB, &data,
     58			       2);
     59	if (ret < 0)
     60		return ret;
     61
     62	return le16_to_cpu(data);
     63}
     64
     65static int noa1305_scale(struct noa1305_priv *priv, int *val, int *val2)
     66{
     67	int data;
     68	int ret;
     69
     70	ret = regmap_read(priv->regmap, NOA1305_REG_INTEGRATION_TIME, &data);
     71	if (ret < 0)
     72		return ret;
     73
     74	/*
     75	 * Lux = count / (<Integration Constant> * <Integration Time>)
     76	 *
     77	 * Integration Constant = 7.7
     78	 * Integration Time in Seconds
     79	 */
     80	switch (data) {
     81	case NOA1305_INTEGR_TIME_800MS:
     82		*val = 100;
     83		*val2 = 77 * 8;
     84		break;
     85	case NOA1305_INTEGR_TIME_400MS:
     86		*val = 100;
     87		*val2 = 77 * 4;
     88		break;
     89	case NOA1305_INTEGR_TIME_200MS:
     90		*val = 100;
     91		*val2 = 77 * 2;
     92		break;
     93	case NOA1305_INTEGR_TIME_100MS:
     94		*val = 100;
     95		*val2 = 77;
     96		break;
     97	case NOA1305_INTEGR_TIME_50MS:
     98		*val = 1000;
     99		*val2 = 77 * 5;
    100		break;
    101	case NOA1305_INTEGR_TIME_25MS:
    102		*val = 10000;
    103		*val2 = 77 * 25;
    104		break;
    105	case NOA1305_INTEGR_TIME_12_5MS:
    106		*val = 100000;
    107		*val2 = 77 * 125;
    108		break;
    109	case NOA1305_INTEGR_TIME_6_25MS:
    110		*val = 1000000;
    111		*val2 = 77 * 625;
    112		break;
    113	default:
    114		return -EINVAL;
    115	}
    116
    117	return IIO_VAL_FRACTIONAL;
    118}
    119
    120static const struct iio_chan_spec noa1305_channels[] = {
    121	{
    122		.type = IIO_LIGHT,
    123		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
    124		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
    125	}
    126};
    127
    128static int noa1305_read_raw(struct iio_dev *indio_dev,
    129				struct iio_chan_spec const *chan,
    130				int *val, int *val2, long mask)
    131{
    132	int ret = -EINVAL;
    133	struct noa1305_priv *priv = iio_priv(indio_dev);
    134
    135	switch (mask) {
    136	case IIO_CHAN_INFO_RAW:
    137		switch (chan->type) {
    138		case IIO_LIGHT:
    139			ret = noa1305_measure(priv);
    140			if (ret < 0)
    141				return ret;
    142			*val = ret;
    143			return IIO_VAL_INT;
    144		default:
    145			break;
    146		}
    147		break;
    148	case IIO_CHAN_INFO_SCALE:
    149		switch (chan->type) {
    150		case IIO_LIGHT:
    151			return noa1305_scale(priv, val, val2);
    152		default:
    153			break;
    154		}
    155		break;
    156	default:
    157		break;
    158	}
    159
    160	return ret;
    161}
    162
    163static const struct iio_info noa1305_info = {
    164	.read_raw = noa1305_read_raw,
    165};
    166
    167static bool noa1305_writable_reg(struct device *dev, unsigned int reg)
    168{
    169	switch (reg) {
    170	case NOA1305_REG_POWER_CONTROL:
    171	case NOA1305_REG_RESET:
    172	case NOA1305_REG_INTEGRATION_TIME:
    173	case NOA1305_REG_INT_SELECT:
    174	case NOA1305_REG_INT_THRESH_LSB:
    175	case NOA1305_REG_INT_THRESH_MSB:
    176		return true;
    177	default:
    178		return false;
    179	}
    180}
    181
    182static const struct regmap_config noa1305_regmap_config = {
    183	.name = NOA1305_DRIVER_NAME,
    184	.reg_bits = 8,
    185	.val_bits = 8,
    186	.max_register = NOA1305_REG_DEVICE_ID_MSB,
    187	.writeable_reg = noa1305_writable_reg,
    188};
    189
    190static void noa1305_reg_remove(void *data)
    191{
    192	struct noa1305_priv *priv = data;
    193
    194	regulator_disable(priv->vin_reg);
    195}
    196
    197static int noa1305_probe(struct i2c_client *client,
    198			 const struct i2c_device_id *id)
    199{
    200	struct noa1305_priv *priv;
    201	struct iio_dev *indio_dev;
    202	struct regmap *regmap;
    203	__le16 data;
    204	unsigned int dev_id;
    205	int ret;
    206
    207	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*priv));
    208	if (!indio_dev)
    209		return -ENOMEM;
    210
    211	regmap = devm_regmap_init_i2c(client, &noa1305_regmap_config);
    212	if (IS_ERR(regmap)) {
    213		dev_err(&client->dev, "Regmap initialization failed.\n");
    214		return PTR_ERR(regmap);
    215	}
    216
    217	priv = iio_priv(indio_dev);
    218
    219	priv->vin_reg = devm_regulator_get(&client->dev, "vin");
    220	if (IS_ERR(priv->vin_reg))
    221		return dev_err_probe(&client->dev, PTR_ERR(priv->vin_reg),
    222				     "get regulator vin failed\n");
    223
    224	ret = regulator_enable(priv->vin_reg);
    225	if (ret) {
    226		dev_err(&client->dev, "enable regulator vin failed\n");
    227		return ret;
    228	}
    229
    230	ret = devm_add_action_or_reset(&client->dev, noa1305_reg_remove, priv);
    231	if (ret) {
    232		dev_err(&client->dev, "addition of devm action failed\n");
    233		return ret;
    234	}
    235
    236	i2c_set_clientdata(client, indio_dev);
    237	priv->client = client;
    238	priv->regmap = regmap;
    239
    240	ret = regmap_bulk_read(regmap, NOA1305_REG_DEVICE_ID_LSB, &data, 2);
    241	if (ret < 0) {
    242		dev_err(&client->dev, "ID reading failed: %d\n", ret);
    243		return ret;
    244	}
    245
    246	dev_id = le16_to_cpu(data);
    247	if (dev_id != NOA1305_DEVICE_ID) {
    248		dev_err(&client->dev, "Unknown device ID: 0x%x\n", dev_id);
    249		return -ENODEV;
    250	}
    251
    252	ret = regmap_write(regmap, NOA1305_REG_POWER_CONTROL,
    253			   NOA1305_POWER_CONTROL_ON);
    254	if (ret < 0) {
    255		dev_err(&client->dev, "Enabling power control failed\n");
    256		return ret;
    257	}
    258
    259	ret = regmap_write(regmap, NOA1305_REG_RESET, NOA1305_RESET_RESET);
    260	if (ret < 0) {
    261		dev_err(&client->dev, "Device reset failed\n");
    262		return ret;
    263	}
    264
    265	ret = regmap_write(regmap, NOA1305_REG_INTEGRATION_TIME,
    266			   NOA1305_INTEGR_TIME_800MS);
    267	if (ret < 0) {
    268		dev_err(&client->dev, "Setting integration time failed\n");
    269		return ret;
    270	}
    271
    272	indio_dev->info = &noa1305_info;
    273	indio_dev->channels = noa1305_channels;
    274	indio_dev->num_channels = ARRAY_SIZE(noa1305_channels);
    275	indio_dev->name = NOA1305_DRIVER_NAME;
    276	indio_dev->modes = INDIO_DIRECT_MODE;
    277
    278	ret = devm_iio_device_register(&client->dev, indio_dev);
    279	if (ret)
    280		dev_err(&client->dev, "registering device failed\n");
    281
    282	return ret;
    283}
    284
    285static const struct of_device_id noa1305_of_match[] = {
    286	{ .compatible = "onnn,noa1305" },
    287	{ }
    288};
    289MODULE_DEVICE_TABLE(of, noa1305_of_match);
    290
    291static const struct i2c_device_id noa1305_ids[] = {
    292	{ "noa1305", 0 },
    293	{ }
    294};
    295MODULE_DEVICE_TABLE(i2c, noa1305_ids);
    296
    297static struct i2c_driver noa1305_driver = {
    298	.driver = {
    299		.name		= NOA1305_DRIVER_NAME,
    300		.of_match_table	= noa1305_of_match,
    301	},
    302	.probe		= noa1305_probe,
    303	.id_table	= noa1305_ids,
    304};
    305
    306module_i2c_driver(noa1305_driver);
    307
    308MODULE_AUTHOR("Sergei Miroshnichenko <sergeimir@emcraft.com>");
    309MODULE_AUTHOR("Martyn Welch <martyn.welch@collabora.com");
    310MODULE_DESCRIPTION("ON Semiconductor NOA1305 ambient light sensor");
    311MODULE_LICENSE("GPL");