linux/drivers/leds/leds-mc13783.c
<<
>>
Prefs
   1/*
   2 * LEDs driver for Freescale MC13783
   3 *
   4 * Copyright (C) 2010 Philippe R├ętornaz
   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/mc13xxx.h>
  25#include <linux/slab.h>
  26
  27struct mc13783_led {
  28        struct led_classdev     cdev;
  29        struct work_struct      work;
  30        struct mc13xxx          *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        mc13xxx_lock(led->master);
 115
 116        mc13xxx_reg_rmw(led->master, reg, mask, value);
 117
 118        mc13xxx_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        mc13xxx_lock(led->master);
 176
 177        ret = mc13xxx_reg_rmw(led->master, reg, mask << shift,
 178                                                value << shift);
 179
 180        mc13xxx_unlock(led->master);
 181        return ret;
 182}
 183
 184static int __devinit mc13783_leds_prepare(struct platform_device *pdev)
 185{
 186        struct mc13xxx_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
 187        struct mc13xxx *dev = dev_get_drvdata(pdev->dev.parent);
 188        int ret = 0;
 189        int reg = 0;
 190
 191        mc13xxx_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 = mc13xxx_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 = mc13xxx_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 = mc13xxx_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 = mc13xxx_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 = mc13xxx_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 = mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_0, reg);
 259
 260out:
 261        mc13xxx_unlock(dev);
 262        return ret;
 263}
 264
 265static int __devinit mc13783_led_probe(struct platform_device *pdev)
 266{
 267        struct mc13xxx_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
 268        struct mc13xxx_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 + 1)) {
 279                dev_err(&pdev->dev, "Invalid led count %d\n", pdata->num_leds);
 280                return -EINVAL;
 281        }
 282
 283        led = devm_kzalloc(&pdev->dev, pdata->num_leds * sizeof(*led),
 284                                GFP_KERNEL);
 285        if (led == NULL) {
 286                dev_err(&pdev->dev, "failed to alloc memory\n");
 287                return -ENOMEM;
 288        }
 289
 290        ret = mc13783_leds_prepare(pdev);
 291        if (ret) {
 292                dev_err(&pdev->dev, "unable to init led driver\n");
 293                return ret;
 294        }
 295
 296        for (i = 0; i < pdata->num_leds; i++) {
 297                led_dat = &led[i];
 298                led_cur = &pdata->led[i];
 299
 300                if (led_cur->id > MC13783_LED_MAX || led_cur->id < 0) {
 301                        dev_err(&pdev->dev, "invalid id %d\n", led_cur->id);
 302                        ret = -EINVAL;
 303                        goto err_register;
 304                }
 305
 306                if (init_led & (1 << led_cur->id)) {
 307                        dev_err(&pdev->dev, "led %d already initialized\n",
 308                                        led_cur->id);
 309                        ret = -EINVAL;
 310                        goto err_register;
 311                }
 312
 313                init_led |= 1 << led_cur->id;
 314                led_dat->cdev.name = led_cur->name;
 315                led_dat->cdev.default_trigger = led_cur->default_trigger;
 316                led_dat->cdev.brightness_set = mc13783_led_set;
 317                led_dat->cdev.brightness = LED_OFF;
 318                led_dat->id = led_cur->id;
 319                led_dat->master = dev_get_drvdata(pdev->dev.parent);
 320
 321                INIT_WORK(&led_dat->work, mc13783_led_work);
 322
 323                ret = led_classdev_register(pdev->dev.parent, &led_dat->cdev);
 324                if (ret) {
 325                        dev_err(&pdev->dev, "failed to register led %d\n",
 326                                        led_dat->id);
 327                        goto err_register;
 328                }
 329
 330                ret = mc13783_led_setup(led_dat, led_cur->max_current);
 331                if (ret) {
 332                        dev_err(&pdev->dev, "unable to init led %d\n",
 333                                        led_dat->id);
 334                        i++;
 335                        goto err_register;
 336                }
 337        }
 338
 339        platform_set_drvdata(pdev, led);
 340        return 0;
 341
 342err_register:
 343        for (i = i - 1; i >= 0; i--) {
 344                led_classdev_unregister(&led[i].cdev);
 345                cancel_work_sync(&led[i].work);
 346        }
 347
 348        return ret;
 349}
 350
 351static int __devexit mc13783_led_remove(struct platform_device *pdev)
 352{
 353        struct mc13xxx_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
 354        struct mc13783_led *led = platform_get_drvdata(pdev);
 355        struct mc13xxx *dev = dev_get_drvdata(pdev->dev.parent);
 356        int i;
 357
 358        for (i = 0; i < pdata->num_leds; i++) {
 359                led_classdev_unregister(&led[i].cdev);
 360                cancel_work_sync(&led[i].work);
 361        }
 362
 363        mc13xxx_lock(dev);
 364
 365        mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_0, 0);
 366        mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_1, 0);
 367        mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_2, 0);
 368        mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_3, 0);
 369        mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_4, 0);
 370        mc13xxx_reg_write(dev, MC13783_REG_LED_CONTROL_5, 0);
 371
 372        mc13xxx_unlock(dev);
 373
 374        platform_set_drvdata(pdev, NULL);
 375        return 0;
 376}
 377
 378static struct platform_driver mc13783_led_driver = {
 379        .driver = {
 380                .name   = "mc13783-led",
 381                .owner  = THIS_MODULE,
 382        },
 383        .probe          = mc13783_led_probe,
 384        .remove         = __devexit_p(mc13783_led_remove),
 385};
 386
 387module_platform_driver(mc13783_led_driver);
 388
 389MODULE_DESCRIPTION("LEDs driver for Freescale MC13783 PMIC");
 390MODULE_AUTHOR("Philippe Retornaz <philippe.retornaz@epfl.ch>");
 391MODULE_LICENSE("GPL");
 392MODULE_ALIAS("platform:mc13783-led");
 393
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.