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

📄 pci-calgary_64.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
	writel(cpu_to_be32(val32), target);	readl(target); /* flush */	init_timer(&tbl->watchdog_timer);	tbl->watchdog_timer.function = &calgary_watchdog;	tbl->watchdog_timer.data = (unsigned long)dev;	mod_timer(&tbl->watchdog_timer, jiffies);}static void __init calgary_disable_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;	/* disable 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: disabling translation on PHB %#x!\n", busnum);	writel(cpu_to_be32(val32), target);	readl(target); /* flush */	del_timer_sync(&tbl->watchdog_timer);}static void __init calgary_init_one_nontraslated(struct pci_dev *dev){	pci_dev_get(dev);	set_pci_iommu(dev->bus, NULL);	/* is the device behind a bridge? */	if (dev->bus->parent)		dev->bus->parent->self = dev;	else		dev->bus->self = dev;}static int __init calgary_init_one(struct pci_dev *dev){	void __iomem *bbar;	struct iommu_table *tbl;	int ret;	BUG_ON(dev->bus->number >= MAX_PHB_BUS_NUM);	bbar = busno_to_bbar(dev->bus->number);	ret = calgary_setup_tar(dev, bbar);	if (ret)		goto done;	pci_dev_get(dev);	if (dev->bus->parent) {		if (dev->bus->parent->self)			printk(KERN_WARNING "Calgary: IEEEE, dev %p has "			       "bus->parent->self!\n", dev);		dev->bus->parent->self = dev;	} else		dev->bus->self = dev;	tbl = pci_iommu(dev->bus);	tbl->chip_ops->handle_quirks(tbl, dev);	calgary_enable_translation(dev);	return 0;done:	return ret;}static int __init calgary_locate_bbars(void){	int ret;	int rioidx, phb, bus;	void __iomem *bbar;	void __iomem *target;	unsigned long offset;	u8 start_bus, end_bus;	u32 val;	ret = -ENODATA;	for (rioidx = 0; rioidx < rio_table_hdr->num_rio_dev; rioidx++) {		struct rio_detail *rio = rio_devs[rioidx];		if ((rio->type != COMPAT_CALGARY) && (rio->type != ALT_CALGARY))			continue;		/* map entire 1MB of Calgary config space */		bbar = ioremap_nocache(rio->BBAR, 1024 * 1024);		if (!bbar)			goto error;		for (phb = 0; phb < PHBS_PER_CALGARY; phb++) {			offset = phb_debug_offsets[phb] | PHB_DEBUG_STUFF_OFFSET;			target = calgary_reg(bbar, offset);			val = be32_to_cpu(readl(target));			start_bus = (u8)((val & 0x00FF0000) >> 16);			end_bus = (u8)((val & 0x0000FF00) >> 8);			if (end_bus) {				for (bus = start_bus; bus <= end_bus; bus++) {					bus_info[bus].bbar = bbar;					bus_info[bus].phbid = phb;				}			} else {				bus_info[start_bus].bbar = bbar;				bus_info[start_bus].phbid = phb;			}		}	}	return 0;error:	/* scan bus_info and iounmap any bbars we previously ioremap'd */	for (bus = 0; bus < ARRAY_SIZE(bus_info); bus++)		if (bus_info[bus].bbar)			iounmap(bus_info[bus].bbar);	return ret;}static int __init calgary_init(void){	int ret;	struct pci_dev *dev = NULL;	struct calgary_bus_info *info;	ret = calgary_locate_bbars();	if (ret)		return ret;	do {		dev = pci_get_device(PCI_VENDOR_ID_IBM, PCI_ANY_ID, dev);		if (!dev)			break;		if (!is_cal_pci_dev(dev->device))			continue;		info = &bus_info[dev->bus->number];		if (info->translation_disabled) {			calgary_init_one_nontraslated(dev);			continue;		}		if (!info->tce_space && !translate_empty_slots)			continue;		ret = calgary_init_one(dev);		if (ret)			goto error;	} while (1);	return ret;error:	do {		dev = pci_get_device_reverse(PCI_VENDOR_ID_IBM,					     PCI_ANY_ID, dev);		if (!dev)			break;		if (!is_cal_pci_dev(dev->device))			continue;		info = &bus_info[dev->bus->number];		if (info->translation_disabled) {			pci_dev_put(dev);			continue;		}		if (!info->tce_space && !translate_empty_slots)			continue;		calgary_disable_translation(dev);		calgary_free_bus(dev);		pci_dev_put(dev); /* Undo calgary_init_one()'s pci_dev_get() */	} while (1);	return ret;}static inline int __init determine_tce_table_size(u64 ram){	int ret;	if (specified_table_size != TCE_TABLE_SIZE_UNSPECIFIED)		return specified_table_size;	/*	 * Table sizes are from 0 to 7 (TCE_TABLE_SIZE_64K to	 * TCE_TABLE_SIZE_8M). Table size 0 has 8K entries and each	 * larger table size has twice as many entries, so shift the	 * max ram address by 13 to divide by 8K and then look at the	 * order of the result to choose between 0-7.	 */	ret = get_order(ram >> 13);	if (ret > TCE_TABLE_SIZE_8M)		ret = TCE_TABLE_SIZE_8M;	return ret;}static int __init build_detail_arrays(void){	unsigned long ptr;	int i, scal_detail_size, rio_detail_size;	if (rio_table_hdr->num_scal_dev > MAX_NUMNODES){		printk(KERN_WARNING			"Calgary: MAX_NUMNODES too low! Defined as %d, "			"but system has %d nodes.\n",			MAX_NUMNODES, rio_table_hdr->num_scal_dev);		return -ENODEV;	}	switch (rio_table_hdr->version){	case 2:		scal_detail_size = 11;		rio_detail_size = 13;		break;	case 3:		scal_detail_size = 12;		rio_detail_size = 15;		break;	default:		printk(KERN_WARNING		       "Calgary: Invalid Rio Grande Table Version: %d\n",		       rio_table_hdr->version);		return -EPROTO;	}	ptr = ((unsigned long)rio_table_hdr) + 3;	for (i = 0; i < rio_table_hdr->num_scal_dev;		    i++, ptr += scal_detail_size)		scal_devs[i] = (struct scal_detail *)ptr;	for (i = 0; i < rio_table_hdr->num_rio_dev;		    i++, ptr += rio_detail_size)		rio_devs[i] = (struct rio_detail *)ptr;	return 0;}static int __init calgary_bus_has_devices(int bus, unsigned short pci_dev){	int dev;	u32 val;	if (pci_dev == PCI_DEVICE_ID_IBM_CALIOC2) {		/*		 * FIXME: properly scan for devices accross the		 * PCI-to-PCI bridge on every CalIOC2 port.		 */		return 1;	}	for (dev = 1; dev < 8; dev++) {		val = read_pci_config(bus, dev, 0, 0);		if (val != 0xffffffff)			break;	}	return (val != 0xffffffff);}void __init detect_calgary(void){	int bus;	void *tbl;	int calgary_found = 0;	unsigned long ptr;	unsigned int offset, prev_offset;	int ret;	/*	 * if the user specified iommu=off or iommu=soft or we found	 * another HW IOMMU already, bail out.	 */	if (swiotlb || no_iommu || iommu_detected)		return;	if (!use_calgary)		return;	if (!early_pci_allowed())		return;	printk(KERN_DEBUG "Calgary: detecting Calgary via BIOS EBDA area\n");	ptr = (unsigned long)phys_to_virt(get_bios_ebda());	rio_table_hdr = NULL;	prev_offset = 0;	offset = 0x180;	/*	 * The next offset is stored in the 1st word.	 * Only parse up until the offset increases:	 */	while (offset > prev_offset) {		/* The block id is stored in the 2nd word */		if (*((unsigned short *)(ptr + offset + 2)) == 0x4752){			/* set the pointer past the offset & block id */			rio_table_hdr = (struct rio_table_hdr *)(ptr + offset + 4);			break;		}		prev_offset = offset;		offset = *((unsigned short *)(ptr + offset));	}	if (!rio_table_hdr) {		printk(KERN_DEBUG "Calgary: Unable to locate Rio Grande table "		       "in EBDA - bailing!\n");		return;	}	ret = build_detail_arrays();	if (ret) {		printk(KERN_DEBUG "Calgary: build_detail_arrays ret %d\n", ret);		return;	}	specified_table_size = determine_tce_table_size(end_pfn * PAGE_SIZE);	for (bus = 0; bus < MAX_PHB_BUS_NUM; bus++) {		struct calgary_bus_info *info = &bus_info[bus];		unsigned short pci_device;		u32 val;		val = read_pci_config(bus, 0, 0, 0);		pci_device = (val & 0xFFFF0000) >> 16;		if (!is_cal_pci_dev(pci_device))			continue;		if (info->translation_disabled)			continue;		if (calgary_bus_has_devices(bus, pci_device) ||		    translate_empty_slots) {			tbl = alloc_tce_table();			if (!tbl)				goto cleanup;			info->tce_space = tbl;			calgary_found = 1;		}	}	printk(KERN_DEBUG "Calgary: finished detection, Calgary %s\n",	       calgary_found ? "found" : "not found");	if (calgary_found) {		iommu_detected = 1;		calgary_detected = 1;		printk(KERN_INFO "PCI-DMA: Calgary IOMMU detected.\n");		printk(KERN_INFO "PCI-DMA: Calgary TCE table spec is %d, "		       "CONFIG_IOMMU_DEBUG is %s.\n", specified_table_size,		       debugging ? "enabled" : "disabled");	}	return;cleanup:	for (--bus; bus >= 0; --bus) {		struct calgary_bus_info *info = &bus_info[bus];		if (info->tce_space)			free_tce_table(info->tce_space);	}}int __init calgary_iommu_init(void){	int ret;	if (no_iommu || swiotlb)		return -ENODEV;	if (!calgary_detected)		return -ENODEV;	/* ok, we're trying to use Calgary - let's roll */	printk(KERN_INFO "PCI-DMA: Using Calgary IOMMU\n");	ret = calgary_init();	if (ret) {		printk(KERN_ERR "PCI-DMA: Calgary init failed %d, "		       "falling back to no_iommu\n", ret);		if (end_pfn > MAX_DMA32_PFN)			printk(KERN_ERR "WARNING more than 4GB of memory, "					"32bit PCI may malfunction.\n");		return ret;	}	force_iommu = 1;	bad_dma_address = 0x0;	dma_ops = &calgary_dma_ops;	return 0;}static int __init calgary_parse_options(char *p){	unsigned int bridge;	size_t len;	char* endp;	while (*p) {		if (!strncmp(p, "64k", 3))			specified_table_size = TCE_TABLE_SIZE_64K;		else if (!strncmp(p, "128k", 4))			specified_table_size = TCE_TABLE_SIZE_128K;		else if (!strncmp(p, "256k", 4))			specified_table_size = TCE_TABLE_SIZE_256K;		else if (!strncmp(p, "512k", 4))			specified_table_size = TCE_TABLE_SIZE_512K;		else if (!strncmp(p, "1M", 2))			specified_table_size = TCE_TABLE_SIZE_1M;		else if (!strncmp(p, "2M", 2))			specified_table_size = TCE_TABLE_SIZE_2M;		else if (!strncmp(p, "4M", 2))			specified_table_size = TCE_TABLE_SIZE_4M;		else if (!strncmp(p, "8M", 2))			specified_table_size = TCE_TABLE_SIZE_8M;		len = strlen("translate_empty_slots");		if (!strncmp(p, "translate_empty_slots", len))			translate_empty_slots = 1;		len = strlen("disable");		if (!strncmp(p, "disable", len)) {			p += len;			if (*p == '=')				++p;			if (*p == '\0')				break;			bridge = simple_strtol(p, &endp, 0);			if (p == endp)				break;			if (bridge < MAX_PHB_BUS_NUM) {				printk(KERN_INFO "Calgary: disabling "				       "translation for PHB %#x\n", bridge);				bus_info[bridge].translation_disabled = 1;			}		}		p = strpbrk(p, ",");		if (!p)			break;		p++; /* skip ',' */	}	return 1;}__setup("calgary=", calgary_parse_options);static void __init calgary_fixup_one_tce_space(struct pci_dev *dev){	struct iommu_table *tbl;	unsigned int npages;	int i;	tbl = pci_iommu(dev->bus);	for (i = 0; i < 4; i++) {		struct resource *r = &dev->resource[PCI_BRIDGE_RESOURCES + i];		/* Don't give out TCEs that map MEM resources */		if (!(r->flags & IORESOURCE_MEM))			continue;		/* 0-based? we reserve the whole 1st MB anyway */		if (!r->start)			continue;		/* cover the whole region */		npages = (r->end - r->start) >> PAGE_SHIFT;		npages++;		iommu_range_reserve(tbl, r->start, npages);	}}static int __init calgary_fixup_tce_spaces(void){	struct pci_dev *dev = NULL;	struct calgary_bus_info *info;	if (no_iommu || swiotlb || !calgary_detected)		return -ENODEV;	printk(KERN_DEBUG "Calgary: fixing up tce spaces\n");	do {		dev = pci_get_device(PCI_VENDOR_ID_IBM, PCI_ANY_ID, dev);		if (!dev)			break;		if (!is_cal_pci_dev(dev->device))			continue;		info = &bus_info[dev->bus->number];		if (info->translation_disabled)			continue;		if (!info->tce_space)			continue;		calgary_fixup_one_tce_space(dev);	} while (1);	return 0;}/* * We need to be call after pcibios_assign_resources (fs_initcall level) * and before device_initcall. */rootfs_initcall(calgary_fixup_tce_spaces);

⌨️ 快捷键说明

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