linux/drivers/leds/leds-lp55xx-common.c
<<
>>
Prefs
   1/*
   2 * LP5521/LP5523/LP55231/LP5562 Common Driver
   3 *
   4 * Copyright 2012 Texas Instruments
   5 *
   6 * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License version 2 as
  10 * published by the Free Software Foundation.
  11 *
  12 * Derived from leds-lp5521.c, leds-lp5523.c
  13 */
  14
  15#include <linux/clk.h>
  16#include <linux/delay.h>
  17#include <linux/firmware.h>
  18#include <linux/i2c.h>
  19#include <linux/leds.h>
  20#include <linux/module.h>
  21#include <linux/platform_data/leds-lp55xx.h>
  22#include <linux/slab.h>
  23
  24#include "leds-lp55xx-common.h"
  25
  26/* External clock rate */
  27#define LP55XX_CLK_32K                  32768
  28
  29static struct lp55xx_led *cdev_to_lp55xx_led(struct led_classdev *cdev)
  30{
  31        return container_of(cdev, struct lp55xx_led, cdev);
  32}
  33
  34static struct lp55xx_led *dev_to_lp55xx_led(struct device *dev)
  35{
  36        return cdev_to_lp55xx_led(dev_get_drvdata(dev));
  37}
  38
  39static void lp55xx_reset_device(struct lp55xx_chip *chip)
  40{
  41        struct lp55xx_device_config *cfg = chip->cfg;
  42        u8 addr = cfg->reset.addr;
  43        u8 val  = cfg->reset.val;
  44
  45        /* no error checking here because no ACK from the device after reset */
  46        lp55xx_write(chip, addr, val);
  47}
  48
  49static int lp55xx_detect_device(struct lp55xx_chip *chip)
  50{
  51        struct lp55xx_device_config *cfg = chip->cfg;
  52        u8 addr = cfg->enable.addr;
  53        u8 val  = cfg->enable.val;
  54        int ret;
  55
  56        ret = lp55xx_write(chip, addr, val);
  57        if (ret)
  58                return ret;
  59
  60        usleep_range(1000, 2000);
  61
  62        ret = lp55xx_read(chip, addr, &val);
  63        if (ret)
  64                return ret;
  65
  66        if (val != cfg->enable.val)
  67                return -ENODEV;
  68
  69        return 0;
  70}
  71
  72static int lp55xx_post_init_device(struct lp55xx_chip *chip)
  73{
  74        struct lp55xx_device_config *cfg = chip->cfg;
  75
  76        if (!cfg->post_init_device)
  77                return 0;
  78
  79        return cfg->post_init_device(chip);
  80}
  81
  82static ssize_t lp55xx_show_current(struct device *dev,
  83                            struct device_attribute *attr,
  84                            char *buf)
  85{
  86        struct lp55xx_led *led = dev_to_lp55xx_led(dev);
  87
  88        return scnprintf(buf, PAGE_SIZE, "%d\n", led->led_current);
  89}
  90
  91static ssize_t lp55xx_store_current(struct device *dev,
  92                             struct device_attribute *attr,
  93                             const char *buf, size_t len)
  94{
  95        struct lp55xx_led *led = dev_to_lp55xx_led(dev);
  96        struct lp55xx_chip *chip = led->chip;
  97        unsigned long curr;
  98
  99        if (kstrtoul(buf, 0, &curr))
 100                return -EINVAL;
 101
 102        if (curr > led->max_current)
 103                return -EINVAL;
 104
 105        if (!chip->cfg->set_led_current)
 106                return len;
 107
 108        mutex_lock(&chip->lock);
 109        chip->cfg->set_led_current(led, (u8)curr);
 110        mutex_unlock(&chip->lock);
 111
 112        return len;
 113}
 114
 115static ssize_t lp55xx_show_max_current(struct device *dev,
 116                            struct device_attribute *attr,
 117                            char *buf)
 118{
 119        struct lp55xx_led *led = dev_to_lp55xx_led(dev);
 120
 121        return scnprintf(buf, PAGE_SIZE, "%d\n", led->max_current);
 122}
 123
 124static DEVICE_ATTR(led_current, S_IRUGO | S_IWUSR, lp55xx_show_current,
 125                lp55xx_store_current);
 126static DEVICE_ATTR(max_current, S_IRUGO , lp55xx_show_max_current, NULL);
 127
 128static struct attribute *lp55xx_led_attributes[] = {
 129        &dev_attr_led_current.attr,
 130        &dev_attr_max_current.attr,
 131        NULL,
 132};
 133
 134static struct attribute_group lp55xx_led_attr_group = {
 135        .attrs = lp55xx_led_attributes
 136};
 137
 138static void lp55xx_set_brightness(struct led_classdev *cdev,
 139                             enum led_brightness brightness)
 140{
 141        struct lp55xx_led *led = cdev_to_lp55xx_led(cdev);
 142
 143        led->brightness = (u8)brightness;
 144        schedule_work(&led->brightness_work);
 145}
 146
 147static int lp55xx_init_led(struct lp55xx_led *led,
 148                        struct lp55xx_chip *chip, int chan)
 149{
 150        struct lp55xx_platform_data *pdata = chip->pdata;
 151        struct lp55xx_device_config *cfg = chip->cfg;
 152        struct device *dev = &chip->cl->dev;
 153        char name[32];
 154        int ret;
 155        int max_channel = cfg->max_channel;
 156
 157        if (chan >= max_channel) {
 158                dev_err(dev, "invalid channel: %d / %d\n", chan, max_channel);
 159                return -EINVAL;
 160        }
 161
 162        if (pdata->led_config[chan].led_current == 0)
 163                return 0;
 164
 165        led->led_current = pdata->led_config[chan].led_current;
 166        led->max_current = pdata->led_config[chan].max_current;
 167        led->chan_nr = pdata->led_config[chan].chan_nr;
 168
 169        if (led->chan_nr >= max_channel) {
 170                dev_err(dev, "Use channel numbers between 0 and %d\n",
 171                        max_channel - 1);
 172                return -EINVAL;
 173        }
 174
 175        led->cdev.brightness_set = lp55xx_set_brightness;
 176
 177        if (pdata->led_config[chan].name) {
 178                led->cdev.name = pdata->led_config[chan].name;
 179        } else {
 180                snprintf(name, sizeof(name), "%s:channel%d",
 181                        pdata->label ? : chip->cl->name, chan);
 182                led->cdev.name = name;
 183        }
 184
 185        /*
 186         * register led class device for each channel and
 187         * add device attributes
 188         */
 189
 190        ret = led_classdev_register(dev, &led->cdev);
 191        if (ret) {
 192                dev_err(dev, "led register err: %d\n", ret);
 193                return ret;
 194        }
 195
 196        ret = sysfs_create_group(&led->cdev.dev->kobj, &lp55xx_led_attr_group);
 197        if (ret) {
 198                dev_err(dev, "led sysfs err: %d\n", ret);
 199                led_classdev_unregister(&led->cdev);
 200                return ret;
 201        }
 202
 203        return 0;
 204}
 205
 206static void lp55xx_firmware_loaded(const struct firmware *fw, void *context)
 207{
 208        struct lp55xx_chip *chip = context;
 209        struct device *dev = &chip->cl->dev;
 210
 211        if (!fw) {
 212                dev_err(dev, "firmware request failed\n");
 213                goto out;
 214        }
 215
 216        /* handling firmware data is chip dependent */
 217        mutex_lock(&chip->lock);
 218
 219        chip->fw = fw;
 220        if (chip->cfg->firmware_cb)
 221                chip->cfg->firmware_cb(chip);
 222
 223        mutex_unlock(&chip->lock);
 224
 225out:
 226        /* firmware should be released for other channel use */
 227        release_firmware(chip->fw);
 228}
 229
 230static int lp55xx_request_firmware(struct lp55xx_chip *chip)
 231{
 232        const char *name = chip->cl->name;
 233        struct device *dev = &chip->cl->dev;
 234
 235        return request_firmware_nowait(THIS_MODULE, true, name, dev,
 236                                GFP_KERNEL, chip, lp55xx_firmware_loaded);
 237}
 238
 239static ssize_t lp55xx_show_engine_select(struct device *dev,
 240                            struct device_attribute *attr,
 241                            char *buf)
 242{
 243        struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
 244        struct lp55xx_chip *chip = led->chip;
 245
 246        return sprintf(buf, "%d\n", chip->engine_idx);
 247}
 248
 249static ssize_t lp55xx_store_engine_select(struct device *dev,
 250                             struct device_attribute *attr,
 251                             const char *buf, size_t len)
 252{
 253        struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
 254        struct lp55xx_chip *chip = led->chip;
 255        unsigned long val;
 256        int ret;
 257
 258        if (kstrtoul(buf, 0, &val))
 259                return -EINVAL;
 260
 261        /* select the engine to be run */
 262
 263        switch (val) {
 264        case LP55XX_ENGINE_1:
 265        case LP55XX_ENGINE_2:
 266        case LP55XX_ENGINE_3:
 267                mutex_lock(&chip->lock);
 268                chip->engine_idx = val;
 269                ret = lp55xx_request_firmware(chip);
 270                mutex_unlock(&chip->lock);
 271                break;
 272        default:
 273                dev_err(dev, "%lu: invalid engine index. (1, 2, 3)\n", val);
 274                return -EINVAL;
 275        }
 276
 277        if (ret) {
 278                dev_err(dev, "request firmware err: %d\n", ret);
 279                return ret;
 280        }
 281
 282        return len;
 283}
 284
 285static inline void lp55xx_run_engine(struct lp55xx_chip *chip, bool start)
 286{
 287        if (chip->cfg->run_engine)
 288                chip->cfg->run_engine(chip, start);
 289}
 290
 291static ssize_t lp55xx_store_engine_run(struct device *dev,
 292                             struct device_attribute *attr,
 293                             const char *buf, size_t len)
 294{
 295        struct lp55xx_led *led = i2c_get_clientdata(to_i2c_client(dev));
 296        struct lp55xx_chip *chip = led->chip;
 297        unsigned long val;
 298
 299        if (kstrtoul(buf, 0, &val))
 300                return -EINVAL;
 301
 302        /* run or stop the selected engine */
 303
 304        if (val <= 0) {
 305                lp55xx_run_engine(chip, false);
 306                return len;
 307        }
 308
 309        mutex_lock(&chip->lock);
 310        lp55xx_run_engine(chip, true);
 311        mutex_unlock(&chip->lock);
 312
 313        return len;
 314}
 315
 316static DEVICE_ATTR(select_engine, S_IRUGO | S_IWUSR,
 317                   lp55xx_show_engine_select, lp55xx_store_engine_select);
 318static DEVICE_ATTR(run_engine, S_IWUSR, NULL, lp55xx_store_engine_run);
 319
 320static struct attribute *lp55xx_engine_attributes[] = {
 321        &dev_attr_select_engine.attr,
 322        &dev_attr_run_engine.attr,
 323        NULL,
 324};
 325
 326static const struct attribute_group lp55xx_engine_attr_group = {
 327        .attrs = lp55xx_engine_attributes,
 328};
 329
 330int lp55xx_write(struct lp55xx_chip *chip, u8 reg, u8 val)
 331{
 332        return i2c_smbus_write_byte_data(chip->cl, reg, val);
 333}
 334EXPORT_SYMBOL_GPL(lp55xx_write);
 335
 336int lp55xx_read(struct lp55xx_chip *chip, u8 reg, u8 *val)
 337{
 338        s32 ret;
 339
 340        ret = i2c_smbus_read_byte_data(chip->cl, reg);
 341        if (ret < 0)
 342                return ret;
 343
 344        *val = ret;
 345        return 0;
 346}
 347EXPORT_SYMBOL_GPL(lp55xx_read);
 348
 349int lp55xx_update_bits(struct lp55xx_chip *chip, u8 reg, u8 mask, u8 val)
 350{
 351        int ret;
 352        u8 tmp;
 353
 354        ret = lp55xx_read(chip, reg, &tmp);
 355        if (ret)
 356                return ret;
 357
 358        tmp &= ~mask;
 359        tmp |= val & mask;
 360
 361        return lp55xx_write(chip, reg, tmp);
 362}
 363EXPORT_SYMBOL_GPL(lp55xx_update_bits);
 364
 365bool lp55xx_is_extclk_used(struct lp55xx_chip *chip)
 366{
 367        struct clk *clk;
 368        int err;
 369
 370        clk = devm_clk_get(&chip->cl->dev, "32k_clk");
 371        if (IS_ERR(clk))
 372                goto use_internal_clk;
 373
 374        err = clk_prepare_enable(clk);
 375        if (err)
 376                goto use_internal_clk;
 377
 378        if (clk_get_rate(clk) != LP55XX_CLK_32K) {
 379                clk_disable_unprepare(clk);
 380                goto use_internal_clk;
 381        }
 382
 383        dev_info(&chip->cl->dev, "%dHz external clock used\n",  LP55XX_CLK_32K);
 384
 385        chip->clk = clk;
 386        return true;
 387
 388use_internal_clk:
 389        dev_info(&chip->cl->dev, "internal clock used\n");
 390        return false;
 391}
 392EXPORT_SYMBOL_GPL(lp55xx_is_extclk_used);
 393
 394int lp55xx_init_device(struct lp55xx_chip *chip)
 395{
 396        struct lp55xx_platform_data *pdata;
 397        struct lp55xx_device_config *cfg;
 398        struct device *dev = &chip->cl->dev;
 399        int ret = 0;
 400
 401        WARN_ON(!chip);
 402
 403        pdata = chip->pdata;
 404        cfg = chip->cfg;
 405
 406        if (!pdata || !cfg)
 407                return -EINVAL;
 408
 409        if (pdata->setup_resources) {
 410                ret = pdata->setup_resources();
 411                if (ret < 0) {
 412                        dev_err(dev, "setup resoure err: %d\n", ret);
 413                        goto err;
 414                }
 415        }
 416
 417        if (pdata->enable) {
 418                pdata->enable(0);
 419                usleep_range(1000, 2000); /* Keep enable down at least 1ms */
 420                pdata->enable(1);
 421                usleep_range(1000, 2000); /* 500us abs min. */
 422        }
 423
 424        lp55xx_reset_device(chip);
 425
 426        /*
 427         * Exact value is not available. 10 - 20ms
 428         * appears to be enough for reset.
 429         */
 430        usleep_range(10000, 20000);
 431
 432        ret = lp55xx_detect_device(chip);
 433        if (ret) {
 434                dev_err(dev, "device detection err: %d\n", ret);
 435                goto err;
 436        }
 437
 438        /* chip specific initialization */
 439        ret = lp55xx_post_init_device(chip);
 440        if (ret) {
 441                dev_err(dev, "post init device err: %d\n", ret);
 442                goto err_post_init;
 443        }
 444
 445        return 0;
 446
 447err_post_init:
 448        lp55xx_deinit_device(chip);
 449err:
 450        return ret;
 451}
 452EXPORT_SYMBOL_GPL(lp55xx_init_device);
 453
 454void lp55xx_deinit_device(struct lp55xx_chip *chip)
 455{
 456        struct lp55xx_platform_data *pdata = chip->pdata;
 457
 458        if (chip->clk)
 459                clk_disable_unprepare(chip->clk);
 460
 461        if (pdata->enable)
 462                pdata->enable(0);
 463
 464        if (pdata->release_resources)
 465                pdata->release_resources();
 466}
 467EXPORT_SYMBOL_GPL(lp55xx_deinit_device);
 468
 469int lp55xx_register_leds(struct lp55xx_led *led, struct lp55xx_chip *chip)
 470{
 471        struct lp55xx_platform_data *pdata = chip->pdata;
 472        struct lp55xx_device_config *cfg = chip->cfg;
 473        int num_channels = pdata->num_channels;
 474        struct lp55xx_led *each;
 475        u8 led_current;
 476        int ret;
 477        int i;
 478
 479        if (!cfg->brightness_work_fn) {
 480                dev_err(&chip->cl->dev, "empty brightness configuration\n");
 481                return -EINVAL;
 482        }
 483
 484        for (i = 0; i < num_channels; i++) {
 485
 486                /* do not initialize channels that are not connected */
 487                if (pdata->led_config[i].led_current == 0)
 488                        continue;
 489
 490                led_current = pdata->led_config[i].led_current;
 491                each = led + i;
 492                ret = lp55xx_init_led(each, chip, i);
 493                if (ret)
 494                        goto err_init_led;
 495
 496                INIT_WORK(&each->brightness_work, cfg->brightness_work_fn);
 497
 498                chip->num_leds++;
 499                each->chip = chip;
 500
 501                /* setting led current at each channel */
 502                if (cfg->set_led_current)
 503                        cfg->set_led_current(each, led_current);
 504        }
 505
 506        return 0;
 507
 508err_init_led:
 509        lp55xx_unregister_leds(led, chip);
 510        return ret;
 511}
 512EXPORT_SYMBOL_GPL(lp55xx_register_leds);
 513
 514void lp55xx_unregister_leds(struct lp55xx_led *led, struct lp55xx_chip *chip)
 515{
 516        int i;
 517        struct lp55xx_led *each;
 518
 519        for (i = 0; i < chip->num_leds; i++) {
 520                each = led + i;
 521                led_classdev_unregister(&each->cdev);
 522                flush_work(&each->brightness_work);
 523        }
 524}
 525EXPORT_SYMBOL_GPL(lp55xx_unregister_leds);
 526
 527int lp55xx_register_sysfs(struct lp55xx_chip *chip)
 528{
 529        struct device *dev = &chip->cl->dev;
 530        struct lp55xx_device_config *cfg = chip->cfg;
 531        int ret;
 532
 533        if (!cfg->run_engine || !cfg->firmware_cb)
 534                goto dev_specific_attrs;
 535
 536        ret = sysfs_create_group(&dev->kobj, &lp55xx_engine_attr_group);
 537        if (ret)
 538                return ret;
 539
 540dev_specific_attrs:
 541        return cfg->dev_attr_group ?
 542                sysfs_create_group(&dev->kobj, cfg->dev_attr_group) : 0;
 543}
 544EXPORT_SYMBOL_GPL(lp55xx_register_sysfs);
 545
 546void lp55xx_unregister_sysfs(struct lp55xx_chip *chip)
 547{
 548        struct device *dev = &chip->cl->dev;
 549        struct lp55xx_device_config *cfg = chip->cfg;
 550
 551        if (cfg->dev_attr_group)
 552                sysfs_remove_group(&dev->kobj, cfg->dev_attr_group);
 553
 554        sysfs_remove_group(&dev->kobj, &lp55xx_engine_attr_group);
 555}
 556EXPORT_SYMBOL_GPL(lp55xx_unregister_sysfs);
 557
 558int lp55xx_of_populate_pdata(struct device *dev, struct device_node *np)
 559{
 560        struct device_node *child;
 561        struct lp55xx_platform_data *pdata;
 562        struct lp55xx_led_config *cfg;
 563        int num_channels;
 564        int i = 0;
 565
 566        pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
 567        if (!pdata)
 568                return -ENOMEM;
 569
 570        num_channels = of_get_child_count(np);
 571        if (num_channels == 0) {
 572                dev_err(dev, "no LED channels\n");
 573                return -EINVAL;
 574        }
 575
 576        cfg = devm_kzalloc(dev, sizeof(*cfg) * num_channels, GFP_KERNEL);
 577        if (!cfg)
 578                return -ENOMEM;
 579
 580        pdata->led_config = &cfg[0];
 581        pdata->num_channels = num_channels;
 582
 583        for_each_child_of_node(np, child) {
 584                cfg[i].chan_nr = i;
 585
 586                of_property_read_string(child, "chan-name", &cfg[i].name);
 587                of_property_read_u8(child, "led-cur", &cfg[i].led_current);
 588                of_property_read_u8(child, "max-cur", &cfg[i].max_current);
 589
 590                i++;
 591        }
 592
 593        of_property_read_string(np, "label", &pdata->label);
 594        of_property_read_u8(np, "clock-mode", &pdata->clock_mode);
 595
 596        dev->platform_data = pdata;
 597
 598        return 0;
 599}
 600EXPORT_SYMBOL_GPL(lp55xx_of_populate_pdata);
 601
 602MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>");
 603MODULE_DESCRIPTION("LP55xx Common Driver");
 604MODULE_LICENSE("GPL");
 605
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.