diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2017-11-14 13:11:25 -0500 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2017-11-14 13:11:25 -0500 |
commit | 104d1e40cfcd69934f3f57c6abf13980eb703feb (patch) | |
tree | b57f657381267187610c1dbc2606de2142ceca7d | |
parent | 8dceeaf8fff35a0b70a8cf3ca66883ac4bf4c9dd (diff) | |
parent | a405f191f42ead45a03c000110a16683d30f7333 (diff) |
Merge branch 'pci/resource' into next
* pci/resource:
PCI: Fail pci_map_rom() if the option ROM is invalid
PCI: Move pci_map_rom() error path
x86/PCI: Enable a 64bit BAR on AMD Family 15h (Models 00-1f, 30-3f, 60-7f)
PCI: Add pci_resize_resource() for resizing BARs
PCI: Add resizable BAR infrastructure
PCI: Add PCI resource type mask #define
-rw-r--r-- | arch/x86/pci/fixup.c | 85 | ||||
-rw-r--r-- | drivers/pci/pci.c | 101 | ||||
-rw-r--r-- | drivers/pci/pci.h | 8 | ||||
-rw-r--r-- | drivers/pci/rom.c | 19 | ||||
-rw-r--r-- | drivers/pci/setup-bus.c | 115 | ||||
-rw-r--r-- | drivers/pci/setup-res.c | 58 | ||||
-rw-r--r-- | include/linux/pci.h | 3 | ||||
-rw-r--r-- | include/uapi/linux/pci_regs.h | 8 |
8 files changed, 380 insertions, 17 deletions
diff --git a/arch/x86/pci/fixup.c b/arch/x86/pci/fixup.c index f2228b150faa..e59378bf37d9 100644 --- a/arch/x86/pci/fixup.c +++ b/arch/x86/pci/fixup.c | |||
@@ -635,3 +635,88 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x2030, quirk_no_aersid); | |||
635 | DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x2031, quirk_no_aersid); | 635 | DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x2031, quirk_no_aersid); |
636 | DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x2032, quirk_no_aersid); | 636 | DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x2032, quirk_no_aersid); |
637 | DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x2033, quirk_no_aersid); | 637 | DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x2033, quirk_no_aersid); |
638 | |||
639 | #ifdef CONFIG_PHYS_ADDR_T_64BIT | ||
640 | |||
641 | #define AMD_141b_MMIO_BASE(x) (0x80 + (x) * 0x8) | ||
642 | #define AMD_141b_MMIO_BASE_RE_MASK BIT(0) | ||
643 | #define AMD_141b_MMIO_BASE_WE_MASK BIT(1) | ||
644 | #define AMD_141b_MMIO_BASE_MMIOBASE_MASK GENMASK(31,8) | ||
645 | |||
646 | #define AMD_141b_MMIO_LIMIT(x) (0x84 + (x) * 0x8) | ||
647 | #define AMD_141b_MMIO_LIMIT_MMIOLIMIT_MASK GENMASK(31,8) | ||
648 | |||
649 | #define AMD_141b_MMIO_HIGH(x) (0x180 + (x) * 0x4) | ||
650 | #define AMD_141b_MMIO_HIGH_MMIOBASE_MASK GENMASK(7,0) | ||
651 | #define AMD_141b_MMIO_HIGH_MMIOLIMIT_SHIFT 16 | ||
652 | #define AMD_141b_MMIO_HIGH_MMIOLIMIT_MASK GENMASK(23,16) | ||
653 | |||
654 | /* | ||
655 | * The PCI Firmware Spec, rev 3.2, notes that ACPI should optionally allow | ||
656 | * configuring host bridge windows using the _PRS and _SRS methods. | ||
657 | * | ||
658 | * But this is rarely implemented, so we manually enable a large 64bit BAR for | ||
659 | * PCIe device on AMD Family 15h (Models 00h-1fh, 30h-3fh, 60h-7fh) Processors | ||
660 | * here. | ||
661 | */ | ||
662 | static void pci_amd_enable_64bit_bar(struct pci_dev *dev) | ||
663 | { | ||
664 | unsigned i; | ||
665 | u32 base, limit, high; | ||
666 | struct resource *res, *conflict; | ||
667 | |||
668 | for (i = 0; i < 8; i++) { | ||
669 | pci_read_config_dword(dev, AMD_141b_MMIO_BASE(i), &base); | ||
670 | pci_read_config_dword(dev, AMD_141b_MMIO_HIGH(i), &high); | ||
671 | |||
672 | /* Is this slot free? */ | ||
673 | if (!(base & (AMD_141b_MMIO_BASE_RE_MASK | | ||
674 | AMD_141b_MMIO_BASE_WE_MASK))) | ||
675 | break; | ||
676 | |||
677 | base >>= 8; | ||
678 | base |= high << 24; | ||
679 | |||
680 | /* Abort if a slot already configures a 64bit BAR. */ | ||
681 | if (base > 0x10000) | ||
682 | return; | ||
683 | } | ||
684 | if (i == 8) | ||
685 | return; | ||
686 | |||
687 | res = kzalloc(sizeof(*res), GFP_KERNEL); | ||
688 | if (!res) | ||
689 | return; | ||
690 | |||
691 | res->name = "PCI Bus 0000:00"; | ||
692 | res->flags = IORESOURCE_PREFETCH | IORESOURCE_MEM | | ||
693 | IORESOURCE_MEM_64 | IORESOURCE_WINDOW; | ||
694 | res->start = 0x100000000ull; | ||
695 | res->end = 0xfd00000000ull - 1; | ||
696 | |||
697 | /* Just grab the free area behind system memory for this */ | ||
698 | while ((conflict = request_resource_conflict(&iomem_resource, res))) | ||
699 | res->start = conflict->end + 1; | ||
700 | |||
701 | dev_info(&dev->dev, "adding root bus resource %pR\n", res); | ||
702 | |||
703 | base = ((res->start >> 8) & AMD_141b_MMIO_BASE_MMIOBASE_MASK) | | ||
704 | AMD_141b_MMIO_BASE_RE_MASK | AMD_141b_MMIO_BASE_WE_MASK; | ||
705 | limit = ((res->end + 1) >> 8) & AMD_141b_MMIO_LIMIT_MMIOLIMIT_MASK; | ||
706 | high = ((res->start >> 40) & AMD_141b_MMIO_HIGH_MMIOBASE_MASK) | | ||
707 | ((((res->end + 1) >> 40) << AMD_141b_MMIO_HIGH_MMIOLIMIT_SHIFT) | ||
708 | & AMD_141b_MMIO_HIGH_MMIOLIMIT_MASK); | ||
709 | |||
710 | pci_write_config_dword(dev, AMD_141b_MMIO_HIGH(i), high); | ||
711 | pci_write_config_dword(dev, AMD_141b_MMIO_LIMIT(i), limit); | ||
712 | pci_write_config_dword(dev, AMD_141b_MMIO_BASE(i), base); | ||
713 | |||
714 | pci_bus_add_resource(dev->bus, res, 0); | ||
715 | } | ||
716 | DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AMD, 0x1401, pci_amd_enable_64bit_bar); | ||
717 | DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AMD, 0x141b, pci_amd_enable_64bit_bar); | ||
718 | DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AMD, 0x1571, pci_amd_enable_64bit_bar); | ||
719 | DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AMD, 0x15b1, pci_amd_enable_64bit_bar); | ||
720 | DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AMD, 0x1601, pci_amd_enable_64bit_bar); | ||
721 | |||
722 | #endif | ||
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index d50c58d6acd3..c5172b01164d 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 1db36f2e1ef5..186278fa6917 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/drivers/pci/rom.c b/drivers/pci/rom.c index b6edb187d160..1f5e6af96c83 100644 --- a/drivers/pci/rom.c +++ b/drivers/pci/rom.c | |||
@@ -147,12 +147,8 @@ void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size) | |||
147 | return NULL; | 147 | return NULL; |
148 | 148 | ||
149 | rom = ioremap(start, *size); | 149 | rom = ioremap(start, *size); |
150 | if (!rom) { | 150 | if (!rom) |
151 | /* restore enable if ioremap fails */ | 151 | goto err_ioremap; |
152 | if (!(res->flags & IORESOURCE_ROM_ENABLE)) | ||
153 | pci_disable_rom(pdev); | ||
154 | return NULL; | ||
155 | } | ||
156 | 152 | ||
157 | /* | 153 | /* |
158 | * Try to find the true size of the ROM since sometimes the PCI window | 154 | * Try to find the true size of the ROM since sometimes the PCI window |
@@ -160,7 +156,18 @@ void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size) | |||
160 | * True size is important if the ROM is going to be copied. | 156 | * True size is important if the ROM is going to be copied. |
161 | */ | 157 | */ |
162 | *size = pci_get_rom_size(pdev, rom, *size); | 158 | *size = pci_get_rom_size(pdev, rom, *size); |
159 | if (!*size) | ||
160 | goto invalid_rom; | ||
161 | |||
163 | return rom; | 162 | return rom; |
163 | |||
164 | invalid_rom: | ||
165 | iounmap(rom); | ||
166 | err_ioremap: | ||
167 | /* restore enable if ioremap fails */ | ||
168 | if (!(res->flags & IORESOURCE_ROM_ENABLE)) | ||
169 | pci_disable_rom(pdev); | ||
170 | return NULL; | ||
164 | } | 171 | } |
165 | EXPORT_SYMBOL(pci_map_rom); | 172 | EXPORT_SYMBOL(pci_map_rom); |
166 | 173 | ||
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 5e547e0dc47b..b1ad466199ad 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c | |||
@@ -1518,13 +1518,16 @@ static void __pci_bridge_assign_resources(const struct pci_dev *bridge, | |||
1518 | break; | 1518 | break; |
1519 | } | 1519 | } |
1520 | } | 1520 | } |
1521 | |||
1522 | #define PCI_RES_TYPE_MASK \ | ||
1523 | (IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH |\ | ||
1524 | IORESOURCE_MEM_64) | ||
1525 | |||
1521 | static void pci_bridge_release_resources(struct pci_bus *bus, | 1526 | static void pci_bridge_release_resources(struct pci_bus *bus, |
1522 | unsigned long type) | 1527 | unsigned long type) |
1523 | { | 1528 | { |
1524 | struct pci_dev *dev = bus->self; | 1529 | struct pci_dev *dev = bus->self; |
1525 | struct resource *r; | 1530 | struct resource *r; |
1526 | unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM | | ||
1527 | IORESOURCE_PREFETCH | IORESOURCE_MEM_64; | ||
1528 | unsigned old_flags = 0; | 1531 | unsigned old_flags = 0; |
1529 | struct resource *b_res; | 1532 | struct resource *b_res; |
1530 | int idx = 1; | 1533 | int idx = 1; |
@@ -1567,7 +1570,7 @@ static void pci_bridge_release_resources(struct pci_bus *bus, | |||
1567 | */ | 1570 | */ |
1568 | release_child_resources(r); | 1571 | release_child_resources(r); |
1569 | if (!release_resource(r)) { | 1572 | if (!release_resource(r)) { |
1570 | type = old_flags = r->flags & type_mask; | 1573 | type = old_flags = r->flags & PCI_RES_TYPE_MASK; |
1571 | dev_printk(KERN_DEBUG, &dev->dev, "resource %d %pR released\n", | 1574 | dev_printk(KERN_DEBUG, &dev->dev, "resource %d %pR released\n", |
1572 | PCI_BRIDGE_RESOURCES + idx, r); | 1575 | PCI_BRIDGE_RESOURCES + idx, r); |
1573 | /* keep the old size */ | 1576 | /* keep the old size */ |
@@ -1758,8 +1761,6 @@ void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus) | |||
1758 | enum release_type rel_type = leaf_only; | 1761 | enum release_type rel_type = leaf_only; |
1759 | LIST_HEAD(fail_head); | 1762 | LIST_HEAD(fail_head); |
1760 | struct pci_dev_resource *fail_res; | 1763 | struct pci_dev_resource *fail_res; |
1761 | unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM | | ||
1762 | IORESOURCE_PREFETCH | IORESOURCE_MEM_64; | ||
1763 | int pci_try_num = 1; | 1764 | int pci_try_num = 1; |
1764 | enum enable_type enable_local; | 1765 | enum enable_type enable_local; |
1765 | 1766 | ||
@@ -1818,7 +1819,7 @@ again: | |||
1818 | */ | 1819 | */ |
1819 | list_for_each_entry(fail_res, &fail_head, list) | 1820 | list_for_each_entry(fail_res, &fail_head, list) |
1820 | pci_bus_release_bridge_resources(fail_res->dev->bus, | 1821 | pci_bus_release_bridge_resources(fail_res->dev->bus, |
1821 | fail_res->flags & type_mask, | 1822 | fail_res->flags & PCI_RES_TYPE_MASK, |
1822 | rel_type); | 1823 | rel_type); |
1823 | 1824 | ||
1824 | /* restore size and flags */ | 1825 | /* restore size and flags */ |
@@ -2031,8 +2032,6 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge) | |||
2031 | LIST_HEAD(fail_head); | 2032 | LIST_HEAD(fail_head); |
2032 | struct pci_dev_resource *fail_res; | 2033 | struct pci_dev_resource *fail_res; |
2033 | int retval; | 2034 | int retval; |
2034 | unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM | | ||
2035 | IORESOURCE_PREFETCH | IORESOURCE_MEM_64; | ||
2036 | 2035 | ||
2037 | again: | 2036 | again: |
2038 | __pci_bus_size_bridges(parent, &add_list); | 2037 | __pci_bus_size_bridges(parent, &add_list); |
@@ -2066,7 +2065,7 @@ again: | |||
2066 | */ | 2065 | */ |
2067 | list_for_each_entry(fail_res, &fail_head, list) | 2066 | list_for_each_entry(fail_res, &fail_head, list) |
2068 | pci_bus_release_bridge_resources(fail_res->dev->bus, | 2067 | pci_bus_release_bridge_resources(fail_res->dev->bus, |
2069 | fail_res->flags & type_mask, | 2068 | fail_res->flags & PCI_RES_TYPE_MASK, |
2070 | whole_subtree); | 2069 | whole_subtree); |
2071 | 2070 | ||
2072 | /* restore size and flags */ | 2071 | /* restore size and flags */ |
@@ -2091,6 +2090,104 @@ enable_all: | |||
2091 | } | 2090 | } |
2092 | EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources); | 2091 | EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources); |
2093 | 2092 | ||
2093 | int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type) | ||
2094 | { | ||
2095 | struct pci_dev_resource *dev_res; | ||
2096 | struct pci_dev *next; | ||
2097 | LIST_HEAD(saved); | ||
2098 | LIST_HEAD(added); | ||
2099 | LIST_HEAD(failed); | ||
2100 | unsigned int i; | ||
2101 | int ret; | ||
2102 | |||
2103 | /* Walk to the root hub, releasing bridge BARs when possible */ | ||
2104 | next = bridge; | ||
2105 | do { | ||
2106 | bridge = next; | ||
2107 | for (i = PCI_BRIDGE_RESOURCES; i < PCI_BRIDGE_RESOURCE_END; | ||
2108 | i++) { | ||
2109 | struct resource *res = &bridge->resource[i]; | ||
2110 | |||
2111 | if ((res->flags ^ type) & PCI_RES_TYPE_MASK) | ||
2112 | continue; | ||
2113 | |||
2114 | /* Ignore BARs which are still in use */ | ||
2115 | if (res->child) | ||
2116 | continue; | ||
2117 | |||
2118 | ret = add_to_list(&saved, bridge, res, 0, 0); | ||
2119 | if (ret) | ||
2120 | goto cleanup; | ||
2121 | |||
2122 | dev_info(&bridge->dev, "BAR %d: releasing %pR\n", | ||
2123 | i, res); | ||
2124 | |||
2125 | if (res->parent) | ||
2126 | release_resource(res); | ||
2127 | res->start = 0; | ||
2128 | res->end = 0; | ||
2129 | break; | ||
2130 | } | ||
2131 | if (i == PCI_BRIDGE_RESOURCE_END) | ||
2132 | break; | ||
2133 | |||
2134 | next = bridge->bus ? bridge->bus->self : NULL; | ||
2135 | } while (next); | ||
2136 | |||
2137 | if (list_empty(&saved)) | ||
2138 | return -ENOENT; | ||
2139 | |||
2140 | __pci_bus_size_bridges(bridge->subordinate, &added); | ||
2141 | __pci_bridge_assign_resources(bridge, &added, &failed); | ||
2142 | BUG_ON(!list_empty(&added)); | ||
2143 | |||
2144 | if (!list_empty(&failed)) { | ||
2145 | ret = -ENOSPC; | ||
2146 | goto cleanup; | ||
2147 | } | ||
2148 | |||
2149 | list_for_each_entry(dev_res, &saved, list) { | ||
2150 | /* Skip the bridge we just assigned resources for. */ | ||
2151 | if (bridge == dev_res->dev) | ||
2152 | continue; | ||
2153 | |||
2154 | bridge = dev_res->dev; | ||
2155 | pci_setup_bridge(bridge->subordinate); | ||
2156 | } | ||
2157 | |||
2158 | free_list(&saved); | ||
2159 | return 0; | ||
2160 | |||
2161 | cleanup: | ||
2162 | /* restore size and flags */ | ||
2163 | list_for_each_entry(dev_res, &failed, list) { | ||
2164 | struct resource *res = dev_res->res; | ||
2165 | |||
2166 | res->start = dev_res->start; | ||
2167 | res->end = dev_res->end; | ||
2168 | res->flags = dev_res->flags; | ||
2169 | } | ||
2170 | free_list(&failed); | ||
2171 | |||
2172 | /* Revert to the old configuration */ | ||
2173 | list_for_each_entry(dev_res, &saved, list) { | ||
2174 | struct resource *res = dev_res->res; | ||
2175 | |||
2176 | bridge = dev_res->dev; | ||
2177 | i = res - bridge->resource; | ||
2178 | |||
2179 | res->start = dev_res->start; | ||
2180 | res->end = dev_res->end; | ||
2181 | res->flags = dev_res->flags; | ||
2182 | |||
2183 | pci_claim_resource(bridge, i); | ||
2184 | pci_setup_bridge(bridge->subordinate); | ||
2185 | } | ||
2186 | free_list(&saved); | ||
2187 | |||
2188 | return ret; | ||
2189 | } | ||
2190 | |||
2094 | void pci_assign_unassigned_bus_resources(struct pci_bus *bus) | 2191 | void pci_assign_unassigned_bus_resources(struct pci_bus *bus) |
2095 | { | 2192 | { |
2096 | struct pci_dev *dev; | 2193 | struct pci_dev *dev; |
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index e576e1a8d978..bf0089ef2177 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c | |||
@@ -396,6 +396,64 @@ int pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsiz | |||
396 | return 0; | 396 | return 0; |
397 | } | 397 | } |
398 | 398 | ||
399 | void pci_release_resource(struct pci_dev *dev, int resno) | ||
400 | { | ||
401 | struct resource *res = dev->resource + resno; | ||
402 | |||
403 | dev_info(&dev->dev, "BAR %d: releasing %pR\n", resno, res); | ||
404 | release_resource(res); | ||
405 | res->end = resource_size(res) - 1; | ||
406 | res->start = 0; | ||
407 | res->flags |= IORESOURCE_UNSET; | ||
408 | } | ||
409 | EXPORT_SYMBOL(pci_release_resource); | ||
410 | |||
411 | int pci_resize_resource(struct pci_dev *dev, int resno, int size) | ||
412 | { | ||
413 | struct resource *res = dev->resource + resno; | ||
414 | int old, ret; | ||
415 | u32 sizes; | ||
416 | u16 cmd; | ||
417 | |||
418 | /* Make sure the resource isn't assigned before resizing it. */ | ||
419 | if (!(res->flags & IORESOURCE_UNSET)) | ||
420 | return -EBUSY; | ||
421 | |||
422 | pci_read_config_word(dev, PCI_COMMAND, &cmd); | ||
423 | if (cmd & PCI_COMMAND_MEMORY) | ||
424 | return -EBUSY; | ||
425 | |||
426 | sizes = pci_rebar_get_possible_sizes(dev, resno); | ||
427 | if (!sizes) | ||
428 | return -ENOTSUPP; | ||
429 | |||
430 | if (!(sizes & BIT(size))) | ||
431 | return -EINVAL; | ||
432 | |||
433 | old = pci_rebar_get_current_size(dev, resno); | ||
434 | if (old < 0) | ||
435 | return old; | ||
436 | |||
437 | ret = pci_rebar_set_size(dev, resno, size); | ||
438 | if (ret) | ||
439 | return ret; | ||
440 | |||
441 | res->end = res->start + pci_rebar_size_to_bytes(size) - 1; | ||
442 | |||
443 | /* Check if the new config works by trying to assign everything. */ | ||
444 | ret = pci_reassign_bridge_resources(dev->bus->self, res->flags); | ||
445 | if (ret) | ||
446 | goto error_resize; | ||
447 | |||
448 | return 0; | ||
449 | |||
450 | error_resize: | ||
451 | pci_rebar_set_size(dev, resno, old); | ||
452 | res->end = res->start + pci_rebar_size_to_bytes(old) - 1; | ||
453 | return ret; | ||
454 | } | ||
455 | EXPORT_SYMBOL(pci_resize_resource); | ||
456 | |||
399 | int pci_enable_resources(struct pci_dev *dev, int mask) | 457 | int pci_enable_resources(struct pci_dev *dev, int mask) |
400 | { | 458 | { |
401 | u16 cmd, old_cmd; | 459 | u16 cmd, old_cmd; |
diff --git a/include/linux/pci.h b/include/linux/pci.h index 3dbe947b4152..f3b6909305ca 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h | |||
@@ -1110,6 +1110,8 @@ void pci_reset_bridge_secondary_bus(struct pci_dev *dev); | |||
1110 | void pci_update_resource(struct pci_dev *dev, int resno); | 1110 | void pci_update_resource(struct pci_dev *dev, int resno); |
1111 | int __must_check pci_assign_resource(struct pci_dev *dev, int i); | 1111 | int __must_check pci_assign_resource(struct pci_dev *dev, int i); |
1112 | int __must_check pci_reassign_resource(struct pci_dev *dev, int i, resource_size_t add_size, resource_size_t align); | 1112 | int __must_check pci_reassign_resource(struct pci_dev *dev, int i, resource_size_t add_size, resource_size_t align); |
1113 | void pci_release_resource(struct pci_dev *dev, int resno); | ||
1114 | int __must_check pci_resize_resource(struct pci_dev *dev, int i, int size); | ||
1113 | int pci_select_bars(struct pci_dev *dev, unsigned long flags); | 1115 | int pci_select_bars(struct pci_dev *dev, unsigned long flags); |
1114 | bool pci_device_is_present(struct pci_dev *pdev); | 1116 | bool pci_device_is_present(struct pci_dev *pdev); |
1115 | void pci_ignore_hotplug(struct pci_dev *dev); | 1117 | void pci_ignore_hotplug(struct pci_dev *dev); |
@@ -1189,6 +1191,7 @@ void pci_assign_unassigned_resources(void); | |||
1189 | void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge); | 1191 | void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge); |
1190 | void pci_assign_unassigned_bus_resources(struct pci_bus *bus); | 1192 | void pci_assign_unassigned_bus_resources(struct pci_bus *bus); |
1191 | void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus); | 1193 | void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus); |
1194 | int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type); | ||
1192 | void pdev_enable_device(struct pci_dev *); | 1195 | void pdev_enable_device(struct pci_dev *); |
1193 | int pci_enable_resources(struct pci_dev *, int mask); | 1196 | int pci_enable_resources(struct pci_dev *, int mask); |
1194 | void pci_assign_irq(struct pci_dev *dev); | 1197 | void pci_assign_irq(struct pci_dev *dev); |
diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index 12241709092b..66cd64595d7e 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h | |||
@@ -940,9 +940,13 @@ | |||
940 | #define PCI_SATA_SIZEOF_LONG 16 | 940 | #define PCI_SATA_SIZEOF_LONG 16 |
941 | 941 | ||
942 | /* Resizable BARs */ | 942 | /* Resizable BARs */ |
943 | #define PCI_REBAR_CAP 4 /* capability register */ | ||
944 | #define PCI_REBAR_CAP_SIZES 0x00FFFFF0 /* supported BAR sizes */ | ||
943 | #define PCI_REBAR_CTRL 8 /* control register */ | 945 | #define PCI_REBAR_CTRL 8 /* control register */ |
944 | #define PCI_REBAR_CTRL_NBAR_MASK (7 << 5) /* mask for # bars */ | 946 | #define PCI_REBAR_CTRL_BAR_IDX 0x00000007 /* BAR index */ |
945 | #define PCI_REBAR_CTRL_NBAR_SHIFT 5 /* shift for # bars */ | 947 | #define PCI_REBAR_CTRL_NBAR_MASK 0x000000E0 /* # of resizable BARs */ |
948 | #define PCI_REBAR_CTRL_NBAR_SHIFT 5 /* shift for # of BARs */ | ||
949 | #define PCI_REBAR_CTRL_BAR_SIZE 0x00001F00 /* BAR size */ | ||
946 | 950 | ||
947 | /* Dynamic Power Allocation */ | 951 | /* Dynamic Power Allocation */ |
948 | #define PCI_DPA_CAP 4 /* capability register */ | 952 | #define PCI_DPA_CAP 4 /* capability register */ |