blob: 81c06410aa76fd1ed8237228c42428c768262f6f [file] [log] [blame]
Wu Zhangjina9e86412009-11-11 14:57:41 +08001/*
2 * Lemote loongson2f family machines' specific suspend support
3 *
4 * Copyright (C) 2009 Lemote Inc.
5 * Author: Wu Zhangjin <wuzj@lemote.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
13#include <linux/suspend.h>
14#include <linux/interrupt.h>
15#include <linux/pm.h>
16#include <linux/i8042.h>
Wu Zhangjincb1ed9e2009-11-21 19:05:24 +080017#include <linux/module.h>
Wu Zhangjina9e86412009-11-11 14:57:41 +080018
19#include <asm/i8259.h>
20#include <asm/mipsregs.h>
21#include <asm/bootinfo.h>
22
23#include <loongson.h>
24
Wu Zhangjincb1ed9e2009-11-21 19:05:24 +080025#include "ec_kb3310b.h"
26
Wu Zhangjina9e86412009-11-11 14:57:41 +080027#define I8042_KBD_IRQ 1
28#define I8042_CTR_KBDINT 0x01
29#define I8042_CTR_KBDDIS 0x10
30
31static unsigned char i8042_ctr;
32
33static int i8042_enable_kbd_port(void)
34{
35 if (i8042_command(&i8042_ctr, I8042_CMD_CTL_RCTR)) {
36 pr_err("i8042.c: Can't read CTR while enabling i8042 kbd port."
37 "\n");
38 return -EIO;
39 }
40
41 i8042_ctr &= ~I8042_CTR_KBDDIS;
42 i8042_ctr |= I8042_CTR_KBDINT;
43
44 if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
45 i8042_ctr &= ~I8042_CTR_KBDINT;
46 i8042_ctr |= I8042_CTR_KBDDIS;
47 pr_err("i8042.c: Failed to enable KBD port.\n");
48
49 return -EIO;
50 }
51
52 return 0;
53}
54
Wu Zhangjina9e86412009-11-11 14:57:41 +080055void setup_wakeup_events(void)
56{
57 int irq_mask;
58
59 switch (mips_machtype) {
60 case MACH_LEMOTE_ML2F7:
61 case MACH_LEMOTE_YL2F89:
62 /* open the keyboard irq in i8259A */
63 outb((0xff & ~(1 << I8042_KBD_IRQ)), PIC_MASTER_IMR);
64 irq_mask = inb(PIC_MASTER_IMR);
65
66 /* enable keyboard port */
67 i8042_enable_kbd_port();
Wu Zhangjincb1ed9e2009-11-21 19:05:24 +080068
69 /* Wakeup CPU via SCI lid open event */
70 outb(irq_mask & ~(1 << PIC_CASCADE_IR), PIC_MASTER_IMR);
71 inb(PIC_MASTER_IMR);
72 outb(0xff & ~(1 << (SCI_IRQ_NUM - 8)), PIC_SLAVE_IMR);
73 inb(PIC_SLAVE_IMR);
74
Wu Zhangjina9e86412009-11-11 14:57:41 +080075 break;
76
77 default:
78 break;
79 }
80}
Wu Zhangjincb1ed9e2009-11-21 19:05:24 +080081
82static struct delayed_work lid_task;
83static int initialized;
84/* yeeloong_report_lid_status will be implemented in yeeloong_laptop.c */
85sci_handler yeeloong_report_lid_status;
86EXPORT_SYMBOL(yeeloong_report_lid_status);
87static void yeeloong_lid_update_task(struct work_struct *work)
88{
89 if (yeeloong_report_lid_status)
90 yeeloong_report_lid_status(BIT_LID_DETECT_ON);
91}
92
93int wakeup_loongson(void)
94{
95 int irq;
96
97 /* query the interrupt number */
98 irq = mach_i8259_irq();
99 if (irq < 0)
100 return 0;
101
102 printk(KERN_INFO "%s: irq = %d\n", __func__, irq);
103
104 if (irq == I8042_KBD_IRQ)
105 return 1;
106 else if (irq == SCI_IRQ_NUM) {
107 int ret, sci_event;
108 /* query the event number */
109 ret = ec_query_seq(CMD_GET_EVENT_NUM);
110 if (ret < 0)
111 return 0;
112 sci_event = ec_get_event_num();
113 if (sci_event < 0)
114 return 0;
115 if (sci_event == EVENT_LID) {
116 int lid_status;
117 /* check the LID status */
118 lid_status = ec_read(REG_LID_DETECT);
119 /* wakeup cpu when people open the LID */
120 if (lid_status == BIT_LID_DETECT_ON) {
121 /* If we call it directly here, the WARNING
122 * will be sent out by getnstimeofday
123 * via "WARN_ON(timekeeping_suspended);"
124 * because we can not schedule in suspend mode.
125 */
126 if (initialized == 0) {
127 INIT_DELAYED_WORK(&lid_task,
128 yeeloong_lid_update_task);
129 initialized = 1;
130 }
131 schedule_delayed_work(&lid_task, 1);
132 return 1;
133 }
134 }
135 }
136
137 return 0;
138}