diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1998-03-17 22:05:47 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1998-03-17 22:05:47 +0000 |
commit | 27cfca1ec98e91261b1a5355d10a8996464b63af (patch) | |
tree | 8e895a53e372fa682b4c0a585b9377d67ed70d0e /arch/sparc64/kernel/psycho.c | |
parent | 6a76fb7214c477ccf6582bd79c5b4ccc4f9c41b1 (diff) |
Look Ma' what I found on my harddisk ...
o New faster syscalls for 2.1.x, too
o Upgrade to 2.1.89.
Don't try to run this. It's flaky as hell. But feel free to debug ...
Diffstat (limited to 'arch/sparc64/kernel/psycho.c')
-rw-r--r-- | arch/sparc64/kernel/psycho.c | 550 |
1 files changed, 414 insertions, 136 deletions
diff --git a/arch/sparc64/kernel/psycho.c b/arch/sparc64/kernel/psycho.c index 8aa1c342b..b3b403e33 100644 --- a/arch/sparc64/kernel/psycho.c +++ b/arch/sparc64/kernel/psycho.c @@ -1,7 +1,8 @@ -/* $Id: psycho.c,v 1.22 1997/08/31 03:51:40 davem Exp $ +/* $Id: psycho.c,v 1.31 1998/01/10 18:26:15 ecd 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) */ #include <linux/config.h> @@ -11,6 +12,16 @@ #include <asm/ebus.h> #include <asm/sbus.h> /* for sanity check... */ +#undef PROM_DEBUG +#undef FIXUP_REGS_DEBUG +#undef FIXUP_IRQ_DEBUG + +#ifdef PROM_DEBUG +#define dprintf prom_printf +#else +#define dprintf printk +#endif + #ifndef CONFIG_PCI int pcibios_present(void) @@ -49,6 +60,28 @@ asmlinkage int sys_pciconfig_write(unsigned long bus, #include <asm/uaccess.h> struct linux_psycho *psycho_root = NULL; +struct linux_psycho **psycho_index_map; +int linux_num_psycho = 0; +static struct linux_pbm_info *bus2pbm[256]; + +static int pbm_read_config_byte(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned char *value); +static int pbm_read_config_word(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned short *value); +static int pbm_read_config_dword(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned int *value); +static int pbm_write_config_byte(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned char value); +static int pbm_write_config_word(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned short value); +static int pbm_write_config_dword(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned int value); /* This is used to make the scan_bus in the generic PCI code be * a nop, as we need to control the actual bus probing sequence. @@ -69,10 +102,22 @@ static unsigned long psycho_iommu_init(struct linux_psycho *psycho, unsigned long control, i; unsigned long *iopte; + /* + * Invalidate TLB Entries. + */ + control = psycho->psycho_regs->iommu_control; + control |= IOMMU_CTRL_DENAB; + psycho->psycho_regs->iommu_control = control; + for(i = 0; i < 16; i++) { + psycho->psycho_regs->iommu_data[i] = 0; + } + control &= ~(IOMMU_CTRL_DENAB); + psycho->psycho_regs->iommu_control = control; + memory_start = (tsbbase + ((32 * 1024) * 8)); iopte = (unsigned long *)tsbbase; - for(i = 0; i < (65536 / 2); i++) { + for(i = 0; i < (32 * 1024); i++) { *iopte = (IOPTE_VALID | IOPTE_64K | IOPTE_CACHE | IOPTE_WRITE); *iopte |= (i << 16); @@ -94,21 +139,26 @@ 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) { struct linux_prom64_registers pr_regs[3]; + struct linux_psycho *psycho; char namebuf[128]; u32 portid; int node; printk("PSYCHO: Probing for controllers.\n"); +#ifdef PROM_DEBUG + dprintf("PSYCHO: Probing for controllers.\n"); +#endif memory_start = long_align(memory_start); node = prom_getchild(prom_root_node); while((node = prom_searchsiblings(node, "pci")) != 0) { - struct linux_psycho *psycho = (struct linux_psycho *)memory_start; struct linux_psycho *search; struct linux_pbm_info *pbm = NULL; u32 busrange[2]; int err, is_pbm_a; + psycho = (struct linux_psycho *)memory_start; + portid = prom_getintdefault(node, "upa-portid", 0xff); for(search = psycho_root; search; search = search->next) { if(search->upa_portid == portid) { @@ -123,7 +173,8 @@ unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end) } } - memory_start = long_align(memory_start + sizeof(struct linux_psycho)); + memory_start = long_align(memory_start + + sizeof(struct linux_psycho)); memset(psycho, 0, sizeof(*psycho)); @@ -131,8 +182,12 @@ unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end) psycho_root = psycho; psycho->upa_portid = portid; + psycho->index = linux_num_psycho++; - /* Map in PSYCHO register set and report the presence of this PSYCHO. */ + /* + * Map in PSYCHO register set and report the presence + * of this PSYCHO. + */ err = prom_getproperty(node, "reg", (char *)&pr_regs[0], sizeof(pr_regs)); if(err == 0 || err == -1) { @@ -141,7 +196,10 @@ unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end) prom_halt(); } - /* Third REG in property is base of entire PSYCHO register space. */ + /* + * Third REG in property is base of entire PSYCHO + * register space. + */ psycho->psycho_regs = sparc_alloc_io((pr_regs[2].phys_addr & 0xffffffff), NULL, sizeof(struct psycho_regs), "PSYCHO Registers", @@ -154,12 +212,19 @@ unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end) 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); +#ifdef PROM_DEBUG + dprintf("PSYCHO: Found controller, main regs at %p\n", + psycho->psycho_regs); #endif + psycho->psycho_regs->irq_retry = 0xff; +#if 0 + psycho->psycho_regs->ecc_control |= 1; + psycho->psycho_regs->sbuf_a_control = 0; + psycho->psycho_regs->sbuf_b_control = 0; +#endif + /* Now map in PCI config space for entire PSYCHO. */ psycho->pci_config_space = sparc_alloc_io(((pr_regs[2].phys_addr & 0xffffffff)+0x01000000), @@ -172,7 +237,12 @@ unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end) } /* Report some more info. */ - printk("PSYCHO: PCI config space at %p\n", psycho->pci_config_space); + printk("PSYCHO: PCI config space at %p\n", + psycho->pci_config_space); +#ifdef PROM_DEBUG + dprintf("PSYCHO: PCI config space at %p\n", + psycho->pci_config_space); +#endif memory_start = psycho_iommu_init(psycho, memory_start); @@ -223,6 +293,13 @@ unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end) prom_halt(); } + psycho_index_map = (struct linux_psycho **)long_align(memory_start); + memory_start = long_align(memory_start + linux_num_psycho + * sizeof(struct linux_psycho *)); + + for (psycho = psycho_root; psycho; psycho = psycho->next) + psycho_index_map[psycho->index] = psycho; + return memory_start; } @@ -361,18 +438,118 @@ static inline struct pcidev_cookie *pci_devcookie_alloc(void) return pci_init_alloc(sizeof(struct pcidev_cookie)); } + +static void +pbm_reconfigure_bridges(struct linux_pbm_info *pbm, unsigned char bus) +{ + unsigned int devfn, l, class; + unsigned char hdr_type = 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)) { + /* not a multi-function device */ + continue; + } + + /* 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; + continue; + } + + /* See if this is a bridge device. */ + pbm_read_config_dword(pbm, bus, devfn, + PCI_CLASS_REVISION, &class); + + if ((class >> 16) == PCI_CLASS_BRIDGE_PCI) { + unsigned int buses; + + pbm_read_config_dword(pbm, bus, devfn, + PCI_PRIMARY_BUS, &buses); + + /* + * First reconfigure everything underneath the bridge. + */ + pbm_reconfigure_bridges(pbm, (buses >> 8) & 0xff); + + /* + * Unconfigure this bridges bus numbers, + * pci_scan_bus() will fix this up properly. + */ + buses &= 0xff000000; + pbm_write_config_dword(pbm, bus, devfn, + PCI_PRIMARY_BUS, buses); + } + } +} + +static void pbm_fixup_busno(struct linux_pbm_info *pbm, unsigned char bus) +{ + unsigned int nbus; + + /* + * First, reconfigure all bridge devices underneath this pbm. + */ + pbm_reconfigure_bridges(pbm, pbm->pci_first_busno); + + /* + * Now reconfigure the pbm to it's new bus number and set up + * our bus2pbm mapping for this pbm. + */ + nbus = pbm->pci_last_busno - pbm->pci_first_busno; + + pbm_write_config_byte(pbm, pbm->pci_first_busno, 0, 0x40, bus); + + pbm->pci_first_busno = bus; + pbm_write_config_byte(pbm, bus, 0, 0x41, 0xff); + + do { + bus2pbm[bus++] = pbm; + } while (nbus--); +} + + static void pbm_probe(struct linux_pbm_info *pbm, unsigned long *mstart) { + static struct pci_bus *pchain = NULL; struct pci_bus *pbus = &pbm->pci_bus; + static unsigned char busno = 0; /* PSYCHO PBM's include child PCI bridges in bus-range property, * but we don't scan each of those ourselves, Linux generic PCI * probing code will find child bridges and link them into this * pbm's root PCI device hierarchy. */ - pbus->number = pbm->pci_first_busno; + + pbus->number = pbus->secondary = busno; pbus->sysdata = pbm; + + pbm_fixup_busno(pbm, busno); + pbus->subordinate = pci_scan_bus(pbus, mstart); + + /* + * Set the maximum subordinate bus of this pbm. + */ + pbm->pci_last_busno = pbus->subordinate; + pbm_write_config_byte(pbm, busno, 0, 0x41, pbm->pci_last_busno); + + busno = pbus->subordinate + 1; + + /* + * Fixup the chain of primary PCI busses. + */ + if (pchain) { + pchain->next = &pbm->pci_bus; + pchain = pchain->next; + } else { + pchain = &pci_root; + memcpy(pchain, &pbm->pci_bus, sizeof(pci_root)); + } } static int pdev_to_pnode_sibtraverse(struct linux_pbm_info *pbm, @@ -433,8 +610,6 @@ static void fill_in_pbm_cookies(struct linux_pbm_info *pbm) pdev_cookie_fillin(pbm, pdev); } -/* #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. @@ -534,8 +709,6 @@ 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, @@ -556,12 +729,14 @@ static void fixup_regs(struct pci_dev *pdev, if(bustype == 0) { /* Config space cookie, nothing to do. */ if(preg != 0) - prom_printf("fixup_doit: strange, config space not 0\n"); + printk("%s: strange, config space not 0\n", + __FUNCTION__); continue; } else if(bustype == 3) { /* XXX add support for this... */ - prom_printf("fixup_doit: Warning, ignoring 64-bit PCI " - "memory space, tell DaveM.\n"); + printk("%s: Warning, ignoring 64-bit PCI memory space, " + "tell Eddie C. Dost (ecd@skynet.be).\n", + __FUNCTION__); continue; } bsreg = (pregs[preg].phys_hi & 0xff); @@ -574,8 +749,13 @@ static void fixup_regs(struct pci_dev *pdev, 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); + printk("%s: [%04x:%04x]: " + "Warning, ignoring bogus basereg [%x]\n", + __FUNCTION__, pdev->vendor, pdev->device, bsreg); + printk(" PROM reg: %08x.%08x.%08x %08x.%08x\n", + pregs[preg].phys_hi, pregs[preg].phys_mid, + pregs[preg].phys_lo, pregs[preg].size_hi, + pregs[preg].size_lo); continue; } @@ -740,22 +920,22 @@ static void fixup_regs(struct pci_dev *pdev, pdev->devfn, PCI_COMMAND, &l); #ifdef FIXUP_REGS_DEBUG - prom_printf("["); + dprintf("["); #endif if(IO_seen) { #ifdef FIXUP_REGS_DEBUG - prom_printf("IO "); + dprintf("IO "); #endif l |= PCI_COMMAND_IO; } if(MEM_seen) { #ifdef FIXUP_REGS_DEBUG - prom_printf("MEM"); + dprintf("MEM"); #endif l |= PCI_COMMAND_MEMORY; } #ifdef FIXUP_REGS_DEBUG - prom_printf("]"); + dprintf("]"); #endif pcibios_write_config_dword(pdev->bus->number, pdev->devfn, @@ -763,7 +943,7 @@ static void fixup_regs(struct pci_dev *pdev, } #ifdef FIXUP_REGS_DEBUG - prom_printf("REG_FIXUP[%s]: ", pci_strdev(pdev->vendor, pdev->device)); + dprintf("REG_FIXUP[%04x,%04x]: ", 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]); @@ -814,7 +994,7 @@ static unsigned long psycho_pcislot_imap_offset(unsigned long ino) } /* Exported for EBUS probing layer. */ -unsigned int psycho_irq_build(unsigned int full_ino) +unsigned int psycho_irq_build(struct linux_pbm_info *pbm, unsigned int full_ino) { unsigned long imap_off, ign, ino; @@ -906,11 +1086,9 @@ unsigned int psycho_irq_build(unsigned int full_ino) } imap_off -= imap_offset(imap_a_slot0); - return pci_irq_encode(imap_off, 0 /* XXX */, ign, ino); + return pci_irq_encode(imap_off, pbm->parent->index, ign, ino); } -/* #define FIXUP_IRQ_DEBUG */ - static void fixup_irq(struct pci_dev *pdev, struct linux_pbm_info *pbm, int node) @@ -920,24 +1098,30 @@ static void fixup_irq(struct pci_dev *pdev, int err; #ifdef FIXUP_IRQ_DEBUG - printk("fixup_irq[%s:%s]: ", - pci_strvendor(pdev->vendor), - pci_strdev(pdev->vendor, pdev->device)); + dprintf("fixup_irq[%04x:%04x]: ", 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_printf("fixup_irq: No interrupts property for dev[%04x:%04x]\n", + 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); + pdev->irq = psycho_irq_build(pbm, prom_irq); +#ifdef FIXUP_IRQ_DEBUG + dprintf("fully specified prom_irq[%x] pdev->irq[%x]", + prom_irq, pdev->irq); +#endif + /* See if onboard device interrupt (i.e. bit 5 set) */ + } else if((prom_irq & PSYCHO_IMAP_INO) & 0x20) { + pdev->irq = psycho_irq_build(pbm, + (pbm->parent->upa_portid << 6) + | prom_irq); #ifdef FIXUP_IRQ_DEBUG - printk("fully specified prom_irq[%x] pdev->irq[%x]", - prom_irq, pdev->irq); + dprintf("partially specified prom_irq[%x] pdev->irq[%x]", + prom_irq, pdev->irq); #endif } else { unsigned int bus, slot, line; @@ -952,20 +1136,25 @@ static void fixup_irq(struct pci_dev *pdev, if(pbm == &pbm->parent->pbm_A) slot = (pdev->devfn >> 3) - 1; else - slot = ((pdev->devfn >> 3) >> 1) - 1; + slot = (pdev->devfn >> 3) - 2; } else { /* Underneath a bridge, use slot number of parent * bridge. */ - slot = (pdev->bus->self->devfn >> 3) - 1; + if(pbm == &pbm->parent->pbm_A) + slot = (pdev->bus->self->devfn >> 3) - 1; + else + slot = (pdev->bus->self->devfn >> 3) - 2; /* Use low slot number bits of child as IRQ line. */ - line = ((pdev->devfn >> 3) & 3); + line = (line + ((pdev->devfn >> 3) - 4)) % 4; } slot = (slot << 2); - pdev->irq = psycho_irq_build((((portid << 6) & PSYCHO_IMAP_IGN) | - (bus | slot | line))); + pdev->irq = psycho_irq_build(pbm, + (((portid << 6) & PSYCHO_IMAP_IGN) + | (bus | slot | line))); + #ifdef FIXUP_IRQ_DEBUG do { unsigned char iline, ipin; @@ -978,15 +1167,24 @@ static void fixup_irq(struct pci_dev *pdev, 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); + dprintf("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 } + + /* + * Write the INO to config space PCI_INTERRUPT_LINE. + */ + (void)pcibios_write_config_byte(pdev->bus->number, + pdev->devfn, + PCI_INTERRUPT_LINE, + pdev->irq & PCI_IRQ_INO); + #ifdef FIXUP_IRQ_DEBUG - printk("\n"); + dprintf("\n"); #endif } @@ -1093,15 +1291,11 @@ static void psycho_final_fixup(struct linux_psycho *psycho) /* 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) { - struct linux_psycho *psycho = psycho_root; + struct linux_psycho *psycho; pci_probe_enable = 1; @@ -1117,11 +1311,13 @@ unsigned long pcibios_fixup(unsigned long memory_start, unsigned long memory_end * XXX apps that need to get at PCI configuration space. */ - /* Probe busses under PBM A. */ - pbm_probe(&psycho->pbm_A, &memory_start); + for (psycho = psycho_root; psycho; psycho = psycho->next) { + /* Probe busses under PBM B. */ + pbm_probe(&psycho->pbm_B, &memory_start); - /* Probe busses under PBM B. */ - pbm_probe(&psycho->pbm_B, &memory_start); + /* Probe busses under PBM A. */ + pbm_probe(&psycho->pbm_A, &memory_start); + } pci_init_alloc_init(&memory_start); @@ -1130,15 +1326,17 @@ unsigned long pcibios_fixup(unsigned long memory_start, unsigned long memory_end * 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); + for (psycho = psycho_root; psycho; psycho = psycho->next) { + 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); + /* 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); + /* Now, fix it all up. */ + psycho_final_fixup(psycho); + } pci_init_alloc_fini(); @@ -1153,113 +1351,124 @@ volatile int pci_poke_faulted = 0; * XXX space exists, on Ultra we can have many of them, especially with * XXX 'dual-pci' boards on Sunfire/Starfire/Wildfire. */ -static char *pci_mkaddr(unsigned char bus, unsigned char device_fn, - unsigned char where) +static void * +pci_mkaddr(struct linux_pbm_info *pbm, unsigned char bus, + unsigned char devfn, unsigned char where) { - unsigned long ret = (unsigned long) psycho_root->pci_config_space; + unsigned long ret; + + if (!pbm) + return NULL; + + ret = (unsigned long) pbm->parent->pci_config_space; ret |= (1 << 24); - ret |= ((bus & 0xff) << 16); - ret |= ((device_fn & 0xff) << 8); - ret |= (where & 0xfc); - return (unsigned char *)ret; + ret |= (bus << 16); + ret |= (devfn << 8); + ret |= where; + + return (void *)ret; } -static inline int out_of_range(unsigned char bus, unsigned char device_fn) +static inline int +out_of_range(struct linux_pbm_info *pbm, unsigned char bus, unsigned char devfn) { - return ((bus == 0 && PCI_SLOT(device_fn) > 4) || - (bus == 1 && PCI_SLOT(device_fn) > 6) || + return (((pbm == &pbm->parent->pbm_B) && PCI_SLOT(devfn) > 4) || + ((pbm == &pbm->parent->pbm_A) && PCI_SLOT(devfn) > 6) || (pci_probe_enable == 0)); } -int pcibios_read_config_byte (unsigned char bus, unsigned char device_fn, - unsigned char where, unsigned char *value) +static int +pbm_read_config_byte(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned char *value) { - unsigned char *addr = pci_mkaddr(bus, device_fn, where); - unsigned int word, trapped; + unsigned char *addr = pci_mkaddr(pbm, bus, devfn, where); + unsigned int trapped; + unsigned char byte; *value = 0xff; - if(out_of_range(bus, device_fn)) + if (!addr) + return PCIBIOS_SUCCESSFUL; + + if (out_of_range(pbm, bus, devfn)) return PCIBIOS_SUCCESSFUL; pci_poke_in_progress = 1; pci_poke_faulted = 0; __asm__ __volatile__("membar #Sync\n\t" - "lduwa [%1] %2, %0\n\t" + "lduba [%1] %2, %0\n\t" "membar #Sync" - : "=r" (word) + : "=r" (byte) : "r" (addr), "i" (ASI_PL)); pci_poke_in_progress = 0; trapped = pci_poke_faulted; pci_poke_faulted = 0; - if(!trapped) { - switch(where & 3) { - case 0: - *value = word & 0xff; - break; - case 1: - *value = (word >> 8) & 0xff; - break; - case 2: - *value = (word >> 16) & 0xff; - break; - case 3: - *value = (word >> 24) & 0xff; - break; - }; - } + if(!trapped) + *value = byte; return PCIBIOS_SUCCESSFUL; } -int pcibios_read_config_word (unsigned char bus, unsigned char device_fn, - unsigned char where, unsigned short *value) +static int +pbm_read_config_word(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned short *value) { - unsigned short *addr = (unsigned short *)pci_mkaddr(bus, device_fn, where); - unsigned int word, trapped; + unsigned short *addr = pci_mkaddr(pbm, bus, devfn, where); + unsigned int trapped; + unsigned short word; *value = 0xffff; - if(out_of_range(bus, device_fn)) + if (!addr) + return PCIBIOS_SUCCESSFUL; + + if (out_of_range(pbm, bus, devfn)) return PCIBIOS_SUCCESSFUL; + if (where & 0x01) { + printk("pcibios_read_config_word: misaligned reg [%x]\n", + where); + return PCIBIOS_SUCCESSFUL; + } + pci_poke_in_progress = 1; pci_poke_faulted = 0; __asm__ __volatile__("membar #Sync\n\t" - "lduwa [%1] %2, %0\n\t" + "lduha [%1] %2, %0\n\t" "membar #Sync" : "=r" (word) : "r" (addr), "i" (ASI_PL)); pci_poke_in_progress = 0; trapped = pci_poke_faulted; pci_poke_faulted = 0; - if(!trapped) { - switch(where & 3) { - case 0: - *value = word & 0xffff; - break; - case 2: - *value = (word >> 16) & 0xffff; - break; - default: - printk("pcibios_read_config_word: misaligned " - "reg [%x]\n", where); - break; - }; - } + if(!trapped) + *value = word; return PCIBIOS_SUCCESSFUL; } -int pcibios_read_config_dword (unsigned char bus, unsigned char device_fn, - unsigned char where, unsigned int *value) +static int +pbm_read_config_dword(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned int *value) { - unsigned int *addr = (unsigned int *)pci_mkaddr(bus, device_fn, where); + unsigned int *addr = pci_mkaddr(pbm, bus, devfn, where); unsigned int word, trapped; *value = 0xffffffff; - if(out_of_range(bus, device_fn)) + if (!addr) + return PCIBIOS_SUCCESSFUL; + + if (out_of_range(pbm, bus, devfn)) + return PCIBIOS_SUCCESSFUL; + + if (where & 0x03) { + printk("pcibios_read_config_dword: misaligned reg [%x]\n", + where); return PCIBIOS_SUCCESSFUL; + } pci_poke_in_progress = 1; pci_poke_faulted = 0; @@ -1276,12 +1485,17 @@ int pcibios_read_config_dword (unsigned char bus, unsigned char device_fn, return PCIBIOS_SUCCESSFUL; } -int pcibios_write_config_byte (unsigned char bus, unsigned char device_fn, - unsigned char where, unsigned char value) +static int +pbm_write_config_byte(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned char value) { - unsigned char *addr = pci_mkaddr(bus, device_fn, where); + unsigned char *addr = pci_mkaddr(pbm, bus, devfn, where); - if(out_of_range(bus, device_fn)) + if (!addr) + return PCIBIOS_SUCCESSFUL; + + if (out_of_range(pbm, bus, devfn)) return PCIBIOS_SUCCESSFUL; pci_poke_in_progress = 1; @@ -1299,14 +1513,25 @@ int pcibios_write_config_byte (unsigned char bus, unsigned char device_fn, return PCIBIOS_SUCCESSFUL; } -int pcibios_write_config_word (unsigned char bus, unsigned char device_fn, - unsigned char where, unsigned short value) +static int +pbm_write_config_word(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned short value) { - unsigned short *addr = (unsigned short *)pci_mkaddr(bus, device_fn, where); + unsigned short *addr = pci_mkaddr(pbm, bus, devfn, where); - if(out_of_range(bus, device_fn)) + if (!addr) return PCIBIOS_SUCCESSFUL; + if (out_of_range(pbm, bus, devfn)) + return PCIBIOS_SUCCESSFUL; + + if (where & 0x01) { + printk("pcibios_write_config_word: misaligned reg [%x]\n", + where); + return PCIBIOS_SUCCESSFUL; + } + pci_poke_in_progress = 1; __asm__ __volatile__("membar #Sync\n\t" "stha %0, [%1] %2\n\t" @@ -1317,14 +1542,25 @@ int pcibios_write_config_word (unsigned char bus, unsigned char device_fn, return PCIBIOS_SUCCESSFUL; } -int pcibios_write_config_dword (unsigned char bus, unsigned char device_fn, - unsigned char where, unsigned int value) +static int +pbm_write_config_dword(struct linux_pbm_info *pbm, + unsigned char bus, unsigned char devfn, + unsigned char where, unsigned int value) { - unsigned int *addr = (unsigned int *)pci_mkaddr(bus, device_fn, where); + unsigned int *addr = pci_mkaddr(pbm, bus, devfn, where); - if(out_of_range(bus, device_fn)) + if (!addr) return PCIBIOS_SUCCESSFUL; + if (out_of_range(pbm, bus, devfn)) + return PCIBIOS_SUCCESSFUL; + + if (where & 0x03) { + printk("pcibios_write_config_dword: misaligned reg [%x]\n", + where); + return PCIBIOS_SUCCESSFUL; + } + pci_poke_in_progress = 1; __asm__ __volatile__("membar #Sync\n\t" "stwa %0, [%1] %2\n\t" @@ -1335,6 +1571,42 @@ int pcibios_write_config_dword (unsigned char bus, unsigned char device_fn, return PCIBIOS_SUCCESSFUL; } +int pcibios_read_config_byte (unsigned char bus, unsigned char devfn, + unsigned char where, unsigned char *value) +{ + return pbm_read_config_byte(bus2pbm[bus], bus, devfn, where, value); +} + +int pcibios_read_config_word (unsigned char bus, unsigned char devfn, + unsigned char where, unsigned short *value) +{ + return pbm_read_config_word(bus2pbm[bus], bus, devfn, where, value); +} + +int pcibios_read_config_dword (unsigned char bus, unsigned char devfn, + unsigned char where, unsigned int *value) +{ + return pbm_read_config_dword(bus2pbm[bus], bus, devfn, where, value); +} + +int pcibios_write_config_byte (unsigned char bus, unsigned char devfn, + unsigned char where, unsigned char value) +{ + return pbm_write_config_byte(bus2pbm[bus], bus, devfn, where, value); +} + +int pcibios_write_config_word (unsigned char bus, unsigned char devfn, + unsigned char where, unsigned short value) +{ + return pbm_write_config_word(bus2pbm[bus], bus, devfn, where, value); +} + +int pcibios_write_config_dword (unsigned char bus, unsigned char devfn, + unsigned char where, unsigned int value) +{ + return pbm_write_config_dword(bus2pbm[bus], bus, devfn, where, value); +} + asmlinkage int sys_pciconfig_read(unsigned long bus, unsigned long dfn, unsigned long off, @@ -1346,19 +1618,22 @@ asmlinkage int sys_pciconfig_read(unsigned long bus, unsigned int uint; int err = 0; + if(!suser()) + return -EPERM; + lock_kernel(); switch(len) { case 1: pcibios_read_config_byte(bus, dfn, off, &ubyte); - put_user(ubyte, buf); + put_user(ubyte, (unsigned char *)buf); break; case 2: pcibios_read_config_word(bus, dfn, off, &ushort); - put_user(ushort, buf); + put_user(ushort, (unsigned short *)buf); break; case 4: pcibios_read_config_dword(bus, dfn, off, &uint); - put_user(uint, buf); + put_user(uint, (unsigned int *)buf); break; default: @@ -1381,6 +1656,9 @@ asmlinkage int sys_pciconfig_write(unsigned long bus, unsigned int uint; int err = 0; + if(!suser()) + return -EPERM; + lock_kernel(); switch(len) { case 1: |