linux/drivers/leds/leds-mc13783.c
<<
>>
Prefs
   1/*
   2 * LEDs driver for Freescale MC13783
   3 *
   4 * Copyright (C) 2010 Philippe R\xC3\xA9tornaz
   5 *
   6 * Based on leds-da903x:
   7 * Copyright (C) 2008 Compulab, Ltd.
   8 *      Mike Rapoport <mike@compulab.co.il>
   9 *
  10 * Copyright (C) 2006-2008 Marvell International Ltd.
  11 *      Eric Miao <eric.miao@marvell.com>
  12 *
  13 * This program is free software; you can redistribute it and/or modify
  14 * it under the terms of the GNU General Public License version 2 as
  15 * published by the Free Software Foundation.
  16 */
  17
  18#include <linux/module.h>
  19#include <linux/kernel.h>
  20#include <linux/init.h>
  21#include <linux/platform_device.h>
  22#include <linux/leds.h>
  23#include <linux/workqueue.h>
  24#include <linux/mfd/mc13783.h>
  25#include <linux/slab.h>
  26
  27struct mc13783_led {
  28        struct led_classdev     cdev;
  29        struct work_struct      work;
  30        struct mc13783          *master;
  31        enum led_brightness     new_brightness;
  32        int                     id;
  33};
  34
  35#define MC13783_REG_LED_CONTROL_0       51
  36#define MC13783_LED_C0_ENABLE_BIT       (1 << 0)
  37#define MC13783_LED_C0_TRIODE_MD_BIT    (1 << 7)
  38#define MC13783_LED_C0_TRIODE_AD_BIT    (1 << 8)
  39#define MC13783_LED_C0_TRIODE_KP_BIT    (1 << 9)
  40#define MC13783_LED_C0_BOOST_BIT        (1 << 10)
  41#define MC13783_LED_C0_ABMODE_MASK      0x7
  42#define MC13783_LED_C0_ABMODE           11
  43#define MC13783_LED_C0_ABREF_MASK       0x3
  44#define MC13783_LED_C0_ABREF            14
  45
  46#define MC13783_REG_LED_CONTROL_1       52
  47#define MC13783_LED_C1_TC1HALF_BIT      (1 << 18)
  48
  49#define MC13783_REG_LED_CONTROL_2       53
  50#define MC13783_LED_C2_BL_P_MASK        0xf
  51#define MC13783_LED_C2_MD_P             9
  52#define MC13783_LED_C2_AD_P             13
  53#define MC13783_LED_C2_KP_P             17
  54#define MC13783_LED_C2_BL_C_MASK        0x7
  55#define MC13783_LED_C2_MD_C             0
  56#define MC13783_LED_C2_AD_C             3
  57#define MC13783_LED_C2_KP_C             6
  58
  59#define MC13783_REG_LED_CONTROL_3       54
  60#define MC13783_LED_C3_TC_P             6
  61#define MC13783_LED_C3_TC_P_MASK        0x1f
  62
  63#define MC13783_REG_LED_CONTROL_4       55
  64#define MC13783_REG_LED_CONTROL_5       56
  65
  66#define MC13783_LED_Cx_PERIOD           21
  67#define MC13783_LED_Cx_PERIOD_MASK      0x3
  68#define MC13783_LED_Cx_SLEWLIM_BIT      (1 << 23)
  69#define MC13783_LED_Cx_TRIODE_TC_BIT    (1 << 23)
  70#define MC13783_LED_Cx_TC_C_MASK        0x3
  71
  72static void mc13783_led_work(struct work_struct *work)
  73{
  74        struct mc13783_led *led = container_of(work, struct mc13783_led, work);
  75        int reg = 0;
  76        int mask = 0;
  77        int value = 0;
  78        int bank, off, shift;
  79
  80        switch (led->id) {
  81        case MC13783_LED_MD:
  82                reg = MC13783_REG_LED_CONTROL_2;
  83                mask = MC13783_LED_C2_BL_P_MASK << MC13783_LED_C2_MD_P;
  84                value = (led->new_brightness >> 4) << MC13783_LED_C2_MD_P;
  85                break;
  86        case MC13783_LED_AD:
  87                reg = MC13783_REG_LED_CONTROL_2;
  88                mask = MC13783_LED_C2_BL_P_MASK << MC13783_LED_C2_AD_P;
  89                value = (led->new_brightness >> 4) << MC13783_LED_C2_AD_P;
  90                break;
  91        case MC13783_LED_KP:
  92                reg = MC13783_REG_LED_CONTROL_2;
  93                mask = MC13783_LED_C2_BL_P_MASK << MC13783_LED_C2_KP_P;
  94                value = (led->new_brightness >> 4) << MC13783_LED_C2_KP_P;
  95                break;
  96        case MC13783_LED_R1:
  97        case MC13783_LED_G1:
  98        case MC13783_LED_B1:
  99        case MC13783_LED_R2:
 100        case MC13783_LED_G2:
 101        case MC13783_LED_B2:
 102        case MC13783_LED_R3:
 103        case MC13783_LED_G3:
 104        case MC13783_LED_B3:
 105                off = led->id - MC13783_LED_R1;
 106                bank = off/3;
 107                reg = MC13783_REG_LED_CONTROL_3 + off/3;
 108                shift = (off - bank * 3) * 5 + MC13783_LED_C3_TC_P;
 109                value = (led->new_brightness >> 3) << shift;
 110                mask = MC13783_LED_C3_TC_P_MASK << shift;
 111                break;
 112        }
 113
 114        mc13783_lock(led->master);
 115
 116        mc13783_reg_rmw(led->master, reg, mask, value);
 117
 118        mc13783_unlock(led->master);
 119}
 120
 121static void mc13783_led_set(struct led_classdev *led_cdev,
 122                           enum led_brightness value)
 123{
 124        struct mc13783_led *led;
 125
 126        led = container_of(led_cdev, struct mc13783_led, cdev);
 127        led->new_brightness = value;
 128        schedule_work(&led->work);
 129}
 130
 131static int __devinit mc13783_led_setup(struct mc13783_led *led, int max_current)
 132{
 133        int shift = 0;
 134        int mask = 0;
 135        int value = 0;
 136        int reg = 0;
 137        int ret, bank;
 138
 139        switch (led->id) {
 140        case MC13783_LED_MD:
 141                shift = MC13783_LED_C2_MD_C;
 142                mask = MC13783_LED_C2_BL_C_MASK;
 143                value = max_current & MC13783_LED_C2_BL_C_MASK;
 144                reg = MC13783_REG_LED_CONTROL_2;
 145                break;
 146        case MC13783_LED_AD:
 147                shift = MC13783_LED_C2_AD_C;
 148                mask = MC13783_LED_C2_BL_C_MASK;
 149                value = max_current & MC13783_LED_C2_BL_C_MASK;
 150                reg = MC13783_REG_LED_CONTROL_2;
 151                break;
 152        case MC13783_LED_KP:
 153                shift = MC13783_LED_C2_KP_C;
 154                mask = MC13783_LED_C2_BL_C_MASK;
 155                value = max_current & MC13783_LED_C2_BL_C_MASK;
 156                reg = MC13783_REG_LED_CONTROL_2;
 157                break;
 158        case MC13783_LED_R1:
 159        case MC13783_LED_G1:
 160        case MC13783_LED_B1:
 161        case MC13783_LED_R2:
 162        case MC13783_LED_G2:
 163        case MC13783_LED_B2:
 164        case MC13783_LED_R3:
 165        case MC13783_LED_G3:
 166        case MC13783_LED_B3:
 167                bank = (led->id - MC13783_LED_R1)/3;
 168                reg = MC13783_REG_LED_CONTROL_3 + bank;
 169                shift = ((led->id - MC13783_LED_R1) - bank * 3) * 2;
 170                mask = MC13783_LED_Cx_TC_C_MASK;
 171                value = max_current & MC13783_LED_Cx_TC_C_MASK;
 172                break;
 173        }
 174
 175        mc13783_lock(led->master);
 176
 177        ret = mc13783_reg_rmw(led->master, reg, mask << shift,
 178                                                value << shift);
 179
 180        mc13783_unlock(led->master);
 181        return ret;
 182}
 183
 184static int __devinit mc13783_leds_prepare(struct platform_device *pdev)
 185{
 186        struct mc13783_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
 187        struct mc13783 *dev = dev_get_drvdata(pdev->dev.parent);
 188        int ret = 0;
 189        int reg = 0;
 190
 191        mc13783_lock(dev);
 192
 193        if (pdata->flags & MC13783_LED_TC1HALF)
 194                reg |= MC13783_LED_C1_TC1HALF_BIT;
 195
 196        if (pdata->flags & MC13783_LED_SLEWLIMTC)
 197                reg |= MC13783_LED_Cx_SLEWLIM_BIT;
 198
 199        ret = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_1, reg);
 200        if (ret)
 201                goto out;
 202
 203        reg = (pdata->bl_period & MC13783_LED_Cx_PERIOD_MASK) <<
 204                                                        MC13783_LED_Cx_PERIOD;
 205
 206        if (pdata->flags & MC13783_LED_SLEWLIMBL)
 207                reg |= MC13783_LED_Cx_SLEWLIM_BIT;
 208
 209        ret = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_2, reg);
 210        if (ret)
 211                goto out;
 212
 213        reg = (pdata->tc1_period & MC13783_LED_Cx_PERIOD_MASK) <<
 214                                                        MC13783_LED_Cx_PERIOD;
 215
 216        if (pdata->flags & MC13783_LED_TRIODE_TC1)
 217                reg |= MC13783_LED_Cx_TRIODE_TC_BIT;
 218
 219        ret = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_3, reg);
 220        if (ret)
 221                goto out;
 222
 223        reg = (pdata->tc2_period & MC13783_LED_Cx_PERIOD_MASK) <<
 224                                                        MC13783_LED_Cx_PERIOD;
 225
 226        if (pdata->flags & MC13783_LED_TRIODE_TC2)
 227                reg |= MC13783_LED_Cx_TRIODE_TC_BIT;
 228
 229        ret = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_4, reg);
 230        if (ret)
 231                goto out;
 232
 233        reg = (pdata->tc3_period & MC13783_LED_Cx_PERIOD_MASK) <<
 234                                                        MC13783_LED_Cx_PERIOD;
 235
 236        if (pdata->flags & MC13783_LED_TRIODE_TC3)
 237                reg |= MC13783_LED_Cx_TRIODE_TC_BIT;;
 238
 239        ret = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_5, reg);
 240        if (ret)
 241                goto out;
 242
 243        reg = MC13783_LED_C0_ENABLE_BIT;
 244        if (pdata->flags & MC13783_LED_TRIODE_MD)
 245                reg |= MC13783_LED_C0_TRIODE_MD_BIT;
 246        if (pdata->flags & MC13783_LED_TRIODE_AD)
 247                reg |= MC13783_LED_C0_TRIODE_AD_BIT;
 248        if (pdata->flags & MC13783_LED_TRIODE_KP)
 249                reg |= MC13783_LED_C0_TRIODE_KP_BIT;
 250        if (pdata->flags & MC13783_LED_BOOST_EN)
 251                reg |= MC13783_LED_C0_BOOST_BIT;
 252
 253        reg |= (pdata->abmode & MC13783_LED_C0_ABMODE_MASK) <<
 254                                                        MC13783_LED_C0_ABMODE;
 255        reg |= (pdata->abref & MC13783_LED_C0_ABREF_MASK) <<
 256                                                        MC13783_LED_C0_ABREF;
 257
 258        ret = mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_0, reg);
 259
 260out:
 261        mc13783_unlock(dev);
 262        return ret;
 263}
 264
 265static int __devinit mc13783_led_probe(struct platform_device *pdev)
 266{
 267        struct mc13783_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
 268        struct mc13783_led_platform_data *led_cur;
 269        struct mc13783_led *led, *led_dat;
 270        int ret, i;
 271        int init_led = 0;
 272
 273        if (pdata == NULL) {
 274                dev_err(&pdev->dev, "missing platform data\n");
 275                return -ENODEV;
 276        }
 277
 278        if (pdata->num_leds < 1 || pdata->num_leds > MC13783_LED_MAX) {
 279                dev_err(&pdev->dev, "Invalid led count %d\n", pdata->num_leds);
 280                return -EINVAL;
 281        }
 282
 283        led = kzalloc(sizeof(*led) * pdata->num_leds, GFP_KERNEL);
 284        if (led == NULL) {
 285                dev_err(&pdev->dev, "failed to alloc memory\n");
 286                return -ENOMEM;
 287        }
 288
 289        ret = mc13783_leds_prepare(pdev);
 290        if (ret) {
 291                dev_err(&pdev->dev, "unable to init led driver\n");
 292                goto err_free;
 293        }
 294
 295        for (i = 0; i < pdata->num_leds; i++) {
 296                led_dat = &led[i];
 297                led_cur = &pdata->led[i];
 298
 299                if (led_cur->id > MC13783_LED_MAX || led_cur->id < 0) {
 300                        dev_err(&pdev->dev, "invalid id %d\n", led_cur->id);
 301                        ret = -EINVAL;
 302                        goto err_register;
 303                }
 304
 305                if (init_led & (1 << led_cur->id)) {
 306                        dev_err(&pdev->dev, "led %d already initialized\n",
 307                                        led_cur->id);
 308                        ret = -EINVAL;
 309                        goto err_register;
 310                }
 311
 312                init_led |= 1 << led_cur->id;
 313                led_dat->cdev.name = led_cur->name;
 314                led_dat->cdev.default_trigger = led_cur->default_trigger;
 315                led_dat->cdev.brightness_set = mc13783_led_set;
 316                led_dat->cdev.brightness = LED_OFF;
 317                led_dat->id = led_cur->id;
 318                led_dat->master = dev_get_drvdata(pdev->dev.parent);
 319
 320                INIT_WORK(&led_dat->work, mc13783_led_work);
 321
 322                ret = led_classdev_register(pdev->dev.parent, &led_dat->cdev);
 323                if (ret) {
 324                        dev_err(&pdev->dev, "failed to register led %d\n",
 325                                        led_dat->id);
 326                        goto err_register;
 327                }
 328
 329                ret = mc13783_led_setup(led_dat, led_cur->max_current);
 330                if (ret) {
 331                        dev_err(&pdev->dev, "unable to init led %d\n",
 332                                        led_dat->id);
 333                        i++;
 334                        goto err_register;
 335                }
 336        }
 337
 338        platform_set_drvdata(pdev, led);
 339        return 0;
 340
 341err_register:
 342        for (i = i - 1; i >= 0; i--) {
 343                led_classdev_unregister(&led[i].cdev);
 344                cancel_work_sync(&led[i].work);
 345        }
 346
 347err_free:
 348        kfree(led);
 349        return ret;
 350}
 351
 352static int __devexit mc13783_led_remove(struct platform_device *pdev)
 353{
 354        struct mc13783_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
 355        struct mc13783_led *led = platform_get_drvdata(pdev);
 356        struct mc13783 *dev = dev_get_drvdata(pdev->dev.parent);
 357        int i;
 358
 359        for (i = 0; i < pdata->num_leds; i++) {
 360                led_classdev_unregister(&led[i].cdev);
 361                cancel_work_sync(&led[i].work);
 362        }
 363
 364        mc13783_lock(dev);
 365
 366        mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_0, 0);
 367        mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_1, 0);
 368        mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_2, 0);
 369        mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_3, 0);
 370        mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_4, 0);
 371        mc13783_reg_write(dev, MC13783_REG_LED_CONTROL_5, 0);
 372
 373        mc13783_unlock(dev);
 374
 375        kfree(led);
 376        return 0;
 377}
 378
 379static struct platform_driver mc13783_led_driver = {
 380        .driver = {
 381                .name   = "mc13783-led",
 382                .owner  = THIS_MODULE,
 383        },
 384        .probe          = mc13783_led_probe,
 385        .remove         = __devexit_p(mc13783_led_remove),
 386};
 387
 388static int __init mc13783_led_init(void)
 389{
 390        return platform_driver_register(&mc13783_led_driver);
 391}
 392module_init(mc13783_led_init);
 393
 394static void __exit mc13783_led_exit(void)
 395{
 396        platform_driver_unregister(&mc13783_led_driver);
 397}
 398module_exit(mc13783_led_exit);
 399
 400MODULE_DESCRIPTION("LEDs driver for Freescale MC13783 PMIC");
 401MODULE_AUTHOR("Philippe Retornaz <philippe.retornaz@epfl.ch>");
 402MODULE_LICENSE("GPL");
 403MODULE_ALIAS("platform:mc13783-led");
 404