RK2818 PM: Enable wake-up events for USB ports
author黄涛 <huangtao@rock-chips.com>
Thu, 5 Aug 2010 04:01:16 +0000 (12:01 +0800)
committer黄涛 <huangtao@rock-chips.com>
Thu, 5 Aug 2010 04:01:16 +0000 (12:01 +0800)
arch/arm/configs/rk2818_mid_defconfig
arch/arm/configs/rk2818_phone_defconfig
arch/arm/configs/rk2818_raho_defconfig
arch/arm/mach-rk2818/Kconfig
arch/arm/mach-rk2818/pm.c
arch/arm/mach-rk2818/timer.c

index cf07eb912960196ac7fc4bc7df133bf24a760b9d..25aae0d2442b4342241d638be416e29f88f7c377 100644 (file)
@@ -204,6 +204,7 @@ CONFIG_MACH_RK2818MID=y
 # CONFIG_MACH_RAHO is not set
 CONFIG_RK28_GPIO_IRQ=16
 CONFIG_RK28_ADC=y
+CONFIG_RK28_USB_WAKE=y
 
 #
 # Processor Type
index 0c5fc7f7bd506f7760a94f1d65a35570c1566c40..6e061986913f6c781ee415736601f35a75ea2ec6 100644 (file)
@@ -204,6 +204,7 @@ CONFIG_MACH_RK2818PHONE=y
 # CONFIG_MACH_RAHO is not set
 CONFIG_RK28_GPIO_IRQ=16
 CONFIG_RK28_ADC=y
+CONFIG_RK28_USB_WAKE=y
 
 #
 # Processor Type
index 092b58e09fe73bd5284c952e8b83d81f1f5e5f3d..0c279cf7cfb5f54f990e55764bad7b3ae70def9f 100644 (file)
@@ -204,6 +204,7 @@ CONFIG_ARCH_RK2818=y
 CONFIG_MACH_RAHO=y
 CONFIG_RK28_GPIO_IRQ=16
 CONFIG_RK28_ADC=y
+CONFIG_RK28_USB_WAKE=y
 
 #
 # Processor Type
index fc7ed47d09228770502acb94ed08efc16bffa58c..76b14ae042dfb978c38565076c69a0fa3087b1c9 100644 (file)
@@ -34,4 +34,13 @@ config RK28_ADC
        default y
        ---help---
                RK28 ADC Driver
+
+config RK28_USB_WAKE
+       bool "Enable wake-up events for USB ports"
+       depends on ARCH_RK2818 && PM
+       default y
+       help
+         Select this option if you want to have your system wake up
+         from USB.
+
 endif
index 765fb8b746e7ac3b209e3dc2d71caad8aa8bb0e6..a3a8f40dd39c9ee8489b54d8be5943c202994c35 100644 (file)
 #include <mach/iomux.h>
 #include <mach/gpio.h>
 
+extern void rockchip_timer_clocksource_suspend_resume(int suspend);
+extern int rockchip_timer_clocksource_irq_checkandclear(void);
+
+#ifdef CONFIG_DWC_OTG_HOST_ONLY
+static int rk28_usb_suspend(int exitsuspend) { return 0; }
+static int rk28_usb_check_vbus_change(void) { return 0; }
+#else
+extern int rk28_usb_suspend(int exitsuspend);
+extern int rk28_usb_check_vbus_change(void);
+#endif
+#ifdef CONFIG_DWC_OTG_BOTH_HOST_SLAVE
+extern int rk28_usb_check_connectid_change(void);
+#else
+static int rk28_usb_check_connectid_change(void) { return 0; }
+#endif
+
 #define regfile_readl(offset)  readl(RK2818_REGFILE_BASE + offset)
 
 #define scu_readl(offset)      readl(RK2818_SCU_BASE + offset)
