patch-2.1.53 linux/arch/sparc64/kernel/psycho.c
Next file: linux/arch/sparc64/kernel/rtrap.S
Previous file: linux/arch/sparc64/kernel/process.c
Back to the patch index
Back to the overall index
- Lines: 1030
- Date:
Thu Sep 4 12:54:48 1997
- Orig file:
v2.1.52/linux/arch/sparc64/kernel/psycho.c
- Orig date:
Mon Aug 18 18:19:45 1997
diff -u --recursive --new-file v2.1.52/linux/arch/sparc64/kernel/psycho.c linux/arch/sparc64/kernel/psycho.c
@@ -1,4 +1,4 @@
-/* $Id: psycho.c,v 1.5 1997/08/15 06:44:18 davem Exp $
+/* $Id: psycho.c,v 1.22 1997/08/31 03:51:40 davem Exp $
* psycho.c: Ultra/AX U2P PCI controller support.
*
* Copyright (C) 1997 David S. Miller (davem@caipfs.rutgers.edu)
@@ -62,6 +62,33 @@
~(sizeof(unsigned long) - 1));
}
+static unsigned long psycho_iommu_init(struct linux_psycho *psycho,
+ unsigned long memory_start)
+{
+ unsigned long tsbbase = PAGE_ALIGN(memory_start);
+ unsigned long control, i;
+ unsigned long *iopte;
+
+ memory_start = (tsbbase + ((32 * 1024) * 8));
+ iopte = (unsigned long *)tsbbase;
+
+ for(i = 0; i < (65536 / 2); i++) {
+ *iopte = (IOPTE_VALID | IOPTE_64K |
+ IOPTE_CACHE | IOPTE_WRITE);
+ *iopte |= (i << 16);
+ iopte++;
+ }
+
+ psycho->psycho_regs->iommu_tsbbase = __pa(tsbbase);
+
+ control = psycho->psycho_regs->iommu_control;
+ control &= ~(IOMMU_CTRL_TSBSZ);
+ control |= (IOMMU_TSBSZ_32K | IOMMU_CTRL_TBWSZ | IOMMU_CTRL_ENAB);
+ psycho->psycho_regs->iommu_control = control;
+
+ return memory_start;
+}
+
extern void prom_pbm_ranges_init(int node, struct linux_pbm_info *pbm);
unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end)
@@ -71,7 +98,6 @@
u32 portid;
int node;
- /* prom_printf("PSYCHO: Probing for controllers.\n"); */
printk("PSYCHO: Probing for controllers.\n");
memory_start = long_align(memory_start);
@@ -126,10 +152,13 @@
prom_halt();
}
- prom_printf("PSYCHO: Found controller, main regs at %p\n",
- psycho->psycho_regs);
printk("PSYCHO: Found controller, main regs at %p\n",
psycho->psycho_regs);
+#if 0
+ printk("PSYCHO: Interrupt retry [%016lx]\n",
+ psycho->psycho_regs->irq_retry);
+#endif
+ psycho->psycho_regs->irq_retry = 0xff;
/* Now map in PCI config space for entire PSYCHO. */
psycho->pci_config_space =
@@ -142,34 +171,17 @@
prom_halt();
}
- /* Finally map in I/O space for both PBM's. This is essentially
- * backwards compatability for non-conformant PCI cards which
- * do not map themselves into the PCI memory space.
- */
- psycho->pbm_B.pbm_IO = __va(pr_regs[2].phys_addr + 0x02000000UL);
- psycho->pbm_A.pbm_IO = __va(pr_regs[2].phys_addr + 0x02010000UL);
-
- /* Now record MEM space for both PBM's.
- *
- * XXX Eddie, these can be reversed if BOOT_BUS pin is clear, is
- * XXX there some way to find out what value of BOOT_BUS pin is?
- */
- psycho->pbm_B.pbm_mem = __va(pr_regs[2].phys_addr + 0x180000000UL);
- psycho->pbm_A.pbm_mem = __va(pr_regs[2].phys_addr + 0x100000000UL);
-
/* Report some more info. */
- prom_printf("PSYCHO: PCI config space at %p\n",
- psycho->pci_config_space);
- prom_printf("PSYCHO: PBM A I/O space at %p, PBM B I/O at %p\n",
- psycho->pbm_A.pbm_IO, psycho->pbm_B.pbm_IO);
- prom_printf("PSYCHO: PBM A MEM at %p, PBM B MEM at %p\n");
-
printk("PSYCHO: PCI config space at %p\n", psycho->pci_config_space);
- printk("PSYCHO: PBM A I/O space at %p, PBM B I/O at %p\n",
- psycho->pbm_A.pbm_IO, psycho->pbm_B.pbm_IO);
+
+ memory_start = psycho_iommu_init(psycho, memory_start);
is_pbm_a = ((pr_regs[0].phys_addr & 0x6000) == 0x2000);
+ /* Enable arbitration for all PCI slots. */
+ psycho->psycho_regs->pci_a_control |= 0x3f;
+ psycho->psycho_regs->pci_b_control |= 0x3f;
+
other_pbm:
if(is_pbm_a)
pbm = &psycho->pbm_A;
@@ -177,6 +189,8 @@
pbm = &psycho->pbm_B;
pbm->parent = psycho;
+ pbm->IO_assignments = NULL;
+ pbm->MEM_assignments = NULL;
pbm->prom_node = node;
prom_getstring(node, "name", namebuf, sizeof(namebuf));
@@ -256,6 +270,97 @@
return PCIBIOS_DEVICE_NOT_FOUND;
}
+static inline struct pci_vma *pci_find_vma(struct linux_pbm_info *pbm,
+ unsigned long start,
+ int io)
+{
+ struct pci_vma *vp = (io ? pbm->IO_assignments : pbm->MEM_assignments);
+
+ while(vp) {
+ if(vp->end > start)
+ break;
+ vp = vp->next;
+ }
+ return vp;
+}
+
+static inline void pci_add_vma(struct linux_pbm_info *pbm, struct pci_vma *new, int io)
+{
+ struct pci_vma *vp = (io ? pbm->IO_assignments : pbm->MEM_assignments);
+
+ if(!vp) {
+ new->next = NULL;
+ if(io)
+ pbm->IO_assignments = new;
+ else
+ pbm->MEM_assignments = new;
+ } else {
+ struct pci_vma *prev = NULL;
+
+ while(vp && (vp->end < new->end)) {
+ prev = vp;
+ vp = vp->next;
+ }
+ new->next = vp;
+ if(!prev) {
+ if(io)
+ pbm->IO_assignments = new;
+ else
+ pbm->MEM_assignments = new;
+ } else {
+ prev->next = new;
+ }
+
+ /* Check for programming errors. */
+ if(vp &&
+ ((vp->start >= new->start && vp->start < new->end) ||
+ ((vp->end - 1) >= new->start && (vp->end - 1) < new->end))) {
+ prom_printf("pci_add_vma: Wheee, overlapping %s PCI vma's\n",
+ io ? "IO" : "MEM");
+ prom_printf("pci_add_vma: vp[%016lx:%016lx] "
+ "new[%016lx:%016lx]\n",
+ vp->start, vp->end,
+ new->start, new->end);
+ }
+ }
+}
+
+static unsigned long *pci_alloc_arena = NULL;
+
+static inline void pci_init_alloc_init(unsigned long *mstart)
+{
+ pci_alloc_arena = mstart;
+}
+
+static inline void pci_init_alloc_fini(void)
+{
+ pci_alloc_arena = NULL;
+}
+
+static void *pci_init_alloc(int size)
+{
+ unsigned long start = long_align(*pci_alloc_arena);
+ void *mp = (void *)start;
+
+ if(!pci_alloc_arena) {
+ prom_printf("pci_init_alloc: pci_vma arena not init'd\n");
+ prom_halt();
+ }
+ start += size;
+ *pci_alloc_arena = start;
+ return mp;
+}
+
+static inline struct pci_vma *pci_vma_alloc(void)
+{
+ return pci_init_alloc(sizeof(struct pci_vma));
+}
+
+static inline struct pcidev_cookie *pci_devcookie_alloc(void)
+{
+ return pci_init_alloc(sizeof(struct pcidev_cookie));
+}
+
static void pbm_probe(struct linux_pbm_info *pbm, unsigned long *mstart)
{
struct pci_bus *pbus = &pbm->pci_bus;
@@ -270,6 +375,51 @@
pbus->subordinate = pci_scan_bus(pbus, mstart);
}
+static int pdev_to_pnode_sibtraverse(struct linux_pbm_info *pbm,
+ struct pci_dev *pdev,
+ int node)
+{
+ struct linux_prom_pci_registers pregs[PROMREG_MAX];
+ int err;
+
+ while(node) {
+ int child;
+
+ child = prom_getchild(node);
+ if(child != 0 && child != -1) {
+ int res;
+
+ res = pdev_to_pnode_sibtraverse(pbm, pdev, child);
+ if(res != 0 && res != -1)
+ return res;
+ }
+ err = prom_getproperty(node, "reg", (char *)&pregs[0], sizeof(pregs));
+ if(err != 0 && err != -1) {
+ u32 devfn = (pregs[0].phys_hi >> 8) & 0xff;
+
+ if(devfn == pdev->devfn)
+ return node; /* Match */
+ }
+
+ node = prom_getsibling(node);
+ }
+ return 0;
+}
+
+static void pdev_cookie_fillin(struct linux_pbm_info *pbm, struct pci_dev *pdev)
+{
+ struct pcidev_cookie *pcp;
+ int node = prom_getchild(pbm->prom_node);
+
+ node = pdev_to_pnode_sibtraverse(pbm, pdev, node);
+ if(node == 0)
+ node = -1;
+ pcp = pci_devcookie_alloc();
+ pcp->pbm = pbm;
+ pcp->prom_node = node;
+ pdev->sysdata = pcp;
+}
+
static void fill_in_pbm_cookies(struct linux_pbm_info *pbm)
{
struct pci_bus *pbtmp, *pbus = &pbm->pci_bus;
@@ -280,42 +430,638 @@
for( ; pbus; pbus = pbus->children)
for(pdev = pbus->devices; pdev; pdev = pdev->sibling)
- pdev->sysdata = pbm;
+ pdev_cookie_fillin(pbm, pdev);
}
-static void fixup_pci_dev(struct pci_dev *pdev,
- struct pci_bus *pbus,
- struct linux_pbm_info *pbm)
+/* #define RECORD_ASSIGNMENTS_DEBUG */
+
+/* Walk PROM device tree under PBM, looking for 'assigned-address'
+ * properties, and recording them in pci_vma's linked in via
+ * PBM->assignments.
+ */
+static int gimme_ebus_assignments(int node, struct linux_prom_pci_registers *aregs)
{
- struct linux_prom_pci_registers pregs[PROMREG_MAX];
- int node;
-#if 0
- int nregs, busno = pbus->number;
-#endif
+ struct linux_prom_ebus_ranges erng[PROMREG_MAX];
+ int err, iter;
+
+ err = prom_getproperty(node, "ranges", (char *)&erng[0], sizeof(erng));
+ if(err == 0 || err == -1) {
+ prom_printf("EBUS: fatal error, no range property.\n");
+ prom_halt();
+ }
+ err = (err / sizeof(struct linux_prom_ebus_ranges));
+ for(iter = 0; iter < err; iter++) {
+ struct linux_prom_ebus_ranges *ep = &erng[iter];
+ struct linux_prom_pci_registers *ap = &aregs[iter];
+
+ ap->phys_hi = ep->parent_phys_hi;
+ ap->phys_mid = ep->parent_phys_mid;
+ ap->phys_lo = ep->parent_phys_lo;
+ }
+ return err;
+}
+
+static void assignment_process(struct linux_pbm_info *pbm, int node)
+{
+ struct linux_prom_pci_registers aregs[PROMREG_MAX];
+ char pname[256];
+ int err, iter, numa;
- node = prom_getchild(pbm->prom_node);
+ err = prom_getproperty(node, "name", (char *)&pname[0], sizeof(pname));
+ if(strncmp(pname, "ebus", 4) == 0) {
+ numa = gimme_ebus_assignments(node, &aregs[0]);
+ } else {
+ err = prom_getproperty(node, "assigned-addresses",
+ (char *)&aregs[0], sizeof(aregs));
+
+ /* No assignments, nothing to do. */
+ if(err == 0 || err == -1)
+ return;
+
+ numa = (err / sizeof(struct linux_prom_pci_ranges));
+ }
+
+ for(iter = 0; iter < numa; iter++) {
+ struct linux_prom_pci_registers *ap = &aregs[iter];
+ struct pci_vma *vp;
+ int space, breg, io;
+
+ space = (ap->phys_hi >> 24) & 3;
+ if(space != 1 && space != 2)
+ continue;
+ io = (space == 1);
+
+ breg = (ap->phys_hi & 0xff);
+ if(breg == PCI_ROM_ADDRESS)
+ continue;
+
+ vp = pci_vma_alloc();
+
+ /* XXX Means we don't support > 32-bit range of
+ * XXX PCI MEM space, PSYCHO/PBM does not support it
+ * XXX either due to it's layout so...
+ */
+ vp->start = ap->phys_lo;
+ vp->end = vp->start + ap->size_lo;
+ vp->base_reg = breg;
+
+ /* Sanity */
+ if(io && (vp->end & ~(0xffff))) {
+ prom_printf("assignment_process: Out of range PCI I/O "
+ "[%08lx:%08lx]\n", vp->start, vp->end);
+ prom_halt();
+ }
+
+ pci_add_vma(pbm, vp, io);
+ }
+}
+
+static void assignment_walk_siblings(struct linux_pbm_info *pbm, int node)
+{
while(node) {
- u32 devfn;
- int err, nregs;
+ int child = prom_getchild(node);
+ if(child)
+ assignment_walk_siblings(pbm, child);
- err = prom_getproperty(node, "reg", (char *)&pregs[0], sizeof(pregs));
- if(err == 0 || err == -1) {
- prom_printf("fixup_pci_dev: No PCI device reg property?!?!\n");
+ assignment_process(pbm, node);
+
+ node = prom_getsibling(node);
+ }
+}
+
+static void record_assignments(struct linux_pbm_info *pbm)
+{
+ assignment_walk_siblings(pbm, prom_getchild(pbm->prom_node));
+}
+
+/* #define FIXUP_REGS_DEBUG */
+
+static void fixup_regs(struct pci_dev *pdev,
+ struct linux_pbm_info *pbm,
+ struct linux_prom_pci_registers *pregs,
+ int nregs,
+ struct linux_prom_pci_registers *assigned,
+ int numaa)
+{
+ int preg, rng;
+ int IO_seen = 0;
+ int MEM_seen = 0;
+
+ for(preg = 0; preg < nregs; preg++) {
+ struct linux_prom_pci_registers *ap = NULL;
+ int bustype = (pregs[preg].phys_hi >> 24) & 0x3;
+ int bsreg, brindex;
+ u64 pci_addr;
+
+ if(bustype == 0) {
+ /* Config space cookie, nothing to do. */
+ if(preg != 0)
+ prom_printf("fixup_doit: strange, config space not 0\n");
+ continue;
+ } else if(bustype == 3) {
+ /* XXX add support for this... */
+ prom_printf("fixup_doit: Warning, ignoring 64-bit PCI "
+ "memory space, tell DaveM.\n");
+ continue;
+ }
+ bsreg = (pregs[preg].phys_hi & 0xff);
+
+ /* We can safely ignore these. */
+ if(bsreg == PCI_ROM_ADDRESS)
+ continue;
+
+ /* Sanity */
+ if((bsreg < PCI_BASE_ADDRESS_0) ||
+ (bsreg > (PCI_BASE_ADDRESS_5 + 4)) ||
+ (bsreg & 3)) {
+ prom_printf("fixup_doit: Warning, ignoring bogus basereg [%x]\n",
+ bsreg);
+ continue;
+ }
+
+ brindex = (bsreg - PCI_BASE_ADDRESS_0) >> 2;
+ if(numaa) {
+ int r;
+
+ for(r = 0; r < numaa; r++) {
+ int abreg;
+
+ abreg = (assigned[r].phys_hi & 0xff);
+ if(abreg == bsreg) {
+ ap = &assigned[r];
+ break;
+ }
+ }
+ }
+
+ /* Now construct UPA physical address. */
+ pci_addr = (((u64)pregs[preg].phys_mid) << 32UL);
+ pci_addr |= (((u64)pregs[preg].phys_lo));
+
+ if(ap) {
+ pci_addr += ((u64)ap->phys_lo);
+ pci_addr += (((u64)ap->phys_mid) << 32UL);
+ }
+
+ /* Final step, apply PBM range. */
+ for(rng = 0; rng < pbm->num_pbm_ranges; rng++) {
+ struct linux_prom_pci_ranges *rp = &pbm->pbm_ranges[rng];
+ int space = (rp->child_phys_hi >> 24) & 3;
+
+ if(space == bustype) {
+ pci_addr += ((u64)rp->parent_phys_lo);
+ pci_addr += (((u64)rp->parent_phys_hi) << 32UL);
+ break;
+ }
+ }
+ if(rng == pbm->num_pbm_ranges) {
+ /* AIEEE */
+ prom_printf("fixup_doit: YIEEE, cannot find PBM ranges\n");
+ }
+ pdev->base_address[brindex] = (unsigned long)__va(pci_addr);
+
+ /* Preserve I/O space bit. */
+ if(bustype == 0x1) {
+ pdev->base_address[brindex] |= 1;
+ IO_seen = 1;
+ } else {
+ MEM_seen = 1;
+ }
+ }
+
+ /* Now handle assignments PROM did not take care of. */
+ if(nregs) {
+ int breg;
+
+ for(breg = PCI_BASE_ADDRESS_0; breg <= PCI_BASE_ADDRESS_5; breg += 4) {
+ unsigned int rtmp, ridx = ((breg - PCI_BASE_ADDRESS_0) >> 2);
+ unsigned int base = (unsigned int)pdev->base_address[ridx];
+ struct pci_vma *vp;
+ u64 pci_addr;
+ int io;
+
+ if(pdev->base_address[ridx] > PAGE_OFFSET)
+ continue;
+
+ io = (base & PCI_BASE_ADDRESS_SPACE)==PCI_BASE_ADDRESS_SPACE_IO;
+ base &= ~((io ?
+ PCI_BASE_ADDRESS_IO_MASK :
+ PCI_BASE_ADDRESS_MEM_MASK));
+ vp = pci_find_vma(pbm, base, io);
+ if(!vp || vp->start > base) {
+ unsigned int size, new_base;
+
+ pcibios_read_config_dword(pdev->bus->number,
+ pdev->devfn,
+ breg, &rtmp);
+ pcibios_write_config_dword(pdev->bus->number,
+ pdev->devfn,
+ breg, 0xffffffff);
+ pcibios_read_config_dword(pdev->bus->number,
+ pdev->devfn,
+ breg, &size);
+ if(io)
+ size &= ~1;
+ size = (~(size) + 1);
+ if(!size)
+ continue;
+
+ new_base = 0;
+ for(vp=pci_find_vma(pbm,new_base,io); ; vp=vp->next) {
+ if(!vp || new_base + size <= vp->start)
+ break;
+ new_base = (vp->end + (size - 1)) & ~(size-1);
+ }
+ if(vp && (new_base + size > vp->start)) {
+ prom_printf("PCI: Impossible full %s space.\n",
+ (io ? "IO" : "MEM"));
+ prom_halt();
+ }
+ vp = pci_vma_alloc();
+ vp->start = new_base;
+ vp->end = vp->start + size;
+ vp->base_reg = breg;
+
+ /* Sanity */
+ if(io && vp->end & ~(0xffff)) {
+ prom_printf("PCI: Out of range PCI I/O "
+ "[%08lx:%08lx] during fixup\n",
+ vp->start, vp->end);
+ prom_halt();
+ }
+ pci_add_vma(pbm, vp, io);
+
+ rtmp = new_base;
+ if(io)
+ rtmp |= (rtmp & PCI_BASE_ADDRESS_IO_MASK);
+ else
+ rtmp |= (rtmp & PCI_BASE_ADDRESS_MEM_MASK);
+ pcibios_write_config_dword(pdev->bus->number,
+ pdev->devfn,
+ breg, rtmp);
+
+ /* Apply PBM ranges and update pci_dev. */
+ pci_addr = new_base;
+ for(rng = 0; rng < pbm->num_pbm_ranges; rng++) {
+ struct linux_prom_pci_ranges *rp;
+ int rspace;
+
+ rp = &pbm->pbm_ranges[rng];
+ rspace = (rp->child_phys_hi >> 24) & 3;
+ if(io && rspace != 1)
+ continue;
+ else if(!io && rspace != 2)
+ continue;
+ pci_addr += ((u64)rp->parent_phys_lo);
+ pci_addr += (((u64)rp->parent_phys_hi)<<32UL);
+ break;
+ }
+ if(rng == pbm->num_pbm_ranges) {
+ /* AIEEE */
+ prom_printf("fixup_doit: YIEEE, cannot find "
+ "PBM ranges\n");
+ }
+ pdev->base_address[ridx] = (unsigned long)__va(pci_addr);
+
+ /* Preserve I/O space bit. */
+ if(io) {
+ pdev->base_address[ridx] |= 1;
+ IO_seen = 1;
+ } else {
+ MEM_seen = 1;
+ }
+ }
+ }
+ }
+ if(IO_seen || MEM_seen) {
+ unsigned int l;
+
+ pcibios_read_config_dword(pdev->bus->number,
+ pdev->devfn,
+ PCI_COMMAND, &l);
+#ifdef FIXUP_REGS_DEBUG
+ prom_printf("[");
+#endif
+ if(IO_seen) {
+#ifdef FIXUP_REGS_DEBUG
+ prom_printf("IO ");
+#endif
+ l |= PCI_COMMAND_IO;
+ }
+ if(MEM_seen) {
+#ifdef FIXUP_REGS_DEBUG
+ prom_printf("MEM");
+#endif
+ l |= PCI_COMMAND_MEMORY;
+ }
+#ifdef FIXUP_REGS_DEBUG
+ prom_printf("]");
+#endif
+ pcibios_write_config_dword(pdev->bus->number,
+ pdev->devfn,
+ PCI_COMMAND, l);
+ }
+
+#ifdef FIXUP_REGS_DEBUG
+ prom_printf("REG_FIXUP[%s]: ", pci_strdev(pdev->vendor, pdev->device));
+ for(preg = 0; preg < 6; preg++) {
+ if(pdev->base_address[preg] != 0)
+ prom_printf("%d[%016lx] ", preg, pdev->base_address[preg]);
+ }
+ prom_printf("\n");
+#endif
+}
+
+#define imap_offset(__member) \
+ ((unsigned long)(&(((struct psycho_regs *)0)->__member)))
+
+static unsigned long psycho_pcislot_imap_offset(unsigned long ino)
+{
+ unsigned int bus, slot;
+
+ bus = (ino & 0x10) >> 4;
+ slot = (ino & 0x0c) >> 2;
+
+ if(bus == 0) {
+ /* Perform a sanity check, we might as well.
+ * PBM A only has 2 PCI slots.
+ */
+ if(slot > 1) {
+ prom_printf("pcislot_imap: Bogus slot on PBM A (%ld)\n", slot);
prom_halt();
}
- nregs = (err / sizeof(struct linux_prom_pci_registers));
+ if(slot == 0)
+ return imap_offset(imap_a_slot0);
+ else
+ return imap_offset(imap_a_slot1);
+ } else {
+ switch(slot) {
+ case 0:
+ return imap_offset(imap_b_slot0);
+ case 1:
+ return imap_offset(imap_b_slot1);
+ case 2:
+ return imap_offset(imap_b_slot2);
+ case 3:
+ return imap_offset(imap_b_slot3);
+ default:
+ prom_printf("pcislot_imap: IMPOSSIBLE [%d:%d]\n",
+ bus, slot);
+ prom_halt();
+ return 0; /* Make gcc happy */
+ };
+ }
+}
- devfn = (pregs[0].phys_hi >> 8) & 0xff;
- if(devfn == pdev->devfn) {
+/* Exported for EBUS probing layer. */
+unsigned int psycho_irq_build(unsigned int full_ino)
+{
+ unsigned long imap_off, ign, ino;
- return;
+ ign = (full_ino & PSYCHO_IMAP_IGN) >> 6;
+ ino = (full_ino & PSYCHO_IMAP_INO);
+
+ /* Compute IMAP register offset, generic IRQ layer figures out
+ * the ICLR register address as this is simple given the 32-bit
+ * irq number and IMAP register address.
+ */
+ if((ino & 0x20) == 0)
+ imap_off = psycho_pcislot_imap_offset(ino);
+ else {
+ switch(ino) {
+ case 0x20:
+ /* Onboard SCSI. */
+ imap_off = imap_offset(imap_scsi);
+ break;
+
+ case 0x21:
+ /* Onboard Ethernet (ie. CheerIO/HME) */
+ imap_off = imap_offset(imap_eth);
+ break;
+
+ case 0x22:
+ /* Onboard Parallel Port */
+ imap_off = imap_offset(imap_bpp);
+ break;
+
+ case 0x23:
+ /* Audio Record */
+ imap_off = imap_offset(imap_au_rec);
+ break;
+
+ case 0x24:
+ /* Audio Play */
+ imap_off = imap_offset(imap_au_play);
+ break;
+
+ case 0x25:
+ /* Power Fail */
+ imap_off = imap_offset(imap_pfail);
+ break;
+
+ case 0x26:
+ /* Onboard KBD/MOUSE/SERIAL */
+ imap_off = imap_offset(imap_kms);
+ break;
+
+ case 0x27:
+ /* Floppy (ie. fdthree) */
+ imap_off = imap_offset(imap_flpy);
+ break;
+
+ case 0x28:
+ /* Spare HW INT */
+ imap_off = imap_offset(imap_shw);
+ break;
+
+ case 0x29:
+ /* Onboard Keyboard (only) */
+ imap_off = imap_offset(imap_kbd);
+ break;
+
+ case 0x2a:
+ /* Onboard Mouse (only) */
+ imap_off = imap_offset(imap_ms);
+ break;
+
+ case 0x2b:
+ /* Onboard Serial (only) */
+ imap_off = imap_offset(imap_ser);
+ break;
+
+ case 0x32:
+ /* Power Management */
+ imap_off = imap_offset(imap_pmgmt);
+ break;
+
+ default:
+ /* We don't expect anything else. The other possible
+ * values are not found in PCI device nodes, and are
+ * so hardware specific that they should use DCOOKIE's
+ * anyways.
+ */
+ prom_printf("psycho_irq_build: Wacky INO [%x]\n", ino);
+ prom_halt();
+ };
+ }
+ imap_off -= imap_offset(imap_a_slot0);
+
+ return pci_irq_encode(imap_off, 0 /* XXX */, ign, ino);
+}
+
+/* #define FIXUP_IRQ_DEBUG */
+
+static void fixup_irq(struct pci_dev *pdev,
+ struct linux_pbm_info *pbm,
+ int node)
+{
+ unsigned int prom_irq, portid = pbm->parent->upa_portid;
+ unsigned char pci_irq_line = pdev->irq;
+ int err;
+
+#ifdef FIXUP_IRQ_DEBUG
+ printk("fixup_irq[%s:%s]: ",
+ pci_strvendor(pdev->vendor),
+ pci_strdev(pdev->vendor, pdev->device));
+#endif
+ err = prom_getproperty(node, "interrupts", (void *)&prom_irq, sizeof(prom_irq));
+ if(err == 0 || err == -1) {
+ prom_printf("fixup_irq: No interrupts property for dev[%s:%s]\n",
+ pci_strvendor(pdev->vendor),
+ pci_strdev(pdev->vendor, pdev->device));
+ prom_halt();
+ }
+
+ /* See if fully specified already (ie. for onboard devices like hme) */
+ if(((prom_irq & PSYCHO_IMAP_IGN) >> 6) == pbm->parent->upa_portid) {
+ pdev->irq = psycho_irq_build(prom_irq);
+#ifdef FIXUP_IRQ_DEBUG
+ printk("fully specified prom_irq[%x] pdev->irq[%x]",
+ prom_irq, pdev->irq);
+#endif
+ } else {
+ unsigned int bus, slot, line;
+
+ bus = (pbm == &pbm->parent->pbm_B) ? (1 << 4) : 0;
+ line = (pci_irq_line) & 3;
+
+ /* Slot determination is only slightly complex. Handle
+ * the easy case first.
+ */
+ if(pdev->bus->number == pbm->pci_first_busno) {
+ if(pbm == &pbm->parent->pbm_A)
+ slot = (pdev->devfn >> 3) - 1;
+ else
+ slot = ((pdev->devfn >> 3) >> 1) - 1;
+ } else {
+ /* Underneath a bridge, use slot number of parent
+ * bridge.
+ */
+ slot = (pdev->bus->self->devfn >> 3) - 1;
+
+ /* Use low slot number bits of child as IRQ line. */
+ line = ((pdev->devfn >> 3) & 3);
}
+ slot = (slot << 2);
- node = prom_getsibling(node);
+ pdev->irq = psycho_irq_build((((portid << 6) & PSYCHO_IMAP_IGN) |
+ (bus | slot | line)));
+#ifdef FIXUP_IRQ_DEBUG
+ do {
+ unsigned char iline, ipin;
+
+ (void)pcibios_read_config_byte(pdev->bus->number,
+ pdev->devfn,
+ PCI_INTERRUPT_PIN,
+ &ipin);
+ (void)pcibios_read_config_byte(pdev->bus->number,
+ pdev->devfn,
+ PCI_INTERRUPT_LINE,
+ &iline);
+ printk("FIXED portid[%x] bus[%x] slot[%x] line[%x] irq[%x] "
+ "iline[%x] ipin[%x] prom_irq[%x]",
+ portid, bus>>4, slot>>2, line, pdev->irq,
+ iline, ipin, prom_irq);
+ } while(0);
+#endif
}
+#ifdef FIXUP_IRQ_DEBUG
+ printk("\n");
+#endif
+}
- prom_printf("fixup_pci_dev: Cannot find prom node for PCI device\n");
- prom_halt();
+static void fixup_doit(struct pci_dev *pdev,
+ struct linux_pbm_info *pbm,
+ struct linux_prom_pci_registers *pregs,
+ int nregs,
+ int node)
+{
+ struct linux_prom_pci_registers assigned[PROMREG_MAX];
+ int numaa, err;
+
+ /* Get assigned addresses, if any. */
+ err = prom_getproperty(node, "assigned-addresses",
+ (char *)&assigned[0], sizeof(assigned));
+ if(err == 0 || err == -1)
+ numaa = 0;
+ else
+ numaa = (err / sizeof(struct linux_prom_pci_registers));
+
+ /* First, scan and fixup base registers. */
+ fixup_regs(pdev, pbm, pregs, nregs, &assigned[0], numaa);
+
+ /* Next, fixup interrupt numbers. */
+ fixup_irq(pdev, pbm, node);
+}
+
+static void fixup_pci_dev(struct pci_dev *pdev,
+ struct pci_bus *pbus,
+ struct linux_pbm_info *pbm)
+{
+ struct linux_prom_pci_registers pregs[PROMREG_MAX];
+ struct pcidev_cookie *pcp = pdev->sysdata;
+ int node, nregs, err;
+
+ /* If this is a PCI bridge, we must program it. */
+ if(pdev->class >> 8 == PCI_CLASS_BRIDGE_PCI) {
+ unsigned short cmd;
+
+ /* First, enable bus mastering. */
+ pcibios_read_config_word(pdev->bus->number,
+ pdev->devfn,
+ PCI_COMMAND, &cmd);
+ cmd |= PCI_COMMAND_MASTER;
+ pcibios_write_config_word(pdev->bus->number,
+ pdev->devfn,
+ PCI_COMMAND, cmd);
+
+ /* Now, set cache line size to 64-bytes. */
+ pcibios_write_config_byte(pdev->bus->number,
+ pdev->devfn,
+ PCI_CACHE_LINE_SIZE, 64);
+ }
+
+ /* Ignore if this is one of the PBM's, EBUS, or a
+ * sub-bridge underneath the PBM. We only need to fixup
+ * true devices.
+ */
+ if((pdev->class >> 8 == PCI_CLASS_BRIDGE_PCI) ||
+ (pdev->class >> 8 == PCI_CLASS_BRIDGE_HOST) ||
+ (pdev->class >> 8 == PCI_CLASS_BRIDGE_OTHER) ||
+ (pcp == NULL))
+ return;
+
+ node = pcp->prom_node;
+
+ err = prom_getproperty(node, "reg", (char *)&pregs[0], sizeof(pregs));
+ if(err == 0 || err == -1) {
+ prom_printf("Cannot find REG for pci_dev\n");
+ prom_halt();
+ }
+
+ nregs = (err / sizeof(pregs[0]));
+
+ fixup_doit(pdev, pbm, &pregs[0], nregs, node);
}
static void fixup_pci_bus(struct pci_bus *pbus, struct linux_pbm_info *pbm)
@@ -344,16 +1090,13 @@
*/
static void psycho_final_fixup(struct linux_psycho *psycho)
{
- /* First, walk all PCI devices found. For each device, and
- * PCI bridge which is not one of the PSYCHO PBM's, fill in the
- * sysdata with a pointer to the PBM.
- */
- fill_in_pbm_cookies(&psycho->pbm_A);
- fill_in_pbm_cookies(&psycho->pbm_B);
-
/* Second, fixup base address registers and IRQ lines... */
fixup_addr_irq(&psycho->pbm_A);
fixup_addr_irq(&psycho->pbm_B);
+
+#if 0
+ prom_halt();
+#endif
}
unsigned long pcibios_fixup(unsigned long memory_start, unsigned long memory_end)
@@ -380,9 +1123,25 @@
/* Probe busses under PBM B. */
pbm_probe(&psycho->pbm_B, &memory_start);
+ pci_init_alloc_init(&memory_start);
+
+ /* Walk all PCI devices found. For each device, and
+ * PCI bridge which is not one of the PSYCHO PBM's, fill in the
+ * sysdata with a pointer to the PBM (for pci_bus's) or
+ * a pci_dev cookie (PBM+PROM_NODE, for pci_dev's).
+ */
+ fill_in_pbm_cookies(&psycho->pbm_A);
+ fill_in_pbm_cookies(&psycho->pbm_B);
+
+ /* See what OBP has taken care of already. */
+ record_assignments(&psycho->pbm_A);
+ record_assignments(&psycho->pbm_B);
+ /* Now, fix it all up. */
psycho_final_fixup(psycho);
+ pci_init_alloc_fini();
+
return ebus_init(memory_start, memory_end);
}
@@ -464,8 +1223,6 @@
if(out_of_range(bus, device_fn))
return PCIBIOS_SUCCESSFUL;
- /* XXX Check no-probe-list conflicts here. XXX */
-
pci_poke_in_progress = 1;
pci_poke_faulted = 0;
__asm__ __volatile__("membar #Sync\n\t"
@@ -485,9 +1242,9 @@
*value = (word >> 16) & 0xffff;
break;
default:
- prom_printf("pcibios_read_config_word: misaligned "
- "reg [%x]\n", where);
- prom_halt();
+ printk("pcibios_read_config_word: misaligned "
+ "reg [%x]\n", where);
+ break;
};
}
return PCIBIOS_SUCCESSFUL;
@@ -504,8 +1261,6 @@
if(out_of_range(bus, device_fn))
return PCIBIOS_SUCCESSFUL;
- /* XXX Check no-probe-list conflicts here. XXX */
-
pci_poke_in_progress = 1;
pci_poke_faulted = 0;
__asm__ __volatile__("membar #Sync\n\t"
@@ -529,8 +1284,6 @@
if(out_of_range(bus, device_fn))
return PCIBIOS_SUCCESSFUL;
- /* XXX Check no-probe-list conflicts here. XXX */
-
pci_poke_in_progress = 1;
/* Endianness doesn't matter but we have to get the memory
@@ -554,8 +1307,6 @@
if(out_of_range(bus, device_fn))
return PCIBIOS_SUCCESSFUL;
- /* XXX Check no-probe-list conflicts here. XXX */
-
pci_poke_in_progress = 1;
__asm__ __volatile__("membar #Sync\n\t"
"stha %0, [%1] %2\n\t"
@@ -573,8 +1324,6 @@
if(out_of_range(bus, device_fn))
return PCIBIOS_SUCCESSFUL;
-
- /* XXX Check no-probe-list conflicts here. XXX */
pci_poke_in_progress = 1;
__asm__ __volatile__("membar #Sync\n\t"
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov