From 40476f4afc08ccb6f1f95d7790b6a29159e8b30a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 May 2019 10:52:13 -0600 Subject: x86: sysreset: Separate out the EFI code The EFI implementation of reset sits inside the driver and is called directly from outside the driver, breaking the normal driver-model conventions. Worse, it passed NULL as the device pointer, hoping that the called function won't use it, which breaks as soon as code is added to use it. Separate out the implementation to improve the situation enough to allow a future patch to add new sysreset features. Signed-off-by: Simon Glass Reviewed-by: Bin Meng --- drivers/sysreset/sysreset_x86.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'drivers/sysreset/sysreset_x86.c') diff --git a/drivers/sysreset/sysreset_x86.c b/drivers/sysreset/sysreset_x86.c index 009f3766027..d484ec5de49 100644 --- a/drivers/sysreset/sysreset_x86.c +++ b/drivers/sysreset/sysreset_x86.c @@ -12,8 +12,7 @@ #include #include -static __efi_runtime int x86_sysreset_request(struct udevice *dev, - enum sysreset_t type) +static int x86_sysreset_request(struct udevice *dev, enum sysreset_t type) { int value; @@ -39,11 +38,18 @@ void __efi_runtime EFIAPI efi_reset_system( 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) - x86_sysreset_request(NULL, SYSRESET_COLD); - else if (reset_type == EFI_RESET_WARM) - x86_sysreset_request(NULL, SYSRESET_WARM); + 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 */ -- cgit v1.2.3 From d6b1ba2fb99cdda35983b1e70c745728b8df9b4b Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 May 2019 10:52:14 -0600 Subject: x86: sysreset: Implement power-off if available On modern x86 devices we can power the system off using the power- management features of the PCH. Add an implementation for this. Signed-off-by: Simon Glass Reviewed-by: Bin Meng --- drivers/sysreset/sysreset_x86.c | 79 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) (limited to 'drivers/sysreset/sysreset_x86.c') diff --git a/drivers/sysreset/sysreset_x86.c b/drivers/sysreset/sysreset_x86.c index d484ec5de49..ca892b305e2 100644 --- a/drivers/sysreset/sysreset_x86.c +++ b/drivers/sysreset/sysreset_x86.c @@ -7,14 +7,75 @@ #include #include +#include +#include #include +#include #include #include -#include + +struct x86_sysreset_platdata { + struct udevice *pch; +}; + +/* + * 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_platdata *plat = dev_get_platdata(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: @@ -23,6 +84,11 @@ static int x86_sysreset_request(struct udevice *dev, enum sysreset_t type) 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 -ENOSYS; } @@ -57,6 +123,15 @@ void __efi_runtime EFIAPI efi_reset_system( } #endif +static int x86_sysreset_probe(struct udevice *dev) +{ + struct x86_sysreset_platdata *plat = dev_get_platdata(dev); + + /* Locate the PCH if there is one. It isn't essential */ + uclass_first_device(UCLASS_PCH, &plat->pch); + + return 0; +} static const struct udevice_id x86_sysreset_ids[] = { { .compatible = "x86,reset" }, @@ -72,4 +147,6 @@ U_BOOT_DRIVER(x86_sysreset) = { .id = UCLASS_SYSRESET, .of_match = x86_sysreset_ids, .ops = &x86_sysreset_ops, + .probe = x86_sysreset_probe, + .platdata_auto_alloc_size = sizeof(struct x86_sysreset_platdata), }; -- cgit v1.2.3 From f77ab00e898bb24323d027f3f27c5661e19c8461 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 2 May 2019 10:52:15 -0600 Subject: x86: sysreset: Implement the get_last() method Add a default implementation of this method which always indicates that the last reset was a power-on reset. This is the most likely type of reset and without a PCH-specific driver we cannot determine any other type. Signed-off-by: Simon Glass Reviewed-by: Bin Meng --- drivers/sysreset/sysreset_x86.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/sysreset/sysreset_x86.c') diff --git a/drivers/sysreset/sysreset_x86.c b/drivers/sysreset/sysreset_x86.c index ca892b305e2..072f7948efa 100644 --- a/drivers/sysreset/sysreset_x86.c +++ b/drivers/sysreset/sysreset_x86.c @@ -98,6 +98,11 @@ static int x86_sysreset_request(struct udevice *dev, enum sysreset_t type) 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, @@ -140,6 +145,7 @@ static const struct udevice_id x86_sysreset_ids[] = { static struct sysreset_ops x86_sysreset_ops = { .request = x86_sysreset_request, + .get_last = x86_sysreset_get_last, }; U_BOOT_DRIVER(x86_sysreset) = { -- cgit v1.2.3