RK2818 PM: Enable wake-up events for USB ports

This commit is contained in:
黄涛
2010-08-05 12:01:16 +08:00
parent c2e9e93c48
commit 15099b789f
6 changed files with 152 additions and 34 deletions

View 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

View 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

View 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

View 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

View File

@@ -12,6 +12,22 @@
#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;
}

View File

@@ -25,10 +25,6 @@
#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
@@ -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