162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Lemote loongson2f family machines' specific suspend support
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 2009 Lemote Inc.
662306a36Sopenharmony_ci *  Author: Wu Zhangjin <wuzhangjin@gmail.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/suspend.h>
1062306a36Sopenharmony_ci#include <linux/interrupt.h>
1162306a36Sopenharmony_ci#include <linux/pm.h>
1262306a36Sopenharmony_ci#include <linux/i8042.h>
1362306a36Sopenharmony_ci#include <linux/export.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <asm/i8259.h>
1662306a36Sopenharmony_ci#include <asm/mipsregs.h>
1762306a36Sopenharmony_ci#include <asm/bootinfo.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <loongson.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <cs5536/cs5536_mfgpt.h>
2262306a36Sopenharmony_ci#include "ec_kb3310b.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define I8042_KBD_IRQ		1
2562306a36Sopenharmony_ci#define I8042_CTR_KBDINT	0x01
2662306a36Sopenharmony_ci#define I8042_CTR_KBDDIS	0x10
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic unsigned char i8042_ctr;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic int i8042_enable_kbd_port(void)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	if (i8042_command(&i8042_ctr, I8042_CMD_CTL_RCTR)) {
3362306a36Sopenharmony_ci		pr_err("i8042.c: Can't read CTR while enabling i8042 kbd port."
3462306a36Sopenharmony_ci		       "\n");
3562306a36Sopenharmony_ci		return -EIO;
3662306a36Sopenharmony_ci	}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	i8042_ctr &= ~I8042_CTR_KBDDIS;
3962306a36Sopenharmony_ci	i8042_ctr |= I8042_CTR_KBDINT;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
4262306a36Sopenharmony_ci		i8042_ctr &= ~I8042_CTR_KBDINT;
4362306a36Sopenharmony_ci		i8042_ctr |= I8042_CTR_KBDDIS;
4462306a36Sopenharmony_ci		pr_err("i8042.c: Failed to enable KBD port.\n");
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci		return -EIO;
4762306a36Sopenharmony_ci	}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	return 0;
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_civoid setup_wakeup_events(void)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	int irq_mask;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	switch (mips_machtype) {
5762306a36Sopenharmony_ci	case MACH_LEMOTE_ML2F7:
5862306a36Sopenharmony_ci	case MACH_LEMOTE_YL2F89:
5962306a36Sopenharmony_ci		/* open the keyboard irq in i8259A */
6062306a36Sopenharmony_ci		outb((0xff & ~(1 << I8042_KBD_IRQ)), PIC_MASTER_IMR);
6162306a36Sopenharmony_ci		irq_mask = inb(PIC_MASTER_IMR);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci		/* enable keyboard port */
6462306a36Sopenharmony_ci		i8042_enable_kbd_port();
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci		/* Wakeup CPU via SCI lid open event */
6762306a36Sopenharmony_ci		outb(irq_mask & ~(1 << PIC_CASCADE_IR), PIC_MASTER_IMR);
6862306a36Sopenharmony_ci		inb(PIC_MASTER_IMR);
6962306a36Sopenharmony_ci		outb(0xff & ~(1 << (SCI_IRQ_NUM - 8)), PIC_SLAVE_IMR);
7062306a36Sopenharmony_ci		inb(PIC_SLAVE_IMR);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci		break;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	default:
7562306a36Sopenharmony_ci		break;
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic struct delayed_work lid_task;
8062306a36Sopenharmony_cistatic int initialized;
8162306a36Sopenharmony_ci/* yeeloong_report_lid_status will be implemented in yeeloong_laptop.c */
8262306a36Sopenharmony_cisci_handler yeeloong_report_lid_status;
8362306a36Sopenharmony_ciEXPORT_SYMBOL(yeeloong_report_lid_status);
8462306a36Sopenharmony_cistatic void yeeloong_lid_update_task(struct work_struct *work)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	if (yeeloong_report_lid_status)
8762306a36Sopenharmony_ci		yeeloong_report_lid_status(BIT_LID_DETECT_ON);
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ciint wakeup_loongson(void)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	int irq;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	/* query the interrupt number */
9562306a36Sopenharmony_ci	irq = mach_i8259_irq();
9662306a36Sopenharmony_ci	if (irq < 0)
9762306a36Sopenharmony_ci		return 0;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	printk(KERN_INFO "%s: irq = %d\n", __func__, irq);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	if (irq == I8042_KBD_IRQ)
10262306a36Sopenharmony_ci		return 1;
10362306a36Sopenharmony_ci	else if (irq == SCI_IRQ_NUM) {
10462306a36Sopenharmony_ci		int ret, sci_event;
10562306a36Sopenharmony_ci		/* query the event number */
10662306a36Sopenharmony_ci		ret = ec_query_seq(CMD_GET_EVENT_NUM);
10762306a36Sopenharmony_ci		if (ret < 0)
10862306a36Sopenharmony_ci			return 0;
10962306a36Sopenharmony_ci		sci_event = ec_get_event_num();
11062306a36Sopenharmony_ci		if (sci_event < 0)
11162306a36Sopenharmony_ci			return 0;
11262306a36Sopenharmony_ci		if (sci_event == EVENT_LID) {
11362306a36Sopenharmony_ci			int lid_status;
11462306a36Sopenharmony_ci			/* check the LID status */
11562306a36Sopenharmony_ci			lid_status = ec_read(REG_LID_DETECT);
11662306a36Sopenharmony_ci			/* wakeup cpu when people open the LID */
11762306a36Sopenharmony_ci			if (lid_status == BIT_LID_DETECT_ON) {
11862306a36Sopenharmony_ci				/* If we call it directly here, the WARNING
11962306a36Sopenharmony_ci				 * will be sent out by getnstimeofday
12062306a36Sopenharmony_ci				 * via "WARN_ON(timekeeping_suspended);"
12162306a36Sopenharmony_ci				 * because we can not schedule in suspend mode.
12262306a36Sopenharmony_ci				 */
12362306a36Sopenharmony_ci				if (initialized == 0) {
12462306a36Sopenharmony_ci					INIT_DELAYED_WORK(&lid_task,
12562306a36Sopenharmony_ci						yeeloong_lid_update_task);
12662306a36Sopenharmony_ci					initialized = 1;
12762306a36Sopenharmony_ci				}
12862306a36Sopenharmony_ci				schedule_delayed_work(&lid_task, 1);
12962306a36Sopenharmony_ci				return 1;
13062306a36Sopenharmony_ci			}
13162306a36Sopenharmony_ci		}
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	return 0;
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_civoid __weak mach_suspend(void)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	disable_mfgpt0_counter();
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_civoid __weak mach_resume(void)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	enable_mfgpt0_counter();
14562306a36Sopenharmony_ci}
146