- Updated to 2.6.22-rc2-git7:
[linux-flexiantxendom0-3.2.10.git] / drivers / xen / pciback / conf_space_capability_pm.c
1 /*
2  * PCI Backend - Configuration space overlay for power management
3  *
4  * Author: Ryan Wilson <hap9@epoch.ncsc.mil>
5  */
6
7 #include <linux/pci.h>
8 #include "conf_space.h"
9 #include "conf_space_capability.h"
10
11 static int pm_caps_read(struct pci_dev *dev, int offset, u16 *value,
12                         void *data)
13 {
14         int err;
15         u16 real_value;
16
17         err = pci_read_config_word(dev, offset, &real_value);
18         if (err)
19                 goto out;
20
21         *value = real_value & ~PCI_PM_CAP_PME_MASK;
22
23       out:
24         return err;
25 }
26
27 /* PM_OK_BITS specifies the bits that the driver domain is allowed to change.
28  * Can't allow driver domain to enable PMEs - they're shared */
29 #define PM_OK_BITS (PCI_PM_CTRL_PME_STATUS|PCI_PM_CTRL_DATA_SEL_MASK)
30
31 static int pm_ctrl_write(struct pci_dev *dev, int offset, u16 new_value,
32                          void *data)
33 {
34         int err;
35         u16 old_value;
36         pci_power_t new_state, old_state;
37
38         err = pci_read_config_word(dev, offset, &old_value);
39         if (err)
40                 goto out;
41
42         old_state = (pci_power_t)(old_value & PCI_PM_CTRL_STATE_MASK);
43         new_state = (pci_power_t)(new_value & PCI_PM_CTRL_STATE_MASK);
44
45         new_value &= PM_OK_BITS;
46         if ((old_value & PM_OK_BITS) != new_value) {
47                 new_value = (old_value & ~PM_OK_BITS) | new_value;
48                 err = pci_write_config_word(dev, offset, new_value);
49                 if (err)
50                         goto out;
51         }
52
53         /* Let pci core handle the power management change */
54         dev_dbg(&dev->dev, "set power state to %x\n", new_state);
55         err = pci_set_power_state(dev, new_state);
56         if (err) {
57                 err = PCIBIOS_SET_FAILED;
58                 goto out;
59         }
60
61         /*
62          * Device may lose PCI config info on D3->D0 transition. This
63          * is a problem for some guests which will not reset BARs. Even
64          * those that have a go will be foiled by our BAR-write handler
65          * which will discard the write! Since Linux won't re-init
66          * the config space automatically in all cases, we do it here.
67          * Future: Should we re-initialise all first 64 bytes of config space?
68          */
69         if (new_state == PCI_D0 &&
70             (old_state == PCI_D3hot || old_state == PCI_D3cold) &&
71             !(old_value & PCI_PM_CTRL_NO_SOFT_RESET))
72                 pci_restore_bars(dev);
73
74  out:
75         return err;
76 }
77
78 /* Ensure PMEs are disabled */
79 static void *pm_ctrl_init(struct pci_dev *dev, int offset)
80 {
81         int err;
82         u16 value;
83
84         err = pci_read_config_word(dev, offset, &value);
85         if (err)
86                 goto out;
87
88         if (value & PCI_PM_CTRL_PME_ENABLE) {
89                 value &= ~PCI_PM_CTRL_PME_ENABLE;
90                 err = pci_write_config_word(dev, offset, value);
91         }
92
93       out:
94         return ERR_PTR(err);
95 }
96
97 static struct config_field caplist_pm[] = {
98         {
99                 .offset     = PCI_PM_PMC,
100                 .size       = 2,
101                 .u.w.read   = pm_caps_read,
102         },
103         {
104                 .offset     = PCI_PM_CTRL,
105                 .size       = 2,
106                 .init       = pm_ctrl_init,
107                 .u.w.read   = pciback_read_config_word,
108                 .u.w.write  = pm_ctrl_write,
109         },
110         {
111                 .offset     = PCI_PM_PPB_EXTENSIONS,
112                 .size       = 1,
113                 .u.b.read   = pciback_read_config_byte,
114         },
115         {
116                 .offset     = PCI_PM_DATA_REGISTER,
117                 .size       = 1,
118                 .u.b.read   = pciback_read_config_byte,
119         },
120         {
121                 .size = 0,
122         },
123 };
124
125 struct pciback_config_capability pciback_config_capability_pm = {
126         .capability = PCI_CAP_ID_PM,
127         .fields = caplist_pm,
128 };