Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * LED Flash class interface
0004  *
0005  * Copyright (C) 2015 Samsung Electronics Co., Ltd.
0006  * Author: Jacek Anaszewski <j.anaszewski@samsung.com>
0007  */
0008 
0009 #include <linux/device.h>
0010 #include <linux/init.h>
0011 #include <linux/led-class-flash.h>
0012 #include <linux/leds.h>
0013 #include <linux/module.h>
0014 #include <linux/slab.h>
0015 #include "leds.h"
0016 
0017 #define has_flash_op(fled_cdev, op)             \
0018     (fled_cdev && fled_cdev->ops->op)
0019 
0020 #define call_flash_op(fled_cdev, op, args...)       \
0021     ((has_flash_op(fled_cdev, op)) ?            \
0022             (fled_cdev->ops->op(fled_cdev, args)) : \
0023             -EINVAL)
0024 
0025 static const char * const led_flash_fault_names[] = {
0026     "led-over-voltage",
0027     "flash-timeout-exceeded",
0028     "controller-over-temperature",
0029     "controller-short-circuit",
0030     "led-power-supply-over-current",
0031     "indicator-led-fault",
0032     "led-under-voltage",
0033     "controller-under-voltage",
0034     "led-over-temperature",
0035 };
0036 
0037 static ssize_t flash_brightness_store(struct device *dev,
0038         struct device_attribute *attr, const char *buf, size_t size)
0039 {
0040     struct led_classdev *led_cdev = dev_get_drvdata(dev);
0041     struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
0042     unsigned long state;
0043     ssize_t ret;
0044 
0045     mutex_lock(&led_cdev->led_access);
0046 
0047     if (led_sysfs_is_disabled(led_cdev)) {
0048         ret = -EBUSY;
0049         goto unlock;
0050     }
0051 
0052     ret = kstrtoul(buf, 10, &state);
0053     if (ret)
0054         goto unlock;
0055 
0056     ret = led_set_flash_brightness(fled_cdev, state);
0057     if (ret < 0)
0058         goto unlock;
0059 
0060     ret = size;
0061 unlock:
0062     mutex_unlock(&led_cdev->led_access);
0063     return ret;
0064 }
0065 
0066 static ssize_t flash_brightness_show(struct device *dev,
0067         struct device_attribute *attr, char *buf)
0068 {
0069     struct led_classdev *led_cdev = dev_get_drvdata(dev);
0070     struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
0071 
0072     /* no lock needed for this */
0073     led_update_flash_brightness(fled_cdev);
0074 
0075     return sprintf(buf, "%u\n", fled_cdev->brightness.val);
0076 }
0077 static DEVICE_ATTR_RW(flash_brightness);
0078 
0079 static ssize_t max_flash_brightness_show(struct device *dev,
0080         struct device_attribute *attr, char *buf)
0081 {
0082     struct led_classdev *led_cdev = dev_get_drvdata(dev);
0083     struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
0084 
0085     return sprintf(buf, "%u\n", fled_cdev->brightness.max);
0086 }
0087 static DEVICE_ATTR_RO(max_flash_brightness);
0088 
0089 static ssize_t flash_strobe_store(struct device *dev,
0090         struct device_attribute *attr, const char *buf, size_t size)
0091 {
0092     struct led_classdev *led_cdev = dev_get_drvdata(dev);
0093     struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
0094     unsigned long state;
0095     ssize_t ret = -EBUSY;
0096 
0097     mutex_lock(&led_cdev->led_access);
0098 
0099     if (led_sysfs_is_disabled(led_cdev))
0100         goto unlock;
0101 
0102     ret = kstrtoul(buf, 10, &state);
0103     if (ret)
0104         goto unlock;
0105 
0106     if (state > 1) {
0107         ret = -EINVAL;
0108         goto unlock;
0109     }
0110 
0111     ret = led_set_flash_strobe(fled_cdev, state);
0112     if (ret < 0)
0113         goto unlock;
0114     ret = size;
0115 unlock:
0116     mutex_unlock(&led_cdev->led_access);
0117     return ret;
0118 }
0119 
0120 static ssize_t flash_strobe_show(struct device *dev,
0121         struct device_attribute *attr, char *buf)
0122 {
0123     struct led_classdev *led_cdev = dev_get_drvdata(dev);
0124     struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
0125     bool state;
0126     int ret;
0127 
0128     /* no lock needed for this */
0129     ret = led_get_flash_strobe(fled_cdev, &state);
0130     if (ret < 0)
0131         return ret;
0132 
0133     return sprintf(buf, "%u\n", state);
0134 }
0135 static DEVICE_ATTR_RW(flash_strobe);
0136 
0137 static ssize_t flash_timeout_store(struct device *dev,
0138         struct device_attribute *attr, const char *buf, size_t size)
0139 {
0140     struct led_classdev *led_cdev = dev_get_drvdata(dev);
0141     struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
0142     unsigned long flash_timeout;
0143     ssize_t ret;
0144 
0145     mutex_lock(&led_cdev->led_access);
0146 
0147     if (led_sysfs_is_disabled(led_cdev)) {
0148         ret = -EBUSY;
0149         goto unlock;
0150     }
0151 
0152     ret = kstrtoul(buf, 10, &flash_timeout);
0153     if (ret)
0154         goto unlock;
0155 
0156     ret = led_set_flash_timeout(fled_cdev, flash_timeout);
0157     if (ret < 0)
0158         goto unlock;
0159 
0160     ret = size;
0161 unlock:
0162     mutex_unlock(&led_cdev->led_access);
0163     return ret;
0164 }
0165 
0166 static ssize_t flash_timeout_show(struct device *dev,
0167         struct device_attribute *attr, char *buf)
0168 {
0169     struct led_classdev *led_cdev = dev_get_drvdata(dev);
0170     struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
0171 
0172     return sprintf(buf, "%u\n", fled_cdev->timeout.val);
0173 }
0174 static DEVICE_ATTR_RW(flash_timeout);
0175 
0176 static ssize_t max_flash_timeout_show(struct device *dev,
0177         struct device_attribute *attr, char *buf)
0178 {
0179     struct led_classdev *led_cdev = dev_get_drvdata(dev);
0180     struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
0181 
0182     return sprintf(buf, "%u\n", fled_cdev->timeout.max);
0183 }
0184 static DEVICE_ATTR_RO(max_flash_timeout);
0185 
0186 static ssize_t flash_fault_show(struct device *dev,
0187         struct device_attribute *attr, char *buf)
0188 {
0189     struct led_classdev *led_cdev = dev_get_drvdata(dev);
0190     struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
0191     u32 fault, mask = 0x1;
0192     char *pbuf = buf;
0193     int i, ret, buf_len;
0194 
0195     ret = led_get_flash_fault(fled_cdev, &fault);
0196     if (ret < 0)
0197         return -EINVAL;
0198 
0199     *buf = '\0';
0200 
0201     for (i = 0; i < LED_NUM_FLASH_FAULTS; ++i) {
0202         if (fault & mask) {
0203             buf_len = sprintf(pbuf, "%s ",
0204                       led_flash_fault_names[i]);
0205             pbuf += buf_len;
0206         }
0207         mask <<= 1;
0208     }
0209 
0210     return strlen(strcat(buf, "\n"));
0211 }
0212 static DEVICE_ATTR_RO(flash_fault);
0213 
0214 static struct attribute *led_flash_strobe_attrs[] = {
0215     &dev_attr_flash_strobe.attr,
0216     NULL,
0217 };
0218 
0219 static struct attribute *led_flash_timeout_attrs[] = {
0220     &dev_attr_flash_timeout.attr,
0221     &dev_attr_max_flash_timeout.attr,
0222     NULL,
0223 };
0224 
0225 static struct attribute *led_flash_brightness_attrs[] = {
0226     &dev_attr_flash_brightness.attr,
0227     &dev_attr_max_flash_brightness.attr,
0228     NULL,
0229 };
0230 
0231 static struct attribute *led_flash_fault_attrs[] = {
0232     &dev_attr_flash_fault.attr,
0233     NULL,
0234 };
0235 
0236 static const struct attribute_group led_flash_strobe_group = {
0237     .attrs = led_flash_strobe_attrs,
0238 };
0239 
0240 static const struct attribute_group led_flash_timeout_group = {
0241     .attrs = led_flash_timeout_attrs,
0242 };
0243 
0244 static const struct attribute_group led_flash_brightness_group = {
0245     .attrs = led_flash_brightness_attrs,
0246 };
0247 
0248 static const struct attribute_group led_flash_fault_group = {
0249     .attrs = led_flash_fault_attrs,
0250 };
0251 
0252 static void led_flash_resume(struct led_classdev *led_cdev)
0253 {
0254     struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev);
0255 
0256     call_flash_op(fled_cdev, flash_brightness_set,
0257                     fled_cdev->brightness.val);
0258     call_flash_op(fled_cdev, timeout_set, fled_cdev->timeout.val);
0259 }
0260 
0261 static void led_flash_init_sysfs_groups(struct led_classdev_flash *fled_cdev)
0262 {
0263     struct led_classdev *led_cdev = &fled_cdev->led_cdev;
0264     const struct led_flash_ops *ops = fled_cdev->ops;
0265     const struct attribute_group **flash_groups = fled_cdev->sysfs_groups;
0266 
0267     int num_sysfs_groups = 0;
0268 
0269     flash_groups[num_sysfs_groups++] = &led_flash_strobe_group;
0270 
0271     if (ops->flash_brightness_set)
0272         flash_groups[num_sysfs_groups++] = &led_flash_brightness_group;
0273 
0274     if (ops->timeout_set)
0275         flash_groups[num_sysfs_groups++] = &led_flash_timeout_group;
0276 
0277     if (ops->fault_get)
0278         flash_groups[num_sysfs_groups++] = &led_flash_fault_group;
0279 
0280     led_cdev->groups = flash_groups;
0281 }
0282 
0283 int led_classdev_flash_register_ext(struct device *parent,
0284                     struct led_classdev_flash *fled_cdev,
0285                     struct led_init_data *init_data)
0286 {
0287     struct led_classdev *led_cdev;
0288     const struct led_flash_ops *ops;
0289     int ret;
0290 
0291     if (!fled_cdev)
0292         return -EINVAL;
0293 
0294     led_cdev = &fled_cdev->led_cdev;
0295 
0296     if (led_cdev->flags & LED_DEV_CAP_FLASH) {
0297         if (!led_cdev->brightness_set_blocking)
0298             return -EINVAL;
0299 
0300         ops = fled_cdev->ops;
0301         if (!ops || !ops->strobe_set)
0302             return -EINVAL;
0303 
0304         led_cdev->flash_resume = led_flash_resume;
0305 
0306         /* Select the sysfs attributes to be created for the device */
0307         led_flash_init_sysfs_groups(fled_cdev);
0308     }
0309 
0310     /* Register led class device */
0311     ret = led_classdev_register_ext(parent, led_cdev, init_data);
0312     if (ret < 0)
0313         return ret;
0314 
0315     return 0;
0316 }
0317 EXPORT_SYMBOL_GPL(led_classdev_flash_register_ext);
0318 
0319 void led_classdev_flash_unregister(struct led_classdev_flash *fled_cdev)
0320 {
0321     if (!fled_cdev)
0322         return;
0323 
0324     led_classdev_unregister(&fled_cdev->led_cdev);
0325 }
0326 EXPORT_SYMBOL_GPL(led_classdev_flash_unregister);
0327 
0328 static void devm_led_classdev_flash_release(struct device *dev, void *res)
0329 {
0330     led_classdev_flash_unregister(*(struct led_classdev_flash **)res);
0331 }
0332 
0333 int devm_led_classdev_flash_register_ext(struct device *parent,
0334                      struct led_classdev_flash *fled_cdev,
0335                      struct led_init_data *init_data)
0336 {
0337     struct led_classdev_flash **dr;
0338     int ret;
0339 
0340     dr = devres_alloc(devm_led_classdev_flash_release, sizeof(*dr),
0341               GFP_KERNEL);
0342     if (!dr)
0343         return -ENOMEM;
0344 
0345     ret = led_classdev_flash_register_ext(parent, fled_cdev, init_data);
0346     if (ret) {
0347         devres_free(dr);
0348         return ret;
0349     }
0350 
0351     *dr = fled_cdev;
0352     devres_add(parent, dr);
0353 
0354     return 0;
0355 }
0356 EXPORT_SYMBOL_GPL(devm_led_classdev_flash_register_ext);
0357 
0358 static int devm_led_classdev_flash_match(struct device *dev,
0359                           void *res, void *data)
0360 {
0361     struct led_classdev_flash **p = res;
0362 
0363     if (WARN_ON(!p || !*p))
0364         return 0;
0365 
0366     return *p == data;
0367 }
0368 
0369 void devm_led_classdev_flash_unregister(struct device *dev,
0370                     struct led_classdev_flash *fled_cdev)
0371 {
0372     WARN_ON(devres_release(dev,
0373                    devm_led_classdev_flash_release,
0374                    devm_led_classdev_flash_match, fled_cdev));
0375 }
0376 EXPORT_SYMBOL_GPL(devm_led_classdev_flash_unregister);
0377 
0378 static void led_clamp_align(struct led_flash_setting *s)
0379 {
0380     u32 v, offset;
0381 
0382     v = s->val + s->step / 2;
0383     v = clamp(v, s->min, s->max);
0384     offset = v - s->min;
0385     offset = s->step * (offset / s->step);
0386     s->val = s->min + offset;
0387 }
0388 
0389 int led_set_flash_timeout(struct led_classdev_flash *fled_cdev, u32 timeout)
0390 {
0391     struct led_classdev *led_cdev = &fled_cdev->led_cdev;
0392     struct led_flash_setting *s = &fled_cdev->timeout;
0393 
0394     s->val = timeout;
0395     led_clamp_align(s);
0396 
0397     if (!(led_cdev->flags & LED_SUSPENDED))
0398         return call_flash_op(fled_cdev, timeout_set, s->val);
0399 
0400     return 0;
0401 }
0402 EXPORT_SYMBOL_GPL(led_set_flash_timeout);
0403 
0404 int led_get_flash_fault(struct led_classdev_flash *fled_cdev, u32 *fault)
0405 {
0406     return call_flash_op(fled_cdev, fault_get, fault);
0407 }
0408 EXPORT_SYMBOL_GPL(led_get_flash_fault);
0409 
0410 int led_set_flash_brightness(struct led_classdev_flash *fled_cdev,
0411                 u32 brightness)
0412 {
0413     struct led_classdev *led_cdev = &fled_cdev->led_cdev;
0414     struct led_flash_setting *s = &fled_cdev->brightness;
0415 
0416     s->val = brightness;
0417     led_clamp_align(s);
0418 
0419     if (!(led_cdev->flags & LED_SUSPENDED))
0420         return call_flash_op(fled_cdev, flash_brightness_set, s->val);
0421 
0422     return 0;
0423 }
0424 EXPORT_SYMBOL_GPL(led_set_flash_brightness);
0425 
0426 int led_update_flash_brightness(struct led_classdev_flash *fled_cdev)
0427 {
0428     struct led_flash_setting *s = &fled_cdev->brightness;
0429     u32 brightness;
0430 
0431     if (has_flash_op(fled_cdev, flash_brightness_get)) {
0432         int ret = call_flash_op(fled_cdev, flash_brightness_get,
0433                         &brightness);
0434         if (ret < 0)
0435             return ret;
0436 
0437         s->val = brightness;
0438     }
0439 
0440     return 0;
0441 }
0442 EXPORT_SYMBOL_GPL(led_update_flash_brightness);
0443 
0444 MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>");
0445 MODULE_DESCRIPTION("LED Flash class interface");
0446 MODULE_LICENSE("GPL v2");