linux/drivers/i2c/chips/tsl2550.c
<<
>>
Prefs
   1/*
   2 *  tsl2550.c - Linux kernel modules for ambient light sensor
   3 *
   4 *  Copyright (C) 2007 Rodolfo Giometti <giometti@linux.it>
   5 *  Copyright (C) 2007 Eurotech S.p.A. <info@eurotech.it>
   6 *
   7 *  This program is free software; you can redistribute it and/or modify
   8 *  it under the terms of the GNU General Public License as published by
   9 *  the Free Software Foundation; either version 2 of the License, or
  10 *  (at your option) any later version.
  11 *
  12 *  This program is distributed in the hope that it will be useful,
  13 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 *  GNU General Public License for more details.
  16 *
  17 *  You should have received a copy of the GNU General Public License
  18 *  along with this program; if not, write to the Free Software
  19 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20 */
  21
  22#include <linux/module.h>
  23#include <linux/init.h>
  24#include <linux/slab.h>
  25#include <linux/i2c.h>
  26#include <linux/mutex.h>
  27
  28#define TSL2550_DRV_NAME        "tsl2550"
  29#define DRIVER_VERSION          "1.2"
  30
  31/*
  32 * Defines
  33 */
  34
  35#define TSL2550_POWER_DOWN              0x00
  36#define TSL2550_POWER_UP                0x03
  37#define TSL2550_STANDARD_RANGE          0x18
  38#define TSL2550_EXTENDED_RANGE          0x1d
  39#define TSL2550_READ_ADC0               0x43
  40#define TSL2550_READ_ADC1               0x83
  41
  42/*
  43 * Structs
  44 */
  45
  46struct tsl2550_data {
  47        struct i2c_client *client;
  48        struct mutex update_lock;
  49
  50        unsigned int power_state : 1;
  51        unsigned int operating_mode : 1;
  52};
  53
  54/*
  55 * Global data
  56 */
  57
  58static const u8 TSL2550_MODE_RANGE[2] = {
  59        TSL2550_STANDARD_RANGE, TSL2550_EXTENDED_RANGE,
  60};
  61
  62/*
  63 * Management functions
  64 */
  65
  66static int tsl2550_set_operating_mode(struct i2c_client *client, int mode)
  67{
  68        struct tsl2550_data *data = i2c_get_clientdata(client);
  69
  70        int ret = i2c_smbus_write_byte(client, TSL2550_MODE_RANGE[mode]);
  71
  72        data->operating_mode = mode;
  73
  74        return ret;
  75}
  76
  77static int tsl2550_set_power_state(struct i2c_client *client, int state)
  78{
  79        struct tsl2550_data *data = i2c_get_clientdata(client);
  80        int ret;
  81
  82        if (state == 0)
  83                ret = i2c_smbus_write_byte(client, TSL2550_POWER_DOWN);
  84        else {
  85                ret = i2c_smbus_write_byte(client, TSL2550_POWER_UP);
  86
  87                /* On power up we should reset operating mode also... */
  88                tsl2550_set_operating_mode(client, data->operating_mode);
  89        }
  90
  91        data->power_state = state;
  92
  93        return ret;
  94}
  95
  96static int tsl2550_get_adc_value(struct i2c_client *client, u8 cmd)
  97{
  98        int ret;
  99
 100        ret = i2c_smbus_read_byte_data(client, cmd);
 101        if (ret < 0)
 102                return ret;
 103        if (!(ret & 0x80))
 104                return -EAGAIN;
 105        return ret & 0x7f;      /* remove the "valid" bit */
 106}
 107
 108/*
 109 * LUX calculation
 110 */
 111
 112#define TSL2550_MAX_LUX         1846
 113
 114static const u8 ratio_lut[] = {
 115        100, 100, 100, 100, 100, 100, 100, 100,
 116        100, 100, 100, 100, 100, 100, 99, 99,
 117        99, 99, 99, 99, 99, 99, 99, 99,
 118        99, 99, 99, 98, 98, 98, 98, 98,
 119        98, 98, 97, 97, 97, 97, 97, 96,
 120        96, 96, 96, 95, 95, 95, 94, 94,
 121        93, 93, 93, 92, 92, 91, 91, 90,
 122        89, 89, 88, 87, 87, 86, 85, 84,
 123        83, 82, 81, 80, 79, 78, 77, 75,
 124        74, 73, 71, 69, 68, 66, 64, 62,
 125        60, 58, 56, 54, 52, 49, 47, 44,
 126        42, 41, 40, 40, 39, 39, 38, 38,
 127        37, 37, 37, 36, 36, 36, 35, 35,
 128        35, 35, 34, 34, 34, 34, 33, 33,
 129        33, 33, 32, 32, 32, 32, 32, 31,
 130        31, 31, 31, 31, 30, 30, 30, 30,
 131        30,
 132};
 133
 134static const u16 count_lut[] = {
 135        0, 1, 2, 3, 4, 5, 6, 7,
 136        8, 9, 10, 11, 12, 13, 14, 15,
 137        16, 18, 20, 22, 24, 26, 28, 30,
 138        32, 34, 36, 38, 40, 42, 44, 46,
 139        49, 53, 57, 61, 65, 69, 73, 77,
 140        81, 85, 89, 93, 97, 101, 105, 109,
 141        115, 123, 131, 139, 147, 155, 163, 171,
 142        179, 187, 195, 203, 211, 219, 227, 235,
 143        247, 263, 279, 295, 311, 327, 343, 359,
 144        375, 391, 407, 423, 439, 455, 471, 487,
 145        511, 543, 575, 607, 639, 671, 703, 735,
 146        767, 799, 831, 863, 895, 927, 959, 991,
 147        1039, 1103, 1167, 1231, 1295, 1359, 1423, 1487,
 148        1551, 1615, 1679, 1743, 1807, 1871, 1935, 1999,
 149        2095, 2223, 2351, 2479, 2607, 2735, 2863, 2991,
 150        3119, 3247, 3375, 3503, 3631, 3759, 3887, 4015,
 151};
 152
 153/*
 154 * This function is described into Taos TSL2550 Designer's Notebook
 155 * pages 2, 3.
 156 */
 157static int tsl2550_calculate_lux(u8 ch0, u8 ch1)
 158{
 159        unsigned int lux;
 160
 161        /* Look up count from channel values */
 162        u16 c0 = count_lut[ch0];
 163        u16 c1 = count_lut[ch1];
 164
 165        /*
 166         * Calculate ratio.
 167         * Note: the "128" is a scaling factor
 168         */
 169        u8 r = 128;
 170
 171        /* Avoid division by 0 and count 1 cannot be greater than count 0 */
 172        if (c1 <= c0)
 173                if (c0) {
 174                        r = c1 * 128 / c0;
 175
 176                        /* Calculate LUX */
 177                        lux = ((c0 - c1) * ratio_lut[r]) / 256;
 178                } else
 179                        lux = 0;
 180        else
 181                return -EAGAIN;
 182
 183        /* LUX range check */
 184        return lux > TSL2550_MAX_LUX ? TSL2550_MAX_LUX : lux;
 185}
 186
 187/*
 188 * SysFS support
 189 */
 190
 191static ssize_t tsl2550_show_power_state(struct device *dev,
 192                struct device_attribute *attr, char *buf)
 193{
 194        struct tsl2550_data *data = i2c_get_clientdata(to_i2c_client(dev));
 195
 196        return sprintf(buf, "%u\n", data->power_state);
 197}
 198
 199static ssize_t tsl2550_store_power_state(struct device *dev,
 200                struct device_attribute *attr, const char *buf, size_t count)
 201{
 202        struct i2c_client *client = to_i2c_client(dev);
 203        struct tsl2550_data *data = i2c_get_clientdata(client);
 204        unsigned long val = simple_strtoul(buf, NULL, 10);
 205        int ret;
 206
 207        if (val < 0 || val > 1)
 208                return -EINVAL;
 209
 210        mutex_lock(&data->update_lock);
 211        ret = tsl2550_set_power_state(client, val);
 212        mutex_unlock(&data->update_lock);
 213
 214        if (ret < 0)
 215                return ret;
 216
 217        return count;
 218}
 219
 220static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO,
 221                   tsl2550_show_power_state, tsl2550_store_power_state);
 222
 223static ssize_t tsl2550_show_operating_mode(struct device *dev,
 224                struct device_attribute *attr, char *buf)
 225{
 226        struct tsl2550_data *data = i2c_get_clientdata(to_i2c_client(dev));
 227
 228        return sprintf(buf, "%u\n", data->operating_mode);
 229}
 230
 231static ssize_t tsl2550_store_operating_mode(struct device *dev,
 232                struct device_attribute *attr, const char *buf, size_t count)
 233{
 234        struct i2c_client *client = to_i2c_client(dev);
 235        struct tsl2550_data *data = i2c_get_clientdata(client);
 236        unsigned long val = simple_strtoul(buf, NULL, 10);
 237        int ret;
 238
 239        if (val < 0 || val > 1)
 240                return -EINVAL;
 241
 242        if (data->power_state == 0)
 243                return -EBUSY;
 244
 245        mutex_lock(&data->update_lock);
 246        ret = tsl2550_set_operating_mode(client, val);
 247        mutex_unlock(&data->update_lock);
 248
 249        if (ret < 0)
 250                return ret;
 251
 252        return count;
 253}
 254
 255static DEVICE_ATTR(operating_mode, S_IWUSR | S_IRUGO,
 256                   tsl2550_show_operating_mode, tsl2550_store_operating_mode);
 257
 258static ssize_t __tsl2550_show_lux(struct i2c_client *client, char *buf)
 259{
 260        struct tsl2550_data *data = i2c_get_clientdata(client);
 261        u8 ch0, ch1;
 262        int ret;
 263
 264        ret = tsl2550_get_adc_value(client, TSL2550_READ_ADC0);
 265        if (ret < 0)
 266                return ret;
 267        ch0 = ret;
 268
 269        ret = tsl2550_get_adc_value(client, TSL2550_READ_ADC1);
 270        if (ret < 0)
 271                return ret;
 272        ch1 = ret;
 273
 274        /* Do the job */
 275        ret = tsl2550_calculate_lux(ch0, ch1);
 276        if (ret < 0)
 277                return ret;
 278        if (data->operating_mode == 1)
 279                ret *= 5;
 280
 281        return sprintf(buf, "%d\n", ret);
 282}
 283
 284static ssize_t tsl2550_show_lux1_input(struct device *dev,
 285                        struct device_attribute *attr, char *buf)
 286{
 287        struct i2c_client *client = to_i2c_client(dev);
 288        struct tsl2550_data *data = i2c_get_clientdata(client);
 289        int ret;
 290
 291        /* No LUX data if not operational */
 292        if (!data->power_state)
 293                return -EBUSY;
 294
 295        mutex_lock(&data->update_lock);
 296        ret = __tsl2550_show_lux(client, buf);
 297        mutex_unlock(&data->update_lock);
 298
 299        return ret;
 300}
 301
 302static DEVICE_ATTR(lux1_input, S_IRUGO,
 303                   tsl2550_show_lux1_input, NULL);
 304
 305static struct attribute *tsl2550_attributes[] = {
 306        &dev_attr_power_state.attr,
 307        &dev_attr_operating_mode.attr,
 308        &dev_attr_lux1_input.attr,
 309        NULL
 310};
 311
 312static const struct attribute_group tsl2550_attr_group = {
 313        .attrs = tsl2550_attributes,
 314};
 315
 316/*
 317 * Initialization function
 318 */
 319
 320static int tsl2550_init_client(struct i2c_client *client)
 321{
 322        struct tsl2550_data *data = i2c_get_clientdata(client);
 323        int err;
 324
 325        /*
 326         * Probe the chip. To do so we try to power up the device and then to
 327         * read back the 0x03 code
 328         */
 329        err = i2c_smbus_read_byte_data(client, TSL2550_POWER_UP);
 330        if (err < 0)
 331                return err;
 332        if (err != TSL2550_POWER_UP)
 333                return -ENODEV;
 334        data->power_state = 1;
 335
 336        /* Set the default operating mode */
 337        err = i2c_smbus_write_byte(client,
 338                                   TSL2550_MODE_RANGE[data->operating_mode]);
 339        if (err < 0)
 340                return err;
 341
 342        return 0;
 343}
 344
 345/*
 346 * I2C init/probing/exit functions
 347 */
 348
 349static struct i2c_driver tsl2550_driver;
 350static int __devinit tsl2550_probe(struct i2c_client *client,
 351                                   const struct i2c_device_id *id)
 352{
 353        struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
 354        struct tsl2550_data *data;
 355        int *opmode, err = 0;
 356
 357        if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE
 358                                            | I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
 359                err = -EIO;
 360                goto exit;
 361        }
 362
 363        data = kzalloc(sizeof(struct tsl2550_data), GFP_KERNEL);
 364        if (!data) {
 365                err = -ENOMEM;
 366                goto exit;
 367        }
 368        data->client = client;
 369        i2c_set_clientdata(client, data);
 370
 371        /* Check platform data */
 372        opmode = client->dev.platform_data;
 373        if (opmode) {
 374                if (*opmode < 0 || *opmode > 1) {
 375                        dev_err(&client->dev, "invalid operating_mode (%d)\n",
 376                                        *opmode);
 377                        err = -EINVAL;
 378                        goto exit_kfree;
 379                }
 380                data->operating_mode = *opmode;
 381        } else
 382                data->operating_mode = 0;       /* default mode is standard */
 383        dev_info(&client->dev, "%s operating mode\n",
 384                        data->operating_mode ? "extended" : "standard");
 385
 386        mutex_init(&data->update_lock);
 387
 388        /* Initialize the TSL2550 chip */
 389        err = tsl2550_init_client(client);
 390        if (err)
 391                goto exit_kfree;
 392
 393        /* Register sysfs hooks */
 394        err = sysfs_create_group(&client->dev.kobj, &tsl2550_attr_group);
 395        if (err)
 396                goto exit_kfree;
 397
 398        dev_info(&client->dev, "support ver. %s enabled\n", DRIVER_VERSION);
 399
 400        return 0;
 401
 402exit_kfree:
 403        kfree(data);
 404exit:
 405        return err;
 406}
 407
 408static int __devexit tsl2550_remove(struct i2c_client *client)
 409{
 410        sysfs_remove_group(&client->dev.kobj, &tsl2550_attr_group);
 411
 412        /* Power down the device */
 413        tsl2550_set_power_state(client, 0);
 414
 415        kfree(i2c_get_clientdata(client));
 416
 417        return 0;
 418}
 419
 420#ifdef CONFIG_PM
 421
 422static int tsl2550_suspend(struct i2c_client *client, pm_message_t mesg)
 423{
 424        return tsl2550_set_power_state(client, 0);
 425}
 426
 427static int tsl2550_resume(struct i2c_client *client)
 428{
 429        return tsl2550_set_power_state(client, 1);
 430}
 431
 432#else
 433
 434#define tsl2550_suspend         NULL
 435#define tsl2550_resume          NULL
 436
 437#endif /* CONFIG_PM */
 438
 439static const struct i2c_device_id tsl2550_id[] = {
 440        { "tsl2550", 0 },
 441        { }
 442};
 443MODULE_DEVICE_TABLE(i2c, tsl2550_id);
 444
 445static struct i2c_driver tsl2550_driver = {
 446        .driver = {
 447                .name   = TSL2550_DRV_NAME,
 448                .owner  = THIS_MODULE,
 449        },
 450        .suspend = tsl2550_suspend,
 451        .resume = tsl2550_resume,
 452        .probe  = tsl2550_probe,
 453        .remove = __devexit_p(tsl2550_remove),
 454        .id_table = tsl2550_id,
 455};
 456
 457static int __init tsl2550_init(void)
 458{
 459        return i2c_add_driver(&tsl2550_driver);
 460}
 461
 462static void __exit tsl2550_exit(void)
 463{
 464        i2c_del_driver(&tsl2550_driver);
 465}
 466
 467MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
 468MODULE_DESCRIPTION("TSL2550 ambient light sensor driver");
 469MODULE_LICENSE("GPL");
 470MODULE_VERSION(DRIVER_VERSION);
 471
 472module_init(tsl2550_init);
 473module_exit(tsl2550_exit);
 474
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.