?? ccio-dma.c
字號:
* This function searches the ioc_list for an ioc that matches * the provide hardware path. */static struct ioc * ccio_find_ioc(int hw_path){ int i; struct ioc *ioc; ioc = ioc_list; for (i = 0; i < ioc_count; i++) { if (ioc->hw_path == hw_path) return ioc; ioc = ioc->next; } return NULL;}/** * ccio_get_iommu - Find the iommu which controls this device * @dev: The parisc device. * * This function searches through the registerd IOMMU's and returns the * appropriate IOMMU for the device based upon the devices hardware path. */void * ccio_get_iommu(const struct parisc_device *dev){ dev = find_pa_parent_type(dev, HPHW_IOA); if (!dev) return NULL; return ccio_find_ioc(dev->hw_path);}#define CUJO_20_STEP 0x10000000 /* inc upper nibble *//* Cujo 2.0 has a bug which will silently corrupt data being transferred * to/from certain pages. To avoid this happening, we mark these pages * as `used', and ensure that nothing will try to allocate from them. */void ccio_cujo20_fixup(struct parisc_device *dev, u32 iovp){ unsigned int idx; struct ioc *ioc = ccio_get_iommu(dev); u8 *res_ptr;#ifdef CONFIG_PROC_FS ioc->cujo20_bug = 1;#endif res_ptr = ioc->res_map; idx = PDIR_INDEX(iovp) >> 3; while (idx < ioc->res_size) { res_ptr[idx] |= 0xff; idx += PDIR_INDEX(CUJO_20_STEP) >> 3; }}#if 0/* GRANT - is this needed for U2 or not? *//*** 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 *//** * 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, iov_order; u32 iova_space_size; unsigned long physmem; /* ** 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). */ /* limit IOVA space size to 1MB-1GB */ physmem = num_physpages << PAGE_SHIFT; if(physmem < (ccio_mem_ratio * 1024 * 1024)) { iova_space_size = 1024 * 1024;#ifdef __LP64__ } else if(physmem > (ccio_mem_ratio * 512 * 1024 * 1024)) { iova_space_size = 512 * 1024 * 1024;#endif } else { iova_space_size = (u32)(physmem / ccio_mem_ratio); } /* ** 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) >> (IOVP_SHIFT - PAGE_SHIFT); ASSERT(iov_order <= (30 - IOVP_SHIFT)); /* iova_space_size <= 1GB */ ASSERT(iov_order >= (20 - IOVP_SHIFT)); /* iova_space_size >= 1MB */ iova_space_size = 1 << (iov_order + IOVP_SHIFT); ioc->pdir_size = (iova_space_size / IOVP_SIZE) * sizeof(u64); ASSERT(ioc->pdir_size < 4 * 1024 * 1024); /* max pdir size < 4MB */ /* Verify it's a power of two */ ASSERT((1 << get_order(ioc->pdir_size)) == (ioc->pdir_size >> PAGE_SHIFT)); DBG_INIT("%s() hpa 0x%p mem %luMB IOV %dMB (%d bits) PDIR size 0x%0x", __FUNCTION__, ioc->ioc_hpa, physmem>>20, iova_space_size>>20, iov_order + PAGE_SHIFT, ioc->pdir_size); ioc->pdir_base = (u64 *)__get_free_pages(GFP_KERNEL, get_order(ioc->pdir_size)); if(NULL == ioc->pdir_base) { panic(__FILE__ ":%s() could not allocate I/O Page Table\n", __FUNCTION__); } memset(ioc->pdir_base, 0, ioc->pdir_size); ASSERT((((unsigned long)ioc->pdir_base) & PAGE_MASK) == (unsigned long)ioc->pdir_base); DBG_INIT(" base %p", 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(__FILE__ ":%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_hpa->io_chain_id_mask); WRITE_U32(virt_to_phys(ioc->pdir_base), &ioc->ioc_hpa->io_pdir_base); /* ** Go to "Virtual Mode" */ WRITE_U32(IOA_NORMAL_MODE, &ioc->ioc_hpa->io_control); /* ** Initialize all I/O TLB entries to 0 (Valid bit off). */ WRITE_U32(0, &ioc->ioc_hpa->io_tlb_entry_m); WRITE_U32(0, &ioc->ioc_hpa->io_tlb_entry_l); for(i = 1 << CCIO_CHAINID_SHIFT; i ; i--) { WRITE_U32((CMD_TLB_DIRECT_WRITE | (i << ioc->chainid_shift)), &ioc->ioc_hpa->io_command); }}static voidccio_init_resource(struct resource *res, char *name, unsigned long ioaddr){ int result; res->flags = IORESOURCE_MEM; res->start = (unsigned long)(signed) __raw_readl(ioaddr) << 16; res->end = (unsigned long)(signed) (__raw_readl(ioaddr + 4) << 16) - 1; if (res->end + 1 == res->start) return; res->name = name; result = request_resource(&iomem_resource, res); if (result < 0) { printk(KERN_ERR "%s: failed to claim CCIO bus address space (%lx,%lx)\n", __FILE__, 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); sprintf(name, "GSC Bus [%d/]", ioc->hw_path); ccio_init_resource(res, name, (unsigned long)&ioc->ioc_hpa->io_io_low); ccio_init_resource(res + 1, name, (unsigned long)&ioc->ioc_hpa->io_io_low_hv);}static void expand_ioc_area(struct ioc *ioc, unsigned long size, unsigned long min, unsigned long max, unsigned long align){#ifdef NASTY_HACK_FOR_K_CLASS __raw_writel(0xfffff600, (unsigned long)&(ioc->ioc_hpa->io_io_high)); ioc->mmio_region[0].end = 0xf5ffffff;#endif}static struct resource *ccio_get_resource(struct ioc* ioc, const struct parisc_device *dev){ if (!ioc) { return &iomem_resource; } else if ((ioc->mmio_region->start <= dev->hpa) && (dev->hpa < ioc->mmio_region->end)) { return ioc->mmio_region; } else if (((ioc->mmio_region + 1)->start <= dev->hpa) && (dev->hpa < (ioc->mmio_region + 1)->end)) { return ioc->mmio_region + 1; } else { return NULL; }}int ccio_allocate_resource(const struct parisc_device *dev, struct resource *res, unsigned long size, unsigned long min, unsigned long max, unsigned long align, void (*alignf)(void *, struct resource *, unsigned long, unsigned long), void *alignf_data){ struct ioc *ioc = ccio_get_iommu(dev); struct resource *parent = ccio_get_resource(ioc, dev); if (!parent) return -EBUSY; if (!allocate_resource(parent, res, size, min, max, align, alignf, alignf_data)) return 0; expand_ioc_area(ioc, size, min, max, align); return allocate_resource(parent, res, size, min, max, align, alignf, alignf_data);}int ccio_request_resource(const struct parisc_device *dev, struct resource *res){ struct ioc *ioc = ccio_get_iommu(dev); struct resource *parent = ccio_get_resource(ioc, dev); return request_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 ccio_probe(struct parisc_device *dev){ int i; struct ioc *ioc, **ioc_p = &ioc_list; ioc = kmalloc(sizeof(struct ioc), GFP_KERNEL); if (ioc == NULL) { printk(KERN_ERR MODULE_NAME ": memory allocation failure\n"); return 1; } memset(ioc, 0, sizeof(struct ioc)); ioc->name = dev->id.hversion == U2_IOA_RUNWAY ? "U2" : "UTurn"; printk(KERN_INFO "Found %s at 0x%lx\n", ioc->name, dev->hpa); for (i = 0; i < ioc_count; i++) { ioc_p = &(*ioc_p)->next; } *ioc_p = ioc; ioc->hw_path = dev->hw_path; ioc->ioc_hpa = (struct ioa_registers *)dev->hpa; ccio_ioc_init(ioc); ccio_init_resources(ioc); hppa_dma_ops = &ccio_ops; if (ioc_count == 0) { /* XXX: Create separate entries for each ioc */ create_proc_read_entry(MODULE_NAME, S_IRWXU, proc_runway_root, ccio_proc_info, NULL); create_proc_read_entry(MODULE_NAME"-bitmap", S_IRWXU, proc_runway_root, ccio_resource_map, NULL); } ioc_count++; return 0;}struct pci_dev * ccio_get_fake(const struct parisc_device *dev){ struct ioc *ioc; dev = find_pa_parent_type(dev, HPHW_IOA); if(!dev) return NULL; ioc = ccio_find_ioc(dev->hw_path); if(!ioc) return NULL; if(ioc->fake_pci_dev) return ioc->fake_pci_dev; ioc->fake_pci_dev = kmalloc(sizeof(struct pci_dev), GFP_KERNEL); if(ioc->fake_pci_dev == NULL) { printk(KERN_ERR MODULE_NAME ": memory allocation failure\n"); return NULL; } memset(ioc->fake_pci_dev, 0, sizeof(struct pci_dev)); ioc->fake_pci_dev->sysdata = kmalloc(sizeof(struct pci_hba_data), GFP_KERNEL); if(ioc->fake_pci_dev->sysdata == NULL) { printk(KERN_ERR MODULE_NAME ": memory allocation failure\n"); return NULL; } HBA_DATA(ioc->fake_pci_dev->sysdata)->iommu = ioc; return ioc->fake_pci_dev;}/* We *can't* support JAVA (T600). Venture there at your own risk. */static 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 struct parisc_driver ccio_driver = { name: "U2/Uturn", id_table: ccio_tbl, probe: ccio_probe,};/** * 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 + -