linux/drivers/iio/light/bh1780.c
<<
>>
Prefs
   1/*
   2 * ROHM 1780GLI Ambient Light Sensor Driver
   3 *
   4 * Copyright (C) 2016 Linaro Ltd.
   5 * Author: Linus Walleij <linus.walleij@linaro.org>
   6 * Loosely based on the previous BH1780 ALS misc driver
   7 * Copyright (C) 2010 Texas Instruments
   8 * Author: Hemanth V <hemanthv@ti.com>
   9 */
  10#include <linux/i2c.h>
  11#include <linux/slab.h>
  12#include <linux/platform_device.h>
  13#include <linux/delay.h>
  14#include <linux/module.h>
  15#include <linux/of.h>
  16#include <linux/pm_runtime.h>
  17#include <linux/iio/iio.h>
  18#include <linux/iio/sysfs.h>
  19#include <linux/bitops.h>
  20
  21#define BH1780_CMD_BIT          BIT(7)
  22#define BH1780_REG_CONTROL      0x00
  23#define BH1780_REG_PARTID       0x0A
  24#define BH1780_REG_MANFID       0x0B
  25#define BH1780_REG_DLOW         0x0C
  26#define BH1780_REG_DHIGH        0x0D
  27
  28#define BH1780_REVMASK          GENMASK(3,0)
  29#define BH1780_POWMASK          GENMASK(1,0)
  30#define BH1780_POFF             (0x0)
  31#define BH1780_PON              (0x3)
  32
  33/* power on settling time in ms */
  34#define BH1780_PON_DELAY        2
  35/* max time before value available in ms */
  36#define BH1780_INTERVAL         250
  37
  38struct bh1780_data {
  39        struct i2c_client *client;
  40};
  41
  42static int bh1780_write(struct bh1780_data *bh1780, u8 reg, u8 val)
  43{
  44        int ret = i2c_smbus_write_byte_data(bh1780->client,
  45                                            BH1780_CMD_BIT | reg,
  46                                            val);
  47        if (ret < 0)
  48                dev_err(&bh1780->client->dev,
  49                        "i2c_smbus_write_byte_data failed error "
  50                        "%d, register %01x\n",
  51                        ret, reg);
  52        return ret;
  53}
  54
  55static int bh1780_read(struct bh1780_data *bh1780, u8 reg)
  56{
  57        int ret = i2c_smbus_read_byte_data(bh1780->client,
  58                                           BH1780_CMD_BIT | reg);
  59        if (ret < 0)
  60                dev_err(&bh1780->client->dev,
  61                        "i2c_smbus_read_byte_data failed error "
  62                        "%d, register %01x\n",
  63                        ret, reg);
  64        return ret;
  65}
  66
  67static int bh1780_read_word(struct bh1780_data *bh1780, u8 reg)
  68{
  69        int ret = i2c_smbus_read_word_data(bh1780->client,
  70                                           BH1780_CMD_BIT | reg);
  71        if (ret < 0)
  72                dev_err(&bh1780->client->dev,
  73                        "i2c_smbus_read_word_data failed error "
  74                        "%d, register %01x\n",
  75                        ret, reg);
  76        return ret;
  77}
  78
  79static int bh1780_debugfs_reg_access(struct iio_dev *indio_dev,
  80                              unsigned int reg, unsigned int writeval,
  81                              unsigned int *readval)
  82{
  83        struct bh1780_data *bh1780 = iio_priv(indio_dev);
  84        int ret;
  85
  86        if (!readval)
  87                return bh1780_write(bh1780, (u8)reg, (u8)writeval);
  88
  89        ret = bh1780_read(bh1780, (u8)reg);
  90        if (ret < 0)
  91                return ret;
  92
  93        *readval = ret;
  94
  95        return 0;
  96}
  97
  98static int bh1780_read_raw(struct iio_dev *indio_dev,
  99                           struct iio_chan_spec const *chan,
 100                           int *val, int *val2, long mask)
 101{
 102        struct bh1780_data *bh1780 = iio_priv(indio_dev);
 103        int value;
 104
 105        switch (mask) {
 106        case IIO_CHAN_INFO_RAW:
 107                switch (chan->type) {
 108                case IIO_LIGHT:
 109                        pm_runtime_get_sync(&bh1780->client->dev);
 110                        value = bh1780_read_word(bh1780, BH1780_REG_DLOW);
 111                        if (value < 0)
 112                                return value;
 113                        pm_runtime_mark_last_busy(&bh1780->client->dev);
 114                        pm_runtime_put_autosuspend(&bh1780->client->dev);
 115                        *val = value;
 116
 117                        return IIO_VAL_INT;
 118                default:
 119                        return -EINVAL;
 120                }
 121        case IIO_CHAN_INFO_INT_TIME:
 122                *val = 0;
 123                *val2 = BH1780_INTERVAL * 1000;
 124                return IIO_VAL_INT_PLUS_MICRO;
 125        default:
 126                return -EINVAL;
 127        }
 128}
 129
 130static const struct iio_info bh1780_info = {
 131        .driver_module = THIS_MODULE,
 132        .read_raw = bh1780_read_raw,
 133        .debugfs_reg_access = bh1780_debugfs_reg_access,
 134};
 135
 136static const struct iio_chan_spec bh1780_channels[] = {
 137        {
 138                .type = IIO_LIGHT,
 139                .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
 140                                      BIT(IIO_CHAN_INFO_INT_TIME)
 141        }
 142};
 143
 144static int bh1780_probe(struct i2c_client *client,
 145                        const struct i2c_device_id *id)
 146{
 147        int ret;
 148        struct bh1780_data *bh1780;
 149        struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
 150        struct iio_dev *indio_dev;
 151
 152        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
 153                return -EIO;
 154
 155        indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*bh1780));
 156        if (!indio_dev)
 157                return -ENOMEM;
 158
 159        bh1780 = iio_priv(indio_dev);
 160        bh1780->client = client;
 161        i2c_set_clientdata(client, indio_dev);
 162
 163        /* Power up the device */
 164        ret = bh1780_write(bh1780, BH1780_REG_CONTROL, BH1780_PON);
 165        if (ret < 0)
 166                return ret;
 167        msleep(BH1780_PON_DELAY);
 168        pm_runtime_get_noresume(&client->dev);
 169        pm_runtime_set_active(&client->dev);
 170        pm_runtime_enable(&client->dev);
 171
 172        ret = bh1780_read(bh1780, BH1780_REG_PARTID);
 173        if (ret < 0)
 174                goto out_disable_pm;
 175        dev_info(&client->dev,
 176                 "Ambient Light Sensor, Rev : %lu\n",
 177                 (ret & BH1780_REVMASK));
 178
 179        /*
 180         * As the device takes 250 ms to even come up with a fresh
 181         * measurement after power-on, do not shut it down unnecessarily.
 182         * Set autosuspend to a five seconds.
 183         */
 184        pm_runtime_set_autosuspend_delay(&client->dev, 5000);
 185        pm_runtime_use_autosuspend(&client->dev);
 186        pm_runtime_put(&client->dev);
 187
 188        indio_dev->dev.parent = &client->dev;
 189        indio_dev->info = &bh1780_info;
 190        indio_dev->name = "bh1780";
 191        indio_dev->channels = bh1780_channels;
 192        indio_dev->num_channels = ARRAY_SIZE(bh1780_channels);
 193        indio_dev->modes = INDIO_DIRECT_MODE;
 194
 195        ret = iio_device_register(indio_dev);
 196        if (ret)
 197                goto out_disable_pm;
 198        return 0;
 199
 200out_disable_pm:
 201        pm_runtime_put_noidle(&client->dev);
 202        pm_runtime_disable(&client->dev);
 203        return ret;
 204}
 205
 206static int bh1780_remove(struct i2c_client *client)
 207{
 208        struct iio_dev *indio_dev = i2c_get_clientdata(client);
 209        struct bh1780_data *bh1780 = iio_priv(indio_dev);
 210        int ret;
 211
 212        iio_device_unregister(indio_dev);
 213        pm_runtime_get_sync(&client->dev);
 214        pm_runtime_put_noidle(&client->dev);
 215        pm_runtime_disable(&client->dev);
 216        ret = bh1780_write(bh1780, BH1780_REG_CONTROL, BH1780_POFF);
 217        if (ret < 0) {
 218                dev_err(&client->dev, "failed to power off\n");
 219                return ret;
 220        }
 221
 222        return 0;
 223}
 224
 225#ifdef CONFIG_PM
 226static int bh1780_runtime_suspend(struct device *dev)
 227{
 228        struct i2c_client *client = to_i2c_client(dev);
 229        struct iio_dev *indio_dev = i2c_get_clientdata(client);
 230        struct bh1780_data *bh1780 = iio_priv(indio_dev);
 231        int ret;
 232
 233        ret = bh1780_write(bh1780, BH1780_REG_CONTROL, BH1780_POFF);
 234        if (ret < 0) {
 235                dev_err(dev, "failed to runtime suspend\n");
 236                return ret;
 237        }
 238
 239        return 0;
 240}
 241
 242static int bh1780_runtime_resume(struct device *dev)
 243{
 244        struct i2c_client *client = to_i2c_client(dev);
 245        struct iio_dev *indio_dev = i2c_get_clientdata(client);
 246        struct bh1780_data *bh1780 = iio_priv(indio_dev);
 247        int ret;
 248
 249        ret = bh1780_write(bh1780, BH1780_REG_CONTROL, BH1780_PON);
 250        if (ret < 0) {
 251                dev_err(dev, "failed to runtime resume\n");
 252                return ret;
 253        }
 254
 255        /* Wait for power on, then for a value to be available */
 256        msleep(BH1780_PON_DELAY + BH1780_INTERVAL);
 257
 258        return 0;
 259}
 260#endif /* CONFIG_PM */
 261
 262static const struct dev_pm_ops bh1780_dev_pm_ops = {
 263        SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
 264                                pm_runtime_force_resume)
 265        SET_RUNTIME_PM_OPS(bh1780_runtime_suspend,
 266                           bh1780_runtime_resume, NULL)
 267};
 268
 269static const struct i2c_device_id bh1780_id[] = {
 270        { "bh1780", 0 },
 271        { },
 272};
 273
 274MODULE_DEVICE_TABLE(i2c, bh1780_id);
 275
 276#ifdef CONFIG_OF
 277static const struct of_device_id of_bh1780_match[] = {
 278        { .compatible = "rohm,bh1780gli", },
 279        {},
 280};
 281MODULE_DEVICE_TABLE(of, of_bh1780_match);
 282#endif
 283
 284static struct i2c_driver bh1780_driver = {
 285        .probe          = bh1780_probe,
 286        .remove         = bh1780_remove,
 287        .id_table       = bh1780_id,
 288        .driver = {
 289                .name = "bh1780",
 290                .pm = &bh1780_dev_pm_ops,
 291                .of_match_table = of_match_ptr(of_bh1780_match),
 292        },
 293};
 294
 295module_i2c_driver(bh1780_driver);
 296
 297MODULE_DESCRIPTION("ROHM BH1780GLI Ambient Light Sensor Driver");
 298MODULE_LICENSE("GPL");
 299MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
 300