Blame view
drivers/leds/leds-mc13783.c
7.36 KB
d2912cb15 treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-only |
7fdcef8a4 leds: Add mc13783... |
2 |
/* |
a59ce6584 leds: leds-mc1378... |
3 |
* LEDs driver for Freescale MC13783/MC13892/MC34708 |
7fdcef8a4 leds: Add mc13783... |
4 5 6 7 8 9 10 11 12 |
* * Copyright (C) 2010 Philippe Rétornaz * * Based on leds-da903x: * Copyright (C) 2008 Compulab, Ltd. * Mike Rapoport <mike@compulab.co.il> * * Copyright (C) 2006-2008 Marvell International Ltd. * Eric Miao <eric.miao@marvell.com> |
7fdcef8a4 leds: Add mc13783... |
13 14 15 16 |
*/ #include <linux/module.h> #include <linux/kernel.h> |
7fdcef8a4 leds: Add mc13783... |
17 18 |
#include <linux/platform_device.h> #include <linux/leds.h> |
25c6579f8 leds: leds-mc1378... |
19 |
#include <linux/of.h> |
f3ca07824 leds: Convert mc1... |
20 |
#include <linux/mfd/mc13xxx.h> |
7fdcef8a4 leds: Add mc13783... |
21 |
|
9d263813c leds: leds-mc1378... |
22 23 24 25 |
struct mc13xxx_led_devtype { int led_min; int led_max; int num_regs; |
a59ce6584 leds: leds-mc1378... |
26 |
u32 ledctrl_base; |
9d263813c leds: leds-mc1378... |
27 28 29 |
}; struct mc13xxx_led { |
7fdcef8a4 leds: Add mc13783... |
30 |
struct led_classdev cdev; |
7fdcef8a4 leds: Add mc13783... |
31 |
int id; |
a59ce6584 leds: leds-mc1378... |
32 |
struct mc13xxx_leds *leds; |
7fdcef8a4 leds: Add mc13783... |
33 |
}; |
9d263813c leds: leds-mc1378... |
34 |
struct mc13xxx_leds { |
a59ce6584 leds: leds-mc1378... |
35 |
struct mc13xxx *master; |
9d263813c leds: leds-mc1378... |
36 37 |
struct mc13xxx_led_devtype *devtype; int num_leds; |
25c6579f8 leds: leds-mc1378... |
38 |
struct mc13xxx_led *led; |
9d263813c leds: leds-mc1378... |
39 |
}; |
677d13f27 leds: leds-mc1378... |
40 41 42 43 44 45 46 47 48 |
static unsigned int mc13xxx_max_brightness(int id) { if (id >= MC13783_LED_MD && id <= MC13783_LED_KP) return 0x0f; else if (id >= MC13783_LED_R1 && id <= MC13783_LED_B3) return 0x1f; return 0x3f; } |
4330f2f26 leds: mc13783: Re... |
49 50 |
static int mc13xxx_led_set(struct led_classdev *led_cdev, enum led_brightness value) |
7fdcef8a4 leds: Add mc13783... |
51 |
{ |
4330f2f26 leds: mc13783: Re... |
52 53 |
struct mc13xxx_led *led = container_of(led_cdev, struct mc13xxx_led, cdev); |
a59ce6584 leds: leds-mc1378... |
54 |
struct mc13xxx_leds *leds = led->leds; |
677d13f27 leds: leds-mc1378... |
55 |
unsigned int reg, bank, off, shift; |
7fdcef8a4 leds: Add mc13783... |
56 57 58 |
switch (led->id) { case MC13783_LED_MD: |
7fdcef8a4 leds: Add mc13783... |
59 |
case MC13783_LED_AD: |
7fdcef8a4 leds: Add mc13783... |
60 |
case MC13783_LED_KP: |
a59ce6584 leds: leds-mc1378... |
61 62 |
reg = 2; shift = 9 + (led->id - MC13783_LED_MD) * 4; |
7fdcef8a4 leds: Add mc13783... |
63 64 65 66 67 68 69 70 71 72 73 |
break; case MC13783_LED_R1: case MC13783_LED_G1: case MC13783_LED_B1: case MC13783_LED_R2: case MC13783_LED_G2: case MC13783_LED_B2: case MC13783_LED_R3: case MC13783_LED_G3: case MC13783_LED_B3: off = led->id - MC13783_LED_R1; |
9d263813c leds: leds-mc1378... |
74 |
bank = off / 3; |
a59ce6584 leds: leds-mc1378... |
75 |
reg = 3 + bank; |
9d263813c leds: leds-mc1378... |
76 |
shift = (off - bank * 3) * 5 + 6; |
7fdcef8a4 leds: Add mc13783... |
77 |
break; |
ae6cdb03e leds: leds-mc1378... |
78 |
case MC13892_LED_MD: |
ae6cdb03e leds: leds-mc1378... |
79 |
case MC13892_LED_AD: |
ae6cdb03e leds: leds-mc1378... |
80 |
case MC13892_LED_KP: |
cce35f357 leds: mc13783: Fi... |
81 82 83 |
off = led->id - MC13892_LED_MD; reg = off / 2; shift = 3 + (off - reg * 2) * 12; |
ae6cdb03e leds: leds-mc1378... |
84 85 86 87 88 89 |
break; case MC13892_LED_R: case MC13892_LED_G: case MC13892_LED_B: off = led->id - MC13892_LED_R; bank = off / 2; |
a59ce6584 leds: leds-mc1378... |
90 |
reg = 2 + bank; |
ae6cdb03e leds: leds-mc1378... |
91 |
shift = (off - bank * 2) * 12 + 3; |
ae6cdb03e leds: leds-mc1378... |
92 |
break; |
a59ce6584 leds: leds-mc1378... |
93 94 95 96 |
case MC34708_LED_R: case MC34708_LED_G: reg = 0; shift = 3 + (led->id - MC34708_LED_R) * 12; |
a59ce6584 leds: leds-mc1378... |
97 |
break; |
9d263813c leds: leds-mc1378... |
98 99 |
default: BUG(); |
7fdcef8a4 leds: Add mc13783... |
100 |
} |
4330f2f26 leds: mc13783: Re... |
101 |
return mc13xxx_reg_rmw(leds->master, leds->devtype->ledctrl_base + reg, |
677d13f27 leds: leds-mc1378... |
102 |
mc13xxx_max_brightness(led->id) << shift, |
4330f2f26 leds: mc13783: Re... |
103 |
value << shift); |
7fdcef8a4 leds: Add mc13783... |
104 |
} |
25c6579f8 leds: leds-mc1378... |
105 106 107 108 109 110 111 112 113 114 115 116 117 |
#ifdef CONFIG_OF static struct mc13xxx_leds_platform_data __init *mc13xxx_led_probe_dt( struct platform_device *pdev) { struct mc13xxx_leds *leds = platform_get_drvdata(pdev); struct mc13xxx_leds_platform_data *pdata; struct device_node *parent, *child; struct device *dev = &pdev->dev; int i = 0, ret = -ENODATA; pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return ERR_PTR(-ENOMEM); |
8853c95e9 leds: various: us... |
118 |
parent = of_get_child_by_name(dev_of_node(dev->parent), "leds"); |
25c6579f8 leds: leds-mc1378... |
119 120 121 122 123 124 125 126 |
if (!parent) goto out_node_put; ret = of_property_read_u32_array(parent, "led-control", pdata->led_control, leds->devtype->num_regs); if (ret) goto out_node_put; |
99a013c84 leds: various: us... |
127 |
pdata->num_leds = of_get_available_child_count(parent); |
25c6579f8 leds: leds-mc1378... |
128 |
|
a86854d0c treewide: devm_kz... |
129 |
pdata->led = devm_kcalloc(dev, pdata->num_leds, sizeof(*pdata->led), |
25c6579f8 leds: leds-mc1378... |
130 131 132 133 134 |
GFP_KERNEL); if (!pdata->led) { ret = -ENOMEM; goto out_node_put; } |
99a013c84 leds: various: us... |
135 |
for_each_available_child_of_node(parent, child) { |
25c6579f8 leds: leds-mc1378... |
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
const char *str; u32 tmp; if (of_property_read_u32(child, "reg", &tmp)) continue; pdata->led[i].id = leds->devtype->led_min + tmp; if (!of_property_read_string(child, "label", &str)) pdata->led[i].name = str; if (!of_property_read_string(child, "linux,default-trigger", &str)) pdata->led[i].default_trigger = str; i++; } pdata->num_leds = i; ret = i > 0 ? 0 : -ENODATA; out_node_put: of_node_put(parent); return ret ? ERR_PTR(ret) : pdata; } #else static inline struct mc13xxx_leds_platform_data __init *mc13xxx_led_probe_dt( struct platform_device *pdev) { return ERR_PTR(-ENOSYS); } #endif |
9d263813c leds: leds-mc1378... |
167 |
static int __init mc13xxx_led_probe(struct platform_device *pdev) |
7fdcef8a4 leds: Add mc13783... |
168 |
{ |
a59ce6584 leds: leds-mc1378... |
169 170 171 |
struct device *dev = &pdev->dev; struct mc13xxx_leds_platform_data *pdata = dev_get_platdata(dev); struct mc13xxx *mcdev = dev_get_drvdata(dev->parent); |
9d263813c leds: leds-mc1378... |
172 173 174 |
struct mc13xxx_led_devtype *devtype = (struct mc13xxx_led_devtype *)pdev->id_entry->driver_data; struct mc13xxx_leds *leds; |
25c6579f8 leds: leds-mc1378... |
175 |
int i, id, ret = -ENODATA; |
a59ce6584 leds: leds-mc1378... |
176 |
u32 init_led = 0; |
9d263813c leds: leds-mc1378... |
177 |
|
25c6579f8 leds: leds-mc1378... |
178 |
leds = devm_kzalloc(dev, sizeof(*leds), GFP_KERNEL); |
9d263813c leds: leds-mc1378... |
179 |
if (!leds) |
7fdcef8a4 leds: Add mc13783... |
180 |
return -ENOMEM; |
9d263813c leds: leds-mc1378... |
181 182 |
leds->devtype = devtype; |
a59ce6584 leds: leds-mc1378... |
183 |
leds->master = mcdev; |
9d263813c leds: leds-mc1378... |
184 |
platform_set_drvdata(pdev, leds); |
8853c95e9 leds: various: us... |
185 |
if (dev_of_node(dev->parent)) { |
25c6579f8 leds: leds-mc1378... |
186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
pdata = mc13xxx_led_probe_dt(pdev); if (IS_ERR(pdata)) return PTR_ERR(pdata); } else if (!pdata) return -ENODATA; leds->num_leds = pdata->num_leds; if ((leds->num_leds < 1) || (leds->num_leds > (devtype->led_max - devtype->led_min + 1))) { dev_err(dev, "Invalid LED count %d ", leds->num_leds); return -EINVAL; } |
a86854d0c treewide: devm_kz... |
200 |
leds->led = devm_kcalloc(dev, leds->num_leds, sizeof(*leds->led), |
25c6579f8 leds: leds-mc1378... |
201 202 203 |
GFP_KERNEL); if (!leds->led) return -ENOMEM; |
9d263813c leds: leds-mc1378... |
204 |
for (i = 0; i < devtype->num_regs; i++) { |
a59ce6584 leds: leds-mc1378... |
205 206 |
ret = mc13xxx_reg_write(mcdev, leds->devtype->ledctrl_base + i, pdata->led_control[i]); |
9d263813c leds: leds-mc1378... |
207 |
if (ret) |
3df22c06b leds: leds-mc1378... |
208 |
return ret; |
7fdcef8a4 leds: Add mc13783... |
209 |
} |
25c6579f8 leds: leds-mc1378... |
210 |
for (i = 0; i < leds->num_leds; i++) { |
9d263813c leds: leds-mc1378... |
211 |
const char *name, *trig; |
7fdcef8a4 leds: Add mc13783... |
212 |
|
9d263813c leds: leds-mc1378... |
213 214 215 216 217 |
ret = -EINVAL; id = pdata->led[i].id; name = pdata->led[i].name; trig = pdata->led[i].default_trigger; |
9d263813c leds: leds-mc1378... |
218 219 |
if ((id > devtype->led_max) || (id < devtype->led_min)) { |
a59ce6584 leds: leds-mc1378... |
220 221 |
dev_err(dev, "Invalid ID %i ", id); |
9d263813c leds: leds-mc1378... |
222 |
break; |
7fdcef8a4 leds: Add mc13783... |
223 |
} |
9d263813c leds: leds-mc1378... |
224 |
if (init_led & (1 << id)) { |
a59ce6584 leds: leds-mc1378... |
225 226 |
dev_warn(dev, "LED %i already initialized ", id); |
9d263813c leds: leds-mc1378... |
227 |
break; |
7fdcef8a4 leds: Add mc13783... |
228 |
} |
9d263813c leds: leds-mc1378... |
229 230 |
init_led |= 1 << id; leds->led[i].id = id; |
a59ce6584 leds: leds-mc1378... |
231 |
leds->led[i].leds = leds; |
9d263813c leds: leds-mc1378... |
232 233 |
leds->led[i].cdev.name = name; leds->led[i].cdev.default_trigger = trig; |
02e9e11e2 leds: leds-mc1378... |
234 |
leds->led[i].cdev.flags = LED_CORE_SUSPENDRESUME; |
4330f2f26 leds: mc13783: Re... |
235 |
leds->led[i].cdev.brightness_set_blocking = mc13xxx_led_set; |
677d13f27 leds: leds-mc1378... |
236 |
leds->led[i].cdev.max_brightness = mc13xxx_max_brightness(id); |
7fdcef8a4 leds: Add mc13783... |
237 |
|
a59ce6584 leds: leds-mc1378... |
238 |
ret = led_classdev_register(dev->parent, &leds->led[i].cdev); |
7fdcef8a4 leds: Add mc13783... |
239 |
if (ret) { |
a59ce6584 leds: leds-mc1378... |
240 241 |
dev_err(dev, "Failed to register LED %i ", id); |
9d263813c leds: leds-mc1378... |
242 |
break; |
7fdcef8a4 leds: Add mc13783... |
243 244 |
} } |
9d263813c leds: leds-mc1378... |
245 |
if (ret) |
4330f2f26 leds: mc13783: Re... |
246 |
while (--i >= 0) |
9d263813c leds: leds-mc1378... |
247 |
led_classdev_unregister(&leds->led[i].cdev); |
7fdcef8a4 leds: Add mc13783... |
248 |
|
7fdcef8a4 leds: Add mc13783... |
249 250 |
return ret; } |
9d263813c leds: leds-mc1378... |
251 |
static int mc13xxx_led_remove(struct platform_device *pdev) |
7fdcef8a4 leds: Add mc13783... |
252 |
{ |
9d263813c leds: leds-mc1378... |
253 |
struct mc13xxx_leds *leds = platform_get_drvdata(pdev); |
7fdcef8a4 leds: Add mc13783... |
254 |
int i; |
4330f2f26 leds: mc13783: Re... |
255 |
for (i = 0; i < leds->num_leds; i++) |
9d263813c leds: leds-mc1378... |
256 |
led_classdev_unregister(&leds->led[i].cdev); |
7fdcef8a4 leds: Add mc13783... |
257 |
|
7fdcef8a4 leds: Add mc13783... |
258 259 |
return 0; } |
9d263813c leds: leds-mc1378... |
260 261 262 263 |
static const struct mc13xxx_led_devtype mc13783_led_devtype = { .led_min = MC13783_LED_MD, .led_max = MC13783_LED_B3, .num_regs = 6, |
a59ce6584 leds: leds-mc1378... |
264 |
.ledctrl_base = 51, |
9d263813c leds: leds-mc1378... |
265 |
}; |
ae6cdb03e leds: leds-mc1378... |
266 267 268 269 |
static const struct mc13xxx_led_devtype mc13892_led_devtype = { .led_min = MC13892_LED_MD, .led_max = MC13892_LED_B, .num_regs = 4, |
a59ce6584 leds: leds-mc1378... |
270 271 272 273 274 275 276 277 |
.ledctrl_base = 51, }; static const struct mc13xxx_led_devtype mc34708_led_devtype = { .led_min = MC34708_LED_R, .led_max = MC34708_LED_G, .num_regs = 1, .ledctrl_base = 54, |
ae6cdb03e leds: leds-mc1378... |
278 |
}; |
9d263813c leds: leds-mc1378... |
279 280 |
static const struct platform_device_id mc13xxx_led_id_table[] = { { "mc13783-led", (kernel_ulong_t)&mc13783_led_devtype, }, |
ae6cdb03e leds: leds-mc1378... |
281 |
{ "mc13892-led", (kernel_ulong_t)&mc13892_led_devtype, }, |
a59ce6584 leds: leds-mc1378... |
282 |
{ "mc34708-led", (kernel_ulong_t)&mc34708_led_devtype, }, |
9d263813c leds: leds-mc1378... |
283 284 285 286 287 |
{ } }; MODULE_DEVICE_TABLE(platform, mc13xxx_led_id_table); static struct platform_driver mc13xxx_led_driver = { |
7fdcef8a4 leds: Add mc13783... |
288 |
.driver = { |
9d263813c leds: leds-mc1378... |
289 |
.name = "mc13xxx-led", |
7fdcef8a4 leds: Add mc13783... |
290 |
}, |
9d263813c leds: leds-mc1378... |
291 292 |
.remove = mc13xxx_led_remove, .id_table = mc13xxx_led_id_table, |
7fdcef8a4 leds: Add mc13783... |
293 |
}; |
9d263813c leds: leds-mc1378... |
294 |
module_platform_driver_probe(mc13xxx_led_driver, mc13xxx_led_probe); |
7fdcef8a4 leds: Add mc13783... |
295 |
|
9d263813c leds: leds-mc1378... |
296 |
MODULE_DESCRIPTION("LEDs driver for Freescale MC13XXX PMIC"); |
7fdcef8a4 leds: Add mc13783... |
297 298 |
MODULE_AUTHOR("Philippe Retornaz <philippe.retornaz@epfl.ch>"); MODULE_LICENSE("GPL"); |