/*
** 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 <grundler @ parisc-linux x org>
+** (c) Copyright 2004 Naresh Kumar Inna <knaresh at india x hp x com>
+** (c) Copyright 2000-2004 Hewlett-Packard Company
**
** Portions (c) 1999 Dave S. Miller (from sparc64 I/O MMU code)
**
#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
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];
};
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);
} /* 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;
__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
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",
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,
};
/*
-** 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.
*/
* 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;
}