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