u-boot/drivers/sysreset/sysreset_x86.c
Tom Rini d678a59d2d Revert "Merge patch series "arm: dts: am62-beagleplay: Fix Beagleplay Ethernet""
When bringing in the series 'arm: dts: am62-beagleplay: Fix Beagleplay
Ethernet"' I failed to notice that b4 noticed it was based on next and
so took that as the base commit and merged that part of next to master.

This reverts commit c8ffd1356d, reversing
changes made to 2ee6f3a5f7.

Reported-by: Jonas Karlman <jonas@kwiboo.se>
Signed-off-by: Tom Rini <trini@konsulko.com>
2024-05-19 08:16:36 -06:00

160 lines
3.7 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
*
* Generic reset driver for x86 processor
*/
#include <common.h>
#include <dm.h>
#include <efi_loader.h>
#include <pch.h>
#include <sysreset.h>
#include <acpi/acpi_s3.h>
#include <asm/io.h>
#include <asm/processor.h>
#include <asm/sysreset.h>
/*
* Power down the machine by using the power management sleep control
* of the chipset. This will currently only work on Intel chipsets.
* However, adapting it to new chipsets is fairly simple. You will
* have to find the IO address of the power management register block
* in your southbridge, and look up the appropriate SLP_TYP_S5 value
* from your southbridge's data sheet.
*
* This function never returns.
*/
int pch_sysreset_power_off(struct udevice *dev)
{
struct x86_sysreset_plat *plat = dev_get_plat(dev);
struct pch_pmbase_info pm;
u32 reg32;
int ret;
if (!plat->pch)
return -ENOENT;
ret = pch_ioctl(plat->pch, PCH_REQ_PMBASE_INFO, &pm, sizeof(pm));
if (ret)
return ret;
/*
* Mask interrupts or system might stay in a coma, not executing code
* anymore, but not powered off either.
*/
asm("cli");
/*
* Avoid any GPI waking the system from S5* or the system might stay in
* a coma
*/
outl(0x00000000, pm.base + pm.gpio0_en_ofs);
/* Clear Power Button Status */
outw(PWRBTN_STS, pm.base + pm.pm1_sts_ofs);
/* PMBASE + 4, Bit 10-12, Sleeping Type, * set to 111 -> S5, soft_off */
reg32 = inl(pm.base + pm.pm1_cnt_ofs);
/* Set Sleeping Type to S5 (poweroff) */
reg32 &= ~(SLP_EN | SLP_TYP);
reg32 |= SLP_TYP_S5;
outl(reg32, pm.base + pm.pm1_cnt_ofs);
/* Now set the Sleep Enable bit */
reg32 |= SLP_EN;
outl(reg32, pm.base + pm.pm1_cnt_ofs);
for (;;)
asm("hlt");
}
static int x86_sysreset_request(struct udevice *dev, enum sysreset_t type)
{
int value;
int ret;
switch (type) {
case SYSRESET_WARM:
value = SYS_RST | RST_CPU;
break;
case SYSRESET_COLD:
value = SYS_RST | RST_CPU | FULL_RST;
break;
case SYSRESET_POWER_OFF:
ret = pch_sysreset_power_off(dev);
if (ret)
return ret;
return -EINPROGRESS;
default:
return -EPROTONOSUPPORT;
}
outb(value, IO_PORT_RESET);
return -EINPROGRESS;
}
static int x86_sysreset_get_last(struct udevice *dev)
{
return SYSRESET_POWER;
}
#ifdef CONFIG_EFI_LOADER
void __efi_runtime EFIAPI efi_reset_system(
enum efi_reset_type reset_type,
efi_status_t reset_status,
unsigned long data_size, void *reset_data)
{
int value;
/*
* inline this code since we are not caused in the context of a
* udevice and passing NULL to x86_sysreset_request() is too horrible.
*/
if (reset_type == EFI_RESET_COLD ||
reset_type == EFI_RESET_PLATFORM_SPECIFIC)
value = SYS_RST | RST_CPU | FULL_RST;
else /* assume EFI_RESET_WARM since we cannot return an error */
value = SYS_RST | RST_CPU;
outb(value, IO_PORT_RESET);
/* TODO EFI_RESET_SHUTDOWN */
while (1) { }
}
#endif
static int x86_sysreset_probe(struct udevice *dev)
{
struct x86_sysreset_plat *plat = dev_get_plat(dev);
/*
* Locate the PCH if there is one. It isn't essential. Avoid this before
* relocation as we shouldn't need reset then and it needs a lot of
* memory for PCI enumeration.
*/
if (gd->flags & GD_FLG_RELOC)
uclass_first_device(UCLASS_PCH, &plat->pch);
return 0;
}
static const struct udevice_id x86_sysreset_ids[] = {
{ .compatible = "x86,reset" },
{ }
};
static struct sysreset_ops x86_sysreset_ops = {
.request = x86_sysreset_request,
.get_last = x86_sysreset_get_last,
};
U_BOOT_DRIVER(x86_reset) = {
.name = "x86_reset",
.id = UCLASS_SYSRESET,
.of_match = x86_sysreset_ids,
.ops = &x86_sysreset_ops,
.probe = x86_sysreset_probe,
.plat_auto = sizeof(struct x86_sysreset_plat),
};