@@ -197,9 +213,32 @@ static void rk2818_idle(void)
 
 static int rk2818_pm_enter(suspend_state_t state)
 {
-       rk2818_idle();
-       __udelay(40);
+       int irq_val = 0;
+
+       printk(KERN_DEBUG "before core halt\n");
+
+#ifdef CONFIG_RK28_USB_WAKE
+       rockchip_timer_clocksource_suspend_resume(1);
+
+       while (!irq_val) {
+               rk28_usb_suspend(0);
+#endif
+
+               rk2818_idle();
+
+#ifdef CONFIG_RK28_USB_WAKE
+               rk28_usb_suspend(1);
+               __udelay(400);
+
+               irq_val = rockchip_timer_clocksource_irq_checkandclear();
+               irq_val |= rk28_usb_check_vbus_change();
+               irq_val |= rk28_usb_check_connectid_change();
+       }
+
+       rockchip_timer_clocksource_suspend_resume(0);
+#endif
 
+       printk(KERN_DEBUG "quit arm halt,irq_val=0x%x\n", irq_val);
        return 0;
 }
 
index 2caa74d4c90efd227655e620c9a5ee5e4c12c58e..c54db294b2adfd4ca029d502e31f1f0b0b012eec 100644 (file)
 #include <asm/mach/time.h>
 #include <mach/rk2818_iomap.h>
 
-#define RK2818_TIMER1_BASE      RK2818_TIMER_BASE
-#define RK2818_TIMER2_BASE      RK2818_TIMER_BASE + 0x14
-#define RK2818_TIMER3_BASE      RK2818_TIMER_BASE + 0x28
-
 #define TIMER_LOAD_COUNT       0x0000
 #define TIMER_CUR_VALUE                0x0004
 #define TIMER_CONTROL_REG      0x0008
 #define TIMER_ENABLE                   3
 #define TIMER_ENABLE_FREE_RUNNING      1
 
+#define CHECK_VBUS_MS                  1000    /* ms */
+
+#define RK_TIMER_ENABLE(n)             writel(TIMER_ENABLE, RK2818_TIMER_BASE + 0x14 * (n - 1) + TIMER_CONTROL_REG)
+#define RK_TIMER_ENABLE_FREE_RUNNING(n)        writel(TIMER_ENABLE_FREE_RUNNING, RK2818_TIMER_BASE + 0x14 * (n - 1) + TIMER_CONTROL_REG)
+#define RK_TIMER_DISABLE(n)            writel(TIMER_DISABLE, RK2818_TIMER_BASE + 0x14 * (n - 1) + TIMER_CONTROL_REG)
+
+#define RK_TIMER_SETCOUNT(n, count)    writel(count, RK2818_TIMER_BASE + 0x14 * (n - 1) + TIMER_LOAD_COUNT)
+#define RK_TIMER_GETCOUNT(n)           readl(RK2818_TIMER_BASE + 0x14 * (n - 1) + TIMER_LOAD_COUNT)
+
+#define RK_TIMER_READVALUE(n)          readl(RK2818_TIMER_BASE + 0x14 * (n - 1) + TIMER_CUR_VALUE)
+#define RK_TIMER_INT_CLEAR(n)          readl(RK2818_TIMER_BASE + 0x14 * (n - 1) + TIMER_EOI)
+
+#define TIMER_CLKEVT                   2       /* timer2 */
+#define IRQ_NR_TIMER_CLKEVT            IRQ_NR_TIMER2
+#define TIMER_CLKEVT_NAME              "timer2"
+
+#define TIMER_CLKSRC                   3       /* timer3 */
+#define IRQ_NR_TIMER_CLKSRC            IRQ_NR_TIMER3
+#define TIMER_CLKSRC_NAME              "timer3"
+
 static struct clk *timer_clk;
-static volatile unsigned long timer_mult;
+static volatile unsigned long timer_mult; /* timer count = cycle * timer_mult */
 
 static int rk2818_timer_set_next_event(unsigned long cycles, struct clock_event_device *evt)
 {
-       writel(TIMER_DISABLE, RK2818_TIMER2_BASE + TIMER_CONTROL_REG);
-       writel(cycles * timer_mult, RK2818_TIMER2_BASE + TIMER_LOAD_COUNT);
-       writel(TIMER_ENABLE, RK2818_TIMER2_BASE + TIMER_CONTROL_REG);
+       RK_TIMER_DISABLE(TIMER_CLKEVT);
+       RK_TIMER_SETCOUNT(TIMER_CLKEVT, cycles * timer_mult);
+       RK_TIMER_ENABLE(TIMER_CLKEVT);
 
        return 0;
 }
