root/arch/mips/loongson2ef/lemote-2f/pm.c
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *  Lemote loongson2f family machines' specific suspend support
 *
 *  Copyright (C) 2009 Lemote Inc.
 *  Author: Wu Zhangjin <wuzhangjin@gmail.com>
 */

#include <linux/suspend.h>
#include <linux/interrupt.h>
#include <linux/pm.h>
#include <linux/i8042.h>
#include <linux/export.h>

#include <asm/i8259.h>
#include <asm/mipsregs.h>
#include <asm/bootinfo.h>

#include <loongson.h>

#include <cs5536/cs5536_mfgpt.h>
#include "ec_kb3310b.h"

#define I8042_KBD_IRQ           1
#define I8042_CTR_KBDINT        0x01
#define I8042_CTR_KBDDIS        0x10

static unsigned char i8042_ctr;

static int i8042_enable_kbd_port(void)
{
        if (i8042_command(&i8042_ctr, I8042_CMD_CTL_RCTR)) {
                pr_err("i8042.c: Can't read CTR while enabling i8042 kbd port."
                       "\n");
                return -EIO;
        }

        i8042_ctr &= ~I8042_CTR_KBDDIS;
        i8042_ctr |= I8042_CTR_KBDINT;

        if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
                i8042_ctr &= ~I8042_CTR_KBDINT;
                i8042_ctr |= I8042_CTR_KBDDIS;
                pr_err("i8042.c: Failed to enable KBD port.\n");

                return -EIO;
        }

        return 0;
}

void setup_wakeup_events(void)
{
        int irq_mask;

        switch (mips_machtype) {
        case MACH_LEMOTE_ML2F7:
        case MACH_LEMOTE_YL2F89:
                /* open the keyboard irq in i8259A */
                outb((0xff & ~(1 << I8042_KBD_IRQ)), PIC_MASTER_IMR);
                irq_mask = inb(PIC_MASTER_IMR);

                /* enable keyboard port */
                i8042_enable_kbd_port();

                /* Wakeup CPU via SCI lid open event */
                outb(irq_mask & ~(1 << PIC_CASCADE_IR), PIC_MASTER_IMR);
                inb(PIC_MASTER_IMR);
                outb(0xff & ~(1 << (SCI_IRQ_NUM - 8)), PIC_SLAVE_IMR);
                inb(PIC_SLAVE_IMR);

                break;

        default:
                break;
        }
}

static struct delayed_work lid_task;
static int initialized;
/* yeeloong_report_lid_status will be implemented in yeeloong_laptop.c */
sci_handler yeeloong_report_lid_status;
EXPORT_SYMBOL(yeeloong_report_lid_status);
static void yeeloong_lid_update_task(struct work_struct *work)
{
        if (yeeloong_report_lid_status)
                yeeloong_report_lid_status(BIT_LID_DETECT_ON);
}

int wakeup_loongson(void)
{
        int irq;

        /* query the interrupt number */
        irq = mach_i8259_irq();
        if (irq < 0)
                return 0;

        printk(KERN_INFO "%s: irq = %d\n", __func__, irq);

        if (irq == I8042_KBD_IRQ)
                return 1;
        else if (irq == SCI_IRQ_NUM) {
                int ret, sci_event;
                /* query the event number */
                ret = ec_query_seq(CMD_GET_EVENT_NUM);
                if (ret < 0)
                        return 0;
                sci_event = ec_get_event_num();
                if (sci_event < 0)
                        return 0;
                if (sci_event == EVENT_LID) {
                        int lid_status;
                        /* check the LID status */
                        lid_status = ec_read(REG_LID_DETECT);
                        /* wakeup cpu when people open the LID */
                        if (lid_status == BIT_LID_DETECT_ON) {
                                /* If we call it directly here, the WARNING
                                 * will be sent out by getnstimeofday
                                 * via "WARN_ON(timekeeping_suspended);"
                                 * because we can not schedule in suspend mode.
                                 */
                                if (initialized == 0) {
                                        INIT_DELAYED_WORK(&lid_task,
                                                yeeloong_lid_update_task);
                                        initialized = 1;
                                }
                                schedule_delayed_work(&lid_task, 1);
                                return 1;
                        }
                }
        }

        return 0;
}

void __weak mach_suspend(void)
{
        disable_mfgpt0_counter();
}

void __weak mach_resume(void)
{
        enable_mfgpt0_counter();
}