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