/arch/arm/plat-omap/debug-leds.c
C | 317 lines | 217 code | 63 blank | 37 comment | 27 complexity | e0095c2e92fcefd4ca4242beea54a002 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.0
1/* 2 * linux/arch/arm/plat-omap/debug-leds.c 3 * 4 * Copyright 2003 by Texas Instruments Incorporated 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 */ 10 11#include <linux/init.h> 12#include <linux/platform_device.h> 13#include <linux/leds.h> 14#include <linux/io.h> 15 16#include <mach/hardware.h> 17#include <asm/leds.h> 18#include <asm/system.h> 19#include <asm/mach-types.h> 20 21#include <plat/fpga.h> 22#include <mach/gpio.h> 23 24 25/* Many OMAP development platforms reuse the same "debug board"; these 26 * platforms include H2, H3, H4, and Perseus2. There are 16 LEDs on the 27 * debug board (all green), accessed through FPGA registers. 28 * 29 * The "surfer" expansion board and H2 sample board also have two-color 30 * green+red LEDs (in parallel), used here for timer and idle indicators 31 * in preference to the ones on the debug board, for a "Disco LED" effect. 32 * 33 * This driver exports either the original ARM LED API, the new generic 34 * one, or both. 35 */ 36 37static spinlock_t lock; 38static struct h2p2_dbg_fpga __iomem *fpga; 39static u16 led_state, hw_led_state; 40 41 42#ifdef CONFIG_LEDS_OMAP_DEBUG 43#define new_led_api() 1 44#else 45#define new_led_api() 0 46#endif 47 48 49/*-------------------------------------------------------------------------*/ 50 51/* original ARM debug LED API: 52 * - timer and idle leds (some boards use non-FPGA leds here); 53 * - up to 4 generic leds, easily accessed in-kernel (any context) 54 */ 55 56#define GPIO_LED_RED 3 57#define GPIO_LED_GREEN OMAP_MPUIO(4) 58 59#define LED_STATE_ENABLED 0x01 60#define LED_STATE_CLAIMED 0x02 61#define LED_TIMER_ON 0x04 62 63#define GPIO_IDLE GPIO_LED_GREEN 64#define GPIO_TIMER GPIO_LED_RED 65 66static void h2p2_dbg_leds_event(led_event_t evt) 67{ 68 unsigned long flags; 69 70 spin_lock_irqsave(&lock, flags); 71 72 if (!(led_state & LED_STATE_ENABLED) && evt != led_start) 73 goto done; 74 75 switch (evt) { 76 case led_start: 77 if (fpga) 78 led_state |= LED_STATE_ENABLED; 79 break; 80 81 case led_stop: 82 case led_halted: 83 /* all leds off during suspend or shutdown */ 84 85 if (!(machine_is_omap_perseus2() || machine_is_omap_h4())) { 86 gpio_set_value(GPIO_TIMER, 0); 87 gpio_set_value(GPIO_IDLE, 0); 88 } 89 90 __raw_writew(~0, &fpga->leds); 91 led_state &= ~LED_STATE_ENABLED; 92 goto done; 93 94 case led_claim: 95 led_state |= LED_STATE_CLAIMED; 96 hw_led_state = 0; 97 break; 98 99 case led_release: 100 led_state &= ~LED_STATE_CLAIMED; 101 break; 102 103#ifdef CONFIG_LEDS_TIMER 104 case led_timer: 105 led_state ^= LED_TIMER_ON; 106 107 if (machine_is_omap_perseus2() || machine_is_omap_h4()) 108 hw_led_state ^= H2P2_DBG_FPGA_P2_LED_TIMER; 109 else { 110 gpio_set_value(GPIO_TIMER, 111 led_state & LED_TIMER_ON); 112 goto done; 113 } 114 115 break; 116#endif 117 118#ifdef CONFIG_LEDS_CPU 119 /* LED lit iff busy */ 120 case led_idle_start: 121 if (machine_is_omap_perseus2() || machine_is_omap_h4()) 122 hw_led_state &= ~H2P2_DBG_FPGA_P2_LED_IDLE; 123 else { 124 gpio_set_value(GPIO_IDLE, 1); 125 goto done; 126 } 127 128 break; 129 130 case led_idle_end: 131 if (machine_is_omap_perseus2() || machine_is_omap_h4()) 132 hw_led_state |= H2P2_DBG_FPGA_P2_LED_IDLE; 133 else { 134 gpio_set_value(GPIO_IDLE, 0); 135 goto done; 136 } 137 138 break; 139#endif 140 141 case led_green_on: 142 hw_led_state |= H2P2_DBG_FPGA_LED_GREEN; 143 break; 144 case led_green_off: 145 hw_led_state &= ~H2P2_DBG_FPGA_LED_GREEN; 146 break; 147 148 case led_amber_on: 149 hw_led_state |= H2P2_DBG_FPGA_LED_AMBER; 150 break; 151 case led_amber_off: 152 hw_led_state &= ~H2P2_DBG_FPGA_LED_AMBER; 153 break; 154 155 case led_red_on: 156 hw_led_state |= H2P2_DBG_FPGA_LED_RED; 157 break; 158 case led_red_off: 159 hw_led_state &= ~H2P2_DBG_FPGA_LED_RED; 160 break; 161 162 case led_blue_on: 163 hw_led_state |= H2P2_DBG_FPGA_LED_BLUE; 164 break; 165 case led_blue_off: 166 hw_led_state &= ~H2P2_DBG_FPGA_LED_BLUE; 167 break; 168 169 default: 170 break; 171 } 172 173 174 /* 175 * Actually burn the LEDs 176 */ 177 if (led_state & LED_STATE_ENABLED) 178 __raw_writew(~hw_led_state, &fpga->leds); 179 180done: 181 spin_unlock_irqrestore(&lock, flags); 182} 183 184/*-------------------------------------------------------------------------*/ 185 186/* "new" LED API 187 * - with syfs access and generic triggering 188 * - not readily accessible to in-kernel drivers 189 */ 190 191struct dbg_led { 192 struct led_classdev cdev; 193 u16 mask; 194}; 195 196static struct dbg_led dbg_leds[] = { 197 /* REVISIT at least H2 uses different timer & cpu leds... */ 198#ifndef CONFIG_LEDS_TIMER 199 { .mask = 1 << 0, .cdev.name = "d4:green", 200 .cdev.default_trigger = "heartbeat", }, 201#endif 202#ifndef CONFIG_LEDS_CPU 203 { .mask = 1 << 1, .cdev.name = "d5:green", }, /* !idle */ 204#endif 205 { .mask = 1 << 2, .cdev.name = "d6:green", }, 206 { .mask = 1 << 3, .cdev.name = "d7:green", }, 207 208 { .mask = 1 << 4, .cdev.name = "d8:green", }, 209 { .mask = 1 << 5, .cdev.name = "d9:green", }, 210 { .mask = 1 << 6, .cdev.name = "d10:green", }, 211 { .mask = 1 << 7, .cdev.name = "d11:green", }, 212 213 { .mask = 1 << 8, .cdev.name = "d12:green", }, 214 { .mask = 1 << 9, .cdev.name = "d13:green", }, 215 { .mask = 1 << 10, .cdev.name = "d14:green", }, 216 { .mask = 1 << 11, .cdev.name = "d15:green", }, 217 218#ifndef CONFIG_LEDS 219 { .mask = 1 << 12, .cdev.name = "d16:green", }, 220 { .mask = 1 << 13, .cdev.name = "d17:green", }, 221 { .mask = 1 << 14, .cdev.name = "d18:green", }, 222 { .mask = 1 << 15, .cdev.name = "d19:green", }, 223#endif 224}; 225 226static void 227fpga_led_set(struct led_classdev *cdev, enum led_brightness value) 228{ 229 struct dbg_led *led = container_of(cdev, struct dbg_led, cdev); 230 unsigned long flags; 231 232 spin_lock_irqsave(&lock, flags); 233 if (value == LED_OFF) 234 hw_led_state &= ~led->mask; 235 else 236 hw_led_state |= led->mask; 237 __raw_writew(~hw_led_state, &fpga->leds); 238 spin_unlock_irqrestore(&lock, flags); 239} 240 241static void __init newled_init(struct device *dev) 242{ 243 unsigned i; 244 struct dbg_led *led; 245 int status; 246 247 for (i = 0, led = dbg_leds; i < ARRAY_SIZE(dbg_leds); i++, led++) { 248 led->cdev.brightness_set = fpga_led_set; 249 status = led_classdev_register(dev, &led->cdev); 250 if (status < 0) 251 break; 252 } 253 return; 254} 255 256 257/*-------------------------------------------------------------------------*/ 258 259static int /* __init */ fpga_probe(struct platform_device *pdev) 260{ 261 struct resource *iomem; 262 263 spin_lock_init(&lock); 264 265 iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 266 if (!iomem) 267 return -ENODEV; 268 269 fpga = ioremap(iomem->start, H2P2_DBG_FPGA_SIZE); 270 __raw_writew(~0, &fpga->leds); 271 272#ifdef CONFIG_LEDS 273 leds_event = h2p2_dbg_leds_event; 274 leds_event(led_start); 275#endif 276 277 if (new_led_api()) { 278 newled_init(&pdev->dev); 279 } 280 281 return 0; 282} 283 284static int fpga_suspend_noirq(struct device *dev) 285{ 286 __raw_writew(~0, &fpga->leds); 287 return 0; 288} 289 290static int fpga_resume_noirq(struct device *dev) 291{ 292 __raw_writew(~hw_led_state, &fpga->leds); 293 return 0; 294} 295 296static const struct dev_pm_ops fpga_dev_pm_ops = { 297 .suspend_noirq = fpga_suspend_noirq, 298 .resume_noirq = fpga_resume_noirq, 299}; 300 301static struct platform_driver led_driver = { 302 .driver.name = "omap_dbg_led", 303 .driver.pm = &fpga_dev_pm_ops, 304 .probe = fpga_probe, 305}; 306 307static int __init fpga_init(void) 308{ 309 if (machine_is_omap_h4() 310 || machine_is_omap_h3() 311 || machine_is_omap_h2() 312 || machine_is_omap_perseus2() 313 ) 314 return platform_driver_register(&led_driver); 315 return 0; 316} 317fs_initcall(fpga_init);