1/*
2 *  Lemote loongson2f family machines' specific suspend support
3 *
4 *  Copyright (C) 2009 Lemote Inc.
5 *  Author: Wu Zhangjin <wuzhangjin@gmail.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
32static unsigned char i8042_ctr;
33
34static 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
56void 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
83static struct delayed_work lid_task;
84static int initialized;
85/* yeeloong_report_lid_status will be implemented in yeeloong_laptop.c */
86sci_handler yeeloong_report_lid_status;
87EXPORT_SYMBOL(yeeloong_report_lid_status);
88static 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
94int 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
141void __weak mach_suspend(void)
142{
143	disable_mfgpt0_counter();
144}
145
146void __weak mach_resume(void)
147{
148	enable_mfgpt0_counter();
149}
150