Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 | // SPDX-License-Identifier: GPL-2.0 /* * PCI Backend - Handles the virtual fields found on the capability lists * in the configuration space. * * Author: Ryan Wilson <hap9@epoch.ncsc.mil> */ #include <linux/kernel.h> #include <linux/pci.h> #include "pciback.h" #include "conf_space.h" static LIST_HEAD(capabilities); struct xen_pcibk_config_capability { struct list_head cap_list; int capability; /* If the device has the capability found above, add these fields */ const struct config_field *fields; }; static const struct config_field caplist_header[] = { { .offset = PCI_CAP_LIST_ID, .size = 2, /* encompass PCI_CAP_LIST_ID & PCI_CAP_LIST_NEXT */ .u.w.read = xen_pcibk_read_config_word, .u.w.write = NULL, }, {} }; static inline void register_capability(struct xen_pcibk_config_capability *cap) { list_add_tail(&cap->cap_list, &capabilities); } int xen_pcibk_config_capability_add_fields(struct pci_dev *dev) { int err = 0; struct xen_pcibk_config_capability *cap; int cap_offset; list_for_each_entry(cap, &capabilities, cap_list) { cap_offset = pci_find_capability(dev, cap->capability); if (cap_offset) { dev_dbg(&dev->dev, "Found capability 0x%x at 0x%x\n", cap->capability, cap_offset); err = xen_pcibk_config_add_fields_offset(dev, caplist_header, cap_offset); if (err) goto out; err = xen_pcibk_config_add_fields_offset(dev, cap->fields, cap_offset); if (err) goto out; } } out: return err; } static int vpd_address_write(struct pci_dev *dev, int offset, u16 value, void *data) { /* Disallow writes to the vital product data */ if (value & PCI_VPD_ADDR_F) return PCIBIOS_SET_FAILED; else return pci_write_config_word(dev, offset, value); } static const struct config_field caplist_vpd[] = { { .offset = PCI_VPD_ADDR, .size = 2, .u.w.read = xen_pcibk_read_config_word, .u.w.write = vpd_address_write, }, { .offset = PCI_VPD_DATA, .size = 4, .u.dw.read = xen_pcibk_read_config_dword, .u.dw.write = NULL, }, {} }; static int pm_caps_read(struct pci_dev *dev, int offset, u16 *value, void *data) { int err; u16 real_value; err = pci_read_config_word(dev, offset, &real_value); if (err) goto out; *value = real_value & ~PCI_PM_CAP_PME_MASK; out: return err; } /* PM_OK_BITS specifies the bits that the driver domain is allowed to change. * Can't allow driver domain to enable PMEs - they're shared */ #define PM_OK_BITS (PCI_PM_CTRL_PME_STATUS|PCI_PM_CTRL_DATA_SEL_MASK) static int pm_ctrl_write(struct pci_dev *dev, int offset, u16 new_value, void *data) { int err; u16 old_value; pci_power_t new_state, old_state; err = pci_read_config_word(dev, offset, &old_value); if (err) goto out; old_state = (pci_power_t)(old_value & PCI_PM_CTRL_STATE_MASK); new_state = (pci_power_t)(new_value & PCI_PM_CTRL_STATE_MASK); new_value &= PM_OK_BITS; if ((old_value & PM_OK_BITS) != new_value) { new_value = (old_value & ~PM_OK_BITS) | new_value; err = pci_write_config_word(dev, offset, new_value); if (err) goto out; } /* Let pci core handle the power management change */ dev_dbg(&dev->dev, "set power state to %x\n", new_state); err = pci_set_power_state(dev, new_state); if (err) { err = PCIBIOS_SET_FAILED; goto out; } out: return err; } /* Ensure PMEs are disabled */ static void *pm_ctrl_init(struct pci_dev *dev, int offset) { int err; u16 value; err = pci_read_config_word(dev, offset, &value); if (err) goto out; if (value & PCI_PM_CTRL_PME_ENABLE) { value &= ~PCI_PM_CTRL_PME_ENABLE; err = pci_write_config_word(dev, offset, value); } out: return ERR_PTR(err); } static const struct config_field caplist_pm[] = { { .offset = PCI_PM_PMC, .size = 2, .u.w.read = pm_caps_read, }, { .offset = PCI_PM_CTRL, .size = 2, .init = pm_ctrl_init, .u.w.read = xen_pcibk_read_config_word, .u.w.write = pm_ctrl_write, }, { .offset = PCI_PM_PPB_EXTENSIONS, .size = 1, .u.b.read = xen_pcibk_read_config_byte, }, { .offset = PCI_PM_DATA_REGISTER, .size = 1, .u.b.read = xen_pcibk_read_config_byte, }, {} }; static struct xen_pcibk_config_capability xen_pcibk_config_capability_pm = { .capability = PCI_CAP_ID_PM, .fields = caplist_pm, }; static struct xen_pcibk_config_capability xen_pcibk_config_capability_vpd = { .capability = PCI_CAP_ID_VPD, .fields = caplist_vpd, }; int xen_pcibk_config_capability_init(void) { register_capability(&xen_pcibk_config_capability_vpd); register_capability(&xen_pcibk_config_capability_pm); return 0; } |