diff options
| -rw-r--r-- | drivers/pci/Kconfig | 9 | ||||
| -rw-r--r-- | drivers/pci/ats.c | 167 | ||||
| -rw-r--r-- | include/linux/pci-ats.h | 42 | ||||
| -rw-r--r-- | include/linux/pci_regs.h | 12 |
4 files changed, 230 insertions, 0 deletions
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 1d8ce8395861..fb1e9707f91e 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig | |||
| @@ -85,6 +85,15 @@ config PCI_IOV | |||
| 85 | 85 | ||
| 86 | If unsure, say N. | 86 | If unsure, say N. |
| 87 | 87 | ||
| 88 | config PCI_PRI | ||
| 89 | bool "PCI PRI support" | ||
| 90 | select PCI_ATS | ||
| 91 | help | ||
| 92 | PRI is the PCI Page Request Interface. It allows PCI devices that are | ||
| 93 | behind an IOMMU to recover from page faults. | ||
| 94 | |||
| 95 | If unsure, say N. | ||
| 96 | |||
| 88 | config PCI_IOAPIC | 97 | config PCI_IOAPIC |
| 89 | bool | 98 | bool |
| 90 | depends on PCI | 99 | depends on PCI |
diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c index 5ceff3e16e1b..bf892a025d4f 100644 --- a/drivers/pci/ats.c +++ b/drivers/pci/ats.c | |||
| @@ -2,9 +2,11 @@ | |||
| 2 | * drivers/pci/ats.c | 2 | * drivers/pci/ats.c |
| 3 | * | 3 | * |
| 4 | * Copyright (C) 2009 Intel Corporation, Yu Zhao <yu.zhao@intel.com> | 4 | * Copyright (C) 2009 Intel Corporation, Yu Zhao <yu.zhao@intel.com> |
| 5 | * Copyright (C) 2011 Advanced Micro Devices, | ||
| 5 | * | 6 | * |
| 6 | * PCI Express I/O Virtualization (IOV) support. | 7 | * PCI Express I/O Virtualization (IOV) support. |
| 7 | * Address Translation Service 1.0 | 8 | * Address Translation Service 1.0 |
| 9 | * Page Request Interface added by Joerg Roedel <joerg.roedel@amd.com> | ||
| 8 | */ | 10 | */ |
| 9 | 11 | ||
| 10 | #include <linux/pci-ats.h> | 12 | #include <linux/pci-ats.h> |
| @@ -156,3 +158,168 @@ int pci_ats_queue_depth(struct pci_dev *dev) | |||
| 156 | PCI_ATS_MAX_QDEP; | 158 | PCI_ATS_MAX_QDEP; |
| 157 | } | 159 | } |
| 158 | EXPORT_SYMBOL_GPL(pci_ats_queue_depth); | 160 | EXPORT_SYMBOL_GPL(pci_ats_queue_depth); |
| 161 | |||
| 162 | #ifdef CONFIG_PCI_PRI | ||
| 163 | /** | ||
| 164 | * pci_enable_pri - Enable PRI capability | ||
| 165 | * @ pdev: PCI device structure | ||
| 166 | * | ||
| 167 | * Returns 0 on success, negative value on error | ||
| 168 | */ | ||
| 169 | int pci_enable_pri(struct pci_dev *pdev, u32 reqs) | ||
| 170 | { | ||
| 171 | u16 control, status; | ||
| 172 | u32 max_requests; | ||
| 173 | int pos; | ||
| 174 | |||
| 175 | pos = pci_find_ext_capability(pdev, PCI_PRI_CAP); | ||
| 176 | if (!pos) | ||
| 177 | return -EINVAL; | ||
| 178 | |||
| 179 | pci_read_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, &control); | ||
| 180 | pci_read_config_word(pdev, pos + PCI_PRI_STATUS_OFF, &status); | ||
| 181 | if ((control & PCI_PRI_ENABLE) || !(status & PCI_PRI_STATUS_STOPPED)) | ||
| 182 | return -EBUSY; | ||
| 183 | |||
| 184 | pci_read_config_dword(pdev, pos + PCI_PRI_MAX_REQ_OFF, &max_requests); | ||
| 185 | reqs = min(max_requests, reqs); | ||
| 186 | pci_write_config_dword(pdev, pos + PCI_PRI_ALLOC_REQ_OFF, reqs); | ||
| 187 | |||
| 188 | control |= PCI_PRI_ENABLE; | ||
| 189 | pci_write_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, control); | ||
| 190 | |||
| 191 | return 0; | ||
| 192 | } | ||
| 193 | EXPORT_SYMBOL_GPL(pci_enable_pri); | ||
| 194 | |||
| 195 | /** | ||
| 196 | * pci_disable_pri - Disable PRI capability | ||
| 197 | * @pdev: PCI device structure | ||
| 198 | * | ||
| 199 | * Only clears the enabled-bit, regardless of its former value | ||
| 200 | */ | ||
| 201 | void pci_disable_pri(struct pci_dev *pdev) | ||
| 202 | { | ||
| 203 | u16 control; | ||
| 204 | int pos; | ||
| 205 | |||
| 206 | pos = pci_find_ext_capability(pdev, PCI_PRI_CAP); | ||
| 207 | if (!pos) | ||
| 208 | return; | ||
| 209 | |||
| 210 | pci_read_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, &control); | ||
| 211 | control &= ~PCI_PRI_ENABLE; | ||
| 212 | pci_write_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, control); | ||
| 213 | } | ||
| 214 | EXPORT_SYMBOL_GPL(pci_disable_pri); | ||
| 215 | |||
| 216 | /** | ||
| 217 | * pci_pri_enabled - Checks if PRI capability is enabled | ||
| 218 | * @pdev: PCI device structure | ||
| 219 | * | ||
| 220 | * Returns true if PRI is enabled on the device, false otherwise | ||
| 221 | */ | ||
| 222 | bool pci_pri_enabled(struct pci_dev *pdev) | ||
| 223 | { | ||
| 224 | u16 control; | ||
| 225 | int pos; | ||
| 226 | |||
| 227 | pos = pci_find_ext_capability(pdev, PCI_PRI_CAP); | ||
| 228 | if (!pos) | ||
| 229 | return false; | ||
| 230 | |||
| 231 | pci_read_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, &control); | ||
| 232 | |||
| 233 | return (control & PCI_PRI_ENABLE) ? true : false; | ||
| 234 | } | ||
| 235 | EXPORT_SYMBOL_GPL(pci_pri_enabled); | ||
| 236 | |||
| 237 | /** | ||
| 238 | * pci_reset_pri - Resets device's PRI state | ||
| 239 | * @pdev: PCI device structure | ||
| 240 | * | ||
| 241 | * The PRI capability must be disabled before this function is called. | ||
| 242 | * Returns 0 on success, negative value on error. | ||
| 243 | */ | ||
| 244 | int pci_reset_pri(struct pci_dev *pdev) | ||
| 245 | { | ||
| 246 | u16 control; | ||
| 247 | int pos; | ||
| 248 | |||
| 249 | pos = pci_find_ext_capability(pdev, PCI_PRI_CAP); | ||
| 250 | if (!pos) | ||
| 251 | return -EINVAL; | ||
| 252 | |||
| 253 | pci_read_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, &control); | ||
| 254 | if (control & PCI_PRI_ENABLE) | ||
| 255 | return -EBUSY; | ||
| 256 | |||
| 257 | control |= PCI_PRI_RESET; | ||
| 258 | |||
| 259 | pci_write_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, control); | ||
| 260 | |||
| 261 | return 0; | ||
| 262 | } | ||
| 263 | EXPORT_SYMBOL_GPL(pci_reset_pri); | ||
| 264 | |||
| 265 | /** | ||
| 266 | * pci_pri_stopped - Checks whether the PRI capability is stopped | ||
| 267 | * @pdev: PCI device structure | ||
| 268 | * | ||
| 269 | * Returns true if the PRI capability on the device is disabled and the | ||
| 270 | * device has no outstanding PRI requests, false otherwise. The device | ||
| 271 | * indicates this via the STOPPED bit in the status register of the | ||
| 272 | * capability. | ||
| 273 | * The device internal state can be cleared by resetting the PRI state | ||
| 274 | * with pci_reset_pri(). This can force the capability into the STOPPED | ||
| 275 | * state. | ||
| 276 | */ | ||
| 277 | bool pci_pri_stopped(struct pci_dev *pdev) | ||
| 278 | { | ||
| 279 | u16 control, status; | ||
| 280 | int pos; | ||
| 281 | |||
| 282 | pos = pci_find_ext_capability(pdev, PCI_PRI_CAP); | ||
| 283 | if (!pos) | ||
| 284 | return true; | ||
| 285 | |||
| 286 | pci_read_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, &control); | ||
| 287 | pci_read_config_word(pdev, pos + PCI_PRI_STATUS_OFF, &status); | ||
| 288 | |||
| 289 | if (control & PCI_PRI_ENABLE) | ||
| 290 | return false; | ||
| 291 | |||
| 292 | return (status & PCI_PRI_STATUS_STOPPED) ? true : false; | ||
| 293 | } | ||
| 294 | EXPORT_SYMBOL_GPL(pci_pri_stopped); | ||
| 295 | |||
| 296 | /** | ||
| 297 | * pci_pri_status - Request PRI status of a device | ||
| 298 | * @pdev: PCI device structure | ||
| 299 | * | ||
| 300 | * Returns negative value on failure, status on success. The status can | ||
| 301 | * be checked against status-bits. Supported bits are currently: | ||
| 302 | * PCI_PRI_STATUS_RF: Response failure | ||
| 303 | * PCI_PRI_STATUS_UPRGI: Unexpected Page Request Group Index | ||
| 304 | * PCI_PRI_STATUS_STOPPED: PRI has stopped | ||
| 305 | */ | ||
| 306 | int pci_pri_status(struct pci_dev *pdev) | ||
| 307 | { | ||
| 308 | u16 status, control; | ||
| 309 | int pos; | ||
| 310 | |||
| 311 | pos = pci_find_ext_capability(pdev, PCI_PRI_CAP); | ||
| 312 | if (!pos) | ||
| 313 | return -EINVAL; | ||
| 314 | |||
| 315 | pci_read_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, &control); | ||
| 316 | pci_read_config_word(pdev, pos + PCI_PRI_STATUS_OFF, &status); | ||
| 317 | |||
| 318 | /* Stopped bit is undefined when enable == 1, so clear it */ | ||
| 319 | if (control & PCI_PRI_ENABLE) | ||
| 320 | status &= ~PCI_PRI_STATUS_STOPPED; | ||
| 321 | |||
| 322 | return status; | ||
| 323 | } | ||
| 324 | EXPORT_SYMBOL_GPL(pci_pri_status); | ||
| 325 | #endif /* CONFIG_PCI_PRI */ | ||
diff --git a/include/linux/pci-ats.h b/include/linux/pci-ats.h index 4eab42bf2af9..071395251abf 100644 --- a/include/linux/pci-ats.h +++ b/include/linux/pci-ats.h | |||
| @@ -17,6 +17,7 @@ struct pci_ats { | |||
| 17 | extern int pci_enable_ats(struct pci_dev *dev, int ps); | 17 | extern int pci_enable_ats(struct pci_dev *dev, int ps); |
| 18 | extern void pci_disable_ats(struct pci_dev *dev); | 18 | extern void pci_disable_ats(struct pci_dev *dev); |
| 19 | extern int pci_ats_queue_depth(struct pci_dev *dev); | 19 | extern int pci_ats_queue_depth(struct pci_dev *dev); |
| 20 | |||
| 20 | /** | 21 | /** |
| 21 | * pci_ats_enabled - query the ATS status | 22 | * pci_ats_enabled - query the ATS status |
| 22 | * @dev: the PCI device | 23 | * @dev: the PCI device |
| @@ -51,4 +52,45 @@ static inline int pci_ats_enabled(struct pci_dev *dev) | |||
| 51 | 52 | ||
| 52 | #endif /* CONFIG_PCI_IOV */ | 53 | #endif /* CONFIG_PCI_IOV */ |
| 53 | 54 | ||
| 55 | #ifdef CONFIG_PCI_PRI | ||
| 56 | |||
| 57 | extern int pci_enable_pri(struct pci_dev *pdev, u32 reqs); | ||
| 58 | extern void pci_disable_pri(struct pci_dev *pdev); | ||
| 59 | extern bool pci_pri_enabled(struct pci_dev *pdev); | ||
| 60 | extern int pci_reset_pri(struct pci_dev *pdev); | ||
| 61 | extern bool pci_pri_stopped(struct pci_dev *pdev); | ||
| 62 | extern int pci_pri_status(struct pci_dev *pdev); | ||
| 63 | |||
| 64 | #else /* CONFIG_PCI_PRI */ | ||
| 65 | |||
| 66 | static inline int pci_enable_pri(struct pci_dev *pdev, u32 reqs) | ||
| 67 | { | ||
| 68 | return -ENODEV; | ||
| 69 | } | ||
| 70 | |||
| 71 | static inline void pci_disable_pri(struct pci_dev *pdev) | ||
| 72 | { | ||
| 73 | } | ||
| 74 | |||
| 75 | static inline bool pci_pri_enabled(struct pci_dev *pdev) | ||
| 76 | { | ||
| 77 | return false; | ||
| 78 | } | ||
| 79 | |||
| 80 | static inline int pci_reset_pri(struct pci_dev *pdev) | ||
| 81 | { | ||
| 82 | return -ENODEV; | ||
| 83 | } | ||
| 84 | |||
| 85 | static inline bool pci_pri_stopped(struct pci_dev *pdev) | ||
| 86 | { | ||
| 87 | return true; | ||
| 88 | } | ||
| 89 | |||
| 90 | static inline int pci_pri_status(struct pci_dev *pdev) | ||
| 91 | { | ||
| 92 | return -ENODEV; | ||
| 93 | } | ||
| 94 | #endif /* CONFIG_PCI_PRI */ | ||
| 95 | |||
| 54 | #endif /* LINUX_PCI_ATS_H*/ | 96 | #endif /* LINUX_PCI_ATS_H*/ |
diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index e8840964aca1..7fc32aff94d2 100644 --- a/include/linux/pci_regs.h +++ b/include/linux/pci_regs.h | |||
| @@ -663,6 +663,18 @@ | |||
| 663 | #define PCI_ATS_CTRL_STU(x) ((x) & 0x1f) /* Smallest Translation Unit */ | 663 | #define PCI_ATS_CTRL_STU(x) ((x) & 0x1f) /* Smallest Translation Unit */ |
| 664 | #define PCI_ATS_MIN_STU 12 /* shift of minimum STU block */ | 664 | #define PCI_ATS_MIN_STU 12 /* shift of minimum STU block */ |
| 665 | 665 | ||
| 666 | /* Page Request Interface */ | ||
| 667 | #define PCI_PRI_CAP 0x13 /* PRI capability ID */ | ||
| 668 | #define PCI_PRI_CONTROL_OFF 0x04 /* Offset of control register */ | ||
| 669 | #define PCI_PRI_STATUS_OFF 0x06 /* Offset of status register */ | ||
| 670 | #define PCI_PRI_ENABLE 0x0001 /* Enable mask */ | ||
| 671 | #define PCI_PRI_RESET 0x0002 /* Reset bit mask */ | ||
| 672 | #define PCI_PRI_STATUS_RF 0x0001 /* Request Failure */ | ||
| 673 | #define PCI_PRI_STATUS_UPRGI 0x0002 /* Unexpected PRG index */ | ||
| 674 | #define PCI_PRI_STATUS_STOPPED 0x0100 /* PRI Stopped */ | ||
| 675 | #define PCI_PRI_MAX_REQ_OFF 0x08 /* Cap offset for max reqs supported */ | ||
| 676 | #define PCI_PRI_ALLOC_REQ_OFF 0x0c /* Cap offset for max reqs allowed */ | ||
| 677 | |||
| 666 | /* Single Root I/O Virtualization */ | 678 | /* Single Root I/O Virtualization */ |
| 667 | #define PCI_SRIOV_CAP 0x04 /* SR-IOV Capabilities */ | 679 | #define PCI_SRIOV_CAP 0x04 /* SR-IOV Capabilities */ |
| 668 | #define PCI_SRIOV_CAP_VFM 0x01 /* VF Migration Capable */ | 680 | #define PCI_SRIOV_CAP_VFM 0x01 /* VF Migration Capable */ |
