From 15099b789ff33e4326966ebac9fb5f5d3344104d Mon Sep 17 00:00:00 2001 From: =?utf8?q?=E9=BB=84=E6=B6=9B?= Date: Thu, 5 Aug 2010 12:01:16 +0800 Subject: [PATCH] RK2818 PM: Enable wake-up events for USB ports --- arch/arm/configs/rk2818_mid_defconfig | 1 + arch/arm/configs/rk2818_phone_defconfig | 1 + arch/arm/configs/rk2818_raho_defconfig | 1 + arch/arm/mach-rk2818/Kconfig | 9 ++ arch/arm/mach-rk2818/pm.c | 43 +++++++- arch/arm/mach-rk2818/timer.c | 131 ++++++++++++++++++------ 6 files changed, 152 insertions(+), 34 deletions(-) diff --git a/arch/arm/configs/rk2818_mid_defconfig b/arch/arm/configs/rk2818_mid_defconfig index cf07eb912960..25aae0d2442b 100644 --- a/arch/arm/configs/rk2818_mid_defconfig +++ b/arch/arm/configs/rk2818_mid_defconfig @@ -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 diff --git a/arch/arm/configs/rk2818_phone_defconfig b/arch/arm/configs/rk2818_phone_defconfig index 0c5fc7f7bd50..6e061986913f 100644 --- a/arch/arm/configs/rk2818_phone_defconfig +++ b/arch/arm/configs/rk2818_phone_defconfig @@ -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 diff --git a/arch/arm/configs/rk2818_raho_defconfig b/arch/arm/configs/rk2818_raho_defconfig index 092b58e09fe7..0c279cf7cfb5 100644 --- a/arch/arm/configs/rk2818_raho_defconfig +++ b/arch/arm/configs/rk2818_raho_defconfig @@ -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 diff --git a/arch/arm/mach-rk2818/Kconfig b/arch/arm/mach-rk2818/Kconfig index fc7ed47d0922..76b14ae042df 100644 --- a/arch/arm/mach-rk2818/Kconfig +++ b/arch/arm/mach-rk2818/Kconfig @@ -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 diff --git a/arch/arm/mach-rk2818/pm.c b/arch/arm/mach-rk2818/pm.c index 765fb8b746e7..a3a8f40dd39c 100644 --- a/arch/arm/mach-rk2818/pm.c +++ b/arch/arm/mach-rk2818/pm.c @@ -12,6 +12,22 @@ #include #include +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; } diff --git a/arch/arm/mach-rk2818/timer.c b/arch/arm/mach-rk2818/timer.c index 2caa74d4c90e..c54db294b2ad 100644 --- a/arch/arm/mach-rk2818/timer.c +++ b/arch/arm/mach-rk2818/timer.c @@ -25,10 +25,6 @@ #include #include -#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 @@ -39,14 +35,34 @@ #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 + -- 2.34.1