diff options
Diffstat (limited to 'arch/x86_64')
-rw-r--r-- | arch/x86_64/kernel/pci-calgary.c | 161 |
1 files changed, 125 insertions, 36 deletions
diff --git a/arch/x86_64/kernel/pci-calgary.c b/arch/x86_64/kernel/pci-calgary.c index f53b581dfd0b..afc0a53505f1 100644 --- a/arch/x86_64/kernel/pci-calgary.c +++ b/arch/x86_64/kernel/pci-calgary.c | |||
@@ -41,6 +41,7 @@ | |||
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> | ||
44 | 45 | ||
45 | #define PCI_DEVICE_ID_IBM_CALGARY 0x02a1 | 46 | #define PCI_DEVICE_ID_IBM_CALGARY 0x02a1 |
46 | #define PCI_VENDOR_DEVICE_ID_CALGARY \ | 47 | #define PCI_VENDOR_DEVICE_ID_CALGARY \ |
@@ -115,14 +116,35 @@ static const unsigned long phb_offsets[] = { | |||
115 | 0xB000 /* PHB3 */ | 116 | 0xB000 /* PHB3 */ |
116 | }; | 117 | }; |
117 | 118 | ||
119 | /* PHB debug registers */ | ||
120 | |||
121 | static const unsigned long phb_debug_offsets[] = { | ||
122 | 0x4000 /* PHB 0 DEBUG */, | ||
123 | 0x5000 /* PHB 1 DEBUG */, | ||
124 | 0x6000 /* PHB 2 DEBUG */, | ||
125 | 0x7000 /* PHB 3 DEBUG */ | ||
126 | }; | ||
127 | |||
128 | /* | ||
129 | * STUFF register for each debug PHB, | ||
130 | * byte 1 = start bus number, byte 2 = end bus number | ||
131 | */ | ||
132 | |||
133 | #define PHB_DEBUG_STUFF_OFFSET 0x0020 | ||
134 | |||
118 | unsigned int specified_table_size = TCE_TABLE_SIZE_UNSPECIFIED; | 135 | unsigned int specified_table_size = TCE_TABLE_SIZE_UNSPECIFIED; |
119 | static int translate_empty_slots __read_mostly = 0; | 136 | static int translate_empty_slots __read_mostly = 0; |
120 | static int calgary_detected __read_mostly = 0; | 137 | static int calgary_detected __read_mostly = 0; |
121 | 138 | ||
139 | static struct rio_table_hdr *rio_table_hdr __initdata; | ||
140 | static struct scal_detail *scal_devs[MAX_NUMNODES] __initdata; | ||
141 | static struct rio_detail *rio_devs[MAX_NUMNODES*4] __initdata; | ||
142 | |||
122 | struct calgary_bus_info { | 143 | struct calgary_bus_info { |
123 | void *tce_space; | 144 | void *tce_space; |
124 | unsigned char translation_disabled; | 145 | unsigned char translation_disabled; |
125 | signed char phbid; | 146 | signed char phbid; |
147 | void __iomem *bbar; | ||
126 | }; | 148 | }; |
127 | 149 | ||
128 | static struct calgary_bus_info bus_info[MAX_PHB_BUS_NUM] = { { NULL, 0, 0 }, }; | 150 | static struct calgary_bus_info bus_info[MAX_PHB_BUS_NUM] = { { NULL, 0, 0 }, }; |
@@ -475,6 +497,11 @@ static struct dma_mapping_ops calgary_dma_ops = { | |||
475 | .unmap_sg = calgary_unmap_sg, | 497 | .unmap_sg = calgary_unmap_sg, |
476 | }; | 498 | }; |
477 | 499 | ||
500 | static inline void __iomem * busno_to_bbar(unsigned char num) | ||
501 | { | ||
502 | return bus_info[num].bbar; | ||
503 | } | ||
504 | |||
478 | static inline int busno_to_phbid(unsigned char num) | 505 | static inline int busno_to_phbid(unsigned char num) |
479 | { | 506 | { |
480 | return bus_info[num].phbid; | 507 | return bus_info[num].phbid; |
@@ -828,31 +855,9 @@ static void __init calgary_disable_translation(struct pci_dev *dev) | |||
828 | del_timer_sync(&tbl->watchdog_timer); | 855 | del_timer_sync(&tbl->watchdog_timer); |
829 | } | 856 | } |
830 | 857 | ||
831 | static inline unsigned int __init locate_register_space(struct pci_dev *dev) | 858 | static inline void __iomem * __init locate_register_space(struct pci_dev *dev) |
832 | { | 859 | { |
833 | int rionodeid; | 860 | return busno_to_bbar(dev->bus->number); |
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 | } | 861 | } |
857 | 862 | ||
858 | static void __init calgary_init_one_nontraslated(struct pci_dev *dev) | 863 | static void __init calgary_init_one_nontraslated(struct pci_dev *dev) |
@@ -864,15 +869,12 @@ static void __init calgary_init_one_nontraslated(struct pci_dev *dev) | |||
864 | 869 | ||
865 | static int __init calgary_init_one(struct pci_dev *dev) | 870 | static int __init calgary_init_one(struct pci_dev *dev) |
866 | { | 871 | { |
867 | u32 address; | ||
868 | void __iomem *bbar; | 872 | void __iomem *bbar; |
869 | int ret; | 873 | int ret; |
870 | 874 | ||
871 | BUG_ON(dev->bus->number >= MAX_PHB_BUS_NUM); | 875 | BUG_ON(dev->bus->number >= MAX_PHB_BUS_NUM); |
872 | 876 | ||
873 | address = locate_register_space(dev); | 877 | bbar = locate_register_space(dev); |
874 | /* map entire 1MB of Calgary config space */ | ||
875 | bbar = ioremap_nocache(address, 1024 * 1024); | ||
876 | if (!bbar) { | 878 | if (!bbar) { |
877 | ret = -ENODATA; | 879 | ret = -ENODATA; |
878 | goto done; | 880 | goto done; |
@@ -898,6 +900,35 @@ static int __init calgary_init(void) | |||
898 | { | 900 | { |
899 | int ret = -ENODEV; | 901 | int ret = -ENODEV; |
900 | struct pci_dev *dev = NULL; | 902 | struct pci_dev *dev = NULL; |
903 | int rio, phb, bus; | ||
904 | void __iomem *bbar; | ||
905 | void __iomem *target; | ||
906 | u8 start_bus, end_bus; | ||
907 | u32 val; | ||
908 | |||
909 | for (rio = 0; rio < rio_table_hdr->num_rio_dev; rio++) { | ||
910 | |||
911 | if ( (rio_devs[rio]->type != COMPAT_CALGARY) && | ||
912 | (rio_devs[rio]->type != ALT_CALGARY) ) | ||
913 | continue; | ||
914 | |||
915 | /* map entire 1MB of Calgary config space */ | ||
916 | bbar = ioremap_nocache(rio_devs[rio]->BBAR, 1024 * 1024); | ||
917 | |||
918 | for (phb = 0; phb < PHBS_PER_CALGARY; phb++) { | ||
919 | |||
920 | target = calgary_reg(bbar, phb_debug_offsets[phb] | | ||
921 | PHB_DEBUG_STUFF_OFFSET); | ||
922 | val = be32_to_cpu(readl(target)); | ||
923 | start_bus = (u8)((val & 0x00FF0000) >> 16); | ||
924 | end_bus = (u8)((val & 0x0000FF00) >> 8); | ||
925 | for (bus = start_bus; bus <= end_bus; bus++) { | ||
926 | bus_info[bus].bbar = bbar; | ||
927 | bus_info[bus].phbid = phb; | ||
928 | } | ||
929 | } | ||
930 | } | ||
931 | |||
901 | 932 | ||
902 | do { | 933 | do { |
903 | dev = pci_get_device(PCI_VENDOR_ID_IBM, | 934 | dev = pci_get_device(PCI_VENDOR_ID_IBM, |
@@ -962,13 +993,55 @@ static inline int __init determine_tce_table_size(u64 ram) | |||
962 | return ret; | 993 | return ret; |
963 | } | 994 | } |
964 | 995 | ||
996 | static int __init build_detail_arrays(void) | ||
997 | { | ||
998 | unsigned long ptr; | ||
999 | int i, scal_detail_size, rio_detail_size; | ||
1000 | |||
1001 | if (rio_table_hdr->num_scal_dev > MAX_NUMNODES){ | ||
1002 | printk(KERN_WARNING | ||
1003 | "Calgary: MAX_NUMNODES too low! Defined as %d, " | ||
1004 | "but system has %d nodes.\n", | ||
1005 | MAX_NUMNODES, rio_table_hdr->num_scal_dev); | ||
1006 | return -ENODEV; | ||
1007 | } | ||
1008 | |||
1009 | switch (rio_table_hdr->version){ | ||
1010 | default: | ||
1011 | printk(KERN_WARNING | ||
1012 | "Calgary: Invalid Rio Grande Table Version: %d\n", | ||
1013 | rio_table_hdr->version); | ||
1014 | return -ENODEV; | ||
1015 | case 2: | ||
1016 | scal_detail_size = 11; | ||
1017 | rio_detail_size = 13; | ||
1018 | break; | ||
1019 | case 3: | ||
1020 | scal_detail_size = 12; | ||
1021 | rio_detail_size = 15; | ||
1022 | break; | ||
1023 | } | ||
1024 | |||
1025 | ptr = ((unsigned long)rio_table_hdr) + 3; | ||
1026 | for (i = 0; i < rio_table_hdr->num_scal_dev; | ||
1027 | i++, ptr += scal_detail_size) | ||
1028 | scal_devs[i] = (struct scal_detail *)ptr; | ||
1029 | |||
1030 | for (i = 0; i < rio_table_hdr->num_rio_dev; | ||
1031 | i++, ptr += rio_detail_size) | ||
1032 | rio_devs[i] = (struct rio_detail *)ptr; | ||
1033 | |||
1034 | return 0; | ||
1035 | } | ||
1036 | |||
965 | void __init detect_calgary(void) | 1037 | void __init detect_calgary(void) |
966 | { | 1038 | { |
967 | u32 val; | 1039 | u32 val; |
968 | int bus; | 1040 | int bus; |
969 | void *tbl; | 1041 | void *tbl; |
970 | int calgary_found = 0; | 1042 | int calgary_found = 0; |
971 | int phb = -1; | 1043 | unsigned long ptr; |
1044 | int offset; | ||
972 | 1045 | ||
973 | /* | 1046 | /* |
974 | * if the user specified iommu=off or iommu=soft or we found | 1047 | * if the user specified iommu=off or iommu=soft or we found |
@@ -980,6 +1053,29 @@ void __init detect_calgary(void) | |||
980 | if (!early_pci_allowed()) | 1053 | if (!early_pci_allowed()) |
981 | return; | 1054 | return; |
982 | 1055 | ||
1056 | ptr = (unsigned long)phys_to_virt(get_bios_ebda()); | ||
1057 | |||
1058 | rio_table_hdr = NULL; | ||
1059 | offset = 0x180; | ||
1060 | while (offset) { | ||
1061 | /* The block id is stored in the 2nd word */ | ||
1062 | if (*((unsigned short *)(ptr + offset + 2)) == 0x4752){ | ||
1063 | /* set the pointer past the offset & block id */ | ||
1064 | rio_table_hdr = (struct rio_table_hdr *)(ptr+offset+4); | ||
1065 | break; | ||
1066 | } | ||
1067 | /* The next offset is stored in the 1st word. 0 means no more */ | ||
1068 | offset = *((unsigned short *)(ptr + offset)); | ||
1069 | } | ||
1070 | if (!rio_table_hdr){ | ||
1071 | printk(KERN_ERR "Calgary: Unable to locate " | ||
1072 | "Rio Grande Table in EBDA - bailing!\n"); | ||
1073 | return; | ||
1074 | } | ||
1075 | |||
1076 | if (build_detail_arrays()) | ||
1077 | return; | ||
1078 | |||
983 | specified_table_size = determine_tce_table_size(end_pfn * PAGE_SIZE); | 1079 | specified_table_size = determine_tce_table_size(end_pfn * PAGE_SIZE); |
984 | 1080 | ||
985 | for (bus = 0; bus < MAX_PHB_BUS_NUM; bus++) { | 1081 | for (bus = 0; bus < MAX_PHB_BUS_NUM; bus++) { |
@@ -990,12 +1086,6 @@ void __init detect_calgary(void) | |||
990 | if (read_pci_config(bus, 0, 0, 0) != PCI_VENDOR_DEVICE_ID_CALGARY) | 1086 | if (read_pci_config(bus, 0, 0, 0) != PCI_VENDOR_DEVICE_ID_CALGARY) |
991 | continue; | 1087 | continue; |
992 | 1088 | ||
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) | 1089 | if (info->translation_disabled) |
1000 | continue; | 1090 | continue; |
1001 | 1091 | ||
@@ -1010,7 +1100,6 @@ void __init detect_calgary(void) | |||
1010 | if (!tbl) | 1100 | if (!tbl) |
1011 | goto cleanup; | 1101 | goto cleanup; |
1012 | info->tce_space = tbl; | 1102 | info->tce_space = tbl; |
1013 | info->phbid = phb; | ||
1014 | calgary_found = 1; | 1103 | calgary_found = 1; |
1015 | break; | 1104 | break; |
1016 | } | 1105 | } |