Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* |
Bryan Wu | 95f4965 | 2012-03-14 01:50:00 +0800 | [diff] [blame] | 2 | * Driver for the 4 user LEDs found on the Integrator AP/CP baseboard |
| 3 | * Based on Versatile and RealView machine LED code |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 4 | * |
Bryan Wu | 95f4965 | 2012-03-14 01:50:00 +0800 | [diff] [blame] | 5 | * License terms: GNU General Public License (GPL) version 2 |
| 6 | * Author: Bryan Wu <bryan.wu@canonical.com> |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 7 | */ |
| 8 | #include <linux/kernel.h> |
| 9 | #include <linux/init.h> |
Russell King | fced80c | 2008-09-06 12:10:45 +0100 | [diff] [blame] | 10 | #include <linux/io.h> |
Bryan Wu | 95f4965 | 2012-03-14 01:50:00 +0800 | [diff] [blame] | 11 | #include <linux/slab.h> |
| 12 | #include <linux/leds.h> |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 13 | |
Linus Walleij | 1b1ef75 | 2014-02-13 21:26:24 +0100 | [diff] [blame] | 14 | #include "hardware.h" |
Linus Walleij | bb4dbef | 2013-06-16 02:44:27 +0200 | [diff] [blame] | 15 | #include "cm.h" |
| 16 | |
Bryan Wu | 95f4965 | 2012-03-14 01:50:00 +0800 | [diff] [blame] | 17 | #if defined(CONFIG_NEW_LEDS) && defined(CONFIG_LEDS_CLASS) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 18 | |
Bryan Wu | 95f4965 | 2012-03-14 01:50:00 +0800 | [diff] [blame] | 19 | #define ALPHA_REG __io_address(INTEGRATOR_DBG_BASE) |
| 20 | #define LEDREG (__io_address(INTEGRATOR_DBG_BASE) + INTEGRATOR_DBG_LEDS_OFFSET) |
| 21 | |
| 22 | struct integrator_led { |
| 23 | struct led_classdev cdev; |
| 24 | u8 mask; |
| 25 | }; |
| 26 | |
| 27 | /* |
| 28 | * The triggers lines up below will only be used if the |
| 29 | * LED triggers are compiled in. |
| 30 | */ |
| 31 | static const struct { |
| 32 | const char *name; |
| 33 | const char *trigger; |
| 34 | } integrator_leds[] = { |
| 35 | { "integrator:green0", "heartbeat", }, |
| 36 | { "integrator:yellow", }, |
| 37 | { "integrator:red", }, |
| 38 | { "integrator:green1", }, |
| 39 | { "integrator:core_module", "cpu0", }, |
| 40 | }; |
| 41 | |
| 42 | static void integrator_led_set(struct led_classdev *cdev, |
| 43 | enum led_brightness b) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 44 | { |
Bryan Wu | 95f4965 | 2012-03-14 01:50:00 +0800 | [diff] [blame] | 45 | struct integrator_led *led = container_of(cdev, |
| 46 | struct integrator_led, cdev); |
| 47 | u32 reg = __raw_readl(LEDREG); |
Russell King | 1f9c381 | 2005-05-03 12:22:19 +0100 | [diff] [blame] | 48 | |
Bryan Wu | 95f4965 | 2012-03-14 01:50:00 +0800 | [diff] [blame] | 49 | if (b != LED_OFF) |
| 50 | reg |= led->mask; |
| 51 | else |
| 52 | reg &= ~led->mask; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 53 | |
Bryan Wu | 95f4965 | 2012-03-14 01:50:00 +0800 | [diff] [blame] | 54 | while (__raw_readl(ALPHA_REG) & 1) |
| 55 | cpu_relax(); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 56 | |
Bryan Wu | 95f4965 | 2012-03-14 01:50:00 +0800 | [diff] [blame] | 57 | __raw_writel(reg, LEDREG); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 58 | } |
| 59 | |
Bryan Wu | 95f4965 | 2012-03-14 01:50:00 +0800 | [diff] [blame] | 60 | static enum led_brightness integrator_led_get(struct led_classdev *cdev) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 61 | { |
Bryan Wu | 95f4965 | 2012-03-14 01:50:00 +0800 | [diff] [blame] | 62 | struct integrator_led *led = container_of(cdev, |
| 63 | struct integrator_led, cdev); |
| 64 | u32 reg = __raw_readl(LEDREG); |
| 65 | |
| 66 | return (reg & led->mask) ? LED_FULL : LED_OFF; |
| 67 | } |
| 68 | |
| 69 | static void cm_led_set(struct led_classdev *cdev, |
| 70 | enum led_brightness b) |
| 71 | { |
| 72 | if (b != LED_OFF) |
| 73 | cm_control(CM_CTRL_LED, CM_CTRL_LED); |
| 74 | else |
| 75 | cm_control(CM_CTRL_LED, 0); |
| 76 | } |
| 77 | |
| 78 | static enum led_brightness cm_led_get(struct led_classdev *cdev) |
| 79 | { |
Linus Walleij | fb61f86 | 2013-10-10 14:11:18 +0200 | [diff] [blame] | 80 | u32 reg = cm_get(); |
Bryan Wu | 95f4965 | 2012-03-14 01:50:00 +0800 | [diff] [blame] | 81 | |
| 82 | return (reg & CM_CTRL_LED) ? LED_FULL : LED_OFF; |
| 83 | } |
| 84 | |
| 85 | static int __init integrator_leds_init(void) |
| 86 | { |
| 87 | int i; |
| 88 | |
| 89 | for (i = 0; i < ARRAY_SIZE(integrator_leds); i++) { |
| 90 | struct integrator_led *led; |
| 91 | |
| 92 | led = kzalloc(sizeof(*led), GFP_KERNEL); |
| 93 | if (!led) |
| 94 | break; |
| 95 | |
| 96 | |
| 97 | led->cdev.name = integrator_leds[i].name; |
| 98 | |
| 99 | if (i == 4) { /* Setting for LED in core module */ |
| 100 | led->cdev.brightness_set = cm_led_set; |
| 101 | led->cdev.brightness_get = cm_led_get; |
| 102 | } else { |
| 103 | led->cdev.brightness_set = integrator_led_set; |
| 104 | led->cdev.brightness_get = integrator_led_get; |
| 105 | } |
| 106 | |
| 107 | led->cdev.default_trigger = integrator_leds[i].trigger; |
| 108 | led->mask = BIT(i); |
| 109 | |
| 110 | if (led_classdev_register(NULL, &led->cdev) < 0) { |
| 111 | kfree(led); |
| 112 | break; |
| 113 | } |
| 114 | } |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 115 | |
| 116 | return 0; |
| 117 | } |
| 118 | |
Bryan Wu | 95f4965 | 2012-03-14 01:50:00 +0800 | [diff] [blame] | 119 | /* |
| 120 | * Since we may have triggers on any subsystem, defer registration |
| 121 | * until after subsystem_init. |
| 122 | */ |
| 123 | fs_initcall(integrator_leds_init); |
| 124 | #endif |