Commit | Line | Data |
---|---|---|
b5417019 LW |
1 | /* |
2 | * Driver for the 8 user LEDs found on the RealViews and Versatiles | |
3 | * Based on DaVinci's DM365 board code | |
4 | * | |
5 | * License terms: GNU General Public License (GPL) version 2 | |
6 | * Author: Linus Walleij <triad@df.lth.se> | |
7 | */ | |
8 | #include <linux/kernel.h> | |
9 | #include <linux/init.h> | |
e4ecf2bd | 10 | #include <linux/module.h> |
b5417019 LW |
11 | #include <linux/io.h> |
12 | #include <linux/slab.h> | |
13 | #include <linux/leds.h> | |
e4ecf2bd | 14 | #include <linux/platform_device.h> |
b5417019 LW |
15 | |
16 | struct versatile_led { | |
e4ecf2bd | 17 | void __iomem *base; |
b5417019 LW |
18 | struct led_classdev cdev; |
19 | u8 mask; | |
20 | }; | |
21 | ||
22 | /* | |
23 | * The triggers lines up below will only be used if the | |
24 | * LED triggers are compiled in. | |
25 | */ | |
26 | static const struct { | |
27 | const char *name; | |
28 | const char *trigger; | |
29 | } versatile_leds[] = { | |
30 | { "versatile:0", "heartbeat", }, | |
31 | { "versatile:1", "mmc0", }, | |
e031cd51 BW |
32 | { "versatile:2", "cpu0" }, |
33 | { "versatile:3", "cpu1" }, | |
34 | { "versatile:4", "cpu2" }, | |
35 | { "versatile:5", "cpu3" }, | |
b5417019 LW |
36 | { "versatile:6", }, |
37 | { "versatile:7", }, | |
38 | }; | |
39 | ||
40 | static void versatile_led_set(struct led_classdev *cdev, | |
41 | enum led_brightness b) | |
42 | { | |
43 | struct versatile_led *led = container_of(cdev, | |
44 | struct versatile_led, cdev); | |
e4ecf2bd | 45 | u32 reg = readl(led->base); |
b5417019 LW |
46 | |
47 | if (b != LED_OFF) | |
48 | reg |= led->mask; | |
49 | else | |
50 | reg &= ~led->mask; | |
e4ecf2bd | 51 | writel(reg, led->base); |
b5417019 LW |
52 | } |
53 | ||
54 | static enum led_brightness versatile_led_get(struct led_classdev *cdev) | |
55 | { | |
56 | struct versatile_led *led = container_of(cdev, | |
57 | struct versatile_led, cdev); | |
e4ecf2bd | 58 | u32 reg = readl(led->base); |
b5417019 LW |
59 | |
60 | return (reg & led->mask) ? LED_FULL : LED_OFF; | |
61 | } | |
62 | ||
e4ecf2bd | 63 | static int versatile_leds_probe(struct platform_device *dev) |
b5417019 LW |
64 | { |
65 | int i; | |
e4ecf2bd LW |
66 | struct resource *res; |
67 | void __iomem *base; | |
68 | ||
69 | res = platform_get_resource(dev, IORESOURCE_MEM, 0); | |
70 | base = devm_ioremap_resource(&dev->dev, res); | |
71 | if (IS_ERR(base)) | |
72 | return PTR_ERR(base); | |
b5417019 | 73 | |
d1cb3ecf | 74 | /* All off */ |
e4ecf2bd | 75 | writel(0, base); |
b5417019 LW |
76 | for (i = 0; i < ARRAY_SIZE(versatile_leds); i++) { |
77 | struct versatile_led *led; | |
78 | ||
79 | led = kzalloc(sizeof(*led), GFP_KERNEL); | |
80 | if (!led) | |
81 | break; | |
82 | ||
e4ecf2bd | 83 | led->base = base; |
b5417019 LW |
84 | led->cdev.name = versatile_leds[i].name; |
85 | led->cdev.brightness_set = versatile_led_set; | |
86 | led->cdev.brightness_get = versatile_led_get; | |
87 | led->cdev.default_trigger = versatile_leds[i].trigger; | |
88 | led->mask = BIT(i); | |
89 | ||
90 | if (led_classdev_register(NULL, &led->cdev) < 0) { | |
91 | kfree(led); | |
92 | break; | |
93 | } | |
94 | } | |
95 | ||
96 | return 0; | |
97 | } | |
98 | ||
e4ecf2bd LW |
99 | static struct platform_driver versatile_leds_driver = { |
100 | .driver = { | |
101 | .name = "versatile-leds", | |
102 | }, | |
103 | .probe = versatile_leds_probe, | |
104 | }; | |
105 | ||
106 | module_platform_driver(versatile_leds_driver); | |
107 | ||
108 | MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); | |
109 | MODULE_DESCRIPTION("ARM Versatile LED driver"); | |
110 | MODULE_LICENSE("GPL v2"); |