/* * linux/arch/arm/plat-omap/debug-leds.c * * Copyright 2003 by Texas Instruments Incorporated * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include <linux/init.h> #include <linux/platform_device.h> #include <linux/leds.h> #include <linux/io.h> #include <mach/hardware.h> #include <asm/leds.h> #include <asm/system.h> #include <asm/mach-types.h> #include <plat/fpga.h> #include <mach/gpio.h> /* Many OMAP development platforms reuse the same "debug board"; these * platforms include H2, H3, H4, and Perseus2. There are 16 LEDs on the * debug board (all green), accessed through FPGA registers. * * The "surfer" expansion board and H2 sample board also have two-color * green+red LEDs (in parallel), used here for timer and idle indicators * in preference to the ones on the debug board, for a "Disco LED" effect. * * This driver exports either the original ARM LED API, the new generic * one, or both. */ static spinlock_t lock; static struct h2p2_dbg_fpga __iomem *fpga; static u16 led_state, hw_led_state; #ifdef CONFIG_LEDS_OMAP_DEBUG #define new_led_api() 1 #else #define new_led_api() 0 #endif /*-------------------------------------------------------------------------*/ /* original ARM debug LED API: * - timer and idle leds (some boards use non-FPGA leds here); * - up to 4 generic leds, easily accessed in-kernel (any context) */ #define GPIO_LED_RED 3 #define GPIO_LED_GREEN OMAP_MPUIO(4) #define LED_STATE_ENABLED 0x01 #define LED_STATE_CLAIMED 0x02 #define LED_TIMER_ON 0x04 #define GPIO_IDLE GPIO_LED_GREEN #define GPIO_TIMER GPIO_LED_RED static void h2p2_dbg_leds_event(led_event_t evt) { unsigned long flags; spin_lock_irqsave(&lock, flags); if (!(led_state & LED_STATE_ENABLED) && evt != led_start) goto done; switch (evt) { case led_start: if (fpga) led_state |= LED_STATE_ENABLED; break; case led_stop: case led_halted: /* all leds off during suspend or shutdown */ if (!(machine_is_omap_perseus2() || machine_is_omap_h4())) { gpio_set_value(GPIO_TIMER, 0); gpio_set_value(GPIO_IDLE, 0); } __raw_writew(~0, &fpga->leds); led_state &= ~LED_STATE_ENABLED; goto done; case led_claim: led_state |= LED_STATE_CLAIMED; hw_led_state = 0; break; case led_release: led_state &= ~LED_STATE_CLAIMED; break; #ifdef CONFIG_LEDS_TIMER case led_timer: led_state ^= LED_TIMER_ON; if (machine_is_omap_perseus2() || machine_is_omap_h4()) hw_led_state ^= H2P2_DBG_FPGA_P2_LED_TIMER; else { gpio_set_value(GPIO_TIMER, led_state & LED_TIMER_ON); goto done; } break; #endif #ifdef CONFIG_LEDS_CPU /* LED lit iff busy */ case led_idle_start: if (machine_is_omap_perseus2() || machine_is_omap_h4()) hw_led_state &= ~H2P2_DBG_FPGA_P2_LED_IDLE; else { gpio_set_value(GPIO_IDLE, 1); goto done; } break; case led_idle_end: if (machine_is_omap_perseus2() || machine_is_omap_h4()) hw_led_state |= H2P2_DBG_FPGA_P2_LED_IDLE; else { gpio_set_value(GPIO_IDLE, 0); goto done; } break; #endif case led_green_on: hw_led_state |= H2P2_DBG_FPGA_LED_GREEN; break; case led_green_off: hw_led_state &= ~H2P2_DBG_FPGA_LED_GREEN; break; case led_amber_on: hw_led_state |= H2P2_DBG_FPGA_LED_AMBER; break; case led_amber_off: hw_led_state &= ~H2P2_DBG_FPGA_LED_AMBER; break; case led_red_on: hw_led_state |= H2P2_DBG_FPGA_LED_RED; break; case led_red_off: hw_led_state &= ~H2P2_DBG_FPGA_LED_RED; break; case led_blue_on: hw_led_state |= H2P2_DBG_FPGA_LED_BLUE; break; case led_blue_off: hw_led_state &= ~H2P2_DBG_FPGA_LED_BLUE; break; default: break; } /* * Actually burn the LEDs */ if (led_state & LED_STATE_ENABLED) __raw_writew(~hw_led_state, &fpga->leds); done: spin_unlock_irqrestore(&lock, flags); } /*-------------------------------------------------------------------------*/ /* "new" LED API * - with syfs access and generic triggering * - not readily accessible to in-kernel drivers */ struct dbg_led { struct led_classdev cdev; u16 mask; }; static struct dbg_led dbg_leds[] = { /* REVISIT at least H2 uses different timer & cpu leds... */ #ifndef CONFIG_LEDS_TIMER { .mask = 1 << 0, .cdev.name = "d4:green", .cdev.default_trigger = "heartbeat", }, #endif #ifndef CONFIG_LEDS_CPU { .mask = 1 << 1, .cdev.name = "d5:green", }, /* !idle */ #endif { .mask = 1 << 2, .cdev.name = "d6:green", }, { .mask = 1 << 3, .cdev.name = "d7:green", }, { .mask = 1 << 4, .cdev.name = "d8:green", }, { .mask = 1 << 5, .cdev.name = "d9:green", }, { .mask = 1 << 6, .cdev.name = "d10:green", }, { .mask = 1 << 7, .cdev.name = "d11:green", }, { .mask = 1 << 8, .cdev.name = "d12:green", }, { .mask = 1 << 9, .cdev.name = "d13:green", }, { .mask = 1 << 10, .cdev.name = "d14:green", }, { .mask = 1 << 11, .cdev.name = "d15:green", }, #ifndef CONFIG_LEDS { .mask = 1 << 12, .cdev.name = "d16:green", }, { .mask = 1 << 13, .cdev.name = "d17:green", }, { .mask = 1 << 14, .cdev.name = "d18:green", }, { .mask = 1 << 15, .cdev.name = "d19:green", }, #endif }; static void fpga_led_set(struct led_classdev *cdev, enum led_brightness value) { struct dbg_led *led = container_of(cdev, struct dbg_led, cdev); unsigned long flags; spin_lock_irqsave(&lock, flags); if (value == LED_OFF) hw_led_state &= ~led->mask; else hw_led_state |= led->mask; __raw_writew(~hw_led_state, &fpga->leds); spin_unlock_irqrestore(&lock, flags); } static void __init newled_init(struct device *dev) { unsigned i; struct dbg_led *led; int status; for (i = 0, led = dbg_leds; i < ARRAY_SIZE(dbg_leds); i++, led++) { led->cdev.brightness_set = fpga_led_set; status = led_classdev_register(dev, &led->cdev); if (status < 0) break; } return; } /*-------------------------------------------------------------------------*/ static int /* __init */ fpga_probe(struct platform_device *pdev) { struct resource *iomem; spin_lock_init(&lock); iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!iomem) return -ENODEV; fpga = ioremap(iomem->start, H2P2_DBG_FPGA_SIZE); __raw_writew(~0, &fpga->leds); #ifdef CONFIG_LEDS leds_event = h2p2_dbg_leds_event; leds_event(led_start); #endif if (new_led_api()) { newled_init(&pdev->dev); } return 0; } static int fpga_suspend_noirq(struct device *dev) { __raw_writew(~0, &fpga->leds); return 0; } static int fpga_resume_noirq(struct device *dev) { __raw_writew(~hw_led_state, &fpga->leds); return 0; } static const struct dev_pm_ops fpga_dev_pm_ops = { .suspend_noirq = fpga_suspend_noirq, .resume_noirq = fpga_resume_noirq, }; static struct platform_driver led_driver = { .driver.name = "omap_dbg_led", .driver.pm = &fpga_dev_pm_ops, .probe = fpga_probe, }; static int __init fpga_init(void) { if (machine_is_omap_h4() || machine_is_omap_h3() || machine_is_omap_h2() || machine_is_omap_perseus2() ) return platform_driver_register(&led_driver); return 0; } fs_initcall(fpga_init);