linux/drivers/leds/leds-pca9633.c
<<
>>
Prefs
   1/*
   2 * Copyright 2011 bct electronic GmbH
   3 *
   4 * Author: Peter Meerwald <p.meerwald@bct-electronic.com>
   5 *
   6 * Based on leds-pca955x.c
   7 *
   8 * This file is subject to the terms and conditions of version 2 of
   9 * the GNU General Public License.  See the file COPYING in the main
  10 * directory of this archive for more details.
  11 *
  12 * LED driver for the PCA9633 I2C LED driver (7-bit slave address 0x62)
  13 *
  14 */
  15
  16#include <linux/module.h>
  17#include <linux/delay.h>
  18#include <linux/string.h>
  19#include <linux/ctype.h>
  20#include <linux/leds.h>
  21#include <linux/err.h>
  22#include <linux/i2c.h>
  23#include <linux/workqueue.h>
  24#include <linux/slab.h>
  25
  26/* LED select registers determine the source that drives LED outputs */
  27#define PCA9633_LED_OFF         0x0     /* LED driver off */
  28#define PCA9633_LED_ON          0x1     /* LED driver on */
  29#define PCA9633_LED_PWM         0x2     /* Controlled through PWM */
  30#define PCA9633_LED_GRP_PWM     0x3     /* Controlled through PWM/GRPPWM */
  31
  32#define PCA9633_MODE1           0x00
  33#define PCA9633_MODE2           0x01
  34#define PCA9633_PWM_BASE        0x02
  35#define PCA9633_LEDOUT          0x08
  36
  37static const struct i2c_device_id pca9633_id[] = {
  38        { "pca9633", 0 },
  39        { }
  40};
  41MODULE_DEVICE_TABLE(i2c, pca9633_id);
  42
  43struct pca9633_led {
  44        struct i2c_client *client;
  45        struct work_struct work;
  46        enum led_brightness brightness;
  47        struct led_classdev led_cdev;
  48        int led_num; /* 0 .. 3 potentially */
  49        char name[32];
  50};
  51
  52static void pca9633_led_work(struct work_struct *work)
  53{
  54        struct pca9633_led *pca9633 = container_of(work,
  55                struct pca9633_led, work);
  56        u8 ledout = i2c_smbus_read_byte_data(pca9633->client, PCA9633_LEDOUT);
  57        int shift = 2 * pca9633->led_num;
  58        u8 mask = 0x3 << shift;
  59
  60        switch (pca9633->brightness) {
  61        case LED_FULL:
  62                i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT,
  63                        (ledout & ~mask) | (PCA9633_LED_ON << shift));
  64                break;
  65        case LED_OFF:
  66                i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT,
  67                        ledout & ~mask);
  68                break;
  69        default:
  70                i2c_smbus_write_byte_data(pca9633->client,
  71                        PCA9633_PWM_BASE + pca9633->led_num,
  72                        pca9633->brightness);
  73                i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT,
  74                        (ledout & ~mask) | (PCA9633_LED_PWM << shift));
  75                break;
  76        }
  77}
  78
  79static void pca9633_led_set(struct led_classdev *led_cdev,
  80        enum led_brightness value)
  81{
  82        struct pca9633_led *pca9633;
  83
  84        pca9633 = container_of(led_cdev, struct pca9633_led, led_cdev);
  85
  86        pca9633->brightness = value;
  87
  88        /*
  89         * Must use workqueue for the actual I/O since I2C operations
  90         * can sleep.
  91         */
  92        schedule_work(&pca9633->work);
  93}
  94
  95static int __devinit pca9633_probe(struct i2c_client *client,
  96                                        const struct i2c_device_id *id)
  97{
  98        struct pca9633_led *pca9633;
  99        struct led_platform_data *pdata;
 100        int i, err;
 101
 102        pdata = client->dev.platform_data;
 103
 104        if (pdata) {
 105                if (pdata->num_leds <= 0 || pdata->num_leds > 4) {
 106                        dev_err(&client->dev, "board info must claim at most 4 LEDs");
 107                        return -EINVAL;
 108                }
 109        }
 110
 111        pca9633 = devm_kzalloc(&client->dev, 4 * sizeof(*pca9633), GFP_KERNEL);
 112        if (!pca9633)
 113                return -ENOMEM;
 114
 115        i2c_set_clientdata(client, pca9633);
 116
 117        for (i = 0; i < 4; i++) {
 118                pca9633[i].client = client;
 119                pca9633[i].led_num = i;
 120
 121                /* Platform data can specify LED names and default triggers */
 122                if (pdata && i < pdata->num_leds) {
 123                        if (pdata->leds[i].name)
 124                                snprintf(pca9633[i].name,
 125                                         sizeof(pca9633[i].name), "pca9633:%s",
 126                                         pdata->leds[i].name);
 127                        if (pdata->leds[i].default_trigger)
 128                                pca9633[i].led_cdev.default_trigger =
 129                                        pdata->leds[i].default_trigger;
 130                } else {
 131                        snprintf(pca9633[i].name, sizeof(pca9633[i].name),
 132                                 "pca9633:%d", i);
 133                }
 134
 135                pca9633[i].led_cdev.name = pca9633[i].name;
 136                pca9633[i].led_cdev.brightness_set = pca9633_led_set;
 137
 138                INIT_WORK(&pca9633[i].work, pca9633_led_work);
 139
 140                err = led_classdev_register(&client->dev, &pca9633[i].led_cdev);
 141                if (err < 0)
 142                        goto exit;
 143        }
 144
 145        /* Disable LED all-call address and set normal mode */
 146        i2c_smbus_write_byte_data(client, PCA9633_MODE1, 0x00);
 147
 148        /* Turn off LEDs */
 149        i2c_smbus_write_byte_data(client, PCA9633_LEDOUT, 0x00);
 150
 151        return 0;
 152
 153exit:
 154        while (i--) {
 155                led_classdev_unregister(&pca9633[i].led_cdev);
 156                cancel_work_sync(&pca9633[i].work);
 157        }
 158
 159        return err;
 160}
 161
 162static int __devexit pca9633_remove(struct i2c_client *client)
 163{
 164        struct pca9633_led *pca9633 = i2c_get_clientdata(client);
 165        int i;
 166
 167        for (i = 0; i < 4; i++) {
 168                led_classdev_unregister(&pca9633[i].led_cdev);
 169                cancel_work_sync(&pca9633[i].work);
 170        }
 171
 172        return 0;
 173}
 174
 175static struct i2c_driver pca9633_driver = {
 176        .driver = {
 177                .name   = "leds-pca9633",
 178                .owner  = THIS_MODULE,
 179        },
 180        .probe  = pca9633_probe,
 181        .remove = __devexit_p(pca9633_remove),
 182        .id_table = pca9633_id,
 183};
 184
 185module_i2c_driver(pca9633_driver);
 186
 187MODULE_AUTHOR("Peter Meerwald <p.meerwald@bct-electronic.com>");
 188MODULE_DESCRIPTION("PCA9633 LED driver");
 189MODULE_LICENSE("GPL v2");
 190
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.