diff options
-rw-r--r-- | drivers/leds/Kconfig | 10 | ||||
-rw-r--r-- | drivers/leds/Makefile | 1 | ||||
-rw-r--r-- | drivers/leds/leds-syscon.c | 166 |
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 | ||
481 | config 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 | |||
481 | config LEDS_VERSATILE | 491 | config 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 | |||
53 | obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o | 53 | obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o |
54 | obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o | 54 | obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o |
55 | obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o | 55 | obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o |
56 | obj-$(CONFIG_LEDS_SYSCON) += leds-syscon.o | ||
56 | obj-$(CONFIG_LEDS_VERSATILE) += leds-versatile.o | 57 | obj-$(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 | */ | ||
44 | struct syscon_led { | ||
45 | struct led_classdev cdev; | ||
46 | struct regmap *map; | ||
47 | u32 offset; | ||
48 | u32 mask; | ||
49 | bool state; | ||
50 | }; | ||
51 | |||
52 | static 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 | |||
73 | static const struct of_device_id syscon_match[] = { | ||
74 | { .compatible = "syscon", }, | ||
75 | {}, | ||
76 | }; | ||
77 | |||
78 | static 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 | } | ||
166 | device_initcall(syscon_leds_init); | ||