162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * PCI Backend - Handles the virtual fields found on the capability lists 462306a36Sopenharmony_ci * in the configuration space. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Author: Ryan Wilson <hap9@epoch.ncsc.mil> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/pci.h> 1162306a36Sopenharmony_ci#include "pciback.h" 1262306a36Sopenharmony_ci#include "conf_space.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_cistatic LIST_HEAD(capabilities); 1562306a36Sopenharmony_cistruct xen_pcibk_config_capability { 1662306a36Sopenharmony_ci struct list_head cap_list; 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci int capability; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci /* If the device has the capability found above, add these fields */ 2162306a36Sopenharmony_ci const struct config_field *fields; 2262306a36Sopenharmony_ci}; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic const struct config_field caplist_header[] = { 2562306a36Sopenharmony_ci { 2662306a36Sopenharmony_ci .offset = PCI_CAP_LIST_ID, 2762306a36Sopenharmony_ci .size = 2, /* encompass PCI_CAP_LIST_ID & PCI_CAP_LIST_NEXT */ 2862306a36Sopenharmony_ci .u.w.read = xen_pcibk_read_config_word, 2962306a36Sopenharmony_ci .u.w.write = NULL, 3062306a36Sopenharmony_ci }, 3162306a36Sopenharmony_ci {} 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic inline void register_capability(struct xen_pcibk_config_capability *cap) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci list_add_tail(&cap->cap_list, &capabilities); 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ciint xen_pcibk_config_capability_add_fields(struct pci_dev *dev) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci int err = 0; 4262306a36Sopenharmony_ci struct xen_pcibk_config_capability *cap; 4362306a36Sopenharmony_ci int cap_offset; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci list_for_each_entry(cap, &capabilities, cap_list) { 4662306a36Sopenharmony_ci cap_offset = pci_find_capability(dev, cap->capability); 4762306a36Sopenharmony_ci if (cap_offset) { 4862306a36Sopenharmony_ci dev_dbg(&dev->dev, "Found capability 0x%x at 0x%x\n", 4962306a36Sopenharmony_ci cap->capability, cap_offset); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci err = xen_pcibk_config_add_fields_offset(dev, 5262306a36Sopenharmony_ci caplist_header, 5362306a36Sopenharmony_ci cap_offset); 5462306a36Sopenharmony_ci if (err) 5562306a36Sopenharmony_ci goto out; 5662306a36Sopenharmony_ci err = xen_pcibk_config_add_fields_offset(dev, 5762306a36Sopenharmony_ci cap->fields, 5862306a36Sopenharmony_ci cap_offset); 5962306a36Sopenharmony_ci if (err) 6062306a36Sopenharmony_ci goto out; 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ciout: 6562306a36Sopenharmony_ci return err; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic int vpd_address_write(struct pci_dev *dev, int offset, u16 value, 6962306a36Sopenharmony_ci void *data) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci /* Disallow writes to the vital product data */ 7262306a36Sopenharmony_ci if (value & PCI_VPD_ADDR_F) 7362306a36Sopenharmony_ci return PCIBIOS_SET_FAILED; 7462306a36Sopenharmony_ci else 7562306a36Sopenharmony_ci return pci_write_config_word(dev, offset, value); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic const struct config_field caplist_vpd[] = { 7962306a36Sopenharmony_ci { 8062306a36Sopenharmony_ci .offset = PCI_VPD_ADDR, 8162306a36Sopenharmony_ci .size = 2, 8262306a36Sopenharmony_ci .u.w.read = xen_pcibk_read_config_word, 8362306a36Sopenharmony_ci .u.w.write = vpd_address_write, 8462306a36Sopenharmony_ci }, 8562306a36Sopenharmony_ci { 8662306a36Sopenharmony_ci .offset = PCI_VPD_DATA, 8762306a36Sopenharmony_ci .size = 4, 8862306a36Sopenharmony_ci .u.dw.read = xen_pcibk_read_config_dword, 8962306a36Sopenharmony_ci .u.dw.write = NULL, 9062306a36Sopenharmony_ci }, 9162306a36Sopenharmony_ci {} 9262306a36Sopenharmony_ci}; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic int pm_caps_read(struct pci_dev *dev, int offset, u16 *value, 9562306a36Sopenharmony_ci void *data) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci int err; 9862306a36Sopenharmony_ci u16 real_value; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci err = pci_read_config_word(dev, offset, &real_value); 10162306a36Sopenharmony_ci if (err) 10262306a36Sopenharmony_ci goto out; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci *value = real_value & ~PCI_PM_CAP_PME_MASK; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ciout: 10762306a36Sopenharmony_ci return err; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci/* PM_OK_BITS specifies the bits that the driver domain is allowed to change. 11162306a36Sopenharmony_ci * Can't allow driver domain to enable PMEs - they're shared */ 11262306a36Sopenharmony_ci#define PM_OK_BITS (PCI_PM_CTRL_PME_STATUS|PCI_PM_CTRL_DATA_SEL_MASK) 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic int pm_ctrl_write(struct pci_dev *dev, int offset, u16 new_value, 11562306a36Sopenharmony_ci void *data) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci int err; 11862306a36Sopenharmony_ci u16 old_value; 11962306a36Sopenharmony_ci pci_power_t new_state; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci err = pci_read_config_word(dev, offset, &old_value); 12262306a36Sopenharmony_ci if (err) 12362306a36Sopenharmony_ci goto out; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci new_state = (pci_power_t)(new_value & PCI_PM_CTRL_STATE_MASK); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci new_value &= PM_OK_BITS; 12862306a36Sopenharmony_ci if ((old_value & PM_OK_BITS) != new_value) { 12962306a36Sopenharmony_ci new_value = (old_value & ~PM_OK_BITS) | new_value; 13062306a36Sopenharmony_ci err = pci_write_config_word(dev, offset, new_value); 13162306a36Sopenharmony_ci if (err) 13262306a36Sopenharmony_ci goto out; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* Let pci core handle the power management change */ 13662306a36Sopenharmony_ci dev_dbg(&dev->dev, "set power state to %x\n", new_state); 13762306a36Sopenharmony_ci err = pci_set_power_state(dev, new_state); 13862306a36Sopenharmony_ci if (err) { 13962306a36Sopenharmony_ci err = PCIBIOS_SET_FAILED; 14062306a36Sopenharmony_ci goto out; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci out: 14462306a36Sopenharmony_ci return err; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci/* Ensure PMEs are disabled */ 14862306a36Sopenharmony_cistatic void *pm_ctrl_init(struct pci_dev *dev, int offset) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci int err; 15162306a36Sopenharmony_ci u16 value; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci err = pci_read_config_word(dev, offset, &value); 15462306a36Sopenharmony_ci if (err) 15562306a36Sopenharmony_ci goto out; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (value & PCI_PM_CTRL_PME_ENABLE) { 15862306a36Sopenharmony_ci value &= ~PCI_PM_CTRL_PME_ENABLE; 15962306a36Sopenharmony_ci err = pci_write_config_word(dev, offset, value); 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ciout: 16362306a36Sopenharmony_ci return err ? ERR_PTR(err) : NULL; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic const struct config_field caplist_pm[] = { 16762306a36Sopenharmony_ci { 16862306a36Sopenharmony_ci .offset = PCI_PM_PMC, 16962306a36Sopenharmony_ci .size = 2, 17062306a36Sopenharmony_ci .u.w.read = pm_caps_read, 17162306a36Sopenharmony_ci }, 17262306a36Sopenharmony_ci { 17362306a36Sopenharmony_ci .offset = PCI_PM_CTRL, 17462306a36Sopenharmony_ci .size = 2, 17562306a36Sopenharmony_ci .init = pm_ctrl_init, 17662306a36Sopenharmony_ci .u.w.read = xen_pcibk_read_config_word, 17762306a36Sopenharmony_ci .u.w.write = pm_ctrl_write, 17862306a36Sopenharmony_ci }, 17962306a36Sopenharmony_ci { 18062306a36Sopenharmony_ci .offset = PCI_PM_PPB_EXTENSIONS, 18162306a36Sopenharmony_ci .size = 1, 18262306a36Sopenharmony_ci .u.b.read = xen_pcibk_read_config_byte, 18362306a36Sopenharmony_ci }, 18462306a36Sopenharmony_ci { 18562306a36Sopenharmony_ci .offset = PCI_PM_DATA_REGISTER, 18662306a36Sopenharmony_ci .size = 1, 18762306a36Sopenharmony_ci .u.b.read = xen_pcibk_read_config_byte, 18862306a36Sopenharmony_ci }, 18962306a36Sopenharmony_ci {} 19062306a36Sopenharmony_ci}; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic struct msi_msix_field_config { 19362306a36Sopenharmony_ci u16 enable_bit; /* bit for enabling MSI/MSI-X */ 19462306a36Sopenharmony_ci u16 allowed_bits; /* bits allowed to be changed */ 19562306a36Sopenharmony_ci unsigned int int_type; /* interrupt type for exclusiveness check */ 19662306a36Sopenharmony_ci} msi_field_config = { 19762306a36Sopenharmony_ci .enable_bit = PCI_MSI_FLAGS_ENABLE, 19862306a36Sopenharmony_ci .allowed_bits = PCI_MSI_FLAGS_ENABLE, 19962306a36Sopenharmony_ci .int_type = INTERRUPT_TYPE_MSI, 20062306a36Sopenharmony_ci}, msix_field_config = { 20162306a36Sopenharmony_ci .enable_bit = PCI_MSIX_FLAGS_ENABLE, 20262306a36Sopenharmony_ci .allowed_bits = PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL, 20362306a36Sopenharmony_ci .int_type = INTERRUPT_TYPE_MSIX, 20462306a36Sopenharmony_ci}; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic void *msi_field_init(struct pci_dev *dev, int offset) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci return &msi_field_config; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic void *msix_field_init(struct pci_dev *dev, int offset) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci return &msix_field_config; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic int msi_msix_flags_write(struct pci_dev *dev, int offset, u16 new_value, 21762306a36Sopenharmony_ci void *data) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci int err; 22062306a36Sopenharmony_ci u16 old_value; 22162306a36Sopenharmony_ci const struct msi_msix_field_config *field_config = data; 22262306a36Sopenharmony_ci const struct xen_pcibk_dev_data *dev_data = pci_get_drvdata(dev); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (xen_pcibk_permissive || dev_data->permissive) 22562306a36Sopenharmony_ci goto write; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci err = pci_read_config_word(dev, offset, &old_value); 22862306a36Sopenharmony_ci if (err) 22962306a36Sopenharmony_ci return err; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (new_value == old_value) 23262306a36Sopenharmony_ci return 0; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (!dev_data->allow_interrupt_control || 23562306a36Sopenharmony_ci (new_value ^ old_value) & ~field_config->allowed_bits) 23662306a36Sopenharmony_ci return PCIBIOS_SET_FAILED; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (new_value & field_config->enable_bit) { 23962306a36Sopenharmony_ci /* 24062306a36Sopenharmony_ci * Don't allow enabling together with other interrupt type, but do 24162306a36Sopenharmony_ci * allow enabling MSI(-X) while INTx is still active to please Linuxes 24262306a36Sopenharmony_ci * MSI(-X) startup sequence. It is safe to do, as according to PCI 24362306a36Sopenharmony_ci * spec, device with enabled MSI(-X) shouldn't use INTx. 24462306a36Sopenharmony_ci */ 24562306a36Sopenharmony_ci int int_type = xen_pcibk_get_interrupt_type(dev); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (int_type == INTERRUPT_TYPE_NONE || 24862306a36Sopenharmony_ci int_type == INTERRUPT_TYPE_INTX || 24962306a36Sopenharmony_ci int_type == field_config->int_type) 25062306a36Sopenharmony_ci goto write; 25162306a36Sopenharmony_ci return PCIBIOS_SET_FAILED; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ciwrite: 25562306a36Sopenharmony_ci return pci_write_config_word(dev, offset, new_value); 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_cistatic const struct config_field caplist_msix[] = { 25962306a36Sopenharmony_ci { 26062306a36Sopenharmony_ci .offset = PCI_MSIX_FLAGS, 26162306a36Sopenharmony_ci .size = 2, 26262306a36Sopenharmony_ci .init = msix_field_init, 26362306a36Sopenharmony_ci .u.w.read = xen_pcibk_read_config_word, 26462306a36Sopenharmony_ci .u.w.write = msi_msix_flags_write, 26562306a36Sopenharmony_ci }, 26662306a36Sopenharmony_ci {} 26762306a36Sopenharmony_ci}; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic const struct config_field caplist_msi[] = { 27062306a36Sopenharmony_ci { 27162306a36Sopenharmony_ci .offset = PCI_MSI_FLAGS, 27262306a36Sopenharmony_ci .size = 2, 27362306a36Sopenharmony_ci .init = msi_field_init, 27462306a36Sopenharmony_ci .u.w.read = xen_pcibk_read_config_word, 27562306a36Sopenharmony_ci .u.w.write = msi_msix_flags_write, 27662306a36Sopenharmony_ci }, 27762306a36Sopenharmony_ci {} 27862306a36Sopenharmony_ci}; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic struct xen_pcibk_config_capability xen_pcibk_config_capability_pm = { 28162306a36Sopenharmony_ci .capability = PCI_CAP_ID_PM, 28262306a36Sopenharmony_ci .fields = caplist_pm, 28362306a36Sopenharmony_ci}; 28462306a36Sopenharmony_cistatic struct xen_pcibk_config_capability xen_pcibk_config_capability_vpd = { 28562306a36Sopenharmony_ci .capability = PCI_CAP_ID_VPD, 28662306a36Sopenharmony_ci .fields = caplist_vpd, 28762306a36Sopenharmony_ci}; 28862306a36Sopenharmony_cistatic struct xen_pcibk_config_capability xen_pcibk_config_capability_msi = { 28962306a36Sopenharmony_ci .capability = PCI_CAP_ID_MSI, 29062306a36Sopenharmony_ci .fields = caplist_msi, 29162306a36Sopenharmony_ci}; 29262306a36Sopenharmony_cistatic struct xen_pcibk_config_capability xen_pcibk_config_capability_msix = { 29362306a36Sopenharmony_ci .capability = PCI_CAP_ID_MSIX, 29462306a36Sopenharmony_ci .fields = caplist_msix, 29562306a36Sopenharmony_ci}; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ciint xen_pcibk_config_capability_init(void) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci register_capability(&xen_pcibk_config_capability_vpd); 30062306a36Sopenharmony_ci register_capability(&xen_pcibk_config_capability_pm); 30162306a36Sopenharmony_ci register_capability(&xen_pcibk_config_capability_msi); 30262306a36Sopenharmony_ci register_capability(&xen_pcibk_config_capability_msix); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci return 0; 30562306a36Sopenharmony_ci} 306