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

cm3232.c (10624B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * CM3232 Ambient Light Sensor
      4 *
      5 * Copyright (C) 2014-2015 Capella Microsystems Inc.
      6 * Author: Kevin Tsai <ktsai@capellamicro.com>
      7 *
      8 * IIO driver for CM3232 (7-bit I2C slave address 0x10).
      9 */
     10
     11#include <linux/i2c.h>
     12#include <linux/module.h>
     13#include <linux/mod_devicetable.h>
     14#include <linux/iio/iio.h>
     15#include <linux/iio/sysfs.h>
     16#include <linux/init.h>
     17
     18/* Registers Address */
     19#define CM3232_REG_ADDR_CMD		0x00
     20#define CM3232_REG_ADDR_ALS		0x50
     21#define CM3232_REG_ADDR_ID		0x53
     22
     23#define CM3232_CMD_ALS_DISABLE		BIT(0)
     24
     25#define CM3232_CMD_ALS_IT_SHIFT		2
     26#define CM3232_CMD_ALS_IT_MASK		(BIT(2) | BIT(3) | BIT(4))
     27#define CM3232_CMD_ALS_IT_DEFAULT	(0x01 << CM3232_CMD_ALS_IT_SHIFT)
     28
     29#define CM3232_CMD_ALS_RESET		BIT(6)
     30
     31#define CM3232_CMD_DEFAULT		CM3232_CMD_ALS_IT_DEFAULT
     32
     33#define CM3232_HW_ID			0x32
     34#define CM3232_CALIBSCALE_DEFAULT	100000
     35#define CM3232_CALIBSCALE_RESOLUTION	100000
     36#define CM3232_MLUX_PER_LUX		1000
     37
     38#define CM3232_MLUX_PER_BIT_DEFAULT	64
     39#define CM3232_MLUX_PER_BIT_BASE_IT	100000
     40
     41static const struct {
     42	int val;
     43	int val2;
     44	u8 it;
     45} cm3232_als_it_scales[] = {
     46	{0, 100000, 0},	/* 0.100000 */
     47	{0, 200000, 1},	/* 0.200000 */
     48	{0, 400000, 2},	/* 0.400000 */
     49	{0, 800000, 3},	/* 0.800000 */
     50	{1, 600000, 4},	/* 1.600000 */
     51	{3, 200000, 5},	/* 3.200000 */
     52};
     53
     54struct cm3232_als_info {
     55	u8 regs_cmd_default;
     56	u8 hw_id;
     57	int calibscale;
     58	int mlux_per_bit;
     59	int mlux_per_bit_base_it;
     60};
     61
     62static struct cm3232_als_info cm3232_als_info_default = {
     63	.regs_cmd_default = CM3232_CMD_DEFAULT,
     64	.hw_id = CM3232_HW_ID,
     65	.calibscale = CM3232_CALIBSCALE_DEFAULT,
     66	.mlux_per_bit = CM3232_MLUX_PER_BIT_DEFAULT,
     67	.mlux_per_bit_base_it = CM3232_MLUX_PER_BIT_BASE_IT,
     68};
     69
     70struct cm3232_chip {
     71	struct i2c_client *client;
     72	struct cm3232_als_info *als_info;
     73	u8 regs_cmd;
     74	u16 regs_als;
     75};
     76
     77/**
     78 * cm3232_reg_init() - Initialize CM3232
     79 * @chip:	pointer of struct cm3232_chip.
     80 *
     81 * Check and initialize CM3232 ambient light sensor.
     82 *
     83 * Return: 0 for success; otherwise for error code.
     84 */
     85static int cm3232_reg_init(struct cm3232_chip *chip)
     86{
     87	struct i2c_client *client = chip->client;
     88	s32 ret;
     89
     90	chip->als_info = &cm3232_als_info_default;
     91
     92	/* Identify device */
     93	ret = i2c_smbus_read_word_data(client, CM3232_REG_ADDR_ID);
     94	if (ret < 0) {
     95		dev_err(&chip->client->dev, "Error reading addr_id\n");
     96		return ret;
     97	}
     98
     99	if ((ret & 0xFF) != chip->als_info->hw_id)
    100		return -ENODEV;
    101
    102	/* Disable and reset device */
    103	chip->regs_cmd = CM3232_CMD_ALS_DISABLE | CM3232_CMD_ALS_RESET;
    104	ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
    105					chip->regs_cmd);
    106	if (ret < 0) {
    107		dev_err(&chip->client->dev, "Error writing reg_cmd\n");
    108		return ret;
    109	}
    110
    111	/* Register default value */
    112	chip->regs_cmd = chip->als_info->regs_cmd_default;
    113
    114	/* Configure register */
    115	ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
    116					chip->regs_cmd);
    117	if (ret < 0)
    118		dev_err(&chip->client->dev, "Error writing reg_cmd\n");
    119
    120	return ret;
    121}
    122
    123/**
    124 *  cm3232_read_als_it() - Get sensor integration time
    125 *  @chip:	pointer of struct cm3232_chip
    126 *  @val:	pointer of int to load the integration (sec).
    127 *  @val2:	pointer of int to load the integration time (microsecond).
    128 *
    129 *  Report the current integration time.
    130 *
    131 *  Return: IIO_VAL_INT_PLUS_MICRO for success, otherwise -EINVAL.
    132 */
    133static int cm3232_read_als_it(struct cm3232_chip *chip, int *val, int *val2)
    134{
    135	u16 als_it;
    136	int i;
    137
    138	als_it = chip->regs_cmd;
    139	als_it &= CM3232_CMD_ALS_IT_MASK;
    140	als_it >>= CM3232_CMD_ALS_IT_SHIFT;
    141	for (i = 0; i < ARRAY_SIZE(cm3232_als_it_scales); i++) {
    142		if (als_it == cm3232_als_it_scales[i].it) {
    143			*val = cm3232_als_it_scales[i].val;
    144			*val2 = cm3232_als_it_scales[i].val2;
    145			return IIO_VAL_INT_PLUS_MICRO;
    146		}
    147	}
    148
    149	return -EINVAL;
    150}
    151
    152/**
    153 * cm3232_write_als_it() - Write sensor integration time
    154 * @chip:	pointer of struct cm3232_chip.
    155 * @val:	integration time in second.
    156 * @val2:	integration time in microsecond.
    157 *
    158 * Convert integration time to sensor value.
    159 *
    160 * Return: i2c_smbus_write_byte_data command return value.
    161 */
    162static int cm3232_write_als_it(struct cm3232_chip *chip, int val, int val2)
    163{
    164	struct i2c_client *client = chip->client;
    165	u16 als_it, cmd;
    166	int i;
    167	s32 ret;
    168
    169	for (i = 0; i < ARRAY_SIZE(cm3232_als_it_scales); i++) {
    170		if (val == cm3232_als_it_scales[i].val &&
    171			val2 == cm3232_als_it_scales[i].val2) {
    172
    173			als_it = cm3232_als_it_scales[i].it;
    174			als_it <<= CM3232_CMD_ALS_IT_SHIFT;
    175
    176			cmd = chip->regs_cmd & ~CM3232_CMD_ALS_IT_MASK;
    177			cmd |= als_it;
    178			ret = i2c_smbus_write_byte_data(client,
    179							CM3232_REG_ADDR_CMD,
    180							cmd);
    181			if (ret < 0)
    182				return ret;
    183			chip->regs_cmd = cmd;
    184			return 0;
    185		}
    186	}
    187	return -EINVAL;
    188}
    189
    190/**
    191 * cm3232_get_lux() - report current lux value
    192 * @chip:	pointer of struct cm3232_chip.
    193 *
    194 * Convert sensor data to lux.  It depends on integration
    195 * time and calibscale variable.
    196 *
    197 * Return: Zero or positive value is lux, otherwise error code.
    198 */
    199static int cm3232_get_lux(struct cm3232_chip *chip)
    200{
    201	struct i2c_client *client = chip->client;
    202	struct cm3232_als_info *als_info = chip->als_info;
    203	int ret;
    204	int val, val2;
    205	int als_it;
    206	u64 lux;
    207
    208	/* Calculate mlux per bit based on als_it */
    209	ret = cm3232_read_als_it(chip, &val, &val2);
    210	if (ret < 0)
    211		return -EINVAL;
    212	als_it = val * 1000000 + val2;
    213	lux = (__force u64)als_info->mlux_per_bit;
    214	lux *= als_info->mlux_per_bit_base_it;
    215	lux = div_u64(lux, als_it);
    216
    217	ret = i2c_smbus_read_word_data(client, CM3232_REG_ADDR_ALS);
    218	if (ret < 0) {
    219		dev_err(&client->dev, "Error reading reg_addr_als\n");
    220		return ret;
    221	}
    222
    223	chip->regs_als = (u16)ret;
    224	lux *= chip->regs_als;
    225	lux *= als_info->calibscale;
    226	lux = div_u64(lux, CM3232_CALIBSCALE_RESOLUTION);
    227	lux = div_u64(lux, CM3232_MLUX_PER_LUX);
    228
    229	if (lux > 0xFFFF)
    230		lux = 0xFFFF;
    231
    232	return (int)lux;
    233}
    234
    235static int cm3232_read_raw(struct iio_dev *indio_dev,
    236			struct iio_chan_spec const *chan,
    237			int *val, int *val2, long mask)
    238{
    239	struct cm3232_chip *chip = iio_priv(indio_dev);
    240	struct cm3232_als_info *als_info = chip->als_info;
    241	int ret;
    242
    243	switch (mask) {
    244	case IIO_CHAN_INFO_PROCESSED:
    245		ret = cm3232_get_lux(chip);
    246		if (ret < 0)
    247			return ret;
    248		*val = ret;
    249		return IIO_VAL_INT;
    250	case IIO_CHAN_INFO_CALIBSCALE:
    251		*val = als_info->calibscale;
    252		return IIO_VAL_INT;
    253	case IIO_CHAN_INFO_INT_TIME:
    254		return cm3232_read_als_it(chip, val, val2);
    255	}
    256
    257	return -EINVAL;
    258}
    259
    260static int cm3232_write_raw(struct iio_dev *indio_dev,
    261			struct iio_chan_spec const *chan,
    262			int val, int val2, long mask)
    263{
    264	struct cm3232_chip *chip = iio_priv(indio_dev);
    265	struct cm3232_als_info *als_info = chip->als_info;
    266
    267	switch (mask) {
    268	case IIO_CHAN_INFO_CALIBSCALE:
    269		als_info->calibscale = val;
    270		return 0;
    271	case IIO_CHAN_INFO_INT_TIME:
    272		return cm3232_write_als_it(chip, val, val2);
    273	}
    274
    275	return -EINVAL;
    276}
    277
    278/**
    279 * cm3232_get_it_available() - Get available ALS IT value
    280 * @dev:	pointer of struct device.
    281 * @attr:	pointer of struct device_attribute.
    282 * @buf:	pointer of return string buffer.
    283 *
    284 * Display the available integration time in second.
    285 *
    286 * Return: string length.
    287 */
    288static ssize_t cm3232_get_it_available(struct device *dev,
    289			struct device_attribute *attr, char *buf)
    290{
    291	int i, len;
    292
    293	for (i = 0, len = 0; i < ARRAY_SIZE(cm3232_als_it_scales); i++)
    294		len += scnprintf(buf + len, PAGE_SIZE - len, "%u.%06u ",
    295			cm3232_als_it_scales[i].val,
    296			cm3232_als_it_scales[i].val2);
    297	return len + scnprintf(buf + len, PAGE_SIZE - len, "\n");
    298}
    299
    300static const struct iio_chan_spec cm3232_channels[] = {
    301	{
    302		.type = IIO_LIGHT,
    303		.info_mask_separate =
    304			BIT(IIO_CHAN_INFO_PROCESSED) |
    305			BIT(IIO_CHAN_INFO_CALIBSCALE) |
    306			BIT(IIO_CHAN_INFO_INT_TIME),
    307	}
    308};
    309
    310static IIO_DEVICE_ATTR(in_illuminance_integration_time_available,
    311			S_IRUGO, cm3232_get_it_available, NULL, 0);
    312
    313static struct attribute *cm3232_attributes[] = {
    314	&iio_dev_attr_in_illuminance_integration_time_available.dev_attr.attr,
    315	NULL,
    316};
    317
    318static const struct attribute_group cm3232_attribute_group = {
    319	.attrs = cm3232_attributes
    320};
    321
    322static const struct iio_info cm3232_info = {
    323	.read_raw		= &cm3232_read_raw,
    324	.write_raw		= &cm3232_write_raw,
    325	.attrs			= &cm3232_attribute_group,
    326};
    327
    328static int cm3232_probe(struct i2c_client *client,
    329			const struct i2c_device_id *id)
    330{
    331	struct cm3232_chip *chip;
    332	struct iio_dev *indio_dev;
    333	int ret;
    334
    335	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
    336	if (!indio_dev)
    337		return -ENOMEM;
    338
    339	chip = iio_priv(indio_dev);
    340	i2c_set_clientdata(client, indio_dev);
    341	chip->client = client;
    342
    343	indio_dev->channels = cm3232_channels;
    344	indio_dev->num_channels = ARRAY_SIZE(cm3232_channels);
    345	indio_dev->info = &cm3232_info;
    346	indio_dev->name = id->name;
    347	indio_dev->modes = INDIO_DIRECT_MODE;
    348
    349	ret = cm3232_reg_init(chip);
    350	if (ret) {
    351		dev_err(&client->dev,
    352			"%s: register init failed\n",
    353			__func__);
    354		return ret;
    355	}
    356
    357	return iio_device_register(indio_dev);
    358}
    359
    360static int cm3232_remove(struct i2c_client *client)
    361{
    362	struct iio_dev *indio_dev = i2c_get_clientdata(client);
    363
    364	i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
    365		CM3232_CMD_ALS_DISABLE);
    366
    367	iio_device_unregister(indio_dev);
    368
    369	return 0;
    370}
    371
    372static const struct i2c_device_id cm3232_id[] = {
    373	{"cm3232", 0},
    374	{}
    375};
    376
    377static int cm3232_suspend(struct device *dev)
    378{
    379	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
    380	struct cm3232_chip *chip = iio_priv(indio_dev);
    381	struct i2c_client *client = chip->client;
    382	int ret;
    383
    384	chip->regs_cmd |= CM3232_CMD_ALS_DISABLE;
    385	ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
    386					chip->regs_cmd);
    387
    388	return ret;
    389}
    390
    391static int cm3232_resume(struct device *dev)
    392{
    393	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
    394	struct cm3232_chip *chip = iio_priv(indio_dev);
    395	struct i2c_client *client = chip->client;
    396	int ret;
    397
    398	chip->regs_cmd &= ~CM3232_CMD_ALS_DISABLE;
    399	ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
    400					chip->regs_cmd | CM3232_CMD_ALS_RESET);
    401
    402	return ret;
    403}
    404
    405static DEFINE_SIMPLE_DEV_PM_OPS(cm3232_pm_ops, cm3232_suspend, cm3232_resume);
    406
    407MODULE_DEVICE_TABLE(i2c, cm3232_id);
    408
    409static const struct of_device_id cm3232_of_match[] = {
    410	{.compatible = "capella,cm3232"},
    411	{}
    412};
    413MODULE_DEVICE_TABLE(of, cm3232_of_match);
    414
    415static struct i2c_driver cm3232_driver = {
    416	.driver = {
    417		.name	= "cm3232",
    418		.of_match_table = cm3232_of_match,
    419		.pm	= pm_sleep_ptr(&cm3232_pm_ops),
    420	},
    421	.id_table	= cm3232_id,
    422	.probe		= cm3232_probe,
    423	.remove		= cm3232_remove,
    424};
    425
    426module_i2c_driver(cm3232_driver);
    427
    428MODULE_AUTHOR("Kevin Tsai <ktsai@capellamicro.com>");
    429MODULE_DESCRIPTION("CM3232 ambient light sensor driver");
    430MODULE_LICENSE("GPL");