X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fparisc%2Fsba_iommu.c;h=166d5fe79bc55b1ed5b3ae9b19eb0ea4b14a70e4;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=f3cf291adc776b239794dece21ec134973b86b7f;hpb=87fc8d1bb10cd459024a742c6a10961fefcef18f;p=linux-2.6.git diff --git a/drivers/parisc/sba_iommu.c b/drivers/parisc/sba_iommu.c index f3cf291ad..166d5fe79 100644 --- a/drivers/parisc/sba_iommu.c +++ b/drivers/parisc/sba_iommu.c @@ -1,8 +1,9 @@ /* ** System Bus Adapter (SBA) I/O MMU manager ** -** (c) Copyright 2000 Grant Grundler -** (c) Copyright 2000 Hewlett-Packard Company +** (c) Copyright 2000-2004 Grant Grundler +** (c) Copyright 2004 Naresh Kumar Inna +** (c) Copyright 2000-2004 Hewlett-Packard Company ** ** Portions (c) 1999 Dave S. Miller (from sparc64 I/O MMU code) ** @@ -167,11 +168,21 @@ extern struct proc_dir_entry * proc_mckinley_root; #define MAX_IOC 2 /* per Ike. Pluto/Astro only have 1. */ +#define ROPES_PER_IOC 8 /* per Ike half or Pluto/Astro */ + /* ** Offsets into MBIB (Function 0 on Ike and hopefully Astro) ** Firmware programs this stuff. Don't touch it. */ +#define LMMIO_DIRECT0_BASE 0x300 +#define LMMIO_DIRECT0_MASK 0x308 +#define LMMIO_DIRECT0_ROUTE 0x310 + +#define LMMIO_DIST_BASE 0x360 +#define LMMIO_DIST_MASK 0x368 +#define LMMIO_DIST_ROUTE 0x370 + #define IOS_DIST_BASE 0x390 #define IOS_DIST_MASK 0x398 #define IOS_DIST_ROUTE 0x3A0 @@ -289,6 +300,9 @@ struct sba_device { unsigned int flags; /* state/functionality enabled */ unsigned int hw_rev; /* HW revision of chip */ + struct resource chip_resv; /* MMIO reserved for chip */ + struct resource iommu_resv; /* MMIO reserved for iommu */ + unsigned int num_ioc; /* number of on-board IOC's */ struct ioc ioc[MAX_IOC]; }; @@ -569,7 +583,7 @@ sba_search_bitmap(struct ioc *ioc, unsigned long bits_wanted) while(res_ptr < res_end) { DBG_RES(" %p %lx %lx\n", res_ptr, mask, *res_ptr); - ASSERT(0 != mask); + BUG_ON(0 == mask); if(0 == ((*res_ptr) & mask)) { *res_ptr |= mask; /* mark resources busy! */ pide = ((unsigned long)res_ptr - (unsigned long)ioc->res_map); @@ -1696,19 +1710,46 @@ sba_hw_init(struct sba_device *sba_dev) } /* if !PLUTO */ if (IS_ASTRO(sba_dev->iodc)) { + int err; /* PAT_PDC (L-class) also reports the same goofy base */ sba_dev->ioc[0].ioc_hpa = ASTRO_IOC_OFFSET; num_ioc = 1; + + sba_dev->chip_resv.name = "Astro Intr Ack"; + sba_dev->chip_resv.start = PCI_F_EXTEND | 0xfef00000UL; + sba_dev->chip_resv.end = PCI_F_EXTEND | (0xff000000UL - 1) ; + err = request_resource(&iomem_resource, &(sba_dev->chip_resv)); + if (err < 0) { + BUG(); + } + } else if (IS_PLUTO(sba_dev->iodc)) { + int err; + /* We use a negative value for IOC HPA so it gets * corrected when we add it with IKE's IOC offset. * Doesnt look clean, but fewer code. */ sba_dev->ioc[0].ioc_hpa = -PLUTO_IOC_OFFSET; num_ioc = 1; + + sba_dev->chip_resv.name = "Pluto Intr/PIOP/VGA"; + sba_dev->chip_resv.start = PCI_F_EXTEND | 0xfee00000UL; + sba_dev->chip_resv.end = PCI_F_EXTEND | (0xff200000UL - 1); + err = request_resource(&iomem_resource, &(sba_dev->chip_resv)); + BUG_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)); + BUG_ON(err < 0); } else { + /* IS_IKE (ie N-class, L3000, L1500) */ sba_dev->ioc[0].ioc_hpa = sba_dev->ioc[1].ioc_hpa = 0; num_ioc = 2; + + /* TODO - LOOKUP Ike/Stretch chipset mem map */ } sba_dev->num_ioc = num_ioc; @@ -1820,7 +1861,7 @@ sba_common_init(struct sba_device *sba_dev) __FUNCTION__, i, res_size, sba_dev->ioc[i].res_map); } - sba_dev->sba_lock = SPIN_LOCK_UNLOCKED; + spin_lock_init(&sba_dev->sba_lock); ioc_needs_fdc = boot_cpu_data.pdc.capabilities & PDC_MODEL_IOPDIR_FDC; #ifdef DEBUG_SBA_INIT @@ -1843,8 +1884,9 @@ static 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; #ifdef SBA_COLLECT_STATS - unsigned long i = 0, avg = 0, min, max; + unsigned long avg = 0, min, max; #endif sprintf(buf, "%s rev %d.%d\n", @@ -1860,6 +1902,21 @@ static int sba_proc_info(char *buf, char **start, off_t offset, int len) sprintf(buf, "%sResource bitmap : %d bytes (%d pages)\n", buf, ioc->res_size, ioc->res_size << 3); /* 8 bits per byte */ + sprintf(buf, "%sLMMIO_BASE/MASK/ROUTE %08x %08x %08x\n", + buf, + 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++) + sprintf(buf, "%sDIR%ld_BASE/MASK/ROUTE %08x %08x %08x\n", + buf, 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 sprintf(buf, "%sIO PDIR entries : %ld free %ld used (%d%%)\n", buf, total_pages - ioc->used_pages, ioc->used_pages, @@ -1949,7 +2006,7 @@ static struct parisc_driver sba_driver = { }; /* -** Determine if lba should claim this chip (return 0) or not (return 1). +** 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. */ @@ -2062,17 +2119,95 @@ void __init sba_init(void) * 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. + * 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 sba_device *sba = (struct sba_device *) pci_hba->parent->sysdata; - char t = pci_hba->parent->id.hw_type; + 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 # */ + BUG_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 # */ + if ((t!=HPHW_IOA) && (t!=HPHW_BCPORT)) BUG(); - return &(sba->ioc[iocnum]); + 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; + unsigned long 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 # */ + + if ((t!=HPHW_IOA) && (t!=HPHW_BCPORT)) + BUG(); + + 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; }