162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* Board-specific reboot/shutdown routines
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (c) 2009 Philippe Vachon <philippe@cowpig.ca>
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 2009 Lemote Inc.
762306a36Sopenharmony_ci * Author: Wu Zhangjin, wuzhangjin@gmail.com
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/io.h>
1162306a36Sopenharmony_ci#include <linux/delay.h>
1262306a36Sopenharmony_ci#include <linux/types.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <asm/bootinfo.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <loongson.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <cs5536/cs5536.h>
1962306a36Sopenharmony_ci#include "ec_kb3310b.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic void reset_cpu(void)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	/*
2462306a36Sopenharmony_ci	 * reset cpu to full speed, this is needed when enabling cpu frequency
2562306a36Sopenharmony_ci	 * scalling
2662306a36Sopenharmony_ci	 */
2762306a36Sopenharmony_ci	writel(readl(LOONGSON_CHIPCFG) | 0x7, LOONGSON_CHIPCFG);
2862306a36Sopenharmony_ci}
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/* reset support for fuloong2f */
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic void fl2f_reboot(void)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	reset_cpu();
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	/* send a reset signal to south bridge.
3762306a36Sopenharmony_ci	 *
3862306a36Sopenharmony_ci	 * NOTE: if enable "Power Management" in kernel, rtl8169 will not reset
3962306a36Sopenharmony_ci	 * normally with this reset operation and it will not work in PMON, but
4062306a36Sopenharmony_ci	 * you can type halt command and then reboot, seems the hardware reset
4162306a36Sopenharmony_ci	 * logic not work normally.
4262306a36Sopenharmony_ci	 */
4362306a36Sopenharmony_ci	{
4462306a36Sopenharmony_ci		u32 hi, lo;
4562306a36Sopenharmony_ci		_rdmsr(DIVIL_MSR_REG(DIVIL_SOFT_RESET), &hi, &lo);
4662306a36Sopenharmony_ci		lo |= 0x00000001;
4762306a36Sopenharmony_ci		_wrmsr(DIVIL_MSR_REG(DIVIL_SOFT_RESET), hi, lo);
4862306a36Sopenharmony_ci	}
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic void fl2f_shutdown(void)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	u32 hi, lo, val;
5462306a36Sopenharmony_ci	int gpio_base;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	/* get gpio base */
5762306a36Sopenharmony_ci	_rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_GPIO), &hi, &lo);
5862306a36Sopenharmony_ci	gpio_base = lo & 0xff00;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	/* make cs5536 gpio13 output enable */
6162306a36Sopenharmony_ci	val = inl(gpio_base + GPIOL_OUT_EN);
6262306a36Sopenharmony_ci	val &= ~(1 << (16 + 13));
6362306a36Sopenharmony_ci	val |= (1 << 13);
6462306a36Sopenharmony_ci	outl(val, gpio_base + GPIOL_OUT_EN);
6562306a36Sopenharmony_ci	mmiowb();
6662306a36Sopenharmony_ci	/* make cs5536 gpio13 output low level voltage. */
6762306a36Sopenharmony_ci	val = inl(gpio_base + GPIOL_OUT_VAL) & ~(1 << (13));
6862306a36Sopenharmony_ci	val |= (1 << (16 + 13));
6962306a36Sopenharmony_ci	outl(val, gpio_base + GPIOL_OUT_VAL);
7062306a36Sopenharmony_ci	mmiowb();
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci/* reset support for yeeloong2f and mengloong2f notebook */
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic void ml2f_reboot(void)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	reset_cpu();
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	/* sending an reset signal to EC(embedded controller) */
8062306a36Sopenharmony_ci	ec_write(REG_RESET, BIT_RESET_ON);
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci#define yl2f89_reboot ml2f_reboot
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/* menglong(7inches) laptop has different shutdown logic from 8.9inches */
8662306a36Sopenharmony_ci#define EC_SHUTDOWN_IO_PORT_HIGH 0xff2d
8762306a36Sopenharmony_ci#define EC_SHUTDOWN_IO_PORT_LOW	 0xff2e
8862306a36Sopenharmony_ci#define EC_SHUTDOWN_IO_PORT_DATA 0xff2f
8962306a36Sopenharmony_ci#define REG_SHUTDOWN_HIGH	 0xFC
9062306a36Sopenharmony_ci#define REG_SHUTDOWN_LOW	 0x29
9162306a36Sopenharmony_ci#define BIT_SHUTDOWN_ON		 (1 << 1)
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic void ml2f_shutdown(void)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	u8 val;
9662306a36Sopenharmony_ci	u64 i;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	outb(REG_SHUTDOWN_HIGH, EC_SHUTDOWN_IO_PORT_HIGH);
9962306a36Sopenharmony_ci	outb(REG_SHUTDOWN_LOW, EC_SHUTDOWN_IO_PORT_LOW);
10062306a36Sopenharmony_ci	mmiowb();
10162306a36Sopenharmony_ci	val = inb(EC_SHUTDOWN_IO_PORT_DATA);
10262306a36Sopenharmony_ci	outb(val & (~BIT_SHUTDOWN_ON), EC_SHUTDOWN_IO_PORT_DATA);
10362306a36Sopenharmony_ci	mmiowb();
10462306a36Sopenharmony_ci	/* need enough wait here... how many microseconds needs? */
10562306a36Sopenharmony_ci	for (i = 0; i < 0x10000; i++)
10662306a36Sopenharmony_ci		delay();
10762306a36Sopenharmony_ci	outb(val | BIT_SHUTDOWN_ON, EC_SHUTDOWN_IO_PORT_DATA);
10862306a36Sopenharmony_ci	mmiowb();
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic void yl2f89_shutdown(void)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	/* cpu-gpio0 output low */
11462306a36Sopenharmony_ci	LOONGSON_GPIODATA &= ~0x00000001;
11562306a36Sopenharmony_ci	/* cpu-gpio0 as output */
11662306a36Sopenharmony_ci	LOONGSON_GPIOIE &= ~0x00000001;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_civoid mach_prepare_reboot(void)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	switch (mips_machtype) {
12262306a36Sopenharmony_ci	case MACH_LEMOTE_FL2F:
12362306a36Sopenharmony_ci	case MACH_LEMOTE_NAS:
12462306a36Sopenharmony_ci	case MACH_LEMOTE_LL2F:
12562306a36Sopenharmony_ci		fl2f_reboot();
12662306a36Sopenharmony_ci		break;
12762306a36Sopenharmony_ci	case MACH_LEMOTE_ML2F7:
12862306a36Sopenharmony_ci		ml2f_reboot();
12962306a36Sopenharmony_ci		break;
13062306a36Sopenharmony_ci	case MACH_LEMOTE_YL2F89:
13162306a36Sopenharmony_ci		yl2f89_reboot();
13262306a36Sopenharmony_ci		break;
13362306a36Sopenharmony_ci	default:
13462306a36Sopenharmony_ci		break;
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_civoid mach_prepare_shutdown(void)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	switch (mips_machtype) {
14162306a36Sopenharmony_ci	case MACH_LEMOTE_FL2F:
14262306a36Sopenharmony_ci	case MACH_LEMOTE_NAS:
14362306a36Sopenharmony_ci	case MACH_LEMOTE_LL2F:
14462306a36Sopenharmony_ci		fl2f_shutdown();
14562306a36Sopenharmony_ci		break;
14662306a36Sopenharmony_ci	case MACH_LEMOTE_ML2F7:
14762306a36Sopenharmony_ci		ml2f_shutdown();
14862306a36Sopenharmony_ci		break;
14962306a36Sopenharmony_ci	case MACH_LEMOTE_YL2F89:
15062306a36Sopenharmony_ci		yl2f89_shutdown();
15162306a36Sopenharmony_ci		break;
15262306a36Sopenharmony_ci	default:
15362306a36Sopenharmony_ci		break;
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci}
156