📄 sba_iommu.c
字号:
err = request_resource(&iomem_resource, &(sba_dev->chip_resv)); WARN_ON(err < 0); sba_dev->iommu_resv.name = "IOVA Space"; sba_dev->iommu_resv.start = 0x40000000UL; sba_dev->iommu_resv.end = 0x50000000UL - 1; err = request_resource(&iomem_resource, &(sba_dev->iommu_resv)); WARN_ON(err < 0); } else { /* IKE, REO */ sba_dev->ioc[0].ioc_hpa = ioc_remap(sba_dev, IKE_IOC_OFFSET(0)); sba_dev->ioc[1].ioc_hpa = ioc_remap(sba_dev, IKE_IOC_OFFSET(1)); num_ioc = 2; /* TODO - LOOKUP Ike/Stretch chipset mem map */ } /* XXX: What about Reo Grande? */ sba_dev->num_ioc = num_ioc; for (i = 0; i < num_ioc; i++) { void __iomem *ioc_hpa = sba_dev->ioc[i].ioc_hpa; unsigned int j; for (j=0; j < sizeof(u64) * ROPES_PER_IOC; j+=sizeof(u64)) { /* * Clear ROPE(N)_CONFIG AO bit. * Disables "NT Ordering" (~= !"Relaxed Ordering") * Overrides bit 1 in DMA Hint Sets. * Improves netperf UDP_STREAM by ~10% for bcm5701. */ if (IS_PLUTO(sba_dev->dev)) { void __iomem *rope_cfg; unsigned long cfg_val; rope_cfg = ioc_hpa + IOC_ROPE0_CFG + j; cfg_val = READ_REG(rope_cfg); cfg_val &= ~IOC_ROPE_AO; WRITE_REG(cfg_val, rope_cfg); } /* ** Make sure the box crashes on rope errors. */ WRITE_REG(HF_ENABLE, ioc_hpa + ROPE0_CTL + j); } /* flush out the last writes */ READ_REG(sba_dev->ioc[i].ioc_hpa + ROPE7_CTL); DBG_INIT(" ioc[%d] ROPE_CFG 0x%Lx ROPE_DBG 0x%Lx\n", i, READ_REG(sba_dev->ioc[i].ioc_hpa + 0x40), READ_REG(sba_dev->ioc[i].ioc_hpa + 0x50) ); DBG_INIT(" STATUS_CONTROL 0x%Lx FLUSH_CTRL 0x%Lx\n", READ_REG(sba_dev->ioc[i].ioc_hpa + 0x108), READ_REG(sba_dev->ioc[i].ioc_hpa + 0x400) ); if (IS_PLUTO(sba_dev->dev)) { sba_ioc_init_pluto(sba_dev->dev, &(sba_dev->ioc[i]), i); } else { 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("%s:%s() could not allocate resource map\n", __FILE__, __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); } spin_lock_init(&sba_dev->sba_lock); 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 (ioc_needs_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(struct seq_file *m, void *p){ 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 */#ifdef SBA_COLLECT_STATS unsigned long avg = 0, min, max;#endif int i, len = 0; len += seq_printf(m, "%s rev %d.%d\n", sba_dev->name, (sba_dev->hw_rev & 0x7) + 1, (sba_dev->hw_rev & 0x18) >> 3 ); len += seq_printf(m, "IO PDIR size : %d bytes (%d entries)\n", (int) ((ioc->res_size << 3) * sizeof(u64)), /* 8 bits/byte */ total_pages); len += seq_printf(m, "Resource bitmap : %d bytes (%d pages)\n", ioc->res_size, ioc->res_size << 3); /* 8 bits per byte */ len += seq_printf(m, "LMMIO_BASE/MASK/ROUTE %08x %08x %08x\n", READ_REG32(sba_dev->sba_hpa + LMMIO_DIST_BASE), READ_REG32(sba_dev->sba_hpa + LMMIO_DIST_MASK), READ_REG32(sba_dev->sba_hpa + LMMIO_DIST_ROUTE) ); for (i=0; i<4; i++) len += seq_printf(m, "DIR%d_BASE/MASK/ROUTE %08x %08x %08x\n", i, READ_REG32(sba_dev->sba_hpa + LMMIO_DIRECT0_BASE + i*0x18), READ_REG32(sba_dev->sba_hpa + LMMIO_DIRECT0_MASK + i*0x18), READ_REG32(sba_dev->sba_hpa + LMMIO_DIRECT0_ROUTE + i*0x18) );#ifdef SBA_COLLECT_STATS len += seq_printf(m, "IO PDIR entries : %ld free %ld used (%d%%)\n", total_pages - ioc->used_pages, ioc->used_pages, (int) (ioc->used_pages * 100 / total_pages)); 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; len += seq_printf(m, " Bitmap search : %ld/%ld/%ld (min/avg/max CPU Cycles)\n", min, avg, max); len += seq_printf(m, "pci_map_single(): %12ld calls %12ld pages (avg %d/1000)\n", 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; len += seq_printf(m, "pci_unmap_single: %12ld calls %12ld pages (avg %d/1000)\n", min, max, (int) ((max * 1000)/min)); len += seq_printf(m, "pci_map_sg() : %12ld calls %12ld pages (avg %d/1000)\n", ioc->msg_calls, ioc->msg_pages, (int) ((ioc->msg_pages * 1000)/ioc->msg_calls)); len += seq_printf(m, "pci_unmap_sg() : %12ld calls %12ld pages (avg %d/1000)\n", ioc->usg_calls, ioc->usg_pages, (int) ((ioc->usg_pages * 1000)/ioc->usg_calls));#endif return 0;}static intsba_proc_open(struct inode *i, struct file *f){ return single_open(f, &sba_proc_info, NULL);}static const struct file_operations sba_proc_fops = { .owner = THIS_MODULE, .open = sba_proc_open, .read = seq_read, .llseek = seq_lseek, .release = single_release,};static intsba_proc_bitmap_info(struct seq_file *m, void *p){ struct sba_device *sba_dev = sba_list; struct ioc *ioc = &sba_dev->ioc[0]; /* FIXME: Multi-IOC support! */ unsigned int *res_ptr = (unsigned int *)ioc->res_map; int i, len = 0; for (i = 0; i < (ioc->res_size/sizeof(unsigned int)); ++i, ++res_ptr) { if ((i & 7) == 0) len += seq_printf(m, "\n "); len += seq_printf(m, " %08x", *res_ptr); } len += seq_printf(m, "\n"); return 0;}static intsba_proc_bitmap_open(struct inode *i, struct file *f){ return single_open(f, &sba_proc_bitmap_info, NULL);}static const struct file_operations sba_proc_bitmap_fops = { .owner = THIS_MODULE, .open = sba_proc_bitmap_open, .read = seq_read, .llseek = seq_lseek, .release = single_release,};#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 }, { HPHW_IOA, HVERSION_REV_ANY_ID, PLUTO_MCKINLEY_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 sba 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; void __iomem *sba_addr = ioremap_nocache(dev->hpa.start, SBA_FUNC_SIZE); struct proc_dir_entry *info_entry, *bitmap_entry, *root; sba_dump_ranges(sba_addr); /* Read HW Rev First */ func_class = READ_REG(sba_addr + SBA_FCLASS); if (IS_ASTRO(dev)) { unsigned long fclass; static char astro_rev[]="Astro ?.?"; /* Astro is broken...Read HW Rev First */ fclass = READ_REG(sba_addr); astro_rev[6] = '1' + (char) (fclass & 0x7); astro_rev[8] = '0' + (char) ((fclass & 0x18) >> 3); version = astro_rev; } else if (IS_IKE(dev)) { static char ike_rev[] = "Ike rev ?"; ike_rev[8] = '0' + (char) (func_class & 0xff); version = ike_rev; } else if (IS_PLUTO(dev)) { static char pluto_rev[]="Pluto ?.?"; pluto_rev[6] = '0' + (char) ((func_class & 0xf0) >> 4); pluto_rev[8] = '0' + (char) (func_class & 0x0f); version = pluto_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); /* Astro and Pluto have one IOC per SBA */ if ((!IS_ASTRO(dev)) || (!IS_PLUTO(dev))) global_ioc_cnt *= 2; } printk(KERN_INFO "%s found %s at 0x%llx\n", MODULE_NAME, version, (unsigned long long)dev->hpa.start); sba_dev = kzalloc(sizeof(struct sba_device), GFP_KERNEL); if (!sba_dev) { printk(KERN_ERR MODULE_NAME " - couldn't alloc sba_device\n"); return -ENOMEM; } parisc_set_drvdata(dev, sba_dev); 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->name = dev->name; sba_dev->sba_hpa = sba_addr; 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 switch (dev->id.hversion) { case PLUTO_MCKINLEY_PORT: root = proc_mckinley_root; break; case ASTRO_RUNWAY_PORT: case IKE_MERCED_PORT: default: root = proc_runway_root; break; } info_entry = create_proc_entry("sba_iommu", 0, root); bitmap_entry = create_proc_entry("sba_iommu-bitmap", 0, root); if (info_entry) info_entry->proc_fops = &sba_proc_fops; if (bitmap_entry) bitmap_entry->proc_fops = &sba_proc_bitmap_fops;#endif parisc_vmerge_boundary = IOVP_SIZE; parisc_vmerge_max_size = IOVP_SIZE * BITS_PER_LONG; parisc_has_iommu(); 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. * * Returns the appropriate IOMMU data for the given parisc PCI controller. * This is cached and used later for PCI DMA Mapping. */void * sba_get_iommu(struct parisc_device *pci_hba){ struct parisc_device *sba_dev = parisc_parent(pci_hba); struct sba_device *sba = sba_dev->dev.driver_data; char t = sba_dev->id.hw_type; int iocnum = (pci_hba->hw_path >> 3); /* rope # */ WARN_ON((t != HPHW_IOA) && (t != HPHW_BCPORT)); return &(sba->ioc[iocnum]);}/** * sba_directed_lmmio - return first directed LMMIO range routed to rope * @pa_dev: The parisc device. * @r: resource PCI host controller wants start/end fields assigned. * * For the given parisc PCI controller, determine if any direct ranges * are routed down the corresponding rope. */void sba_directed_lmmio(struct parisc_device *pci_hba, struct resource *r){ struct parisc_device *sba_dev = parisc_parent(pci_hba); struct sba_device *sba = sba_dev->dev.driver_data; char t = sba_dev->id.hw_type; int i; int rope = (pci_hba->hw_path & (ROPES_PER_IOC-1)); /* rope # */ BUG_ON((t!=HPHW_IOA) && (t!=HPHW_BCPORT)); r->start = r->end = 0; /* Astro has 4 directed ranges. Not sure about Ike/Pluto/et al */ for (i=0; i<4; i++) { int base, size; void __iomem *reg = sba->sba_hpa + i*0x18; base = READ_REG32(reg + LMMIO_DIRECT0_BASE); if ((base & 1) == 0) continue; /* not enabled */ size = READ_REG32(reg + LMMIO_DIRECT0_ROUTE); if ((size & (ROPES_PER_IOC-1)) != rope) continue; /* directed down different rope */ r->start = (base & ~1UL) | PCI_F_EXTEND; size = ~ READ_REG32(reg + LMMIO_DIRECT0_MASK); r->end = r->start + size; }}/** * sba_distributed_lmmio - return portion of distributed LMMIO range * @pa_dev: The parisc device. * @r: resource PCI host controller wants start/end fields assigned. * * For the given parisc PCI controller, return portion of distributed LMMIO * range. The distributed LMMIO is always present and it's just a question * of the base address and size of the range. */void sba_distributed_lmmio(struct parisc_device *pci_hba, struct resource *r ){ struct parisc_device *sba_dev = parisc_parent(pci_hba); struct sba_device *sba = sba_dev->dev.driver_data; char t = sba_dev->id.hw_type; int base, size; int rope = (pci_hba->hw_path & (ROPES_PER_IOC-1)); /* rope # */ BUG_ON((t!=HPHW_IOA) && (t!=HPHW_BCPORT)); r->start = r->end = 0; base = READ_REG32(sba->sba_hpa + LMMIO_DIST_BASE); if ((base & 1) == 0) { BUG(); /* Gah! Distr Range wasn't enabled! */ return; } r->start = (base & ~1UL) | PCI_F_EXTEND; size = (~READ_REG32(sba->sba_hpa + LMMIO_DIST_MASK)) / ROPES_PER_IOC; r->start += rope * (size + 1); /* adjust base for this rope */ r->end = r->start + size;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -