]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - drivers/leds/leds-bcm6358.c
leds: bcm6328: simplify duplicated unlock in bcm6328_blink_set
[mirror_ubuntu-bionic-kernel.git] / drivers / leds / leds-bcm6358.c
CommitLineData
589fca16
ÁFR
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{
4ba113b6 52#ifdef CONFIG_CPU_BIG_ENDIAN
589fca16 53 iowrite32be(data, reg);
4ba113b6
ÁFR
54#else
55 writel(data, reg);
56#endif
589fca16
ÁFR
57}
58
59static unsigned long bcm6358_led_read(void __iomem *reg)
60{
4ba113b6 61#ifdef CONFIG_CPU_BIG_ENDIAN
589fca16 62 return ioread32be(reg);
4ba113b6
ÁFR
63#else
64 return readl(reg);
65#endif
589fca16
ÁFR
66}
67
68static unsigned long bcm6358_led_busy(void __iomem *mem)
69{
70 unsigned long val;
71
72 while ((val = bcm6358_led_read(mem + BCM6358_REG_CTRL)) &
73 BCM6358_SLED_BUSY)
74 udelay(BCM6358_SLED_WAIT);
75
76 return val;
77}
78
79static void bcm6358_led_mode(struct bcm6358_led *led, unsigned long value)
80{
81 unsigned long val;
82
83 bcm6358_led_busy(led->mem);
84
85 val = bcm6358_led_read(led->mem + BCM6358_REG_MODE);
86 if ((led->active_low && value == LED_OFF) ||
87 (!led->active_low && value != LED_OFF))
88 val |= BIT(led->pin);
89 else
90 val &= ~(BIT(led->pin));
91 bcm6358_led_write(led->mem + BCM6358_REG_MODE, val);
92}
93
94static void bcm6358_led_set(struct led_classdev *led_cdev,
95 enum led_brightness value)
96{
97 struct bcm6358_led *led =
98 container_of(led_cdev, struct bcm6358_led, cdev);
99 unsigned long flags;
100
101 spin_lock_irqsave(led->lock, flags);
102 bcm6358_led_mode(led, value);
103 spin_unlock_irqrestore(led->lock, flags);
104}
105
106static int bcm6358_led(struct device *dev, struct device_node *nc, u32 reg,
107 void __iomem *mem, spinlock_t *lock)
108{
109 struct bcm6358_led *led;
589fca16
ÁFR
110 const char *state;
111 int rc;
112
113 led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
114 if (!led)
115 return -ENOMEM;
116
117 led->pin = reg;
118 led->mem = mem;
119 led->lock = lock;
120
121 if (of_property_read_bool(nc, "active-low"))
122 led->active_low = true;
123
124 led->cdev.name = of_get_property(nc, "label", NULL) ? : nc->name;
125 led->cdev.default_trigger = of_get_property(nc,
126 "linux,default-trigger",
127 NULL);
128
589fca16
ÁFR
129 if (!of_property_read_string(nc, "default-state", &state)) {
130 if (!strcmp(state, "on")) {
131 led->cdev.brightness = LED_FULL;
132 } else if (!strcmp(state, "keep")) {
133 unsigned long val;
134
135 bcm6358_led_busy(led->mem);
136
137 val = bcm6358_led_read(led->mem + BCM6358_REG_MODE);
138 val &= BIT(led->pin);
139 if ((led->active_low && !val) ||
140 (!led->active_low && val))
141 led->cdev.brightness = LED_FULL;
142 else
143 led->cdev.brightness = LED_OFF;
144 } else {
145 led->cdev.brightness = LED_OFF;
146 }
147 } else {
148 led->cdev.brightness = LED_OFF;
149 }
589fca16 150
42273caa
ÁFR
151 bcm6358_led_set(&led->cdev, led->cdev.brightness);
152
589fca16
ÁFR
153 led->cdev.brightness_set = bcm6358_led_set;
154
155 rc = led_classdev_register(dev, &led->cdev);
156 if (rc < 0)
157 return rc;
158
159 dev_dbg(dev, "registered LED %s\n", led->cdev.name);
160
161 return 0;
162}
163
164static int bcm6358_leds_probe(struct platform_device *pdev)
165{
166 struct device *dev = &pdev->dev;
167 struct device_node *np = pdev->dev.of_node;
168 struct device_node *child;
169 struct resource *mem_r;
170 void __iomem *mem;
171 spinlock_t *lock; /* memory lock */
172 unsigned long val;
173 u32 clk_div;
174
175 mem_r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
176 if (!mem_r)
177 return -EINVAL;
178
179 mem = devm_ioremap_resource(dev, mem_r);
180 if (IS_ERR(mem))
181 return PTR_ERR(mem);
182
183 lock = devm_kzalloc(dev, sizeof(*lock), GFP_KERNEL);
184 if (!lock)
185 return -ENOMEM;
186
187 spin_lock_init(lock);
188
189 val = bcm6358_led_busy(mem);
190 val &= ~(BCM6358_SLED_POLARITY | BCM6358_SLED_CLKDIV_MASK);
191 if (of_property_read_bool(np, "brcm,clk-dat-low"))
192 val |= BCM6358_SLED_POLARITY;
193 of_property_read_u32(np, "brcm,clk-div", &clk_div);
194 switch (clk_div) {
195 case 8:
196 val |= BCM6358_SLED_CLKDIV_8;
197 break;
198 case 4:
199 val |= BCM6358_SLED_CLKDIV_4;
200 break;
201 case 2:
202 val |= BCM6358_SLED_CLKDIV_2;
203 break;
204 default:
205 val |= BCM6358_SLED_CLKDIV_1;
206 break;
207 }
208 bcm6358_led_write(mem + BCM6358_REG_CTRL, val);
209
210 for_each_available_child_of_node(np, child) {
211 int rc;
212 u32 reg;
213
214 if (of_property_read_u32(child, "reg", &reg))
215 continue;
216
217 if (reg >= BCM6358_SLED_MAX_COUNT) {
218 dev_err(dev, "invalid LED (%u >= %d)\n", reg,
219 BCM6358_SLED_MAX_COUNT);
220 continue;
221 }
222
223 rc = bcm6358_led(dev, child, reg, mem, lock);
4b6ba5e2
JL
224 if (rc < 0) {
225 of_node_put(child);
589fca16 226 return rc;
4b6ba5e2 227 }
589fca16
ÁFR
228 }
229
230 return 0;
231}
232
233static const struct of_device_id bcm6358_leds_of_match[] = {
234 { .compatible = "brcm,bcm6358-leds", },
235 { },
236};
01736f07 237MODULE_DEVICE_TABLE(of, bcm6358_leds_of_match);
589fca16
ÁFR
238
239static struct platform_driver bcm6358_leds_driver = {
240 .probe = bcm6358_leds_probe,
241 .driver = {
242 .name = "leds-bcm6358",
243 .of_match_table = bcm6358_leds_of_match,
244 },
245};
246
247module_platform_driver(bcm6358_leds_driver);
248
249MODULE_AUTHOR("Álvaro Fernández Rojas <noltari@gmail.com>");
250MODULE_DESCRIPTION("LED driver for BCM6358 controllers");
251MODULE_LICENSE("GPL v2");
252MODULE_ALIAS("platform:leds-bcm6358");