1/* 2 * Copyright (C) 2014 Imagination Technologies 3 * Author: Paul Burton <paul.burton@imgtec.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/init.h> 13#include <linux/io.h> 14#include <linux/pci.h> 15 16#include <asm/mach-malta/malta-pm.h> 17 18static struct pci_bus *pm_pci_bus; 19static resource_size_t pm_io_offset; 20 21int mips_pm_suspend(unsigned state) 22{ 23 int spec_devid; 24 u16 sts; 25 26 if (!pm_pci_bus || !pm_io_offset) 27 return -ENODEV; 28 29 /* Ensure the power button status is clear */ 30 while (1) { 31 sts = inw(pm_io_offset + PIIX4_FUNC3IO_PMSTS); 32 if (!(sts & PIIX4_FUNC3IO_PMSTS_PWRBTN_STS)) 33 break; 34 outw(sts, pm_io_offset + PIIX4_FUNC3IO_PMSTS); 35 } 36 37 /* Enable entry to suspend */ 38 outw(state | PIIX4_FUNC3IO_PMCNTRL_SUS_EN, 39 pm_io_offset + PIIX4_FUNC3IO_PMCNTRL); 40 41 /* If the special cycle occurs too soon this doesn't work... */ 42 mdelay(10); 43 44 /* 45 * The PIIX4 will enter the suspend state only after seeing a special 46 * cycle with the correct magic data on the PCI bus. Generate that 47 * cycle now. 48 */ 49 spec_devid = PCI_DEVID(0, PCI_DEVFN(0x1f, 0x7)); 50 pci_bus_write_config_dword(pm_pci_bus, spec_devid, 0, 51 PIIX4_SUSPEND_MAGIC); 52 53 /* Give the system some time to power down */ 54 mdelay(1000); 55 56 return 0; 57} 58 59static int __init malta_pm_setup(void) 60{ 61 struct pci_dev *dev; 62 int res, io_region = PCI_BRIDGE_RESOURCES; 63 64 /* Find a reference to the PCI bus */ 65 pm_pci_bus = pci_find_next_bus(NULL); 66 if (!pm_pci_bus) { 67 pr_warn("malta-pm: failed to find reference to PCI bus\n"); 68 return -ENODEV; 69 } 70 71 /* Find the PIIX4 PM device */ 72 dev = pci_get_subsys(PCI_VENDOR_ID_INTEL, 73 PCI_DEVICE_ID_INTEL_82371AB_3, PCI_ANY_ID, 74 PCI_ANY_ID, NULL); 75 if (!dev) { 76 pr_warn("malta-pm: failed to find PIIX4 PM\n"); 77 return -ENODEV; 78 } 79 80 /* Request access to the PIIX4 PM IO registers */ 81 res = pci_request_region(dev, io_region, "PIIX4 PM IO registers"); 82 if (res) { 83 pr_warn("malta-pm: failed to request PM IO registers (%d)\n", 84 res); 85 pci_dev_put(dev); 86 return -ENODEV; 87 } 88 89 /* Find the offset to the PIIX4 PM IO registers */ 90 pm_io_offset = pci_resource_start(dev, io_region); 91 92 pci_dev_put(dev); 93 return 0; 94} 95 96late_initcall(malta_pm_setup); 97