Merge branch 'cpumask-cleanups' of git://git.kernel.org/pub/scm/linux/kernel/git...
[sfrench/cifs-2.6.git] / arch / mips / loongson / lemote-2f / pm.c
1 /*
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>
17 #include <linux/module.h>
18
19 #include <asm/i8259.h>
20 #include <asm/mipsregs.h>
21 #include <asm/bootinfo.h>
22
23 #include <loongson.h>
24
25 #include <cs5536/cs5536_mfgpt.h>
26 #include "ec_kb3310b.h"
27
28 #define I8042_KBD_IRQ           1
29 #define I8042_CTR_KBDINT        0x01
30 #define I8042_CTR_KBDDIS        0x10
31
32 static unsigned char i8042_ctr;
33
34 static int i8042_enable_kbd_port(void)
35 {
36         if (i8042_command(&i8042_ctr, I8042_CMD_CTL_RCTR)) {
37                 pr_err("i8042.c: Can't read CTR while enabling i8042 kbd port."
38                        "\n");
39                 return -EIO;
40         }
41
42         i8042_ctr &= ~I8042_CTR_KBDDIS;
43         i8042_ctr |= I8042_CTR_KBDINT;
44
45         if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
46                 i8042_ctr &= ~I8042_CTR_KBDINT;
47                 i8042_ctr |= I8042_CTR_KBDDIS;
48                 pr_err("i8042.c: Failed to enable KBD port.\n");
49
50                 return -EIO;
51         }
52
53         return 0;
54 }
55
56 void setup_wakeup_events(void)
57 {
58         int irq_mask;
59
60         switch (mips_machtype) {
61         case MACH_LEMOTE_ML2F7:
62         case MACH_LEMOTE_YL2F89:
63                 /* open the keyboard irq in i8259A */
64                 outb((0xff & ~(1 << I8042_KBD_IRQ)), PIC_MASTER_IMR);
65                 irq_mask = inb(PIC_MASTER_IMR);
66
67                 /* enable keyboard port */
68                 i8042_enable_kbd_port();
69
70                 /* Wakeup CPU via SCI lid open event */
71                 outb(irq_mask & ~(1 << PIC_CASCADE_IR), PIC_MASTER_IMR);
72                 inb(PIC_MASTER_IMR);
73                 outb(0xff & ~(1 << (SCI_IRQ_NUM - 8)), PIC_SLAVE_IMR);
74                 inb(PIC_SLAVE_IMR);
75
76                 break;
77
78         default:
79                 break;
80         }
81 }
82
83 static struct delayed_work lid_task;
84 static int initialized;
85 /* yeeloong_report_lid_status will be implemented in yeeloong_laptop.c */
86 sci_handler yeeloong_report_lid_status;
87 EXPORT_SYMBOL(yeeloong_report_lid_status);
88 static void yeeloong_lid_update_task(struct work_struct *work)
89 {
90         if (yeeloong_report_lid_status)
91                 yeeloong_report_lid_status(BIT_LID_DETECT_ON);
92 }
93
94 int wakeup_loongson(void)
95 {
96         int irq;
97
98         /* query the interrupt number */
99         irq = mach_i8259_irq();
100         if (irq < 0)
101                 return 0;
102
103         printk(KERN_INFO "%s: irq = %d\n", __func__, irq);
104
105         if (irq == I8042_KBD_IRQ)
106                 return 1;
107         else if (irq == SCI_IRQ_NUM) {
108                 int ret, sci_event;
109                 /* query the event number */
110                 ret = ec_query_seq(CMD_GET_EVENT_NUM);
111                 if (ret < 0)
112                         return 0;
113                 sci_event = ec_get_event_num();
114                 if (sci_event < 0)
115                         return 0;
116                 if (sci_event == EVENT_LID) {
117                         int lid_status;
118                         /* check the LID status */
119                         lid_status = ec_read(REG_LID_DETECT);
120                         /* wakeup cpu when people open the LID */
121                         if (lid_status == BIT_LID_DETECT_ON) {
122                                 /* If we call it directly here, the WARNING
123                                  * will be sent out by getnstimeofday
124                                  * via "WARN_ON(timekeeping_suspended);"
125                                  * because we can not schedule in suspend mode.
126                                  */
127                                 if (initialized == 0) {
128                                         INIT_DELAYED_WORK(&lid_task,
129                                                 yeeloong_lid_update_task);
130                                         initialized = 1;
131                                 }
132                                 schedule_delayed_work(&lid_task, 1);
133                                 return 1;
134                         }
135                 }
136         }
137
138         return 0;
139 }
140
141 void __weak mach_suspend(void)
142 {
143         disable_mfgpt0_counter();
144 }
145
146 void __weak mach_resume(void)
147 {
148         enable_mfgpt0_counter();
149 }