@@ -60,13 +76,13 @@ static void rk2818_timer_set_mode(enum clock_event_mode mode, struct clock_event
                break;
        case CLOCK_EVT_MODE_UNUSED:
        case CLOCK_EVT_MODE_SHUTDOWN:
-               writel(TIMER_DISABLE, RK2818_TIMER2_BASE + TIMER_CONTROL_REG);
+               RK_TIMER_DISABLE(TIMER_CLKEVT);
                break;
        }
 }
 
-static struct clock_event_device clockenent_timer2 = {
-       .name           = "timer2",
+static struct clock_event_device rk2818_timer_clockevent = {
+       .name           = TIMER_CLKEVT_NAME,
        .features       = CLOCK_EVT_FEAT_ONESHOT,
        .shift          = 32,
        .rating         = 200,
@@ -74,24 +90,24 @@ static struct clock_event_device clockenent_timer2 = {
        .set_mode       = rk2818_timer_set_mode,
 };
 
-static irqreturn_t rk2818_timer2_interrupt(int irq, void *dev_id)
+static irqreturn_t rk2818_timer_clockevent_interrupt(int irq, void *dev_id)
 {
        struct clock_event_device *evt = dev_id;
 
-       readl(RK2818_TIMER2_BASE + TIMER_EOI);
-       writel(TIMER_DISABLE, RK2818_TIMER2_BASE + TIMER_CONTROL_REG);
+       RK_TIMER_INT_CLEAR(TIMER_CLKEVT);
+       RK_TIMER_DISABLE(TIMER_CLKEVT);
 
        evt->event_handler(evt);
 
        return IRQ_HANDLED;
 }
 
-static struct irqaction rk2818_timer2_irq = {
-       .name           = "timer2",
+static struct irqaction rk2818_timer_clockevent_irq = {
+       .name           = TIMER_CLKEVT_NAME,
        .flags          = IRQF_DISABLED | IRQF_TIMER,
-       .handler        = rk2818_timer2_interrupt,
-       .irq            = IRQ_NR_TIMER2,
-       .dev_id         = &clockenent_timer2,
+       .handler        = rk2818_timer_clockevent_interrupt,
+       .irq            = IRQ_NR_TIMER_CLKEVT,
+       .dev_id         = &rk2818_timer_clockevent,
 };
 
 static int rk2818_timer_cpufreq_notifier(struct notifier_block *nb, unsigned long val, void *data)
@@ -118,18 +134,18 @@ arch_initcall_sync(rk2818_timer_init_cpufreq);
 
 static __init int rk2818_timer_init_clockevent(void)
 {
-       struct clock_event_device *ce = &clockenent_timer2;
+       struct clock_event_device *ce = &rk2818_timer_clockevent;
 
        timer_clk = clk_get(NULL, "timer");
        timer_mult = clk_get_rate(timer_clk) / 1000000;
 
-       writel(TIMER_DISABLE, RK2818_TIMER2_BASE + TIMER_CONTROL_REG);
+       RK_TIMER_DISABLE(TIMER_CLKEVT);
 
-       setup_irq(rk2818_timer2_irq.irq, &rk2818_timer2_irq);
+       setup_irq(rk2818_timer_clockevent_irq.irq, &rk2818_timer_clockevent_irq);
 
        ce->mult = div_sc(1000000, NSEC_PER_SEC, ce->shift);
        ce->max_delta_ns = clockevent_delta2ns(0xFFFFFFFFUL / 256, ce); // max pclk < 256MHz
-       ce->min_delta_ns = clockevent_delta2ns(1, ce);
+       ce->min_delta_ns = clockevent_delta2ns(1, ce) + 1;
 
        ce->cpumask = cpumask_of(0);
 
@@ -138,32 +154,50 @@ static __init int rk2818_timer_init_clockevent(void)
        return 0;
 }
 
-static cycle_t rk2818_timer3_read(struct clocksource *cs)
+static cycle_t rk2818_timer_read(struct clocksource *cs)
 {
-       return ~readl(RK2818_TIMER3_BASE + TIMER_CUR_VALUE);
+       return ~RK_TIMER_READVALUE(TIMER_CLKSRC);
 }
 
-static struct clocksource clocksource_timer3 = {
-       .name           = "timer3",
+static struct clocksource rk2818_timer_clocksource = {
+       .name           = TIMER_CLKSRC_NAME,
        .rating         = 200,
-       .read           = rk2818_timer3_read,
+       .read           = rk2818_timer_read,
        .mask           = CLOCKSOURCE_MASK(32),
        .shift          = 26,
        .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
 };
 
+#ifdef CONFIG_RK28_USB_WAKE
+static irqreturn_t rk2818_timer_clocksource_interrupt(int irq, void *dev_id)
+{
+       RK_TIMER_INT_CLEAR(TIMER_CLKSRC);
+       return IRQ_HANDLED;
+}
+
+static struct irqaction rk2818_timer_clocksource_irq = {
+       .name           = TIMER_CLKSRC_NAME,
+       .handler        = rk2818_timer_clocksource_interrupt,
+       .irq            = IRQ_NR_TIMER_CLKSRC,
+};
+#endif
+
 static void __init rk2818_timer_init_clocksource(void)
 {
        static char err[] __initdata = KERN_ERR "%s: can't register clocksource!\n";
-       struct clocksource *cs = &clocksource_timer3;
+       struct clocksource *cs = &rk2818_timer_clocksource;
 
-       writel(TIMER_DISABLE, RK2818_TIMER3_BASE + TIMER_CONTROL_REG);
-       writel(0xFFFFFFFF, RK2818_TIMER3_BASE + TIMER_LOAD_COUNT);
-       writel(TIMER_ENABLE_FREE_RUNNING, RK2818_TIMER3_BASE + TIMER_CONTROL_REG);
+       RK_TIMER_DISABLE(TIMER_CLKSRC);
+       RK_TIMER_SETCOUNT(TIMER_CLKSRC, 0xFFFFFFFF);
+       RK_TIMER_ENABLE_FREE_RUNNING(TIMER_CLKSRC);
 
        cs->mult = clocksource_hz2mult(24000000, cs->shift);
        if (clocksource_register(cs))
                printk(err, cs->name);
+
+#ifdef CONFIG_RK28_USB_WAKE
+       setup_irq(rk2818_timer_clocksource_irq.irq, &rk2818_timer_clocksource_irq);
+#endif
 }
 
 static void __init rk2818_timer_init(void)
@@ -176,3 +210,36 @@ struct sys_timer rk2818_timer = {
        .init = rk2818_timer_init
 };
 
+#ifdef CONFIG_RK28_USB_WAKE
+void rockchip_timer_clocksource_suspend_resume(int suspend)
+{
+       RK_TIMER_DISABLE(TIMER_CLKSRC);
+       if (suspend) {
+               RK_TIMER_SETCOUNT(TIMER_CLKSRC, 24000 * CHECK_VBUS_MS);
+               RK_TIMER_ENABLE(TIMER_CLKSRC);
+       } else {
+               RK_TIMER_SETCOUNT(TIMER_CLKSRC, 0xFFFFFFFF);
+               RK_TIMER_ENABLE_FREE_RUNNING(TIMER_CLKSRC);
+       }
+}
+
+int rockchip_timer_clocksource_irq_checkandclear(void)
+{
+       u32 l, h;
+
+       l = readl(RK2818_INTC_BASE + IRQ_REG_MASKSTATUS_L);
+       h = readl(RK2818_INTC_BASE + IRQ_REG_MASKSTATUS_H);
+
+       /* clock source irq */
+       if (l & (1 << IRQ_NR_TIMER_CLKSRC)) {
+               RK_TIMER_INT_CLEAR(TIMER_CLKSRC);
+               l &= ~(1 << IRQ_NR_TIMER_CLKSRC);
+       }
+
+       /* 20091103,HSL@RK,all irq must be handle !*/
+       l |= (h & 0xffff);
+
+       return l;
+}
+#endif
+