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