Linus Walleij | b5417019 | 2010-07-20 22:32:00 +0100 | [diff] [blame] | 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> |
Linus Walleij | e4ecf2b | 2014-02-27 14:29:22 +0100 | [diff] [blame] | 10 | #include <linux/module.h> |
Linus Walleij | b5417019 | 2010-07-20 22:32:00 +0100 | [diff] [blame] | 11 | #include <linux/io.h> |
| 12 | #include <linux/slab.h> |
| 13 | #include <linux/leds.h> |
Linus Walleij | e4ecf2b | 2014-02-27 14:29:22 +0100 | [diff] [blame] | 14 | #include <linux/platform_device.h> |
Linus Walleij | b5417019 | 2010-07-20 22:32:00 +0100 | [diff] [blame] | 15 | |
| 16 | struct versatile_led { |
Linus Walleij | e4ecf2b | 2014-02-27 14:29:22 +0100 | [diff] [blame] | 17 | void __iomem *base; |
Linus Walleij | b5417019 | 2010-07-20 22:32:00 +0100 | [diff] [blame] | 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", }, |
Bryan Wu | e031cd5 | 2012-03-13 18:06:36 +0800 | [diff] [blame] | 32 | { "versatile:2", "cpu0" }, |
| 33 | { "versatile:3", "cpu1" }, |
| 34 | { "versatile:4", "cpu2" }, |
| 35 | { "versatile:5", "cpu3" }, |
Linus Walleij | b5417019 | 2010-07-20 22:32:00 +0100 | [diff] [blame] | 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); |
Linus Walleij | e4ecf2b | 2014-02-27 14:29:22 +0100 | [diff] [blame] | 45 | u32 reg = readl(led->base); |
Linus Walleij | b5417019 | 2010-07-20 22:32:00 +0100 | [diff] [blame] | 46 | |
| 47 | if (b != LED_OFF) |
| 48 | reg |= led->mask; |
| 49 | else |
| 50 | reg &= ~led->mask; |
Linus Walleij | e4ecf2b | 2014-02-27 14:29:22 +0100 | [diff] [blame] | 51 | writel(reg, led->base); |
Linus Walleij | b5417019 | 2010-07-20 22:32:00 +0100 | [diff] [blame] | 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); |
Linus Walleij | e4ecf2b | 2014-02-27 14:29:22 +0100 | [diff] [blame] | 58 | u32 reg = readl(led->base); |
Linus Walleij | b5417019 | 2010-07-20 22:32:00 +0100 | [diff] [blame] | 59 | |
| 60 | return (reg & led->mask) ? LED_FULL : LED_OFF; |
| 61 | } |
| 62 | |
Linus Walleij | e4ecf2b | 2014-02-27 14:29:22 +0100 | [diff] [blame] | 63 | static int versatile_leds_probe(struct platform_device *dev) |
Linus Walleij | b5417019 | 2010-07-20 22:32:00 +0100 | [diff] [blame] | 64 | { |
| 65 | int i; |
Linus Walleij | e4ecf2b | 2014-02-27 14:29:22 +0100 | [diff] [blame] | 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); |
Linus Walleij | b5417019 | 2010-07-20 22:32:00 +0100 | [diff] [blame] | 73 | |
Russell King | d1cb3ec | 2013-11-26 19:44:18 +0000 | [diff] [blame] | 74 | /* All off */ |
Linus Walleij | e4ecf2b | 2014-02-27 14:29:22 +0100 | [diff] [blame] | 75 | writel(0, base); |
Linus Walleij | b5417019 | 2010-07-20 22:32:00 +0100 | [diff] [blame] | 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 | |
Linus Walleij | e4ecf2b | 2014-02-27 14:29:22 +0100 | [diff] [blame] | 83 | led->base = base; |
Linus Walleij | b5417019 | 2010-07-20 22:32:00 +0100 | [diff] [blame] | 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 | |
Linus Walleij | e4ecf2b | 2014-02-27 14:29:22 +0100 | [diff] [blame] | 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"); |