diff options
author | Christian König <christian.koenig@amd.com> | 2017-10-24 15:40:20 -0400 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2017-10-24 15:40:20 -0400 |
commit | 276b738deb5bf856b9f6049fcd92a967f52643d7 (patch) | |
tree | cac0ccc12391b30c519e116c696955da34fdb8cb | |
parent | cb21bc9469c4c8a4d38f52d779ccc11e4329f016 (diff) |
PCI: Add resizable BAR infrastructure
Add resizable BAR infrastructure, including defines and helper functions to
read the possible sizes of a BAR and update its size. See PCIe r3.1, sec
7.22.
Link: https://pcisig.com/sites/default/files/specification_documents/ECN_Resizable-BAR_24Apr2008.pdf
Signed-off-by: Christian König <christian.koenig@amd.com>
[bhelgaas: rename to functions with "rebar" (to match #defines), drop shift
#defines, drop "_MASK" suffixes, fix typos, fix kerneldoc]
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
-rw-r--r-- | drivers/pci/pci.c | 101 | ||||
-rw-r--r-- | drivers/pci/pci.h | 8 | ||||
-rw-r--r-- | include/uapi/linux/pci_regs.h | 8 |
3 files changed, 115 insertions, 2 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 6078dfc11b11..832b96756e83 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c | |||
@@ -2966,6 +2966,107 @@ bool pci_acs_path_enabled(struct pci_dev *start, | |||
2966 | } | 2966 | } |
2967 | 2967 | ||
2968 | /** | 2968 | /** |
2969 | * pci_rebar_find_pos - find position of resize ctrl reg for BAR | ||
2970 | * @pdev: PCI device | ||
2971 | * @bar: BAR to find | ||
2972 | * | ||
2973 | * Helper to find the position of the ctrl register for a BAR. | ||
2974 | * Returns -ENOTSUPP if resizable BARs are not supported at all. | ||
2975 | * Returns -ENOENT if no ctrl register for the BAR could be found. | ||
2976 | */ | ||
2977 | static int pci_rebar_find_pos(struct pci_dev *pdev, int bar) | ||
2978 | { | ||
2979 | unsigned int pos, nbars, i; | ||
2980 | u32 ctrl; | ||
2981 | |||
2982 | pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR); | ||
2983 | if (!pos) | ||
2984 | return -ENOTSUPP; | ||
2985 | |||
2986 | pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); | ||
2987 | nbars = (ctrl & PCI_REBAR_CTRL_NBAR_MASK) >> | ||
2988 | PCI_REBAR_CTRL_NBAR_SHIFT; | ||
2989 | |||
2990 | for (i = 0; i < nbars; i++, pos += 8) { | ||
2991 | int bar_idx; | ||
2992 | |||
2993 | pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); | ||
2994 | bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX; | ||
2995 | if (bar_idx == bar) | ||
2996 | return pos; | ||
2997 | } | ||
2998 | |||
2999 | return -ENOENT; | ||
3000 | } | ||
3001 | |||
3002 | /** | ||
3003 | * pci_rebar_get_possible_sizes - get possible sizes for BAR | ||
3004 | * @pdev: PCI device | ||
3005 | * @bar: BAR to query | ||
3006 | * | ||
3007 | * Get the possible sizes of a resizable BAR as bitmask defined in the spec | ||
3008 | * (bit 0=1MB, bit 19=512GB). Returns 0 if BAR isn't resizable. | ||
3009 | */ | ||
3010 | u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar) | ||
3011 | { | ||
3012 | int pos; | ||
3013 | u32 cap; | ||
3014 | |||
3015 | pos = pci_rebar_find_pos(pdev, bar); | ||
3016 | if (pos < 0) | ||
3017 | return 0; | ||
3018 | |||
3019 | pci_read_config_dword(pdev, pos + PCI_REBAR_CAP, &cap); | ||
3020 | return (cap & PCI_REBAR_CAP_SIZES) >> 4; | ||
3021 | } | ||
3022 | |||
3023 | /** | ||
3024 | * pci_rebar_get_current_size - get the current size of a BAR | ||
3025 | * @pdev: PCI device | ||
3026 | * @bar: BAR to set size to | ||
3027 | * | ||
3028 | * Read the size of a BAR from the resizable BAR config. | ||
3029 | * Returns size if found or negative error code. | ||
3030 | */ | ||
3031 | int pci_rebar_get_current_size(struct pci_dev *pdev, int bar) | ||
3032 | { | ||
3033 | int pos; | ||
3034 | u32 ctrl; | ||
3035 | |||
3036 | pos = pci_rebar_find_pos(pdev, bar); | ||
3037 | if (pos < 0) | ||
3038 | return pos; | ||
3039 | |||
3040 | pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); | ||
3041 | return (ctrl & PCI_REBAR_CTRL_BAR_SIZE) >> 8; | ||
3042 | } | ||
3043 | |||
3044 | /** | ||
3045 | * pci_rebar_set_size - set a new size for a BAR | ||
3046 | * @pdev: PCI device | ||
3047 | * @bar: BAR to set size to | ||
3048 | * @size: new size as defined in the spec (0=1MB, 19=512GB) | ||
3049 | * | ||
3050 | * Set the new size of a BAR as defined in the spec. | ||
3051 | * Returns zero if resizing was successful, error code otherwise. | ||
3052 | */ | ||
3053 | int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size) | ||
3054 | { | ||
3055 | int pos; | ||
3056 | u32 ctrl; | ||
3057 | |||
3058 | pos = pci_rebar_find_pos(pdev, bar); | ||
3059 | if (pos < 0) | ||
3060 | return pos; | ||
3061 | |||
3062 | pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); | ||
3063 | ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE; | ||
3064 | ctrl |= size << 8; | ||
3065 | pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl); | ||
3066 | return 0; | ||
3067 | } | ||
3068 | |||
3069 | /** | ||
2969 | * pci_swizzle_interrupt_pin - swizzle INTx for device behind bridge | 3070 | * pci_swizzle_interrupt_pin - swizzle INTx for device behind bridge |
2970 | * @dev: the PCI device | 3071 | * @dev: the PCI device |
2971 | * @pin: the INTx pin (1=INTA, 2=INTB, 3=INTC, 4=INTD) | 3072 | * @pin: the INTx pin (1=INTA, 2=INTB, 3=INTC, 4=INTD) |
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index a6560c9baa52..33469a33738d 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h | |||
@@ -366,4 +366,12 @@ int acpi_get_rc_resources(struct device *dev, const char *hid, u16 segment, | |||
366 | struct resource *res); | 366 | struct resource *res); |
367 | #endif | 367 | #endif |
368 | 368 | ||
369 | u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar); | ||
370 | int pci_rebar_get_current_size(struct pci_dev *pdev, int bar); | ||
371 | int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size); | ||
372 | static inline u64 pci_rebar_size_to_bytes(int size) | ||
373 | { | ||
374 | return 1ULL << (size + 20); | ||
375 | } | ||
376 | |||
369 | #endif /* DRIVERS_PCI_H */ | 377 | #endif /* DRIVERS_PCI_H */ |
diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index f8d58045926f..d34000a59f24 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h | |||
@@ -939,9 +939,13 @@ | |||
939 | #define PCI_SATA_SIZEOF_LONG 16 | 939 | #define PCI_SATA_SIZEOF_LONG 16 |
940 | 940 | ||
941 | /* Resizable BARs */ | 941 | /* Resizable BARs */ |
942 | #define PCI_REBAR_CAP 4 /* capability register */ | ||
943 | #define PCI_REBAR_CAP_SIZES 0x00FFFFF0 /* supported BAR sizes */ | ||
942 | #define PCI_REBAR_CTRL 8 /* control register */ | 944 | #define PCI_REBAR_CTRL 8 /* control register */ |
943 | #define PCI_REBAR_CTRL_NBAR_MASK (7 << 5) /* mask for # bars */ | 945 | #define PCI_REBAR_CTRL_BAR_IDX 0x00000007 /* BAR index */ |
944 | #define PCI_REBAR_CTRL_NBAR_SHIFT 5 /* shift for # bars */ | 946 | #define PCI_REBAR_CTRL_NBAR_MASK 0x000000E0 /* # of resizable BARs */ |
947 | #define PCI_REBAR_CTRL_NBAR_SHIFT 5 /* shift for # of BARs */ | ||
948 | #define PCI_REBAR_CTRL_BAR_SIZE 0x00001F00 /* BAR size */ | ||
945 | 949 | ||
946 | /* Dynamic Power Allocation */ | 950 | /* Dynamic Power Allocation */ |
947 | #define PCI_DPA_CAP 4 /* capability register */ | 951 | #define PCI_DPA_CAP 4 /* capability register */ |