xref: /linux/drivers/acpi/reboot.c (revision fd639726bf15fca8ee1a00dce8e0096d0ad9bd18)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 #include <linux/pci.h>
4 #include <linux/acpi.h>
5 #include <acpi/reboot.h>
6 
7 void acpi_reboot(void)
8 {
9 	struct acpi_generic_address *rr;
10 	struct pci_bus *bus0;
11 	u8 reset_value;
12 	unsigned int devfn;
13 
14 	if (acpi_disabled)
15 		return;
16 
17 	rr = &acpi_gbl_FADT.reset_register;
18 
19 	/* ACPI reset register was only introduced with v2 of the FADT */
20 
21 	if (acpi_gbl_FADT.header.revision < 2)
22 		return;
23 
24 	/* Is the reset register supported? The spec says we should be
25 	 * checking the bit width and bit offset, but Windows ignores
26 	 * these fields */
27 	if (!(acpi_gbl_FADT.flags & ACPI_FADT_RESET_REGISTER))
28 		return;
29 
30 	reset_value = acpi_gbl_FADT.reset_value;
31 
32 	/* The reset register can only exist in I/O, Memory or PCI config space
33 	 * on a device on bus 0. */
34 	switch (rr->space_id) {
35 	case ACPI_ADR_SPACE_PCI_CONFIG:
36 		/* The reset register can only live on bus 0. */
37 		bus0 = pci_find_bus(0, 0);
38 		if (!bus0)
39 			return;
40 		/* Form PCI device/function pair. */
41 		devfn = PCI_DEVFN((rr->address >> 32) & 0xffff,
42 				  (rr->address >> 16) & 0xffff);
43 		printk(KERN_DEBUG "Resetting with ACPI PCI RESET_REG.");
44 		/* Write the value that resets us. */
45 		pci_bus_write_config_byte(bus0, devfn,
46 				(rr->address & 0xffff), reset_value);
47 		break;
48 
49 	case ACPI_ADR_SPACE_SYSTEM_MEMORY:
50 	case ACPI_ADR_SPACE_SYSTEM_IO:
51 		printk(KERN_DEBUG "ACPI MEMORY or I/O RESET_REG.\n");
52 		acpi_reset();
53 		break;
54 	}
55 }
56