📄 sba_iommu.c
字号:
static voidsba_ioc_init(struct parisc_device *sba, struct ioc *ioc, int ioc_num){ /* lba_set_iregs() is in arch/parisc/kernel/lba_pci.c */ extern void lba_set_iregs(struct parisc_device *, u32, u32); u32 iova_space_size, iova_space_mask; int pdir_size, iov_order; unsigned long physmem; struct parisc_device *lba; /* ** 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. */ physmem = num_physpages << PAGE_SHIFT; iova_space_size = (u32) (physmem/(sba_mem_ratio*global_ioc_cnt)); /* limit IOVA space size to 1MB-1GB */ if (iova_space_size < 1024*1024) { iova_space_size = 1024*1024; }#ifdef __LP64__ else if (iova_space_size > 512*1024*1024) { iova_space_size = 512*1024*1024; }#endif /* ** 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 >> (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 = pdir_size = (iova_space_size/IOVP_SIZE) * sizeof(u64); ASSERT(pdir_size < 4*1024*1024); /* max pdir size == 2MB */ /* Verify it's a power of two */ ASSERT((1 << get_order(pdir_size)) == (pdir_size >> PAGE_SHIFT)); DBG_INIT("%s() hpa 0x%lx mem %dMB IOV %dMB (%d bits) PDIR size 0x%0x\n", __FUNCTION__, ioc->ioc_hpa, (int) (physmem>>20), iova_space_size>>20, iov_order + PAGE_SHIFT, pdir_size); /* FIXME : DMA HINTs not used */ ioc->hint_shift_pdir = iov_order + PAGE_SHIFT; ioc->hint_mask_pdir = ~(0x3 << (iov_order + PAGE_SHIFT)); ioc->pdir_base = sba_alloc_pdir(pdir_size); DBG_INIT("%s() pdir %p size %x hint_shift_pdir %x hint_mask_pdir %lx\n", __FUNCTION__, ioc->pdir_base, pdir_size, ioc->hint_shift_pdir, ioc->hint_mask_pdir); ASSERT((((unsigned long) ioc->pdir_base) & PAGE_MASK) == (unsigned long) ioc->pdir_base); 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 = IOC_IOVA_SPACE_BASE | 1; /* bit 0 == enable bit */ ioc->imask = iova_space_mask; /* save it */ 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 Elroy IBASE/IMASK registers as well. */ for (lba = sba->child; lba; lba = lba->sibling) { int rope_num = (lba->hpa >> 13) & 0xf; if (rope_num >> 3 == ioc_num) lba_set_iregs(lba, ioc->ibase, ioc->imask); } /* ** Program the IOC's ibase and enable IOVA translation */ WRITE_REG(ioc->ibase, 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); 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 voidsba_hw_init(struct sba_device *sba_dev){ int i; int num_ioc; u64 ioc_ctl; 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_TC; /* Astro: firmware enables this */ 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 (IS_ASTRO(sba_dev->iodc)) { /* PAT_PDC (L-class) also reports the same goofy base */ sba_dev->ioc[0].ioc_hpa = ASTRO_IOC_OFFSET; num_ioc = 1; } else { sba_dev->ioc[0].ioc_hpa = sba_dev->ioc[1].ioc_hpa = 0; num_ioc = 2; } sba_dev->num_ioc = num_ioc; for (i = 0; i < num_ioc; i++) { sba_dev->ioc[i].ioc_hpa += sba_dev->sba_hpa + IKE_IOC_OFFSET(i); /* ** Make sure the box crashes if we get any errors on a rope. */ WRITE_REG(HF_ENABLE, sba_dev->ioc[i].ioc_hpa + ROPE0_CTL); WRITE_REG(HF_ENABLE, sba_dev->ioc[i].ioc_hpa + ROPE1_CTL); WRITE_REG(HF_ENABLE, sba_dev->ioc[i].ioc_hpa + ROPE2_CTL); WRITE_REG(HF_ENABLE, sba_dev->ioc[i].ioc_hpa + ROPE3_CTL); WRITE_REG(HF_ENABLE, sba_dev->ioc[i].ioc_hpa + ROPE4_CTL); WRITE_REG(HF_ENABLE, sba_dev->ioc[i].ioc_hpa + ROPE5_CTL); WRITE_REG(HF_ENABLE, sba_dev->ioc[i].ioc_hpa + ROPE6_CTL); WRITE_REG(HF_ENABLE, sba_dev->ioc[i].ioc_hpa + ROPE7_CTL); /* flush out the writes */ READ_REG(sba_dev->ioc[i].ioc_hpa + ROPE7_CTL); sba_ioc_init(sba_dev->dev, &(sba_dev->ioc[i]), i); }}static voidsba_common_init(struct sba_device *sba_dev){ int i; /* add this one to the head of the list (order doesn't matter) ** This will be useful for debugging - especially if we get coredumps */ sba_dev->next = sba_list; sba_list = sba_dev; for(i=0; i< sba_dev->num_ioc; i++) { int res_size;#ifdef DEBUG_DMB_TRAP extern void iterate_pages(unsigned long , unsigned long , void (*)(pte_t * , unsigned long), unsigned long ); void set_data_memory_break(pte_t * , unsigned long);#endif /* resource map size dictated by pdir_size */ res_size = sba_dev->ioc[i].pdir_size/sizeof(u64); /* entries */ /* Second part of PIRANHA BUG */ if (piranha_bad_128k) { res_size -= (128*1024)/sizeof(u64); } res_size >>= 3; /* convert bit count to byte count */ DBG_INIT("%s() res_size 0x%x\n", __FUNCTION__, res_size); sba_dev->ioc[i].res_size = res_size; sba_dev->ioc[i].res_map = (char *) __get_free_pages(GFP_KERNEL, get_order(res_size));#ifdef DEBUG_DMB_TRAP iterate_pages( sba_dev->ioc[i].res_map, res_size, set_data_memory_break, 0);#endif if (NULL == sba_dev->ioc[i].res_map) { panic(__FILE__ ":%s() could not allocate resource map\n", __FUNCTION__ ); } memset(sba_dev->ioc[i].res_map, 0, res_size); /* next available IOVP - circular search */ sba_dev->ioc[i].res_hint = (unsigned long *) &(sba_dev->ioc[i].res_map[L1_CACHE_BYTES]);#ifdef ASSERT_PDIR_SANITY /* Mark first bit busy - ie no IOVA 0 */ sba_dev->ioc[i].res_map[0] = 0x80; sba_dev->ioc[i].pdir_base[0] = 0xeeffc0addbba0080ULL;#endif /* Third (and last) part of PIRANHA BUG */ if (piranha_bad_128k) { /* region from +1408K to +1536 is un-usable. */ int idx_start = (1408*1024/sizeof(u64)) >> 3; int idx_end = (1536*1024/sizeof(u64)) >> 3; long *p_start = (long *) &(sba_dev->ioc[i].res_map[idx_start]); long *p_end = (long *) &(sba_dev->ioc[i].res_map[idx_end]); /* mark that part of the io pdir busy */ while (p_start < p_end) *p_start++ = -1; }#ifdef DEBUG_DMB_TRAP iterate_pages( sba_dev->ioc[i].res_map, res_size, set_data_memory_break, 0); iterate_pages( sba_dev->ioc[i].pdir_base, sba_dev->ioc[i].pdir_size, set_data_memory_break, 0);#endif DBG_INIT("%s() %d res_map %x %p\n", __FUNCTION__, i, res_size, sba_dev->ioc[i].res_map); } sba_dev->sba_lock = SPIN_LOCK_UNLOCKED; ioc_needs_fdc = boot_cpu_data.pdc.capabilities & PDC_MODEL_IOPDIR_FDC;#ifdef DEBUG_SBA_INIT /* * If the PDC_MODEL capabilities has Non-coherent IO-PDIR bit set * (bit #61, big endian), we have to flush and sync every time * IO-PDIR is changed in Ike/Astro. */ if (boot_cpu_data.pdc.capabilities & PDC_MODEL_IOPDIR_FDC) { printk(KERN_INFO MODULE_NAME " FDC/SYNC required.\n"); } else { printk(KERN_INFO MODULE_NAME " IOC has cache coherent PDIR.\n"); }#endif}#ifdef CONFIG_PROC_FSstatic int sba_proc_info(char *buf, char **start, off_t offset, int len){ struct sba_device *sba_dev = sba_list; struct ioc *ioc = &sba_dev->ioc[0]; /* FIXME: Multi-IOC support! */ int total_pages = (int) (ioc->res_size << 3); /* 8 bits per byte */ unsigned long i = 0, avg = 0, min, max; sprintf(buf, "%s rev %d.%d\n", sba_dev->name, (sba_dev->hw_rev & 0x7) + 1, (sba_dev->hw_rev & 0x18) >> 3 ); sprintf(buf, "%sIO PDIR size : %d bytes (%d entries)\n", buf, (int) ((ioc->res_size << 3) * sizeof(u64)), /* 8 bits/byte */ total_pages); sprintf(buf, "%sIO PDIR entries : %ld free %ld used (%d%%)\n", buf, total_pages - ioc->used_pages, ioc->used_pages, (int) (ioc->used_pages * 100 / total_pages)); sprintf(buf, "%sResource bitmap : %d bytes (%d pages)\n", buf, ioc->res_size, ioc->res_size << 3); /* 8 bits per byte */ min = max = ioc->avg_search[0]; for (i = 0; i < SBA_SEARCH_SAMPLE; i++) { avg += ioc->avg_search[i]; if (ioc->avg_search[i] > max) max = ioc->avg_search[i]; if (ioc->avg_search[i] < min) min = ioc->avg_search[i]; } avg /= SBA_SEARCH_SAMPLE; sprintf(buf, "%s Bitmap search : %ld/%ld/%ld (min/avg/max CPU Cycles)\n", buf, min, avg, max); sprintf(buf, "%spci_map_single(): %12ld calls %12ld pages (avg %d/1000)\n", buf, ioc->msingle_calls, ioc->msingle_pages, (int) ((ioc->msingle_pages * 1000)/ioc->msingle_calls)); /* KLUGE - unmap_sg calls unmap_single for each mapped page */ min = ioc->usingle_calls; max = ioc->usingle_pages - ioc->usg_pages; sprintf(buf, "%spci_unmap_single: %12ld calls %12ld pages (avg %d/1000)\n", buf, min, max, (int) ((max * 1000)/min)); sprintf(buf, "%spci_map_sg() : %12ld calls %12ld pages (avg %d/1000)\n", buf, ioc->msg_calls, ioc->msg_pages, (int) ((ioc->msg_pages * 1000)/ioc->msg_calls)); sprintf(buf, "%spci_unmap_sg() : %12ld calls %12ld pages (avg %d/1000)\n", buf, ioc->usg_calls, ioc->usg_pages, (int) ((ioc->usg_pages * 1000)/ioc->usg_calls)); return strlen(buf);}#if 0/* XXX too much output - exceeds 4k limit and needs to be re-written */static intsba_resource_map(char *buf, char **start, off_t offset, int len){ struct sba_device *sba_dev = sba_list; struct ioc *ioc = &sba_dev->ioc[0]; /* FIXME: Mutli-IOC suppoer! */ unsigned int *res_ptr = (unsigned int *)ioc->res_map; int i; buf[0] = '\0'; for(i = 0; i < (ioc->res_size / sizeof(unsigned int)); ++i, ++res_ptr) { if ((i & 7) == 0) strcat(buf,"\n "); sprintf(buf, "%s %08x", buf, *res_ptr); } strcat(buf, "\n"); return strlen(buf);}#endif /* 0 */#endif /* CONFIG_PROC_FS */static struct parisc_device_id sba_tbl[] = { { HPHW_IOA, HVERSION_REV_ANY_ID, ASTRO_RUNWAY_PORT, 0xb }, { HPHW_BCPORT, HVERSION_REV_ANY_ID, IKE_MERCED_PORT, 0xc }, { HPHW_BCPORT, HVERSION_REV_ANY_ID, REO_MERCED_PORT, 0xc }, { HPHW_BCPORT, HVERSION_REV_ANY_ID, REOG_MERCED_PORT, 0xc },/* These two entries commented out because we don't find them in a * buswalk yet. If/when we do, they would cause us to think we had * many more SBAs then we really do. * { HPHW_BCPORT, HVERSION_REV_ANY_ID, ASTRO_ROPES_PORT, 0xc }, * { HPHW_BCPORT, HVERSION_REV_ANY_ID, IKE_ROPES_PORT, 0xc }, */ { 0, }};int sba_driver_callback(struct parisc_device *);static struct parisc_driver sba_driver = { name: MODULE_NAME, id_table: sba_tbl, probe: sba_driver_callback,};/*** Determine if lba 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.*/intsba_driver_callback(struct parisc_device *dev){ struct sba_device *sba_dev; u32 func_class; int i; char *version;#ifdef DEBUG_SBA_INIT sba_dump_ranges(dev->hpa);#endif /* Read HW Rev First */ func_class = READ_REG(dev->hpa + SBA_FCLASS); if (IS_ASTRO(&dev->id)) { unsigned long fclass; static char astro_rev[]="Astro ?.?"; /* Astro is broken...Read HW Rev First */ fclass = READ_REG(dev->hpa); astro_rev[6] = '1' + (char) (fclass & 0x7); astro_rev[8] = '0' + (char) ((fclass & 0x18) >> 3); version = astro_rev; } else if (IS_IKE(&dev->id)) { static char ike_rev[]="Ike rev ?"; ike_rev[8] = '0' + (char) (func_class & 0xff); version = ike_rev; } else { static char reo_rev[]="REO rev ?"; reo_rev[8] = '0' + (char) (func_class & 0xff); version = reo_rev; } if (!global_ioc_cnt) { global_ioc_cnt = count_parisc_driver(&sba_driver); /* Only Astro has one IOC per SBA */ if (!IS_ASTRO(&dev->id)) global_ioc_cnt *= 2; } printk(KERN_INFO "%s found %s at 0x%lx\n", MODULE_NAME, version, dev->hpa);#ifdef DEBUG_SBA_INIT sba_dump_tlb(dev->hpa);#endif sba_dev = kmalloc(sizeof(struct sba_device), GFP_KERNEL); if (NULL == sba_dev) { printk(KERN_ERR MODULE_NAME " - couldn't alloc sba_device\n"); return(1); } dev->sysdata = (void *) sba_dev; memset(sba_dev, 0, sizeof(struct sba_device)); for(i=0; i<MAX_IOC; i++) spin_lock_init(&(sba_dev->ioc[i].res_lock)); sba_dev->dev = dev; sba_dev->hw_rev = func_class; sba_dev->iodc = &dev->id; sba_dev->name = dev->name; sba_dev->sba_hpa = dev->hpa; /* faster access */ sba_get_pat_resources(sba_dev); sba_hw_init(sba_dev); sba_common_init(sba_dev); hppa_dma_ops = &sba_ops;#ifdef CONFIG_PROC_FS if (IS_ASTRO(&dev->id)) { create_proc_info_entry("Astro", 0, proc_runway_root, sba_proc_info); } else if (IS_IKE(&dev->id)) { create_proc_info_entry("Ike", 0, proc_runway_root, sba_proc_info); } else { create_proc_info_entry("Reo", 0, proc_runway_root, sba_proc_info); }#if 0 create_proc_info_entry("bitmap", 0, proc_runway_root, sba_resource_map);#endif#endif return 0;}/*** One time initialization to let the world know the SBA was found.** This is the only routine which is NOT static.** Must be called exactly once before pci_init().*/void __init sba_init(void){ register_parisc_driver(&sba_driver);}/** * sba_get_iommu - Assign the iommu pointer for the pci bus controller. * @dev: The parisc device. * * This function searches through the registerd IOMMU's and returns the * appropriate IOMMU data for the given parisc PCI controller. */void * sba_get_iommu(struct parisc_device *pci_hba){ struct sba_device *sba = (struct sba_device *) pci_hba->parent->sysdata; char t = pci_hba->parent->id.hw_type; int iocnum = (pci_hba->hw_path >> 3); /* rope # */ if ((t!=HPHW_IOA) && (t!=HPHW_BCPORT)) BUG(); return &(sba->ioc[iocnum]);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -