diff options
Diffstat (limited to 'arch/x86_64/kernel/pci-calgary.c')
-rw-r--r-- | arch/x86_64/kernel/pci-calgary.c | 142 |
1 files changed, 90 insertions, 52 deletions
diff --git a/arch/x86_64/kernel/pci-calgary.c b/arch/x86_64/kernel/pci-calgary.c index 146924ba5df5..cfb09b07ae99 100644 --- a/arch/x86_64/kernel/pci-calgary.c +++ b/arch/x86_64/kernel/pci-calgary.c | |||
@@ -86,7 +86,8 @@ | |||
86 | 86 | ||
87 | #define MAX_NUM_OF_PHBS 8 /* how many PHBs in total? */ | 87 | #define MAX_NUM_OF_PHBS 8 /* how many PHBs in total? */ |
88 | #define MAX_NUM_CHASSIS 8 /* max number of chassis */ | 88 | #define MAX_NUM_CHASSIS 8 /* max number of chassis */ |
89 | #define MAX_PHB_BUS_NUM (MAX_NUM_OF_PHBS * MAX_NUM_CHASSIS * 2) /* max dev->bus->number */ | 89 | /* MAX_PHB_BUS_NUM is the maximal possible dev->bus->number */ |
90 | #define MAX_PHB_BUS_NUM (MAX_NUM_OF_PHBS * MAX_NUM_CHASSIS * 2) | ||
90 | #define PHBS_PER_CALGARY 4 | 91 | #define PHBS_PER_CALGARY 4 |
91 | 92 | ||
92 | /* register offsets in Calgary's internal register space */ | 93 | /* register offsets in Calgary's internal register space */ |
@@ -111,31 +112,49 @@ static const unsigned long phb_offsets[] = { | |||
111 | 0xB000 /* PHB3 */ | 112 | 0xB000 /* PHB3 */ |
112 | }; | 113 | }; |
113 | 114 | ||
114 | static char bus_to_phb[MAX_PHB_BUS_NUM]; | ||
115 | void* tce_table_kva[MAX_PHB_BUS_NUM]; | ||
116 | unsigned int specified_table_size = TCE_TABLE_SIZE_UNSPECIFIED; | 115 | unsigned int specified_table_size = TCE_TABLE_SIZE_UNSPECIFIED; |
117 | static int translate_empty_slots __read_mostly = 0; | 116 | static int translate_empty_slots __read_mostly = 0; |
118 | static int calgary_detected __read_mostly = 0; | 117 | static int calgary_detected __read_mostly = 0; |
119 | 118 | ||
120 | /* | 119 | struct calgary_bus_info { |
121 | * the bitmap of PHBs the user requested that we disable | 120 | void *tce_space; |
122 | * translation on. | 121 | unsigned char translation_disabled; |
123 | */ | 122 | signed char phbid; |
124 | static DECLARE_BITMAP(translation_disabled, MAX_PHB_BUS_NUM); | 123 | }; |
124 | |||
125 | static struct calgary_bus_info bus_info[MAX_PHB_BUS_NUM] = { { NULL, 0, 0 }, }; | ||
125 | 126 | ||
126 | static void tce_cache_blast(struct iommu_table *tbl); | 127 | static void tce_cache_blast(struct iommu_table *tbl); |
127 | 128 | ||
128 | /* enable this to stress test the chip's TCE cache */ | 129 | /* enable this to stress test the chip's TCE cache */ |
129 | #ifdef CONFIG_IOMMU_DEBUG | 130 | #ifdef CONFIG_IOMMU_DEBUG |
130 | static inline void tce_cache_blast_stress(struct iommu_table *tbl) | 131 | int debugging __read_mostly = 1; |
132 | |||
133 | static inline unsigned long verify_bit_range(unsigned long* bitmap, | ||
134 | int expected, unsigned long start, unsigned long end) | ||
131 | { | 135 | { |
132 | tce_cache_blast(tbl); | 136 | unsigned long idx = start; |
137 | |||
138 | BUG_ON(start >= end); | ||
139 | |||
140 | while (idx < end) { | ||
141 | if (!!test_bit(idx, bitmap) != expected) | ||
142 | return idx; | ||
143 | ++idx; | ||
144 | } | ||
145 | |||
146 | /* all bits have the expected value */ | ||
147 | return ~0UL; | ||
133 | } | 148 | } |
134 | #else | 149 | #else /* debugging is disabled */ |
135 | static inline void tce_cache_blast_stress(struct iommu_table *tbl) | 150 | int debugging __read_mostly = 0; |
151 | |||
152 | static inline unsigned long verify_bit_range(unsigned long* bitmap, | ||
153 | int expected, unsigned long start, unsigned long end) | ||
136 | { | 154 | { |
155 | return ~0UL; | ||
137 | } | 156 | } |
138 | #endif /* BLAST_TCE_CACHE_ON_UNMAP */ | 157 | #endif /* CONFIG_IOMMU_DEBUG */ |
139 | 158 | ||
140 | static inline unsigned int num_dma_pages(unsigned long dma, unsigned int dmalen) | 159 | static inline unsigned int num_dma_pages(unsigned long dma, unsigned int dmalen) |
141 | { | 160 | { |
@@ -149,7 +168,7 @@ static inline unsigned int num_dma_pages(unsigned long dma, unsigned int dmalen) | |||
149 | 168 | ||
150 | static inline int translate_phb(struct pci_dev* dev) | 169 | static inline int translate_phb(struct pci_dev* dev) |
151 | { | 170 | { |
152 | int disabled = test_bit(dev->bus->number, translation_disabled); | 171 | int disabled = bus_info[dev->bus->number].translation_disabled; |
153 | return !disabled; | 172 | return !disabled; |
154 | } | 173 | } |
155 | 174 | ||
@@ -158,6 +177,7 @@ static void iommu_range_reserve(struct iommu_table *tbl, | |||
158 | { | 177 | { |
159 | unsigned long index; | 178 | unsigned long index; |
160 | unsigned long end; | 179 | unsigned long end; |
180 | unsigned long badbit; | ||
161 | 181 | ||
162 | index = start_addr >> PAGE_SHIFT; | 182 | index = start_addr >> PAGE_SHIFT; |
163 | 183 | ||
@@ -169,14 +189,15 @@ static void iommu_range_reserve(struct iommu_table *tbl, | |||
169 | if (end > tbl->it_size) /* don't go off the table */ | 189 | if (end > tbl->it_size) /* don't go off the table */ |
170 | end = tbl->it_size; | 190 | end = tbl->it_size; |
171 | 191 | ||
172 | while (index < end) { | 192 | badbit = verify_bit_range(tbl->it_map, 0, index, end); |
173 | if (test_bit(index, tbl->it_map)) | 193 | if (badbit != ~0UL) { |
194 | if (printk_ratelimit()) | ||
174 | printk(KERN_ERR "Calgary: entry already allocated at " | 195 | printk(KERN_ERR "Calgary: entry already allocated at " |
175 | "0x%lx tbl %p dma 0x%lx npages %u\n", | 196 | "0x%lx tbl %p dma 0x%lx npages %u\n", |
176 | index, tbl, start_addr, npages); | 197 | badbit, tbl, start_addr, npages); |
177 | ++index; | ||
178 | } | 198 | } |
179 | set_bit_string(tbl->it_map, start_addr >> PAGE_SHIFT, npages); | 199 | |
200 | set_bit_string(tbl->it_map, index, npages); | ||
180 | } | 201 | } |
181 | 202 | ||
182 | static unsigned long iommu_range_alloc(struct iommu_table *tbl, | 203 | static unsigned long iommu_range_alloc(struct iommu_table *tbl, |
@@ -243,7 +264,7 @@ static void __iommu_free(struct iommu_table *tbl, dma_addr_t dma_addr, | |||
243 | unsigned int npages) | 264 | unsigned int npages) |
244 | { | 265 | { |
245 | unsigned long entry; | 266 | unsigned long entry; |
246 | unsigned long i; | 267 | unsigned long badbit; |
247 | 268 | ||
248 | entry = dma_addr >> PAGE_SHIFT; | 269 | entry = dma_addr >> PAGE_SHIFT; |
249 | 270 | ||
@@ -251,16 +272,15 @@ static void __iommu_free(struct iommu_table *tbl, dma_addr_t dma_addr, | |||
251 | 272 | ||
252 | tce_free(tbl, entry, npages); | 273 | tce_free(tbl, entry, npages); |
253 | 274 | ||
254 | for (i = 0; i < npages; ++i) { | 275 | badbit = verify_bit_range(tbl->it_map, 1, entry, entry + npages); |
255 | if (!test_bit(entry + i, tbl->it_map)) | 276 | if (badbit != ~0UL) { |
277 | if (printk_ratelimit()) | ||
256 | printk(KERN_ERR "Calgary: bit is off at 0x%lx " | 278 | printk(KERN_ERR "Calgary: bit is off at 0x%lx " |
257 | "tbl %p dma 0x%Lx entry 0x%lx npages %u\n", | 279 | "tbl %p dma 0x%Lx entry 0x%lx npages %u\n", |
258 | entry + i, tbl, dma_addr, entry, npages); | 280 | badbit, tbl, dma_addr, entry, npages); |
259 | } | 281 | } |
260 | 282 | ||
261 | __clear_bit_string(tbl->it_map, entry, npages); | 283 | __clear_bit_string(tbl->it_map, entry, npages); |
262 | |||
263 | tce_cache_blast_stress(tbl); | ||
264 | } | 284 | } |
265 | 285 | ||
266 | static void iommu_free(struct iommu_table *tbl, dma_addr_t dma_addr, | 286 | static void iommu_free(struct iommu_table *tbl, dma_addr_t dma_addr, |
@@ -454,7 +474,7 @@ static struct dma_mapping_ops calgary_dma_ops = { | |||
454 | 474 | ||
455 | static inline int busno_to_phbid(unsigned char num) | 475 | static inline int busno_to_phbid(unsigned char num) |
456 | { | 476 | { |
457 | return bus_to_phb[num]; | 477 | return bus_info[num].phbid; |
458 | } | 478 | } |
459 | 479 | ||
460 | static inline unsigned long split_queue_offset(unsigned char num) | 480 | static inline unsigned long split_queue_offset(unsigned char num) |
@@ -631,6 +651,10 @@ static int __init calgary_setup_tar(struct pci_dev *dev, void __iomem *bbar) | |||
631 | if (ret) | 651 | if (ret) |
632 | return ret; | 652 | return ret; |
633 | 653 | ||
654 | tbl = dev->sysdata; | ||
655 | tbl->it_base = (unsigned long)bus_info[dev->bus->number].tce_space; | ||
656 | tce_free(tbl, 0, tbl->it_size); | ||
657 | |||
634 | calgary_reserve_regions(dev); | 658 | calgary_reserve_regions(dev); |
635 | 659 | ||
636 | /* set TARs for each PHB */ | 660 | /* set TARs for each PHB */ |
@@ -654,11 +678,12 @@ static int __init calgary_setup_tar(struct pci_dev *dev, void __iomem *bbar) | |||
654 | return 0; | 678 | return 0; |
655 | } | 679 | } |
656 | 680 | ||
657 | static void __init calgary_free_tar(struct pci_dev *dev) | 681 | static void __init calgary_free_bus(struct pci_dev *dev) |
658 | { | 682 | { |
659 | u64 val64; | 683 | u64 val64; |
660 | struct iommu_table *tbl = dev->sysdata; | 684 | struct iommu_table *tbl = dev->sysdata; |
661 | void __iomem *target; | 685 | void __iomem *target; |
686 | unsigned int bitmapsz; | ||
662 | 687 | ||
663 | target = calgary_reg(tbl->bbar, tar_offset(dev->bus->number)); | 688 | target = calgary_reg(tbl->bbar, tar_offset(dev->bus->number)); |
664 | val64 = be64_to_cpu(readq(target)); | 689 | val64 = be64_to_cpu(readq(target)); |
@@ -666,8 +691,15 @@ static void __init calgary_free_tar(struct pci_dev *dev) | |||
666 | writeq(cpu_to_be64(val64), target); | 691 | writeq(cpu_to_be64(val64), target); |
667 | readq(target); /* flush */ | 692 | readq(target); /* flush */ |
668 | 693 | ||
694 | bitmapsz = tbl->it_size / BITS_PER_BYTE; | ||
695 | free_pages((unsigned long)tbl->it_map, get_order(bitmapsz)); | ||
696 | tbl->it_map = NULL; | ||
697 | |||
669 | kfree(tbl); | 698 | kfree(tbl); |
670 | dev->sysdata = NULL; | 699 | dev->sysdata = NULL; |
700 | |||
701 | /* Can't free bootmem allocated memory after system is up :-( */ | ||
702 | bus_info[dev->bus->number].tce_space = NULL; | ||
671 | } | 703 | } |
672 | 704 | ||
673 | static void calgary_watchdog(unsigned long data) | 705 | static void calgary_watchdog(unsigned long data) |
@@ -772,12 +804,11 @@ static inline unsigned int __init locate_register_space(struct pci_dev *dev) | |||
772 | return address; | 804 | return address; |
773 | } | 805 | } |
774 | 806 | ||
775 | static int __init calgary_init_one_nontraslated(struct pci_dev *dev) | 807 | static void __init calgary_init_one_nontraslated(struct pci_dev *dev) |
776 | { | 808 | { |
809 | pci_dev_get(dev); | ||
777 | dev->sysdata = NULL; | 810 | dev->sysdata = NULL; |
778 | dev->bus->self = dev; | 811 | dev->bus->self = dev; |
779 | |||
780 | return 0; | ||
781 | } | 812 | } |
782 | 813 | ||
783 | static int __init calgary_init_one(struct pci_dev *dev) | 814 | static int __init calgary_init_one(struct pci_dev *dev) |
@@ -798,6 +829,7 @@ static int __init calgary_init_one(struct pci_dev *dev) | |||
798 | if (ret) | 829 | if (ret) |
799 | goto iounmap; | 830 | goto iounmap; |
800 | 831 | ||
832 | pci_dev_get(dev); | ||
801 | dev->bus->self = dev; | 833 | dev->bus->self = dev; |
802 | calgary_enable_translation(dev); | 834 | calgary_enable_translation(dev); |
803 | 835 | ||
@@ -824,10 +856,9 @@ static int __init calgary_init(void) | |||
824 | calgary_init_one_nontraslated(dev); | 856 | calgary_init_one_nontraslated(dev); |
825 | continue; | 857 | continue; |
826 | } | 858 | } |
827 | if (!tce_table_kva[dev->bus->number] && !translate_empty_slots) { | 859 | if (!bus_info[dev->bus->number].tce_space && !translate_empty_slots) |
828 | pci_dev_put(dev); | ||
829 | continue; | 860 | continue; |
830 | } | 861 | |
831 | ret = calgary_init_one(dev); | 862 | ret = calgary_init_one(dev); |
832 | if (ret) | 863 | if (ret) |
833 | goto error; | 864 | goto error; |
@@ -840,15 +871,18 @@ error: | |||
840 | dev = pci_find_device_reverse(PCI_VENDOR_ID_IBM, | 871 | dev = pci_find_device_reverse(PCI_VENDOR_ID_IBM, |
841 | PCI_DEVICE_ID_IBM_CALGARY, | 872 | PCI_DEVICE_ID_IBM_CALGARY, |
842 | dev); | 873 | dev); |
874 | if (!dev) | ||
875 | break; | ||
843 | if (!translate_phb(dev)) { | 876 | if (!translate_phb(dev)) { |
844 | pci_dev_put(dev); | 877 | pci_dev_put(dev); |
845 | continue; | 878 | continue; |
846 | } | 879 | } |
847 | if (!tce_table_kva[dev->bus->number] && !translate_empty_slots) | 880 | if (!bus_info[dev->bus->number].tce_space && !translate_empty_slots) |
848 | continue; | 881 | continue; |
882 | |||
849 | calgary_disable_translation(dev); | 883 | calgary_disable_translation(dev); |
850 | calgary_free_tar(dev); | 884 | calgary_free_bus(dev); |
851 | pci_dev_put(dev); | 885 | pci_dev_put(dev); /* Undo calgary_init_one()'s pci_dev_get() */ |
852 | } | 886 | } |
853 | 887 | ||
854 | return ret; | 888 | return ret; |
@@ -890,13 +924,15 @@ void __init detect_calgary(void) | |||
890 | if (swiotlb || no_iommu || iommu_detected) | 924 | if (swiotlb || no_iommu || iommu_detected) |
891 | return; | 925 | return; |
892 | 926 | ||
927 | if (!early_pci_allowed()) | ||
928 | return; | ||
929 | |||
893 | specified_table_size = determine_tce_table_size(end_pfn * PAGE_SIZE); | 930 | specified_table_size = determine_tce_table_size(end_pfn * PAGE_SIZE); |
894 | 931 | ||
895 | for (bus = 0; bus < MAX_PHB_BUS_NUM; bus++) { | 932 | for (bus = 0; bus < MAX_PHB_BUS_NUM; bus++) { |
896 | int dev; | 933 | int dev; |
897 | 934 | struct calgary_bus_info *info = &bus_info[bus]; | |
898 | tce_table_kva[bus] = NULL; | 935 | info->phbid = -1; |
899 | bus_to_phb[bus] = -1; | ||
900 | 936 | ||
901 | if (read_pci_config(bus, 0, 0, 0) != PCI_VENDOR_DEVICE_ID_CALGARY) | 937 | if (read_pci_config(bus, 0, 0, 0) != PCI_VENDOR_DEVICE_ID_CALGARY) |
902 | continue; | 938 | continue; |
@@ -907,12 +943,9 @@ void __init detect_calgary(void) | |||
907 | */ | 943 | */ |
908 | phb = (phb + 1) % PHBS_PER_CALGARY; | 944 | phb = (phb + 1) % PHBS_PER_CALGARY; |
909 | 945 | ||
910 | if (test_bit(bus, translation_disabled)) { | 946 | if (info->translation_disabled) |
911 | printk(KERN_INFO "Calgary: translation is disabled for " | ||
912 | "PHB 0x%x\n", bus); | ||
913 | /* skip this phb, don't allocate a tbl for it */ | ||
914 | continue; | 947 | continue; |
915 | } | 948 | |
916 | /* | 949 | /* |
917 | * Scan the slots of the PCI bus to see if there is a device present. | 950 | * Scan the slots of the PCI bus to see if there is a device present. |
918 | * The parent bus will be the zero-ith device, so start at 1. | 951 | * The parent bus will be the zero-ith device, so start at 1. |
@@ -923,8 +956,8 @@ void __init detect_calgary(void) | |||
923 | tbl = alloc_tce_table(); | 956 | tbl = alloc_tce_table(); |
924 | if (!tbl) | 957 | if (!tbl) |
925 | goto cleanup; | 958 | goto cleanup; |
926 | tce_table_kva[bus] = tbl; | 959 | info->tce_space = tbl; |
927 | bus_to_phb[bus] = phb; | 960 | info->phbid = phb; |
928 | calgary_found = 1; | 961 | calgary_found = 1; |
929 | break; | 962 | break; |
930 | } | 963 | } |
@@ -934,15 +967,20 @@ void __init detect_calgary(void) | |||
934 | if (calgary_found) { | 967 | if (calgary_found) { |
935 | iommu_detected = 1; | 968 | iommu_detected = 1; |
936 | calgary_detected = 1; | 969 | calgary_detected = 1; |
937 | printk(KERN_INFO "PCI-DMA: Calgary IOMMU detected. " | 970 | printk(KERN_INFO "PCI-DMA: Calgary IOMMU detected.\n"); |
938 | "TCE table spec is %d.\n", specified_table_size); | 971 | printk(KERN_INFO "PCI-DMA: Calgary TCE table spec is %d, " |
972 | "CONFIG_IOMMU_DEBUG is %s.\n", specified_table_size, | ||
973 | debugging ? "enabled" : "disabled"); | ||
939 | } | 974 | } |
940 | return; | 975 | return; |
941 | 976 | ||
942 | cleanup: | 977 | cleanup: |
943 | for (--bus; bus >= 0; --bus) | 978 | for (--bus; bus >= 0; --bus) { |
944 | if (tce_table_kva[bus]) | 979 | struct calgary_bus_info *info = &bus_info[bus]; |
945 | free_tce_table(tce_table_kva[bus]); | 980 | |
981 | if (info->tce_space) | ||
982 | free_tce_table(info->tce_space); | ||
983 | } | ||
946 | } | 984 | } |
947 | 985 | ||
948 | int __init calgary_iommu_init(void) | 986 | int __init calgary_iommu_init(void) |
@@ -1016,7 +1054,7 @@ static int __init calgary_parse_options(char *p) | |||
1016 | if (bridge < MAX_PHB_BUS_NUM) { | 1054 | if (bridge < MAX_PHB_BUS_NUM) { |
1017 | printk(KERN_INFO "Calgary: disabling " | 1055 | printk(KERN_INFO "Calgary: disabling " |
1018 | "translation for PHB 0x%x\n", bridge); | 1056 | "translation for PHB 0x%x\n", bridge); |
1019 | set_bit(bridge, translation_disabled); | 1057 | bus_info[bridge].translation_disabled = 1; |
1020 | } | 1058 | } |
1021 | } | 1059 | } |
1022 | 1060 | ||