aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorÁlvaro Fernández Rojas <noltari@gmail.com>2015-05-21 13:11:10 -0400
committerBryan Wu <cooloney@gmail.com>2015-05-25 16:26:48 -0400
commit589fca16c14adec7ebeb601e22850826e18b8f8d (patch)
treede16c30eec41c38ca775a82c50ce2e76132e6d59
parentc87cc34273dae2774d24d1ddd4edcadcce571a4e (diff)
leds: add BCM6358 LED driver
This adds support for the LED controller on Broadcom's BCM6358. Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com> Acked-by: Jacek Anaszewski <j.anaszewski@samsung.com> Signed-off-by: Bryan Wu <cooloney@gmail.com>
-rw-r--r--drivers/leds/Kconfig8
-rw-r--r--drivers/leds/Makefile1
-rw-r--r--drivers/leds/leds-bcm6358.c243
3 files changed, 252 insertions, 0 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index ec26dd18b891..86de046d4281 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -58,6 +58,14 @@ config LEDS_BCM6328
58 This option enables support for LEDs connected to the BCM6328 58 This option enables support for LEDs connected to the BCM6328
59 LED HW controller accessed via MMIO registers. 59 LED HW controller accessed via MMIO registers.
60 60
61config LEDS_BCM6358
62 tristate "LED Support for Broadcom BCM6358"
63 depends on LEDS_CLASS
64 depends on OF
65 help
66 This option enables support for LEDs connected to the BCM6358
67 LED HW controller accessed via MMIO registers.
68
61config LEDS_LM3530 69config LEDS_LM3530
62 tristate "LCD Backlight driver for LM3530" 70 tristate "LCD Backlight driver for LM3530"
63 depends on LEDS_CLASS 71 depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index edb3abf270ea..8d6a24a2f513 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o
9obj-$(CONFIG_LEDS_88PM860X) += leds-88pm860x.o 9obj-$(CONFIG_LEDS_88PM860X) += leds-88pm860x.o
10obj-$(CONFIG_LEDS_AAT1290) += leds-aat1290.o 10obj-$(CONFIG_LEDS_AAT1290) += leds-aat1290.o
11obj-$(CONFIG_LEDS_BCM6328) += leds-bcm6328.o 11obj-$(CONFIG_LEDS_BCM6328) += leds-bcm6328.o
12obj-$(CONFIG_LEDS_BCM6358) += leds-bcm6358.o
12obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o 13obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o
13obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o 14obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o
14obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.o 15obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.o
diff --git a/drivers/leds/leds-bcm6358.c b/drivers/leds/leds-bcm6358.c
new file mode 100644
index 000000000000..21f96930b3be
--- /dev/null
+++ b/drivers/leds/leds-bcm6358.c
@@ -0,0 +1,243 @@
1/*
2 * Driver for BCM6358 memory-mapped LEDs, based on leds-syscon.c
3 *
4 * Copyright 2015 Álvaro Fernández Rojas <noltari@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version.
10 */
11#include <linux/delay.h>
12#include <linux/io.h>
13#include <linux/leds.h>
14#include <linux/module.h>
15#include <linux/of.h>
16#include <linux/platform_device.h>
17#include <linux/spinlock.h>
18
19#define BCM6358_REG_MODE 0x0
20#define BCM6358_REG_CTRL 0x4
21
22#define BCM6358_SLED_CLKDIV_MASK 3
23#define BCM6358_SLED_CLKDIV_1 0
24#define BCM6358_SLED_CLKDIV_2 1
25#define BCM6358_SLED_CLKDIV_4 2
26#define BCM6358_SLED_CLKDIV_8 3
27
28#define BCM6358_SLED_POLARITY BIT(2)
29#define BCM6358_SLED_BUSY BIT(3)
30
31#define BCM6358_SLED_MAX_COUNT 32
32#define BCM6358_SLED_WAIT 100
33
34/**
35 * struct bcm6358_led - state container for bcm6358 based LEDs
36 * @cdev: LED class device for this LED
37 * @mem: memory resource
38 * @lock: memory lock
39 * @pin: LED pin number
40 * @active_low: LED is active low
41 */
42struct bcm6358_led {
43 struct led_classdev cdev;
44 void __iomem *mem;
45 spinlock_t *lock;
46 unsigned long pin;
47 bool active_low;
48};
49
50static void bcm6358_led_write(void __iomem *reg, unsigned long data)
51{
52 iowrite32be(data, reg);
53}
54
55static unsigned long bcm6358_led_read(void __iomem *reg)
56{
57 return ioread32be(reg);
58}
59
60static unsigned long bcm6358_led_busy(void __iomem *mem)
61{
62 unsigned long val;
63
64 while ((val = bcm6358_led_read(mem + BCM6358_REG_CTRL)) &
65 BCM6358_SLED_BUSY)
66 udelay(BCM6358_SLED_WAIT);
67
68 return val;
69}
70
71static void bcm6358_led_mode(struct bcm6358_led *led, unsigned long value)
72{
73 unsigned long val;
74
75 bcm6358_led_busy(led->mem);
76
77 val = bcm6358_led_read(led->mem + BCM6358_REG_MODE);
78 if ((led->active_low && value == LED_OFF) ||
79 (!led->active_low && value != LED_OFF))
80 val |= BIT(led->pin);
81 else
82 val &= ~(BIT(led->pin));
83 bcm6358_led_write(led->mem + BCM6358_REG_MODE, val);
84}
85
86static void bcm6358_led_set(struct led_classdev *led_cdev,
87 enum led_brightness value)
88{
89 struct bcm6358_led *led =
90 container_of(led_cdev, struct bcm6358_led, cdev);
91 unsigned long flags;
92
93 spin_lock_irqsave(led->lock, flags);
94 bcm6358_led_mode(led, value);
95 spin_unlock_irqrestore(led->lock, flags);
96}
97
98static int bcm6358_led(struct device *dev, struct device_node *nc, u32 reg,
99 void __iomem *mem, spinlock_t *lock)
100{
101 struct bcm6358_led *led;
102 unsigned long flags;
103 const char *state;
104 int rc;
105
106 led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
107 if (!led)
108 return -ENOMEM;
109
110 led->pin = reg;
111 led->mem = mem;
112 led->lock = lock;
113
114 if (of_property_read_bool(nc, "active-low"))
115 led->active_low = true;
116
117 led->cdev.name = of_get_property(nc, "label", NULL) ? : nc->name;
118 led->cdev.default_trigger = of_get_property(nc,
119 "linux,default-trigger",
120 NULL);
121
122 spin_lock_irqsave(lock, flags);
123 if (!of_property_read_string(nc, "default-state", &state)) {
124 if (!strcmp(state, "on")) {
125 led->cdev.brightness = LED_FULL;
126 } else if (!strcmp(state, "keep")) {
127 unsigned long val;
128
129 bcm6358_led_busy(led->mem);
130
131 val = bcm6358_led_read(led->mem + BCM6358_REG_MODE);
132 val &= BIT(led->pin);
133 if ((led->active_low && !val) ||
134 (!led->active_low && val))
135 led->cdev.brightness = LED_FULL;
136 else
137 led->cdev.brightness = LED_OFF;
138 } else {
139 led->cdev.brightness = LED_OFF;
140 }
141 } else {
142 led->cdev.brightness = LED_OFF;
143 }
144 bcm6358_led_mode(led, led->cdev.brightness);
145 spin_unlock_irqrestore(lock, flags);
146
147 led->cdev.brightness_set = bcm6358_led_set;
148
149 rc = led_classdev_register(dev, &led->cdev);
150 if (rc < 0)
151 return rc;
152
153 dev_dbg(dev, "registered LED %s\n", led->cdev.name);
154
155 return 0;
156}
157
158static int bcm6358_leds_probe(struct platform_device *pdev)
159{
160 struct device *dev = &pdev->dev;
161 struct device_node *np = pdev->dev.of_node;
162 struct device_node *child;
163 struct resource *mem_r;
164 void __iomem *mem;
165 spinlock_t *lock; /* memory lock */
166 unsigned long val;
167 u32 clk_div;
168
169 mem_r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
170 if (!mem_r)
171 return -EINVAL;
172
173 mem = devm_ioremap_resource(dev, mem_r);
174 if (IS_ERR(mem))
175 return PTR_ERR(mem);
176
177 lock = devm_kzalloc(dev, sizeof(*lock), GFP_KERNEL);
178 if (!lock)
179 return -ENOMEM;
180
181 spin_lock_init(lock);
182
183 val = bcm6358_led_busy(mem);
184 val &= ~(BCM6358_SLED_POLARITY | BCM6358_SLED_CLKDIV_MASK);
185 if (of_property_read_bool(np, "brcm,clk-dat-low"))
186 val |= BCM6358_SLED_POLARITY;
187 of_property_read_u32(np, "brcm,clk-div", &clk_div);
188 switch (clk_div) {
189 case 8:
190 val |= BCM6358_SLED_CLKDIV_8;
191 break;
192 case 4:
193 val |= BCM6358_SLED_CLKDIV_4;
194 break;
195 case 2:
196 val |= BCM6358_SLED_CLKDIV_2;
197 break;
198 default:
199 val |= BCM6358_SLED_CLKDIV_1;
200 break;
201 }
202 bcm6358_led_write(mem + BCM6358_REG_CTRL, val);
203
204 for_each_available_child_of_node(np, child) {
205 int rc;
206 u32 reg;
207
208 if (of_property_read_u32(child, "reg", &reg))
209 continue;
210
211 if (reg >= BCM6358_SLED_MAX_COUNT) {
212 dev_err(dev, "invalid LED (%u >= %d)\n", reg,
213 BCM6358_SLED_MAX_COUNT);
214 continue;
215 }
216
217 rc = bcm6358_led(dev, child, reg, mem, lock);
218 if (rc < 0)
219 return rc;
220 }
221
222 return 0;
223}
224
225static const struct of_device_id bcm6358_leds_of_match[] = {
226 { .compatible = "brcm,bcm6358-leds", },
227 { },
228};
229
230static struct platform_driver bcm6358_leds_driver = {
231 .probe = bcm6358_leds_probe,
232 .driver = {
233 .name = "leds-bcm6358",
234 .of_match_table = bcm6358_leds_of_match,
235 },
236};
237
238module_platform_driver(bcm6358_leds_driver);
239
240MODULE_AUTHOR("Álvaro Fernández Rojas <noltari@gmail.com>");
241MODULE_DESCRIPTION("LED driver for BCM6358 controllers");
242MODULE_LICENSE("GPL v2");
243MODULE_ALIAS("platform:leds-bcm6358");