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

Re: xserver-xfree86 4.1.0-2 fails where 4.0.2-7puetzk worked



> > Please send the full X server log (the section about PCI device
> > probing/fixup might prove interesting)
> 
> I can't, due to the kernel crash which occurs when running this.

Sorry, I forgot how bad this can be... Does it also panic with X
 
> Should I file a bug on kernel-image-2.2.19-pmac about how it's
> crashing like this?  I doubt there's anything the Debian maintainers
> can do, though...

They could try including a patch that's been floating around on
debian-powerpc and linuxppc-dev since the first XFree 4.0 days (ajoshi's
xserver devel tree, that's predating Branden's packages by a huge margin). 

I thought this had been fixed in 4.1 though. Anyway, try rebuilding your
kernel with the patch I attached (it's for 2.2.18 but should apply to
2.2.19 as well).
 
> > and the output of lspci -v
> > before starting X (and perhaps after if you manage to do that :-P).
> 
> Here's the one running after, is that good enough?

After? How'd you do that? 

> 00:11.0 Display controller: ATI Technologies Inc 3D Rage LT Pro (rev dc)
>         Flags: bus master, stepping, medium devsel, latency 32, IRQ 24
>         Memory at 82000000 (32-bit, non-prefetchable)
>         I/O ports at 0400
>         Memory at 82fff000 (32-bit, non-prefetchable)
>         Capabilities: [5c] Power Management version 1

Just what I thought. 
The first memory region (82000000, video memory) and the second
(82fff000, MMIO registers) overlap. Now my guess is the X server detects
this, and changes one of the mappings (can someone on debian-x explain
exactly what the X server does with overlapping PCI resources these
days?). If it touches the 82000000 region, there's no way for the kernel
atyfb driver to figure out its memory mappings just got changed, oops
(that's been a problem with 4.0 at least). 

	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: