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 27#define BH1780_REG_CONTROL 0x80 28#define BH1780_REG_PARTID 0x8A 29#define BH1780_REG_MANFID 0x8B 30#define BH1780_REG_DLOW 0x8C 31#define BH1780_REG_DHIGH 0x8D 32 33#define BH1780_REVMASK (0xf) 34#define BH1780_POWMASK (0x3) 35#define BH1780_POFF (0x0) 36#define BH1780_PON (0x3) 37 38/* power on settling time in ms */ 39#define BH1780_PON_DELAY 2 40 41struct bh1780_data { 42 struct i2c_client *client; 43 int power_state; 44 /* lock for sysfs operations */ 45 struct mutex lock; 46}; 47 48static int bh1780_write(struct bh1780_data *ddata, u8 reg, u8 val, char *msg) 49{ 50 int ret = i2c_smbus_write_byte_data(ddata->client, reg, val); 51 if (ret < 0) 52 dev_err(&ddata->client->dev, 53 "i2c_smbus_write_byte_data failed error %d Register (%s)\n", 54 ret, msg); 55 return ret; 56} 57 58static int bh1780_read(struct bh1780_data *ddata, u8 reg, char *msg) 59{ 60 int ret = i2c_smbus_read_byte_data(ddata->client, reg); 61 if (ret < 0) 62 dev_err(&ddata->client->dev, 63 "i2c_smbus_read_byte_data failed error %d Register (%s)\n", 64 ret, msg); 65 return ret; 66} 67 68static ssize_t bh1780_show_lux(struct device *dev, 69 struct device_attribute *attr, char *buf) 70{ 71 struct platform_device *pdev = to_platform_device(dev); 72 struct bh1780_data *ddata = platform_get_drvdata(pdev); 73 int lsb, msb; 74 75 lsb = bh1780_read(ddata, BH1780_REG_DLOW, "DLOW"); 76 if (lsb < 0) 77 return lsb; 78 79 msb = bh1780_read(ddata, BH1780_REG_DHIGH, "DHIGH"); 80 if (msb < 0) 81 return msb; 82 83 return sprintf(buf, "%d\n", (msb << 8) | lsb); 84} 85 86static ssize_t bh1780_show_power_state(struct device *dev, 87 struct device_attribute *attr, 88 char *buf) 89{ 90 struct platform_device *pdev = to_platform_device(dev); 91 struct bh1780_data *ddata = platform_get_drvdata(pdev); 92 int state; 93 94 state = bh1780_read(ddata, BH1780_REG_CONTROL, "CONTROL"); 95 if (state < 0) 96 return state; 97 98 return sprintf(buf, "%d\n", state & BH1780_POWMASK); 99} 100 101static ssize_t bh1780_store_power_state(struct device *dev, 102 struct device_attribute *attr, 103 const char *buf, size_t count) 104{ 105 struct platform_device *pdev = to_platform_device(dev); 106 struct bh1780_data *ddata = platform_get_drvdata(pdev); 107 unsigned long val; 108 int error; 109 110 error = strict_strtoul(buf, 0, &val); 111 if (error) 112 return error; 113 114 if (val < BH1780_POFF || val > BH1780_PON) 115 return -EINVAL; 116 117 mutex_lock(&ddata->lock); 118 119 error = bh1780_write(ddata, BH1780_REG_CONTROL, val, "CONTROL"); 120 if (error < 0) { 121 mutex_unlock(&ddata->lock); 122 return error; 123 } 124 125 msleep(BH1780_PON_DELAY); 126 ddata->power_state = val; 127 mutex_unlock(&ddata->lock); 128 129 return count; 130} 131 132static DEVICE_ATTR(lux, S_IRUGO, bh1780_show_lux, NULL); 133 134static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO, 135 bh1780_show_power_state, bh1780_store_power_state); 136 137static struct attribute *bh1780_attributes[] = { 138 &dev_attr_power_state.attr, 139 &dev_attr_lux.attr, 140 NULL 141}; 142 143static const struct attribute_group bh1780_attr_group = { 144 .attrs = bh1780_attributes, 145}; 146 147static int __devinit bh1780_probe(struct i2c_client *client, 148 const struct i2c_device_id *id) 149{ 150 int ret; 151 struct bh1780_data *ddata = NULL; 152 struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); 153 154 if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) { 155 ret = -EIO; 156 goto err_op_failed; 157 } 158 159 ddata = kzalloc(sizeof(struct bh1780_data), GFP_KERNEL); 160 if (ddata == NULL) { 161 ret = -ENOMEM; 162 goto err_op_failed; 163 } 164 165 ddata->client = client; 166 i2c_set_clientdata(client, ddata); 167 168 ret = bh1780_read(ddata, BH1780_REG_PARTID, "PART ID"); 169 if (ret < 0) 170 goto err_op_failed; 171 172 dev_info(&client->dev, "Ambient Light Sensor, Rev : %d\n", 173 (ret & BH1780_REVMASK)); 174 175 mutex_init(&ddata->lock); 176 177 ret = sysfs_create_group(&client->dev.kobj, &bh1780_attr_group); 178 if (ret) 179 goto err_op_failed; 180 181 return 0; 182 183err_op_failed: 184 kfree(ddata); 185 return ret; 186} 187 188static int __devexit bh1780_remove(struct i2c_client *client) 189{ 190 struct bh1780_data *ddata; 191 192 ddata = i2c_get_clientdata(client); 193 sysfs_remove_group(&client->dev.kobj, &bh1780_attr_group); 194 kfree(ddata); 195 196 return 0; 197} 198 199#ifdef CONFIG_PM 200static int bh1780_suspend(struct device *dev) 201{ 202 struct bh1780_data *ddata; 203 int state, ret; 204 struct i2c_client *client = to_i2c_client(dev); 205 206 ddata = i2c_get_clientdata(client); 207 state = bh1780_read(ddata, BH1780_REG_CONTROL, "CONTROL"); 208 if (state < 0) 209 return state; 210 211 ddata->power_state = state & BH1780_POWMASK; 212 213 ret = bh1780_write(ddata, BH1780_REG_CONTROL, BH1780_POFF, 214 "CONTROL"); 215 216 if (ret < 0) 217 return ret; 218 219 return 0; 220} 221 222static int bh1780_resume(struct device *dev) 223{ 224 struct bh1780_data *ddata; 225 int state, ret; 226 struct i2c_client *client = to_i2c_client(dev); 227 228 ddata = i2c_get_clientdata(client); 229 state = ddata->power_state; 230 ret = bh1780_write(ddata, BH1780_REG_CONTROL, state, 231 "CONTROL"); 232 233 if (ret < 0) 234 return ret; 235 236 return 0; 237} 238static SIMPLE_DEV_PM_OPS(bh1780_pm, bh1780_suspend, bh1780_resume); 239#define BH1780_PMOPS (&bh1780_pm) 240#else 241#define BH1780_PMOPS NULL 242#endif /* CONFIG_PM */ 243 244static const struct i2c_device_id bh1780_id[] = { 245 { "bh1780", 0 }, 246 { }, 247}; 248 249static struct i2c_driver bh1780_driver = { 250 .probe = bh1780_probe, 251 .remove = bh1780_remove, 252 .id_table = bh1780_id, 253 .driver = { 254 .name = "bh1780", 255 .pm = BH1780_PMOPS, 256}, 257}; 258 259static int __init bh1780_init(void) 260{ 261 return i2c_add_driver(&bh1780_driver); 262} 263 264static void __exit bh1780_exit(void) 265{ 266 i2c_del_driver(&bh1780_driver); 267} 268 269module_init(bh1780_init) 270module_exit(bh1780_exit) 271 272MODULE_DESCRIPTION("BH1780GLI Ambient Light Sensor Driver"); 273MODULE_LICENSE("GPL"); 274MODULE_AUTHOR("Hemanth V <hemanthv@ti.com>"); 275