Commit 7efe15f2a4cd9d40826d31d7d5ef56094f8b65f5

Authored by Hemanth V
Committed by Linus Torvalds
1 parent 37ed19d5cc

drivers/misc: ROHM BH1780GLI ambient light sensor driver

Add support for ROHM BH1780GLI Ambient light sensor.

BH1780 supports I2C interface.  Driver supports read/update of power state
and read of lux value (through SYSFS).  Writing value 3 to power_state
enables the sensor and current lux value could be read.

Currently this driver follows the same sysfs convention as supported by
drivers/misc/isl29003.c.

Signed-off-by: Hemanth V <hemanthv@ti.com>
Reviewed-by: Daniel Mack <daniel@caiaq.de>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Cc: Jean Delvare <khali@linux-fr.org>
Cc: Wolfram Sang <w.sang@pengutronix.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Showing 3 changed files with 284 additions and 0 deletions Side-by-side Diff

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