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