diff options
Diffstat (limited to 'arch/x86_64/kernel/pci-calgary.c')
-rw-r--r-- | arch/x86_64/kernel/pci-calgary.c | 83 |
1 files changed, 48 insertions, 35 deletions
diff --git a/arch/x86_64/kernel/pci-calgary.c b/arch/x86_64/kernel/pci-calgary.c index d91cb843f54d..146924ba5df5 100644 --- a/arch/x86_64/kernel/pci-calgary.c +++ b/arch/x86_64/kernel/pci-calgary.c | |||
@@ -1,9 +1,11 @@ | |||
1 | /* | 1 | /* |
2 | * Derived from arch/powerpc/kernel/iommu.c | 2 | * Derived from arch/powerpc/kernel/iommu.c |
3 | * | 3 | * |
4 | * Copyright (C) 2006 Jon Mason <jdmason@us.ibm.com>, IBM Corporation | 4 | * Copyright (C) IBM Corporation, 2006 |
5 | * Copyright (C) 2006 Muli Ben-Yehuda <muli@il.ibm.com>, IBM Corporation | ||
6 | * | 5 | * |
6 | * Author: Jon Mason <jdmason@us.ibm.com> | ||
7 | * Author: Muli Ben-Yehuda <muli@il.ibm.com> | ||
8 | |||
7 | * This program is free software; you can redistribute it and/or modify | 9 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License as published by | 10 | * it under the terms of the GNU General Public License as published by |
9 | * the Free Software Foundation; either version 2 of the License, or | 11 | * the Free Software Foundation; either version 2 of the License, or |
@@ -83,7 +85,8 @@ | |||
83 | #define CSR_AGENT_MASK 0xffe0ffff | 85 | #define CSR_AGENT_MASK 0xffe0ffff |
84 | 86 | ||
85 | #define MAX_NUM_OF_PHBS 8 /* how many PHBs in total? */ | 87 | #define MAX_NUM_OF_PHBS 8 /* how many PHBs in total? */ |
86 | #define MAX_PHB_BUS_NUM (MAX_NUM_OF_PHBS * 2) /* max dev->bus->number */ | 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 */ | ||
87 | #define PHBS_PER_CALGARY 4 | 90 | #define PHBS_PER_CALGARY 4 |
88 | 91 | ||
89 | /* register offsets in Calgary's internal register space */ | 92 | /* register offsets in Calgary's internal register space */ |
@@ -108,7 +111,8 @@ static const unsigned long phb_offsets[] = { | |||
108 | 0xB000 /* PHB3 */ | 111 | 0xB000 /* PHB3 */ |
109 | }; | 112 | }; |
110 | 113 | ||
111 | void* tce_table_kva[MAX_NUM_OF_PHBS * MAX_NUMNODES]; | 114 | static char bus_to_phb[MAX_PHB_BUS_NUM]; |
115 | void* tce_table_kva[MAX_PHB_BUS_NUM]; | ||
112 | unsigned int specified_table_size = TCE_TABLE_SIZE_UNSPECIFIED; | 116 | unsigned int specified_table_size = TCE_TABLE_SIZE_UNSPECIFIED; |
113 | static int translate_empty_slots __read_mostly = 0; | 117 | static int translate_empty_slots __read_mostly = 0; |
114 | static int calgary_detected __read_mostly = 0; | 118 | static int calgary_detected __read_mostly = 0; |
@@ -117,7 +121,7 @@ static int calgary_detected __read_mostly = 0; | |||
117 | * the bitmap of PHBs the user requested that we disable | 121 | * the bitmap of PHBs the user requested that we disable |
118 | * translation on. | 122 | * translation on. |
119 | */ | 123 | */ |
120 | static DECLARE_BITMAP(translation_disabled, MAX_NUMNODES * MAX_PHB_BUS_NUM); | 124 | static DECLARE_BITMAP(translation_disabled, MAX_PHB_BUS_NUM); |
121 | 125 | ||
122 | static void tce_cache_blast(struct iommu_table *tbl); | 126 | static void tce_cache_blast(struct iommu_table *tbl); |
123 | 127 | ||
@@ -450,7 +454,7 @@ static struct dma_mapping_ops calgary_dma_ops = { | |||
450 | 454 | ||
451 | static inline int busno_to_phbid(unsigned char num) | 455 | static inline int busno_to_phbid(unsigned char num) |
452 | { | 456 | { |
453 | return bus_to_phb(num) % PHBS_PER_CALGARY; | 457 | return bus_to_phb[num]; |
454 | } | 458 | } |
455 | 459 | ||
456 | static inline unsigned long split_queue_offset(unsigned char num) | 460 | static inline unsigned long split_queue_offset(unsigned char num) |
@@ -810,7 +814,7 @@ static int __init calgary_init(void) | |||
810 | int i, ret = -ENODEV; | 814 | int i, ret = -ENODEV; |
811 | struct pci_dev *dev = NULL; | 815 | struct pci_dev *dev = NULL; |
812 | 816 | ||
813 | for (i = 0; i <= num_online_nodes() * MAX_NUM_OF_PHBS; i++) { | 817 | for (i = 0; i < MAX_PHB_BUS_NUM; i++) { |
814 | dev = pci_get_device(PCI_VENDOR_ID_IBM, | 818 | dev = pci_get_device(PCI_VENDOR_ID_IBM, |
815 | PCI_DEVICE_ID_IBM_CALGARY, | 819 | PCI_DEVICE_ID_IBM_CALGARY, |
816 | dev); | 820 | dev); |
@@ -820,7 +824,7 @@ static int __init calgary_init(void) | |||
820 | calgary_init_one_nontraslated(dev); | 824 | calgary_init_one_nontraslated(dev); |
821 | continue; | 825 | continue; |
822 | } | 826 | } |
823 | if (!tce_table_kva[i] && !translate_empty_slots) { | 827 | if (!tce_table_kva[dev->bus->number] && !translate_empty_slots) { |
824 | pci_dev_put(dev); | 828 | pci_dev_put(dev); |
825 | continue; | 829 | continue; |
826 | } | 830 | } |
@@ -840,7 +844,7 @@ error: | |||
840 | pci_dev_put(dev); | 844 | pci_dev_put(dev); |
841 | continue; | 845 | continue; |
842 | } | 846 | } |
843 | if (!tce_table_kva[i] && !translate_empty_slots) | 847 | if (!tce_table_kva[dev->bus->number] && !translate_empty_slots) |
844 | continue; | 848 | continue; |
845 | calgary_disable_translation(dev); | 849 | calgary_disable_translation(dev); |
846 | calgary_free_tar(dev); | 850 | calgary_free_tar(dev); |
@@ -874,9 +878,10 @@ static inline int __init determine_tce_table_size(u64 ram) | |||
874 | void __init detect_calgary(void) | 878 | void __init detect_calgary(void) |
875 | { | 879 | { |
876 | u32 val; | 880 | u32 val; |
877 | int bus, table_idx; | 881 | int bus; |
878 | void *tbl; | 882 | void *tbl; |
879 | int detected = 0; | 883 | int calgary_found = 0; |
884 | int phb = -1; | ||
880 | 885 | ||
881 | /* | 886 | /* |
882 | * if the user specified iommu=off or iommu=soft or we found | 887 | * if the user specified iommu=off or iommu=soft or we found |
@@ -887,38 +892,46 @@ void __init detect_calgary(void) | |||
887 | 892 | ||
888 | specified_table_size = determine_tce_table_size(end_pfn * PAGE_SIZE); | 893 | specified_table_size = determine_tce_table_size(end_pfn * PAGE_SIZE); |
889 | 894 | ||
890 | for (bus = 0, table_idx = 0; | 895 | for (bus = 0; bus < MAX_PHB_BUS_NUM; bus++) { |
891 | bus <= num_online_nodes() * MAX_PHB_BUS_NUM; | 896 | int dev; |
892 | bus++) { | 897 | |
893 | BUG_ON(bus > MAX_NUMNODES * MAX_PHB_BUS_NUM); | 898 | tce_table_kva[bus] = NULL; |
899 | bus_to_phb[bus] = -1; | ||
900 | |||
894 | if (read_pci_config(bus, 0, 0, 0) != PCI_VENDOR_DEVICE_ID_CALGARY) | 901 | if (read_pci_config(bus, 0, 0, 0) != PCI_VENDOR_DEVICE_ID_CALGARY) |
895 | continue; | 902 | continue; |
903 | |||
904 | /* | ||
905 | * There are 4 PHBs per Calgary chip. Set phb to which phb (0-3) | ||
906 | * it is connected to releative to the clagary chip. | ||
907 | */ | ||
908 | phb = (phb + 1) % PHBS_PER_CALGARY; | ||
909 | |||
896 | if (test_bit(bus, translation_disabled)) { | 910 | if (test_bit(bus, translation_disabled)) { |
897 | printk(KERN_INFO "Calgary: translation is disabled for " | 911 | printk(KERN_INFO "Calgary: translation is disabled for " |
898 | "PHB 0x%x\n", bus); | 912 | "PHB 0x%x\n", bus); |
899 | /* skip this phb, don't allocate a tbl for it */ | 913 | /* skip this phb, don't allocate a tbl for it */ |
900 | tce_table_kva[table_idx] = NULL; | ||
901 | table_idx++; | ||
902 | continue; | 914 | continue; |
903 | } | 915 | } |
904 | /* | 916 | /* |
905 | * scan the first slot of the PCI bus to see if there | 917 | * Scan the slots of the PCI bus to see if there is a device present. |
906 | * are any devices present | 918 | * The parent bus will be the zero-ith device, so start at 1. |
907 | */ | 919 | */ |
908 | val = read_pci_config(bus, 1, 0, 0); | 920 | for (dev = 1; dev < 8; dev++) { |
909 | if (val != 0xffffffff || translate_empty_slots) { | 921 | val = read_pci_config(bus, dev, 0, 0); |
910 | tbl = alloc_tce_table(); | 922 | if (val != 0xffffffff || translate_empty_slots) { |
911 | if (!tbl) | 923 | tbl = alloc_tce_table(); |
912 | goto cleanup; | 924 | if (!tbl) |
913 | detected = 1; | 925 | goto cleanup; |
914 | } else | 926 | tce_table_kva[bus] = tbl; |
915 | tbl = NULL; | 927 | bus_to_phb[bus] = phb; |
916 | 928 | calgary_found = 1; | |
917 | tce_table_kva[table_idx] = tbl; | 929 | break; |
918 | table_idx++; | 930 | } |
931 | } | ||
919 | } | 932 | } |
920 | 933 | ||
921 | if (detected) { | 934 | if (calgary_found) { |
922 | iommu_detected = 1; | 935 | iommu_detected = 1; |
923 | calgary_detected = 1; | 936 | calgary_detected = 1; |
924 | printk(KERN_INFO "PCI-DMA: Calgary IOMMU detected. " | 937 | printk(KERN_INFO "PCI-DMA: Calgary IOMMU detected. " |
@@ -927,9 +940,9 @@ void __init detect_calgary(void) | |||
927 | return; | 940 | return; |
928 | 941 | ||
929 | cleanup: | 942 | cleanup: |
930 | for (--table_idx; table_idx >= 0; --table_idx) | 943 | for (--bus; bus >= 0; --bus) |
931 | if (tce_table_kva[table_idx]) | 944 | if (tce_table_kva[bus]) |
932 | free_tce_table(tce_table_kva[table_idx]); | 945 | free_tce_table(tce_table_kva[bus]); |
933 | } | 946 | } |
934 | 947 | ||
935 | int __init calgary_iommu_init(void) | 948 | int __init calgary_iommu_init(void) |
@@ -1000,7 +1013,7 @@ static int __init calgary_parse_options(char *p) | |||
1000 | if (p == endp) | 1013 | if (p == endp) |
1001 | break; | 1014 | break; |
1002 | 1015 | ||
1003 | if (bridge <= (num_online_nodes() * MAX_PHB_BUS_NUM)) { | 1016 | if (bridge < MAX_PHB_BUS_NUM) { |
1004 | printk(KERN_INFO "Calgary: disabling " | 1017 | printk(KERN_INFO "Calgary: disabling " |
1005 | "translation for PHB 0x%x\n", bridge); | 1018 | "translation for PHB 0x%x\n", bridge); |
1006 | set_bit(bridge, translation_disabled); | 1019 | set_bit(bridge, translation_disabled); |