Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2 | * DIGITAL Shark LED control routines. |
| 3 | * |
Bryan Wu | 408a4b2 | 2012-03-14 01:31:30 +0800 | [diff] [blame] | 4 | * Driver for the 3 user LEDs found on the Shark |
| 5 | * Based on Versatile and RealView machine LED code |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 6 | * |
Bryan Wu | 408a4b2 | 2012-03-14 01:31:30 +0800 | [diff] [blame] | 7 | * License terms: GNU General Public License (GPL) version 2 |
| 8 | * Author: Bryan Wu <bryan.wu@canonical.com> |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 9 | */ |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 10 | #include <linux/kernel.h> |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 11 | #include <linux/init.h> |
Russell King | fced80c | 2008-09-06 12:10:45 +0100 | [diff] [blame] | 12 | #include <linux/io.h> |
Bryan Wu | 408a4b2 | 2012-03-14 01:31:30 +0800 | [diff] [blame] | 13 | #include <linux/ioport.h> |
| 14 | #include <linux/slab.h> |
| 15 | #include <linux/leds.h> |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 16 | |
Bryan Wu | 408a4b2 | 2012-03-14 01:31:30 +0800 | [diff] [blame] | 17 | #include <asm/mach-types.h> |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 18 | |
Bryan Wu | 408a4b2 | 2012-03-14 01:31:30 +0800 | [diff] [blame] | 19 | #if defined(CONFIG_NEW_LEDS) && defined(CONFIG_LEDS_CLASS) |
| 20 | struct shark_led { |
| 21 | struct led_classdev cdev; |
| 22 | u8 mask; |
| 23 | }; |
Alexander Schulz | eab184c | 2009-01-08 18:05:58 +0100 | [diff] [blame] | 24 | |
Bryan Wu | 408a4b2 | 2012-03-14 01:31:30 +0800 | [diff] [blame] | 25 | /* |
| 26 | * The triggers lines up below will only be used if the |
| 27 | * LED triggers are compiled in. |
| 28 | */ |
| 29 | static const struct { |
| 30 | const char *name; |
| 31 | const char *trigger; |
| 32 | } shark_leds[] = { |
| 33 | { "shark:amber0", "default-on", }, /* Bit 5 */ |
| 34 | { "shark:green", "heartbeat", }, /* Bit 6 */ |
| 35 | { "shark:amber1", "cpu0" }, /* Bit 7 */ |
| 36 | }; |
Alexander Schulz | eab184c | 2009-01-08 18:05:58 +0100 | [diff] [blame] | 37 | |
Bryan Wu | 408a4b2 | 2012-03-14 01:31:30 +0800 | [diff] [blame] | 38 | static u16 led_reg_read(void) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 39 | { |
Bryan Wu | 408a4b2 | 2012-03-14 01:31:30 +0800 | [diff] [blame] | 40 | outw(0x09, 0x24); |
| 41 | return inw(0x26); |
| 42 | } |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 43 | |
Bryan Wu | 408a4b2 | 2012-03-14 01:31:30 +0800 | [diff] [blame] | 44 | static void led_reg_write(u16 value) |
| 45 | { |
| 46 | outw(0x09, 0x24); |
| 47 | outw(value, 0x26); |
| 48 | } |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 49 | |
Bryan Wu | 408a4b2 | 2012-03-14 01:31:30 +0800 | [diff] [blame] | 50 | static void shark_led_set(struct led_classdev *cdev, |
| 51 | enum led_brightness b) |
| 52 | { |
| 53 | struct shark_led *led = container_of(cdev, |
| 54 | struct shark_led, cdev); |
| 55 | u16 reg = led_reg_read(); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 56 | |
Bryan Wu | 408a4b2 | 2012-03-14 01:31:30 +0800 | [diff] [blame] | 57 | if (b != LED_OFF) |
| 58 | reg |= led->mask; |
| 59 | else |
| 60 | reg &= ~led->mask; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 61 | |
Bryan Wu | 408a4b2 | 2012-03-14 01:31:30 +0800 | [diff] [blame] | 62 | led_reg_write(reg); |
| 63 | } |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 64 | |
Bryan Wu | 408a4b2 | 2012-03-14 01:31:30 +0800 | [diff] [blame] | 65 | static enum led_brightness shark_led_get(struct led_classdev *cdev) |
| 66 | { |
| 67 | struct shark_led *led = container_of(cdev, |
| 68 | struct shark_led, cdev); |
| 69 | u16 reg = led_reg_read(); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 70 | |
Bryan Wu | 408a4b2 | 2012-03-14 01:31:30 +0800 | [diff] [blame] | 71 | return (reg & led->mask) ? LED_FULL : LED_OFF; |
| 72 | } |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 73 | |
Bryan Wu | 408a4b2 | 2012-03-14 01:31:30 +0800 | [diff] [blame] | 74 | static int __init shark_leds_init(void) |
| 75 | { |
| 76 | int i; |
| 77 | u16 reg; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 78 | |
Bryan Wu | 408a4b2 | 2012-03-14 01:31:30 +0800 | [diff] [blame] | 79 | if (!machine_is_shark()) |
| 80 | return -ENODEV; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 81 | |
Bryan Wu | 408a4b2 | 2012-03-14 01:31:30 +0800 | [diff] [blame] | 82 | for (i = 0; i < ARRAY_SIZE(shark_leds); i++) { |
| 83 | struct shark_led *led; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 84 | |
Bryan Wu | 408a4b2 | 2012-03-14 01:31:30 +0800 | [diff] [blame] | 85 | led = kzalloc(sizeof(*led), GFP_KERNEL); |
| 86 | if (!led) |
| 87 | break; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 88 | |
Bryan Wu | 408a4b2 | 2012-03-14 01:31:30 +0800 | [diff] [blame] | 89 | led->cdev.name = shark_leds[i].name; |
| 90 | led->cdev.brightness_set = shark_led_set; |
| 91 | led->cdev.brightness_get = shark_led_get; |
| 92 | led->cdev.default_trigger = shark_leds[i].trigger; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 93 | |
Bryan Wu | 408a4b2 | 2012-03-14 01:31:30 +0800 | [diff] [blame] | 94 | /* Count in 5 bits offset */ |
| 95 | led->mask = BIT(i + 5); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 96 | |
Bryan Wu | 408a4b2 | 2012-03-14 01:31:30 +0800 | [diff] [blame] | 97 | if (led_classdev_register(NULL, &led->cdev) < 0) { |
| 98 | kfree(led); |
| 99 | break; |
| 100 | } |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 101 | } |
| 102 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 103 | /* Make LEDs independent of power-state */ |
Bryan Wu | 408a4b2 | 2012-03-14 01:31:30 +0800 | [diff] [blame] | 104 | request_region(0x24, 4, "led_reg"); |
| 105 | reg = led_reg_read(); |
| 106 | reg |= 1 << 10; |
| 107 | led_reg_write(reg); |
| 108 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 109 | return 0; |
| 110 | } |
| 111 | |
Bryan Wu | 408a4b2 | 2012-03-14 01:31:30 +0800 | [diff] [blame] | 112 | /* |
| 113 | * Since we may have triggers on any subsystem, defer registration |
| 114 | * until after subsystem_init. |
| 115 | */ |
| 116 | fs_initcall(shark_leds_init); |
| 117 | #endif |