summaryrefslogtreecommitdiffstats
path: root/arch/sparc64/kernel/psycho.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/sparc64/kernel/psycho.c')
-rw-r--r--arch/sparc64/kernel/psycho.c433
1 files changed, 310 insertions, 123 deletions
diff --git a/arch/sparc64/kernel/psycho.c b/arch/sparc64/kernel/psycho.c
index 96b1ac2e9..7df9a5882 100644
--- a/arch/sparc64/kernel/psycho.c
+++ b/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.85 1999/04/02 14:54:28 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 @@ asmlinkage int sys_pciconfig_write(unsigned long bus,
#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 @@ static __inline__ void set_dvma_hash(unsigned long paddr, unsigned long daddr)
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 @@ __initfunc(static void psycho_iommu_init(struct linux_psycho *psycho, int tsbsiz
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;
- for (i = 0; i < (mlist->num_bytes >> 16); i++) {
+ 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);
+
+ /* 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 @@ out:
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 @@ extern void prom_pbm_intmap_init(int node, struct linux_pbm_info *pbm);
/*
* 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 @@ __initfunc(void sabre_init(int pnode))
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 @@ __initfunc(void sabre_init(int pnode))
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 @@ apb_present(struct linux_psycho *psycho)
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 @@ __initfunc(void pcibios_init(void))
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 @@ __initfunc(void pcibios_init(void))
}
}
+ 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 @@ __initfunc(void pcibios_init(void))
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,25 +675,28 @@ static inline struct pcidev_cookie *pci_devcookie_alloc(void)
}
-__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;
+ int is_multi = 0;
for (devfn = 0; devfn < 0xff; ++devfn) {
- if (PCI_FUNC(devfn) == 0) {
- pbm_read_config_byte(pbm, bus, devfn,
- PCI_HEADER_TYPE, &hdr_type);
- } else if (!(hdr_type & 0x80)) {
+ if (PCI_FUNC(devfn) != 0 && is_multi == 0) {
/* not a multi-function device */
continue;
}
+ pbm_read_config_byte(pbm, bus, devfn,
+ PCI_HEADER_TYPE, &hdr_type);
+ if (PCI_FUNC(devfn) == 0)
+ is_multi = hdr_type & 0x80;
/* Check if there is anything here. */
pbm_read_config_dword(pbm, bus, devfn, PCI_VENDOR_ID, &l);
- if (l == 0xffffffff || l == 0x00000000) {
- hdr_type = 0;
+ if (l == 0xffffffff || l == 0x00000000 ||
+ l == 0x0000ffff || l == 0xffff0000) {
+ is_multi = 0;
continue;
}
@@ -657,7 +726,7 @@ pbm_reconfigure_bridges(struct linux_pbm_info *pbm, unsigned char bus))
}
}
-__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 +751,7 @@ __initfunc(static void pbm_fixup_busno(struct linux_pbm_info *pbm, unsigned char
} 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 +760,20 @@ __initfunc(static void apb_init(struct linux_psycho *sabre))
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 +788,25 @@ __initfunc(static void apb_init(struct linux_psycho *sabre))
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 +828,7 @@ __initfunc(static void sabre_probe(struct linux_psycho *sabre))
}
-__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 +867,9 @@ __initfunc(static void pbm_probe(struct linux_pbm_info *pbm))
}
}
-__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 +891,8 @@ __initfunc(static int pdev_to_pnode_sibtraverse(struct linux_pbm_info *pbm,
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 +910,9 @@ __initfunc(static void pdev_cookie_fillin(struct linux_pbm_info *pbm,
#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 +932,7 @@ __initfunc(static void fill_in_pbm_cookies(struct pci_bus *pbus,
}
}
-__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 +950,9 @@ __initfunc(static void sabre_cookie_fillin(struct linux_psycho *sabre))
* 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 +975,7 @@ __initfunc(static int gimme_ebus_assignments(int node, struct linux_prom_pci_reg
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 +1032,7 @@ __initfunc(static void assignment_process(struct linux_pbm_info *pbm, int node))
}
}
-__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 +1141,12 @@ static inline void record_assignments(struct linux_pbm_info *pbm)
#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;
@@ -1173,12 +1237,13 @@ __initfunc(static void fixup_regs(struct pci_dev *pdev,
}
if (bsreg == PCI_ROM_ADDRESS) {
pdev->rom_address = (unsigned long)__va(pci_addr);
- pdev->rom_address |= 1;
+ pdev->rom_address &= ~1UL;
+
/*
- * Enable access to the ROM.
+ * Disable access to the ROM.
*/
pci_read_config_dword(pdev, PCI_ROM_ADDRESS, &rtmp);
- pci_write_config_dword(pdev, PCI_ROM_ADDRESS, rtmp | 1);
+ pci_write_config_dword(pdev, PCI_ROM_ADDRESS, rtmp & ~1);
} else
pdev->base_address[brindex] = (unsigned long)__va(pci_addr);
@@ -1347,7 +1412,7 @@ __initfunc(static void fixup_regs(struct pci_dev *pdev,
rtmp = new_base;
pci_read_config_dword(pdev, breg, &base);
- rtmp |= (base & ~PCI_ROM_ADDRESS_MASK);
+ rtmp &= ~(base & ~PCI_ROM_ADDRESS_MASK);
pci_write_config_dword(pdev, breg, rtmp);
/* Apply PBM ranges and update pci_dev. */
@@ -1370,8 +1435,7 @@ __initfunc(static void fixup_regs(struct pci_dev *pdev,
"PBM ranges\n");
}
pdev->rom_address = (unsigned long)__va(pci_addr);
-
- pdev->rom_address |= (base & ~PCI_ROM_ADDRESS_MASK);
+ pdev->rom_address &= ~(base & ~PCI_ROM_ADDRESS_MASK);
MEM_seen = 1;
}
rom_address_done:
@@ -1415,7 +1479,7 @@ __initfunc(static void fixup_regs(struct pci_dev *pdev,
#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 +1495,8 @@ __initfunc(static unsigned long psycho_pcislot_imap_offset(unsigned long ino))
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 +1507,16 @@ __initfunc(static unsigned long psycho_pcislot_imap_offset(unsigned long ino))
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;
@@ -1533,6 +1591,36 @@ __initfunc(unsigned int psycho_irq_build(struct linux_pbm_info *pbm,
imap_off = imap_offset(imap_ser);
break;
+ case 0x2c:
+ /* Onboard Timer 0 */
+ imap_off = imap_offset(imap_tim0);
+ break;
+
+ case 0x2d:
+ /* Onboard Timer 1 */
+ imap_off = imap_offset(imap_tim1);
+ break;
+
+ case 0x2e:
+ /* Psycho UE Interrupt */
+ imap_off = imap_offset(imap_ue);
+ break;
+
+ case 0x2f:
+ /* Psycho CE Interrupt */
+ imap_off = imap_offset(imap_ce);
+ break;
+
+ case 0x30:
+ /* Psycho PCI A Error Interrupt */
+ imap_off = imap_offset(imap_a_err);
+ break;
+
+ case 0x31:
+ /* Psycho PCI B Error Interrupt */
+ imap_off = imap_offset(imap_b_err);
+ break;
+
case 0x32:
/* Power Management */
imap_off = imap_offset(imap_pmgmt);
@@ -1554,37 +1642,78 @@ __initfunc(unsigned int psycho_irq_build(struct linux_pbm_info *pbm,
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,13 +1747,12 @@ out:
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;
int err;
#ifdef FIXUP_IRQ_DEBUG
@@ -1668,7 +1796,25 @@ __initfunc(static void fixup_irq(struct pci_dev *pdev,
unsigned int bus, slot, line;
bus = (pbm == &pbm->parent->pbm_B) ? (1 << 4) : 0;
- line = (pci_irq_line) & 3;
+
+ /* Use the given interrupt property value as the line if it
+ * is non-zero and legal. Legal encodings are INTA=1, INTB=2,
+ * INTC=3, INTD=4 as per PCI OBP binding spec version 2.1 -DaveM
+ */
+ if(prom_irq > 0 && prom_irq < 5) {
+ line = ((prom_irq - 1) & 3);
+ } else {
+ unsigned char pci_irq_line;
+
+ /* The generic PCI probing layer will read the
+ * interrupt line into pdev->irq if the interrupt
+ * pin is non-zero, so we have to explicitly fetch
+ * the pin here to be certain (the interrupt line is
+ * typically left at zero by OBP).
+ */
+ pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &pci_irq_line);
+ line = ((pci_irq_line - 1) & 3);
+ }
/* Slot determination is only slightly complex. Handle
* the easy case first.
@@ -1721,11 +1867,11 @@ __initfunc(static void fixup_irq(struct pci_dev *pdev,
#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 +1891,9 @@ __initfunc(static void fixup_doit(struct pci_dev *pdev,
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 +1908,12 @@ __initfunc(static void fixup_pci_dev(struct pci_dev *pdev,
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 +1958,7 @@ __initfunc(static void fixup_pci_dev(struct pci_dev *pdev,
}
}
-__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 +1969,7 @@ __initfunc(static void fixup_pci_bus(struct pci_bus *pbus, struct linux_pbm_info
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 +1982,7 @@ __initfunc(static void fixup_addr_irq(struct linux_pbm_info *pbm))
/* 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 +1991,33 @@ __initfunc(static void psycho_final_fixup(struct linux_psycho *psycho))
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 +2037,9 @@ __initfunc(void pcibios_fixup(void))
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 +2072,9 @@ __initfunc(void pcibios_fixup(void))
psycho_final_fixup(psycho);
}
+ if (psycho_reorder & PSYCHO_REORDER_ONBOARDFIRST)
+ psycho_reorder_devs();
+
return ebus_init();
}
@@ -2418,12 +2597,20 @@ asmlinkage int sys_pciconfig_write(unsigned long bus,
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;
}