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 = -EBUSY;
  96
  97        mutex_lock(&led_cdev->led_access);
  98
  99        if (led_sysfs_is_disabled(led_cdev))
 100                goto unlock;
 101
 102        ret = kstrtoul(buf, 10, &state);
 103        if (ret)
 104                goto unlock;
 105
 106        if (state > 1) {
 107                ret = -EINVAL;
 108                goto unlock;
 109        }
 110
 111        ret = led_set_flash_strobe(fled_cdev, state);
 112        if (ret < 0)
 113                goto unlock;
 114        ret = size;
 115unlock:
 116        mutex_unlock(&led_cdev->led_access);
 117        return ret;
 118}
 119
 120static ssize_t flash_strobe_show(struct device *dev,
 121                struct device_attribute *attr, char *buf)
 122{
 123        struct led_classdev *led_cdev = dev_get_drvdata(dev);
 124        struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
 125        bool state;
 126        int ret;
 127
 128        /* no lock needed for this */
 129        ret = led_get_flash_strobe(fled_cdev, &state);
 130        if (ret < 0)
 131                return ret;
 132
 133        return sprintf(buf, "%u\n", state);
 134}
 135static DEVICE_ATTR_RW(flash_strobe);
 136
 137static ssize_t flash_timeout_store(struct device *dev,
 138                struct device_attribute *attr, const char *buf, size_t size)
 139{
 140        struct led_classdev *led_cdev = dev_get_drvdata(dev);
 141        struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
 142        unsigned long flash_timeout;
 143        ssize_t ret;
 144
 145        mutex_lock(&led_cdev->led_access);
 146
 147        if (led_sysfs_is_disabled(led_cdev)) {
 148                ret = -EBUSY;
 149                goto unlock;
 150        }
 151
 152        ret = kstrtoul(buf, 10, &flash_timeout);
 153        if (ret)
 154                goto unlock;
 155
 156        ret = led_set_flash_timeout(fled_cdev, flash_timeout);
 157        if (ret < 0)
 158                goto unlock;
 159
 160        ret = size;
 161unlock:
 162        mutex_unlock(&led_cdev->led_access);
 163        return ret;
 164}
 165
 166static ssize_t flash_timeout_show(struct device *dev,
 167                struct device_attribute *attr, char *buf)
 168{
 169        struct led_classdev *led_cdev = dev_get_drvdata(dev);
 170        struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
 171
 172        return sprintf(buf, "%u\n", fled_cdev->timeout.val);
 173}
 174static DEVICE_ATTR_RW(flash_timeout);
 175
 176static ssize_t max_flash_timeout_show(struct device *dev,
 177                struct device_attribute *attr, char *buf)
 178{
 179        struct led_classdev *led_cdev = dev_get_drvdata(dev);
 180        struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
 181
 182        return sprintf(buf, "%u\n", fled_cdev->timeout.max);
 183}
 184static DEVICE_ATTR_RO(max_flash_timeout);
 185
 186static ssize_t flash_fault_show(struct device *dev,
 187                struct device_attribute *attr, char *buf)
 188{
 189        struct led_classdev *led_cdev = dev_get_drvdata(dev);
 190        struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
 191        u32 fault, mask = 0x1;
 192        char *pbuf = buf;
 193        int i, ret, buf_len;
 194
 195        ret = led_get_flash_fault(fled_cdev, &fault);
 196        if (ret < 0)
 197                return -EINVAL;
 198
 199        *buf = '\0';
 200
 201        for (i = 0; i < LED_NUM_FLASH_FAULTS; ++i) {
 202                if (fault & mask) {
 203                        buf_len = sprintf(pbuf, "%s ",
 204                                          led_flash_fault_names[i]);
 205                        pbuf += buf_len;
 206                }
 207                mask <<= 1;
 208        }
 209
 210        return sprintf(buf, "%s\n", buf);
 211}
 212static DEVICE_ATTR_RO(flash_fault);
 213
 214static struct attribute *led_flash_strobe_attrs[] = {
 215        &dev_attr_flash_strobe.attr,
 216        NULL,
 217};
 218
 219static struct attribute *led_flash_timeout_attrs[] = {
 220        &dev_attr_flash_timeout.attr,
 221        &dev_attr_max_flash_timeout.attr,
 222        NULL,
 223};
 224
 225static struct attribute *led_flash_brightness_attrs[] = {
 226        &dev_attr_flash_brightness.attr,
 227        &dev_attr_max_flash_brightness.attr,
 228        NULL,
 229};
 230
 231static struct attribute *led_flash_fault_attrs[] = {
 232        &dev_attr_flash_fault.attr,
 233        NULL,
 234};
 235
 236static const struct attribute_group led_flash_strobe_group = {
 237        .attrs = led_flash_strobe_attrs,
 238};
 239
 240static const struct attribute_group led_flash_timeout_group = {
 241        .attrs = led_flash_timeout_attrs,
 242};
 243
 244static const struct attribute_group led_flash_brightness_group = {
 245        .attrs = led_flash_brightness_attrs,
 246};
 247
 248static const struct attribute_group led_flash_fault_group = {
 249        .attrs = led_flash_fault_attrs,
 250};
 251
 252static void led_flash_resume(struct led_classdev *led_cdev)
 253{
 254        struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
 255
 256        call_flash_op(fled_cdev, flash_brightness_set,
 257                                        fled_cdev->brightness.val);
 258        call_flash_op(fled_cdev, timeout_set, fled_cdev->timeout.val);
 259}
 260
 261static void led_flash_init_sysfs_groups(struct led_classdev_flash *fled_cdev)
 262{
 263        struct led_classdev *led_cdev = &fled_cdev->led_cdev;
 264        const struct led_flash_ops *ops = fled_cdev->ops;
 265        const struct attribute_group **flash_groups = fled_cdev->sysfs_groups;
 266
 267        int num_sysfs_groups = 0;
 268
 269        flash_groups[num_sysfs_groups++] = &led_flash_strobe_group;
 270
 271        if (ops->flash_brightness_set)
 272                flash_groups[num_sysfs_groups++] = &led_flash_brightness_group;
 273
 274        if (ops->timeout_set)
 275                flash_groups[num_sysfs_groups++] = &led_flash_timeout_group;
 276
 277        if (ops->fault_get)
 278                flash_groups[num_sysfs_groups++] = &led_flash_fault_group;
 279
 280        led_cdev->groups = flash_groups;
 281}
 282
 283int led_classdev_flash_register_ext(struct device *parent,
 284                                    struct led_classdev_flash *fled_cdev,
 285                                    struct led_init_data *init_data)
 286{
 287        struct led_classdev *led_cdev;
 288        const struct led_flash_ops *ops;
 289        int ret;
 290
 291        if (!fled_cdev)
 292                return -EINVAL;
 293
 294        led_cdev = &fled_cdev->led_cdev;
 295
 296        if (led_cdev->flags & LED_DEV_CAP_FLASH) {
 297                if (!led_cdev->brightness_set_blocking)
 298                        return -EINVAL;
 299
 300                ops = fled_cdev->ops;
 301                if (!ops || !ops->strobe_set)
 302                        return -EINVAL;
 303
 304                led_cdev->flash_resume = led_flash_resume;
 305
 306                /* Select the sysfs attributes to be created for the device */
 307                led_flash_init_sysfs_groups(fled_cdev);
 308        }
 309
 310        /* Register led class device */
 311        ret = led_classdev_register_ext(parent, led_cdev, init_data);
 312        if (ret < 0)
 313                return ret;
 314
 315        return 0;
 316}
 317EXPORT_SYMBOL_GPL(led_classdev_flash_register_ext);
 318
 319void led_classdev_flash_unregister(struct led_classdev_flash *fled_cdev)
 320{
 321        if (!fled_cdev)
 322                return;
 323
 324        led_classdev_unregister(&fled_cdev->led_cdev);
 325}
 326EXPORT_SYMBOL_GPL(led_classdev_flash_unregister);
 327
 328static void devm_led_classdev_flash_release(struct device *dev, void *res)
 329{
 330        led_classdev_flash_unregister(*(struct led_classdev_flash **)res);
 331}
 332
 333int devm_led_classdev_flash_register_ext(struct device *parent,
 334                                     struct led_classdev_flash *fled_cdev,
 335                                     struct led_init_data *init_data)
 336{
 337        struct led_classdev_flash **dr;
 338        int ret;
 339
 340        dr = devres_alloc(devm_led_classdev_flash_release, sizeof(*dr),
 341                          GFP_KERNEL);
 342        if (!dr)
 343                return -ENOMEM;
 344
 345        ret = led_classdev_flash_register_ext(parent, fled_cdev, init_data);
 346        if (ret) {
 347                devres_free(dr);
 348                return ret;
 349        }
 350
 351        *dr = fled_cdev;
 352        devres_add(parent, dr);
 353
 354        return 0;
 355}
 356EXPORT_SYMBOL_GPL(devm_led_classdev_flash_register_ext);
 357
 358static int devm_led_classdev_flash_match(struct device *dev,
 359                                              void *res, void *data)
 360{
 361        struct led_classdev_flash **p = res;
 362
 363        if (WARN_ON(!p || !*p))
 364                return 0;
 365
 366        return *p == data;
 367}
 368
 369void devm_led_classdev_flash_unregister(struct device *dev,
 370                                        struct led_classdev_flash *fled_cdev)
 371{
 372        WARN_ON(devres_release(dev,
 373                               devm_led_classdev_flash_release,
 374                               devm_led_classdev_flash_match, fled_cdev));
 375}
 376EXPORT_SYMBOL_GPL(devm_led_classdev_flash_unregister);
 377
 378static void led_clamp_align(struct led_flash_setting *s)
 379{
 380        u32 v, offset;
 381
 382        v = s->val + s->step / 2;
 383        v = clamp(v, s->min, s->max);
 384        offset = v - s->min;
 385        offset = s->step * (offset / s->step);
 386        s->val = s->min + offset;
 387}
 388
 389int led_set_flash_timeout(struct led_classdev_flash *fled_cdev, u32 timeout)
 390{
 391        struct led_classdev *led_cdev = &fled_cdev->led_cdev;
 392        struct led_flash_setting *s = &fled_cdev->timeout;
 393
 394        s->val = timeout;
 395        led_clamp_align(s);
 396
 397        if (!(led_cdev->flags & LED_SUSPENDED))
 398                return call_flash_op(fled_cdev, timeout_set, s->val);
 399
 400        return 0;
 401}
 402EXPORT_SYMBOL_GPL(led_set_flash_timeout);
 403
 404int led_get_flash_fault(struct led_classdev_flash *fled_cdev, u32 *fault)
 405{
 406        return call_flash_op(fled_cdev, fault_get, fault);
 407}
 408EXPORT_SYMBOL_GPL(led_get_flash_fault);
 409
 410int led_set_flash_brightness(struct led_classdev_flash *fled_cdev,
 411                                u32 brightness)
 412{
 413        struct led_classdev *led_cdev = &fled_cdev->led_cdev;
 414        struct led_flash_setting *s = &fled_cdev->brightness;
 415
 416        s->val = brightness;
 417        led_clamp_align(s);
 418
 419        if (!(led_cdev->flags & LED_SUSPENDED))
 420                return call_flash_op(fled_cdev, flash_brightness_set, s->val);
 421
 422        return 0;
 423}
 424EXPORT_SYMBOL_GPL(led_set_flash_brightness);
 425
 426int led_update_flash_brightness(struct led_classdev_flash *fled_cdev)
 427{
 428        struct led_flash_setting *s = &fled_cdev->brightness;
 429        u32 brightness;
 430
 431        if (has_flash_op(fled_cdev, flash_brightness_get)) {
 432                int ret = call_flash_op(fled_cdev, flash_brightness_get,
 433                                                &brightness);
 434                if (ret < 0)
 435                        return ret;
 436
 437                s->val = brightness;
 438        }
 439
 440        return 0;
 441}
 442EXPORT_SYMBOL_GPL(led_update_flash_brightness);
 443
 444MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>");
 445MODULE_DESCRIPTION("LED Flash class interface");
 446MODULE_LICENSE("GPL v2");
 447