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