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

al3320a.c (6482B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * AL3320A - Dyna Image Ambient Light Sensor
      4 *
      5 * Copyright (c) 2014, Intel Corporation.
      6 *
      7 * IIO driver for AL3320A (7-bit I2C slave address 0x1C).
      8 *
      9 * TODO: interrupt support, thresholds
     10 * When the driver will get support for interrupt handling, then interrupt
     11 * will need to be disabled before turning sensor OFF in order to avoid
     12 * potential races with the interrupt handling.
     13 */
     14
     15#include <linux/bitfield.h>
     16#include <linux/i2c.h>
     17#include <linux/module.h>
     18#include <linux/of.h>
     19
     20#include <linux/iio/iio.h>
     21#include <linux/iio/sysfs.h>
     22
     23#define AL3320A_DRV_NAME "al3320a"
     24
     25#define AL3320A_REG_CONFIG		0x00
     26#define AL3320A_REG_STATUS		0x01
     27#define AL3320A_REG_INT			0x02
     28#define AL3320A_REG_WAIT		0x06
     29#define AL3320A_REG_CONFIG_RANGE	0x07
     30#define AL3320A_REG_PERSIST		0x08
     31#define AL3320A_REG_MEAN_TIME		0x09
     32#define AL3320A_REG_ADUMMY		0x0A
     33#define AL3320A_REG_DATA_LOW		0x22
     34
     35#define AL3320A_REG_LOW_THRESH_LOW	0x30
     36#define AL3320A_REG_LOW_THRESH_HIGH	0x31
     37#define AL3320A_REG_HIGH_THRESH_LOW	0x32
     38#define AL3320A_REG_HIGH_THRESH_HIGH	0x33
     39
     40#define AL3320A_CONFIG_DISABLE		0x00
     41#define AL3320A_CONFIG_ENABLE		0x01
     42
     43#define AL3320A_GAIN_MASK		GENMASK(2, 1)
     44
     45/* chip params default values */
     46#define AL3320A_DEFAULT_MEAN_TIME	4
     47#define AL3320A_DEFAULT_WAIT_TIME	0 /* no waiting */
     48
     49#define AL3320A_SCALE_AVAILABLE "0.512 0.128 0.032 0.01"
     50
     51enum al3320a_range {
     52	AL3320A_RANGE_1, /* 33.28 Klx */
     53	AL3320A_RANGE_2, /* 8.32 Klx  */
     54	AL3320A_RANGE_3, /* 2.08 Klx  */
     55	AL3320A_RANGE_4  /* 0.65 Klx  */
     56};
     57
     58static const int al3320a_scales[][2] = {
     59	{0, 512000}, {0, 128000}, {0, 32000}, {0, 10000}
     60};
     61
     62struct al3320a_data {
     63	struct i2c_client *client;
     64};
     65
     66static const struct iio_chan_spec al3320a_channels[] = {
     67	{
     68		.type	= IIO_LIGHT,
     69		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
     70				      BIT(IIO_CHAN_INFO_SCALE),
     71	}
     72};
     73
     74static IIO_CONST_ATTR(in_illuminance_scale_available, AL3320A_SCALE_AVAILABLE);
     75
     76static struct attribute *al3320a_attributes[] = {
     77	&iio_const_attr_in_illuminance_scale_available.dev_attr.attr,
     78	NULL,
     79};
     80
     81static const struct attribute_group al3320a_attribute_group = {
     82	.attrs = al3320a_attributes,
     83};
     84
     85static int al3320a_set_pwr(struct i2c_client *client, bool pwr)
     86{
     87	u8 val = pwr ? AL3320A_CONFIG_ENABLE : AL3320A_CONFIG_DISABLE;
     88	return i2c_smbus_write_byte_data(client, AL3320A_REG_CONFIG, val);
     89}
     90
     91static void al3320a_set_pwr_off(void *_data)
     92{
     93	struct al3320a_data *data = _data;
     94
     95	al3320a_set_pwr(data->client, false);
     96}
     97
     98static int al3320a_init(struct al3320a_data *data)
     99{
    100	int ret;
    101
    102	ret = al3320a_set_pwr(data->client, true);
    103
    104	if (ret < 0)
    105		return ret;
    106
    107	ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_CONFIG_RANGE,
    108					FIELD_PREP(AL3320A_GAIN_MASK,
    109						   AL3320A_RANGE_3));
    110	if (ret < 0)
    111		return ret;
    112
    113	ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_MEAN_TIME,
    114					AL3320A_DEFAULT_MEAN_TIME);
    115	if (ret < 0)
    116		return ret;
    117
    118	ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_WAIT,
    119					AL3320A_DEFAULT_WAIT_TIME);
    120	if (ret < 0)
    121		return ret;
    122
    123	return 0;
    124}
    125
    126static int al3320a_read_raw(struct iio_dev *indio_dev,
    127			    struct iio_chan_spec const *chan, int *val,
    128			    int *val2, long mask)
    129{
    130	struct al3320a_data *data = iio_priv(indio_dev);
    131	int ret;
    132
    133	switch (mask) {
    134	case IIO_CHAN_INFO_RAW:
    135		/*
    136		 * ALS ADC value is stored in two adjacent registers:
    137		 * - low byte of output is stored at AL3320A_REG_DATA_LOW
    138		 * - high byte of output is stored at AL3320A_REG_DATA_LOW + 1
    139		 */
    140		ret = i2c_smbus_read_word_data(data->client,
    141					       AL3320A_REG_DATA_LOW);
    142		if (ret < 0)
    143			return ret;
    144		*val = ret;
    145		return IIO_VAL_INT;
    146	case IIO_CHAN_INFO_SCALE:
    147		ret = i2c_smbus_read_byte_data(data->client,
    148					       AL3320A_REG_CONFIG_RANGE);
    149		if (ret < 0)
    150			return ret;
    151
    152		ret = FIELD_GET(AL3320A_GAIN_MASK, ret);
    153		*val = al3320a_scales[ret][0];
    154		*val2 = al3320a_scales[ret][1];
    155
    156		return IIO_VAL_INT_PLUS_MICRO;
    157	}
    158	return -EINVAL;
    159}
    160
    161static int al3320a_write_raw(struct iio_dev *indio_dev,
    162			     struct iio_chan_spec const *chan, int val,
    163			     int val2, long mask)
    164{
    165	struct al3320a_data *data = iio_priv(indio_dev);
    166	int i;
    167
    168	switch (mask) {
    169	case IIO_CHAN_INFO_SCALE:
    170		for (i = 0; i < ARRAY_SIZE(al3320a_scales); i++) {
    171			if (val != al3320a_scales[i][0] ||
    172			    val2 != al3320a_scales[i][1])
    173				continue;
    174
    175			return i2c_smbus_write_byte_data(data->client,
    176					AL3320A_REG_CONFIG_RANGE,
    177					FIELD_PREP(AL3320A_GAIN_MASK, i));
    178		}
    179		break;
    180	}
    181	return -EINVAL;
    182}
    183
    184static const struct iio_info al3320a_info = {
    185	.read_raw	= al3320a_read_raw,
    186	.write_raw	= al3320a_write_raw,
    187	.attrs		= &al3320a_attribute_group,
    188};
    189
    190static int al3320a_probe(struct i2c_client *client,
    191			 const struct i2c_device_id *id)
    192{
    193	struct al3320a_data *data;
    194	struct iio_dev *indio_dev;
    195	int ret;
    196
    197	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
    198	if (!indio_dev)
    199		return -ENOMEM;
    200
    201	data = iio_priv(indio_dev);
    202	i2c_set_clientdata(client, indio_dev);
    203	data->client = client;
    204
    205	indio_dev->info = &al3320a_info;
    206	indio_dev->name = AL3320A_DRV_NAME;
    207	indio_dev->channels = al3320a_channels;
    208	indio_dev->num_channels = ARRAY_SIZE(al3320a_channels);
    209	indio_dev->modes = INDIO_DIRECT_MODE;
    210
    211	ret = al3320a_init(data);
    212	if (ret < 0) {
    213		dev_err(&client->dev, "al3320a chip init failed\n");
    214		return ret;
    215	}
    216
    217	ret = devm_add_action_or_reset(&client->dev,
    218					al3320a_set_pwr_off,
    219					data);
    220	if (ret < 0)
    221		return ret;
    222
    223	return devm_iio_device_register(&client->dev, indio_dev);
    224}
    225
    226static int __maybe_unused al3320a_suspend(struct device *dev)
    227{
    228	return al3320a_set_pwr(to_i2c_client(dev), false);
    229}
    230
    231static int __maybe_unused al3320a_resume(struct device *dev)
    232{
    233	return al3320a_set_pwr(to_i2c_client(dev), true);
    234}
    235
    236static SIMPLE_DEV_PM_OPS(al3320a_pm_ops, al3320a_suspend, al3320a_resume);
    237
    238static const struct i2c_device_id al3320a_id[] = {
    239	{"al3320a", 0},
    240	{}
    241};
    242MODULE_DEVICE_TABLE(i2c, al3320a_id);
    243
    244static const struct of_device_id al3320a_of_match[] = {
    245	{ .compatible = "dynaimage,al3320a", },
    246	{},
    247};
    248MODULE_DEVICE_TABLE(of, al3320a_of_match);
    249
    250static struct i2c_driver al3320a_driver = {
    251	.driver = {
    252		.name = AL3320A_DRV_NAME,
    253		.of_match_table = al3320a_of_match,
    254		.pm = &al3320a_pm_ops,
    255	},
    256	.probe		= al3320a_probe,
    257	.id_table	= al3320a_id,
    258};
    259
    260module_i2c_driver(al3320a_driver);
    261
    262MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
    263MODULE_DESCRIPTION("AL3320A Ambient Light Sensor driver");
    264MODULE_LICENSE("GPL v2");