diff options
Diffstat (limited to 'arch/x86_64/kernel/pci-calgary.c')
-rw-r--r-- | arch/x86_64/kernel/pci-calgary.c | 218 |
1 files changed, 162 insertions, 56 deletions
diff --git a/arch/x86_64/kernel/pci-calgary.c b/arch/x86_64/kernel/pci-calgary.c index 37a770859e71..3215675ab128 100644 --- a/arch/x86_64/kernel/pci-calgary.c +++ b/arch/x86_64/kernel/pci-calgary.c | |||
@@ -41,6 +41,13 @@ | |||
41 | #include <asm/pci-direct.h> | 41 | #include <asm/pci-direct.h> |
42 | #include <asm/system.h> | 42 | #include <asm/system.h> |
43 | #include <asm/dma.h> | 43 | #include <asm/dma.h> |
44 | #include <asm/rio.h> | ||
45 | |||
46 | #ifdef CONFIG_CALGARY_IOMMU_ENABLED_BY_DEFAULT | ||
47 | int use_calgary __read_mostly = 1; | ||
48 | #else | ||
49 | int use_calgary __read_mostly = 0; | ||
50 | #endif /* CONFIG_CALGARY_DEFAULT_ENABLED */ | ||
44 | 51 | ||
45 | #define PCI_DEVICE_ID_IBM_CALGARY 0x02a1 | 52 | #define PCI_DEVICE_ID_IBM_CALGARY 0x02a1 |
46 | #define PCI_VENDOR_DEVICE_ID_CALGARY \ | 53 | #define PCI_VENDOR_DEVICE_ID_CALGARY \ |
@@ -115,14 +122,35 @@ static const unsigned long phb_offsets[] = { | |||
115 | 0xB000 /* PHB3 */ | 122 | 0xB000 /* PHB3 */ |
116 | }; | 123 | }; |
117 | 124 | ||
125 | /* PHB debug registers */ | ||
126 | |||
127 | static const unsigned long phb_debug_offsets[] = { | ||
128 | 0x4000 /* PHB 0 DEBUG */, | ||
129 | 0x5000 /* PHB 1 DEBUG */, | ||
130 | 0x6000 /* PHB 2 DEBUG */, | ||
131 | 0x7000 /* PHB 3 DEBUG */ | ||
132 | }; | ||
133 | |||
134 | /* | ||
135 | * STUFF register for each debug PHB, | ||
136 | * byte 1 = start bus number, byte 2 = end bus number | ||
137 | */ | ||
138 | |||
139 | #define PHB_DEBUG_STUFF_OFFSET 0x0020 | ||
140 | |||
118 | unsigned int specified_table_size = TCE_TABLE_SIZE_UNSPECIFIED; | 141 | unsigned int specified_table_size = TCE_TABLE_SIZE_UNSPECIFIED; |
119 | static int translate_empty_slots __read_mostly = 0; | 142 | static int translate_empty_slots __read_mostly = 0; |
120 | static int calgary_detected __read_mostly = 0; | 143 | static int calgary_detected __read_mostly = 0; |
121 | 144 | ||
145 | static struct rio_table_hdr *rio_table_hdr __initdata; | ||
146 | static struct scal_detail *scal_devs[MAX_NUMNODES] __initdata; | ||
147 | static struct rio_detail *rio_devs[MAX_NUMNODES * 4] __initdata; | ||
148 | |||
122 | struct calgary_bus_info { | 149 | struct calgary_bus_info { |
123 | void *tce_space; | 150 | void *tce_space; |
124 | unsigned char translation_disabled; | 151 | unsigned char translation_disabled; |
125 | signed char phbid; | 152 | signed char phbid; |
153 | void __iomem *bbar; | ||
126 | }; | 154 | }; |
127 | 155 | ||
128 | static struct calgary_bus_info bus_info[MAX_PHB_BUS_NUM] = { { NULL, 0, 0 }, }; | 156 | static struct calgary_bus_info bus_info[MAX_PHB_BUS_NUM] = { { NULL, 0, 0 }, }; |
@@ -475,6 +503,11 @@ static struct dma_mapping_ops calgary_dma_ops = { | |||
475 | .unmap_sg = calgary_unmap_sg, | 503 | .unmap_sg = calgary_unmap_sg, |
476 | }; | 504 | }; |
477 | 505 | ||
506 | static inline void __iomem * busno_to_bbar(unsigned char num) | ||
507 | { | ||
508 | return bus_info[num].bbar; | ||
509 | } | ||
510 | |||
478 | static inline int busno_to_phbid(unsigned char num) | 511 | static inline int busno_to_phbid(unsigned char num) |
479 | { | 512 | { |
480 | return bus_info[num].phbid; | 513 | return bus_info[num].phbid; |
@@ -620,14 +653,9 @@ static void __init calgary_reserve_peripheral_mem_2(struct pci_dev *dev) | |||
620 | static void __init calgary_reserve_regions(struct pci_dev *dev) | 653 | static void __init calgary_reserve_regions(struct pci_dev *dev) |
621 | { | 654 | { |
622 | unsigned int npages; | 655 | unsigned int npages; |
623 | void __iomem *bbar; | ||
624 | unsigned char busnum; | ||
625 | u64 start; | 656 | u64 start; |
626 | struct iommu_table *tbl = dev->sysdata; | 657 | struct iommu_table *tbl = dev->sysdata; |
627 | 658 | ||
628 | bbar = tbl->bbar; | ||
629 | busnum = dev->bus->number; | ||
630 | |||
631 | /* reserve bad_dma_address in case it's a legal address */ | 659 | /* reserve bad_dma_address in case it's a legal address */ |
632 | iommu_range_reserve(tbl, bad_dma_address, 1); | 660 | iommu_range_reserve(tbl, bad_dma_address, 1); |
633 | 661 | ||
@@ -740,7 +768,7 @@ static void __init calgary_increase_split_completion_timeout(void __iomem *bbar, | |||
740 | { | 768 | { |
741 | u64 val64; | 769 | u64 val64; |
742 | void __iomem *target; | 770 | void __iomem *target; |
743 | unsigned long phb_shift = -1; | 771 | unsigned int phb_shift = ~0; /* silence gcc */ |
744 | u64 mask; | 772 | u64 mask; |
745 | 773 | ||
746 | switch (busno_to_phbid(busnum)) { | 774 | switch (busno_to_phbid(busnum)) { |
@@ -828,33 +856,6 @@ static void __init calgary_disable_translation(struct pci_dev *dev) | |||
828 | del_timer_sync(&tbl->watchdog_timer); | 856 | del_timer_sync(&tbl->watchdog_timer); |
829 | } | 857 | } |
830 | 858 | ||
831 | static inline unsigned int __init locate_register_space(struct pci_dev *dev) | ||
832 | { | ||
833 | int rionodeid; | ||
834 | u32 address; | ||
835 | |||
836 | /* | ||
837 | * Each Calgary has four busses. The first four busses (first Calgary) | ||
838 | * have RIO node ID 2, then the next four (second Calgary) have RIO | ||
839 | * node ID 3, the next four (third Calgary) have node ID 2 again, etc. | ||
840 | * We use a gross hack - relying on the dev->bus->number ordering, | ||
841 | * modulo 14 - to decide which Calgary a given bus is on. Busses 0, 1, | ||
842 | * 2 and 4 are on the first Calgary (id 2), 6, 8, a and c are on the | ||
843 | * second (id 3), and then it repeats modulo 14. | ||
844 | */ | ||
845 | rionodeid = (dev->bus->number % 14 > 4) ? 3 : 2; | ||
846 | /* | ||
847 | * register space address calculation as follows: | ||
848 | * FE0MB-8MB*OneBasedChassisNumber+1MB*(RioNodeId-ChassisBase) | ||
849 | * ChassisBase is always zero for x366/x260/x460 | ||
850 | * RioNodeId is 2 for first Calgary, 3 for second Calgary | ||
851 | */ | ||
852 | address = START_ADDRESS - | ||
853 | (0x800000 * (ONE_BASED_CHASSIS_NUM + dev->bus->number / 14)) + | ||
854 | (0x100000) * (rionodeid - CHASSIS_BASE); | ||
855 | return address; | ||
856 | } | ||
857 | |||
858 | static void __init calgary_init_one_nontraslated(struct pci_dev *dev) | 859 | static void __init calgary_init_one_nontraslated(struct pci_dev *dev) |
859 | { | 860 | { |
860 | pci_dev_get(dev); | 861 | pci_dev_get(dev); |
@@ -864,23 +865,15 @@ static void __init calgary_init_one_nontraslated(struct pci_dev *dev) | |||
864 | 865 | ||
865 | static int __init calgary_init_one(struct pci_dev *dev) | 866 | static int __init calgary_init_one(struct pci_dev *dev) |
866 | { | 867 | { |
867 | u32 address; | ||
868 | void __iomem *bbar; | 868 | void __iomem *bbar; |
869 | int ret; | 869 | int ret; |
870 | 870 | ||
871 | BUG_ON(dev->bus->number >= MAX_PHB_BUS_NUM); | 871 | BUG_ON(dev->bus->number >= MAX_PHB_BUS_NUM); |
872 | 872 | ||
873 | address = locate_register_space(dev); | 873 | bbar = busno_to_bbar(dev->bus->number); |
874 | /* map entire 1MB of Calgary config space */ | ||
875 | bbar = ioremap_nocache(address, 1024 * 1024); | ||
876 | if (!bbar) { | ||
877 | ret = -ENODATA; | ||
878 | goto done; | ||
879 | } | ||
880 | |||
881 | ret = calgary_setup_tar(dev, bbar); | 874 | ret = calgary_setup_tar(dev, bbar); |
882 | if (ret) | 875 | if (ret) |
883 | goto iounmap; | 876 | goto done; |
884 | 877 | ||
885 | pci_dev_get(dev); | 878 | pci_dev_get(dev); |
886 | dev->bus->self = dev; | 879 | dev->bus->self = dev; |
@@ -888,17 +881,66 @@ static int __init calgary_init_one(struct pci_dev *dev) | |||
888 | 881 | ||
889 | return 0; | 882 | return 0; |
890 | 883 | ||
891 | iounmap: | ||
892 | iounmap(bbar); | ||
893 | done: | 884 | done: |
894 | return ret; | 885 | return ret; |
895 | } | 886 | } |
896 | 887 | ||
888 | static int __init calgary_locate_bbars(void) | ||
889 | { | ||
890 | int ret; | ||
891 | int rioidx, phb, bus; | ||
892 | void __iomem *bbar; | ||
893 | void __iomem *target; | ||
894 | unsigned long offset; | ||
895 | u8 start_bus, end_bus; | ||
896 | u32 val; | ||
897 | |||
898 | ret = -ENODATA; | ||
899 | for (rioidx = 0; rioidx < rio_table_hdr->num_rio_dev; rioidx++) { | ||
900 | struct rio_detail *rio = rio_devs[rioidx]; | ||
901 | |||
902 | if ((rio->type != COMPAT_CALGARY) && (rio->type != ALT_CALGARY)) | ||
903 | continue; | ||
904 | |||
905 | /* map entire 1MB of Calgary config space */ | ||
906 | bbar = ioremap_nocache(rio->BBAR, 1024 * 1024); | ||
907 | if (!bbar) | ||
908 | goto error; | ||
909 | |||
910 | for (phb = 0; phb < PHBS_PER_CALGARY; phb++) { | ||
911 | offset = phb_debug_offsets[phb] | PHB_DEBUG_STUFF_OFFSET; | ||
912 | target = calgary_reg(bbar, offset); | ||
913 | |||
914 | val = be32_to_cpu(readl(target)); | ||
915 | start_bus = (u8)((val & 0x00FF0000) >> 16); | ||
916 | end_bus = (u8)((val & 0x0000FF00) >> 8); | ||
917 | for (bus = start_bus; bus <= end_bus; bus++) { | ||
918 | bus_info[bus].bbar = bbar; | ||
919 | bus_info[bus].phbid = phb; | ||
920 | } | ||
921 | } | ||
922 | } | ||
923 | |||
924 | return 0; | ||
925 | |||
926 | error: | ||
927 | /* scan bus_info and iounmap any bbars we previously ioremap'd */ | ||
928 | for (bus = 0; bus < ARRAY_SIZE(bus_info); bus++) | ||
929 | if (bus_info[bus].bbar) | ||
930 | iounmap(bus_info[bus].bbar); | ||
931 | |||
932 | return ret; | ||
933 | } | ||
934 | |||
897 | static int __init calgary_init(void) | 935 | static int __init calgary_init(void) |
898 | { | 936 | { |
899 | int ret = -ENODEV; | 937 | int ret; |
900 | struct pci_dev *dev = NULL; | 938 | struct pci_dev *dev = NULL; |
901 | 939 | ||
940 | ret = calgary_locate_bbars(); | ||
941 | if (ret) | ||
942 | return ret; | ||
943 | |||
902 | do { | 944 | do { |
903 | dev = pci_get_device(PCI_VENDOR_ID_IBM, | 945 | dev = pci_get_device(PCI_VENDOR_ID_IBM, |
904 | PCI_DEVICE_ID_IBM_CALGARY, | 946 | PCI_DEVICE_ID_IBM_CALGARY, |
@@ -921,7 +963,7 @@ static int __init calgary_init(void) | |||
921 | 963 | ||
922 | error: | 964 | error: |
923 | do { | 965 | do { |
924 | dev = pci_find_device_reverse(PCI_VENDOR_ID_IBM, | 966 | dev = pci_get_device_reverse(PCI_VENDOR_ID_IBM, |
925 | PCI_DEVICE_ID_IBM_CALGARY, | 967 | PCI_DEVICE_ID_IBM_CALGARY, |
926 | dev); | 968 | dev); |
927 | if (!dev) | 969 | if (!dev) |
@@ -962,13 +1004,56 @@ static inline int __init determine_tce_table_size(u64 ram) | |||
962 | return ret; | 1004 | return ret; |
963 | } | 1005 | } |
964 | 1006 | ||
1007 | static int __init build_detail_arrays(void) | ||
1008 | { | ||
1009 | unsigned long ptr; | ||
1010 | int i, scal_detail_size, rio_detail_size; | ||
1011 | |||
1012 | if (rio_table_hdr->num_scal_dev > MAX_NUMNODES){ | ||
1013 | printk(KERN_WARNING | ||
1014 | "Calgary: MAX_NUMNODES too low! Defined as %d, " | ||
1015 | "but system has %d nodes.\n", | ||
1016 | MAX_NUMNODES, rio_table_hdr->num_scal_dev); | ||
1017 | return -ENODEV; | ||
1018 | } | ||
1019 | |||
1020 | switch (rio_table_hdr->version){ | ||
1021 | case 2: | ||
1022 | scal_detail_size = 11; | ||
1023 | rio_detail_size = 13; | ||
1024 | break; | ||
1025 | case 3: | ||
1026 | scal_detail_size = 12; | ||
1027 | rio_detail_size = 15; | ||
1028 | break; | ||
1029 | default: | ||
1030 | printk(KERN_WARNING | ||
1031 | "Calgary: Invalid Rio Grande Table Version: %d\n", | ||
1032 | rio_table_hdr->version); | ||
1033 | return -EPROTO; | ||
1034 | } | ||
1035 | |||
1036 | ptr = ((unsigned long)rio_table_hdr) + 3; | ||
1037 | for (i = 0; i < rio_table_hdr->num_scal_dev; | ||
1038 | i++, ptr += scal_detail_size) | ||
1039 | scal_devs[i] = (struct scal_detail *)ptr; | ||
1040 | |||
1041 | for (i = 0; i < rio_table_hdr->num_rio_dev; | ||
1042 | i++, ptr += rio_detail_size) | ||
1043 | rio_devs[i] = (struct rio_detail *)ptr; | ||
1044 | |||
1045 | return 0; | ||
1046 | } | ||
1047 | |||
965 | void __init detect_calgary(void) | 1048 | void __init detect_calgary(void) |
966 | { | 1049 | { |
967 | u32 val; | 1050 | u32 val; |
968 | int bus; | 1051 | int bus; |
969 | void *tbl; | 1052 | void *tbl; |
970 | int calgary_found = 0; | 1053 | int calgary_found = 0; |
971 | int phb = -1; | 1054 | unsigned long ptr; |
1055 | int offset; | ||
1056 | int ret; | ||
972 | 1057 | ||
973 | /* | 1058 | /* |
974 | * if the user specified iommu=off or iommu=soft or we found | 1059 | * if the user specified iommu=off or iommu=soft or we found |
@@ -977,25 +1062,47 @@ void __init detect_calgary(void) | |||
977 | if (swiotlb || no_iommu || iommu_detected) | 1062 | if (swiotlb || no_iommu || iommu_detected) |
978 | return; | 1063 | return; |
979 | 1064 | ||
1065 | if (!use_calgary) | ||
1066 | return; | ||
1067 | |||
980 | if (!early_pci_allowed()) | 1068 | if (!early_pci_allowed()) |
981 | return; | 1069 | return; |
982 | 1070 | ||
1071 | ptr = (unsigned long)phys_to_virt(get_bios_ebda()); | ||
1072 | |||
1073 | rio_table_hdr = NULL; | ||
1074 | offset = 0x180; | ||
1075 | while (offset) { | ||
1076 | /* The block id is stored in the 2nd word */ | ||
1077 | if (*((unsigned short *)(ptr + offset + 2)) == 0x4752){ | ||
1078 | /* set the pointer past the offset & block id */ | ||
1079 | rio_table_hdr = (struct rio_table_hdr *)(ptr + offset + 4); | ||
1080 | break; | ||
1081 | } | ||
1082 | /* The next offset is stored in the 1st word. 0 means no more */ | ||
1083 | offset = *((unsigned short *)(ptr + offset)); | ||
1084 | } | ||
1085 | if (!rio_table_hdr) { | ||
1086 | printk(KERN_ERR "Calgary: Unable to locate " | ||
1087 | "Rio Grande Table in EBDA - bailing!\n"); | ||
1088 | return; | ||
1089 | } | ||
1090 | |||
1091 | ret = build_detail_arrays(); | ||
1092 | if (ret) { | ||
1093 | printk(KERN_ERR "Calgary: build_detail_arrays ret %d\n", ret); | ||
1094 | return; | ||
1095 | } | ||
1096 | |||
983 | specified_table_size = determine_tce_table_size(end_pfn * PAGE_SIZE); | 1097 | specified_table_size = determine_tce_table_size(end_pfn * PAGE_SIZE); |
984 | 1098 | ||
985 | for (bus = 0; bus < MAX_PHB_BUS_NUM; bus++) { | 1099 | for (bus = 0; bus < MAX_PHB_BUS_NUM; bus++) { |
986 | int dev; | 1100 | int dev; |
987 | struct calgary_bus_info *info = &bus_info[bus]; | 1101 | struct calgary_bus_info *info = &bus_info[bus]; |
988 | info->phbid = -1; | ||
989 | 1102 | ||
990 | if (read_pci_config(bus, 0, 0, 0) != PCI_VENDOR_DEVICE_ID_CALGARY) | 1103 | if (read_pci_config(bus, 0, 0, 0) != PCI_VENDOR_DEVICE_ID_CALGARY) |
991 | continue; | 1104 | continue; |
992 | 1105 | ||
993 | /* | ||
994 | * There are 4 PHBs per Calgary chip. Set phb to which phb (0-3) | ||
995 | * it is connected to releative to the clagary chip. | ||
996 | */ | ||
997 | phb = (phb + 1) % PHBS_PER_CALGARY; | ||
998 | |||
999 | if (info->translation_disabled) | 1106 | if (info->translation_disabled) |
1000 | continue; | 1107 | continue; |
1001 | 1108 | ||
@@ -1010,7 +1117,6 @@ void __init detect_calgary(void) | |||
1010 | if (!tbl) | 1117 | if (!tbl) |
1011 | goto cleanup; | 1118 | goto cleanup; |
1012 | info->tce_space = tbl; | 1119 | info->tce_space = tbl; |
1013 | info->phbid = phb; | ||
1014 | calgary_found = 1; | 1120 | calgary_found = 1; |
1015 | break; | 1121 | break; |
1016 | } | 1122 | } |