linux/drivers/leds/led-class-flash.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * LED Flash class interface
   4 *
   5 * Copyright (C) 2015 Samsung Electronics Co., Ltd.
   6 * Author: Jacek Anaszewski <j.anaszewski@samsung.com>
   7 */
   8
   9#include <linux/device.h>
  10#include <linux/init.h>
  11#include <linux/led-class-flash.h>
  12#include <linux/leds.h>
  13#include <linux/module.h>
  14#include <linux/slab.h>
  15#include "leds.h"
  16
  17#define has_flash_op(fled_cdev, op)                             \
  18        (fled_cdev && fled_cdev->ops->op)
  19
  20#define call_flash_op(fled_cdev, op, args...)           \
  21        ((has_flash_op(fled_cdev, op)) ?                        \
  22                        (fled_cdev->ops->op(fled_cdev, args)) : \
  23                        -EINVAL)
  24
  25static const char * const led_flash_fault_names[] = {
  26        "led-over-voltage",
  27        "flash-timeout-exceeded",
  28        "controller-over-temperature",
  29        "controller-short-circuit",
  30        "led-power-supply-over-current",
  31        "indicator-led-fault",
  32        "led-under-voltage",
  33        "controller-under-voltage",
  34        "led-over-temperature",
  35};
  36
  37static ssize_t flash_brightness_store(struct device *dev,
  38                struct device_attribute *attr, const char *buf, size_t size)
  39{
  40        struct led_classdev *led_cdev = dev_get_drvdata(dev);
  41        struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
  42        unsigned long state;
  43        ssize_t ret;
  44
  45        mutex_lock(&led_cdev->led_access);
  46
  47        if (led_sysfs_is_disabled(led_cdev)) {
  48                ret = -EBUSY;
  49                goto unlock;
  50        }
  51
  52        ret = kstrtoul(buf, 10, &state);
  53        if (ret)
  54                goto unlock;
  55
  56        ret = led_set_flash_brightness(fled_cdev, state);
  57        if (ret < 0)
  58                goto unlock;
  59
  60        ret = size;
  61unlock:
  62        mutex_unlock(&led_cdev->led_access);
  63        return ret;
  64}
  65
  66static ssize_t flash_brightness_show(struct device *dev,
  67                struct device_attribute *attr, char *buf)
  68{
  69        struct led_classdev *led_cdev = dev_get_drvdata(dev);
  70        struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
  71
  72        /* no lock needed for this */
  73        led_update_flash_brightness(fled_cdev);
  74
  75        return sprintf(buf, "%u\n", fled_cdev->brightness.val);
  76}
  77static DEVICE_ATTR_RW(flash_brightness);
  78
  79static ssize_t max_flash_brightness_show(struct device *dev,
  80                struct device_attribute *attr, char *buf)
  81{
  82        struct led_classdev *led_cdev = dev_get_drvdata(dev);
  83        struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
  84
  85        return sprintf(buf, "%u\n", fled_cdev->brightness.max);
  86}
  87static DEVICE_ATTR_RO(max_flash_brightness);
  88
  89static ssize_t flash_strobe_store(struct device *dev,
  90                struct device_attribute *attr, const char *buf, size_t size)
  91{
  92        struct led_classdev *led_cdev = dev_get_drvdata(dev);
  93        struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
  94        unsigned long state;
  95        ssize_t ret = -EINVAL;
  96
  97        mutex_lock(&led_cdev->led_access);
  98
  99        if (led_sysfs_is_disabled(led_cdev)) {
 100                ret = -EBUSY;
 101                goto unlock;
 102        }
 103
 104        ret = kstrtoul(buf, 10, &state);
 105        if (ret)
 106                goto unlock;
 107
 108        if (state > 1) {
 109                ret = -EINVAL;
 110                goto unlock;
 111        }
 112
 113        ret = led_set_flash_strobe(fled_cdev, state);
 114        if (ret < 0)
 115                goto unlock;
 116        ret = size;
 117unlock:
 118        mutex_unlock(&led_cdev->led_access);
 119        return ret;
 120}
 121
 122static ssize_t flash_strobe_show(struct device *dev,
 123                struct device_attribute *attr, char *buf)
 124{
 125        struct led_classdev *led_cdev = dev_get_drvdata(dev);
 126        struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
 127        bool state;
 128        int ret;
 129
 130        /* no lock needed for this */
 131        ret = led_get_flash_strobe(fled_cdev, &state);
 132        if (ret < 0)
 133                return ret;
 134
 135        return sprintf(buf, "%u\n", state);
 136}
 137static DEVICE_ATTR_RW(flash_strobe);
 138
 139static ssize_t flash_timeout_store(struct device *dev,
 140                struct device_attribute *attr, const char *buf, size_t size)
 141{
 142        struct led_classdev *led_cdev = dev_get_drvdata(dev);
 143        struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
 144        unsigned long flash_timeout;
 145        ssize_t ret;
 146
 147        mutex_lock(&led_cdev->led_access);
 148
 149        if (led_sysfs_is_disabled(led_cdev)) {
 150                ret = -EBUSY;
 151                goto unlock;
 152        }
 153
 154        ret = kstrtoul(buf, 10, &flash_timeout);
 155        if (ret)
 156                goto unlock;
 157
 158        ret = led_set_flash_timeout(fled_cdev, flash_timeout);
 159        if (ret < 0)
 160                goto unlock;
 161
 162        ret = size;
 163unlock:
 164        mutex_unlock(&led_cdev->led_access);
 165        return ret;
 166}
 167
 168static ssize_t flash_timeout_show(struct device *dev,
 169                struct device_attribute *attr, char *buf)
 170{
 171        struct led_classdev *led_cdev = dev_get_drvdata(dev);
 172        struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
 173
 174        return sprintf(buf, "%u\n", fled_cdev->timeout.val);
 175}
 176static DEVICE_ATTR_RW(flash_timeout);
 177
 178static ssize_t max_flash_timeout_show(struct device *dev,
 179                struct device_attribute *attr, char *buf)
 180{
 181        struct led_classdev *led_cdev = dev_get_drvdata(dev);
 182        struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
 183
 184        return sprintf(buf, "%u\n", fled_cdev->timeout.max);
 185}
 186static DEVICE_ATTR_RO(max_flash_timeout);
 187
 188static ssize_t flash_fault_show(struct device *dev,
 189                struct device_attribute *attr, char *buf)
 190{
 191        struct led_classdev *led_cdev = dev_get_drvdata(dev);
 192        struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
 193        u32 fault, mask = 0x1;
 194        char *pbuf = buf;
 195        int i, ret, buf_len;
 196
 197        ret = led_get_flash_fault(fled_cdev, &fault);
 198        if (ret < 0)
 199                return -EINVAL;
 200
 201        *buf = '\0';
 202
 203        for (i = 0; i < LED_NUM_FLASH_FAULTS; ++i) {
 204                if (fault & mask) {
 205                        buf_len = sprintf(pbuf, "%s ",
 206                                          led_flash_fault_names[i]);
 207                        pbuf += buf_len;
 208                }
 209                mask <<= 1;
 210        }
 211
 212        return sprintf(buf, "%s\n", buf);
 213}
 214static DEVICE_ATTR_RO(flash_fault);
 215
 216static struct attribute *led_flash_strobe_attrs[] = {
 217        &dev_attr_flash_strobe.attr,
 218        NULL,
 219};
 220
 221static struct attribute *led_flash_timeout_attrs[] = {
 222        &dev_attr_flash_timeout.attr,
 223        &dev_attr_max_flash_timeout.attr,
 224        NULL,
 225};
 226
 227static struct attribute *led_flash_brightness_attrs[] = {
 228        &dev_attr_flash_brightness.attr,
 229        &dev_attr_max_flash_brightness.attr,
 230        NULL,
 231};
 232
 233static struct attribute *led_flash_fault_attrs[] = {
 234        &dev_attr_flash_fault.attr,
 235        NULL,
 236};
 237
 238static const struct attribute_group led_flash_strobe_group = {
 239        .attrs = led_flash_strobe_attrs,
 240};
 241
 242static const struct attribute_group led_flash_timeout_group = {
 243        .attrs = led_flash_timeout_attrs,
 244};
 245
 246static const struct attribute_group led_flash_brightness_group = {
 247        .attrs = led_flash_brightness_attrs,
 248};
 249
 250static const struct attribute_group led_flash_fault_group = {
 251        .attrs = led_flash_fault_attrs,
 252};
 253
 254static void led_flash_resume(struct led_classdev *led_cdev)
 255{
 256        struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
 257
 258        call_flash_op(fled_cdev, flash_brightness_set,
 259                                        fled_cdev->brightness.val);
 260        call_flash_op(fled_cdev, timeout_set, fled_cdev->timeout.val);
 261}
 262
 263static void led_flash_init_sysfs_groups(struct led_classdev_flash *fled_cdev)
 264{
 265        struct led_classdev *led_cdev = &fled_cdev->led_cdev;
 266        const struct led_flash_ops *ops = fled_cdev->ops;
 267        const struct attribute_group **flash_groups = fled_cdev->sysfs_groups;
 268
 269        int num_sysfs_groups = 0;
 270
 271        flash_groups[num_sysfs_groups++] = &led_flash_strobe_group;
 272
 273        if (ops->flash_brightness_set)
 274                flash_groups[num_sysfs_groups++] = &led_flash_brightness_group;
 275
 276        if (ops->timeout_set)
 277                flash_groups[num_sysfs_groups++] = &led_flash_timeout_group;
 278
 279        if (ops->fault_get)
 280                flash_groups[num_sysfs_groups++] = &led_flash_fault_group;
 281
 282        led_cdev->groups = flash_groups;
 283}
 284
 285int led_classdev_flash_register_ext(struct device *parent,
 286                                    struct led_classdev_flash *fled_cdev,
 287                                    struct led_init_data *init_data)
 288{
 289        struct led_classdev *led_cdev;
 290        const struct led_flash_ops *ops;
 291        int ret;
 292
 293        if (!fled_cdev)
 294                return -EINVAL;
 295
 296        led_cdev = &fled_cdev->led_cdev;
 297
 298        if (led_cdev->flags & LED_DEV_CAP_FLASH) {
 299                if (!led_cdev->brightness_set_blocking)
 300                        return -EINVAL;
 301
 302                ops = fled_cdev->ops;
 303                if (!ops || !ops->strobe_set)
 304                        return -EINVAL;
 305
 306                led_cdev->flash_resume = led_flash_resume;
 307
 308                /* Select the sysfs attributes to be created for the device */
 309                led_flash_init_sysfs_groups(fled_cdev);
 310        }
 311
 312        /* Register led class device */
 313        ret = led_classdev_register_ext(parent, led_cdev, init_data);
 314        if (ret < 0)
 315                return ret;
 316
 317        return 0;
 318}
 319EXPORT_SYMBOL_GPL(led_classdev_flash_register_ext);
 320
 321void led_classdev_flash_unregister(struct led_classdev_flash *fled_cdev)
 322{
 323        if (!fled_cdev)
 324                return;
 325
 326        led_classdev_unregister(&fled_cdev->led_cdev);
 327}
 328EXPORT_SYMBOL_GPL(led_classdev_flash_unregister);
 329
 330static void devm_led_classdev_flash_release(struct device *dev, void *res)
 331{
 332        led_classdev_flash_unregister(*(struct led_classdev_flash **)res);
 333}
 334
 335int devm_led_classdev_flash_register_ext(struct device *parent,
 336                                     struct led_classdev_flash *fled_cdev,
 337                                     struct led_init_data *init_data)
 338{
 339        struct led_classdev_flash **dr;
 340        int ret;
 341
 342        dr = devres_alloc(devm_led_classdev_flash_release, sizeof(*dr),
 343                          GFP_KERNEL);
 344        if (!dr)
 345                return -ENOMEM;
 346
 347        ret = led_classdev_flash_register_ext(parent, fled_cdev, init_data);
 348        if (ret) {
 349                devres_free(dr);
 350                return ret;
 351        }
 352
 353        *dr = fled_cdev;
 354        devres_add(parent, dr);
 355
 356        return 0;
 357}
 358EXPORT_SYMBOL_GPL(devm_led_classdev_flash_register_ext);
 359
 360static int devm_led_classdev_flash_match(struct device *dev,
 361                                              void *res, void *data)
 362{
 363        struct led_classdev_flash **p = res;
 364
 365        if (WARN_ON(!p || !*p))
 366                return 0;
 367
 368        return *p == data;
 369}
 370
 371void devm_led_classdev_flash_unregister(struct device *dev,
 372                                        struct led_classdev_flash *fled_cdev)
 373{
 374        WARN_ON(devres_release(dev,
 375                               devm_led_classdev_flash_release,
 376                               devm_led_classdev_flash_match, fled_cdev));
 377}
 378EXPORT_SYMBOL_GPL(devm_led_classdev_flash_unregister);
 379
 380static void led_clamp_align(struct led_flash_setting *s)
 381{
 382        u32 v, offset;
 383
 384        v = s->val + s->step / 2;
 385        v = clamp(v, s->min, s->max);
 386        offset = v - s->min;
 387        offset = s->step * (offset / s->step);
 388        s->val = s->min + offset;
 389}
 390
 391int led_set_flash_timeout(struct led_classdev_flash *fled_cdev, u32 timeout)
 392{
 393        struct led_classdev *led_cdev = &fled_cdev->led_cdev;
 394        struct led_flash_setting *s = &fled_cdev->timeout;
 395
 396        s->val = timeout;
 397        led_clamp_align(s);
 398
 399        if (!(led_cdev->flags & LED_SUSPENDED))
 400                return call_flash_op(fled_cdev, timeout_set, s->val);
 401
 402        return 0;
 403}
 404EXPORT_SYMBOL_GPL(led_set_flash_timeout);
 405
 406int led_get_flash_fault(struct led_classdev_flash *fled_cdev, u32 *fault)
 407{
 408        return call_flash_op(fled_cdev, fault_get, fault);
 409}
 410EXPORT_SYMBOL_GPL(led_get_flash_fault);
 411
 412int led_set_flash_brightness(struct led_classdev_flash *fled_cdev,
 413                                u32 brightness)
 414{
 415        struct led_classdev *led_cdev = &fled_cdev->led_cdev;
 416        struct led_flash_setting *s = &fled_cdev->brightness;
 417
 418        s->val = brightness;
 419        led_clamp_align(s);
 420
 421        if (!(led_cdev->flags & LED_SUSPENDED))
 422                return call_flash_op(fled_cdev, flash_brightness_set, s->val);
 423
 424        return 0;
 425}
 426EXPORT_SYMBOL_GPL(led_set_flash_brightness);
 427
 428int led_update_flash_brightness(struct led_classdev_flash *fled_cdev)
 429{
 430        struct led_flash_setting *s = &fled_cdev->brightness;
 431        u32 brightness;
 432
 433        if (has_flash_op(fled_cdev, flash_brightness_get)) {
 434                int ret = call_flash_op(fled_cdev, flash_brightness_get,
 435                                                &brightness);
 436                if (ret < 0)
 437                        return ret;
 438
 439                s->val = brightness;
 440        }
 441
 442        return 0;
 443}
 444EXPORT_SYMBOL_GPL(led_update_flash_brightness);
 445
 446MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>");
 447MODULE_DESCRIPTION("LED Flash class interface");
 448MODULE_LICENSE("GPL v2");
 449