162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * CM3232 Ambient Light Sensor
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2014-2015 Capella Microsystems Inc.
662306a36Sopenharmony_ci * Author: Kevin Tsai <ktsai@capellamicro.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * IIO driver for CM3232 (7-bit I2C slave address 0x10).
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/i2c.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/mod_devicetable.h>
1462306a36Sopenharmony_ci#include <linux/iio/iio.h>
1562306a36Sopenharmony_ci#include <linux/iio/sysfs.h>
1662306a36Sopenharmony_ci#include <linux/init.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/* Registers Address */
1962306a36Sopenharmony_ci#define CM3232_REG_ADDR_CMD		0x00
2062306a36Sopenharmony_ci#define CM3232_REG_ADDR_ALS		0x50
2162306a36Sopenharmony_ci#define CM3232_REG_ADDR_ID		0x53
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define CM3232_CMD_ALS_DISABLE		BIT(0)
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define CM3232_CMD_ALS_IT_SHIFT		2
2662306a36Sopenharmony_ci#define CM3232_CMD_ALS_IT_MASK		(BIT(2) | BIT(3) | BIT(4))
2762306a36Sopenharmony_ci#define CM3232_CMD_ALS_IT_DEFAULT	(0x01 << CM3232_CMD_ALS_IT_SHIFT)
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define CM3232_CMD_ALS_RESET		BIT(6)
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define CM3232_CMD_DEFAULT		CM3232_CMD_ALS_IT_DEFAULT
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define CM3232_HW_ID			0x32
3462306a36Sopenharmony_ci#define CM3232_CALIBSCALE_DEFAULT	100000
3562306a36Sopenharmony_ci#define CM3232_CALIBSCALE_RESOLUTION	100000
3662306a36Sopenharmony_ci#define CM3232_MLUX_PER_LUX		1000
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define CM3232_MLUX_PER_BIT_DEFAULT	64
3962306a36Sopenharmony_ci#define CM3232_MLUX_PER_BIT_BASE_IT	100000
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic const struct {
4262306a36Sopenharmony_ci	int val;
4362306a36Sopenharmony_ci	int val2;
4462306a36Sopenharmony_ci	u8 it;
4562306a36Sopenharmony_ci} cm3232_als_it_scales[] = {
4662306a36Sopenharmony_ci	{0, 100000, 0},	/* 0.100000 */
4762306a36Sopenharmony_ci	{0, 200000, 1},	/* 0.200000 */
4862306a36Sopenharmony_ci	{0, 400000, 2},	/* 0.400000 */
4962306a36Sopenharmony_ci	{0, 800000, 3},	/* 0.800000 */
5062306a36Sopenharmony_ci	{1, 600000, 4},	/* 1.600000 */
5162306a36Sopenharmony_ci	{3, 200000, 5},	/* 3.200000 */
5262306a36Sopenharmony_ci};
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistruct cm3232_als_info {
5562306a36Sopenharmony_ci	u8 regs_cmd_default;
5662306a36Sopenharmony_ci	u8 hw_id;
5762306a36Sopenharmony_ci	int calibscale;
5862306a36Sopenharmony_ci	int mlux_per_bit;
5962306a36Sopenharmony_ci	int mlux_per_bit_base_it;
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic struct cm3232_als_info cm3232_als_info_default = {
6362306a36Sopenharmony_ci	.regs_cmd_default = CM3232_CMD_DEFAULT,
6462306a36Sopenharmony_ci	.hw_id = CM3232_HW_ID,
6562306a36Sopenharmony_ci	.calibscale = CM3232_CALIBSCALE_DEFAULT,
6662306a36Sopenharmony_ci	.mlux_per_bit = CM3232_MLUX_PER_BIT_DEFAULT,
6762306a36Sopenharmony_ci	.mlux_per_bit_base_it = CM3232_MLUX_PER_BIT_BASE_IT,
6862306a36Sopenharmony_ci};
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistruct cm3232_chip {
7162306a36Sopenharmony_ci	struct i2c_client *client;
7262306a36Sopenharmony_ci	struct cm3232_als_info *als_info;
7362306a36Sopenharmony_ci	u8 regs_cmd;
7462306a36Sopenharmony_ci	u16 regs_als;
7562306a36Sopenharmony_ci};
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci/**
7862306a36Sopenharmony_ci * cm3232_reg_init() - Initialize CM3232
7962306a36Sopenharmony_ci * @chip:	pointer of struct cm3232_chip.
8062306a36Sopenharmony_ci *
8162306a36Sopenharmony_ci * Check and initialize CM3232 ambient light sensor.
8262306a36Sopenharmony_ci *
8362306a36Sopenharmony_ci * Return: 0 for success; otherwise for error code.
8462306a36Sopenharmony_ci */
8562306a36Sopenharmony_cistatic int cm3232_reg_init(struct cm3232_chip *chip)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	struct i2c_client *client = chip->client;
8862306a36Sopenharmony_ci	s32 ret;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	chip->als_info = &cm3232_als_info_default;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	/* Identify device */
9362306a36Sopenharmony_ci	ret = i2c_smbus_read_word_data(client, CM3232_REG_ADDR_ID);
9462306a36Sopenharmony_ci	if (ret < 0) {
9562306a36Sopenharmony_ci		dev_err(&chip->client->dev, "Error reading addr_id\n");
9662306a36Sopenharmony_ci		return ret;
9762306a36Sopenharmony_ci	}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	if ((ret & 0xFF) != chip->als_info->hw_id)
10062306a36Sopenharmony_ci		return -ENODEV;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	/* Disable and reset device */
10362306a36Sopenharmony_ci	chip->regs_cmd = CM3232_CMD_ALS_DISABLE | CM3232_CMD_ALS_RESET;
10462306a36Sopenharmony_ci	ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
10562306a36Sopenharmony_ci					chip->regs_cmd);
10662306a36Sopenharmony_ci	if (ret < 0) {
10762306a36Sopenharmony_ci		dev_err(&chip->client->dev, "Error writing reg_cmd\n");
10862306a36Sopenharmony_ci		return ret;
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	/* Register default value */
11262306a36Sopenharmony_ci	chip->regs_cmd = chip->als_info->regs_cmd_default;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	/* Configure register */
11562306a36Sopenharmony_ci	ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
11662306a36Sopenharmony_ci					chip->regs_cmd);
11762306a36Sopenharmony_ci	if (ret < 0)
11862306a36Sopenharmony_ci		dev_err(&chip->client->dev, "Error writing reg_cmd\n");
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	return ret;
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci/**
12462306a36Sopenharmony_ci *  cm3232_read_als_it() - Get sensor integration time
12562306a36Sopenharmony_ci *  @chip:	pointer of struct cm3232_chip
12662306a36Sopenharmony_ci *  @val:	pointer of int to load the integration (sec).
12762306a36Sopenharmony_ci *  @val2:	pointer of int to load the integration time (microsecond).
12862306a36Sopenharmony_ci *
12962306a36Sopenharmony_ci *  Report the current integration time.
13062306a36Sopenharmony_ci *
13162306a36Sopenharmony_ci *  Return: IIO_VAL_INT_PLUS_MICRO for success, otherwise -EINVAL.
13262306a36Sopenharmony_ci */
13362306a36Sopenharmony_cistatic int cm3232_read_als_it(struct cm3232_chip *chip, int *val, int *val2)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	u16 als_it;
13662306a36Sopenharmony_ci	int i;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	als_it = chip->regs_cmd;
13962306a36Sopenharmony_ci	als_it &= CM3232_CMD_ALS_IT_MASK;
14062306a36Sopenharmony_ci	als_it >>= CM3232_CMD_ALS_IT_SHIFT;
14162306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(cm3232_als_it_scales); i++) {
14262306a36Sopenharmony_ci		if (als_it == cm3232_als_it_scales[i].it) {
14362306a36Sopenharmony_ci			*val = cm3232_als_it_scales[i].val;
14462306a36Sopenharmony_ci			*val2 = cm3232_als_it_scales[i].val2;
14562306a36Sopenharmony_ci			return IIO_VAL_INT_PLUS_MICRO;
14662306a36Sopenharmony_ci		}
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	return -EINVAL;
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci/**
15362306a36Sopenharmony_ci * cm3232_write_als_it() - Write sensor integration time
15462306a36Sopenharmony_ci * @chip:	pointer of struct cm3232_chip.
15562306a36Sopenharmony_ci * @val:	integration time in second.
15662306a36Sopenharmony_ci * @val2:	integration time in microsecond.
15762306a36Sopenharmony_ci *
15862306a36Sopenharmony_ci * Convert integration time to sensor value.
15962306a36Sopenharmony_ci *
16062306a36Sopenharmony_ci * Return: i2c_smbus_write_byte_data command return value.
16162306a36Sopenharmony_ci */
16262306a36Sopenharmony_cistatic int cm3232_write_als_it(struct cm3232_chip *chip, int val, int val2)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	struct i2c_client *client = chip->client;
16562306a36Sopenharmony_ci	u16 als_it, cmd;
16662306a36Sopenharmony_ci	int i;
16762306a36Sopenharmony_ci	s32 ret;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(cm3232_als_it_scales); i++) {
17062306a36Sopenharmony_ci		if (val == cm3232_als_it_scales[i].val &&
17162306a36Sopenharmony_ci			val2 == cm3232_als_it_scales[i].val2) {
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci			als_it = cm3232_als_it_scales[i].it;
17462306a36Sopenharmony_ci			als_it <<= CM3232_CMD_ALS_IT_SHIFT;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci			cmd = chip->regs_cmd & ~CM3232_CMD_ALS_IT_MASK;
17762306a36Sopenharmony_ci			cmd |= als_it;
17862306a36Sopenharmony_ci			ret = i2c_smbus_write_byte_data(client,
17962306a36Sopenharmony_ci							CM3232_REG_ADDR_CMD,
18062306a36Sopenharmony_ci							cmd);
18162306a36Sopenharmony_ci			if (ret < 0)
18262306a36Sopenharmony_ci				return ret;
18362306a36Sopenharmony_ci			chip->regs_cmd = cmd;
18462306a36Sopenharmony_ci			return 0;
18562306a36Sopenharmony_ci		}
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci	return -EINVAL;
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci/**
19162306a36Sopenharmony_ci * cm3232_get_lux() - report current lux value
19262306a36Sopenharmony_ci * @chip:	pointer of struct cm3232_chip.
19362306a36Sopenharmony_ci *
19462306a36Sopenharmony_ci * Convert sensor data to lux.  It depends on integration
19562306a36Sopenharmony_ci * time and calibscale variable.
19662306a36Sopenharmony_ci *
19762306a36Sopenharmony_ci * Return: Zero or positive value is lux, otherwise error code.
19862306a36Sopenharmony_ci */
19962306a36Sopenharmony_cistatic int cm3232_get_lux(struct cm3232_chip *chip)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	struct i2c_client *client = chip->client;
20262306a36Sopenharmony_ci	struct cm3232_als_info *als_info = chip->als_info;
20362306a36Sopenharmony_ci	int ret;
20462306a36Sopenharmony_ci	int val, val2;
20562306a36Sopenharmony_ci	int als_it;
20662306a36Sopenharmony_ci	u64 lux;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	/* Calculate mlux per bit based on als_it */
20962306a36Sopenharmony_ci	ret = cm3232_read_als_it(chip, &val, &val2);
21062306a36Sopenharmony_ci	if (ret < 0)
21162306a36Sopenharmony_ci		return -EINVAL;
21262306a36Sopenharmony_ci	als_it = val * 1000000 + val2;
21362306a36Sopenharmony_ci	lux = (__force u64)als_info->mlux_per_bit;
21462306a36Sopenharmony_ci	lux *= als_info->mlux_per_bit_base_it;
21562306a36Sopenharmony_ci	lux = div_u64(lux, als_it);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	ret = i2c_smbus_read_word_data(client, CM3232_REG_ADDR_ALS);
21862306a36Sopenharmony_ci	if (ret < 0) {
21962306a36Sopenharmony_ci		dev_err(&client->dev, "Error reading reg_addr_als\n");
22062306a36Sopenharmony_ci		return ret;
22162306a36Sopenharmony_ci	}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	chip->regs_als = (u16)ret;
22462306a36Sopenharmony_ci	lux *= chip->regs_als;
22562306a36Sopenharmony_ci	lux *= als_info->calibscale;
22662306a36Sopenharmony_ci	lux = div_u64(lux, CM3232_CALIBSCALE_RESOLUTION);
22762306a36Sopenharmony_ci	lux = div_u64(lux, CM3232_MLUX_PER_LUX);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	if (lux > 0xFFFF)
23062306a36Sopenharmony_ci		lux = 0xFFFF;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	return (int)lux;
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic int cm3232_read_raw(struct iio_dev *indio_dev,
23662306a36Sopenharmony_ci			struct iio_chan_spec const *chan,
23762306a36Sopenharmony_ci			int *val, int *val2, long mask)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	struct cm3232_chip *chip = iio_priv(indio_dev);
24062306a36Sopenharmony_ci	struct cm3232_als_info *als_info = chip->als_info;
24162306a36Sopenharmony_ci	int ret;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	switch (mask) {
24462306a36Sopenharmony_ci	case IIO_CHAN_INFO_PROCESSED:
24562306a36Sopenharmony_ci		ret = cm3232_get_lux(chip);
24662306a36Sopenharmony_ci		if (ret < 0)
24762306a36Sopenharmony_ci			return ret;
24862306a36Sopenharmony_ci		*val = ret;
24962306a36Sopenharmony_ci		return IIO_VAL_INT;
25062306a36Sopenharmony_ci	case IIO_CHAN_INFO_CALIBSCALE:
25162306a36Sopenharmony_ci		*val = als_info->calibscale;
25262306a36Sopenharmony_ci		return IIO_VAL_INT;
25362306a36Sopenharmony_ci	case IIO_CHAN_INFO_INT_TIME:
25462306a36Sopenharmony_ci		return cm3232_read_als_it(chip, val, val2);
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	return -EINVAL;
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic int cm3232_write_raw(struct iio_dev *indio_dev,
26162306a36Sopenharmony_ci			struct iio_chan_spec const *chan,
26262306a36Sopenharmony_ci			int val, int val2, long mask)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	struct cm3232_chip *chip = iio_priv(indio_dev);
26562306a36Sopenharmony_ci	struct cm3232_als_info *als_info = chip->als_info;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	switch (mask) {
26862306a36Sopenharmony_ci	case IIO_CHAN_INFO_CALIBSCALE:
26962306a36Sopenharmony_ci		als_info->calibscale = val;
27062306a36Sopenharmony_ci		return 0;
27162306a36Sopenharmony_ci	case IIO_CHAN_INFO_INT_TIME:
27262306a36Sopenharmony_ci		return cm3232_write_als_it(chip, val, val2);
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	return -EINVAL;
27662306a36Sopenharmony_ci}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci/**
27962306a36Sopenharmony_ci * cm3232_get_it_available() - Get available ALS IT value
28062306a36Sopenharmony_ci * @dev:	pointer of struct device.
28162306a36Sopenharmony_ci * @attr:	pointer of struct device_attribute.
28262306a36Sopenharmony_ci * @buf:	pointer of return string buffer.
28362306a36Sopenharmony_ci *
28462306a36Sopenharmony_ci * Display the available integration time in second.
28562306a36Sopenharmony_ci *
28662306a36Sopenharmony_ci * Return: string length.
28762306a36Sopenharmony_ci */
28862306a36Sopenharmony_cistatic ssize_t cm3232_get_it_available(struct device *dev,
28962306a36Sopenharmony_ci			struct device_attribute *attr, char *buf)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	int i, len;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	for (i = 0, len = 0; i < ARRAY_SIZE(cm3232_als_it_scales); i++)
29462306a36Sopenharmony_ci		len += scnprintf(buf + len, PAGE_SIZE - len, "%u.%06u ",
29562306a36Sopenharmony_ci			cm3232_als_it_scales[i].val,
29662306a36Sopenharmony_ci			cm3232_als_it_scales[i].val2);
29762306a36Sopenharmony_ci	return len + scnprintf(buf + len, PAGE_SIZE - len, "\n");
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cistatic const struct iio_chan_spec cm3232_channels[] = {
30162306a36Sopenharmony_ci	{
30262306a36Sopenharmony_ci		.type = IIO_LIGHT,
30362306a36Sopenharmony_ci		.info_mask_separate =
30462306a36Sopenharmony_ci			BIT(IIO_CHAN_INFO_PROCESSED) |
30562306a36Sopenharmony_ci			BIT(IIO_CHAN_INFO_CALIBSCALE) |
30662306a36Sopenharmony_ci			BIT(IIO_CHAN_INFO_INT_TIME),
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci};
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_cistatic IIO_DEVICE_ATTR(in_illuminance_integration_time_available,
31162306a36Sopenharmony_ci			S_IRUGO, cm3232_get_it_available, NULL, 0);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cistatic struct attribute *cm3232_attributes[] = {
31462306a36Sopenharmony_ci	&iio_dev_attr_in_illuminance_integration_time_available.dev_attr.attr,
31562306a36Sopenharmony_ci	NULL,
31662306a36Sopenharmony_ci};
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic const struct attribute_group cm3232_attribute_group = {
31962306a36Sopenharmony_ci	.attrs = cm3232_attributes
32062306a36Sopenharmony_ci};
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_cistatic const struct iio_info cm3232_info = {
32362306a36Sopenharmony_ci	.read_raw		= &cm3232_read_raw,
32462306a36Sopenharmony_ci	.write_raw		= &cm3232_write_raw,
32562306a36Sopenharmony_ci	.attrs			= &cm3232_attribute_group,
32662306a36Sopenharmony_ci};
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_cistatic int cm3232_probe(struct i2c_client *client)
32962306a36Sopenharmony_ci{
33062306a36Sopenharmony_ci	const struct i2c_device_id *id = i2c_client_get_device_id(client);
33162306a36Sopenharmony_ci	struct cm3232_chip *chip;
33262306a36Sopenharmony_ci	struct iio_dev *indio_dev;
33362306a36Sopenharmony_ci	int ret;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
33662306a36Sopenharmony_ci	if (!indio_dev)
33762306a36Sopenharmony_ci		return -ENOMEM;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	chip = iio_priv(indio_dev);
34062306a36Sopenharmony_ci	i2c_set_clientdata(client, indio_dev);
34162306a36Sopenharmony_ci	chip->client = client;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	indio_dev->channels = cm3232_channels;
34462306a36Sopenharmony_ci	indio_dev->num_channels = ARRAY_SIZE(cm3232_channels);
34562306a36Sopenharmony_ci	indio_dev->info = &cm3232_info;
34662306a36Sopenharmony_ci	indio_dev->name = id->name;
34762306a36Sopenharmony_ci	indio_dev->modes = INDIO_DIRECT_MODE;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	ret = cm3232_reg_init(chip);
35062306a36Sopenharmony_ci	if (ret) {
35162306a36Sopenharmony_ci		dev_err(&client->dev,
35262306a36Sopenharmony_ci			"%s: register init failed\n",
35362306a36Sopenharmony_ci			__func__);
35462306a36Sopenharmony_ci		return ret;
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	return iio_device_register(indio_dev);
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_cistatic void cm3232_remove(struct i2c_client *client)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	struct iio_dev *indio_dev = i2c_get_clientdata(client);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
36562306a36Sopenharmony_ci		CM3232_CMD_ALS_DISABLE);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	iio_device_unregister(indio_dev);
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic const struct i2c_device_id cm3232_id[] = {
37162306a36Sopenharmony_ci	{"cm3232", 0},
37262306a36Sopenharmony_ci	{}
37362306a36Sopenharmony_ci};
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_cistatic int cm3232_suspend(struct device *dev)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
37862306a36Sopenharmony_ci	struct cm3232_chip *chip = iio_priv(indio_dev);
37962306a36Sopenharmony_ci	struct i2c_client *client = chip->client;
38062306a36Sopenharmony_ci	int ret;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	chip->regs_cmd |= CM3232_CMD_ALS_DISABLE;
38362306a36Sopenharmony_ci	ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
38462306a36Sopenharmony_ci					chip->regs_cmd);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	return ret;
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_cistatic int cm3232_resume(struct device *dev)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
39262306a36Sopenharmony_ci	struct cm3232_chip *chip = iio_priv(indio_dev);
39362306a36Sopenharmony_ci	struct i2c_client *client = chip->client;
39462306a36Sopenharmony_ci	int ret;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	chip->regs_cmd &= ~CM3232_CMD_ALS_DISABLE;
39762306a36Sopenharmony_ci	ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
39862306a36Sopenharmony_ci					chip->regs_cmd | CM3232_CMD_ALS_RESET);
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	return ret;
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(cm3232_pm_ops, cm3232_suspend, cm3232_resume);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, cm3232_id);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_cistatic const struct of_device_id cm3232_of_match[] = {
40862306a36Sopenharmony_ci	{.compatible = "capella,cm3232"},
40962306a36Sopenharmony_ci	{}
41062306a36Sopenharmony_ci};
41162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, cm3232_of_match);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_cistatic struct i2c_driver cm3232_driver = {
41462306a36Sopenharmony_ci	.driver = {
41562306a36Sopenharmony_ci		.name	= "cm3232",
41662306a36Sopenharmony_ci		.of_match_table = cm3232_of_match,
41762306a36Sopenharmony_ci		.pm	= pm_sleep_ptr(&cm3232_pm_ops),
41862306a36Sopenharmony_ci	},
41962306a36Sopenharmony_ci	.id_table	= cm3232_id,
42062306a36Sopenharmony_ci	.probe		= cm3232_probe,
42162306a36Sopenharmony_ci	.remove		= cm3232_remove,
42262306a36Sopenharmony_ci};
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cimodule_i2c_driver(cm3232_driver);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ciMODULE_AUTHOR("Kevin Tsai <ktsai@capellamicro.com>");
42762306a36Sopenharmony_ciMODULE_DESCRIPTION("CM3232 ambient light sensor driver");
42862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
429