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
  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
  40struct bh1780_data {
  41        struct i2c_client *client;
  42        int power_state;
  43        /* lock for sysfs operations */
  44        struct mutex lock;
  45};
  46
  47static 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
  57static 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
  67static 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
  85static 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
 100static 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
 131static DEVICE_ATTR(lux, S_IRUGO, bh1780_show_lux, NULL);
 132
 133static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO,
 134                bh1780_show_power_state, bh1780_store_power_state);
 135
 136static struct attribute *bh1780_attributes[] = {
 137        &dev_attr_power_state.attr,
 138        &dev_attr_lux.attr,
 139        NULL
 140};
 141
 142static const struct attribute_group bh1780_attr_group = {
 143        .attrs = bh1780_attributes,
 144};
 145
 146static 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
 182err_op_failed:
 183        kfree(ddata);
 184        return ret;
 185}
 186
 187static 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        kfree(ddata);
 194
 195        return 0;
 196}
 197
 198#ifdef CONFIG_PM
 199static int bh1780_suspend(struct i2c_client *client, pm_message_t mesg)
 200{
 201        struct bh1780_data *ddata;
 202        int state, ret;
 203
 204        ddata = i2c_get_clientdata(client);
 205        state = bh1780_read(ddata, BH1780_REG_CONTROL, "CONTROL");
 206        if (state < 0)
 207                return state;
 208
 209        ddata->power_state = state & BH1780_POWMASK;
 210
 211        ret = bh1780_write(ddata, BH1780_REG_CONTROL, BH1780_POFF,
 212                                "CONTROL");
 213
 214        if (ret < 0)
 215                return ret;
 216
 217        return 0;
 218}
 219
 220static int bh1780_resume(struct i2c_client *client)
 221{
 222        struct bh1780_data *ddata;
 223        int state, ret;
 224
 225        ddata = i2c_get_clientdata(client);
 226        state = ddata->power_state;
 227
 228        ret = bh1780_write(ddata, BH1780_REG_CONTROL, state,
 229                                "CONTROL");
 230
 231        if (ret < 0)
 232                return ret;
 233
 234        return 0;
 235}
 236#else
 237#define bh1780_suspend NULL
 238#define bh1780_resume NULL
 239#endif /* CONFIG_PM */
 240
 241static const struct i2c_device_id bh1780_id[] = {
 242        { "bh1780", 0 },
 243        { },
 244};
 245
 246static struct i2c_driver bh1780_driver = {
 247        .probe          = bh1780_probe,
 248        .remove         = bh1780_remove,
 249        .id_table       = bh1780_id,
 250        .suspend        = bh1780_suspend,
 251        .resume         = bh1780_resume,
 252        .driver = {
 253                .name = "bh1780"
 254        },
 255};
 256
 257static int __init bh1780_init(void)
 258{
 259        return i2c_add_driver(&bh1780_driver);
 260}
 261
 262static void __exit bh1780_exit(void)
 263{
 264        i2c_del_driver(&bh1780_driver);
 265}
 266
 267module_init(bh1780_init)
 268module_exit(bh1780_exit)
 269
 270MODULE_DESCRIPTION("BH1780GLI Ambient Light Sensor Driver");
 271MODULE_LICENSE("GPL");
 272MODULE_AUTHOR("Hemanth V <hemanthv@ti.com>");
 273