aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMuli Ben-Yehuda <muli@il.ibm.com>2007-07-21 11:10:52 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-21 21:37:11 -0400
commit8a244590ca699ebbf8c5682d11c47732b7cc9db9 (patch)
tree3c625e05f04092117b2a015f21f40a966a2751a5
parent35b6dfa08736e8a362b9d41ff52ffa1eb36505a9 (diff)
x86_64: introduce CalIOC2 support
CalIOC2 is a PCI-e implementation of the Calgary logic. Most of the programming details are the same, but some differ, e.g., TCE cache flush. This patch introduces CalIOC2 support - detection and various support routines. It's not expected to work yet (but will with follow-on patches). Signed-off-by: Muli Ben-Yehuda <muli@il.ibm.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Andi Kleen <ak@suse.de> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--arch/x86_64/kernel/pci-calgary.c196
1 files changed, 156 insertions, 40 deletions
diff --git a/arch/x86_64/kernel/pci-calgary.c b/arch/x86_64/kernel/pci-calgary.c
index a2828e156c3d..558e68aa6b05 100644
--- a/arch/x86_64/kernel/pci-calgary.c
+++ b/arch/x86_64/kernel/pci-calgary.c
@@ -50,8 +50,7 @@ int use_calgary __read_mostly = 0;
50#endif /* CONFIG_CALGARY_DEFAULT_ENABLED */ 50#endif /* CONFIG_CALGARY_DEFAULT_ENABLED */
51 51
52#define PCI_DEVICE_ID_IBM_CALGARY 0x02a1 52#define PCI_DEVICE_ID_IBM_CALGARY 0x02a1
53#define PCI_VENDOR_DEVICE_ID_CALGARY \ 53#define PCI_DEVICE_ID_IBM_CALIOC2 0x0308
54 (PCI_VENDOR_ID_IBM | PCI_DEVICE_ID_IBM_CALGARY << 16)
55 54
56/* we need these for register space address calculation */ 55/* we need these for register space address calculation */
57#define START_ADDRESS 0xfe000000 56#define START_ADDRESS 0xfe000000
@@ -193,6 +192,7 @@ static inline unsigned long verify_bit_range(unsigned long* bitmap,
193{ 192{
194 return ~0UL; 193 return ~0UL;
195} 194}
195
196#endif /* CONFIG_IOMMU_DEBUG */ 196#endif /* CONFIG_IOMMU_DEBUG */
197 197
198static inline unsigned int num_dma_pages(unsigned long dma, unsigned int dmalen) 198static inline unsigned int num_dma_pages(unsigned long dma, unsigned int dmalen)
@@ -346,9 +346,20 @@ static void iommu_free(struct iommu_table *tbl, dma_addr_t dma_addr,
346 346
347static inline struct iommu_table *find_iommu_table(struct device *dev) 347static inline struct iommu_table *find_iommu_table(struct device *dev)
348{ 348{
349 struct pci_dev *pdev;
350 struct pci_bus *pbus;
349 struct iommu_table *tbl; 351 struct iommu_table *tbl;
350 352
351 tbl = to_pci_dev(dev)->bus->self->sysdata; 353 pdev = to_pci_dev(dev);
354
355 /* is the device behind a bridge? */
356 if (unlikely(pdev->bus->parent))
357 pbus = pdev->bus->parent;
358 else
359 pbus = pdev->bus;
360
361 tbl = pbus->self->sysdata;
362 BUG_ON(pdev->bus->parent && (tbl->it_busno != pdev->bus->parent->number));
352 363
353 return tbl; 364 return tbl;
354} 365}
@@ -565,6 +576,21 @@ static inline void __iomem* calgary_reg(void __iomem *bar, unsigned long offset)
565 return (void __iomem*)target; 576 return (void __iomem*)target;
566} 577}
567 578
579static inline int is_calioc2(unsigned short device)
580{
581 return (device == PCI_DEVICE_ID_IBM_CALIOC2);
582}
583
584static inline int is_calgary(unsigned short device)
585{
586 return (device == PCI_DEVICE_ID_IBM_CALGARY);
587}
588
589static inline int is_cal_pci_dev(unsigned short device)
590{
591 return (is_calgary(device) || is_calioc2(device));
592}
593
568static void calgary_tce_cache_blast(struct iommu_table *tbl) 594static void calgary_tce_cache_blast(struct iommu_table *tbl)
569{ 595{
570 u64 val; 596 u64 val;
@@ -685,8 +711,14 @@ static void __init calgary_reserve_regions(struct pci_dev *dev)
685 iommu_range_reserve(tbl, bad_dma_address, EMERGENCY_PAGES); 711 iommu_range_reserve(tbl, bad_dma_address, EMERGENCY_PAGES);
686 712
687 /* avoid the BIOS/VGA first 640KB-1MB region */ 713 /* avoid the BIOS/VGA first 640KB-1MB region */
688 start = (640 * 1024); 714 /* for CalIOC2 - avoid the entire first 2MB */
689 npages = ((1024 - 640) * 1024) >> PAGE_SHIFT; 715 if (is_calgary(dev->device)) {
716 start = (640 * 1024);
717 npages = ((1024 - 640) * 1024) >> PAGE_SHIFT;
718 } else { /* calioc2 */
719 start = 0;
720 npages = (2 * 1024 * 1024) >> PAGE_SHIFT;
721 }
690 iommu_range_reserve(tbl, start, npages); 722 iommu_range_reserve(tbl, start, npages);
691 723
692 /* reserve the two PCI peripheral memory regions in IO space */ 724 /* reserve the two PCI peripheral memory regions in IO space */
@@ -721,15 +753,15 @@ static int __init calgary_setup_tar(struct pci_dev *dev, void __iomem *bbar)
721 753
722 /* zero out all TAR bits under sw control */ 754 /* zero out all TAR bits under sw control */
723 val64 &= ~TAR_SW_BITS; 755 val64 &= ~TAR_SW_BITS;
724
725 tbl = dev->sysdata;
726 table_phys = (u64)__pa(tbl->it_base); 756 table_phys = (u64)__pa(tbl->it_base);
757
727 val64 |= table_phys; 758 val64 |= table_phys;
728 759
729 BUG_ON(specified_table_size > TCE_TABLE_SIZE_8M); 760 BUG_ON(specified_table_size > TCE_TABLE_SIZE_8M);
730 val64 |= (u64) specified_table_size; 761 val64 |= (u64) specified_table_size;
731 762
732 tbl->tar_val = cpu_to_be64(val64); 763 tbl->tar_val = cpu_to_be64(val64);
764
733 writeq(tbl->tar_val, target); 765 writeq(tbl->tar_val, target);
734 readq(target); /* flush */ 766 readq(target); /* flush */
735 767
@@ -760,6 +792,43 @@ static void __init calgary_free_bus(struct pci_dev *dev)
760 bus_info[dev->bus->number].tce_space = NULL; 792 bus_info[dev->bus->number].tce_space = NULL;
761} 793}
762 794
795static void calgary_dump_error_regs(struct iommu_table *tbl)
796{
797 void __iomem *bbar = tbl->bbar;
798 u32 csr, csmr, plssr, mck;
799 void __iomem *target;
800 unsigned long phboff = phb_offset(tbl->it_busno);
801 unsigned long erroff;
802 u32 errregs[7];
803 int i;
804
805 /* dump CSR */
806 target = calgary_reg(bbar, phboff | PHB_CSR_OFFSET);
807 csr = be32_to_cpu(readl(target));
808 /* dump PLSSR */
809 target = calgary_reg(bbar, phboff | PHB_PLSSR_OFFSET);
810 plssr = be32_to_cpu(readl(target));
811 /* dump CSMR */
812 target = calgary_reg(bbar, phboff | 0x290);
813 csmr = be32_to_cpu(readl(target));
814 /* dump mck */
815 target = calgary_reg(bbar, phboff | 0x800);
816 mck = be32_to_cpu(readl(target));
817
818 printk(KERN_EMERG "Calgary: 0x%08x@CSR 0x%08x@PLSSR 0x%08x@CSMR "
819 "0x%08x@MCK\n", csr, plssr, csmr, mck);
820
821 /* dump rest of error regs */
822 printk(KERN_EMERG "Calgary: ");
823 for (i = 0; i < ARRAY_SIZE(errregs); i++) {
824 erroff = (0x810 + (i * 0x10)); /* err regs are at 0x810 - 0x870 */
825 target = calgary_reg(bbar, phboff | erroff);
826 errregs[i] = be32_to_cpu(readl(target));
827 printk("0x%08x@0x%lx ", errregs[i], erroff);
828 }
829 printk("\n");
830}
831
763static void calgary_watchdog(unsigned long data) 832static void calgary_watchdog(unsigned long data)
764{ 833{
765 struct pci_dev *dev = (struct pci_dev *)data; 834 struct pci_dev *dev = (struct pci_dev *)data;
@@ -773,13 +842,16 @@ static void calgary_watchdog(unsigned long data)
773 842
774 /* If no error, the agent ID in the CSR is not valid */ 843 /* If no error, the agent ID in the CSR is not valid */
775 if (val32 & CSR_AGENT_MASK) { 844 if (val32 & CSR_AGENT_MASK) {
776 printk(KERN_EMERG "calgary_watchdog: DMA error on PHB %#x, " 845 printk(KERN_EMERG "Calgary: DMA error on PHB %#x\n",
777 "CSR = %#x\n", dev->bus->number, val32); 846 dev->bus->number);
847 calgary_dump_error_regs(tbl);
848
849 /* reset error */
778 writel(0, target); 850 writel(0, target);
779 851
780 /* Disable bus that caused the error */ 852 /* Disable bus that caused the error */
781 target = calgary_reg(bbar, phb_offset(tbl->it_busno) | 853 target = calgary_reg(bbar, phb_offset(tbl->it_busno) |
782 PHB_CONFIG_RW_OFFSET); 854 PHB_CONFIG_RW_OFFSET);
783 val32 = be32_to_cpu(readl(target)); 855 val32 = be32_to_cpu(readl(target));
784 val32 |= PHB_SLOT_DISABLE; 856 val32 |= PHB_SLOT_DISABLE;
785 writel(cpu_to_be32(val32), target); 857 writel(cpu_to_be32(val32), target);
@@ -853,7 +925,9 @@ static void __init calgary_enable_translation(struct pci_dev *dev)
853 val32 = be32_to_cpu(readl(target)); 925 val32 = be32_to_cpu(readl(target));
854 val32 |= PHB_TCE_ENABLE | PHB_DAC_DISABLE | PHB_MCSR_ENABLE; 926 val32 |= PHB_TCE_ENABLE | PHB_DAC_DISABLE | PHB_MCSR_ENABLE;
855 927
856 printk(KERN_INFO "Calgary: enabling translation on PHB %#x\n", busnum); 928 printk(KERN_INFO "Calgary: enabling translation on %s PHB %#x\n",
929 (dev->device == PCI_DEVICE_ID_IBM_CALGARY) ?
930 "Calgary" : "CalIOC2", busnum);
857 printk(KERN_INFO "Calgary: errant DMAs will now be prevented on this " 931 printk(KERN_INFO "Calgary: errant DMAs will now be prevented on this "
858 "bus.\n"); 932 "bus.\n");
859 933
@@ -894,7 +968,12 @@ static void __init calgary_init_one_nontraslated(struct pci_dev *dev)
894{ 968{
895 pci_dev_get(dev); 969 pci_dev_get(dev);
896 dev->sysdata = NULL; 970 dev->sysdata = NULL;
897 dev->bus->self = dev; 971
972 /* is the device behind a bridge? */
973 if (dev->bus->parent)
974 dev->bus->parent->self = dev;
975 else
976 dev->bus->self = dev;
898} 977}
899 978
900static int __init calgary_init_one(struct pci_dev *dev) 979static int __init calgary_init_one(struct pci_dev *dev)
@@ -911,7 +990,14 @@ static int __init calgary_init_one(struct pci_dev *dev)
911 goto done; 990 goto done;
912 991
913 pci_dev_get(dev); 992 pci_dev_get(dev);
914 dev->bus->self = dev; 993
994 if (dev->bus->parent) {
995 if (dev->bus->parent->self)
996 printk(KERN_WARNING "Calgary: IEEEE, dev %p has "
997 "bus->parent->self!\n", dev);
998 dev->bus->parent->self = dev;
999 } else
1000 dev->bus->self = dev;
915 1001
916 tbl = dev->sysdata; 1002 tbl = dev->sysdata;
917 tbl->chip_ops->handle_quirks(tbl, dev); 1003 tbl->chip_ops->handle_quirks(tbl, dev);
@@ -951,11 +1037,18 @@ static int __init calgary_locate_bbars(void)
951 target = calgary_reg(bbar, offset); 1037 target = calgary_reg(bbar, offset);
952 1038
953 val = be32_to_cpu(readl(target)); 1039 val = be32_to_cpu(readl(target));
1040
954 start_bus = (u8)((val & 0x00FF0000) >> 16); 1041 start_bus = (u8)((val & 0x00FF0000) >> 16);
955 end_bus = (u8)((val & 0x0000FF00) >> 8); 1042 end_bus = (u8)((val & 0x0000FF00) >> 8);
956 for (bus = start_bus; bus <= end_bus; bus++) { 1043
957 bus_info[bus].bbar = bbar; 1044 if (end_bus) {
958 bus_info[bus].phbid = phb; 1045 for (bus = start_bus; bus <= end_bus; bus++) {
1046 bus_info[bus].bbar = bbar;
1047 bus_info[bus].phbid = phb;
1048 }
1049 } else {
1050 bus_info[start_bus].bbar = bbar;
1051 bus_info[start_bus].phbid = phb;
959 } 1052 }
960 } 1053 }
961 } 1054 }
@@ -975,24 +1068,27 @@ static int __init calgary_init(void)
975{ 1068{
976 int ret; 1069 int ret;
977 struct pci_dev *dev = NULL; 1070 struct pci_dev *dev = NULL;
1071 void* tce_space;
978 1072
979 ret = calgary_locate_bbars(); 1073 ret = calgary_locate_bbars();
980 if (ret) 1074 if (ret)
981 return ret; 1075 return ret;
982 1076
983 do { 1077 do {
984 dev = pci_get_device(PCI_VENDOR_ID_IBM, 1078 dev = pci_get_device(PCI_VENDOR_ID_IBM, PCI_ANY_ID, dev);
985 PCI_DEVICE_ID_IBM_CALGARY,
986 dev);
987 if (!dev) 1079 if (!dev)
988 break; 1080 break;
1081 if (!is_cal_pci_dev(dev->device))
1082 continue;
989 if (!translate_phb(dev)) { 1083 if (!translate_phb(dev)) {
990 calgary_init_one_nontraslated(dev); 1084 calgary_init_one_nontraslated(dev);
991 continue; 1085 continue;
992 } 1086 }
993 if (!bus_info[dev->bus->number].tce_space && !translate_empty_slots) 1087 tce_space = bus_info[dev->bus->number].tce_space;
1088 if (!tce_space && !translate_empty_slots) {
1089 printk("Calg: %p failed tce_space check\n", dev);
994 continue; 1090 continue;
995 1091 }
996 ret = calgary_init_one(dev); 1092 ret = calgary_init_one(dev);
997 if (ret) 1093 if (ret)
998 goto error; 1094 goto error;
@@ -1003,10 +1099,11 @@ static int __init calgary_init(void)
1003error: 1099error:
1004 do { 1100 do {
1005 dev = pci_get_device_reverse(PCI_VENDOR_ID_IBM, 1101 dev = pci_get_device_reverse(PCI_VENDOR_ID_IBM,
1006 PCI_DEVICE_ID_IBM_CALGARY, 1102 PCI_ANY_ID, dev);
1007 dev);
1008 if (!dev) 1103 if (!dev)
1009 break; 1104 break;
1105 if (!is_cal_pci_dev(dev->device))
1106 continue;
1010 if (!translate_phb(dev)) { 1107 if (!translate_phb(dev)) {
1011 pci_dev_put(dev); 1108 pci_dev_put(dev);
1012 continue; 1109 continue;
@@ -1084,9 +1181,29 @@ static int __init build_detail_arrays(void)
1084 return 0; 1181 return 0;
1085} 1182}
1086 1183
1087void __init detect_calgary(void) 1184static int __init calgary_bus_has_devices(int bus, unsigned short pci_dev)
1088{ 1185{
1186 int dev;
1089 u32 val; 1187 u32 val;
1188
1189 if (pci_dev == PCI_DEVICE_ID_IBM_CALIOC2) {
1190 /*
1191 * FIXME: properly scan for devices accross the
1192 * PCI-to-PCI bridge on every CalIOC2 port.
1193 */
1194 return 1;
1195 }
1196
1197 for (dev = 1; dev < 8; dev++) {
1198 val = read_pci_config(bus, dev, 0, 0);
1199 if (val != 0xffffffff)
1200 break;
1201 }
1202 return (val != 0xffffffff);
1203}
1204
1205void __init detect_calgary(void)
1206{
1090 int bus; 1207 int bus;
1091 void *tbl; 1208 void *tbl;
1092 int calgary_found = 0; 1209 int calgary_found = 0;
@@ -1143,29 +1260,28 @@ void __init detect_calgary(void)
1143 specified_table_size = determine_tce_table_size(end_pfn * PAGE_SIZE); 1260 specified_table_size = determine_tce_table_size(end_pfn * PAGE_SIZE);
1144 1261
1145 for (bus = 0; bus < MAX_PHB_BUS_NUM; bus++) { 1262 for (bus = 0; bus < MAX_PHB_BUS_NUM; bus++) {
1146 int dev;
1147 struct calgary_bus_info *info = &bus_info[bus]; 1263 struct calgary_bus_info *info = &bus_info[bus];
1264 unsigned short pci_device;
1265 u32 val;
1266
1267 val = read_pci_config(bus, 0, 0, 0);
1268 pci_device = (val & 0xFFFF0000) >> 16;
1148 1269
1149 if (read_pci_config(bus, 0, 0, 0) != PCI_VENDOR_DEVICE_ID_CALGARY) 1270 if (!is_cal_pci_dev(pci_device))
1150 continue; 1271 continue;
1151 1272
1152 if (info->translation_disabled) 1273 if (info->translation_disabled)
1153 continue; 1274 continue;
1154 1275
1155 /* 1276 if (calgary_bus_has_devices(bus, pci_device) ||
1156 * Scan the slots of the PCI bus to see if there is a device present. 1277 translate_empty_slots) {
1157 * The parent bus will be the zero-ith device, so start at 1. 1278 tbl = alloc_tce_table();
1158 */ 1279 if (!tbl)
1159 for (dev = 1; dev < 8; dev++) { 1280 goto cleanup;
1160 val = read_pci_config(bus, dev, 0, 0); 1281 info->tce_space = tbl;
1161 if (val != 0xffffffff || translate_empty_slots) { 1282 calgary_found = 1;
1162 tbl = alloc_tce_table(); 1283 printk("Calg: allocated tce_table %p for bus 0x%x\n",
1163 if (!tbl) 1284 info->tce_space, bus);
1164 goto cleanup;
1165 info->tce_space = tbl;
1166 calgary_found = 1;
1167 break;
1168 }
1169 } 1285 }
1170 } 1286 }
1171 1287