> 00:11.0 Display controller: ATI Technologies Inc 3D Rage LT Pro (rev dc) > Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- > Stepping+ SERR- FastB2B- > Status: Cap+ 66Mhz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- > <TAbort- <MAbort- >SERR- <PERR- > Latency: 32 (2000ns min), cache line size 08 > Interrupt: pin A routed to IRQ 24 > Region 0: Memory at 82000000 (32-bit, non-prefetchable) > Region 1: I/O ports at 0400 [disabled] > Region 2: Memory at 82fff000 (32-bit, non-prefetchable)
That's what I expected... Please try to apply the attached patch to the 2.2.18 ppc kernel source and recompile. Michael
--- drivers/video/atyfb.c.orig Thu Oct 19 19:49:53 2000 +++ drivers/video/atyfb.c Fri Oct 20 12:10:37 2000 @@ -3242,14 +3242,72 @@ return 1; } +/* + * Check PCI resource overlap between any PCI device (but mydev) and this region. + * This function does not discriminate between IO and memory space so memory regions + * will be considered to overlap IO regions though they might be decoded separate on + * machines with separate memory and IO access. This being for PPC I don't care - MSch. + */ +__initfunc(int atyfb_check_overlap(struct pci_dev *mydev, unsigned long mybase, unsigned int mysize)) +{ + + int i,j; + struct pci_dev *pdev; + + for (pdev = pci_devices; pdev; pdev = pdev->next) { + + if (pdev == mydev) + continue; + + for (i = 0, j = 2; i < 6 && pdev->base_address[i]; i++) { + int io, breg = PCI_BASE_ADDRESS_0 + (i << 2); + unsigned long base; + u32 size, pbase; + + base = pdev->base_address[i]; + + if (!base) + continue; + + io = (base & PCI_BASE_ADDRESS_SPACE)==PCI_BASE_ADDRESS_SPACE_IO; + + pci_read_config_dword(pdev, breg, &pbase); + pci_write_config_dword(pdev, breg, 0xffffffff); + pci_read_config_dword(pdev, breg, &size); + pci_write_config_dword(pdev, breg, pbase); + + if (io) { + size &= PCI_BASE_ADDRESS_IO_MASK; + base &= PCI_BASE_ADDRESS_IO_MASK; + } else { + size &= PCI_BASE_ADDRESS_MEM_MASK; + base &= PCI_BASE_ADDRESS_MEM_MASK; + } + size = ~(size) + 1; + + if ( (base < mybase+mysize-1 && base+size-1 >= mybase) + || (mybase < base+size-1 && mybase+mysize-1 >= base) ) { + printk("\nPCI resource conflict: %lx-%lx overlaps %lx-%lx !\n", + base, base+size-1, mybase, mybase+mysize-1); + return 1; /* conflicting region overlaps */ + } + + } + } + + return 0; /* no conflicting region found */ + +} + + #ifdef CONFIG_FB_OF __initfunc(void atyfb_of_init(struct device_node *dp)) { - unsigned long addr; + unsigned long addr, io_base=NULL; u8 bus, devfn; u16 cmd; struct fb_info_aty *info; - int i; + int i, i_frame, i_regs, i_io, naddr; if (device_is_compatible(dp, "ATY,264LTPro")) { /* XXX kludge for now */ @@ -3284,6 +3342,12 @@ } memset(info, 0, sizeof(struct fb_info_aty)); + /* + * Use register set in the little endian aperture regardless of what was set + * up in OF or the PCI registers for MMIO. We could use the registers in the + * big endian aperture as well (at least on some LTPro), or set up a separate + * PCI mapping. Seems to work either way (again, on my Lombard) - MSch. + */ info->ati_regbase_phys = 0x7ff000+addr; info->ati_regbase = (unsigned long)ioremap(info->ati_regbase_phys, 0x1000); @@ -3297,8 +3361,49 @@ info->ati_regbase_phys += 0xc00; info->ati_regbase += 0xc00; - /* enable memory-space accesses using config-space command register */ + /* search PCI config space for VRAM, IO and MMIO regions */ if (pci_device_loc(dp, &bus, &devfn) == 0) { + + for (i = 0; i < dp->n_addrs + 2; i++) { + int io, breg = PCI_BASE_ADDRESS_0 + (i << 2); + unsigned long base; + u32 size, pbase; + + base = dp->addrs[i].address; + + pcibios_read_config_dword(bus, devfn, breg, &pbase); + pcibios_write_config_dword(bus, devfn, breg, 0xffffffff); + pcibios_read_config_dword(bus, devfn, breg, &size); + pcibios_write_config_dword(bus, devfn, breg, pbase); + + io = (pbase & PCI_BASE_ADDRESS_SPACE)==PCI_BASE_ADDRESS_SPACE_IO; + + if (io) { + size &= ~1; + pbase &= ~1; + i_io = i; + } + + size = ~(size) + 1; + + if (size == 0) + break; + + if (!base) { + dp->addrs[i].address = pbase; + dp->addrs[i].size = size; + } + if (pbase == addr) { + i_frame = i; + } else if (size == 0x1000) { + i_regs = i; + io_base = pbase; + } + } + + naddr = i; + + /* enable memory-space accesses using config-space command register */ pcibios_read_config_word(bus, devfn, PCI_COMMAND, &cmd); if (cmd != 0xffff) { cmd |= PCI_COMMAND_MEMORY; @@ -3319,6 +3424,105 @@ printk("atyfb_init: ioremap() returned NULL\n"); kfree(info); return; + } + + /* + * Fix MMIO mapping if MMIO and VRAM PCI mappings set up by MacOS overlap - MSch. + * Note that we can't move the VRAM base address to the BE aperture (this would move the whole + * VRAM region, not resize it) so it's easier to remap MMIO someplace else. + */ + if ( (dp->addrs[i_frame].address < dp->addrs[i_regs].address+dp->addrs[i_regs].size + && dp->addrs[i_frame].address+dp->addrs[i_frame].size >= dp->addrs[i_regs].address) + || (dp->addrs[i_regs].address < dp->addrs[i_frame].address+dp->addrs[i_frame].size + && dp->addrs[i_regs].address+dp->addrs[i_regs].size >= dp->addrs[i_frame].address) ) { + + struct pci_dev *pdev = pci_find_slot(bus, devfn); + int io, breg = PCI_BASE_ADDRESS_0 + (i_regs << 2); + int flags; + unsigned long base; + u32 size, pbase, new; + + base = dp->addrs[i_regs].address; + + pcibios_read_config_dword(bus, devfn, breg, &pbase); + pcibios_write_config_dword(bus, devfn, breg, 0xffffffff); + pcibios_read_config_dword(bus, devfn, breg, &size); + pcibios_write_config_dword(bus, devfn, breg, pbase); + + io = (pbase & PCI_BASE_ADDRESS_SPACE)==PCI_BASE_ADDRESS_SPACE_IO; + flags = (pbase & PCI_BASE_ADDRESS_MEM_MASK); + + if (io) { + size &= ~1; + pbase &= ~1; + } + size = ~(size) + 1; + + printk("atyfb: region %d ofbase 0x%lx breg %d io %d pbase 0x%lx size 0x%lx overlaps VRAM ", + i_regs, base, breg, io, pbase, size); + + /* + * Best guess: try address used by OF/yaboot, check for overlap with existing devices! + */ + new = 0x80881000; + + if (atyfb_check_overlap(pdev, new, size)) { + /* + * Something overlaps our new mapping. Move MMIO before frame buffer and past I/O for now. + * Need to walk PCI resources to find guaranteed free spot (currently we bail out when our + * next best guess is taken as well). + */ + if (dp->addrs[i_io].address+dp->addrs[i_io].size+dp->addrs[i_regs].size < dp->addrs[i_frame].address) { + new = (dp->addrs[i_io].address&0xff000000) | (dp->addrs[i_regs].address&0x00ffffff); + } else { + new = (dp->addrs[i_frame].address+dp->addrs[i_frame].size); + } + if (atyfb_check_overlap(pdev, new, size)) { + printk("\natyfb: can't resolve overlap, bailing out!\n"); + /* gotos are evil */ + new = base; + } + } + + new |= flags & 0x0f; + + pcibios_write_config_dword(bus, devfn, breg, new); + + pcibios_read_config_dword(bus, devfn, breg, &pbase); + pcibios_write_config_dword(bus, devfn, breg, 0xffffffff); + pcibios_read_config_dword(bus, devfn, breg, &size); + pcibios_write_config_dword(bus, devfn, breg, pbase); + + if (new != pbase) + printk("atyfb: failed to remap MMIO region! \n"); + + /* update PCI struct */ + if (!pdev) + printk("atyfb: no pci_dev registered for device!\n"); + else + pdev->base_address[i_regs] = pbase; + + io = (pbase & PCI_BASE_ADDRESS_SPACE)==PCI_BASE_ADDRESS_SPACE_IO; + flags = (pbase & ~PCI_BASE_ADDRESS_MEM_MASK); + + if (io) { + size &= ~1; + pbase &= ~1; + } + size = ~(size) + 1; + + printk(" - reassigned to pbase 0x%lx size 0x%lx ! \n", pbase, size); + + /* update OF device tree */ + dp->addrs[i_regs].address = pbase; + + /* + * Note: we only fixed up PCI config and brought the OF device tree + * in line with the new PCI config. regbase still is mapped to the original + * location in the LE aperture which overlaps with the full (LE and BE) + * VRAM. If someone at xfree ever starts to check if regbase falls within + * the PCI region of VRAM, slap them silly - MSch. + */ } if (!aty_init(info, dp->full_name)) {