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

📄 sba_iommu.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
	.alloc_noncoherent =	sba_alloc_consistent,	.free_consistent =	sba_free_consistent,	.map_single =		sba_map_single,	.unmap_single =		sba_unmap_single,	.map_sg =		sba_map_sg,	.unmap_sg =		sba_unmap_sg,	.dma_sync_single_for_cpu =	NULL,	.dma_sync_single_for_device =	NULL,	.dma_sync_sg_for_cpu =		NULL,	.dma_sync_sg_for_device =	NULL,};/******************************************************************************   SBA PAT PDC support****   o call pdc_pat_cell_module()**   o store ranges in PCI "resource" structures****************************************************************************/static voidsba_get_pat_resources(struct sba_device *sba_dev){#if 0/*** TODO/REVISIT/FIXME: support for directed ranges requires calls to**      PAT PDC to program the SBA/LBA directed range registers...this**      burden may fall on the LBA code since it directly supports the**      PCI subsystem. It's not clear yet. - ggg*/PAT_MOD(mod)->mod_info.mod_pages   = PAT_GET_MOD_PAGES(temp);	FIXME : ???PAT_MOD(mod)->mod_info.dvi         = PAT_GET_DVI(temp);	Tells where the dvi bits are located in the address.PAT_MOD(mod)->mod_info.ioc         = PAT_GET_IOC(temp);	FIXME : ???#endif}/****************************************************************   Initialization and claim****************************************************************/#define PIRANHA_ADDR_MASK	0x00160000UL /* bit 17,18,20 */#define PIRANHA_ADDR_VAL	0x00060000UL /* bit 17,18 on */static void *sba_alloc_pdir(unsigned int pdir_size){        unsigned long pdir_base;	unsigned long pdir_order = get_order(pdir_size);	pdir_base = __get_free_pages(GFP_KERNEL, pdir_order);	if (NULL == (void *) pdir_base)	{		panic("%s() could not allocate I/O Page Table\n",			__FUNCTION__);	}	/* If this is not PA8700 (PCX-W2)	**	OR newer than ver 2.2	**	OR in a system that doesn't need VINDEX bits from SBA,	**	** then we aren't exposed to the HW bug.	*/	if ( ((boot_cpu_data.pdc.cpuid >> 5) & 0x7f) != 0x13			|| (boot_cpu_data.pdc.versions > 0x202)			|| (boot_cpu_data.pdc.capabilities & 0x08L) )		return (void *) pdir_base;	/*	 * PA8700 (PCX-W2, aka piranha) silent data corruption fix	 *	 * An interaction between PA8700 CPU (Ver 2.2 or older) and	 * Ike/Astro can cause silent data corruption. This is only	 * a problem if the I/O PDIR is located in memory such that	 * (little-endian)  bits 17 and 18 are on and bit 20 is off.	 *	 * Since the max IO Pdir size is 2MB, by cleverly allocating the	 * right physical address, we can either avoid (IOPDIR <= 1MB)	 * or minimize (2MB IO Pdir) the problem if we restrict the	 * IO Pdir to a maximum size of 2MB-128K (1902K).	 *	 * Because we always allocate 2^N sized IO pdirs, either of the	 * "bad" regions will be the last 128K if at all. That's easy	 * to test for.	 * 	 */	if (pdir_order <= (19-12)) {		if (((virt_to_phys(pdir_base)+pdir_size-1) & PIRANHA_ADDR_MASK) == PIRANHA_ADDR_VAL) {			/* allocate a new one on 512k alignment */			unsigned long new_pdir = __get_free_pages(GFP_KERNEL, (19-12));			/* release original */			free_pages(pdir_base, pdir_order);			pdir_base = new_pdir;			/* release excess */			while (pdir_order < (19-12)) {				new_pdir += pdir_size;				free_pages(new_pdir, pdir_order);				pdir_order +=1;				pdir_size <<=1;			}		}	} else {		/*		** 1MB or 2MB Pdir		** Needs to be aligned on an "odd" 1MB boundary.		*/		unsigned long new_pdir = __get_free_pages(GFP_KERNEL, pdir_order+1); /* 2 or 4MB */		/* release original */		free_pages( pdir_base, pdir_order);		/* release first 1MB */		free_pages(new_pdir, 20-12);		pdir_base = new_pdir + 1024*1024;		if (pdir_order > (20-12)) {			/*			** 2MB Pdir.			**			** Flag tells init_bitmap() to mark bad 128k as used			** and to reduce the size by 128k.			*/			piranha_bad_128k = 1;			new_pdir += 3*1024*1024;			/* release last 1MB */			free_pages(new_pdir, 20-12);			/* release unusable 128KB */			free_pages(new_pdir - 128*1024 , 17-12);			pdir_size -= 128*1024;		}	}	memset((void *) pdir_base, 0, pdir_size);	return (void *) pdir_base;}static struct device *next_device(struct klist_iter *i){        struct klist_node * n = klist_next(i);        return n ? container_of(n, struct device, knode_parent) : NULL;}/* setup Mercury or Elroy IBASE/IMASK registers. */static void setup_ibase_imask(struct parisc_device *sba, struct ioc *ioc, int ioc_num){	/* lba_set_iregs() is in drivers/parisc/lba_pci.c */        extern void lba_set_iregs(struct parisc_device *, u32, u32);	struct device *dev;	struct klist_iter i;	klist_iter_init(&sba->dev.klist_children, &i);	while ((dev = next_device(&i))) {		struct parisc_device *lba = to_parisc_device(dev);		int rope_num = (lba->hpa.start >> 13) & 0xf;		if (rope_num >> 3 == ioc_num)			lba_set_iregs(lba, ioc->ibase, ioc->imask);	}	klist_iter_exit(&i);}static voidsba_ioc_init_pluto(struct parisc_device *sba, struct ioc *ioc, int ioc_num){	u32 iova_space_mask;	u32 iova_space_size;	int iov_order, tcnfg;#ifdef SBA_AGP_SUPPORT	int agp_found = 0;#endif	/*	** Firmware programs the base and size of a "safe IOVA space"	** (one that doesn't overlap memory or LMMIO space) in the	** IBASE and IMASK registers.	*/	ioc->ibase = READ_REG(ioc->ioc_hpa + IOC_IBASE);	iova_space_size = ~(READ_REG(ioc->ioc_hpa + IOC_IMASK) & 0xFFFFFFFFUL) + 1;	if ((ioc->ibase < 0xfed00000UL) && ((ioc->ibase + iova_space_size) > 0xfee00000UL)) {		printk("WARNING: IOV space overlaps local config and interrupt message, truncating\n");		iova_space_size /= 2;	}	/*	** iov_order is always based on a 1GB IOVA space since we want to	** turn on the other half for AGP GART.	*/	iov_order = get_order(iova_space_size >> (IOVP_SHIFT - PAGE_SHIFT));	ioc->pdir_size = (iova_space_size / IOVP_SIZE) * sizeof(u64);	DBG_INIT("%s() hpa 0x%p IOV %dMB (%d bits)\n",		__FUNCTION__, ioc->ioc_hpa, iova_space_size >> 20,		iov_order + PAGE_SHIFT);	ioc->pdir_base = (void *) __get_free_pages(GFP_KERNEL,						   get_order(ioc->pdir_size));	if (!ioc->pdir_base)		panic("Couldn't allocate I/O Page Table\n");	memset(ioc->pdir_base, 0, ioc->pdir_size);	DBG_INIT("%s() pdir %p size %x\n",			__FUNCTION__, ioc->pdir_base, ioc->pdir_size);#ifdef SBA_HINT_SUPPORT	ioc->hint_shift_pdir = iov_order + PAGE_SHIFT;	ioc->hint_mask_pdir = ~(0x3 << (iov_order + PAGE_SHIFT));	DBG_INIT("	hint_shift_pdir %x hint_mask_pdir %lx\n",		ioc->hint_shift_pdir, ioc->hint_mask_pdir);#endif	WARN_ON((((unsigned long) ioc->pdir_base) & PAGE_MASK) != (unsigned long) ioc->pdir_base);	WRITE_REG(virt_to_phys(ioc->pdir_base), ioc->ioc_hpa + IOC_PDIR_BASE);	/* build IMASK for IOC and Elroy */	iova_space_mask =  0xffffffff;	iova_space_mask <<= (iov_order + PAGE_SHIFT);	ioc->imask = iova_space_mask;#ifdef ZX1_SUPPORT	ioc->iovp_mask = ~(iova_space_mask + PAGE_SIZE - 1);#endif	sba_dump_tlb(ioc->ioc_hpa);	setup_ibase_imask(sba, ioc, ioc_num);	WRITE_REG(ioc->imask, ioc->ioc_hpa + IOC_IMASK);#ifdef CONFIG_64BIT	/*	** Setting the upper bits makes checking for bypass addresses	** a little faster later on.	*/	ioc->imask |= 0xFFFFFFFF00000000UL;#endif	/* Set I/O PDIR Page size to system page size */	switch (PAGE_SHIFT) {		case 12: tcnfg = 0; break;	/*  4K */		case 13: tcnfg = 1; break;	/*  8K */		case 14: tcnfg = 2; break;	/* 16K */		case 16: tcnfg = 3; break;	/* 64K */		default:			panic(__FILE__ "Unsupported system page size %d",				1 << PAGE_SHIFT);			break;	}	WRITE_REG(tcnfg, ioc->ioc_hpa + IOC_TCNFG);	/*	** Program the IOC's ibase and enable IOVA translation	** Bit zero == enable bit.	*/	WRITE_REG(ioc->ibase | 1, ioc->ioc_hpa + IOC_IBASE);	/*	** Clear I/O TLB of any possible entries.	** (Yes. This is a bit paranoid...but so what)	*/	WRITE_REG(ioc->ibase | 31, ioc->ioc_hpa + IOC_PCOM);#ifdef SBA_AGP_SUPPORT{	struct klist_iter i;	struct device *dev = NULL;	/*	** If an AGP device is present, only use half of the IOV space	** for PCI DMA.  Unfortunately we can't know ahead of time	** whether GART support will actually be used, for now we	** can just key on any AGP device found in the system.	** We program the next pdir index after we stop w/ a key for	** the GART code to handshake on.	*/	klist_iter_init(&sba->dev.klist_children, &i);	while ((dev = next_device(&i))) {		struct parisc_device *lba = to_parisc_device(dev);		if (IS_QUICKSILVER(lba))			agp_found = 1;	}	klist_iter_exit(&i);	if (agp_found && sba_reserve_agpgart) {		printk(KERN_INFO "%s: reserving %dMb of IOVA space for agpgart\n",		       __FUNCTION__, (iova_space_size/2) >> 20);		ioc->pdir_size /= 2;		ioc->pdir_base[PDIR_INDEX(iova_space_size/2)] = SBA_AGPGART_COOKIE;	}}#endif /*SBA_AGP_SUPPORT*/}static voidsba_ioc_init(struct parisc_device *sba, struct ioc *ioc, int ioc_num){	u32 iova_space_size, iova_space_mask;	unsigned int pdir_size, iov_order;	/*	** Determine IOVA Space size from memory size.	**	** Ideally, PCI drivers would register the maximum number	** of DMA they can have outstanding for each device they	** own.  Next best thing would be to guess how much DMA	** can be outstanding based on PCI Class/sub-class. Both	** methods still require some "extra" to support PCI	** Hot-Plug/Removal of PCI cards. (aka PCI OLARD).	**	** While we have 32-bits "IOVA" space, top two 2 bits are used	** for DMA hints - ergo only 30 bits max.	*/	iova_space_size = (u32) (num_physpages/global_ioc_cnt);	/* limit IOVA space size to 1MB-1GB */	if (iova_space_size < (1 << (20 - PAGE_SHIFT))) {		iova_space_size = 1 << (20 - PAGE_SHIFT);	}	else if (iova_space_size > (1 << (30 - PAGE_SHIFT))) {		iova_space_size = 1 << (30 - PAGE_SHIFT);	}	/*	** iova space must be log2() in size.	** thus, pdir/res_map will also be log2().	** PIRANHA BUG: Exception is when IO Pdir is 2MB (gets reduced)	*/	iov_order = get_order(iova_space_size << PAGE_SHIFT);	/* iova_space_size is now bytes, not pages */	iova_space_size = 1 << (iov_order + PAGE_SHIFT);	ioc->pdir_size = pdir_size = (iova_space_size/IOVP_SIZE) * sizeof(u64);	DBG_INIT("%s() hpa 0x%lx mem %ldMB IOV %dMB (%d bits)\n",			__FUNCTION__,			ioc->ioc_hpa,			(unsigned long) num_physpages >> (20 - PAGE_SHIFT),			iova_space_size>>20,			iov_order + PAGE_SHIFT);	ioc->pdir_base = sba_alloc_pdir(pdir_size);	DBG_INIT("%s() pdir %p size %x\n",			__FUNCTION__, ioc->pdir_base, pdir_size);#ifdef SBA_HINT_SUPPORT	/* FIXME : DMA HINTs not used */	ioc->hint_shift_pdir = iov_order + PAGE_SHIFT;	ioc->hint_mask_pdir = ~(0x3 << (iov_order + PAGE_SHIFT));	DBG_INIT("	hint_shift_pdir %x hint_mask_pdir %lx\n",			ioc->hint_shift_pdir, ioc->hint_mask_pdir);#endif	WRITE_REG64(virt_to_phys(ioc->pdir_base), ioc->ioc_hpa + IOC_PDIR_BASE);	/* build IMASK for IOC and Elroy */	iova_space_mask =  0xffffffff;	iova_space_mask <<= (iov_order + PAGE_SHIFT);	/*	** On C3000 w/512MB mem, HP-UX 10.20 reports:	**     ibase=0, imask=0xFE000000, size=0x2000000.	*/	ioc->ibase = 0;	ioc->imask = iova_space_mask;	/* save it */#ifdef ZX1_SUPPORT	ioc->iovp_mask = ~(iova_space_mask + PAGE_SIZE - 1);#endif	DBG_INIT("%s() IOV base 0x%lx mask 0x%0lx\n",		__FUNCTION__, ioc->ibase, ioc->imask);	/*	** FIXME: Hint registers are programmed with default hint	** values during boot, so hints should be sane even if we	** can't reprogram them the way drivers want.	*/	setup_ibase_imask(sba, ioc, ioc_num);	/*	** Program the IOC's ibase and enable IOVA translation	*/	WRITE_REG(ioc->ibase | 1, ioc->ioc_hpa+IOC_IBASE);	WRITE_REG(ioc->imask, ioc->ioc_hpa+IOC_IMASK);	/* Set I/O PDIR Page size to 4K */	WRITE_REG(0, ioc->ioc_hpa+IOC_TCNFG);	/*	** Clear I/O TLB of any possible entries.	** (Yes. This is a bit paranoid...but so what)	*/	WRITE_REG(0 | 31, ioc->ioc_hpa+IOC_PCOM);	ioc->ibase = 0; /* used by SBA_IOVA and related macros */		DBG_INIT("%s() DONE\n", __FUNCTION__);}/******************************************************************************   SBA initialization code (HW and SW)****   o identify SBA chip itself**   o initialize SBA chip modes (HardFail)**   o initialize SBA chip modes (HardFail)**   o FIXME: initialize DMA hints for reasonable defaults****************************************************************************/static void __iomem *ioc_remap(struct sba_device *sba_dev, unsigned int offset){	return ioremap_nocache(sba_dev->dev->hpa.start + offset, SBA_FUNC_SIZE);}static void sba_hw_init(struct sba_device *sba_dev){ 	int i;	int num_ioc;	u64 ioc_ctl;	if (!is_pdc_pat()) {		/* Shutdown the USB controller on Astro-based workstations.		** Once we reprogram the IOMMU, the next DMA performed by		** USB will HPMC the box. USB is only enabled if a		** keyboard is present and found.		**		** With serial console, j6k v5.0 firmware says:		**   mem_kbd hpa 0xfee003f8 sba 0x0 pad 0x0 cl_class 0x7		**		** FIXME: Using GFX+USB console at power up but direct		**	linux to serial console is still broken.		**	USB could generate DMA so we must reset USB.		**	The proper sequence would be:		**	o block console output		**	o reset USB device		**	o reprogram serial port		**	o unblock console output		*/		if (PAGE0->mem_kbd.cl_class == CL_KEYBD) {			pdc_io_reset_devices();		}	}#if 0printk("sba_hw_init(): mem_boot 0x%x 0x%x 0x%x 0x%x\n", PAGE0->mem_boot.hpa,	PAGE0->mem_boot.spa, PAGE0->mem_boot.pad, PAGE0->mem_boot.cl_class);	/*	** Need to deal with DMA from LAN.	**	Maybe use page zero boot device as a handle to talk	**	to PDC about which device to shutdown.	**	** Netbooting, j6k v5.0 firmware says:	** 	mem_boot hpa 0xf4008000 sba 0x0 pad 0x0 cl_class 0x1002	** ARGH! invalid class.	*/	if ((PAGE0->mem_boot.cl_class != CL_RANDOM)		&& (PAGE0->mem_boot.cl_class != CL_SEQU)) {			pdc_io_reset();	}#endif	if (!IS_PLUTO(sba_dev->dev)) {		ioc_ctl = READ_REG(sba_dev->sba_hpa+IOC_CTRL);		DBG_INIT("%s() hpa 0x%lx ioc_ctl 0x%Lx ->",			__FUNCTION__, sba_dev->sba_hpa, ioc_ctl);		ioc_ctl &= ~(IOC_CTRL_RM | IOC_CTRL_NC | IOC_CTRL_CE);		ioc_ctl |= IOC_CTRL_DD | IOC_CTRL_D4 | IOC_CTRL_TC;			/* j6700 v1.6 firmware sets 0x294f */			/* A500 firmware sets 0x4d */		WRITE_REG(ioc_ctl, sba_dev->sba_hpa+IOC_CTRL);#ifdef DEBUG_SBA_INIT		ioc_ctl = READ_REG64(sba_dev->sba_hpa+IOC_CTRL);		DBG_INIT(" 0x%Lx\n", ioc_ctl);#endif	} /* if !PLUTO */	if (IS_ASTRO(sba_dev->dev)) {		int err;		sba_dev->ioc[0].ioc_hpa = ioc_remap(sba_dev, ASTRO_IOC_OFFSET);		num_ioc = 1;		sba_dev->chip_resv.name = "Astro Intr Ack";		sba_dev->chip_resv.start = PCI_F_EXTEND | 0xfef00000UL;		sba_dev->chip_resv.end   = PCI_F_EXTEND | (0xff000000UL - 1) ;		err = request_resource(&iomem_resource, &(sba_dev->chip_resv));		BUG_ON(err < 0);	} else if (IS_PLUTO(sba_dev->dev)) {		int err;		sba_dev->ioc[0].ioc_hpa = ioc_remap(sba_dev, PLUTO_IOC_OFFSET);		num_ioc = 1;		sba_dev->chip_resv.name = "Pluto Intr/PIOP/VGA";		sba_dev->chip_resv.start = PCI_F_EXTEND | 0xfee00000UL;		sba_dev->chip_resv.end   = PCI_F_EXTEND | (0xff200000UL - 1);

⌨️ 快捷键说明

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