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