diff -u -r 70_enable_device/arch/ia64/hp/common/sba_iommu.c 80_multi_ioc/arch/ia64/hp/common/sba_iommu.c --- 70_enable_device/arch/ia64/hp/common/sba_iommu.c Sat Jul 20 16:31:07 2002 +++ 80_multi_ioc/arch/ia64/hp/common/sba_iommu.c Sat Jul 20 16:49:15 2002 @@ -29,6 +29,7 @@ #include #include #include +#include #include /* ia64_get_itc() */ #include @@ -36,11 +37,9 @@ #include -#define DRIVER_NAME "SBA" +#define PFX "IOC: " -#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. @@ -97,10 +96,6 @@ #define ASSERT(expr) #endif -#define KB(x) ((x) * 1024) -#define MB(x) (KB (KB (x))) -#define GB(x) (MB (KB (x))) - /* ** The number of pdir entries to "free" before issueing ** a read to PCOM register to flush out PCOM writes. @@ -112,28 +107,21 @@ #define DEFAULT_DMA_HINT_REG 0 -#define ZX1_FUNC_ID_VALUE ((PCI_DEVICE_ID_HP_ZX1_SBA << 16) | PCI_VENDOR_ID_HP) -#define ZX1_MC_ID ((PCI_DEVICE_ID_HP_ZX1_MC << 16) | PCI_VENDOR_ID_HP) - -#define SBA_FUNC_ID 0x0000 /* function id */ -#define SBA_FCLASS 0x0008 /* function class, bist, header, rev... */ - -#define SBA_FUNC_SIZE 0x10000 /* SBA configuration function reg set */ - -unsigned int __initdata zx1_func_offsets[] = {0x1000, 0x8000, - 0x9000, 0xa000, -1}; - -#define SBA_IOC_OFFSET 0x1000 +#define ZX1_IOC_ID ((PCI_DEVICE_ID_HP_ZX1_IOC << 16) | PCI_VENDOR_ID_HP) +#define REO_IOC_ID ((PCI_DEVICE_ID_HP_REO_IOC << 16) | PCI_VENDOR_ID_HP) -#define MAX_IOC 1 /* we only have 1 for now*/ +#define ZX1_IOC_OFFSET 0x1000 /* ACPI reports SBA, we want IOC */ +#define IOC_FUNC_ID 0x000 +#define IOC_FCLASS 0x008 /* function class, bist, header, rev... */ #define IOC_IBASE 0x300 /* IO TLB */ #define IOC_IMASK 0x308 #define IOC_PCOM 0x310 #define IOC_TCNFG 0x318 #define IOC_PDIR_BASE 0x320 -#define IOC_IOVA_SPACE_BASE 0x40000000 /* IOVA ranges start at 1GB */ +/* AGP GART driver looks for this */ +#define ZX1_SBA_IOMMU_COOKIE 0x0000badbadc0ffeeUL /* ** IOC supports 4/8/16/64KB page sizes (see TCNFG register) @@ -153,7 +141,7 @@ #define IOVP_MASK PAGE_MASK struct ioc { - unsigned long ioc_hpa; /* I/O MMU base address */ + void *ioc_hpa; /* I/O MMU base address */ char *res_map; /* resource map, bit == pdir entry */ u64 *pdir_base; /* physical base address */ unsigned long ibase; /* pdir IOV Space base */ @@ -194,36 +182,25 @@ #endif #endif - /* STUFF We don't need in performance path */ + /* Stuff we don't need in performance path */ + struct ioc *next; /* list of IOC's in system */ + acpi_handle handle; /* for multiple IOC's */ + const char *name; + unsigned int func_id; + unsigned int rev; /* HW revision of chip */ + u32 iov_size; unsigned int pdir_size; /* in bytes, determined by IOV Space size */ + struct pci_dev *sac_only_dev; }; -struct sba_device { - struct sba_device *next; /* list of SBA's in system */ - const char *name; - unsigned long sba_hpa; /* base address */ - spinlock_t sba_lock; - unsigned int flags; /* state/functionality enabled */ - unsigned int hw_rev; /* HW revision of chip */ - - unsigned int num_ioc; /* number of on-board IOC's */ - struct ioc ioc[MAX_IOC]; -}; - - -static struct sba_device *sba_list; -static int sba_count; +static struct ioc *ioc_list; static int reserve_sba_gart = 1; -static struct pci_dev sac_only_dev; #define sba_sg_iova(sg) (sg->address) #define sba_sg_len(sg) (sg->length) #define sba_sg_buffer(sg) (sg->orig_address) -/* REVISIT - fix me for multiple SBAs/IOCs */ -#define GET_IOC(dev) (sba_list->ioc) -#define SBA_SET_AGP(sba_dev) (sba_dev->flags |= 0x1) -#define SBA_GET_AGP(sba_dev) (sba_dev->flags & 0x1) +#define GET_IOC(dev) ((struct ioc *) PCI_CONTROLLER(dev)->iommu) /* ** DMA_CHUNK_SIZE is used by the SCSI mid-layer to break up @@ -234,9 +211,6 @@ */ #define DMA_CHUNK_SIZE (BITS_PER_LONG*PAGE_SIZE) -/* Looks nice and keeps the compiler happy */ -#define SBA_DEV(d) ((struct sba_device *) (d)) - #define ROUNDUP(x,y) ((x + ((y)-1)) & ~((y)-1)) /************************************ @@ -629,7 +603,7 @@ */ #if 1 -#define sba_io_pdir_entry(pdir_ptr, vba) *pdir_ptr = ((vba & ~0xE000000000000FFFULL) | 0x80000000000000FFULL) +#define sba_io_pdir_entry(pdir_ptr, vba) *pdir_ptr = ((vba & ~0xE000000000000FFFULL) | 0x8000000000000000ULL) #else void SBA_INLINE sba_io_pdir_entry(u64 *pdir_ptr, unsigned long vba) @@ -939,6 +913,7 @@ void * sba_alloc_consistent(struct pci_dev *hwdev, size_t size, dma_addr_t *dma_handle) { + struct ioc *ioc; void *ret; if (!hwdev) { @@ -956,7 +931,8 @@ * than dma_mask from the device, this needs to be * updated. */ - *dma_handle = sba_map_single(&sac_only_dev, ret, size, 0); + ioc = GET_IOC(hwdev); + *dma_handle = sba_map_single(ioc->sac_only_dev, ret, size, 0); } return ret; @@ -1401,74 +1377,70 @@ * ***************************************************************/ - -static void -sba_ioc_init(struct sba_device *sba_dev, struct ioc *ioc, int ioc_num) +static void __init +ioc_iova_init(struct ioc *ioc) { - u32 iova_space_size, iova_space_mask; - void * pdir_base; - int pdir_size, iov_order, tcnfg; + u32 iova_space_mask; + int iov_order, tcnfg; + int agp_found = 0; + struct pci_dev *device; /* - ** Firmware programs the maximum IOV space size into the imask reg + ** Firmware programs the base and size of a "safe IOVA space" + ** (one that doesn't overlap memory or LMMIO space) in the + ** IBASE and IMASK registers. */ - iova_space_size = ~(READ_REG(ioc->ioc_hpa + IOC_IMASK) & 0xFFFFFFFFUL) + 1; -#ifdef CONFIG_IA64_HP_PROTO - if (!iova_space_size) - iova_space_size = GB(1); -#endif + ioc->ibase = READ_REG(ioc->ioc_hpa + IOC_IBASE) & ~0x1UL; + ioc->iov_size = ~(READ_REG(ioc->ioc_hpa + IOC_IMASK) & 0xFFFFFFFFUL) + 1; + + if (ioc->ibase == 0) { + if (((unsigned long) ioc->ioc_hpa & 0x3000UL) == 0x2000) + ioc->ibase = 0xc0000000; + else + ioc->ibase = 0x80000000; + printk("WARNING: IBASE is zero; setting to 0x%lx\n", ioc->ibase); + } + + if (ioc->ibase < 0xfed00000UL && ioc->ibase + ioc->iov_size >= 0xfee00000UL) { + printk("WARNING: IOV space overlaps local config and interrupt message, truncating\n"); + ioc->iov_size /= 2; + } /* ** iov_order is always based on a 1GB IOVA space since we want to ** turn on the other half for AGP GART. */ - iov_order = get_order(iova_space_size >> (IOVP_SHIFT-PAGE_SHIFT)); - ioc->pdir_size = pdir_size = (iova_space_size/IOVP_SIZE) * sizeof(u64); + iov_order = get_order(ioc->iov_size >> (IOVP_SHIFT - PAGE_SHIFT)); + ioc->pdir_size = (ioc->iov_size / IOVP_SIZE) * sizeof(u64); DBG_INIT("%s() hpa 0x%lx IOV %dMB (%d bits) PDIR size 0x%0x\n", - __FUNCTION__, ioc->ioc_hpa, iova_space_size>>20, + __FUNCTION__, ioc->ioc_hpa, ioc->iov_size >> 20, iov_order + PAGE_SHIFT, ioc->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 = - pdir_base = (void *) __get_free_pages(GFP_KERNEL, get_order(pdir_size)); - if (NULL == pdir_base) - { - panic(__FILE__ ":%s() could not allocate I/O Page Table\n", __FUNCTION__); - } - memset(pdir_base, 0, pdir_size); + ioc->pdir_base = (void *) __get_free_pages(GFP_KERNEL, + get_order(ioc->pdir_size)); + if (!ioc->pdir_base) + panic(PFX "Couldn't allocate I/O Page Table\n"); + + memset(ioc->pdir_base, 0, ioc->pdir_size); DBG_INIT("%s() pdir %p size %x hint_shift_pdir %x hint_mask_pdir %lx\n", - __FUNCTION__, pdir_base, pdir_size, + __FUNCTION__, ioc->pdir_base, ioc->pdir_size, ioc->hint_shift_pdir, ioc->hint_mask_pdir); - ASSERT((((unsigned long) pdir_base) & PAGE_MASK) == (unsigned long) pdir_base); - WRITE_REG(virt_to_phys(pdir_base), ioc->ioc_hpa + IOC_PDIR_BASE); + ASSERT((((unsigned long) ioc->pdir_base) & PAGE_MASK) == (unsigned long) ioc->pdir_base); + WRITE_REG(virt_to_phys(ioc->pdir_base), ioc->ioc_hpa + IOC_PDIR_BASE); - DBG_INIT(" base %p\n", pdir_base); + DBG_INIT(" base %p\n", ioc->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 */ + ioc->imask = iova_space_mask; DBG_INIT("%s() IOV base 0x%lx mask 0x%0lx\n", __FUNCTION__, ioc->ibase, ioc->imask); @@ -1478,8 +1450,7 @@ ** 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); + WRITE_REG(ioc->imask, ioc->ioc_hpa + IOC_IMASK); /* ** Setting the upper bits makes checking for bypass addresses @@ -1489,32 +1460,28 @@ /* 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; + case 12: tcnfg = 0; break; /* 4K */ + case 13: tcnfg = 1; break; /* 8K */ + case 14: tcnfg = 2; break; /* 16K */ + case 16: tcnfg = 3; break; /* 64K */ + default: + panic(PFX "Unsupported system page size %d", + 1 << PAGE_SHIFT); break; } - WRITE_REG(tcnfg, ioc->ioc_hpa+IOC_TCNFG); + 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); + 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); + WRITE_REG(0 | 31, ioc->ioc_hpa + IOC_PCOM); /* ** If an AGP device is present, only use half of the IOV space @@ -1524,149 +1491,158 @@ ** 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__); + pci_for_each_dev(device) + agp_found |= pci_find_capability(device, PCI_CAP_ID_AGP); + + if (agp_found && reserve_sba_gart) { + DBG_INIT("%s: AGP device found, reserving half of IOVA for GART support\n", __FUNCTION__); ioc->pdir_size /= 2; - ((u64 *)pdir_base)[PDIR_INDEX(iova_space_size/2)] = 0x0000badbadc0ffeeULL; + ((u64 *)ioc->pdir_base)[PDIR_INDEX(ioc->iov_size/2)] = ZX1_SBA_IOMMU_COOKIE; } - - DBG_INIT("%s() DONE\n", __FUNCTION__); } +static void __init +ioc_resource_init(struct ioc *ioc) +{ + spin_lock_init(&ioc->res_lock); + /* resource map size dictated by pdir_size */ + ioc->res_size = ioc->pdir_size / sizeof(u64); /* entries */ + ioc->res_size >>= 3; /* convert bit count to byte count */ + DBG_INIT("%s() res_size 0x%x\n", __FUNCTION__, ioc->res_size); -/************************************************************************** -** -** SBA initialization code (HW and SW) -** -** o identify SBA chip itself -** o FIXME: initialize DMA hints for reasonable defaults -** -**************************************************************************/ + ioc->res_map = (char *) __get_free_pages(GFP_KERNEL, + get_order(ioc->res_size)); + if (!ioc->res_map) + panic(PFX "Couldn't allocate resource map\n"); -static void -sba_hw_init(struct sba_device *sba_dev) -{ - int i; - int num_ioc; - u64 dma_mask; - u32 func_id; + memset(ioc->res_map, 0, ioc->res_size); + /* next available IOVP - circular search */ + ioc->res_hint = (unsigned long *) ioc->res_map; - /* - ** 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); +#ifdef ASSERT_PDIR_SANITY + /* Mark first bit busy - ie no IOVA 0 */ + ioc->res_map[0] = 0x1; + ioc->pdir_base[0] = 0x8000000000000000ULL | ZX1_SBA_IOMMU_COOKIE; +#endif - if (func_id == ZX1_FUNC_ID_VALUE) { - dma_mask = 0xFFFFFFFFFFUL; - } else { - dma_mask = 0xFFFFFFFFFFFFFFFFUL; - } + DBG_INIT("%s() res_map %x %p\n", __FUNCTION__, + ioc->res_size, (void *) ioc->res_map); +} + +static void __init +ioc_sac_init(struct ioc *ioc) +{ + struct pci_dev *sac = NULL; + struct pci_controller *controller = NULL; - 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; + * pci_alloc_consistent() must return a DMA address which is + * SAC (single address cycle) addressable, so allocate a + * pseudo-device to enforce that. + */ + sac = kmalloc(sizeof(*sac), GFP_KERNEL); + if (!sac) + panic(PFX "Couldn't allocate struct pci_dev"); + memset(sac, 0, sizeof(*sac)); - 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); - } + controller = kmalloc(sizeof(*controller), GFP_KERNEL); + if (!controller) + panic(PFX "Couldn't allocate struct pci_controller"); + memset(controller, 0, sizeof(*controller)); + + controller->iommu = ioc; + sac->sysdata = controller; + sac->dma_mask = 0xFFFFFFFFUL; + ioc->sac_only_dev = sac; } -static void -sba_common_init(struct sba_device *sba_dev) +static void __init +ioc_zx1_init(struct ioc *ioc) { - int i; + if (ioc->rev < 0x20) + panic(PFX "IOC 2.0 or later required for IOMMU support\n"); - /* 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__ ); - } + ioc->dma_mask = 0xFFFFFFFFFFUL; +} - 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 = (u64 *) 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); +typedef void (initfunc)(struct ioc *); - } - } +struct ioc_iommu { + u32 func_id; + char *name; + initfunc *init; +}; -#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 +static struct ioc_iommu ioc_iommu_info[] __initdata = { + { ZX1_IOC_ID, "zx1", ioc_zx1_init }, + { REO_IOC_ID, "REO" }, +}; + +static struct ioc * __init +ioc_init(u64 hpa, void *handle) +{ + struct ioc *ioc; + struct ioc_iommu *info; + + ioc = kmalloc(sizeof(*ioc), GFP_KERNEL); + if (!ioc) + return NULL; + + memset(ioc, 0, sizeof(*ioc)); + + ioc->next = ioc_list; + ioc_list = ioc; + + ioc->handle = handle; + ioc->ioc_hpa = ioremap(hpa, 0x1000); - DBG_INIT("%s() %d res_map %x %p\n", __FUNCTION__, - i, res_size, (void *)sba_dev->ioc[i].res_map); + ioc->func_id = READ_REG(ioc->ioc_hpa + IOC_FUNC_ID); + ioc->rev = READ_REG(ioc->ioc_hpa + IOC_FCLASS) & 0xFFUL; + ioc->dma_mask = 0xFFFFFFFFFFFFFFFFUL; /* conservative */ + + for (info = ioc_iommu_info; info < ioc_iommu_info + ARRAY_SIZE(ioc_iommu_info); info++) { + if (ioc->func_id == info->func_id) { + ioc->name = info->name; + if (info->init) + (info->init)(ioc); + } } + if (!ioc->name) + ioc->name = "Unknown"; + + ioc_iova_init(ioc); + ioc_resource_init(ioc); + ioc_sac_init(ioc); + + printk(KERN_INFO PFX + "Found %s IOC %d.%d HPA 0x%lx IOVA space %dMb at 0x%lx\n", + ioc->name, (ioc->rev >> 4) & 0xF, ioc->rev & 0xF, + hpa, ioc->iov_size >> 20, ioc->ibase); - sba_dev->sba_lock = SPIN_LOCK_UNLOCKED; + return ioc; } + + +/************************************************************************** +** +** SBA initialization code (HW and SW) +** +** o identify SBA chip itself +** o FIXME: initialize DMA hints for reasonable defaults +** +**************************************************************************/ + #ifdef CONFIG_PROC_FS -static int sba_proc_info(char *buf, char **start, off_t offset, int len) +static int +sba_proc_info_one(char *buf, struct ioc *ioc) { - 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, "Hewlett Packard %s IOC rev %d.%d\n", + ioc->name, ((ioc->rev >> 4) & 0xF), (ioc->rev & 0xF)); sprintf(buf, "%sIO PDIR size : %d bytes (%d entries)\n", buf, (int) ((ioc->res_size << 3) * sizeof(u64)), /* 8 bits/byte */ @@ -1721,9 +1697,21 @@ } static int -sba_resource_map(char *buf, char **start, off_t offset, int len) +sba_proc_info(char *buf, char **start, off_t offset, int len) +{ + struct ioc *ioc; + char *base = buf; + + for (ioc = ioc_list; ioc; ioc = ioc->next) { + buf += sba_proc_info_one(buf, ioc); + } + + return strlen(base); +} + +static int +sba_resource_map_one(char *buf, struct ioc *ioc) { - struct ioc *ioc = sba_list->ioc; /* FIXME: Multi-IOC support! */ unsigned int *res_ptr = (unsigned int *)ioc->res_map; int i; @@ -1737,126 +1725,136 @@ 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) +static int +sba_resource_map(char *buf, char **start, off_t offset, int len) { - 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; + struct ioc *ioc; + char *base = buf; - for (i = 0; i < PCI_NUM_RESOURCES; i++) { - if (pci_resource_flags(device, i) == IORESOURCE_MEM) { - hpa = (u64) ioremap(pci_resource_start(device, i), - pci_resource_len(device, i)); - break; - } + for (ioc = ioc_list; ioc; ioc = ioc->next) { + buf += sba_resource_map_one(buf, ioc); } - func_id = READ_REG(hpa + SBA_FUNC_ID); + return strlen(base); +} +#endif - if (func_id == ZX1_FUNC_ID_VALUE) { - (void)strcpy(sba_rev, "zx1"); - func_offset = zx1_func_offsets; - } else { - return; - } +void +sba_enable_device(struct pci_dev *dev) +{ + acpi_handle handle, parent; + acpi_status status; + struct ioc *ioc; - /* Read HW Rev First */ - hw_rev = READ_REG(hpa + SBA_FCLASS) & 0xFFUL; + handle = PCI_CONTROLLER(dev)->acpi_handle; + if (!handle) + return; /* - * Not all revision registers of the chipset are updated on every - * turn. Must scan through all functions looking for the highest rev + * The IOC scope encloses PCI root bridges in the ACPI + * namespace, so work our way out until we find an IOC we + * claimed previously. */ - 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; - } - } + do { + for (ioc = ioc_list; ioc; ioc = ioc->next) + if (ioc->handle == handle) { + PCI_CONTROLLER(dev)->iommu = ioc; + return; + } - 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); + status = acpi_get_parent(handle, &parent); + handle = parent; + } while (ACPI_SUCCESS(status)); - 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 - } + printk("No IOC for %s in ACPI\n", dev->slot_name); +} - 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; - } +static int __init +acpi_sba_ioc_add(struct acpi_device *device) +{ + struct ioc *ioc; + acpi_status status; + u64 hpa, length; + + /* + * Only SBA appears in ACPI namespace. It encloses the PCI + * root bridges, and its CSR space includes the IOC function. + */ + status = acpi_hp_csr_space(device->handle, &hpa, &length); + if (ACPI_FAILURE(status)) + return 1; + ioc = ioc_init(hpa + ZX1_IOC_OFFSET, device->handle); + if (!ioc) + return 1; + + return 0; +} - memset(sba_dev, 0, sizeof(struct sba_device)); +static int __init +acpi_ioc_add(struct acpi_device *device) +{ + struct ioc *ioc; + acpi_status status; + u64 hpa, length; - for(i=0; iioc[i].res_lock)); + status = acpi_hp_csr_space(device->handle, &hpa, &length); + if (ACPI_FAILURE(status)) + return 1; + ioc = ioc_init(hpa, device->handle); + if (!ioc) + return 1; - sba_dev->hw_rev = hw_rev; - sba_dev->sba_hpa = hpa; + return 0; +} - /* - * We pass this fake device from alloc_consistent to ensure - * we only use SAC for alloc_consistent mappings. - */ - sac_only_dev.dma_mask = 0xFFFFFFFFUL; +static struct acpi_driver acpi_sba_ioc_driver = { + name: "IOC IOMMU Driver", + ids: "HWP0001", + ops: { + add: acpi_sba_ioc_add, + }, +}; - /* - * 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); +static struct acpi_driver acpi_ioc_driver = { + name: "IOC IOMMU Driver", + ids: "HWP0004", + ops: { + add: acpi_ioc_add, + }, +}; - if (agp_found && reserve_sba_gart) - SBA_SET_AGP(sba_dev); +void __init +ioc_acpi_init(void) +{ + acpi_bus_register_driver(&acpi_sba_ioc_driver); + acpi_bus_register_driver(&acpi_ioc_driver); +} - sba_hw_init(sba_dev); - sba_common_init(sba_dev); +void __init +sba_init(void) +{ + ioc_acpi_init(); #ifdef CONFIG_PROC_FS - { + if (ioc_list) { 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(ioc_list->name, 0, proc_mckinley_root, sba_proc_info); create_proc_info_entry("bitmap", 0, proc_mckinley_root, sba_resource_map); } #endif } static int __init -nosbagart (char *str) +nosbagart(char *str) { reserve_sba_gart = 0; return 1; } -__setup("nosbagart",nosbagart); +__setup("nosbagart", nosbagart); EXPORT_SYMBOL(sba_init); EXPORT_SYMBOL(sba_map_single); diff -u -r 70_enable_device/arch/ia64/hp/zx1/hpzx1_misc.c 80_multi_ioc/arch/ia64/hp/zx1/hpzx1_misc.c --- 70_enable_device/arch/ia64/hp/zx1/hpzx1_misc.c Sat Jul 20 16:33:47 2002 +++ 80_multi_ioc/arch/ia64/hp/zx1/hpzx1_misc.c Sat Jul 20 16:48:53 2002 @@ -182,74 +182,6 @@ hpzx1_devices++; } -typedef struct { - u8 guid_id; - u8 guid[16]; - u8 csr_base[8]; - u8 csr_length[8]; -} acpi_hp_vendor_long; - -#define HP_CCSR_LENGTH 0x21 -#define HP_CCSR_TYPE 0x2 -#define HP_CCSR_GUID EFI_GUID(0x69e9adf9, 0x924f, 0xab5f, \ - 0xf6, 0x4a, 0x24, 0xd2, 0x01, 0x37, 0x0e, 0xad) - -extern acpi_status acpi_get_crs(acpi_handle, acpi_buffer *); -extern acpi_resource *acpi_get_crs_next(acpi_buffer *, int *); -extern acpi_resource_data *acpi_get_crs_type(acpi_buffer *, int *, int); -extern void acpi_dispose_crs(acpi_buffer *); - -static acpi_status -hp_csr_space(acpi_handle obj, u64 *csr_base, u64 *csr_length) -{ - int i, offset = 0; - acpi_status status; - acpi_buffer buf; - acpi_resource_vendor *res; - acpi_hp_vendor_long *hp_res; - efi_guid_t vendor_guid; - - *csr_base = 0; - *csr_length = 0; - - status = acpi_get_crs(obj, &buf); - if (ACPI_FAILURE(status)) { - printk(KERN_ERR PFX "Unable to get _CRS data on object\n"); - return status; - } - - res = (acpi_resource_vendor *)acpi_get_crs_type(&buf, &offset, ACPI_RSTYPE_VENDOR); - if (!res) { - printk(KERN_ERR PFX "Failed to find config space for device\n"); - acpi_dispose_crs(&buf); - return AE_NOT_FOUND; - } - - hp_res = (acpi_hp_vendor_long *)(res->reserved); - - if (res->length != HP_CCSR_LENGTH || hp_res->guid_id != HP_CCSR_TYPE) { - printk(KERN_ERR PFX "Unknown Vendor data\n"); - acpi_dispose_crs(&buf); - return AE_TYPE; /* Revisit error? */ - } - - memcpy(&vendor_guid, hp_res->guid, sizeof(efi_guid_t)); - if (efi_guidcmp(vendor_guid, HP_CCSR_GUID) != 0) { - printk(KERN_ERR PFX "Vendor GUID does not match\n"); - acpi_dispose_crs(&buf); - return AE_TYPE; /* Revisit error? */ - } - - for (i = 0 ; i < 8 ; i++) { - *csr_base |= ((u64)(hp_res->csr_base[i]) << (i * 8)); - *csr_length |= ((u64)(hp_res->csr_length[i]) << (i * 8)); - } - - acpi_dispose_crs(&buf); - - return AE_OK; -} - static acpi_status hpzx1_sba_probe(acpi_handle obj, u32 depth, void *context, void **ret) { @@ -258,7 +190,7 @@ char *name = context; char fullname[16]; - status = hp_csr_space(obj, &csr_base, &csr_length); + status = acpi_hp_csr_space(obj, &csr_base, &csr_length); if (ACPI_FAILURE(status)) return status; @@ -284,7 +216,7 @@ char *name = context; char fullname[32]; - status = hp_csr_space(obj, &csr_base, &csr_length); + status = acpi_hp_csr_space(obj, &csr_base, &csr_length); if (ACPI_FAILURE(status)) return status; diff -u -r 70_enable_device/arch/ia64/kernel/acpi.c 80_multi_ioc/arch/ia64/kernel/acpi.c --- 70_enable_device/arch/ia64/kernel/acpi.c Sat Jul 20 16:35:33 2002 +++ 80_multi_ioc/arch/ia64/kernel/acpi.c Sat Jul 20 16:48:53 2002 @@ -236,6 +236,69 @@ return AE_OK; } + +typedef struct { + u8 guid_id; + u8 guid[16]; + u8 csr_base[8]; + u8 csr_length[8]; +} acpi_hp_vendor_long; + +#define HP_CCSR_LENGTH 0x21 +#define HP_CCSR_TYPE 0x2 +#define HP_CCSR_GUID EFI_GUID(0x69e9adf9, 0x924f, 0xab5f, \ + 0xf6, 0x4a, 0x24, 0xd2, 0x01, 0x37, 0x0e, 0xad) + +acpi_status +acpi_hp_csr_space(acpi_handle obj, u64 *csr_base, u64 *csr_length) +{ + int i, offset = 0; + acpi_status status; + acpi_buffer buf; + acpi_resource_vendor *res; + acpi_hp_vendor_long *hp_res; + efi_guid_t vendor_guid; + + *csr_base = 0; + *csr_length = 0; + + status = acpi_get_crs(obj, &buf); + if (ACPI_FAILURE(status)) { + printk(KERN_ERR PREFIX "Unable to get _CRS data on object\n"); + return status; + } + + res = (acpi_resource_vendor *)acpi_get_crs_type(&buf, &offset, ACPI_RSTYPE_VENDOR); + if (!res) { + printk(KERN_ERR PREFIX "Failed to find config space for device\n"); + acpi_dispose_crs(&buf); + return AE_NOT_FOUND; + } + + hp_res = (acpi_hp_vendor_long *)(res->reserved); + + if (res->length != HP_CCSR_LENGTH || hp_res->guid_id != HP_CCSR_TYPE) { + printk(KERN_ERR PREFIX "Unknown Vendor data\n"); + acpi_dispose_crs(&buf); + return AE_TYPE; /* Revisit error? */ + } + + memcpy(&vendor_guid, hp_res->guid, sizeof(efi_guid_t)); + if (efi_guidcmp(vendor_guid, HP_CCSR_GUID) != 0) { + printk(KERN_ERR PREFIX "Vendor GUID does not match\n"); + acpi_dispose_crs(&buf); + return AE_TYPE; /* Revisit error? */ + } + + for (i = 0 ; i < 8 ; i++) { + *csr_base |= ((u64)(hp_res->csr_base[i]) << (i * 8)); + *csr_length |= ((u64)(hp_res->csr_length[i]) << (i * 8)); + } + + acpi_dispose_crs(&buf); + + return AE_OK; +} #endif /* CONFIG_ACPI */ #ifdef CONFIG_ACPI_BOOT diff -u -r 70_enable_device/include/asm-ia64/machvec_hpzx1.h 80_multi_ioc/include/asm-ia64/machvec_hpzx1.h --- 70_enable_device/include/asm-ia64/machvec_hpzx1.h Sat Jul 20 16:31:09 2002 +++ 80_multi_ioc/include/asm-ia64/machvec_hpzx1.h Sat Jul 20 16:48:53 2002 @@ -3,6 +3,7 @@ extern ia64_mv_setup_t dig_setup; extern ia64_mv_pci_fixup_t hpzx1_pci_fixup; +extern ia64_mv_pci_enable_device_t sba_enable_device; extern ia64_mv_map_nr_t map_nr_dense; extern ia64_mv_pci_alloc_consistent sba_alloc_consistent; extern ia64_mv_pci_free_consistent sba_free_consistent; @@ -23,6 +24,7 @@ #define platform_name "hpzx1" #define platform_setup dig_setup #define platform_pci_fixup hpzx1_pci_fixup +#define platform_pci_enable_device sba_enable_device #define platform_map_nr map_nr_dense #define platform_pci_dma_init ((ia64_mv_pci_dma_init *) machvec_noop) #define platform_pci_alloc_consistent sba_alloc_consistent diff -u -r 70_enable_device/include/asm-ia64/pci.h 80_multi_ioc/include/asm-ia64/pci.h --- 70_enable_device/include/asm-ia64/pci.h Sat Jul 20 16:35:33 2002 +++ 80_multi_ioc/include/asm-ia64/pci.h Sat Jul 20 16:48:53 2002 @@ -84,6 +84,7 @@ struct pci_controller { void *acpi_handle; + void *iommu; int segment; u64 mem_offset; diff -u -r 70_enable_device/include/linux/acpi.h 80_multi_ioc/include/linux/acpi.h --- 70_enable_device/include/linux/acpi.h Sat Jul 20 16:35:33 2002 +++ 80_multi_ioc/include/linux/acpi.h Sat Jul 20 16:48:53 2002 @@ -36,6 +36,7 @@ * isn't the right answer either. Please just ignore it for now. */ #include "../../drivers/acpi/include/acpi.h" +#include "../../drivers/acpi/acpi_drivers.h" #include @@ -378,6 +379,7 @@ int acpi_init(void); int acpi_irq_to_vector(u32 irq); +acpi_status acpi_hp_csr_space (acpi_handle, u64 *base, u64 *length); acpi_status acpi_get_addr_space(acpi_handle, u8 type, u64 *base, u64 *length, u64 *tra); diff -u -r 70_enable_device/include/linux/pci_ids.h 80_multi_ioc/include/linux/pci_ids.h --- 70_enable_device/include/linux/pci_ids.h Sat Jul 20 16:31:10 2002 +++ 80_multi_ioc/include/linux/pci_ids.h Sat Jul 20 16:48:53 2002 @@ -505,6 +505,8 @@ #define PCI_DEVICE_ID_HP_DIVA1 0x1049 #define PCI_DEVICE_ID_HP_DIVA2 0x104A #define PCI_DEVICE_ID_HP_SP2_0 0x104B +#define PCI_DEVICE_ID_HP_REO_SBA 0x10f0 +#define PCI_DEVICE_ID_HP_REO_IOC 0x10f1 #define PCI_DEVICE_ID_HP_ZX1_SBA 0x1229 #define PCI_DEVICE_ID_HP_ZX1_IOC 0x122a #define PCI_DEVICE_ID_HP_ZX1_LBA 0x122e