patch-2.2.4 linux/arch/sparc64/kernel/psycho.c
Next file: linux/arch/sparc64/kernel/ptrace.c
Previous file: linux/arch/sparc64/kernel/process.c
Back to the patch index
Back to the overall index
- Lines: 735
- Date:
Fri Mar 19 07:57:45 1999
- Orig file:
v2.2.3/linux/arch/sparc64/kernel/psycho.c
- Orig date:
Thu Nov 19 09:56:27 1998
diff -u --recursive --new-file v2.2.3/linux/arch/sparc64/kernel/psycho.c linux/arch/sparc64/kernel/psycho.c
@@ -1,8 +1,9 @@
-/* $Id: psycho.c,v 1.66 1998/11/02 22:27:45 davem Exp $
+/* $Id: psycho.c,v 1.79 1999/03/19 05:38:46 davem Exp $
* psycho.c: Ultra/AX U2P PCI controller support.
*
* Copyright (C) 1997 David S. Miller (davem@caipfs.rutgers.edu)
* Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be)
+ * Copyright (C) 1999 Jakub Jelinek (jj@ultra.linux.cz)
*/
#include <linux/config.h>
@@ -29,14 +30,13 @@
#define dprintf printk
#endif
-
unsigned long pci_dvma_offset = 0x00000000UL;
unsigned long pci_dvma_mask = 0xffffffffUL;
+#define PCI_DVMA_HASH_NONE 0xffffffffffffffffUL
unsigned long pci_dvma_v2p_hash[PCI_DVMA_HASHSZ];
unsigned long pci_dvma_p2v_hash[PCI_DVMA_HASHSZ];
-
#ifndef CONFIG_PCI
int pcibios_present(void)
@@ -74,9 +74,12 @@
#include <asm/apb.h>
#include <asm/uaccess.h>
+#define PSYCHO_REORDER_ONBOARDFIRST 1
+
struct linux_psycho *psycho_root = NULL;
int linux_num_psycho = 0;
static struct linux_pbm_info *bus2pbm[256];
+static int psycho_reorder __initdata = 0;
static int pbm_read_config_byte(struct linux_pbm_info *pbm,
unsigned char bus, unsigned char devfn,
@@ -112,8 +115,10 @@
pci_dvma_p2v_hash[pci_dvma_ahashfn(dvma_addr)] = vaddr - dvma_addr;
}
-__initfunc(static void psycho_iommu_init(struct linux_psycho *psycho, int tsbsize))
+static void __init psycho_iommu_init(struct linux_psycho *psycho, int tsbsize)
{
+ extern int this_is_starfire;
+ extern void *starfire_hookup(int);
struct linux_mlist_p1275 *mlist;
unsigned long tsbbase;
unsigned long control, i, n;
@@ -137,37 +142,77 @@
break;
}
tsbbase = __get_free_pages(GFP_DMA, order);
+ if (!tsbbase) {
+ prom_printf("IOMMU: Error, kmalloc(tsb) failed.\n");
+ prom_halt();
+ }
iopte = (unsigned long *)tsbbase;
- memset(pci_dvma_v2p_hash, 0, sizeof(pci_dvma_v2p_hash));
- memset(pci_dvma_p2v_hash, 0, sizeof(pci_dvma_p2v_hash));
+ /* Initialize to "none" settings. */
+ for(i = 0; i < PCI_DVMA_HASHSZ; i++) {
+ pci_dvma_v2p_hash[i] = PCI_DVMA_HASH_NONE;
+ pci_dvma_p2v_hash[i] = PCI_DVMA_HASH_NONE;
+ }
n = 0;
mlist = *prom_meminfo()->p1275_totphys;
while (mlist) {
unsigned long paddr = mlist->start_adr;
+ unsigned long num_bytes = mlist->num_bytes;
+
+ if(paddr >= (((unsigned long) high_memory) - PAGE_OFFSET))
+ goto next;
+
+ if((paddr + num_bytes) >= (((unsigned long) high_memory) - PAGE_OFFSET))
+ num_bytes = (((unsigned long) high_memory) - PAGE_OFFSET) - paddr;
+
+ /* Align base and length so we map whole hash table sized chunks
+ * at a time (and therefore full 64K IOMMU pages).
+ */
+ paddr &= ~((1UL << 24UL) - 1);
+ num_bytes = (num_bytes + ((1UL << 24UL) - 1)) & ~((1UL << 24) - 1);
- for (i = 0; i < (mlist->num_bytes >> 16); i++) {
+ /* Move up the base for mappings already created. */
+ while(pci_dvma_v2p_hash[pci_dvma_ahashfn(paddr)] !=
+ PCI_DVMA_HASH_NONE) {
+ paddr += (1UL << 24UL);
+ num_bytes -= (1UL << 24UL);
+ if(num_bytes == 0UL)
+ goto next;
+ }
+
+ /* Move down the size for tail mappings already created. */
+ while(pci_dvma_v2p_hash[pci_dvma_ahashfn(paddr + num_bytes - (1UL << 24UL))] !=
+ PCI_DVMA_HASH_NONE) {
+ num_bytes -= (1UL << 24UL);
+ if(num_bytes == 0UL)
+ goto next;
+ }
+ /* Now map the rest. */
+ for (i = 0; i < ((num_bytes + ((1 << 16) - 1)) >> 16); i++) {
*iopte = (IOPTE_VALID | IOPTE_64K |
IOPTE_CACHE | IOPTE_WRITE);
*iopte |= paddr;
if (!(n & 0xff))
set_dvma_hash(paddr, (n << 16));
-
+
if (++n > (tsbsize * 1024))
goto out;
paddr += (1 << 16);
iopte++;
}
-
+ next:
mlist = mlist->theres_more;
}
out:
- if (mlist)
- printk("WARNING: not all physical memory mapped in IOMMU\n");
+ if (mlist) {
+ prom_printf("WARNING: not all physical memory mapped in IOMMU\n");
+ prom_printf("Try booting with mem=xxxM or similar\n");
+ prom_halt();
+ }
psycho->psycho_regs->iommu_tsbbase = __pa(tsbbase);
@@ -193,6 +238,12 @@
break;
}
psycho->psycho_regs->iommu_control = control;
+
+ /* If necessary, hook us up for starfire IRQ translations. */
+ if(this_is_starfire)
+ psycho->starfire_cookie = starfire_hookup(psycho->upa_portid);
+ else
+ psycho->starfire_cookie = NULL;
}
extern void prom_pbm_ranges_init(int node, struct linux_pbm_info *pbm);
@@ -201,7 +252,7 @@
/*
* Poor man's PCI...
*/
-__initfunc(void sabre_init(int pnode))
+void __init sabre_init(int pnode)
{
struct linux_prom64_registers pr_regs[2];
struct linux_psycho *sabre;
@@ -213,6 +264,10 @@
int bus;
sabre = kmalloc(sizeof(struct linux_psycho), GFP_ATOMIC);
+ if (!sabre) {
+ prom_printf("SABRE: Error, kmalloc(sabre) failed.\n");
+ prom_halt();
+ }
portid = prom_getintdefault(pnode, "upa-portid", 0xff);
@@ -248,9 +303,11 @@
prom_halt();
}
- printk("PCI: Found SABRE, main regs at %p\n", sabre->psycho_regs);
+ printk("PCI: Found SABRE, main regs at %p CTRL[%016lx]\n",
+ sabre->psycho_regs, sabre->psycho_regs->control);
#ifdef PROM_DEBUG
- dprintf("PCI: Found SABRE, main regs at %p\n", sabre->psycho_regs);
+ dprintf("PCI: Found SABRE, main regs at %p CTRL[%016lx]\n",
+ sabre->psycho_regs, sabre->psycho_regs->control);
#endif
ctrl = sabre->psycho_regs->pci_a_control;
@@ -382,7 +439,7 @@
return psycho->pci_bus ? 1 : 0;
}
-__initfunc(void pcibios_init(void))
+void __init pcibios_init(void)
{
struct linux_prom64_registers pr_regs[3];
struct linux_psycho *psycho;
@@ -408,8 +465,6 @@
goto next_pci;
}
- psycho = kmalloc(sizeof(struct linux_psycho), GFP_ATOMIC);
-
portid = prom_getintdefault(node, "upa-portid", 0xff);
for(search = psycho_root; search; search = search->next) {
if(search->upa_portid == portid) {
@@ -424,6 +479,11 @@
}
}
+ psycho = kmalloc(sizeof(struct linux_psycho), GFP_ATOMIC);
+ if (!psycho) {
+ prom_printf("PSYCHO: Error, kmalloc(psycho) failed.\n");
+ prom_halt();
+ }
memset(psycho, 0, sizeof(*psycho));
psycho->next = psycho_root;
@@ -494,8 +554,14 @@
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;
+ psycho->psycho_regs->pci_a_control |= PSYCHO_PCICTRL_AEN;
+ psycho->psycho_regs->pci_b_control |= PSYCHO_PCICTRL_AEN;
+
+ /* Disable DMA write / PIO rd synchronization on both
+ * PCI bus segments.
+ */
+ psycho->psycho_regs->pci_a_diag |= PSYCHO_PCIDIAG_DDWSYNC;
+ psycho->psycho_regs->pci_b_diag |= PSYCHO_PCIDIAG_DDWSYNC;
other_pbm:
if(is_pbm_a)
@@ -609,8 +675,8 @@
}
-__initfunc(static void
-pbm_reconfigure_bridges(struct linux_pbm_info *pbm, unsigned char bus))
+static void __init
+pbm_reconfigure_bridges(struct linux_pbm_info *pbm, unsigned char bus)
{
unsigned int devfn, l, class;
unsigned char hdr_type = 0;
@@ -657,7 +723,7 @@
}
}
-__initfunc(static void pbm_fixup_busno(struct linux_pbm_info *pbm, unsigned char bus))
+static void __init pbm_fixup_busno(struct linux_pbm_info *pbm, unsigned char bus)
{
unsigned int nbus;
@@ -682,8 +748,7 @@
} while (nbus--);
}
-
-__initfunc(static void apb_init(struct linux_psycho *sabre))
+static void __init apb_init(struct linux_psycho *sabre)
{
struct pci_dev *pdev;
unsigned short stmp;
@@ -692,21 +757,20 @@
for(pdev = pci_devices; pdev; pdev = pdev->next) {
if(pdev->vendor == PCI_VENDOR_ID_SUN &&
pdev->device == PCI_DEVICE_ID_SUN_SABRE) {
- /* Increase latency timer on top level bridge. */
- pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xf8);
+ pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 128);
break;
}
}
for (pdev = sabre->pci_bus->devices; pdev; pdev = pdev->sibling) {
if (pdev->vendor == PCI_VENDOR_ID_SUN &&
pdev->device == PCI_DEVICE_ID_SUN_SIMBA) {
-
pci_read_config_word(pdev, PCI_COMMAND, &stmp);
stmp |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY |
PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY |
PCI_COMMAND_IO;
pci_write_config_word(pdev, PCI_COMMAND, stmp);
+ /* Status register bits are "write 1 to clear". */
pci_write_config_word(pdev, PCI_STATUS, 0xffff);
pci_write_config_word(pdev, PCI_SEC_STATUS, 0xffff);
@@ -721,28 +785,25 @@
APB_PCI_CTL_HIGH_ARBITER_EN;
pci_write_config_dword(pdev, APB_PCI_CONTROL_HIGH, itmp);
+ /* Systems with SIMBA are usually workstations, so
+ * we configure to park to SIMBA not to the previous
+ * bus owner.
+ */
pci_read_config_dword(pdev, APB_PCI_CONTROL_LOW, &itmp);
- itmp = APB_PCI_CTL_LOW_ARB_PARK |
- APB_PCI_CTL_LOW_ERRINT_EN | 0x0f;
+ itmp = APB_PCI_CTL_LOW_ERRINT_EN | 0x0f;
pci_write_config_dword(pdev, APB_PCI_CONTROL_LOW, itmp);
- /*
- * Setup Registers for Guaranteed Completion.
+ /* Don't mess with the retry limit and PIO/DMA latency
+ * timer settings. But do set primary and secondary
+ * latency timers.
*/
- pci_write_config_byte(pdev, APB_PRIMARY_MASTER_RETRY_LIMIT, 0);
- pci_write_config_byte(pdev, APB_SECONDARY_MASTER_RETRY_LIMIT, 0);
- pci_write_config_byte(pdev, APB_PIO_TARGET_RETRY_LIMIT, 0x80);
- pci_write_config_byte(pdev, APB_PIO_TARGET_LATENCY_TIMER, 0);
- pci_write_config_byte(pdev, APB_DMA_TARGET_RETRY_LIMIT, 0x80);
- pci_write_config_byte(pdev, APB_DMA_TARGET_LATENCY_TIMER, 0);
-
- /* Increase primary latency timer. */
- pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xf8);
+ pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 128);
+ pci_write_config_byte(pdev, PCI_SEC_LATENCY_TIMER, 128);
}
}
}
-__initfunc(static void sabre_probe(struct linux_psycho *sabre))
+static void __init sabre_probe(struct linux_psycho *sabre)
{
struct pci_bus *pbus = sabre->pci_bus;
static unsigned char busno = 0;
@@ -764,7 +825,7 @@
}
-__initfunc(static void pbm_probe(struct linux_pbm_info *pbm))
+static void __init pbm_probe(struct linux_pbm_info *pbm)
{
static struct pci_bus *pchain = NULL;
struct pci_bus *pbus = &pbm->pci_bus;
@@ -803,9 +864,9 @@
}
}
-__initfunc(static int pdev_to_pnode_sibtraverse(struct linux_pbm_info *pbm,
- struct pci_dev *pdev,
- int pnode))
+static int __init pdev_to_pnode_sibtraverse(struct linux_pbm_info *pbm,
+ struct pci_dev *pdev,
+ int pnode)
{
struct linux_prom_pci_registers pregs[PROMREG_MAX];
int node;
@@ -827,8 +888,8 @@
return 0;
}
-__initfunc(static void pdev_cookie_fillin(struct linux_pbm_info *pbm,
- struct pci_dev *pdev, int pnode))
+static void __init pdev_cookie_fillin(struct linux_pbm_info *pbm,
+ struct pci_dev *pdev, int pnode)
{
struct pcidev_cookie *pcp;
int node;
@@ -846,9 +907,9 @@
#endif
}
-__initfunc(static void fill_in_pbm_cookies(struct pci_bus *pbus,
- struct linux_pbm_info *pbm,
- int node))
+static void __init fill_in_pbm_cookies(struct pci_bus *pbus,
+ struct linux_pbm_info *pbm,
+ int node)
{
struct pci_dev *pdev;
@@ -868,7 +929,7 @@
}
}
-__initfunc(static void sabre_cookie_fillin(struct linux_psycho *sabre))
+static void __init sabre_cookie_fillin(struct linux_psycho *sabre)
{
struct pci_bus *pbus = sabre->pci_bus;
@@ -886,9 +947,9 @@
* properties, and recording them in pci_vma's linked in via
* PBM->assignments.
*/
-__initfunc(static int gimme_ebus_assignments(int node, struct linux_prom_pci_registers *aregs))
+static int __init gimme_ebus_assignments(int node, struct linux_prom_pci_registers *aregs)
{
- struct linux_prom_ebus_ranges erng[PROMREG_MAX];
+ struct linux_prom_ebus_ranges erng[PROM_PCIRNG_MAX];
int err, iter;
err = prom_getproperty(node, "ranges", (char *)&erng[0], sizeof(erng));
@@ -911,7 +972,7 @@
return err;
}
-__initfunc(static void assignment_process(struct linux_pbm_info *pbm, int node))
+static void __init assignment_process(struct linux_pbm_info *pbm, int node)
{
struct linux_prom_pci_registers aregs[PROMREG_MAX];
char pname[256];
@@ -968,7 +1029,7 @@
}
}
-__initfunc(static void assignment_walk_siblings(struct linux_pbm_info *pbm, int node))
+static void __init assignment_walk_siblings(struct linux_pbm_info *pbm, int node)
{
while(node) {
int child = prom_getchild(node);
@@ -1077,12 +1138,12 @@
#endif
}
-__initfunc(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))
+static void __init 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;
@@ -1415,7 +1476,7 @@
#define imap_offset(__member) \
((unsigned long)(&(((struct psycho_regs *)0)->__member)))
-__initfunc(static unsigned long psycho_pcislot_imap_offset(unsigned long ino))
+static unsigned long __init psycho_pcislot_imap_offset(unsigned long ino)
{
unsigned int bus, slot;
@@ -1431,11 +1492,8 @@
case 2:
return imap_offset(imap_a_slot2);
case 3:
- return imap_offset(imap_a_slot3);
default:
- prom_printf("pcislot_imap: IMPOSSIBLE [%d:%d]\n",
- bus, slot);
- prom_halt();
+ return imap_offset(imap_a_slot3);
}
} else {
switch(slot) {
@@ -1446,19 +1504,16 @@
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 imap_offset(imap_b_slot3);
}
}
}
/* Exported for EBUS probing layer. */
-__initfunc(unsigned int psycho_irq_build(struct linux_pbm_info *pbm,
- struct pci_dev *pdev,
- unsigned int ino))
+unsigned int __init psycho_irq_build(struct linux_pbm_info *pbm,
+ struct pci_dev *pdev,
+ unsigned int ino)
{
unsigned long imap_off;
int need_dma_sync = 0;
@@ -1554,37 +1609,78 @@
return psycho_build_irq(pbm->parent, imap_off, ino, need_dma_sync);
}
-__initfunc(static int pbm_intmap_match(struct linux_pbm_info *pbm,
- struct pci_dev *pdev,
- struct linux_prom_pci_registers *preg,
- unsigned int *interrupt))
+static int __init pbm_intmap_match(struct linux_pbm_info *pbm,
+ struct pci_dev *pdev,
+ struct linux_prom_pci_registers *preg,
+ unsigned int *interrupt)
{
struct linux_prom_pci_registers ppreg;
unsigned int hi, mid, lo, irq;
int i;
- if (!pbm->num_pbm_intmap)
+#ifdef FIXUP_IRQ_DEBUG
+ dprintf("pbm_intmap_match: ");
+#endif
+ if (!pbm->num_pbm_intmap) {
+#ifdef FIXUP_IRQ_DEBUG
+ dprintf("No intmap UPA[%x:%c]\n",
+ pbm->parent->upa_portid,
+ (pbm == &pbm->parent->pbm_A) ? 'A' : 'B');
+#endif
return 0;
-
+ }
/*
* Underneath a bridge, use register of parent bridge.
*/
if (pdev->bus->number != pbm->pci_first_busno) {
- struct pcidev_cookie *pcp = pdev->bus->self->sysdata;
- int node;
+ struct pcidev_cookie *pcp;
+ int node, offset;
+ char prom_name[64];
- if (!pcp)
+#ifdef FIXUP_IRQ_DEBUG
+ dprintf("UnderBridge, ");
+#endif
+ pcp = pdev->bus->self->sysdata;
+ if (!pcp) {
+#ifdef FIXUP_IRQ_DEBUG
+ dprintf("No bus PCP\n");
+#endif
goto out;
-
+ }
node = pcp->prom_node;
i = prom_getproperty(node, "reg", (char*)&ppreg, sizeof(ppreg));
- if(i == 0 || i == -1)
+ if(i == 0 || i == -1) {
+#ifdef FIXUP_IRQ_DEBUG
+ dprintf("No reg property.\n");
+#endif
goto out;
+ }
+ /*
+ * Did PROM know better and assign an interrupt different
+ * to #INTA to the device? - We test here for presence of
+ * FCODE on the card, in this case we assume PROM has set
+ * correct 'interrupts' property, unless it is quadhme.
+ */
+ pcp = pdev->sysdata;
+ if (!pcp) {
+#ifdef FIXUP_IRQ_DEBUG
+ dprintf("No dev PCP\n");
+#endif
+ goto out;
+ }
+ node = pcp->prom_node;
- /* Use low slot number bits of child as IRQ line. */
- *interrupt = ((pdev->devfn >> 3) & 3) + 1;
-
+ offset = prom_getint(node, "fcode-rom-offset");
+ prom_getstring(node, "name", prom_name, sizeof(prom_name));
+ if (offset == -1 ||
+ !strcmp(prom_name, "SUNW,qfe") ||
+ !strcmp(prom_name, "qfe")) {
+ /*
+ * No, use low slot number bits of child as IRQ line.
+ */
+ *interrupt = ((*interrupt - 1 + PCI_SLOT(pdev->devfn)) & 3) + 1;
+ }
preg = &ppreg;
}
@@ -1618,10 +1714,10 @@
prom_halt();
}
-__initfunc(static void fixup_irq(struct pci_dev *pdev,
- struct linux_pbm_info *pbm,
- struct linux_prom_pci_registers *preg,
- int node))
+static void __init fixup_irq(struct pci_dev *pdev,
+ struct linux_pbm_info *pbm,
+ struct linux_prom_pci_registers *preg,
+ int node)
{
unsigned int prom_irq, portid = pbm->parent->upa_portid;
unsigned char pci_irq_line = pdev->irq;
@@ -1721,11 +1817,11 @@
#endif
}
-__initfunc(static void fixup_doit(struct pci_dev *pdev,
- struct linux_pbm_info *pbm,
- struct linux_prom_pci_registers *pregs,
- int nregs,
- int node))
+static void __init 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;
@@ -1745,9 +1841,9 @@
fixup_irq(pdev, pbm, &pregs[0], node);
}
-__initfunc(static void fixup_pci_dev(struct pci_dev *pdev,
- struct pci_bus *pbus,
- struct linux_pbm_info *pbm))
+static void __init 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;
@@ -1762,8 +1858,12 @@
cmd |= PCI_COMMAND_MASTER;
pci_write_config_word(pdev, PCI_COMMAND, cmd);
- /* Now, set cache line size to 64-bytes. */
- pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 64);
+ /* Now, set cache line size to 64-bytes.
+ * NOTE: Cache line size is in 32-bit word units.
+ */
+ pci_write_config_byte(pdev,
+ PCI_CACHE_LINE_SIZE,
+ (64 / sizeof(u32)));
}
/* Ignore if this is one of the PBM's, EBUS, or a
@@ -1808,7 +1908,7 @@
}
}
-__initfunc(static void fixup_pci_bus(struct pci_bus *pbus, struct linux_pbm_info *pbm))
+static void __init fixup_pci_bus(struct pci_bus *pbus, struct linux_pbm_info *pbm)
{
struct pci_dev *pdev;
@@ -1819,7 +1919,7 @@
fixup_pci_bus(pbus, pbm);
}
-__initfunc(static void fixup_addr_irq(struct linux_pbm_info *pbm))
+static void __init fixup_addr_irq(struct linux_pbm_info *pbm)
{
struct pci_bus *pbus = &pbm->pci_bus;
@@ -1832,7 +1932,7 @@
/* Walk all PCI devices probes, fixing up base registers and IRQ registers.
* We use OBP for most of this work.
*/
-__initfunc(static void psycho_final_fixup(struct linux_psycho *psycho))
+static void __init psycho_final_fixup(struct linux_psycho *psycho)
{
/* Second, fixup base address registers and IRQ lines... */
if (psycho->pbm_A.parent)
@@ -1841,7 +1941,33 @@
fixup_addr_irq(&psycho->pbm_B);
}
-__initfunc(void pcibios_fixup(void))
+/* Reorder the pci_dev chain, so that onboard devices come first
+ and then come the pluggable cards. */
+void __init psycho_reorder_devs(void)
+{
+ struct pci_dev **pci_onboard = &pci_devices;
+ struct pci_dev **pci_tail = &pci_devices;
+ struct pci_dev *pdev = pci_devices, *pci_other = NULL;
+
+ while (pdev) {
+ if (pdev->irq && (__irq_ino(pdev->irq) & 0x20)) {
+ if (pci_other) {
+ *pci_onboard = pdev;
+ pci_onboard = &pdev->next;
+ pdev = pdev->next;
+ *pci_onboard = pci_other;
+ *pci_tail = pdev;
+ continue;
+ } else
+ pci_onboard = &pdev->next;
+ } else if (!pci_other)
+ pci_other = pdev;
+ pci_tail = &pdev->next;
+ pdev = pdev->next;
+ }
+}
+
+void __init pcibios_fixup(void)
{
struct linux_psycho *psycho;
@@ -1861,9 +1987,9 @@
for (psycho = psycho_root; psycho; psycho = psycho->next) {
/* Probe bus on builtin PCI. */
- if (apb_present(psycho))
+ if (apb_present(psycho)) {
sabre_probe(psycho);
- else {
+ } else {
/* Probe busses under PBM B. */
pbm_probe(&psycho->pbm_B);
@@ -1896,6 +2022,9 @@
psycho_final_fixup(psycho);
}
+ if (psycho_reorder & PSYCHO_REORDER_ONBOARDFIRST)
+ psycho_reorder_devs();
+
return ebus_init();
}
@@ -2418,12 +2547,20 @@
return err;
}
-__initfunc(void pcibios_fixup_bus(struct pci_bus *bus))
+void __init pcibios_fixup_bus(struct pci_bus *bus)
{
}
-__initfunc(char *pcibios_setup(char *str))
+char * __init pcibios_setup(char *str)
{
+ if (!strcmp(str, "onboardfirst")) {
+ psycho_reorder |= PSYCHO_REORDER_ONBOARDFIRST;
+ return NULL;
+ }
+ if (!strcmp(str, "noreorder")) {
+ psycho_reorder = 0;
+ return NULL;
+ }
return str;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)