Yeeloong2F netbook has an embedded controller(kb3310b) to manage the LID action. when the LID is closed/opened, a relative SCI interrupt take place, and the relative event is saved to a relative EC register. This patch make the LID open action can wake up the processor from wait mode if it in the suspend mode. Signed-off-by: Wu Zhangjin <wuzhangjin@xxxxxxxxx> --- arch/mips/include/asm/mach-loongson/loongson.h | 1 + arch/mips/loongson/lemote-2f/irq.c | 4 +- arch/mips/loongson/lemote-2f/pm.c | 72 ++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 1 deletions(-) diff --git a/arch/mips/include/asm/mach-loongson/loongson.h b/arch/mips/include/asm/mach-loongson/loongson.h index daf7041..71ce760 100644 --- a/arch/mips/include/asm/mach-loongson/loongson.h +++ b/arch/mips/include/asm/mach-loongson/loongson.h @@ -41,6 +41,7 @@ extern void __init bonito_irq_init(void); extern void __init set_irq_trigger_mode(void); extern void __init mach_init_irq(void); extern void mach_irq_dispatch(unsigned int pending); +extern int mach_i8259_irq(void); /* We need this in some places... */ #define delay() ({ \ diff --git a/arch/mips/loongson/lemote-2f/irq.c b/arch/mips/loongson/lemote-2f/irq.c index 50e7bb6..77d32f9 100644 --- a/arch/mips/loongson/lemote-2f/irq.c +++ b/arch/mips/loongson/lemote-2f/irq.c @@ -9,6 +9,7 @@ */ #include <linux/interrupt.h> +#include <linux/module.h> #include <asm/irq_cpu.h> #include <asm/i8259.h> @@ -30,7 +31,7 @@ * The generic i8259_irq() make the kernel hang on booting. Since we cannot * get the irq via the IRR directly, we access the ISR instead. */ -static inline int mach_i8259_irq(void) +int mach_i8259_irq(void) { int irq, isr; @@ -60,6 +61,7 @@ static inline int mach_i8259_irq(void) return irq; } +EXPORT_SYMBOL(mach_i8259_irq); static void i8259_irqdispatch(void) { diff --git a/arch/mips/loongson/lemote-2f/pm.c b/arch/mips/loongson/lemote-2f/pm.c index 8090d05..7202177 100644 --- a/arch/mips/loongson/lemote-2f/pm.c +++ b/arch/mips/loongson/lemote-2f/pm.c @@ -14,6 +14,7 @@ #include <linux/interrupt.h> #include <linux/pm.h> #include <linux/i8042.h> +#include <linux/module.h> #include <asm/i8259.h> #include <asm/mipsregs.h> @@ -21,6 +22,8 @@ #include <loongson.h> +#include "ec_kb3310b.h" + #define I8042_KBD_IRQ 1 #define I8042_CTR_KBDINT 0x01 #define I8042_CTR_KBDDIS 0x10 @@ -52,6 +55,9 @@ static int i8042_enable_kbd_port(void) /* * The i8042 is connnected to i8259A */ +#define PIC_CASCADE_IR 2 /* cascade irq num of i8259A */ + +/* i8042 is connnectted to i8259A */ void setup_wakeup_events(void) { int irq_mask; @@ -65,9 +71,75 @@ void setup_wakeup_events(void) /* enable keyboard port */ i8042_enable_kbd_port(); + + /* wakeup cpu via sci with relative lid openning event */ + outb(irq_mask & ~(1 << PIC_CASCADE_IR), PIC_MASTER_IMR); + inb(PIC_MASTER_IMR); + outb(0xff & ~(1 << (SCI_IRQ_NUM - 8)), PIC_SLAVE_IMR); + inb(PIC_SLAVE_IMR); + break; default: break; } } + +static struct delayed_work lid_task; +static int initialized; +/* yeeloong_report_lid_status will be implemented in yeeloong_laptop.c */ +sci_handler yeeloong_report_lid_status; +EXPORT_SYMBOL(yeeloong_report_lid_status); +static void yeeloong_lid_update_task(struct work_struct *work) +{ + if (yeeloong_report_lid_status) + yeeloong_report_lid_status(BIT_LID_DETECT_ON); +} + +int wakeup_loongson(void) +{ + int irq; + + /* query the interrupt number */ + irq = mach_i8259_irq(); + if (irq < 0) + return 0; + + printk(KERN_INFO "irq = %d\n", irq); + + if (irq == I8042_KBD_IRQ) + return 1; + else if (irq == SCI_IRQ_NUM) { + int ret, sci_event; + /* query the event number */ + ret = ec_query_seq(CMD_GET_EVENT_NUM); + if (ret < 0) + return 0; + sci_event = ec_get_event_num(); + if (sci_event < 0) + return 0; + if (sci_event == EVENT_LID) { + int lid_status; + /* check the LID status */ + lid_status = ec_read(REG_LID_DETECT); + /* wakeup cpu when people open the LID */ + if (lid_status == BIT_LID_DETECT_ON) { + /* if we call it directly here, the WARNING + * will happen at line 98 of + * kerenl/time/timekeeping.c (getnstimeofday) + * via "WARN_ON(timekeeping_suspended);" because, + * currently, we are in the suspend status + */ + if (initialized == 0) { + /* init the rfkill work task */ + INIT_DELAYED_WORK(&lid_task, yeeloong_lid_update_task); + initialized = 1; + } + schedule_delayed_work(&lid_task, 1); + return 1; + } + } + } + + return 0; +} -- 1.6.2.1