18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ROHM 1780GLI Ambient Light Sensor Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2016 Linaro Ltd. 68c2ecf20Sopenharmony_ci * Author: Linus Walleij <linus.walleij@linaro.org> 78c2ecf20Sopenharmony_ci * Loosely based on the previous BH1780 ALS misc driver 88c2ecf20Sopenharmony_ci * Copyright (C) 2010 Texas Instruments 98c2ecf20Sopenharmony_ci * Author: Hemanth V <hemanthv@ti.com> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci#include <linux/i2c.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h> 178c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 188c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 198c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h> 208c2ecf20Sopenharmony_ci#include <linux/bitops.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define BH1780_CMD_BIT BIT(7) 238c2ecf20Sopenharmony_ci#define BH1780_REG_CONTROL 0x00 248c2ecf20Sopenharmony_ci#define BH1780_REG_PARTID 0x0A 258c2ecf20Sopenharmony_ci#define BH1780_REG_MANFID 0x0B 268c2ecf20Sopenharmony_ci#define BH1780_REG_DLOW 0x0C 278c2ecf20Sopenharmony_ci#define BH1780_REG_DHIGH 0x0D 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define BH1780_REVMASK GENMASK(3,0) 308c2ecf20Sopenharmony_ci#define BH1780_POWMASK GENMASK(1,0) 318c2ecf20Sopenharmony_ci#define BH1780_POFF (0x0) 328c2ecf20Sopenharmony_ci#define BH1780_PON (0x3) 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* power on settling time in ms */ 358c2ecf20Sopenharmony_ci#define BH1780_PON_DELAY 2 368c2ecf20Sopenharmony_ci/* max time before value available in ms */ 378c2ecf20Sopenharmony_ci#define BH1780_INTERVAL 250 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistruct bh1780_data { 408c2ecf20Sopenharmony_ci struct i2c_client *client; 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic int bh1780_write(struct bh1780_data *bh1780, u8 reg, u8 val) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci int ret = i2c_smbus_write_byte_data(bh1780->client, 468c2ecf20Sopenharmony_ci BH1780_CMD_BIT | reg, 478c2ecf20Sopenharmony_ci val); 488c2ecf20Sopenharmony_ci if (ret < 0) 498c2ecf20Sopenharmony_ci dev_err(&bh1780->client->dev, 508c2ecf20Sopenharmony_ci "i2c_smbus_write_byte_data failed error " 518c2ecf20Sopenharmony_ci "%d, register %01x\n", 528c2ecf20Sopenharmony_ci ret, reg); 538c2ecf20Sopenharmony_ci return ret; 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic int bh1780_read(struct bh1780_data *bh1780, u8 reg) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci int ret = i2c_smbus_read_byte_data(bh1780->client, 598c2ecf20Sopenharmony_ci BH1780_CMD_BIT | reg); 608c2ecf20Sopenharmony_ci if (ret < 0) 618c2ecf20Sopenharmony_ci dev_err(&bh1780->client->dev, 628c2ecf20Sopenharmony_ci "i2c_smbus_read_byte_data failed error " 638c2ecf20Sopenharmony_ci "%d, register %01x\n", 648c2ecf20Sopenharmony_ci ret, reg); 658c2ecf20Sopenharmony_ci return ret; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic int bh1780_read_word(struct bh1780_data *bh1780, u8 reg) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci int ret = i2c_smbus_read_word_data(bh1780->client, 718c2ecf20Sopenharmony_ci BH1780_CMD_BIT | reg); 728c2ecf20Sopenharmony_ci if (ret < 0) 738c2ecf20Sopenharmony_ci dev_err(&bh1780->client->dev, 748c2ecf20Sopenharmony_ci "i2c_smbus_read_word_data failed error " 758c2ecf20Sopenharmony_ci "%d, register %01x\n", 768c2ecf20Sopenharmony_ci ret, reg); 778c2ecf20Sopenharmony_ci return ret; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic int bh1780_debugfs_reg_access(struct iio_dev *indio_dev, 818c2ecf20Sopenharmony_ci unsigned int reg, unsigned int writeval, 828c2ecf20Sopenharmony_ci unsigned int *readval) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci struct bh1780_data *bh1780 = iio_priv(indio_dev); 858c2ecf20Sopenharmony_ci int ret; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (!readval) 888c2ecf20Sopenharmony_ci return bh1780_write(bh1780, (u8)reg, (u8)writeval); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci ret = bh1780_read(bh1780, (u8)reg); 918c2ecf20Sopenharmony_ci if (ret < 0) 928c2ecf20Sopenharmony_ci return ret; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci *readval = ret; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci return 0; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic int bh1780_read_raw(struct iio_dev *indio_dev, 1008c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 1018c2ecf20Sopenharmony_ci int *val, int *val2, long mask) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci struct bh1780_data *bh1780 = iio_priv(indio_dev); 1048c2ecf20Sopenharmony_ci int value; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci switch (mask) { 1078c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 1088c2ecf20Sopenharmony_ci switch (chan->type) { 1098c2ecf20Sopenharmony_ci case IIO_LIGHT: 1108c2ecf20Sopenharmony_ci pm_runtime_get_sync(&bh1780->client->dev); 1118c2ecf20Sopenharmony_ci value = bh1780_read_word(bh1780, BH1780_REG_DLOW); 1128c2ecf20Sopenharmony_ci if (value < 0) 1138c2ecf20Sopenharmony_ci return value; 1148c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(&bh1780->client->dev); 1158c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(&bh1780->client->dev); 1168c2ecf20Sopenharmony_ci *val = value; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci return IIO_VAL_INT; 1198c2ecf20Sopenharmony_ci default: 1208c2ecf20Sopenharmony_ci return -EINVAL; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_INT_TIME: 1238c2ecf20Sopenharmony_ci *val = 0; 1248c2ecf20Sopenharmony_ci *val2 = BH1780_INTERVAL * 1000; 1258c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 1268c2ecf20Sopenharmony_ci default: 1278c2ecf20Sopenharmony_ci return -EINVAL; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic const struct iio_info bh1780_info = { 1328c2ecf20Sopenharmony_ci .read_raw = bh1780_read_raw, 1338c2ecf20Sopenharmony_ci .debugfs_reg_access = bh1780_debugfs_reg_access, 1348c2ecf20Sopenharmony_ci}; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic const struct iio_chan_spec bh1780_channels[] = { 1378c2ecf20Sopenharmony_ci { 1388c2ecf20Sopenharmony_ci .type = IIO_LIGHT, 1398c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1408c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME) 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci}; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic int bh1780_probe(struct i2c_client *client, 1458c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci int ret; 1488c2ecf20Sopenharmony_ci struct bh1780_data *bh1780; 1498c2ecf20Sopenharmony_ci struct i2c_adapter *adapter = client->adapter; 1508c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) 1538c2ecf20Sopenharmony_ci return -EIO; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*bh1780)); 1568c2ecf20Sopenharmony_ci if (!indio_dev) 1578c2ecf20Sopenharmony_ci return -ENOMEM; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci bh1780 = iio_priv(indio_dev); 1608c2ecf20Sopenharmony_ci bh1780->client = client; 1618c2ecf20Sopenharmony_ci i2c_set_clientdata(client, indio_dev); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci /* Power up the device */ 1648c2ecf20Sopenharmony_ci ret = bh1780_write(bh1780, BH1780_REG_CONTROL, BH1780_PON); 1658c2ecf20Sopenharmony_ci if (ret < 0) 1668c2ecf20Sopenharmony_ci return ret; 1678c2ecf20Sopenharmony_ci msleep(BH1780_PON_DELAY); 1688c2ecf20Sopenharmony_ci pm_runtime_get_noresume(&client->dev); 1698c2ecf20Sopenharmony_ci pm_runtime_set_active(&client->dev); 1708c2ecf20Sopenharmony_ci pm_runtime_enable(&client->dev); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci ret = bh1780_read(bh1780, BH1780_REG_PARTID); 1738c2ecf20Sopenharmony_ci if (ret < 0) 1748c2ecf20Sopenharmony_ci goto out_disable_pm; 1758c2ecf20Sopenharmony_ci dev_info(&client->dev, 1768c2ecf20Sopenharmony_ci "Ambient Light Sensor, Rev : %lu\n", 1778c2ecf20Sopenharmony_ci (ret & BH1780_REVMASK)); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* 1808c2ecf20Sopenharmony_ci * As the device takes 250 ms to even come up with a fresh 1818c2ecf20Sopenharmony_ci * measurement after power-on, do not shut it down unnecessarily. 1828c2ecf20Sopenharmony_ci * Set autosuspend to a five seconds. 1838c2ecf20Sopenharmony_ci */ 1848c2ecf20Sopenharmony_ci pm_runtime_set_autosuspend_delay(&client->dev, 5000); 1858c2ecf20Sopenharmony_ci pm_runtime_use_autosuspend(&client->dev); 1868c2ecf20Sopenharmony_ci pm_runtime_put(&client->dev); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci indio_dev->info = &bh1780_info; 1898c2ecf20Sopenharmony_ci indio_dev->name = "bh1780"; 1908c2ecf20Sopenharmony_ci indio_dev->channels = bh1780_channels; 1918c2ecf20Sopenharmony_ci indio_dev->num_channels = ARRAY_SIZE(bh1780_channels); 1928c2ecf20Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci ret = iio_device_register(indio_dev); 1958c2ecf20Sopenharmony_ci if (ret) 1968c2ecf20Sopenharmony_ci goto out_disable_pm; 1978c2ecf20Sopenharmony_ci return 0; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ciout_disable_pm: 2008c2ecf20Sopenharmony_ci pm_runtime_put_noidle(&client->dev); 2018c2ecf20Sopenharmony_ci pm_runtime_disable(&client->dev); 2028c2ecf20Sopenharmony_ci return ret; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic int bh1780_remove(struct i2c_client *client) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = i2c_get_clientdata(client); 2088c2ecf20Sopenharmony_ci struct bh1780_data *bh1780 = iio_priv(indio_dev); 2098c2ecf20Sopenharmony_ci int ret; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci iio_device_unregister(indio_dev); 2128c2ecf20Sopenharmony_ci pm_runtime_get_sync(&client->dev); 2138c2ecf20Sopenharmony_ci pm_runtime_put_noidle(&client->dev); 2148c2ecf20Sopenharmony_ci pm_runtime_disable(&client->dev); 2158c2ecf20Sopenharmony_ci ret = bh1780_write(bh1780, BH1780_REG_CONTROL, BH1780_POFF); 2168c2ecf20Sopenharmony_ci if (ret < 0) { 2178c2ecf20Sopenharmony_ci dev_err(&client->dev, "failed to power off\n"); 2188c2ecf20Sopenharmony_ci return ret; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci return 0; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 2258c2ecf20Sopenharmony_cistatic int bh1780_runtime_suspend(struct device *dev) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 2288c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = i2c_get_clientdata(client); 2298c2ecf20Sopenharmony_ci struct bh1780_data *bh1780 = iio_priv(indio_dev); 2308c2ecf20Sopenharmony_ci int ret; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci ret = bh1780_write(bh1780, BH1780_REG_CONTROL, BH1780_POFF); 2338c2ecf20Sopenharmony_ci if (ret < 0) { 2348c2ecf20Sopenharmony_ci dev_err(dev, "failed to runtime suspend\n"); 2358c2ecf20Sopenharmony_ci return ret; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci return 0; 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic int bh1780_runtime_resume(struct device *dev) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 2448c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = i2c_get_clientdata(client); 2458c2ecf20Sopenharmony_ci struct bh1780_data *bh1780 = iio_priv(indio_dev); 2468c2ecf20Sopenharmony_ci int ret; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci ret = bh1780_write(bh1780, BH1780_REG_CONTROL, BH1780_PON); 2498c2ecf20Sopenharmony_ci if (ret < 0) { 2508c2ecf20Sopenharmony_ci dev_err(dev, "failed to runtime resume\n"); 2518c2ecf20Sopenharmony_ci return ret; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* Wait for power on, then for a value to be available */ 2558c2ecf20Sopenharmony_ci msleep(BH1780_PON_DELAY + BH1780_INTERVAL); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci return 0; 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic const struct dev_pm_ops bh1780_dev_pm_ops = { 2628c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 2638c2ecf20Sopenharmony_ci pm_runtime_force_resume) 2648c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(bh1780_runtime_suspend, 2658c2ecf20Sopenharmony_ci bh1780_runtime_resume, NULL) 2668c2ecf20Sopenharmony_ci}; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic const struct i2c_device_id bh1780_id[] = { 2698c2ecf20Sopenharmony_ci { "bh1780", 0 }, 2708c2ecf20Sopenharmony_ci { }, 2718c2ecf20Sopenharmony_ci}; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, bh1780_id); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic const struct of_device_id of_bh1780_match[] = { 2768c2ecf20Sopenharmony_ci { .compatible = "rohm,bh1780gli", }, 2778c2ecf20Sopenharmony_ci {}, 2788c2ecf20Sopenharmony_ci}; 2798c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, of_bh1780_match); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic struct i2c_driver bh1780_driver = { 2828c2ecf20Sopenharmony_ci .probe = bh1780_probe, 2838c2ecf20Sopenharmony_ci .remove = bh1780_remove, 2848c2ecf20Sopenharmony_ci .id_table = bh1780_id, 2858c2ecf20Sopenharmony_ci .driver = { 2868c2ecf20Sopenharmony_ci .name = "bh1780", 2878c2ecf20Sopenharmony_ci .pm = &bh1780_dev_pm_ops, 2888c2ecf20Sopenharmony_ci .of_match_table = of_bh1780_match, 2898c2ecf20Sopenharmony_ci }, 2908c2ecf20Sopenharmony_ci}; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cimodule_i2c_driver(bh1780_driver); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ROHM BH1780GLI Ambient Light Sensor Driver"); 2958c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2968c2ecf20Sopenharmony_ciMODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); 297