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

ledtrig-gpio.c (5149B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * ledtrig-gio.c - LED Trigger Based on GPIO events
      4 *
      5 * Copyright 2009 Felipe Balbi <me@felipebalbi.com>
      6 */
      7
      8#include <linux/module.h>
      9#include <linux/kernel.h>
     10#include <linux/init.h>
     11#include <linux/gpio.h>
     12#include <linux/interrupt.h>
     13#include <linux/leds.h>
     14#include <linux/slab.h>
     15#include "../leds.h"
     16
     17struct gpio_trig_data {
     18	struct led_classdev *led;
     19
     20	unsigned desired_brightness;	/* desired brightness when led is on */
     21	unsigned inverted;		/* true when gpio is inverted */
     22	unsigned gpio;			/* gpio that triggers the leds */
     23};
     24
     25static irqreturn_t gpio_trig_irq(int irq, void *_led)
     26{
     27	struct led_classdev *led = _led;
     28	struct gpio_trig_data *gpio_data = led_get_trigger_data(led);
     29	int tmp;
     30
     31	tmp = gpio_get_value_cansleep(gpio_data->gpio);
     32	if (gpio_data->inverted)
     33		tmp = !tmp;
     34
     35	if (tmp) {
     36		if (gpio_data->desired_brightness)
     37			led_set_brightness_nosleep(gpio_data->led,
     38					   gpio_data->desired_brightness);
     39		else
     40			led_set_brightness_nosleep(gpio_data->led, LED_FULL);
     41	} else {
     42		led_set_brightness_nosleep(gpio_data->led, LED_OFF);
     43	}
     44
     45	return IRQ_HANDLED;
     46}
     47
     48static ssize_t gpio_trig_brightness_show(struct device *dev,
     49		struct device_attribute *attr, char *buf)
     50{
     51	struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev);
     52
     53	return sprintf(buf, "%u\n", gpio_data->desired_brightness);
     54}
     55
     56static ssize_t gpio_trig_brightness_store(struct device *dev,
     57		struct device_attribute *attr, const char *buf, size_t n)
     58{
     59	struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev);
     60	unsigned desired_brightness;
     61	int ret;
     62
     63	ret = sscanf(buf, "%u", &desired_brightness);
     64	if (ret < 1 || desired_brightness > 255) {
     65		dev_err(dev, "invalid value\n");
     66		return -EINVAL;
     67	}
     68
     69	gpio_data->desired_brightness = desired_brightness;
     70
     71	return n;
     72}
     73static DEVICE_ATTR(desired_brightness, 0644, gpio_trig_brightness_show,
     74		gpio_trig_brightness_store);
     75
     76static ssize_t gpio_trig_inverted_show(struct device *dev,
     77		struct device_attribute *attr, char *buf)
     78{
     79	struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev);
     80
     81	return sprintf(buf, "%u\n", gpio_data->inverted);
     82}
     83
     84static ssize_t gpio_trig_inverted_store(struct device *dev,
     85		struct device_attribute *attr, const char *buf, size_t n)
     86{
     87	struct led_classdev *led = led_trigger_get_led(dev);
     88	struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev);
     89	unsigned long inverted;
     90	int ret;
     91
     92	ret = kstrtoul(buf, 10, &inverted);
     93	if (ret < 0)
     94		return ret;
     95
     96	if (inverted > 1)
     97		return -EINVAL;
     98
     99	gpio_data->inverted = inverted;
    100
    101	/* After inverting, we need to update the LED. */
    102	if (gpio_is_valid(gpio_data->gpio))
    103		gpio_trig_irq(0, led);
    104
    105	return n;
    106}
    107static DEVICE_ATTR(inverted, 0644, gpio_trig_inverted_show,
    108		gpio_trig_inverted_store);
    109
    110static ssize_t gpio_trig_gpio_show(struct device *dev,
    111		struct device_attribute *attr, char *buf)
    112{
    113	struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev);
    114
    115	return sprintf(buf, "%u\n", gpio_data->gpio);
    116}
    117
    118static ssize_t gpio_trig_gpio_store(struct device *dev,
    119		struct device_attribute *attr, const char *buf, size_t n)
    120{
    121	struct led_classdev *led = led_trigger_get_led(dev);
    122	struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev);
    123	unsigned gpio;
    124	int ret;
    125
    126	ret = sscanf(buf, "%u", &gpio);
    127	if (ret < 1) {
    128		dev_err(dev, "couldn't read gpio number\n");
    129		return -EINVAL;
    130	}
    131
    132	if (gpio_data->gpio == gpio)
    133		return n;
    134
    135	if (!gpio_is_valid(gpio)) {
    136		if (gpio_is_valid(gpio_data->gpio))
    137			free_irq(gpio_to_irq(gpio_data->gpio), led);
    138		gpio_data->gpio = gpio;
    139		return n;
    140	}
    141
    142	ret = request_threaded_irq(gpio_to_irq(gpio), NULL, gpio_trig_irq,
    143			IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_RISING
    144			| IRQF_TRIGGER_FALLING, "ledtrig-gpio", led);
    145	if (ret) {
    146		dev_err(dev, "request_irq failed with error %d\n", ret);
    147	} else {
    148		if (gpio_is_valid(gpio_data->gpio))
    149			free_irq(gpio_to_irq(gpio_data->gpio), led);
    150		gpio_data->gpio = gpio;
    151		/* After changing the GPIO, we need to update the LED. */
    152		gpio_trig_irq(0, led);
    153	}
    154
    155	return ret ? ret : n;
    156}
    157static DEVICE_ATTR(gpio, 0644, gpio_trig_gpio_show, gpio_trig_gpio_store);
    158
    159static struct attribute *gpio_trig_attrs[] = {
    160	&dev_attr_desired_brightness.attr,
    161	&dev_attr_inverted.attr,
    162	&dev_attr_gpio.attr,
    163	NULL
    164};
    165ATTRIBUTE_GROUPS(gpio_trig);
    166
    167static int gpio_trig_activate(struct led_classdev *led)
    168{
    169	struct gpio_trig_data *gpio_data;
    170
    171	gpio_data = kzalloc(sizeof(*gpio_data), GFP_KERNEL);
    172	if (!gpio_data)
    173		return -ENOMEM;
    174
    175	gpio_data->led = led;
    176	gpio_data->gpio = -ENOENT;
    177
    178	led_set_trigger_data(led, gpio_data);
    179
    180	return 0;
    181}
    182
    183static void gpio_trig_deactivate(struct led_classdev *led)
    184{
    185	struct gpio_trig_data *gpio_data = led_get_trigger_data(led);
    186
    187	if (gpio_is_valid(gpio_data->gpio))
    188		free_irq(gpio_to_irq(gpio_data->gpio), led);
    189	kfree(gpio_data);
    190}
    191
    192static struct led_trigger gpio_led_trigger = {
    193	.name		= "gpio",
    194	.activate	= gpio_trig_activate,
    195	.deactivate	= gpio_trig_deactivate,
    196	.groups		= gpio_trig_groups,
    197};
    198module_led_trigger(gpio_led_trigger);
    199
    200MODULE_AUTHOR("Felipe Balbi <me@felipebalbi.com>");
    201MODULE_DESCRIPTION("GPIO LED trigger");
    202MODULE_LICENSE("GPL v2");