^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-or-later
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Copyright (C) 2016 Imagination Technologies
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) * Author: Paul Burton <paul.burton@mips.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) #include <linux/delay.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) #include <linux/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <linux/pci.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/pm.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) static struct pci_dev *pm_dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) static resource_size_t io_offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) enum piix4_pm_io_reg {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) PIIX4_FUNC3IO_PMSTS = 0x00,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #define PIIX4_FUNC3IO_PMSTS_PWRBTN_STS BIT(8)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) PIIX4_FUNC3IO_PMCNTRL = 0x04,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #define PIIX4_FUNC3IO_PMCNTRL_SUS_EN BIT(13)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #define PIIX4_FUNC3IO_PMCNTRL_SUS_TYP_SOFF (0x0 << 10)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #define PIIX4_SUSPEND_MAGIC 0x00120002
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) static const int piix4_pm_io_region = PCI_BRIDGE_RESOURCES;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) static void piix4_poweroff(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) int spec_devid;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) u16 sts;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) /* Ensure the power button status is clear */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) while (1) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) sts = inw(io_offset + PIIX4_FUNC3IO_PMSTS);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) if (!(sts & PIIX4_FUNC3IO_PMSTS_PWRBTN_STS))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) outw(sts, io_offset + PIIX4_FUNC3IO_PMSTS);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) /* Enable entry to suspend */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) outw(PIIX4_FUNC3IO_PMCNTRL_SUS_TYP_SOFF | PIIX4_FUNC3IO_PMCNTRL_SUS_EN,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) io_offset + PIIX4_FUNC3IO_PMCNTRL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) /* If the special cycle occurs too soon this doesn't work... */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) mdelay(10);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) * The PIIX4 will enter the suspend state only after seeing a special
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) * cycle with the correct magic data on the PCI bus. Generate that
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) * cycle now.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) spec_devid = PCI_DEVID(0, PCI_DEVFN(0x1f, 0x7));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) pci_bus_write_config_dword(pm_dev->bus, spec_devid, 0,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) PIIX4_SUSPEND_MAGIC);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) /* Give the system some time to power down, then error */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) mdelay(1000);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) pr_emerg("Unable to poweroff system\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) static int piix4_poweroff_probe(struct pci_dev *dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) const struct pci_device_id *id)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) int res;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) if (pm_dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) /* Request access to the PIIX4 PM IO registers */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) res = pci_request_region(dev, piix4_pm_io_region,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) "PIIX4 PM IO registers");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) if (res) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) dev_err(&dev->dev, "failed to request PM IO registers: %d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) res);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) return res;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) pm_dev = dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) io_offset = pci_resource_start(dev, piix4_pm_io_region);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) pm_power_off = piix4_poweroff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) static void piix4_poweroff_remove(struct pci_dev *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) if (pm_power_off == piix4_poweroff)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) pm_power_off = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) pci_release_region(dev, piix4_pm_io_region);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) pm_dev = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) static const struct pci_device_id piix4_poweroff_ids[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) { 0 },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) static struct pci_driver piix4_poweroff_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) .name = "piix4-poweroff",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) .id_table = piix4_poweroff_ids,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) .probe = piix4_poweroff_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) .remove = piix4_poweroff_remove,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) module_pci_driver(piix4_poweroff_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) MODULE_AUTHOR("Paul Burton <paul.burton@mips.com>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) MODULE_LICENSE("GPL");