⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pci-calgary_64.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
	free_pages((unsigned long)ret, get_order(size));	ret = NULL;error:	return ret;}static const struct dma_mapping_ops calgary_dma_ops = {	.alloc_coherent = calgary_alloc_coherent,	.map_single = calgary_map_single,	.unmap_single = calgary_unmap_single,	.map_sg = calgary_map_sg,	.unmap_sg = calgary_unmap_sg,};static inline void __iomem * busno_to_bbar(unsigned char num){	return bus_info[num].bbar;}static inline int busno_to_phbid(unsigned char num){	return bus_info[num].phbid;}static inline unsigned long split_queue_offset(unsigned char num){	size_t idx = busno_to_phbid(num);	return split_queue_offsets[idx];}static inline unsigned long tar_offset(unsigned char num){	size_t idx = busno_to_phbid(num);	return tar_offsets[idx];}static inline unsigned long phb_offset(unsigned char num){	size_t idx = busno_to_phbid(num);	return phb_offsets[idx];}static inline void __iomem* calgary_reg(void __iomem *bar, unsigned long offset){	unsigned long target = ((unsigned long)bar) | offset;	return (void __iomem*)target;}static inline int is_calioc2(unsigned short device){	return (device == PCI_DEVICE_ID_IBM_CALIOC2);}static inline int is_calgary(unsigned short device){	return (device == PCI_DEVICE_ID_IBM_CALGARY);}static inline int is_cal_pci_dev(unsigned short device){	return (is_calgary(device) || is_calioc2(device));}static void calgary_tce_cache_blast(struct iommu_table *tbl){	u64 val;	u32 aer;	int i = 0;	void __iomem *bbar = tbl->bbar;	void __iomem *target;	/* disable arbitration on the bus */	target = calgary_reg(bbar, phb_offset(tbl->it_busno) | PHB_AER_OFFSET);	aer = readl(target);	writel(0, target);	/* read plssr to ensure it got there */	target = calgary_reg(bbar, phb_offset(tbl->it_busno) | PHB_PLSSR_OFFSET);	val = readl(target);	/* poll split queues until all DMA activity is done */	target = calgary_reg(bbar, split_queue_offset(tbl->it_busno));	do {		val = readq(target);		i++;	} while ((val & 0xff) != 0xff && i < 100);	if (i == 100)		printk(KERN_WARNING "Calgary: PCI bus not quiesced, "		       "continuing anyway\n");	/* invalidate TCE cache */	target = calgary_reg(bbar, tar_offset(tbl->it_busno));	writeq(tbl->tar_val, target);	/* enable arbitration */	target = calgary_reg(bbar, phb_offset(tbl->it_busno) | PHB_AER_OFFSET);	writel(aer, target);	(void)readl(target); /* flush */}static void calioc2_tce_cache_blast(struct iommu_table *tbl){	void __iomem *bbar = tbl->bbar;	void __iomem *target;	u64 val64;	u32 val;	int i = 0;	int count = 1;	unsigned char bus = tbl->it_busno;begin:	printk(KERN_DEBUG "Calgary: CalIOC2 bus 0x%x entering tce cache blast "	       "sequence - count %d\n", bus, count);	/* 1. using the Page Migration Control reg set SoftStop */	target = calgary_reg(bbar, phb_offset(bus) | PHB_PAGE_MIG_CTRL);	val = be32_to_cpu(readl(target));	printk(KERN_DEBUG "1a. read 0x%x [LE] from %p\n", val, target);	val |= PMR_SOFTSTOP;	printk(KERN_DEBUG "1b. writing 0x%x [LE] to %p\n", val, target);	writel(cpu_to_be32(val), target);	/* 2. poll split queues until all DMA activity is done */	printk(KERN_DEBUG "2a. starting to poll split queues\n");	target = calgary_reg(bbar, split_queue_offset(bus));	do {		val64 = readq(target);		i++;	} while ((val64 & 0xff) != 0xff && i < 100);	if (i == 100)		printk(KERN_WARNING "CalIOC2: PCI bus not quiesced, "		       "continuing anyway\n");	/* 3. poll Page Migration DEBUG for SoftStopFault */	target = calgary_reg(bbar, phb_offset(bus) | PHB_PAGE_MIG_DEBUG);	val = be32_to_cpu(readl(target));	printk(KERN_DEBUG "3. read 0x%x [LE] from %p\n", val, target);	/* 4. if SoftStopFault - goto (1) */	if (val & PMR_SOFTSTOPFAULT) {		if (++count < 100)			goto begin;		else {			printk(KERN_WARNING "CalIOC2: too many SoftStopFaults, "			       "aborting TCE cache flush sequence!\n");			return; /* pray for the best */		}	}	/* 5. Slam into HardStop by reading PHB_PAGE_MIG_CTRL */	target = calgary_reg(bbar, phb_offset(bus) | PHB_PAGE_MIG_CTRL);	printk(KERN_DEBUG "5a. slamming into HardStop by reading %p\n", target);	val = be32_to_cpu(readl(target));	printk(KERN_DEBUG "5b. read 0x%x [LE] from %p\n", val, target);	target = calgary_reg(bbar, phb_offset(bus) | PHB_PAGE_MIG_DEBUG);	val = be32_to_cpu(readl(target));	printk(KERN_DEBUG "5c. read 0x%x [LE] from %p (debug)\n", val, target);	/* 6. invalidate TCE cache */	printk(KERN_DEBUG "6. invalidating TCE cache\n");	target = calgary_reg(bbar, tar_offset(bus));	writeq(tbl->tar_val, target);	/* 7. Re-read PMCR */	printk(KERN_DEBUG "7a. Re-reading PMCR\n");	target = calgary_reg(bbar, phb_offset(bus) | PHB_PAGE_MIG_CTRL);	val = be32_to_cpu(readl(target));	printk(KERN_DEBUG "7b. read 0x%x [LE] from %p\n", val, target);	/* 8. Remove HardStop */	printk(KERN_DEBUG "8a. removing HardStop from PMCR\n");	target = calgary_reg(bbar, phb_offset(bus) | PHB_PAGE_MIG_CTRL);	val = 0;	printk(KERN_DEBUG "8b. writing 0x%x [LE] to %p\n", val, target);	writel(cpu_to_be32(val), target);	val = be32_to_cpu(readl(target));	printk(KERN_DEBUG "8c. read 0x%x [LE] from %p\n", val, target);}static void __init calgary_reserve_mem_region(struct pci_dev *dev, u64 start,	u64 limit){	unsigned int numpages;	limit = limit | 0xfffff;	limit++;	numpages = ((limit - start) >> PAGE_SHIFT);	iommu_range_reserve(pci_iommu(dev->bus), start, numpages);}static void __init calgary_reserve_peripheral_mem_1(struct pci_dev *dev){	void __iomem *target;	u64 low, high, sizelow;	u64 start, limit;	struct iommu_table *tbl = pci_iommu(dev->bus);	unsigned char busnum = dev->bus->number;	void __iomem *bbar = tbl->bbar;	/* peripheral MEM_1 region */	target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_1_LOW);	low = be32_to_cpu(readl(target));	target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_1_HIGH);	high = be32_to_cpu(readl(target));	target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_1_SIZE);	sizelow = be32_to_cpu(readl(target));	start = (high << 32) | low;	limit = sizelow;	calgary_reserve_mem_region(dev, start, limit);}static void __init calgary_reserve_peripheral_mem_2(struct pci_dev *dev){	void __iomem *target;	u32 val32;	u64 low, high, sizelow, sizehigh;	u64 start, limit;	struct iommu_table *tbl = pci_iommu(dev->bus);	unsigned char busnum = dev->bus->number;	void __iomem *bbar = tbl->bbar;	/* is it enabled? */	target = calgary_reg(bbar, phb_offset(busnum) | PHB_CONFIG_RW_OFFSET);	val32 = be32_to_cpu(readl(target));	if (!(val32 & PHB_MEM2_ENABLE))		return;	target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_2_LOW);	low = be32_to_cpu(readl(target));	target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_2_HIGH);	high = be32_to_cpu(readl(target));	target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_2_SIZE_LOW);	sizelow = be32_to_cpu(readl(target));	target = calgary_reg(bbar, phb_offset(busnum) | PHB_MEM_2_SIZE_HIGH);	sizehigh = be32_to_cpu(readl(target));	start = (high << 32) | low;	limit = (sizehigh << 32) | sizelow;	calgary_reserve_mem_region(dev, start, limit);}/* * some regions of the IO address space do not get translated, so we * must not give devices IO addresses in those regions. The regions * are the 640KB-1MB region and the two PCI peripheral memory holes. * Reserve all of them in the IOMMU bitmap to avoid giving them out * later. */static void __init calgary_reserve_regions(struct pci_dev *dev){	unsigned int npages;	u64 start;	struct iommu_table *tbl = pci_iommu(dev->bus);	/* reserve EMERGENCY_PAGES from bad_dma_address and up */	iommu_range_reserve(tbl, bad_dma_address, EMERGENCY_PAGES);	/* avoid the BIOS/VGA first 640KB-1MB region */	/* for CalIOC2 - avoid the entire first MB */	if (is_calgary(dev->device)) {		start = (640 * 1024);		npages = ((1024 - 640) * 1024) >> PAGE_SHIFT;	} else { /* calioc2 */		start = 0;		npages = (1 * 1024 * 1024) >> PAGE_SHIFT;	}	iommu_range_reserve(tbl, start, npages);	/* reserve the two PCI peripheral memory regions in IO space */	calgary_reserve_peripheral_mem_1(dev);	calgary_reserve_peripheral_mem_2(dev);}static int __init calgary_setup_tar(struct pci_dev *dev, void __iomem *bbar){	u64 val64;	u64 table_phys;	void __iomem *target;	int ret;	struct iommu_table *tbl;	/* build TCE tables for each PHB */	ret = build_tce_table(dev, bbar);	if (ret)		return ret;	tbl = pci_iommu(dev->bus);	tbl->it_base = (unsigned long)bus_info[dev->bus->number].tce_space;	tce_free(tbl, 0, tbl->it_size);	if (is_calgary(dev->device))		tbl->chip_ops = &calgary_chip_ops;	else if (is_calioc2(dev->device))		tbl->chip_ops = &calioc2_chip_ops;	else		BUG();	calgary_reserve_regions(dev);	/* set TARs for each PHB */	target = calgary_reg(bbar, tar_offset(dev->bus->number));	val64 = be64_to_cpu(readq(target));	/* zero out all TAR bits under sw control */	val64 &= ~TAR_SW_BITS;	table_phys = (u64)__pa(tbl->it_base);	val64 |= table_phys;	BUG_ON(specified_table_size > TCE_TABLE_SIZE_8M);	val64 |= (u64) specified_table_size;	tbl->tar_val = cpu_to_be64(val64);	writeq(tbl->tar_val, target);	readq(target); /* flush */	return 0;}static void __init calgary_free_bus(struct pci_dev *dev){	u64 val64;	struct iommu_table *tbl = pci_iommu(dev->bus);	void __iomem *target;	unsigned int bitmapsz;	target = calgary_reg(tbl->bbar, tar_offset(dev->bus->number));	val64 = be64_to_cpu(readq(target));	val64 &= ~TAR_SW_BITS;	writeq(cpu_to_be64(val64), target);	readq(target); /* flush */	bitmapsz = tbl->it_size / BITS_PER_BYTE;	free_pages((unsigned long)tbl->it_map, get_order(bitmapsz));	tbl->it_map = NULL;	kfree(tbl);		set_pci_iommu(dev->bus, NULL);	/* Can't free bootmem allocated memory after system is up :-( */	bus_info[dev->bus->number].tce_space = NULL;}static void calgary_dump_error_regs(struct iommu_table *tbl){	void __iomem *bbar = tbl->bbar;	void __iomem *target;	u32 csr, plssr;	target = calgary_reg(bbar, phb_offset(tbl->it_busno) | PHB_CSR_OFFSET);	csr = be32_to_cpu(readl(target));	target = calgary_reg(bbar, phb_offset(tbl->it_busno) | PHB_PLSSR_OFFSET);	plssr = be32_to_cpu(readl(target));	/* If no error, the agent ID in the CSR is not valid */	printk(KERN_EMERG "Calgary: DMA error on Calgary PHB 0x%x, "	       "0x%08x@CSR 0x%08x@PLSSR\n", tbl->it_busno, csr, plssr);}static void calioc2_dump_error_regs(struct iommu_table *tbl){	void __iomem *bbar = tbl->bbar;	u32 csr, csmr, plssr, mck, rcstat;	void __iomem *target;	unsigned long phboff = phb_offset(tbl->it_busno);	unsigned long erroff;	u32 errregs[7];	int i;	/* dump CSR */	target = calgary_reg(bbar, phboff | PHB_CSR_OFFSET);	csr = be32_to_cpu(readl(target));	/* dump PLSSR */	target = calgary_reg(bbar, phboff | PHB_PLSSR_OFFSET);	plssr = be32_to_cpu(readl(target));	/* dump CSMR */	target = calgary_reg(bbar, phboff | 0x290);	csmr = be32_to_cpu(readl(target));	/* dump mck */	target = calgary_reg(bbar, phboff | 0x800);	mck = be32_to_cpu(readl(target));	printk(KERN_EMERG "Calgary: DMA error on CalIOC2 PHB 0x%x\n",	       tbl->it_busno);	printk(KERN_EMERG "Calgary: 0x%08x@CSR 0x%08x@PLSSR 0x%08x@CSMR 0x%08x@MCK\n",	       csr, plssr, csmr, mck);	/* dump rest of error regs */	printk(KERN_EMERG "Calgary: ");	for (i = 0; i < ARRAY_SIZE(errregs); i++) {		/* err regs are at 0x810 - 0x870 */		erroff = (0x810 + (i * 0x10));		target = calgary_reg(bbar, phboff | erroff);		errregs[i] = be32_to_cpu(readl(target));		printk("0x%08x@0x%lx ", errregs[i], erroff);	}	printk("\n");	/* root complex status */	target = calgary_reg(bbar, phboff | PHB_ROOT_COMPLEX_STATUS);	rcstat = be32_to_cpu(readl(target));	printk(KERN_EMERG "Calgary: 0x%08x@0x%x\n", rcstat,	       PHB_ROOT_COMPLEX_STATUS);}static void calgary_watchdog(unsigned long data){	struct pci_dev *dev = (struct pci_dev *)data;	struct iommu_table *tbl = pci_iommu(dev->bus);	void __iomem *bbar = tbl->bbar;	u32 val32;	void __iomem *target;	target = calgary_reg(bbar, phb_offset(tbl->it_busno) | PHB_CSR_OFFSET);	val32 = be32_to_cpu(readl(target));	/* If no error, the agent ID in the CSR is not valid */	if (val32 & CSR_AGENT_MASK) {		tbl->chip_ops->dump_error_regs(tbl);		/* reset error */		writel(0, target);		/* Disable bus that caused the error */		target = calgary_reg(bbar, phb_offset(tbl->it_busno) |				     PHB_CONFIG_RW_OFFSET);		val32 = be32_to_cpu(readl(target));		val32 |= PHB_SLOT_DISABLE;		writel(cpu_to_be32(val32), target);		readl(target); /* flush */	} else {		/* Reset the timer */		mod_timer(&tbl->watchdog_timer, jiffies + 2 * HZ);	}}static void __init calgary_set_split_completion_timeout(void __iomem *bbar,	unsigned char busnum, unsigned long timeout){	u64 val64;	void __iomem *target;	unsigned int phb_shift = ~0; /* silence gcc */	u64 mask;	switch (busno_to_phbid(busnum)) {	case 0: phb_shift = (63 - 19);		break;	case 1: phb_shift = (63 - 23);		break;	case 2: phb_shift = (63 - 27);		break;	case 3: phb_shift = (63 - 35);		break;	default:		BUG_ON(busno_to_phbid(busnum));	}	target = calgary_reg(bbar, CALGARY_CONFIG_REG);	val64 = be64_to_cpu(readq(target));	/* zero out this PHB's timer bits */	mask = ~(0xFUL << phb_shift);	val64 &= mask;	val64 |= (timeout << phb_shift);	writeq(cpu_to_be64(val64), target);	readq(target); /* flush */}static void calioc2_handle_quirks(struct iommu_table *tbl, struct pci_dev *dev){	unsigned char busnum = dev->bus->number;	void __iomem *bbar = tbl->bbar;	void __iomem *target;	u32 val;	/*	 * CalIOC2 designers recommend setting bit 8 in 0xnDB0 to 1	 */	target = calgary_reg(bbar, phb_offset(busnum) | PHB_SAVIOR_L2);	val = cpu_to_be32(readl(target));	val |= 0x00800000;	writel(cpu_to_be32(val), target);}static void calgary_handle_quirks(struct iommu_table *tbl, struct pci_dev *dev){	unsigned char busnum = dev->bus->number;	/*	 * Give split completion a longer timeout on bus 1 for aic94xx	 * http://bugzilla.kernel.org/show_bug.cgi?id=7180	 */	if (is_calgary(dev->device) && (busnum == 1))		calgary_set_split_completion_timeout(tbl->bbar, busnum,						     CCR_2SEC_TIMEOUT);}static void __init calgary_enable_translation(struct pci_dev *dev){	u32 val32;	unsigned char busnum;	void __iomem *target;	void __iomem *bbar;	struct iommu_table *tbl;	busnum = dev->bus->number;	tbl = pci_iommu(dev->bus);	bbar = tbl->bbar;	/* enable TCE in PHB Config Register */	target = calgary_reg(bbar, phb_offset(busnum) | PHB_CONFIG_RW_OFFSET);	val32 = be32_to_cpu(readl(target));	val32 |= PHB_TCE_ENABLE | PHB_DAC_DISABLE | PHB_MCSR_ENABLE;	printk(KERN_INFO "Calgary: enabling translation on %s PHB %#x\n",	       (dev->device == PCI_DEVICE_ID_IBM_CALGARY) ?	       "Calgary" : "CalIOC2", busnum);	printk(KERN_INFO "Calgary: errant DMAs will now be prevented on this "	       "bus.\n");

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -