1/* 2 * bh1780gli.c 3 * ROHM Ambient Light Sensor Driver 4 * 5 * Copyright (C) 2010 Texas Instruments 6 * Author: Hemanth V <hemanthv@ti.com> 7 * 8 * This program is free software; you can redistribute it and/or modify it 9 * under the terms of the GNU General Public License version 2 as published by 10 * the Free Software Foundation. 11 * 12 * This program is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 * more details. 16 * 17 * You should have received a copy of the GNU General Public License along with 18 * this program. If not, see <http://www.gnu.org/licenses/>. 19 */ 20#include <linux/i2c.h> 21#include <linux/slab.h> 22#include <linux/mutex.h> 23#include <linux/platform_device.h> 24#include <linux/delay.h> 25#include <linux/module.h> 26#include <linux/of.h> 27 28#define BH1780_REG_CONTROL 0x80 29#define BH1780_REG_PARTID 0x8A 30#define BH1780_REG_MANFID 0x8B 31#define BH1780_REG_DLOW 0x8C 32#define BH1780_REG_DHIGH 0x8D 33 34#define BH1780_REVMASK (0xf) 35#define BH1780_POWMASK (0x3) 36#define BH1780_POFF (0x0) 37#define BH1780_PON (0x3) 38 39/* power on settling time in ms */ 40#define BH1780_PON_DELAY 2 41 42struct bh1780_data { 43 struct i2c_client *client; 44 int power_state; 45 /* lock for sysfs operations */ 46 struct mutex lock; 47}; 48 49static int bh1780_write(struct bh1780_data *ddata, u8 reg, u8 val, char *msg) 50{ 51 int ret = i2c_smbus_write_byte_data(ddata->client, reg, val); 52 if (ret < 0) 53 dev_err(&ddata->client->dev, 54 "i2c_smbus_write_byte_data failed error %d Register (%s)\n", 55 ret, msg); 56 return ret; 57} 58 59static int bh1780_read(struct bh1780_data *ddata, u8 reg, char *msg) 60{ 61 int ret = i2c_smbus_read_byte_data(ddata->client, reg); 62 if (ret < 0) 63 dev_err(&ddata->client->dev, 64 "i2c_smbus_read_byte_data failed error %d Register (%s)\n", 65 ret, msg); 66 return ret; 67} 68 69static ssize_t bh1780_show_lux(struct device *dev, 70 struct device_attribute *attr, char *buf) 71{ 72 struct platform_device *pdev = to_platform_device(dev); 73 struct bh1780_data *ddata = platform_get_drvdata(pdev); 74 int lsb, msb; 75 76 lsb = bh1780_read(ddata, BH1780_REG_DLOW, "DLOW"); 77 if (lsb < 0) 78 return lsb; 79 80 msb = bh1780_read(ddata, BH1780_REG_DHIGH, "DHIGH"); 81 if (msb < 0) 82 return msb; 83 84 return sprintf(buf, "%d\n", (msb << 8) | lsb); 85} 86 87static ssize_t bh1780_show_power_state(struct device *dev, 88 struct device_attribute *attr, 89 char *buf) 90{ 91 struct platform_device *pdev = to_platform_device(dev); 92 struct bh1780_data *ddata = platform_get_drvdata(pdev); 93 int state; 94 95 state = bh1780_read(ddata, BH1780_REG_CONTROL, "CONTROL"); 96 if (state < 0) 97 return state; 98 99 return sprintf(buf, "%d\n", state & BH1780_POWMASK); 100} 101 102static ssize_t bh1780_store_power_state(struct device *dev, 103 struct device_attribute *attr, 104 const char *buf, size_t count) 105{ 106 struct platform_device *pdev = to_platform_device(dev); 107 struct bh1780_data *ddata = platform_get_drvdata(pdev); 108 unsigned long val; 109 int error; 110 111 error = kstrtoul(buf, 0, &val); 112 if (error) 113 return error; 114 115 if (val < BH1780_POFF || val > BH1780_PON) 116 return -EINVAL; 117 118 mutex_lock(&ddata->lock); 119 120 error = bh1780_write(ddata, BH1780_REG_CONTROL, val, "CONTROL"); 121 if (error < 0) { 122 mutex_unlock(&ddata->lock); 123 return error; 124 } 125 126 msleep(BH1780_PON_DELAY); 127 ddata->power_state = val; 128 mutex_unlock(&ddata->lock); 129 130 return count; 131} 132 133static DEVICE_ATTR(lux, S_IRUGO, bh1780_show_lux, NULL); 134 135static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO, 136 bh1780_show_power_state, bh1780_store_power_state); 137 138static struct attribute *bh1780_attributes[] = { 139 &dev_attr_power_state.attr, 140 &dev_attr_lux.attr, 141 NULL 142}; 143 144static const struct attribute_group bh1780_attr_group = { 145 .attrs = bh1780_attributes, 146}; 147 148static int bh1780_probe(struct i2c_client *client, 149 const struct i2c_device_id *id) 150{ 151 int ret; 152 struct bh1780_data *ddata; 153 struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); 154 155 if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) 156 return -EIO; 157 158 ddata = devm_kzalloc(&client->dev, sizeof(struct bh1780_data), 159 GFP_KERNEL); 160 if (ddata == NULL) 161 return -ENOMEM; 162 163 ddata->client = client; 164 i2c_set_clientdata(client, ddata); 165 166 ret = bh1780_read(ddata, BH1780_REG_PARTID, "PART ID"); 167 if (ret < 0) 168 return ret; 169 170 dev_info(&client->dev, "Ambient Light Sensor, Rev : %d\n", 171 (ret & BH1780_REVMASK)); 172 173 mutex_init(&ddata->lock); 174 175 return sysfs_create_group(&client->dev.kobj, &bh1780_attr_group); 176} 177 178static int bh1780_remove(struct i2c_client *client) 179{ 180 sysfs_remove_group(&client->dev.kobj, &bh1780_attr_group); 181 182 return 0; 183} 184 185#ifdef CONFIG_PM_SLEEP 186static int bh1780_suspend(struct device *dev) 187{ 188 struct bh1780_data *ddata; 189 int state, ret; 190 struct i2c_client *client = to_i2c_client(dev); 191 192 ddata = i2c_get_clientdata(client); 193 state = bh1780_read(ddata, BH1780_REG_CONTROL, "CONTROL"); 194 if (state < 0) 195 return state; 196 197 ddata->power_state = state & BH1780_POWMASK; 198 199 ret = bh1780_write(ddata, BH1780_REG_CONTROL, BH1780_POFF, 200 "CONTROL"); 201 202 if (ret < 0) 203 return ret; 204 205 return 0; 206} 207 208static int bh1780_resume(struct device *dev) 209{ 210 struct bh1780_data *ddata; 211 int state, ret; 212 struct i2c_client *client = to_i2c_client(dev); 213 214 ddata = i2c_get_clientdata(client); 215 state = ddata->power_state; 216 ret = bh1780_write(ddata, BH1780_REG_CONTROL, state, 217 "CONTROL"); 218 219 if (ret < 0) 220 return ret; 221 222 return 0; 223} 224#endif /* CONFIG_PM_SLEEP */ 225 226static SIMPLE_DEV_PM_OPS(bh1780_pm, bh1780_suspend, bh1780_resume); 227 228static const struct i2c_device_id bh1780_id[] = { 229 { "bh1780", 0 }, 230 { }, 231}; 232 233MODULE_DEVICE_TABLE(i2c, bh1780_id); 234 235#ifdef CONFIG_OF 236static const struct of_device_id of_bh1780_match[] = { 237 { .compatible = "rohm,bh1780gli", }, 238 {}, 239}; 240 241MODULE_DEVICE_TABLE(of, of_bh1780_match); 242#endif 243 244static struct i2c_driver bh1780_driver = { 245 .probe = bh1780_probe, 246 .remove = bh1780_remove, 247 .id_table = bh1780_id, 248 .driver = { 249 .name = "bh1780", 250 .pm = &bh1780_pm, 251 .of_match_table = of_match_ptr(of_bh1780_match), 252 }, 253}; 254 255module_i2c_driver(bh1780_driver); 256 257MODULE_DESCRIPTION("BH1780GLI Ambient Light Sensor Driver"); 258MODULE_LICENSE("GPL"); 259MODULE_AUTHOR("Hemanth V <hemanthv@ti.com>"); 260