cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

led-class-flash.c (10951B)


      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 strlen(strcat(buf, "\n"));
    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");