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