?? ccio-dma.c
字號:
unsigned long flags; struct ccio_device *ioa = ccio_list; spin_lock_irqsave(&ioa->ccio_lock, flags); ccio_free_size += get_order(size); spin_unlock_irqrestore(&ioa->ccio_lock, flags); ccio_unmap_single(hwdev, dma_handle, size, 0); free_pages((unsigned long) vaddr, get_order(size));}static int ccio_map_sg(struct pci_dev *dev, struct scatterlist *sglist, int nents, int direction){ int tmp = nents; DBG_RUN(KERN_WARNING __FUNCTION__ " START\n"); /* KISS: map each buffer seperately. */ while (nents) { sg_dma_address(sglist) = ccio_map_single(dev, sglist->address, sglist->length, direction); sg_dma_len(sglist) = sglist->length; nents--; sglist++; } DBG_RUN(KERN_WARNING __FUNCTION__ " DONE\n"); return tmp;}static void ccio_unmap_sg(struct pci_dev *dev, struct scatterlist *sglist, int nents, int direction){ DBG_RUN(KERN_WARNING __FUNCTION__ " : unmapping %d entries\n", nents); while (nents) { ccio_unmap_single(dev, sg_dma_address(sglist), sg_dma_len(sglist), direction); nents--; sglist++; } return;}static struct pci_dma_ops ccio_ops = { ccio_dma_supported, ccio_alloc_consistent, ccio_free_consistent, ccio_map_single, ccio_unmap_single, ccio_map_sg, ccio_unmap_sg, NULL, /* dma_sync_single : NOP for U2/Uturn */ NULL, /* dma_sync_sg : ditto */};#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 hp_device *d){ if(d->spa_shift == 0) { panic(__FUNCTION__ ": Can't determine I/O TLB size.\n"); } return(1 << d->spa_shift);}#else/* Uturn supports 256 TLB entries */#define CCIO_CHAINID_SHIFT 8#define CCIO_CHAINID_MASK 0xff#endif /* 0 *//*** Figure out how big the I/O PDIR should be and alloc it.** Also sets variables which depend on pdir size.*/static voidccio_alloc_pdir(struct ccio_device *ioa){ extern unsigned long mem_max; /* arch.../setup.c */ u32 iova_space_size = 0; void * pdir_base; int pdir_size, iov_order; /* ** Determine IOVA Space size from memory size. ** Using "mem_max" is a kluge. ** ** 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 */ if (mem_max < (ccio_mem_ratio*1024*1024)) { iova_space_size = 1024*1024;#ifdef __LP64__ } else if (mem_max > (ccio_mem_ratio*512*1024*1024)) { iova_space_size = 512*1024*1024;#endif } else { iova_space_size = (u32) (mem_max/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); 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); ioa->pdir_size = pdir_size = (iova_space_size/IOVP_SIZE) * sizeof(u64); ASSERT(pdir_size < 4*1024*1024); /* max pdir size < 4MB */ /* Verify it's a power of two */ ASSERT((1 << get_order(pdir_size)) == (pdir_size >> PAGE_SHIFT)); DBG_INIT(__FUNCTION__ " hpa 0x%p mem %dMB IOV %dMB (%d bits)\n PDIR size 0x%0x", ioa->ccio_hpa, (int) (mem_max>>20), iova_space_size>>20, iov_order + PAGE_SHIFT, pdir_size); ioa->pdir_base = pdir_base = (void *) __get_free_pages(GFP_KERNEL, get_order(pdir_size)); if (NULL == pdir_base) { panic(__FILE__ ":" __FUNCTION__ "() could not allocate I/O Page Table\n"); } memset(pdir_base, 0, pdir_size); ASSERT((((unsigned long) pdir_base) & PAGE_MASK) == (unsigned long) pdir_base); DBG_INIT(" base %p", pdir_base); /* ** Chainid is the upper most bits of an IOVP used to determine ** which TLB entry an IOVP will use. */ ioa->chainid_shift = get_order(iova_space_size)+PAGE_SHIFT-CCIO_CHAINID_SHIFT; DBG_INIT(" chainid_shift 0x%x\n", ioa->chainid_shift);}static voidccio_hw_init(struct ccio_device *ioa){ int i; /* ** Initialize IOA hardware */ WRITE_U32(CCIO_CHAINID_MASK << ioa->chainid_shift, &ioa->ccio_hpa->io_chain_id_mask); WRITE_U32(virt_to_phys(ioa->pdir_base), &ioa->ccio_hpa->io_pdir_base); /* ** Go to "Virtual Mode" */ WRITE_U32(IOA_NORMAL_MODE, &ioa->ccio_hpa->io_control); /* ** Initialize all I/O TLB entries to 0 (Valid bit off). */ WRITE_U32(0, &ioa->ccio_hpa->io_tlb_entry_m); WRITE_U32(0, &ioa->ccio_hpa->io_tlb_entry_l); for (i = 1 << CCIO_CHAINID_SHIFT; i ; i--) { WRITE_U32((CMD_TLB_DIRECT_WRITE | (i << ioa->chainid_shift)), &ioa->ccio_hpa->io_command); }}static voidccio_resmap_init(struct ccio_device *ioa){ u32 res_size; /* ** Ok...we do more than just init resource map */ ioa->ccio_lock = SPIN_LOCK_UNLOCKED; ioa->res_hint = 16; /* next available IOVP - circular search */ /* resource map size dictated by pdir_size */ res_size = ioa->pdir_size/sizeof(u64); /* entries */ res_size >>= 3; /* convert bit count to byte count */ DBG_INIT(__FUNCTION__ "() res_size 0x%x\n", res_size); ioa->res_size = res_size; ioa->res_map = (char *) __get_free_pages(GFP_KERNEL, get_order(res_size)); if (NULL == ioa->res_map) { panic(__FILE__ ":" __FUNCTION__ "() could not allocate resource map\n"); } memset(ioa->res_map, 0, res_size);}/* CUJO20 KLUDGE start */static struct { u16 hversion; u8 spa; u8 type; u32 foo[3]; /* 16 bytes total */} cujo_iodc __attribute__ ((aligned (64)));static unsigned long cujo_result[32] __attribute__ ((aligned (16))) = {0,0,0,0};/*** CUJO 2.0 incorrectly decodes a memory access for specific** pages (every page at specific iotlb locations dependent** upon where the cujo is flexed - diff on raven/firehawk.** resulting in an hpmc and/or silent data corruption.** Workaround is to prevent use of those I/O TLB entries** by marking the suspect bitmap range entries as busy.*/static voidccio_cujo20_hack(struct ccio_device *ioa){ unsigned long status; unsigned int idx; u8 *res_ptr = ioa->res_map; u32 iovp=0x0; unsigned long mask; status = pdc_iodc_read( &cujo_result, (void *) CUJO_RAVEN_LOC, 0, &cujo_iodc, 16); if (status == 0) { if (cujo_iodc.hversion==CUJO_20_BADHVERS) iovp = CUJO_20_BADPAGE1; } else { status = pdc_iodc_read( &cujo_result, (void *) CUJO_FIREHAWK_LOC, 0, &cujo_iodc, 16); if (status == 0) { if (cujo_iodc.hversion==CUJO_20_BADHVERS) iovp = CUJO_20_BADPAGE2; } else { /* not a defective system */ return; } } printk(MODULE_NAME ": Cujo 2.0 bug needs a work around\n"); ccio_cujo_bug = 1; /* ** mark bit entries that match "bad page" */ idx = PDIR_INDEX(iovp)>>3; mask = 0xff; while(idx * sizeof(u8) < ioa->res_size) { res_ptr[idx] |= mask; idx += (PDIR_INDEX(CUJO_20_STEP)>>3); ccio_used_pages += 8; ccio_used_bytes += 1; }}/* CUJO20 KLUDGE end */#ifdef CONFIG_PROC_FSstatic int ccio_proc_info(char *buf, char **start, off_t offset, int len){ unsigned long i = 0; struct ccio_device *ioa = ccio_list; unsigned long *res_ptr = (unsigned long *)ioa->res_map; unsigned long total_pages = ioa->res_size << 3; /* 8 bits per byte */ sprintf(buf, "%s\nCujo 2.0 bug : %s\n", parisc_getHWdescription(ioa->iodc->hw_type, ioa->iodc->hversion, ioa->iodc->sversion), (ccio_cujo_bug ? "yes" : "no")); sprintf(buf, "%sIO pdir size : %d bytes (%d entries)\n", buf, ((ioa->res_size << 3) * sizeof(u64)), /* 8 bits per byte */ ioa->res_size << 3); /* 8 bits per byte */ sprintf(buf, "%sResource bitmap : %d bytes (%d pages)\n", buf, ioa->res_size, ioa->res_size << 3); /* 8 bits per byte */ strcat(buf, " total: free: used: % used:\n"); sprintf(buf, "%sblocks %8d %8ld %8ld %8ld%%\n", buf, ioa->res_size, ioa->res_size - ccio_used_bytes, ccio_used_bytes, (ccio_used_bytes * 100) / ioa->res_size); sprintf(buf, "%spages %8ld %8ld %8ld %8ld%%\n", buf, total_pages, total_pages - ccio_used_pages, ccio_used_pages, (ccio_used_pages * 100 / total_pages)); sprintf(buf, "%sconsistent %8ld %8ld\n", buf, ccio_alloc_size, ccio_free_size); strcat(buf, "\nResource bitmap:\n"); for(; i < (ioa->res_size / sizeof(unsigned long)); ++i, ++res_ptr) len += sprintf(buf, "%s%08lx ", buf, *res_ptr); strcat(buf, "\n"); return strlen(buf);}#endif/*** 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 intccio_driver_callback(struct hp_device *d, struct pa_iodc_driver *dri){ struct ccio_device *ioa; printk("%s found %s at 0x%p\n", dri->name, dri->version, d->hpa); if (ccio_list) { printk(MODULE_NAME ": already initialized one device\n"); return(0); } ioa = kmalloc(sizeof(struct ccio_device), GFP_KERNEL); if (NULL == ioa) { printk(MODULE_NAME " - couldn't alloc ccio_device\n"); return(1); } memset(ioa, 0, sizeof(struct ccio_device)); /* ** ccio list is used mainly as a kluge to support a single instance. ** Eventually, with core dumps, it'll be useful for debugging. */ ccio_list = ioa; ioa->iodc = d;#if 1/* KLUGE: determine IOA hpa based on GSC port value.** Needed until we have a PA bus walk. Can only discover IOA via** walking the architected PA MMIO space as described by the I/O ACD.** "Legacy" PA Firmware only tells us about unarchitected devices** that can't be detected by PA/EISA/PCI bus walks.*/ switch((long) d->hpa) { case 0xf3fbf000L: /* C110 IOA0 LBC (aka GSC port) */ /* ccio_hpa same as C200 IOA0 */ case 0xf203f000L: /* C180/C200/240/C360 IOA0 LBC (aka GSC port) */ ioa->ccio_hpa = (struct ioa_registers *) 0xfff88000L; break; case 0xf103f000L: /* C180/C200/240/C360 IOA1 LBC (aka GSC port) */ ioa->ccio_hpa = (struct ioa_registers *) 0xfff8A000L; break; default: panic("ccio-dma.c doesn't know this GSC port Address!\n"); break; };#else ioa->ccio_hpa = d->hpa;#endif ccio_alloc_pdir(ioa); ccio_hw_init(ioa); ccio_resmap_init(ioa); /* CUJO20 KLUDGE start */ ccio_cujo20_hack(ioa); /* CUJO20 KLUDGE end */ hppa_dma_ops = &ccio_ops; create_proc_info_entry(MODULE_NAME, 0, proc_runway_root, ccio_proc_info); return(0);}
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -