?? ccio-dma.c
字號:
/*** Get the size of the I/O TLB for this I/O MMU.**** If spa_shift is non-zero (ie probably U2),** then calculate the I/O TLB size using spa_shift.**** Otherwise we are supposed to get the IODC entry point ENTRY TLB** and execute it. However, both U2 and Uturn firmware supplies spa_shift.** I think only Java (K/D/R-class too?) systems don't do this.*/static intccio_get_iotlb_size(struct parisc_device *dev){ if (dev->spa_shift == 0) { panic("%s() : Can't determine I/O TLB size.\n", __FUNCTION__); } return (1 << dev->spa_shift);}#else/* Uturn supports 256 TLB entries */#define CCIO_CHAINID_SHIFT 8#define CCIO_CHAINID_MASK 0xff#endif /* 0 *//* We *can't* support JAVA (T600). Venture there at your own risk. */static const struct parisc_device_id ccio_tbl[] = { { HPHW_IOA, HVERSION_REV_ANY_ID, U2_IOA_RUNWAY, 0xb }, /* U2 */ { HPHW_IOA, HVERSION_REV_ANY_ID, UTURN_IOA_RUNWAY, 0xb }, /* UTurn */ { 0, }};static int ccio_probe(struct parisc_device *dev);static struct parisc_driver ccio_driver = { .name = "ccio", .id_table = ccio_tbl, .probe = ccio_probe,};/** * ccio_ioc_init - Initalize the I/O Controller * @ioc: The I/O Controller. * * Initalize the I/O Controller which includes setting up the * I/O Page Directory, the resource map, and initalizing the * U2/Uturn chip into virtual mode. */static voidccio_ioc_init(struct ioc *ioc){ int i; unsigned int iov_order; u32 iova_space_size; /* ** 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). */ iova_space_size = (u32) (num_physpages / count_parisc_driver(&ccio_driver)); /* limit IOVA space size to 1MB-1GB */ if (iova_space_size < (1 << (20 - PAGE_SHIFT))) { iova_space_size = 1 << (20 - PAGE_SHIFT);#ifdef __LP64__ } else if (iova_space_size > (1 << (30 - PAGE_SHIFT))) { iova_space_size = 1 << (30 - PAGE_SHIFT);#endif } /* ** iova space must be log2() in size. ** thus, pdir/res_map will also be log2(). */ /* We could use larger page sizes in order to *decrease* the number ** of mappings needed. (ie 8k pages means 1/2 the mappings). ** ** Note: Grant Grunder says "Using 8k I/O pages isn't trivial either ** since the pages must also be physically contiguous - typically ** this is the case under linux." */ 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 = (iova_space_size / IOVP_SIZE) * sizeof(u64); BUG_ON(ioc->pdir_size > 8 * 1024 * 1024); /* max pdir size <= 8MB */ /* Verify it's a power of two */ BUG_ON((1 << get_order(ioc->pdir_size)) != (ioc->pdir_size >> PAGE_SHIFT)); DBG_INIT("%s() hpa 0x%p mem %luMB IOV %dMB (%d bits)\n", __FUNCTION__, ioc->ioc_regs, (unsigned long) num_physpages >> (20 - PAGE_SHIFT), iova_space_size>>20, iov_order + PAGE_SHIFT); ioc->pdir_base = (u64 *)__get_free_pages(GFP_KERNEL, get_order(ioc->pdir_size)); if(NULL == ioc->pdir_base) { panic("%s() could not allocate I/O Page Table\n", __FUNCTION__); } memset(ioc->pdir_base, 0, ioc->pdir_size); BUG_ON((((unsigned long)ioc->pdir_base) & PAGE_MASK) != (unsigned long)ioc->pdir_base); DBG_INIT(" base %p\n", ioc->pdir_base); /* resource map size dictated by pdir_size */ ioc->res_size = (ioc->pdir_size / sizeof(u64)) >> 3; DBG_INIT("%s() res_size 0x%x\n", __FUNCTION__, ioc->res_size); ioc->res_map = (u8 *)__get_free_pages(GFP_KERNEL, get_order(ioc->res_size)); if(NULL == ioc->res_map) { panic("%s() could not allocate resource map\n", __FUNCTION__); } memset(ioc->res_map, 0, ioc->res_size); /* Initialize the res_hint to 16 */ ioc->res_hint = 16; /* Initialize the spinlock */ spin_lock_init(&ioc->res_lock); /* ** Chainid is the upper most bits of an IOVP used to determine ** which TLB entry an IOVP will use. */ ioc->chainid_shift = get_order(iova_space_size) + PAGE_SHIFT - CCIO_CHAINID_SHIFT; DBG_INIT(" chainid_shift 0x%x\n", ioc->chainid_shift); /* ** Initialize IOA hardware */ WRITE_U32(CCIO_CHAINID_MASK << ioc->chainid_shift, &ioc->ioc_regs->io_chain_id_mask); WRITE_U32(virt_to_phys(ioc->pdir_base), &ioc->ioc_regs->io_pdir_base); /* ** Go to "Virtual Mode" */ WRITE_U32(IOA_NORMAL_MODE, &ioc->ioc_regs->io_control); /* ** Initialize all I/O TLB entries to 0 (Valid bit off). */ WRITE_U32(0, &ioc->ioc_regs->io_tlb_entry_m); WRITE_U32(0, &ioc->ioc_regs->io_tlb_entry_l); for(i = 1 << CCIO_CHAINID_SHIFT; i ; i--) { WRITE_U32((CMD_TLB_DIRECT_WRITE | (i << ioc->chainid_shift)), &ioc->ioc_regs->io_command); }}static void __initccio_init_resource(struct resource *res, char *name, void __iomem *ioaddr){ int result; res->parent = NULL; res->flags = IORESOURCE_MEM; /* * bracing ((signed) ...) are required for 64bit kernel because * we only want to sign extend the lower 16 bits of the register. * The upper 16-bits of range registers are hardcoded to 0xffff. */ res->start = (unsigned long)((signed) READ_U32(ioaddr) << 16); res->end = (unsigned long)((signed) (READ_U32(ioaddr + 4) << 16) - 1); res->name = name; /* * Check if this MMIO range is disable */ if (res->end + 1 == res->start) return; /* On some platforms (e.g. K-Class), we have already registered * resources for devices reported by firmware. Some are children * of ccio. * "insert" ccio ranges in the mmio hierarchy (/proc/iomem). */ result = insert_resource(&iomem_resource, res); if (result < 0) { printk(KERN_ERR "%s() failed to claim CCIO bus address space (%08lx,%08lx)\n", __FUNCTION__, res->start, res->end); }}static void __init ccio_init_resources(struct ioc *ioc){ struct resource *res = ioc->mmio_region; char *name = kmalloc(14, GFP_KERNEL); snprintf(name, 14, "GSC Bus [%d/]", ioc->hw_path); ccio_init_resource(res, name, &ioc->ioc_regs->io_io_low); ccio_init_resource(res + 1, name, &ioc->ioc_regs->io_io_low_hv);}static int new_ioc_area(struct resource *res, unsigned long size, unsigned long min, unsigned long max, unsigned long align){ if (max <= min) return -EBUSY; res->start = (max - size + 1) &~ (align - 1); res->end = res->start + size; /* We might be trying to expand the MMIO range to include * a child device that has already registered it's MMIO space. * Use "insert" instead of request_resource(). */ if (!insert_resource(&iomem_resource, res)) return 0; return new_ioc_area(res, size, min, max - size, align);}static int expand_ioc_area(struct resource *res, unsigned long size, unsigned long min, unsigned long max, unsigned long align){ unsigned long start, len; if (!res->parent) return new_ioc_area(res, size, min, max, align); start = (res->start - size) &~ (align - 1); len = res->end - start + 1; if (start >= min) { if (!adjust_resource(res, start, len)) return 0; } start = res->start; len = ((size + res->end + align) &~ (align - 1)) - start; if (start + len <= max) { if (!adjust_resource(res, start, len)) return 0; } return -EBUSY;}/* * Dino calls this function. Beware that we may get called on systems * which have no IOC (725, B180, C160L, etc) but do have a Dino. * So it's legal to find no parent IOC. * * Some other issues: one of the resources in the ioc may be unassigned. */int ccio_allocate_resource(const struct parisc_device *dev, struct resource *res, unsigned long size, unsigned long min, unsigned long max, unsigned long align){ struct resource *parent = &iomem_resource; struct ioc *ioc = ccio_get_iommu(dev); if (!ioc) goto out; parent = ioc->mmio_region; if (parent->parent && !allocate_resource(parent, res, size, min, max, align, NULL, NULL)) return 0; if ((parent + 1)->parent && !allocate_resource(parent + 1, res, size, min, max, align, NULL, NULL)) return 0; if (!expand_ioc_area(parent, size, min, max, align)) { __raw_writel(((parent->start)>>16) | 0xffff0000, &ioc->ioc_regs->io_io_low); __raw_writel(((parent->end)>>16) | 0xffff0000, &ioc->ioc_regs->io_io_high); } else if (!expand_ioc_area(parent + 1, size, min, max, align)) { parent++; __raw_writel(((parent->start)>>16) | 0xffff0000, &ioc->ioc_regs->io_io_low_hv); __raw_writel(((parent->end)>>16) | 0xffff0000, &ioc->ioc_regs->io_io_high_hv); } else { return -EBUSY; } out: return allocate_resource(parent, res, size, min, max, align, NULL,NULL);}int ccio_request_resource(const struct parisc_device *dev, struct resource *res){ struct resource *parent; struct ioc *ioc = ccio_get_iommu(dev); if (!ioc) { parent = &iomem_resource; } else if ((ioc->mmio_region->start <= res->start) && (res->end <= ioc->mmio_region->end)) { parent = ioc->mmio_region; } else if (((ioc->mmio_region + 1)->start <= res->start) && (res->end <= (ioc->mmio_region + 1)->end)) { parent = ioc->mmio_region + 1; } else { return -EBUSY; } /* "transparent" bus bridges need to register MMIO resources * firmware assigned them. e.g. children of hppb.c (e.g. K-class) * registered their resources in the PDC "bus walk" (See * arch/parisc/kernel/inventory.c). */ return insert_resource(parent, res);}/** * ccio_probe - Determine if ccio should claim this device. * @dev: The device which has been found * * Determine if ccio should claim this chip (return 0) or not (return 1). * If so, initialize the chip and tell other partners in crime they * have work to do. */static int __init ccio_probe(struct parisc_device *dev){ int i; struct ioc *ioc, **ioc_p = &ioc_list; struct proc_dir_entry *info_entry, *bitmap_entry; ioc = kzalloc(sizeof(struct ioc), GFP_KERNEL); if (ioc == NULL) { printk(KERN_ERR MODULE_NAME ": memory allocation failure\n"); return 1; } ioc->name = dev->id.hversion == U2_IOA_RUNWAY ? "U2" : "UTurn"; printk(KERN_INFO "Found %s at 0x%lx\n", ioc->name, dev->hpa.start); for (i = 0; i < ioc_count; i++) { ioc_p = &(*ioc_p)->next; } *ioc_p = ioc; ioc->hw_path = dev->hw_path; ioc->ioc_regs = ioremap_nocache(dev->hpa.start, 4096); ccio_ioc_init(ioc); ccio_init_resources(ioc); hppa_dma_ops = &ccio_ops; dev->dev.platform_data = kzalloc(sizeof(struct pci_hba_data), GFP_KERNEL); /* if this fails, no I/O cards will work, so may as well bug */ BUG_ON(dev->dev.platform_data == NULL); HBA_DATA(dev->dev.platform_data)->iommu = ioc; if (ioc_count == 0) { info_entry = create_proc_entry(MODULE_NAME, 0, proc_runway_root); if (info_entry) info_entry->proc_fops = &ccio_proc_info_fops; bitmap_entry = create_proc_entry(MODULE_NAME"-bitmap", 0, proc_runway_root); if (bitmap_entry) bitmap_entry->proc_fops = &ccio_proc_bitmap_fops; } ioc_count++; parisc_vmerge_boundary = IOVP_SIZE; parisc_vmerge_max_size = BITS_PER_LONG * IOVP_SIZE; parisc_has_iommu(); return 0;}/** * ccio_init - ccio initalization procedure. * * Register this driver. */void __init ccio_init(void){ register_parisc_driver(&ccio_driver);}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -