linux/drivers/misc/bh1780gli.c
<<
>>
Prefs
   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