xref: /linux/drivers/iio/light/bh1780.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21f0477f1SLinus Walleij /*
31f0477f1SLinus Walleij  * ROHM 1780GLI Ambient Light Sensor Driver
41f0477f1SLinus Walleij  *
51f0477f1SLinus Walleij  * Copyright (C) 2016 Linaro Ltd.
61f0477f1SLinus Walleij  * Author: Linus Walleij <linus.walleij@linaro.org>
71f0477f1SLinus Walleij  * Loosely based on the previous BH1780 ALS misc driver
81f0477f1SLinus Walleij  * Copyright (C) 2010 Texas Instruments
91f0477f1SLinus Walleij  * Author: Hemanth V <hemanthv@ti.com>
101f0477f1SLinus Walleij  */
111f0477f1SLinus Walleij #include <linux/i2c.h>
121f0477f1SLinus Walleij #include <linux/slab.h>
131f0477f1SLinus Walleij #include <linux/platform_device.h>
141f0477f1SLinus Walleij #include <linux/delay.h>
151f0477f1SLinus Walleij #include <linux/module.h>
16f73a0474SJonathan Cameron #include <linux/mod_devicetable.h>
171f0477f1SLinus Walleij #include <linux/pm_runtime.h>
181f0477f1SLinus Walleij #include <linux/iio/iio.h>
191f0477f1SLinus Walleij #include <linux/iio/sysfs.h>
201f0477f1SLinus Walleij #include <linux/bitops.h>
211f0477f1SLinus Walleij 
221f0477f1SLinus Walleij #define BH1780_CMD_BIT		BIT(7)
231f0477f1SLinus Walleij #define BH1780_REG_CONTROL	0x00
241f0477f1SLinus Walleij #define BH1780_REG_PARTID	0x0A
251f0477f1SLinus Walleij #define BH1780_REG_MANFID	0x0B
261f0477f1SLinus Walleij #define BH1780_REG_DLOW		0x0C
271f0477f1SLinus Walleij #define BH1780_REG_DHIGH	0x0D
281f0477f1SLinus Walleij 
291f0477f1SLinus Walleij #define BH1780_REVMASK		GENMASK(3,0)
301f0477f1SLinus Walleij #define BH1780_POWMASK		GENMASK(1,0)
311f0477f1SLinus Walleij #define BH1780_POFF		(0x0)
321f0477f1SLinus Walleij #define BH1780_PON		(0x3)
331f0477f1SLinus Walleij 
341f0477f1SLinus Walleij /* power on settling time in ms */
351f0477f1SLinus Walleij #define BH1780_PON_DELAY	2
361f0477f1SLinus Walleij /* max time before value available in ms */
371f0477f1SLinus Walleij #define BH1780_INTERVAL		250
381f0477f1SLinus Walleij 
391f0477f1SLinus Walleij struct bh1780_data {
401f0477f1SLinus Walleij 	struct i2c_client *client;
411f0477f1SLinus Walleij };
421f0477f1SLinus Walleij 
bh1780_write(struct bh1780_data * bh1780,u8 reg,u8 val)431f0477f1SLinus Walleij static int bh1780_write(struct bh1780_data *bh1780, u8 reg, u8 val)
441f0477f1SLinus Walleij {
451f0477f1SLinus Walleij 	int ret = i2c_smbus_write_byte_data(bh1780->client,
461f0477f1SLinus Walleij 					    BH1780_CMD_BIT | reg,
471f0477f1SLinus Walleij 					    val);
481f0477f1SLinus Walleij 	if (ret < 0)
491f0477f1SLinus Walleij 		dev_err(&bh1780->client->dev,
501f0477f1SLinus Walleij 			"i2c_smbus_write_byte_data failed error "
511f0477f1SLinus Walleij 			"%d, register %01x\n",
521f0477f1SLinus Walleij 			ret, reg);
531f0477f1SLinus Walleij 	return ret;
541f0477f1SLinus Walleij }
551f0477f1SLinus Walleij 
bh1780_read(struct bh1780_data * bh1780,u8 reg)561f0477f1SLinus Walleij static int bh1780_read(struct bh1780_data *bh1780, u8 reg)
571f0477f1SLinus Walleij {
581f0477f1SLinus Walleij 	int ret = i2c_smbus_read_byte_data(bh1780->client,
591f0477f1SLinus Walleij 					   BH1780_CMD_BIT | reg);
601f0477f1SLinus Walleij 	if (ret < 0)
611f0477f1SLinus Walleij 		dev_err(&bh1780->client->dev,
621f0477f1SLinus Walleij 			"i2c_smbus_read_byte_data failed error "
631f0477f1SLinus Walleij 			"%d, register %01x\n",
641f0477f1SLinus Walleij 			ret, reg);
651f0477f1SLinus Walleij 	return ret;
661f0477f1SLinus Walleij }
671f0477f1SLinus Walleij 
bh1780_read_word(struct bh1780_data * bh1780,u8 reg)681f0477f1SLinus Walleij static int bh1780_read_word(struct bh1780_data *bh1780, u8 reg)
691f0477f1SLinus Walleij {
701f0477f1SLinus Walleij 	int ret = i2c_smbus_read_word_data(bh1780->client,
711f0477f1SLinus Walleij 					   BH1780_CMD_BIT | reg);
721f0477f1SLinus Walleij 	if (ret < 0)
731f0477f1SLinus Walleij 		dev_err(&bh1780->client->dev,
741f0477f1SLinus Walleij 			"i2c_smbus_read_word_data failed error "
751f0477f1SLinus Walleij 			"%d, register %01x\n",
761f0477f1SLinus Walleij 			ret, reg);
771f0477f1SLinus Walleij 	return ret;
781f0477f1SLinus Walleij }
791f0477f1SLinus Walleij 
bh1780_debugfs_reg_access(struct iio_dev * indio_dev,unsigned int reg,unsigned int writeval,unsigned int * readval)801f0477f1SLinus Walleij static int bh1780_debugfs_reg_access(struct iio_dev *indio_dev,
811f0477f1SLinus Walleij 			      unsigned int reg, unsigned int writeval,
821f0477f1SLinus Walleij 			      unsigned int *readval)
831f0477f1SLinus Walleij {
841f0477f1SLinus Walleij 	struct bh1780_data *bh1780 = iio_priv(indio_dev);
851f0477f1SLinus Walleij 	int ret;
861f0477f1SLinus Walleij 
871f0477f1SLinus Walleij 	if (!readval)
8853dfc3b9SLinus Walleij 		return bh1780_write(bh1780, (u8)reg, (u8)writeval);
891f0477f1SLinus Walleij 
901f0477f1SLinus Walleij 	ret = bh1780_read(bh1780, (u8)reg);
911f0477f1SLinus Walleij 	if (ret < 0)
921f0477f1SLinus Walleij 		return ret;
931f0477f1SLinus Walleij 
941f0477f1SLinus Walleij 	*readval = ret;
951f0477f1SLinus Walleij 
961f0477f1SLinus Walleij 	return 0;
971f0477f1SLinus Walleij }
981f0477f1SLinus Walleij 
bh1780_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)991f0477f1SLinus Walleij static int bh1780_read_raw(struct iio_dev *indio_dev,
1001f0477f1SLinus Walleij 			   struct iio_chan_spec const *chan,
1011f0477f1SLinus Walleij 			   int *val, int *val2, long mask)
1021f0477f1SLinus Walleij {
1031f0477f1SLinus Walleij 	struct bh1780_data *bh1780 = iio_priv(indio_dev);
1041f0477f1SLinus Walleij 	int value;
1051f0477f1SLinus Walleij 
1061f0477f1SLinus Walleij 	switch (mask) {
1071f0477f1SLinus Walleij 	case IIO_CHAN_INFO_RAW:
1081f0477f1SLinus Walleij 		switch (chan->type) {
1091f0477f1SLinus Walleij 		case IIO_LIGHT:
1101f0477f1SLinus Walleij 			pm_runtime_get_sync(&bh1780->client->dev);
1111f0477f1SLinus Walleij 			value = bh1780_read_word(bh1780, BH1780_REG_DLOW);
1121f0477f1SLinus Walleij 			if (value < 0)
1131f0477f1SLinus Walleij 				return value;
1141f0477f1SLinus Walleij 			pm_runtime_mark_last_busy(&bh1780->client->dev);
1151f0477f1SLinus Walleij 			pm_runtime_put_autosuspend(&bh1780->client->dev);
1161f0477f1SLinus Walleij 			*val = value;
1171f0477f1SLinus Walleij 
1181f0477f1SLinus Walleij 			return IIO_VAL_INT;
1191f0477f1SLinus Walleij 		default:
1201f0477f1SLinus Walleij 			return -EINVAL;
1211f0477f1SLinus Walleij 		}
1221f0477f1SLinus Walleij 	case IIO_CHAN_INFO_INT_TIME:
1231f0477f1SLinus Walleij 		*val = 0;
1241f0477f1SLinus Walleij 		*val2 = BH1780_INTERVAL * 1000;
1251f0477f1SLinus Walleij 		return IIO_VAL_INT_PLUS_MICRO;
1261f0477f1SLinus Walleij 	default:
1271f0477f1SLinus Walleij 		return -EINVAL;
1281f0477f1SLinus Walleij 	}
1291f0477f1SLinus Walleij }
1301f0477f1SLinus Walleij 
1311f0477f1SLinus Walleij static const struct iio_info bh1780_info = {
1321f0477f1SLinus Walleij 	.read_raw = bh1780_read_raw,
1331f0477f1SLinus Walleij 	.debugfs_reg_access = bh1780_debugfs_reg_access,
1341f0477f1SLinus Walleij };
1351f0477f1SLinus Walleij 
1361f0477f1SLinus Walleij static const struct iio_chan_spec bh1780_channels[] = {
1371f0477f1SLinus Walleij 	{
1381f0477f1SLinus Walleij 		.type = IIO_LIGHT,
1391f0477f1SLinus Walleij 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
1401f0477f1SLinus Walleij 				      BIT(IIO_CHAN_INFO_INT_TIME)
1411f0477f1SLinus Walleij 	}
1421f0477f1SLinus Walleij };
1431f0477f1SLinus Walleij 
bh1780_probe(struct i2c_client * client)144ed5c6b1cSUwe Kleine-König static int bh1780_probe(struct i2c_client *client)
1451f0477f1SLinus Walleij {
1461f0477f1SLinus Walleij 	int ret;
1471f0477f1SLinus Walleij 	struct bh1780_data *bh1780;
148aa709329SWolfram Sang 	struct i2c_adapter *adapter = client->adapter;
1491f0477f1SLinus Walleij 	struct iio_dev *indio_dev;
1501f0477f1SLinus Walleij 
1511f0477f1SLinus Walleij 	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
1521f0477f1SLinus Walleij 		return -EIO;
1531f0477f1SLinus Walleij 
1541f0477f1SLinus Walleij 	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*bh1780));
1551f0477f1SLinus Walleij 	if (!indio_dev)
1561f0477f1SLinus Walleij 		return -ENOMEM;
1571f0477f1SLinus Walleij 
1581f0477f1SLinus Walleij 	bh1780 = iio_priv(indio_dev);
1591f0477f1SLinus Walleij 	bh1780->client = client;
1601f0477f1SLinus Walleij 	i2c_set_clientdata(client, indio_dev);
1611f0477f1SLinus Walleij 
1621f0477f1SLinus Walleij 	/* Power up the device */
1631f0477f1SLinus Walleij 	ret = bh1780_write(bh1780, BH1780_REG_CONTROL, BH1780_PON);
1641f0477f1SLinus Walleij 	if (ret < 0)
1651f0477f1SLinus Walleij 		return ret;
1661f0477f1SLinus Walleij 	msleep(BH1780_PON_DELAY);
1671f0477f1SLinus Walleij 	pm_runtime_get_noresume(&client->dev);
1681f0477f1SLinus Walleij 	pm_runtime_set_active(&client->dev);
1691f0477f1SLinus Walleij 	pm_runtime_enable(&client->dev);
1701f0477f1SLinus Walleij 
1711f0477f1SLinus Walleij 	ret = bh1780_read(bh1780, BH1780_REG_PARTID);
1721f0477f1SLinus Walleij 	if (ret < 0)
1731f0477f1SLinus Walleij 		goto out_disable_pm;
1741f0477f1SLinus Walleij 	dev_info(&client->dev,
1751f0477f1SLinus Walleij 		 "Ambient Light Sensor, Rev : %lu\n",
1761f0477f1SLinus Walleij 		 (ret & BH1780_REVMASK));
1771f0477f1SLinus Walleij 
1781f0477f1SLinus Walleij 	/*
1791f0477f1SLinus Walleij 	 * As the device takes 250 ms to even come up with a fresh
1801f0477f1SLinus Walleij 	 * measurement after power-on, do not shut it down unnecessarily.
1811f0477f1SLinus Walleij 	 * Set autosuspend to a five seconds.
1821f0477f1SLinus Walleij 	 */
1831f0477f1SLinus Walleij 	pm_runtime_set_autosuspend_delay(&client->dev, 5000);
1841f0477f1SLinus Walleij 	pm_runtime_use_autosuspend(&client->dev);
1851f0477f1SLinus Walleij 	pm_runtime_put(&client->dev);
1861f0477f1SLinus Walleij 
1871f0477f1SLinus Walleij 	indio_dev->info = &bh1780_info;
1880dd09ca4SLinus Walleij 	indio_dev->name = "bh1780";
1891f0477f1SLinus Walleij 	indio_dev->channels = bh1780_channels;
1901f0477f1SLinus Walleij 	indio_dev->num_channels = ARRAY_SIZE(bh1780_channels);
1911f0477f1SLinus Walleij 	indio_dev->modes = INDIO_DIRECT_MODE;
1921f0477f1SLinus Walleij 
1931f0477f1SLinus Walleij 	ret = iio_device_register(indio_dev);
1941f0477f1SLinus Walleij 	if (ret)
1951f0477f1SLinus Walleij 		goto out_disable_pm;
1961f0477f1SLinus Walleij 	return 0;
1971f0477f1SLinus Walleij 
1981f0477f1SLinus Walleij out_disable_pm:
1991f0477f1SLinus Walleij 	pm_runtime_put_noidle(&client->dev);
2001f0477f1SLinus Walleij 	pm_runtime_disable(&client->dev);
2011f0477f1SLinus Walleij 	return ret;
2021f0477f1SLinus Walleij }
2031f0477f1SLinus Walleij 
bh1780_remove(struct i2c_client * client)204ed5c2f5fSUwe Kleine-König static void bh1780_remove(struct i2c_client *client)
2051f0477f1SLinus Walleij {
2061f0477f1SLinus Walleij 	struct iio_dev *indio_dev = i2c_get_clientdata(client);
2071f0477f1SLinus Walleij 	struct bh1780_data *bh1780 = iio_priv(indio_dev);
2081f0477f1SLinus Walleij 	int ret;
2091f0477f1SLinus Walleij 
2101f0477f1SLinus Walleij 	iio_device_unregister(indio_dev);
2111f0477f1SLinus Walleij 	pm_runtime_get_sync(&client->dev);
2121f0477f1SLinus Walleij 	pm_runtime_put_noidle(&client->dev);
2131f0477f1SLinus Walleij 	pm_runtime_disable(&client->dev);
2141f0477f1SLinus Walleij 	ret = bh1780_write(bh1780, BH1780_REG_CONTROL, BH1780_POFF);
2155004e24aSUwe Kleine-König 	if (ret < 0)
2165004e24aSUwe Kleine-König 		dev_err(&client->dev, "failed to power off (%pe)\n",
2175004e24aSUwe Kleine-König 			ERR_PTR(ret));
2181f0477f1SLinus Walleij }
2191f0477f1SLinus Walleij 
bh1780_runtime_suspend(struct device * dev)2201f0477f1SLinus Walleij static int bh1780_runtime_suspend(struct device *dev)
2211f0477f1SLinus Walleij {
2221f0477f1SLinus Walleij 	struct i2c_client *client = to_i2c_client(dev);
22313c27e94SLinus Walleij 	struct iio_dev *indio_dev = i2c_get_clientdata(client);
22413c27e94SLinus Walleij 	struct bh1780_data *bh1780 = iio_priv(indio_dev);
2251f0477f1SLinus Walleij 	int ret;
2261f0477f1SLinus Walleij 
2271f0477f1SLinus Walleij 	ret = bh1780_write(bh1780, BH1780_REG_CONTROL, BH1780_POFF);
2281f0477f1SLinus Walleij 	if (ret < 0) {
2291f0477f1SLinus Walleij 		dev_err(dev, "failed to runtime suspend\n");
2301f0477f1SLinus Walleij 		return ret;
2311f0477f1SLinus Walleij 	}
2321f0477f1SLinus Walleij 
2331f0477f1SLinus Walleij 	return 0;
2341f0477f1SLinus Walleij }
2351f0477f1SLinus Walleij 
bh1780_runtime_resume(struct device * dev)2361f0477f1SLinus Walleij static int bh1780_runtime_resume(struct device *dev)
2371f0477f1SLinus Walleij {
2381f0477f1SLinus Walleij 	struct i2c_client *client = to_i2c_client(dev);
23913c27e94SLinus Walleij 	struct iio_dev *indio_dev = i2c_get_clientdata(client);
24013c27e94SLinus Walleij 	struct bh1780_data *bh1780 = iio_priv(indio_dev);
2411f0477f1SLinus Walleij 	int ret;
2421f0477f1SLinus Walleij 
2431f0477f1SLinus Walleij 	ret = bh1780_write(bh1780, BH1780_REG_CONTROL, BH1780_PON);
2441f0477f1SLinus Walleij 	if (ret < 0) {
2451f0477f1SLinus Walleij 		dev_err(dev, "failed to runtime resume\n");
2461f0477f1SLinus Walleij 		return ret;
2471f0477f1SLinus Walleij 	}
2481f0477f1SLinus Walleij 
2491f0477f1SLinus Walleij 	/* Wait for power on, then for a value to be available */
2501f0477f1SLinus Walleij 	msleep(BH1780_PON_DELAY + BH1780_INTERVAL);
2511f0477f1SLinus Walleij 
2521f0477f1SLinus Walleij 	return 0;
2531f0477f1SLinus Walleij }
2541f0477f1SLinus Walleij 
255ea087612SJonathan Cameron static DEFINE_RUNTIME_DEV_PM_OPS(bh1780_dev_pm_ops, bh1780_runtime_suspend,
256ea087612SJonathan Cameron 				bh1780_runtime_resume, NULL);
2571f0477f1SLinus Walleij 
2581f0477f1SLinus Walleij static const struct i2c_device_id bh1780_id[] = {
259*4391affaSUwe Kleine-König 	{ "bh1780" },
260*4391affaSUwe Kleine-König 	{ }
2611f0477f1SLinus Walleij };
2621f0477f1SLinus Walleij 
2631f0477f1SLinus Walleij MODULE_DEVICE_TABLE(i2c, bh1780_id);
2641f0477f1SLinus Walleij 
2651f0477f1SLinus Walleij static const struct of_device_id of_bh1780_match[] = {
2661f0477f1SLinus Walleij 	{ .compatible = "rohm,bh1780gli", },
2671f0477f1SLinus Walleij 	{},
2681f0477f1SLinus Walleij };
2691f0477f1SLinus Walleij MODULE_DEVICE_TABLE(of, of_bh1780_match);
2701f0477f1SLinus Walleij 
2711f0477f1SLinus Walleij static struct i2c_driver bh1780_driver = {
2727cf15f42SUwe Kleine-König 	.probe		= bh1780_probe,
2731f0477f1SLinus Walleij 	.remove		= bh1780_remove,
2741f0477f1SLinus Walleij 	.id_table	= bh1780_id,
2751f0477f1SLinus Walleij 	.driver = {
2761f0477f1SLinus Walleij 		.name = "bh1780",
277ea087612SJonathan Cameron 		.pm = pm_ptr(&bh1780_dev_pm_ops),
278f73a0474SJonathan Cameron 		.of_match_table = of_bh1780_match,
2791f0477f1SLinus Walleij 	},
2801f0477f1SLinus Walleij };
2811f0477f1SLinus Walleij 
2821f0477f1SLinus Walleij module_i2c_driver(bh1780_driver);
2831f0477f1SLinus Walleij 
2841f0477f1SLinus Walleij MODULE_DESCRIPTION("ROHM BH1780GLI Ambient Light Sensor Driver");
2851f0477f1SLinus Walleij MODULE_LICENSE("GPL");
2861f0477f1SLinus Walleij MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
287