Re: XF4 on Wallstret PBG3
> 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)) {
Reply to: