| 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 |