📄 sba_iommu.c
字号:
DBG_INIT(" base %p\n", pdir_base); /* build IMASK for IOC and Elroy */ iova_space_mask = 0xffffffff; iova_space_mask <<= (iov_order + PAGE_SHIFT);#ifdef CONFIG_IA64_HP_PROTO /* ** REVISIT - this is a kludge, but we won't be supporting anything but ** zx1 2.0 or greater for real. When fw is in shape, ibase will ** be preprogrammed w/ the IOVA hole base and imask will give us ** the size. */ if ((sba_dev->hw_rev & 0xFF) < 0x20) { DBG_INIT("%s() Found SBA rev < 2.0, setting IOVA base to 0. This device will not be supported in the future.\n", __FUNCTION__); ioc->ibase = 0x0; } else#endif ioc->ibase = READ_REG(ioc->ioc_hpa + IOC_IBASE) & 0xFFFFFFFEUL; 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. */ WRITE_REG(ioc->imask, ioc->ioc_hpa+IOC_IMASK); /* ** Setting the upper bits makes checking for bypass addresses ** a little faster later on. */ ioc->imask |= 0xFFFFFFFF00000000UL; /* Set I/O PDIR Page size to system page size */ switch (PAGE_SHIFT) { case 12: /* 4K */ tcnfg = 0; break; case 13: /* 8K */ tcnfg = 1; break; case 14: /* 16K */ tcnfg = 2; break; case 16: /* 64K */ tcnfg = 3; break; } WRITE_REG(tcnfg, ioc->ioc_hpa+IOC_TCNFG); /* ** Program the IOC's ibase and enable IOVA translation ** Bit zero == enable bit. */ WRITE_REG(ioc->ibase | 1, ioc->ioc_hpa+IOC_IBASE); /* ** 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); /* ** If an AGP device is present, only use half of the IOV space ** for PCI DMA. Unfortunately we can't know ahead of time ** whether GART support will actually be used, for now we ** can just key on an AGP device found in the system. ** We program the next pdir index after we stop w/ a key for ** the GART code to handshake on. */ if (SBA_GET_AGP(sba_dev)) { DBG_INIT("%s() AGP Device found, reserving 512MB for GART support\n", __FUNCTION__); ioc->pdir_size /= 2; ((u64 *)pdir_base)[PDIR_INDEX(iova_space_size/2)] = 0x0000badbadc0ffeeULL; } DBG_INIT("%s() DONE\n", __FUNCTION__);}/****************************************************************************** SBA initialization code (HW and SW)**** o identify SBA chip itself** o FIXME: initialize DMA hints for reasonable defaults****************************************************************************/static voidsba_hw_init(struct sba_device *sba_dev){ int i; int num_ioc; u64 dma_mask; u32 func_id; /* ** Identify the SBA so we can set the dma_mask. We can make a virtual ** dma_mask of the memory subsystem such that devices not implmenting ** a full 64bit mask might still be able to bypass efficiently. */ func_id = READ_REG(sba_dev->sba_hpa + SBA_FUNC_ID); if (func_id == ZX1_FUNC_ID_VALUE) { dma_mask = 0xFFFFFFFFFFUL; } else { dma_mask = 0xFFFFFFFFFFFFFFFFUL; } DBG_INIT("%s(): ioc->dma_mask == 0x%lx\n", __FUNCTION__, dma_mask); /* ** Leaving in the multiple ioc code from parisc for the future, ** currently there are no muli-ioc mckinley sbas */ sba_dev->ioc[0].ioc_hpa = SBA_IOC_OFFSET; num_ioc = 1; sba_dev->num_ioc = num_ioc; for (i = 0; i < num_ioc; i++) { sba_dev->ioc[i].dma_mask = dma_mask; sba_dev->ioc[i].ioc_hpa += sba_dev->sba_hpa; sba_ioc_init(sba_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; sba_count++; for(i=0; i< sba_dev->num_ioc; i++) { int res_size; /* resource map size dictated by pdir_size */ res_size = sba_dev->ioc[i].pdir_size/sizeof(u64); /* entries */ 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)); 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 */ if ((sba_dev->hw_rev & 0xFF) >= 0x20) { sba_dev->ioc[i].res_hint = (unsigned long *) sba_dev->ioc[i].res_map; } else { u64 reserved_iov; /* Yet another 1.x hack */ printk("zx1 1.x: Starting resource hint offset into IOV space to avoid initial zero value IOVA\n"); sba_dev->ioc[i].res_hint = (unsigned long *) &(sba_dev->ioc[i].res_map[L1_CACHE_BYTES]); sba_dev->ioc[i].res_map[0] = 0x1; sba_dev->ioc[i].pdir_base[0] = 0x8000badbadc0ffeeULL; for (reserved_iov = 0xA0000 ; reserved_iov < 0xC0000 ; reserved_iov += IOVP_SIZE) { u64 *res_ptr = sba_dev->ioc[i].res_map; int index = PDIR_INDEX(reserved_iov); int res_word; u64 mask; res_word = (int)(index / BITS_PER_LONG); mask = 0x1UL << (index - (res_word * BITS_PER_LONG)); res_ptr[res_word] |= mask; sba_dev->ioc[i].pdir_base[PDIR_INDEX(reserved_iov)] = (0x80000000000000FFULL | reserved_iov); } }#ifdef ASSERT_PDIR_SANITY /* Mark first bit busy - ie no IOVA 0 */ sba_dev->ioc[i].res_map[0] = 0x1; sba_dev->ioc[i].pdir_base[0] = 0x8000badbadc0ffeeULL;#endif DBG_INIT("%s() %d res_map %x %p\n", __FUNCTION__, i, res_size, (void *)sba_dev->ioc[i].res_map); } sba_dev->sba_lock = SPIN_LOCK_UNLOCKED;}#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", "Hewlett Packard zx1 SBA", ((sba_dev->hw_rev >> 4) & 0xF), (sba_dev->hw_rev & 0xF) ); 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));#ifdef ALLOW_IOV_BYPASS sprintf(buf, "%spci_map_single(): %12ld bypasses\n", buf, ioc->msingle_bypass);#endif sprintf(buf, "%spci_unmap_single: %12ld calls %12ld pages (avg %d/1000)\n", buf, ioc->usingle_calls, ioc->usingle_pages, (int) ((ioc->usingle_pages * 1000)/ioc->usingle_calls));#ifdef ALLOW_IOV_BYPASS sprintf(buf, "%spci_unmap_single: %12ld bypasses\n", buf, ioc->usingle_bypass);#endif 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));#ifdef ALLOW_IOV_BYPASS sprintf(buf, "%spci_map_sg() : %12ld bypasses\n", buf, ioc->msg_bypass);#endif 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);}static intsba_resource_map(char *buf, char **start, off_t offset, int len){ struct ioc *ioc = sba_list->ioc; /* FIXME: Multi-IOC support! */ 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/*** 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.*/void __init sba_init(void){ struct sba_device *sba_dev; u32 func_id, hw_rev; u32 *func_offset = NULL; int i, agp_found = 0; static char sba_rev[6]; struct pci_dev *device = NULL; u64 hpa = 0; if (!(device = pci_find_device(PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_ZX1_SBA, NULL))) return; for (i = 0; i < PCI_NUM_RESOURCES; i++) { if (pci_resource_flags(device, i) == IORESOURCE_MEM) { hpa = ioremap(pci_resource_start(device, i), pci_resource_len(device, i)); break; } } func_id = READ_REG(hpa + SBA_FUNC_ID); if (func_id == ZX1_FUNC_ID_VALUE) { (void)strcpy(sba_rev, "zx1"); func_offset = zx1_func_offsets; } else { return; } /* Read HW Rev First */ hw_rev = READ_REG(hpa + SBA_FCLASS) & 0xFFUL; /* * Not all revision registers of the chipset are updated on every * turn. Must scan through all functions looking for the highest rev */ if (func_offset) { for (i = 0 ; func_offset[i] != -1 ; i++) { u32 func_rev; func_rev = READ_REG(hpa + SBA_FCLASS + func_offset[i]) & 0xFFUL; DBG_INIT("%s() func offset: 0x%x rev: 0x%x\n", __FUNCTION__, func_offset[i], func_rev); if (func_rev > hw_rev) hw_rev = func_rev; } } printk(KERN_INFO "%s found %s %d.%d at %s, HPA 0x%lx\n", DRIVER_NAME, sba_rev, ((hw_rev >> 4) & 0xF), (hw_rev & 0xF), device->slot_name, hpa); if ((hw_rev & 0xFF) < 0x20) { printk(KERN_INFO "%s WARNING rev 2.0 or greater will be required for IO MMU support in the future\n", DRIVER_NAME);#ifndef CONFIG_IA64_HP_PROTO panic("%s: CONFIG_IA64_HP_PROTO MUST be enabled to support SBA rev less than 2.0", DRIVER_NAME);#endif } sba_dev = kmalloc(sizeof(struct sba_device), GFP_KERNEL); if (NULL == sba_dev) { printk(KERN_ERR DRIVER_NAME " - couldn't alloc sba_device\n"); return; } 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->hw_rev = hw_rev; sba_dev->sba_hpa = hpa; /* * We need to check for an AGP device, if we find one, then only * use part of the IOVA space for PCI DMA, the rest is for GART. * REVISIT for multiple IOC. */ pci_for_each_dev(device) agp_found |= pci_find_capability(device, PCI_CAP_ID_AGP); if (agp_found && reserve_sba_gart) SBA_SET_AGP(sba_dev); sba_hw_init(sba_dev); sba_common_init(sba_dev);#ifdef CONFIG_PROC_FS { struct proc_dir_entry * proc_mckinley_root; proc_mckinley_root = proc_mkdir("bus/mckinley",0); create_proc_info_entry(sba_rev, 0, proc_mckinley_root, sba_proc_info); create_proc_info_entry("bitmap", 0, proc_mckinley_root, sba_resource_map); }#endif}static int __initnosbagart (char *str){ reserve_sba_gart = 0; return 1;}__setup("nosbagart",nosbagart);EXPORT_SYMBOL(sba_init);EXPORT_SYMBOL(sba_map_single);EXPORT_SYMBOL(sba_unmap_single);EXPORT_SYMBOL(sba_map_sg);EXPORT_SYMBOL(sba_unmap_sg);EXPORT_SYMBOL(sba_dma_address);EXPORT_SYMBOL(sba_alloc_consistent);EXPORT_SYMBOL(sba_free_consistent);/*** IA64 System Bus Adapter (SBA) I/O MMU manager**** (c) Copyright 2002 Alex Williamson** (c) Copyright 2002 Hewlett-Packard Company**** Portions (c) 2000 Grant Grundler (from parisc I/O MMU code)** Portions (c) 1999 Dave S. Miller (from sparc64 I/O MMU code)**** This program is free software; you can redistribute it and/or modify** it under the terms of the GNU General Public License as published by** the Free Software Foundation; either version 2 of the License, or** (at your option) any later version.****** This module initializes the IOC (I/O Controller) found on HP** McKinley machines and their successors.***/#include <linux/config.h>#include <linux/types.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/spinlock.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/mm.h>#include <linux/string.h>#include <linux/pci.h>#include <linux/proc_fs.h>#include <asm/delay.h> /* ia64_get_itc() */#include <asm/io.h>#include <asm/page.h> /* PAGE_OFFSET */#include <asm/efi.h>#define DRIVER_NAME "SBA"#ifndef CONFIG_IA64_HP_PROTO#define ALLOW_IOV_BYPASS#endif#define ENABLE_MARK_CLEAN/*** The number of debug flags is a clue - this code is fragile.*/#undef DEBUG_SBA_INIT#undef DEBUG_SBA_RUN#undef DEBUG_SBA_RUN_SG#undef DEBUG_SBA_RESOURCE#undef ASSERT_PDIR_SANITY#undef DEBUG_LARGE_SG_ENTRIES#undef DEBUG_BYPASS#define SBA_INLINE __inline__/* #define SBA_INLINE */#ifdef DEBUG_SBA_INIT#define DBG_INIT(x...) printk(x)#else#define DBG_INIT(x...)#endif#ifdef DEBUG_SBA_RUN#define DBG_RUN(x...) printk(x)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -