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