18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PCI Backend - Handles the virtual fields found on the capability lists 48c2ecf20Sopenharmony_ci * in the configuration space. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Author: Ryan Wilson <hap9@epoch.ncsc.mil> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/pci.h> 118c2ecf20Sopenharmony_ci#include "pciback.h" 128c2ecf20Sopenharmony_ci#include "conf_space.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_cistatic LIST_HEAD(capabilities); 158c2ecf20Sopenharmony_cistruct xen_pcibk_config_capability { 168c2ecf20Sopenharmony_ci struct list_head cap_list; 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci int capability; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci /* If the device has the capability found above, add these fields */ 218c2ecf20Sopenharmony_ci const struct config_field *fields; 228c2ecf20Sopenharmony_ci}; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic const struct config_field caplist_header[] = { 258c2ecf20Sopenharmony_ci { 268c2ecf20Sopenharmony_ci .offset = PCI_CAP_LIST_ID, 278c2ecf20Sopenharmony_ci .size = 2, /* encompass PCI_CAP_LIST_ID & PCI_CAP_LIST_NEXT */ 288c2ecf20Sopenharmony_ci .u.w.read = xen_pcibk_read_config_word, 298c2ecf20Sopenharmony_ci .u.w.write = NULL, 308c2ecf20Sopenharmony_ci }, 318c2ecf20Sopenharmony_ci {} 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic inline void register_capability(struct xen_pcibk_config_capability *cap) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci list_add_tail(&cap->cap_list, &capabilities); 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ciint xen_pcibk_config_capability_add_fields(struct pci_dev *dev) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci int err = 0; 428c2ecf20Sopenharmony_ci struct xen_pcibk_config_capability *cap; 438c2ecf20Sopenharmony_ci int cap_offset; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci list_for_each_entry(cap, &capabilities, cap_list) { 468c2ecf20Sopenharmony_ci cap_offset = pci_find_capability(dev, cap->capability); 478c2ecf20Sopenharmony_ci if (cap_offset) { 488c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "Found capability 0x%x at 0x%x\n", 498c2ecf20Sopenharmony_ci cap->capability, cap_offset); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci err = xen_pcibk_config_add_fields_offset(dev, 528c2ecf20Sopenharmony_ci caplist_header, 538c2ecf20Sopenharmony_ci cap_offset); 548c2ecf20Sopenharmony_ci if (err) 558c2ecf20Sopenharmony_ci goto out; 568c2ecf20Sopenharmony_ci err = xen_pcibk_config_add_fields_offset(dev, 578c2ecf20Sopenharmony_ci cap->fields, 588c2ecf20Sopenharmony_ci cap_offset); 598c2ecf20Sopenharmony_ci if (err) 608c2ecf20Sopenharmony_ci goto out; 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci } 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ciout: 658c2ecf20Sopenharmony_ci return err; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic int vpd_address_write(struct pci_dev *dev, int offset, u16 value, 698c2ecf20Sopenharmony_ci void *data) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci /* Disallow writes to the vital product data */ 728c2ecf20Sopenharmony_ci if (value & PCI_VPD_ADDR_F) 738c2ecf20Sopenharmony_ci return PCIBIOS_SET_FAILED; 748c2ecf20Sopenharmony_ci else 758c2ecf20Sopenharmony_ci return pci_write_config_word(dev, offset, value); 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic const struct config_field caplist_vpd[] = { 798c2ecf20Sopenharmony_ci { 808c2ecf20Sopenharmony_ci .offset = PCI_VPD_ADDR, 818c2ecf20Sopenharmony_ci .size = 2, 828c2ecf20Sopenharmony_ci .u.w.read = xen_pcibk_read_config_word, 838c2ecf20Sopenharmony_ci .u.w.write = vpd_address_write, 848c2ecf20Sopenharmony_ci }, 858c2ecf20Sopenharmony_ci { 868c2ecf20Sopenharmony_ci .offset = PCI_VPD_DATA, 878c2ecf20Sopenharmony_ci .size = 4, 888c2ecf20Sopenharmony_ci .u.dw.read = xen_pcibk_read_config_dword, 898c2ecf20Sopenharmony_ci .u.dw.write = NULL, 908c2ecf20Sopenharmony_ci }, 918c2ecf20Sopenharmony_ci {} 928c2ecf20Sopenharmony_ci}; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic int pm_caps_read(struct pci_dev *dev, int offset, u16 *value, 958c2ecf20Sopenharmony_ci void *data) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci int err; 988c2ecf20Sopenharmony_ci u16 real_value; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci err = pci_read_config_word(dev, offset, &real_value); 1018c2ecf20Sopenharmony_ci if (err) 1028c2ecf20Sopenharmony_ci goto out; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci *value = real_value & ~PCI_PM_CAP_PME_MASK; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ciout: 1078c2ecf20Sopenharmony_ci return err; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci/* PM_OK_BITS specifies the bits that the driver domain is allowed to change. 1118c2ecf20Sopenharmony_ci * Can't allow driver domain to enable PMEs - they're shared */ 1128c2ecf20Sopenharmony_ci#define PM_OK_BITS (PCI_PM_CTRL_PME_STATUS|PCI_PM_CTRL_DATA_SEL_MASK) 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic int pm_ctrl_write(struct pci_dev *dev, int offset, u16 new_value, 1158c2ecf20Sopenharmony_ci void *data) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci int err; 1188c2ecf20Sopenharmony_ci u16 old_value; 1198c2ecf20Sopenharmony_ci pci_power_t new_state; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci err = pci_read_config_word(dev, offset, &old_value); 1228c2ecf20Sopenharmony_ci if (err) 1238c2ecf20Sopenharmony_ci goto out; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci new_state = (pci_power_t)(new_value & PCI_PM_CTRL_STATE_MASK); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci new_value &= PM_OK_BITS; 1288c2ecf20Sopenharmony_ci if ((old_value & PM_OK_BITS) != new_value) { 1298c2ecf20Sopenharmony_ci new_value = (old_value & ~PM_OK_BITS) | new_value; 1308c2ecf20Sopenharmony_ci err = pci_write_config_word(dev, offset, new_value); 1318c2ecf20Sopenharmony_ci if (err) 1328c2ecf20Sopenharmony_ci goto out; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* Let pci core handle the power management change */ 1368c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "set power state to %x\n", new_state); 1378c2ecf20Sopenharmony_ci err = pci_set_power_state(dev, new_state); 1388c2ecf20Sopenharmony_ci if (err) { 1398c2ecf20Sopenharmony_ci err = PCIBIOS_SET_FAILED; 1408c2ecf20Sopenharmony_ci goto out; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci out: 1448c2ecf20Sopenharmony_ci return err; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci/* Ensure PMEs are disabled */ 1488c2ecf20Sopenharmony_cistatic void *pm_ctrl_init(struct pci_dev *dev, int offset) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci int err; 1518c2ecf20Sopenharmony_ci u16 value; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci err = pci_read_config_word(dev, offset, &value); 1548c2ecf20Sopenharmony_ci if (err) 1558c2ecf20Sopenharmony_ci goto out; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (value & PCI_PM_CTRL_PME_ENABLE) { 1588c2ecf20Sopenharmony_ci value &= ~PCI_PM_CTRL_PME_ENABLE; 1598c2ecf20Sopenharmony_ci err = pci_write_config_word(dev, offset, value); 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ciout: 1638c2ecf20Sopenharmony_ci return err ? ERR_PTR(err) : NULL; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic const struct config_field caplist_pm[] = { 1678c2ecf20Sopenharmony_ci { 1688c2ecf20Sopenharmony_ci .offset = PCI_PM_PMC, 1698c2ecf20Sopenharmony_ci .size = 2, 1708c2ecf20Sopenharmony_ci .u.w.read = pm_caps_read, 1718c2ecf20Sopenharmony_ci }, 1728c2ecf20Sopenharmony_ci { 1738c2ecf20Sopenharmony_ci .offset = PCI_PM_CTRL, 1748c2ecf20Sopenharmony_ci .size = 2, 1758c2ecf20Sopenharmony_ci .init = pm_ctrl_init, 1768c2ecf20Sopenharmony_ci .u.w.read = xen_pcibk_read_config_word, 1778c2ecf20Sopenharmony_ci .u.w.write = pm_ctrl_write, 1788c2ecf20Sopenharmony_ci }, 1798c2ecf20Sopenharmony_ci { 1808c2ecf20Sopenharmony_ci .offset = PCI_PM_PPB_EXTENSIONS, 1818c2ecf20Sopenharmony_ci .size = 1, 1828c2ecf20Sopenharmony_ci .u.b.read = xen_pcibk_read_config_byte, 1838c2ecf20Sopenharmony_ci }, 1848c2ecf20Sopenharmony_ci { 1858c2ecf20Sopenharmony_ci .offset = PCI_PM_DATA_REGISTER, 1868c2ecf20Sopenharmony_ci .size = 1, 1878c2ecf20Sopenharmony_ci .u.b.read = xen_pcibk_read_config_byte, 1888c2ecf20Sopenharmony_ci }, 1898c2ecf20Sopenharmony_ci {} 1908c2ecf20Sopenharmony_ci}; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic struct msi_msix_field_config { 1938c2ecf20Sopenharmony_ci u16 enable_bit; /* bit for enabling MSI/MSI-X */ 1948c2ecf20Sopenharmony_ci u16 allowed_bits; /* bits allowed to be changed */ 1958c2ecf20Sopenharmony_ci unsigned int int_type; /* interrupt type for exclusiveness check */ 1968c2ecf20Sopenharmony_ci} msi_field_config = { 1978c2ecf20Sopenharmony_ci .enable_bit = PCI_MSI_FLAGS_ENABLE, 1988c2ecf20Sopenharmony_ci .allowed_bits = PCI_MSI_FLAGS_ENABLE, 1998c2ecf20Sopenharmony_ci .int_type = INTERRUPT_TYPE_MSI, 2008c2ecf20Sopenharmony_ci}, msix_field_config = { 2018c2ecf20Sopenharmony_ci .enable_bit = PCI_MSIX_FLAGS_ENABLE, 2028c2ecf20Sopenharmony_ci .allowed_bits = PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL, 2038c2ecf20Sopenharmony_ci .int_type = INTERRUPT_TYPE_MSIX, 2048c2ecf20Sopenharmony_ci}; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic void *msi_field_init(struct pci_dev *dev, int offset) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci return &msi_field_config; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic void *msix_field_init(struct pci_dev *dev, int offset) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci return &msix_field_config; 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic int msi_msix_flags_write(struct pci_dev *dev, int offset, u16 new_value, 2178c2ecf20Sopenharmony_ci void *data) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci int err; 2208c2ecf20Sopenharmony_ci u16 old_value; 2218c2ecf20Sopenharmony_ci const struct msi_msix_field_config *field_config = data; 2228c2ecf20Sopenharmony_ci const struct xen_pcibk_dev_data *dev_data = pci_get_drvdata(dev); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (xen_pcibk_permissive || dev_data->permissive) 2258c2ecf20Sopenharmony_ci goto write; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci err = pci_read_config_word(dev, offset, &old_value); 2288c2ecf20Sopenharmony_ci if (err) 2298c2ecf20Sopenharmony_ci return err; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (new_value == old_value) 2328c2ecf20Sopenharmony_ci return 0; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci if (!dev_data->allow_interrupt_control || 2358c2ecf20Sopenharmony_ci (new_value ^ old_value) & ~field_config->allowed_bits) 2368c2ecf20Sopenharmony_ci return PCIBIOS_SET_FAILED; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci if (new_value & field_config->enable_bit) { 2398c2ecf20Sopenharmony_ci /* 2408c2ecf20Sopenharmony_ci * Don't allow enabling together with other interrupt type, but do 2418c2ecf20Sopenharmony_ci * allow enabling MSI(-X) while INTx is still active to please Linuxes 2428c2ecf20Sopenharmony_ci * MSI(-X) startup sequence. It is safe to do, as according to PCI 2438c2ecf20Sopenharmony_ci * spec, device with enabled MSI(-X) shouldn't use INTx. 2448c2ecf20Sopenharmony_ci */ 2458c2ecf20Sopenharmony_ci int int_type = xen_pcibk_get_interrupt_type(dev); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (int_type == INTERRUPT_TYPE_NONE || 2488c2ecf20Sopenharmony_ci int_type == INTERRUPT_TYPE_INTX || 2498c2ecf20Sopenharmony_ci int_type == field_config->int_type) 2508c2ecf20Sopenharmony_ci goto write; 2518c2ecf20Sopenharmony_ci return PCIBIOS_SET_FAILED; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ciwrite: 2558c2ecf20Sopenharmony_ci return pci_write_config_word(dev, offset, new_value); 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_cistatic const struct config_field caplist_msix[] = { 2598c2ecf20Sopenharmony_ci { 2608c2ecf20Sopenharmony_ci .offset = PCI_MSIX_FLAGS, 2618c2ecf20Sopenharmony_ci .size = 2, 2628c2ecf20Sopenharmony_ci .init = msix_field_init, 2638c2ecf20Sopenharmony_ci .u.w.read = xen_pcibk_read_config_word, 2648c2ecf20Sopenharmony_ci .u.w.write = msi_msix_flags_write, 2658c2ecf20Sopenharmony_ci }, 2668c2ecf20Sopenharmony_ci {} 2678c2ecf20Sopenharmony_ci}; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic const struct config_field caplist_msi[] = { 2708c2ecf20Sopenharmony_ci { 2718c2ecf20Sopenharmony_ci .offset = PCI_MSI_FLAGS, 2728c2ecf20Sopenharmony_ci .size = 2, 2738c2ecf20Sopenharmony_ci .init = msi_field_init, 2748c2ecf20Sopenharmony_ci .u.w.read = xen_pcibk_read_config_word, 2758c2ecf20Sopenharmony_ci .u.w.write = msi_msix_flags_write, 2768c2ecf20Sopenharmony_ci }, 2778c2ecf20Sopenharmony_ci {} 2788c2ecf20Sopenharmony_ci}; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic struct xen_pcibk_config_capability xen_pcibk_config_capability_pm = { 2818c2ecf20Sopenharmony_ci .capability = PCI_CAP_ID_PM, 2828c2ecf20Sopenharmony_ci .fields = caplist_pm, 2838c2ecf20Sopenharmony_ci}; 2848c2ecf20Sopenharmony_cistatic struct xen_pcibk_config_capability xen_pcibk_config_capability_vpd = { 2858c2ecf20Sopenharmony_ci .capability = PCI_CAP_ID_VPD, 2868c2ecf20Sopenharmony_ci .fields = caplist_vpd, 2878c2ecf20Sopenharmony_ci}; 2888c2ecf20Sopenharmony_cistatic struct xen_pcibk_config_capability xen_pcibk_config_capability_msi = { 2898c2ecf20Sopenharmony_ci .capability = PCI_CAP_ID_MSI, 2908c2ecf20Sopenharmony_ci .fields = caplist_msi, 2918c2ecf20Sopenharmony_ci}; 2928c2ecf20Sopenharmony_cistatic struct xen_pcibk_config_capability xen_pcibk_config_capability_msix = { 2938c2ecf20Sopenharmony_ci .capability = PCI_CAP_ID_MSIX, 2948c2ecf20Sopenharmony_ci .fields = caplist_msix, 2958c2ecf20Sopenharmony_ci}; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ciint xen_pcibk_config_capability_init(void) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci register_capability(&xen_pcibk_config_capability_vpd); 3008c2ecf20Sopenharmony_ci register_capability(&xen_pcibk_config_capability_pm); 3018c2ecf20Sopenharmony_ci register_capability(&xen_pcibk_config_capability_msi); 3028c2ecf20Sopenharmony_ci register_capability(&xen_pcibk_config_capability_msix); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci return 0; 3058c2ecf20Sopenharmony_ci} 306