aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/leds/Kconfig10
-rw-r--r--drivers/leds/Makefile1
-rw-r--r--drivers/leds/leds-syscon.c166
3 files changed, 177 insertions, 0 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 8c96e2ddf43b..dc588b4f5905 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -478,6 +478,16 @@ config LEDS_BLINKM
478 This option enables support for the BlinkM RGB LED connected 478 This option enables support for the BlinkM RGB LED connected
479 through I2C. Say Y to enable support for the BlinkM LED. 479 through I2C. Say Y to enable support for the BlinkM LED.
480 480
481config LEDS_SYSCON
482 bool "LED support for LEDs on system controllers"
483 depends on LEDS_CLASS=y
484 depends on MFD_SYSCON
485 depends on OF
486 help
487 This option enabled support for the LEDs on syscon type
488 devices. This will only work with device tree enabled
489 devices.
490
481config LEDS_VERSATILE 491config LEDS_VERSATILE
482 tristate "LED support for the ARM Versatile and RealView" 492 tristate "LED support for the ARM Versatile and RealView"
483 depends on ARCH_REALVIEW || ARCH_VERSATILE 493 depends on ARCH_REALVIEW || ARCH_VERSATILE
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index d8cc5f2777de..822dd83ef97a 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -53,6 +53,7 @@ obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o
53obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o 53obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o
54obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o 54obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o
55obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o 55obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o
56obj-$(CONFIG_LEDS_SYSCON) += leds-syscon.o
56obj-$(CONFIG_LEDS_VERSATILE) += leds-versatile.o 57obj-$(CONFIG_LEDS_VERSATILE) += leds-versatile.o
57 58
58# LED SPI Drivers 59# LED SPI Drivers
diff --git a/drivers/leds/leds-syscon.c b/drivers/leds/leds-syscon.c
new file mode 100644
index 000000000000..3afec79c43f4
--- /dev/null
+++ b/drivers/leds/leds-syscon.c
@@ -0,0 +1,166 @@
1/*
2 * Generic Syscon LEDs Driver
3 *
4 * Copyright (c) 2014, Linaro Limited
5 * Author: Linus Walleij <linus.walleij@linaro.org>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of
10 * the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
20 * MA 02111-1307 USA
21 *
22 * This driver provides system reboot functionality for APM X-Gene SoC.
23 * For system shutdown, this is board specify. If a board designer
24 * implements GPIO shutdown, use the gpio-poweroff.c driver.
25 */
26#include <linux/io.h>
27#include <linux/of_device.h>
28#include <linux/of_address.h>
29#include <linux/platform_device.h>
30#include <linux/stat.h>
31#include <linux/slab.h>
32#include <linux/mfd/syscon.h>
33#include <linux/regmap.h>
34#include <linux/leds.h>
35
36/**
37 * struct syscon_led - state container for syscon based LEDs
38 * @cdev: LED class device for this LED
39 * @map: regmap to access the syscon device backing this LED
40 * @offset: the offset into the syscon regmap for the LED register
41 * @mask: the bit in the register corresponding to the LED
42 * @state: current state of the LED
43 */
44struct syscon_led {
45 struct led_classdev cdev;
46 struct regmap *map;
47 u32 offset;
48 u32 mask;
49 bool state;
50};
51
52static void syscon_led_set(struct led_classdev *led_cdev,
53 enum led_brightness value)
54{
55 struct syscon_led *sled =
56 container_of(led_cdev, struct syscon_led, cdev);
57 u32 val;
58 int ret;
59
60 if (value == LED_OFF) {
61 val = 0;
62 sled->state = false;
63 } else {
64 val = sled->mask;
65 sled->state = true;
66 }
67
68 ret = regmap_update_bits(sled->map, sled->offset, sled->mask, val);
69 if (ret < 0)
70 dev_err(sled->cdev.dev, "error updating LED status\n");
71}
72
73static const struct of_device_id syscon_match[] = {
74 { .compatible = "syscon", },
75 {},
76};
77
78static int __init syscon_leds_init(void)
79{
80 const struct of_device_id *devid;
81 struct device_node *np;
82 struct device_node *child;
83 struct regmap *map;
84 struct platform_device *pdev;
85 struct device *dev;
86 int ret;
87
88 np = of_find_matching_node_and_match(NULL, syscon_match,
89 &devid);
90 if (!np)
91 return -ENODEV;
92
93 map = syscon_node_to_regmap(np);
94 if (IS_ERR(map))
95 return PTR_ERR(map);
96
97 /*
98 * If the map is there, the device should be there, we allocate
99 * memory on the syscon device's behalf here.
100 */
101 pdev = of_find_device_by_node(np);
102 if (!pdev)
103 return -ENODEV;
104 dev = &pdev->dev;
105
106 for_each_available_child_of_node(np, child) {
107 struct syscon_led *sled;
108 const char *state;
109
110 /* Only check for register-bit-leds */
111 if (of_property_match_string(child, "compatible",
112 "register-bit-led") < 0)
113 continue;
114
115 sled = devm_kzalloc(dev, sizeof(*sled), GFP_KERNEL);
116 if (!sled)
117 return -ENOMEM;
118
119 sled->map = map;
120
121 if (of_property_read_u32(child, "offset", &sled->offset))
122 return -EINVAL;
123 if (of_property_read_u32(child, "mask", &sled->mask))
124 return -EINVAL;
125 sled->cdev.name =
126 of_get_property(child, "label", NULL) ? : child->name;
127 sled->cdev.default_trigger =
128 of_get_property(child, "linux,default-trigger", NULL);
129
130 state = of_get_property(child, "default-state", NULL);
131 if (state) {
132 if (!strcmp(state, "keep")) {
133 u32 val;
134
135 ret = regmap_read(map, sled->offset, &val);
136 if (ret < 0)
137 return ret;
138 sled->state = !!(val & sled->mask);
139 } else if (!strcmp(state, "on")) {
140 sled->state = true;
141 ret = regmap_update_bits(map, sled->offset,
142 sled->mask,
143 sled->mask);
144 if (ret < 0)
145 return ret;
146 } else {
147 sled->state = false;
148 ret = regmap_update_bits(map, sled->offset,
149 sled->mask, 0);
150 if (ret < 0)
151 return ret;
152 }
153
154 }
155 sled->cdev.brightness_set = syscon_led_set;
156
157 ret = led_classdev_register(dev, &sled->cdev);
158 if (ret < 0)
159 return ret;
160
161 dev_info(dev, "registered LED %s\n", sled->cdev.name);
162 }
163
164 return 0;
165}
166device_initcall(syscon_leds_init);