diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2012-09-11 19:01:54 -0400 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2012-09-11 19:01:54 -0400 |
commit | a63ab613ff48c593f4e9ace2d111978e35a202e4 (patch) | |
tree | 067f8172d1a72625458e31869bb587eae5279b80 | |
parent | a690a4cbf09f70723f9721ca96ab8b2b472b3391 (diff) | |
parent | 271fd03a3013b106ccc178d54219c1be0c9759b7 (diff) |
Merge branch 'pci/gavin-window-alignment' into next
* pci/gavin-window-alignment:
powerpc/powernv: I/O and memory alignment for P2P bridges
powerpc/PCI: Override pcibios_window_alignment()
PCI: Refactor pbus_size_mem()
PCI: Align P2P windows using pcibios_window_alignment()
PCI: Add weak pcibios_window_alignment() interface
-rw-r--r-- | arch/powerpc/include/asm/machdep.h | 3 | ||||
-rw-r--r-- | arch/powerpc/kernel/pci-common.c | 20 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/pci-ioda.c | 39 | ||||
-rw-r--r-- | drivers/pci/setup-bus.c | 81 | ||||
-rw-r--r-- | include/linux/pci.h | 2 |
5 files changed, 124 insertions, 21 deletions
diff --git a/arch/powerpc/include/asm/machdep.h b/arch/powerpc/include/asm/machdep.h index 42ce570812c1..f7706d722b39 100644 --- a/arch/powerpc/include/asm/machdep.h +++ b/arch/powerpc/include/asm/machdep.h | |||
@@ -214,6 +214,9 @@ struct machdep_calls { | |||
214 | /* Called after scan and before resource survey */ | 214 | /* Called after scan and before resource survey */ |
215 | void (*pcibios_fixup_phb)(struct pci_controller *hose); | 215 | void (*pcibios_fixup_phb)(struct pci_controller *hose); |
216 | 216 | ||
217 | /* Called during PCI resource reassignment */ | ||
218 | resource_size_t (*pcibios_window_alignment)(struct pci_bus *, unsigned long type); | ||
219 | |||
217 | /* Called to shutdown machine specific hardware not already controlled | 220 | /* Called to shutdown machine specific hardware not already controlled |
218 | * by other drivers. | 221 | * by other drivers. |
219 | */ | 222 | */ |
diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index 2aa04f29e1de..43fea543d686 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c | |||
@@ -99,6 +99,26 @@ void pcibios_free_controller(struct pci_controller *phb) | |||
99 | kfree(phb); | 99 | kfree(phb); |
100 | } | 100 | } |
101 | 101 | ||
102 | /* | ||
103 | * The function is used to return the minimal alignment | ||
104 | * for memory or I/O windows of the associated P2P bridge. | ||
105 | * By default, 4KiB alignment for I/O windows and 1MiB for | ||
106 | * memory windows. | ||
107 | */ | ||
108 | resource_size_t pcibios_window_alignment(struct pci_bus *bus, | ||
109 | unsigned long type) | ||
110 | { | ||
111 | if (ppc_md.pcibios_window_alignment) | ||
112 | return ppc_md.pcibios_window_alignment(bus, type); | ||
113 | |||
114 | /* | ||
115 | * PCI core will figure out the default | ||
116 | * alignment: 4KiB for I/O and 1MiB for | ||
117 | * memory window. | ||
118 | */ | ||
119 | return 1; | ||
120 | } | ||
121 | |||
102 | static resource_size_t pcibios_io_size(const struct pci_controller *hose) | 122 | static resource_size_t pcibios_io_size(const struct pci_controller *hose) |
103 | { | 123 | { |
104 | #ifdef CONFIG_PPC64 | 124 | #ifdef CONFIG_PPC64 |
diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index b46e1dadc882..0e7eccc0f88d 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c | |||
@@ -1139,6 +1139,44 @@ static void __devinit pnv_pci_ioda_fixup_phb(struct pci_controller *hose) | |||
1139 | } | 1139 | } |
1140 | } | 1140 | } |
1141 | 1141 | ||
1142 | /* | ||
1143 | * Returns the alignment for I/O or memory windows for P2P | ||
1144 | * bridges. That actually depends on how PEs are segmented. | ||
1145 | * For now, we return I/O or M32 segment size for PE sensitive | ||
1146 | * P2P bridges. Otherwise, the default values (4KiB for I/O, | ||
1147 | * 1MiB for memory) will be returned. | ||
1148 | * | ||
1149 | * The current PCI bus might be put into one PE, which was | ||
1150 | * create against the parent PCI bridge. For that case, we | ||
1151 | * needn't enlarge the alignment so that we can save some | ||
1152 | * resources. | ||
1153 | */ | ||
1154 | static resource_size_t pnv_pci_window_alignment(struct pci_bus *bus, | ||
1155 | unsigned long type) | ||
1156 | { | ||
1157 | struct pci_dev *bridge; | ||
1158 | struct pci_controller *hose = pci_bus_to_host(bus); | ||
1159 | struct pnv_phb *phb = hose->private_data; | ||
1160 | int num_pci_bridges = 0; | ||
1161 | |||
1162 | bridge = bus->self; | ||
1163 | while (bridge) { | ||
1164 | if (pci_pcie_type(bridge) == PCI_EXP_TYPE_PCI_BRIDGE) { | ||
1165 | num_pci_bridges++; | ||
1166 | if (num_pci_bridges >= 2) | ||
1167 | return 1; | ||
1168 | } | ||
1169 | |||
1170 | bridge = bridge->bus->self; | ||
1171 | } | ||
1172 | |||
1173 | /* We need support prefetchable memory window later */ | ||
1174 | if (type & IORESOURCE_MEM) | ||
1175 | return phb->ioda.m32_segsize; | ||
1176 | |||
1177 | return phb->ioda.io_segsize; | ||
1178 | } | ||
1179 | |||
1142 | /* Prevent enabling devices for which we couldn't properly | 1180 | /* Prevent enabling devices for which we couldn't properly |
1143 | * assign a PE | 1181 | * assign a PE |
1144 | */ | 1182 | */ |
@@ -1306,6 +1344,7 @@ void __init pnv_pci_init_ioda1_phb(struct device_node *np) | |||
1306 | */ | 1344 | */ |
1307 | ppc_md.pcibios_fixup_phb = pnv_pci_ioda_fixup_phb; | 1345 | ppc_md.pcibios_fixup_phb = pnv_pci_ioda_fixup_phb; |
1308 | ppc_md.pcibios_enable_device_hook = pnv_pci_enable_device_hook; | 1346 | ppc_md.pcibios_enable_device_hook = pnv_pci_enable_device_hook; |
1347 | ppc_md.pcibios_window_alignment = pnv_pci_window_alignment; | ||
1309 | pci_add_flags(PCI_PROBE_ONLY | PCI_REASSIGN_ALL_RSRC); | 1348 | pci_add_flags(PCI_PROBE_ONLY | PCI_REASSIGN_ALL_RSRC); |
1310 | 1349 | ||
1311 | /* Reset IODA tables to a clean state */ | 1350 | /* Reset IODA tables to a clean state */ |
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index fb506137aaee..1e808ca338f8 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c | |||
@@ -697,6 +697,38 @@ static resource_size_t calculate_memsize(resource_size_t size, | |||
697 | return size; | 697 | return size; |
698 | } | 698 | } |
699 | 699 | ||
700 | resource_size_t __weak pcibios_window_alignment(struct pci_bus *bus, | ||
701 | unsigned long type) | ||
702 | { | ||
703 | return 1; | ||
704 | } | ||
705 | |||
706 | #define PCI_P2P_DEFAULT_MEM_ALIGN 0x100000 /* 1MiB */ | ||
707 | #define PCI_P2P_DEFAULT_IO_ALIGN 0x1000 /* 4KiB */ | ||
708 | #define PCI_P2P_DEFAULT_IO_ALIGN_1K 0x400 /* 1KiB */ | ||
709 | |||
710 | static resource_size_t window_alignment(struct pci_bus *bus, | ||
711 | unsigned long type) | ||
712 | { | ||
713 | resource_size_t align = 1, arch_align; | ||
714 | |||
715 | if (type & IORESOURCE_MEM) | ||
716 | align = PCI_P2P_DEFAULT_MEM_ALIGN; | ||
717 | else if (type & IORESOURCE_IO) { | ||
718 | /* | ||
719 | * Per spec, I/O windows are 4K-aligned, but some | ||
720 | * bridges have an extension to support 1K alignment. | ||
721 | */ | ||
722 | if (bus->self->io_window_1k) | ||
723 | align = PCI_P2P_DEFAULT_IO_ALIGN_1K; | ||
724 | else | ||
725 | align = PCI_P2P_DEFAULT_IO_ALIGN; | ||
726 | } | ||
727 | |||
728 | arch_align = pcibios_window_alignment(bus, type); | ||
729 | return max(align, arch_align); | ||
730 | } | ||
731 | |||
700 | /** | 732 | /** |
701 | * pbus_size_io() - size the io window of a given bus | 733 | * pbus_size_io() - size the io window of a given bus |
702 | * | 734 | * |
@@ -717,17 +749,12 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, | |||
717 | struct resource *b_res = find_free_bus_resource(bus, IORESOURCE_IO); | 749 | struct resource *b_res = find_free_bus_resource(bus, IORESOURCE_IO); |
718 | unsigned long size = 0, size0 = 0, size1 = 0; | 750 | unsigned long size = 0, size0 = 0, size1 = 0; |
719 | resource_size_t children_add_size = 0; | 751 | resource_size_t children_add_size = 0; |
720 | resource_size_t min_align = 4096, align; | 752 | resource_size_t min_align, io_align, align; |
721 | 753 | ||
722 | if (!b_res) | 754 | if (!b_res) |
723 | return; | 755 | return; |
724 | 756 | ||
725 | /* | 757 | io_align = min_align = window_alignment(bus, IORESOURCE_IO); |
726 | * Per spec, I/O windows are 4K-aligned, but some bridges have an | ||
727 | * extension to support 1K alignment. | ||
728 | */ | ||
729 | if (bus->self->io_window_1k) | ||
730 | min_align = 1024; | ||
731 | list_for_each_entry(dev, &bus->devices, bus_list) { | 758 | list_for_each_entry(dev, &bus->devices, bus_list) { |
732 | int i; | 759 | int i; |
733 | 760 | ||
@@ -754,8 +781,8 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, | |||
754 | } | 781 | } |
755 | } | 782 | } |
756 | 783 | ||
757 | if (min_align > 4096) | 784 | if (min_align > io_align) |
758 | min_align = 4096; | 785 | min_align = io_align; |
759 | 786 | ||
760 | size0 = calculate_iosize(size, min_size, size1, | 787 | size0 = calculate_iosize(size, min_size, size1, |
761 | resource_size(b_res), min_align); | 788 | resource_size(b_res), min_align); |
@@ -785,6 +812,28 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, | |||
785 | } | 812 | } |
786 | } | 813 | } |
787 | 814 | ||
815 | static inline resource_size_t calculate_mem_align(resource_size_t *aligns, | ||
816 | int max_order) | ||
817 | { | ||
818 | resource_size_t align = 0; | ||
819 | resource_size_t min_align = 0; | ||
820 | int order; | ||
821 | |||
822 | for (order = 0; order <= max_order; order++) { | ||
823 | resource_size_t align1 = 1; | ||
824 | |||
825 | align1 <<= (order + 20); | ||
826 | |||
827 | if (!align) | ||
828 | min_align = align1; | ||
829 | else if (ALIGN(align + min_align, min_align) < align1) | ||
830 | min_align = align1 >> 1; | ||
831 | align += aligns[order]; | ||
832 | } | ||
833 | |||
834 | return min_align; | ||
835 | } | ||
836 | |||
788 | /** | 837 | /** |
789 | * pbus_size_mem() - size the memory window of a given bus | 838 | * pbus_size_mem() - size the memory window of a given bus |
790 | * | 839 | * |
@@ -864,19 +913,9 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, | |||
864 | children_add_size += get_res_add_size(realloc_head, r); | 913 | children_add_size += get_res_add_size(realloc_head, r); |
865 | } | 914 | } |
866 | } | 915 | } |
867 | align = 0; | ||
868 | min_align = 0; | ||
869 | for (order = 0; order <= max_order; order++) { | ||
870 | resource_size_t align1 = 1; | ||
871 | 916 | ||
872 | align1 <<= (order + 20); | 917 | min_align = calculate_mem_align(aligns, max_order); |
873 | 918 | min_align = max(min_align, window_alignment(bus, b_res->flags & mask)); | |
874 | if (!align) | ||
875 | min_align = align1; | ||
876 | else if (ALIGN(align + min_align, min_align) < align1) | ||
877 | min_align = align1 >> 1; | ||
878 | align += aligns[order]; | ||
879 | } | ||
880 | size0 = calculate_memsize(size, min_size, 0, resource_size(b_res), min_align); | 919 | size0 = calculate_memsize(size, min_size, 0, resource_size(b_res), min_align); |
881 | if (children_add_size > add_size) | 920 | if (children_add_size > add_size) |
882 | add_size = children_add_size; | 921 | add_size = children_add_size; |
diff --git a/include/linux/pci.h b/include/linux/pci.h index 0d8718904e88..e52f16189cda 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h | |||
@@ -1061,6 +1061,8 @@ int pci_cfg_space_size_ext(struct pci_dev *dev); | |||
1061 | int pci_cfg_space_size(struct pci_dev *dev); | 1061 | int pci_cfg_space_size(struct pci_dev *dev); |
1062 | unsigned char pci_bus_max_busnr(struct pci_bus *bus); | 1062 | unsigned char pci_bus_max_busnr(struct pci_bus *bus); |
1063 | void pci_setup_bridge(struct pci_bus *bus); | 1063 | void pci_setup_bridge(struct pci_bus *bus); |
1064 | resource_size_t pcibios_window_alignment(struct pci_bus *bus, | ||
1065 | unsigned long type); | ||
1064 | 1066 | ||
1065 | #define PCI_VGA_STATE_CHANGE_BRIDGE (1 << 0) | 1067 | #define PCI_VGA_STATE_CHANGE_BRIDGE (1 << 0) |
1066 | #define PCI_VGA_STATE_CHANGE_DECODES (1 << 1) | 1068 | #define PCI_VGA_STATE_CHANGE_DECODES (1 << 1) |