[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

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: