diff options
Diffstat (limited to 'arch/sparc64/kernel/pci_sun4v.c')
| -rw-r--r-- | arch/sparc64/kernel/pci_sun4v.c | 1209 |
1 files changed, 0 insertions, 1209 deletions
diff --git a/arch/sparc64/kernel/pci_sun4v.c b/arch/sparc64/kernel/pci_sun4v.c deleted file mode 100644 index 03ad4c06758..00000000000 --- a/arch/sparc64/kernel/pci_sun4v.c +++ /dev/null @@ -1,1209 +0,0 @@ -/* pci_sun4v.c: SUN4V specific PCI controller support. - * - * Copyright (C) 2006 David S. Miller (davem@davemloft.net) - */ - -#include <linux/kernel.h> -#include <linux/types.h> -#include <linux/pci.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/interrupt.h> -#include <linux/percpu.h> - -#include <asm/pbm.h> -#include <asm/iommu.h> -#include <asm/irq.h> -#include <asm/upa.h> -#include <asm/pstate.h> -#include <asm/oplib.h> -#include <asm/hypervisor.h> -#include <asm/prom.h> - -#include "pci_impl.h" -#include "iommu_common.h" - -#include "pci_sun4v.h" - -#define PGLIST_NENTS (PAGE_SIZE / sizeof(u64)) - -struct pci_iommu_batch { - struct pci_dev *pdev; /* Device mapping is for. */ - unsigned long prot; /* IOMMU page protections */ - unsigned long entry; /* Index into IOTSB. */ - u64 *pglist; /* List of physical pages */ - unsigned long npages; /* Number of pages in list. */ -}; - -static DEFINE_PER_CPU(struct pci_iommu_batch, pci_iommu_batch); - -/* Interrupts must be disabled. */ -static inline void pci_iommu_batch_start(struct pci_dev *pdev, unsigned long prot, unsigned long entry) -{ - struct pci_iommu_batch *p = &__get_cpu_var(pci_iommu_batch); - - p->pdev = pdev; - p->prot = prot; - p->entry = entry; - p->npages = 0; -} - -/* Interrupts must be disabled. */ -static long pci_iommu_batch_flush(struct pci_iommu_batch *p) -{ - struct pcidev_cookie *pcp = p->pdev->sysdata; - unsigned long devhandle = pcp->pbm->devhandle; - unsigned long prot = p->prot; - unsigned long entry = p->entry; - u64 *pglist = p->pglist; - unsigned long npages = p->npages; - - while (npages != 0) { - long num; - - num = pci_sun4v_iommu_map(devhandle, HV_PCI_TSBID(0, entry), - npages, prot, __pa(pglist)); - if (unlikely(num < 0)) { - if (printk_ratelimit()) - printk("pci_iommu_batch_flush: IOMMU map of " - "[%08lx:%08lx:%lx:%lx:%lx] failed with " - "status %ld\n", - devhandle, HV_PCI_TSBID(0, entry), - npages, prot, __pa(pglist), num); - return -1; - } - - entry += num; - npages -= num; - pglist += num; - } - - p->entry = entry; - p->npages = 0; - - return 0; -} - -/* Interrupts must be disabled. */ -static inline long pci_iommu_batch_add(u64 phys_page) -{ - struct pci_iommu_batch *p = &__get_cpu_var(pci_iommu_batch); - - BUG_ON(p->npages >= PGLIST_NENTS); - - p->pglist[p->npages++] = phys_page; - if (p->npages == PGLIST_NENTS) - return pci_iommu_batch_flush(p); - - return 0; -} - -/* Interrupts must be disabled. */ -static inline long pci_iommu_batch_end(void) -{ - struct pci_iommu_batch *p = &__get_cpu_var(pci_iommu_batch); - - BUG_ON(p->npages >= PGLIST_NENTS); - - return pci_iommu_batch_flush(p); -} - -static long pci_arena_alloc(struct pci_iommu_arena *arena, unsigned long npages) -{ - unsigned long n, i, start, end, limit; - int pass; - - limit = arena->limit; - start = arena->hint; - pass = 0; - -again: - n = find_next_zero_bit(arena->map, limit, start); - end = n + npages; - if (unlikely(end >= limit)) { - if (likely(pass < 1)) { - limit = start; - start = 0; - pass++; - goto again; - } else { - /* Scanned the whole thing, give up. */ - return -1; - } - } - - for (i = n; i < end; i++) { - if (test_bit(i, arena->map)) { - start = i + 1; - goto again; - } - } - - for (i = n; i < end; i++) - __set_bit(i, arena->map); - - arena->hint = end; - - return n; -} - -static void pci_arena_free(struct pci_iommu_arena *arena, unsigned long base, unsigned long npages) -{ - unsigned long i; - - for (i = base; i < (base + npages); i++) - __clear_bit(i, arena->map); -} - -static void *pci_4v_alloc_consistent(struct pci_dev *pdev, size_t size, dma_addr_t *dma_addrp, gfp_t gfp) -{ - struct pcidev_cookie *pcp; - struct pci_iommu *iommu; - unsigned long flags, order, first_page, npages, n; - void *ret; - long entry; - - size = IO_PAGE_ALIGN(size); - order = get_order(size); - if (unlikely(order >= MAX_ORDER)) - return NULL; - - npages = size >> IO_PAGE_SHIFT; - - first_page = __get_free_pages(gfp, order); - if (unlikely(first_page == 0UL)) - return NULL; - - memset((char *)first_page, 0, PAGE_SIZE << order); - - pcp = pdev->sysdata; - iommu = pcp->pbm->iommu; - - spin_lock_irqsave(&iommu->lock, flags); - entry = pci_arena_alloc(&iommu->arena, npages); - spin_unlock_irqrestore(&iommu->lock, flags); - - if (unlikely(entry < 0L)) - goto arena_alloc_fail; - - *dma_addrp = (iommu->page_table_map_base + - (entry << IO_PAGE_SHIFT)); - ret = (void *) first_page; - first_page = __pa(first_page); - - local_irq_save(flags); - - pci_iommu_batch_start(pdev, - (HV_PCI_MAP_ATTR_READ | - HV_PCI_MAP_ATTR_WRITE), - entry); - - for (n = 0; n < npages; n++) { - long err = pci_iommu_batch_add(first_page + (n * PAGE_SIZE)); - if (unlikely(err < 0L)) - goto iommu_map_fail; - } - - if (unlikely(pci_iommu_batch_end() < 0L)) - goto iommu_map_fail; - - local_irq_restore(flags); - - return ret; - -iommu_map_fail: - /* Interrupts are disabled. */ - spin_lock(&iommu->lock); - pci_arena_free(&iommu->arena, entry, npages); - spin_unlock_irqrestore(&iommu->lock, flags); - -arena_alloc_fail: - free_pages(first_page, order); - return NULL; -} - -static void pci_4v_free_consistent(struct pci_dev *pdev, size_t size, void *cpu, dma_addr_t dvma) -{ - struct pcidev_cookie *pcp; - struct pci_iommu *iommu; - unsigned long flags, order, npages, entry; - u32 devhandle; - - npages = IO_PAGE_ALIGN(size) >> IO_PAGE_SHIFT; - pcp = pdev->sysdata; - iommu = pcp->pbm->iommu; - devhandle = pcp->pbm->devhandle; - entry = ((dvma - iommu->page_table_map_base) >> IO_PAGE_SHIFT); - - spin_lock_irqsave(&iommu->lock, flags); - - pci_arena_free(&iommu->arena, entry, npages); - - do { - unsigned long num; - - num = pci_sun4v_iommu_demap(devhandle, HV_PCI_TSBID(0, entry), - npages); - entry += num; - npages -= num; - } while (npages != 0); - - spin_unlock_irqrestore(&iommu->lock, flags); - - order = get_order(size); - if (order < 10) - free_pages((unsigned long)cpu, order); -} - -static dma_addr_t pci_4v_map_single(struct pci_dev *pdev, void *ptr, size_t sz, int direction) -{ - struct pcidev_cookie *pcp; - struct pci_iommu *iommu; - unsigned long flags, npages, oaddr; - unsigned long i, base_paddr; - u32 bus_addr, ret; - unsigned long prot; - long entry; - - pcp = pdev->sysdata; - iommu = pcp->pbm->iommu; - - if (unlikely(direction == PCI_DMA_NONE)) - goto bad; - - oaddr = (unsigned long)ptr; - npages = IO_PAGE_ALIGN(oaddr + sz) - (oaddr & IO_PAGE_MASK); - npages >>= IO_PAGE_SHIFT; - - spin_lock_irqsave(&iommu->lock, flags); - entry = pci_arena_alloc(&iommu->arena, npages); - spin_unlock_irqrestore(&iommu->lock, flags); - - if (unlikely(entry < 0L)) - goto bad; - - bus_addr = (iommu->page_table_map_base + - (entry << IO_PAGE_SHIFT)); - ret = bus_addr | (oaddr & ~IO_PAGE_MASK); - base_paddr = __pa(oaddr & IO_PAGE_MASK); - prot = HV_PCI_MAP_ATTR_READ; - if (direction != PCI_DMA_TODEVICE) - prot |= HV_PCI_MAP_ATTR_WRITE; - - local_irq_save(flags); - - pci_iommu_batch_start(pdev, prot, entry); - - for (i = 0; i < npages; i++, base_paddr += IO_PAGE_SIZE) { - long err = pci_iommu_batch_add(base_paddr); - if (unlikely(err < 0L)) - goto iommu_map_fail; - } - if (unlikely(pci_iommu_batch_end() < 0L)) - goto iommu_map_fail; - - local_irq_restore(flags); - - return ret; - -bad: - if (printk_ratelimit()) - WARN_ON(1); - return PCI_DMA_ERROR_CODE; - -iommu_map_fail: - /* Interrupts are disabled. */ - spin_lock(&iommu->lock); - pci_arena_free(&iommu->arena, entry, npages); - spin_unlock_irqrestore(&iommu->lock, flags); - - return PCI_DMA_ERROR_CODE; -} - -static void pci_4v_unmap_single(struct pci_dev *pdev, dma_addr_t bus_addr, size_t sz, int direction) -{ - struct pcidev_cookie *pcp; - struct pci_iommu *iommu; - unsigned long flags, npages; - long entry; - u32 devhandle; - - if (unlikely(direction == PCI_DMA_NONE)) { - if (printk_ratelimit()) - WARN_ON(1); - return; - } - - pcp = pdev->sysdata; - iommu = pcp->pbm->iommu; - devhandle = pcp->pbm->devhandle; - - npages = IO_PAGE_ALIGN(bus_addr + sz) - (bus_addr & IO_PAGE_MASK); - npages >>= IO_PAGE_SHIFT; - bus_addr &= IO_PAGE_MASK; - - spin_lock_irqsave(&iommu->lock, flags); - - entry = (bus_addr - iommu->page_table_map_base) >> IO_PAGE_SHIFT; - pci_arena_free(&iommu->arena, entry, npages); - - do { - unsigned long num; - - num = pci_sun4v_iommu_demap(devhandle, HV_PCI_TSBID(0, entry), - npages); - entry += num; - npages -= num; - } while (npages != 0); - - spin_unlock_irqrestore(&iommu->lock, flags); -} - -#define SG_ENT_PHYS_ADDRESS(SG) \ - (__pa(page_address((SG)->page)) + (SG)->offset) - -static inline long fill_sg(long entry, struct pci_dev *pdev, - struct scatterlist *sg, - int nused, int nelems, unsigned long prot) -{ - struct scatterlist *dma_sg = sg; - struct scatterlist *sg_end = sg + nelems; - unsigned long flags; - int i; - - local_irq_save(flags); - - pci_iommu_batch_start(pdev, prot, entry); - - for (i = 0; i < nused; i++) { - unsigned long pteval = ~0UL; - u32 dma_npages; - - dma_npages = ((dma_sg->dma_address & (IO_PAGE_SIZE - 1UL)) + - dma_sg->dma_length + - ((IO_PAGE_SIZE - 1UL))) >> IO_PAGE_SHIFT; - do { - unsigned long offset; - signed int len; - - /* If we are here, we know we have at least one - * more page to map. So walk forward until we - * hit a page crossing, and begin creating new - * mappings from that spot. - */ - for (;;) { - unsigned long tmp; - - tmp = SG_ENT_PHYS_ADDRESS(sg); - len = sg->length; - if (((tmp ^ pteval) >> IO_PAGE_SHIFT) != 0UL) { - pteval = tmp & IO_PAGE_MASK; - offset = tmp & (IO_PAGE_SIZE - 1UL); - break; - } - if (((tmp ^ (tmp + len - 1UL)) >> IO_PAGE_SHIFT) != 0UL) { - pteval = (tmp + IO_PAGE_SIZE) & IO_PAGE_MASK; - offset = 0UL; - len -= (IO_PAGE_SIZE - (tmp & (IO_PAGE_SIZE - 1UL))); - break; - } - sg++; - } - - pteval = (pteval & IOPTE_PAGE); - while (len > 0) { - long err; - - err = pci_iommu_batch_add(pteval); - if (unlikely(err < 0L)) - goto iommu_map_failed; - - pteval += IO_PAGE_SIZE; - len -= (IO_PAGE_SIZE - offset); - offset = 0; - dma_npages--; - } - - pteval = (pteval & IOPTE_PAGE) + len; - sg++; - - /* Skip over any tail mappings we've fully mapped, - * adjusting pteval along the way. Stop when we - * detect a page crossing event. - */ - while (sg < sg_end && - (pteval << (64 - IO_PAGE_SHIFT)) != 0UL && - (pteval == SG_ENT_PHYS_ADDRESS(sg)) && - ((pteval ^ - (SG_ENT_PHYS_ADDRESS(sg) + sg->length - 1UL)) >> IO_PAGE_SHIFT) == 0UL) { - pteval += sg->length; - sg++; - } - if ((pteval << (64 - IO_PAGE_SHIFT)) == 0UL) - pteval = ~0UL; - } while (dma_npages != 0); - dma_sg++; - } - - if (unlikely(pci_iommu_batch_end() < 0L)) - goto iommu_map_failed; - - local_irq_restore(flags); - return 0; - -iommu_map_failed: - local_irq_restore(flags); - return -1L; -} - -static int pci_4v_map_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, int direction) -{ - struct pcidev_cookie *pcp; - struct pci_iommu *iommu; - unsigned long flags, npages, prot; - u32 dma_base; - struct scatterlist *sgtmp; - long entry, err; - int used; - - /* Fast path single entry scatterlists. */ - if (nelems == 1) { - sglist->dma_address = - pci_4v_map_single(pdev, - (page_address(sglist->page) + sglist->offset), - sglist->length, direction); - if (unlikely(sglist->dma_address == PCI_DMA_ERROR_CODE)) - return 0; - sglist->dma_length = sglist->length; - return 1; - } - - pcp = pdev->sysdata; - iommu = pcp->pbm->iommu; - - if (unlikely(direction == PCI_DMA_NONE)) - goto bad; - - /* Step 1: Prepare scatter list. */ - npages = prepare_sg(sglist, nelems); - - /* Step 2: Allocate a cluster and context, if necessary. */ - spin_lock_irqsave(&iommu->lock, flags); - entry = pci_arena_alloc(&iommu->arena, npages); - spin_unlock_irqrestore(&iommu->lock, flags); - - if (unlikely(entry < 0L)) - goto bad; - - dma_base = iommu->page_table_map_base + - (entry << IO_PAGE_SHIFT); - - /* Step 3: Normalize DMA addresses. */ - used = nelems; - - sgtmp = sglist; - while (used && sgtmp->dma_length) { - sgtmp->dma_address += dma_base; - sgtmp++; - used--; - } - used = nelems - used; - - /* Step 4: Create the mappings. */ - prot = HV_PCI_MAP_ATTR_READ; - if (direction != PCI_DMA_TODEVICE) - prot |= HV_PCI_MAP_ATTR_WRITE; - - err = fill_sg(entry, pdev, sglist, used, nelems, prot); - if (unlikely(err < 0L)) - goto iommu_map_failed; - - return used; - -bad: - if (printk_ratelimit()) - WARN_ON(1); - return 0; - -iommu_map_failed: - spin_lock_irqsave(&iommu->lock, flags); - pci_arena_free(&iommu->arena, entry, npages); - spin_unlock_irqrestore(&iommu->lock, flags); - - return 0; -} - -static void pci_4v_unmap_sg(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, int direction) -{ - struct pcidev_cookie *pcp; - struct pci_iommu *iommu; - unsigned long flags, i, npages; - long entry; - u32 devhandle, bus_addr; - - if (unlikely(direction == PCI_DMA_NONE)) { - if (printk_ratelimit()) - WARN_ON(1); - } - - pcp = pdev->sysdata; - iommu = pcp->pbm->iommu; - devhandle = pcp->pbm->devhandle; - - bus_addr = sglist->dma_address & IO_PAGE_MASK; - - for (i = 1; i < nelems; i++) - if (sglist[i].dma_length == 0) - break; - i--; - npages = (IO_PAGE_ALIGN(sglist[i].dma_address + sglist[i].dma_length) - - bus_addr) >> IO_PAGE_SHIFT; - - entry = ((bus_addr - iommu->page_table_map_base) >> IO_PAGE_SHIFT); - - spin_lock_irqsave(&iommu->lock, flags); - - pci_arena_free(&iommu->arena, entry, npages); - - do { - unsigned long num; - - num = pci_sun4v_iommu_demap(devhandle, HV_PCI_TSBID(0, entry), - npages); - entry += num; - npages -= num; - } while (npages != 0); - - spin_unlock_irqrestore(&iommu->lock, flags); -} - -static void pci_4v_dma_sync_single_for_cpu(struct pci_dev *pdev, dma_addr_t bus_addr, size_t sz, int direction) -{ - /* Nothing to do... */ -} - -static void pci_4v_dma_sync_sg_for_cpu(struct pci_dev *pdev, struct scatterlist *sglist, int nelems, int direction) -{ - /* Nothing to do... */ -} - -struct pci_iommu_ops pci_sun4v_iommu_ops = { - .alloc_consistent = pci_4v_alloc_consistent, - .free_consistent = pci_4v_free_consistent, - .map_single = pci_4v_map_single, - .unmap_single = pci_4v_unmap_single, - .map_sg = pci_4v_map_sg, - .unmap_sg = pci_4v_unmap_sg, - .dma_sync_single_for_cpu = pci_4v_dma_sync_single_for_cpu, - .dma_sync_sg_for_cpu = pci_4v_dma_sync_sg_for_cpu, -}; - -/* SUN4V PCI configuration space accessors. */ - -struct pdev_entry { - struct pdev_entry *next; - u32 devhandle; - unsigned int bus; - unsigned int device; - unsigned int func; -}; - -#define PDEV_HTAB_SIZE 16 -#define PDEV_HTAB_MASK (PDEV_HTAB_SIZE - 1) -static struct pdev_entry *pdev_htab[PDEV_HTAB_SIZE]; - -static inline unsigned int pdev_hashfn(u32 devhandle, unsigned int bus, unsigned int device, unsigned int func) -{ - unsigned int val; - - val = (devhandle ^ (devhandle >> 4)); - val ^= bus; - val ^= device; - val ^= func; - - return val & PDEV_HTAB_MASK; -} - -static int pdev_htab_add(u32 devhandle, unsigned int bus, unsigned int device, unsigned int func) -{ - struct pdev_entry *p = kmalloc(sizeof(*p), GFP_KERNEL); - struct pdev_entry **slot; - - if (!p) - return -ENOMEM; - - slot = &pdev_htab[pdev_hashfn(devhandle, bus, device, func)]; - p->next = *slot; - *slot = p; - - p->devhandle = devhandle; - p->bus = bus; - p->device = device; - p->func = func; - - return 0; -} - -/* Recursively descend into the OBP device tree, rooted at toplevel_node, - * looking for a PCI device matching bus and devfn. - */ -static int obp_find(struct device_node *toplevel_node, unsigned int bus, unsigned int devfn) -{ - toplevel_node = toplevel_node->child; - - while (toplevel_node != NULL) { - struct linux_prom_pci_registers *regs; - struct property *prop; - int ret; - - ret = obp_find(toplevel_node, bus, devfn); - if (ret != 0) - return ret; - - prop = of_find_property(toplevel_node, "reg", NULL); - if (!prop) - goto next_sibling; - - regs = prop->value; - if (((regs->phys_hi >> 16) & 0xff) == bus && - ((regs->phys_hi >> 8) & 0xff) == devfn) - break; - - next_sibling: - toplevel_node = toplevel_node->sibling; - } - - return toplevel_node != NULL; -} - -static int pdev_htab_populate(struct pci_pbm_info *pbm) -{ - u32 devhandle = pbm->devhandle; - unsigned int bus; - - for (bus = pbm->pci_first_busno; bus <= pbm->pci_last_busno; bus++) { - unsigned int devfn; - - for (devfn = 0; devfn < 256; devfn++) { - unsigned int device = PCI_SLOT(devfn); - unsigned int func = PCI_FUNC(devfn); - - if (obp_find(pbm->prom_node, bus, devfn)) { - int err = pdev_htab_add(devhandle, bus, - device, func); - if (err) - return err; - } - } - } - - return 0; -} - -static struct pdev_entry *pdev_find(u32 devhandle, unsigned int bus, unsigned int device, unsigned int func) -{ - struct pdev_entry *p; - - p = pdev_htab[pdev_hashfn(devhandle, bus, device, func)]; - while (p) { - if (p->devhandle == devhandle && - p->bus == bus && - p->device == device && - p->func == func) - break; - - p = p->next; - } - - return p; -} - -static inline int pci_sun4v_out_of_range(struct pci_pbm_info *pbm, unsigned int bus, unsigned int device, unsigned int func) -{ - if (bus < pbm->pci_first_busno || - bus > pbm->pci_last_busno) - return 1; - return pdev_find(pbm->devhandle, bus, device, func) == NULL; -} - -static int pci_sun4v_read_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn, - int where, int size, u32 *value) -{ - struct pci_pbm_info *pbm = bus_dev->sysdata; - u32 devhandle = pbm->devhandle; - unsigned int bus = bus_dev->number; - unsigned int device = PCI_SLOT(devfn); - unsigned int func = PCI_FUNC(devfn); - unsigned long ret; - - if (pci_sun4v_out_of_range(pbm, bus, device, func)) { - ret = ~0UL; - } else { - ret = pci_sun4v_config_get(devhandle, - HV_PCI_DEVICE_BUILD(bus, device, func), - where, size); -#if 0 - printk("rcfg: [%x:%x:%x:%d]=[%lx]\n", - devhandle, HV_PCI_DEVICE_BUILD(bus, device, func), - where, size, ret); -#endif - } - switch (size) { - case 1: - *value = ret & 0xff; - break; - case 2: - *value = ret & 0xffff; - break; - case 4: - *value = ret & 0xffffffff; - break; - }; - - - return PCIBIOS_SUCCESSFUL; -} - -static int pci_sun4v_write_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn, - int where, int size, u32 value) -{ - struct pci_pbm_info *pbm = bus_dev->sysdata; - u32 devhandle = pbm->devhandle; - unsigned int bus = bus_dev->number; - unsigned int device = PCI_SLOT(devfn); - unsigned int func = PCI_FUNC(devfn); - unsigned long ret; - - if (pci_sun4v_out_of_range(pbm, bus, device, func)) { - /* Do nothing. */ - } else { - ret = pci_sun4v_config_put(devhandle, - HV_PCI_DEVICE_BUILD(bus, device, func), - where, size, value); -#if 0 - printk("wcfg: [%x:%x:%x:%d] v[%x] == [%lx]\n", - devhandle, HV_PCI_DEVICE_BUILD(bus, device, func), - where, size, value, ret); -#endif - } - return PCIBIOS_SUCCESSFUL; -} - -static struct pci_ops pci_sun4v_ops = { - .read = pci_sun4v_read_pci_cfg, - .write = pci_sun4v_write_pci_cfg, -}; - - -static void pbm_scan_bus(struct pci_controller_info *p, - struct pci_pbm_info *pbm) -{ - struct pcidev_cookie *cookie = kmalloc(sizeof(*cookie), GFP_KERNEL); - - if (!cookie) { - prom_printf("%s: Critical allocation failure.\n", pbm->name); - prom_halt(); - } - - /* All we care about is the PBM. */ - memset(cookie, 0, sizeof(*cookie)); - cookie->pbm = pbm; - - pbm->pci_bus = pci_scan_bus(pbm->pci_first_busno, p->pci_ops, pbm); -#if 0 - pci_fixup_host_bridge_self(pbm->pci_bus); - pbm->pci_bus->self->sysdata = cookie; -#endif - pci_fill_in_pbm_cookies(pbm->pci_bus, pbm, pbm->prom_node); - pci_record_assignments(pbm, pbm->pci_bus); - pci_assign_unassigned(pbm, pbm->pci_bus); - pci_fixup_irq(pbm, pbm->pci_bus); - pci_determine_66mhz_disposition(pbm, pbm->pci_bus); - pci_setup_busmastering(pbm, pbm->pci_bus); -} - -static void pci_sun4v_scan_bus(struct pci_controller_info *p) -{ - struct property *prop; - struct device_node *dp; - - if ((dp = p->pbm_A.prom_node) != NULL) { - prop = of_find_property(dp, "66mhz-capable", NULL); - p->pbm_A.is_66mhz_capable = (prop != NULL); - - pbm_scan_bus(p, &p->pbm_A); - } - if ((dp = p->pbm_B.prom_node) != NULL) { - prop = of_find_property(dp, "66mhz-capable", NULL); - p->pbm_B.is_66mhz_capable = (prop != NULL); - - pbm_scan_bus(p, &p->pbm_B); - } - - /* XXX register error interrupt handlers XXX */ -} - -static void pci_sun4v_base_address_update(struct pci_dev *pdev, int resource) -{ - struct pcidev_cookie *pcp = pdev->sysdata; - struct pci_pbm_info *pbm = pcp->pbm; - struct resource *res, *root; - u32 reg; - int where, size, is_64bit; - - res = &pdev->resource[resource]; - if (resource < 6) { - where = PCI_BASE_ADDRESS_0 + (resource * 4); - } else if (resource == PCI_ROM_RESOURCE) { - where = pdev->rom_base_reg; - } else { - /* Somebody might have asked allocation of a non-standard resource */ - return; - } - - /* XXX 64-bit MEM handling is not %100 correct... XXX */ - is_64bit = 0; - if (res->flags & IORESOURCE_IO) - root = &pbm->io_space; - else { - root = &pbm->mem_space; - if ((res->flags & PCI_BASE_ADDRESS_MEM_TYPE_MASK) - == PCI_BASE_ADDRESS_MEM_TYPE_64) - is_64bit = 1; - } - - size = res->end - res->start; - pci_read_config_dword(pdev, where, ®); - reg = ((reg & size) | - (((u32)(res->start - root->start)) & ~size)); - if (resource == PCI_ROM_RESOURCE) { - reg |= PCI_ROM_ADDRESS_ENABLE; - res->flags |= IORESOURCE_ROM_ENABLE; - } - pci_write_config_dword(pdev, where, reg); - - /* This knows that the upper 32-bits of the address - * must be zero. Our PCI common layer enforces this. - */ - if (is_64bit) - pci_write_config_dword(pdev, where + 4, 0); -} - -static void pci_sun4v_resource_adjust(struct pci_dev *pdev, - struct resource *res, - struct resource *root) -{ - res->start += root->start; - res->end += root->start; -} - -/* Use ranges property to determine where PCI MEM, I/O, and Config - * space are for this PCI bus module. - */ -static void pci_sun4v_determine_mem_io_space(struct pci_pbm_info *pbm) -{ - int i, saw_mem, saw_io; - - saw_mem = saw_io = 0; - for (i = 0; i < pbm->num_pbm_ranges; i++) { - struct linux_prom_pci_ranges *pr = &pbm->pbm_ranges[i]; - unsigned long a; - int type; - - type = (pr->child_phys_hi >> 24) & 0x3; - a = (((unsigned long)pr->parent_phys_hi << 32UL) | - ((unsigned long)pr->parent_phys_lo << 0UL)); - - switch (type) { - case 1: - /* 16-bit IO space, 16MB */ - pbm->io_space.start = a; - pbm->io_space.end = a + ((16UL*1024UL*1024UL) - 1UL); - pbm->io_space.flags = IORESOURCE_IO; - saw_io = 1; - break; - - case 2: - /* 32-bit MEM space, 2GB */ - pbm->mem_space.start = a; - pbm->mem_space.end = a + (0x80000000UL - 1UL); - pbm->mem_space.flags = IORESOURCE_MEM; - saw_mem = 1; - break; - - case 3: - /* XXX 64-bit MEM handling XXX */ - - default: - break; - }; - } - - if (!saw_io || !saw_mem) { - prom_printf("%s: Fatal error, missing %s PBM range.\n", - pbm->name, - (!saw_io ? "IO" : "MEM")); - prom_halt(); - } - - printk("%s: PCI IO[%lx] MEM[%lx]\n", - pbm->name, - pbm->io_space.start, - pbm->mem_space.start); -} - -static void pbm_register_toplevel_resources(struct pci_controller_info *p, - struct pci_pbm_info *pbm) -{ - pbm->io_space.name = pbm->mem_space.name = pbm->name; - - request_resource(&ioport_resource, &pbm->io_space); - request_resource(&iomem_resource, &pbm->mem_space); - pci_register_legacy_regions(&pbm->io_space, - &pbm->mem_space); -} - -static unsigned long probe_existing_entries(struct pci_pbm_info *pbm, - struct pci_iommu *iommu) -{ - struct pci_iommu_arena *arena = &iommu->arena; - unsigned long i, cnt = 0; - u32 devhandle; - - devhandle = pbm->devhandle; - for (i = 0; i < arena->limit; i++) { - unsigned long ret, io_attrs, ra; - - ret = pci_sun4v_iommu_getmap(devhandle, - HV_PCI_TSBID(0, i), - &io_attrs, &ra); - if (ret == HV_EOK) { - if (page_in_phys_avail(ra)) { - pci_sun4v_iommu_demap(devhandle, - HV_PCI_TSBID(0, i), 1); - } else { - cnt++; - __set_bit(i, arena->map); - } - } - } - - return cnt; -} - -static void pci_sun4v_iommu_init(struct pci_pbm_info *pbm) -{ - struct pci_iommu *iommu = pbm->iommu; - struct property *prop; - unsigned long num_tsb_entries, sz; - u32 vdma[2], dma_mask, dma_offset; - int tsbsize; - - prop = of_find_property(pbm->prom_node, "virtual-dma", NULL); - if (prop) { - u32 *val = prop->value; - - vdma[0] = val[0]; - vdma[1] = val[1]; - } else { - /* No property, use default values. */ - vdma[0] = 0x80000000; - vdma[1] = 0x80000000; - } - - dma_mask = vdma[0]; - switch (vdma[1]) { - case 0x20000000: - dma_mask |= 0x1fffffff; - tsbsize = 64; - break; - - case 0x40000000: - dma_mask |= 0x3fffffff; - tsbsize = 128; - break; - - case 0x80000000: - dma_mask |= 0x7fffffff; - tsbsize = 256; - break; - - default: - prom_printf("PCI-SUN4V: strange virtual-dma size.\n"); - prom_halt(); - }; - - tsbsize *= (8 * 1024); - - num_tsb_entries = tsbsize / sizeof(iopte_t); - - dma_offset = vdma[0]; - - /* Setup initial software IOMMU state. */ - spin_lock_init(&iommu->lock); - iommu->ctx_lowest_free = 1; - iommu->page_table_map_base = dma_offset; - iommu->dma_addr_mask = dma_mask; - - /* Allocate and initialize the free area map. */ - sz = num_tsb_entries / 8; - sz = (sz + 7UL) & ~7UL; - iommu->arena.map = kmalloc(sz, GFP_KERNEL); - if (!iommu->arena.map) { - prom_printf("PCI_IOMMU: Error, kmalloc(arena.map) failed.\n"); - prom_halt(); - } - memset(iommu->arena.map, 0, sz); - iommu->arena.limit = num_tsb_entries; - - sz = probe_existing_entries(pbm, iommu); - if (sz) - printk("%s: Imported %lu TSB entries from OBP\n", - pbm->name, sz); -} - -static void pci_sun4v_get_bus_range(struct pci_pbm_info *pbm) -{ - struct property *prop; - unsigned int *busrange; - - prop = of_find_property(pbm->prom_node, "bus-range", NULL); - - busrange = prop->value; - - pbm->pci_first_busno = busrange[0]; - pbm->pci_last_busno = busrange[1]; - -} - -static void pci_sun4v_pbm_init(struct pci_controller_info *p, struct device_node *dp, u32 devhandle) -{ - struct pci_pbm_info *pbm; - struct property *prop; - int len, i; - - if (devhandle & 0x40) - pbm = &p->pbm_B; - else - pbm = &p->pbm_A; - - pbm->parent = p; - pbm->prom_node = dp; - pbm->pci_first_slot = 1; - - pbm->devhandle = devhandle; - - pbm->name = dp->full_name; - - printk("%s: SUN4V PCI Bus Module\n", pbm->name); - - prop = of_find_property(dp, "ranges", &len); - pbm->pbm_ranges = prop->value; - pbm->num_pbm_ranges = - (len / sizeof(struct linux_prom_pci_ranges)); - - /* Mask out the top 8 bits of the ranges, leaving the real - * physical address. - */ - for (i = 0; i < pbm->num_pbm_ranges; i++) - pbm->pbm_ranges[i].parent_phys_hi &= 0x0fffffff; - - pci_sun4v_determine_mem_io_space(pbm); - pbm_register_toplevel_resources(p, pbm); - - prop = of_find_property(dp, "interrupt-map", &len); - pbm->pbm_intmap = prop->value; - pbm->num_pbm_intmap = - (len / sizeof(struct linux_prom_pci_intmap)); - - prop = of_find_property(dp, "interrupt-map-mask", NULL); - pbm->pbm_intmask = prop->value; - - pci_sun4v_get_bus_range(pbm); - pci_sun4v_iommu_init(pbm); - - pdev_htab_populate(pbm); -} - -void sun4v_pci_init(struct device_node *dp, char *model_name) -{ - struct pci_controller_info *p; - struct pci_iommu *iommu; - struct property *prop; - struct linux_prom64_registers *regs; - u32 devhandle; - int i; - - prop = of_find_property(dp, "reg", NULL); - regs = prop->value; - - devhandle = (regs->phys_addr >> 32UL) & 0x0fffffff; - - for (p = pci_controller_root; p; p = p->next) { - struct pci_pbm_info *pbm; - - if (p->pbm_A.prom_node && p->pbm_B.prom_node) - continue; - - pbm = (p->pbm_A.prom_node ? - &p->pbm_A : - &p->pbm_B); - - if (pbm->devhandle == (devhandle ^ 0x40)) { - pci_sun4v_pbm_init(p, dp, devhandle); - return; - } - } - - for_each_possible_cpu(i) { - unsigned long page = get_zeroed_page(GFP_ATOMIC); - - if (!page) - goto fatal_memory_error; - - per_cpu(pci_iommu_batch, i).pglist = (u64 *) page; - } - - p = kmalloc(sizeof(struct pci_controller_info), GFP_ATOMIC); - if (!p) - goto fatal_memory_error; - - memset(p, 0, sizeof(*p)); - - iommu = kmalloc(sizeof(struct pci_iommu), GFP_ATOMIC); - if (!iommu) - goto fatal_memory_error; - - memset(iommu, 0, sizeof(*iommu)); - p->pbm_A.iommu = iommu; - - iommu = kmalloc(sizeof(struct pci_iommu), GFP_ATOMIC); - if (!iommu) - goto fatal_memory_error; - - memset(iommu, 0, sizeof(*iommu)); - p->pbm_B.iommu = iommu; - - p->next = pci_controller_root; - pci_controller_root = p; - - p->index = pci_num_controllers++; - p->pbms_same_domain = 0; - - p->scan_bus = pci_sun4v_scan_bus; - p->base_address_update = pci_sun4v_base_address_update; - p->resource_adjust = pci_sun4v_resource_adjust; - p->pci_ops = &pci_sun4v_ops; - - /* Like PSYCHO and SCHIZO we have a 2GB aligned area - * for memory space. - */ - pci_memspace_mask = 0x7fffffffUL; - - pci_sun4v_pbm_init(p, dp, devhandle); - return; - -fatal_memory_error: - prom_printf("SUN4V_PCI: Fatal memory allocation error.\n"); - prom_halt(); -} |
