public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Jon Smirl <jonsmirl@gmail.com>
To: Alan Cox <alan@lxorguk.ukuu.org.uk>, Egbert Eich <eich@suse.de>,
	Linux Kernel Mailing List <linux-kernel@vger.kernel.org>,
	Jesse Barnes <jbarnes@engr.sgi.com>
Subject: Patch to control VGA bus routing and active VGA device.
Date: Mon, 17 Jan 2005 22:43:16 -0500	[thread overview]
Message-ID: <9e47339105011719436a9e5038@mail.gmail.com> (raw)

[-- Attachment #1: Type: text/plain, Size: 1671 bytes --]

Attached is a patch to control VGA bus routing and the active VGA
device. It works by adding sysfs attributes to bridge and VGA devices.
The bridge attribute is read only and indicates if the bridge is
routing VGA. The attribute on the device has four values:

/* echo these values to the sysfs vga attribute on a VGA device */
enum eEnable {
	VGA_DISABLE_THIS = 0,	/* If this VGA is enabled, disable it. */
	VGA_ENABLE_THIS = 1,	/* Disable all VGAs then enable this VGA, mark
as active VGA */
	/* Used while resetting a board, board being reset may not be the active VGA */
	VGA_DISABLE_ALL = 2,	/* Remember active VGA then disable all VGAa and
devices */
	VGA_ENABLE_ACTIVE = 3,	/* Make sure all VGAs are disabled, then
reenable active VGA */
};

States 2 and 3 are using during a reset cycle. You need to disable the
active VGA, run the reset on the new card, turn it off and then
restore the active one.

I cannot get this code to work. I have a machine with three buses, the
main one, a PCI one and an AGP one. If I turn off routing from one
bridge and turn it on on the other, I cannot get the VGA card to
respond to the IO requests. There must be something more to routing
VGA than what is in this code. The patch is pretty simple so it
shouldn't be too hard to find the problem. I know my machine can route
VGA since vbios.vm86 does it when reseting my secondary cards.

Can any of you PCI/VGA experts tell me what is wrong? Patch is against
current Linus BK.

This code, plus the ROM code already in the kernel, plus a tiny piece
to generate a hotplug event, is enough to let me write a user space
app for secondary card reset.

-- 
Jon Smirl
jonsmirl@gmail.com

[-- Attachment #2: patch --]
[-- Type: application/octet-stream, Size: 11029 bytes --]

diff -Nru a/arch/i386/pci/fixup.c b/arch/i386/pci/fixup.c
--- a/arch/i386/pci/fixup.c	2005-01-17 22:40:51 -05:00
+++ b/arch/i386/pci/fixup.c	2005-01-17 22:40:51 -05:00
@@ -375,6 +375,6 @@
 		}
 		bus = bus->parent;
 	}
-	pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_SHADOW;
+	pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_SHADOW | IORESOURCE_VGA_ACTIVE;
 }
 DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pci_fixup_video);
diff -Nru a/drivers/pci/Kconfig b/drivers/pci/Kconfig
--- a/drivers/pci/Kconfig	2005-01-17 22:40:51 -05:00
+++ b/drivers/pci/Kconfig	2005-01-17 22:40:51 -05:00
@@ -47,3 +47,13 @@
 
 	  When in doubt, say Y.
 
+config VGA_CONTROL
+	bool "VGA Control"
+	depends on PCI
+	---help---
+	  Provides sysfs attributes for ensuring that only a single VGA
+	  device can be enabled per PCI domain. If a VGA device is removed
+	  via hotplug, display is routed to another VGA device if available.
+
+	  If you have more than one VGA device, say Y.
+
diff -Nru a/drivers/pci/Makefile b/drivers/pci/Makefile
--- a/drivers/pci/Makefile	2005-01-17 22:40:51 -05:00
+++ b/drivers/pci/Makefile	2005-01-17 22:40:51 -05:00
@@ -28,6 +28,7 @@
 obj-$(CONFIG_MIPS) += setup-bus.o setup-irq.o
 obj-$(CONFIG_X86_VISWS) += setup-irq.o
 obj-$(CONFIG_PCI_MSI) += msi.o
+obj-$(CONFIG_VGA_CONTROL) += vga.o
 
 #
 # ACPI Related PCI FW Functions
diff -Nru a/drivers/pci/bus.c b/drivers/pci/bus.c
--- a/drivers/pci/bus.c	2005-01-17 22:40:51 -05:00
+++ b/drivers/pci/bus.c	2005-01-17 22:40:51 -05:00
@@ -85,6 +85,9 @@
 
 	pci_proc_attach_device(dev);
 	pci_create_sysfs_dev_files(dev);
+#if CONFIG_VGA_CONTROL
+	pci_vga_add_device(dev);
+#endif
 }
 
 /**
diff -Nru a/drivers/pci/pci.h b/drivers/pci/pci.h
--- a/drivers/pci/pci.h	2005-01-17 22:40:51 -05:00
+++ b/drivers/pci/pci.h	2005-01-17 22:40:51 -05:00
@@ -11,6 +11,8 @@
 				  void (*alignf)(void *, struct resource *,
 					  	 unsigned long, unsigned long),
 				  void *alignf_data);
+extern int pci_vga_add_device(struct pci_dev *pdev);
+extern int pci_vga_remove_device(struct pci_dev *pdev);
 /* PCI /proc functions */
 #ifdef CONFIG_PROC_FS
 extern int pci_proc_attach_device(struct pci_dev *dev);
diff -Nru a/drivers/pci/remove.c b/drivers/pci/remove.c
--- a/drivers/pci/remove.c	2005-01-17 22:40:51 -05:00
+++ b/drivers/pci/remove.c	2005-01-17 22:40:51 -05:00
@@ -26,6 +26,9 @@
 
 static void pci_destroy_dev(struct pci_dev *dev)
 {
+#if CONFIG_VGA_CONTROL
+	pci_vga_remove_device(dev);
+#endif
 	pci_proc_detach_device(dev);
 	pci_remove_sysfs_dev_files(dev);
 	device_unregister(&dev->dev);
diff -Nru a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
--- a/drivers/pci/setup-bus.c	2005-01-17 22:40:51 -05:00
+++ b/drivers/pci/setup-bus.c	2005-01-17 22:40:51 -05:00
@@ -64,7 +64,9 @@
 
 		if (class == PCI_CLASS_DISPLAY_VGA ||
 		    class == PCI_CLASS_NOT_DEFINED_VGA)
-			bus->bridge_ctl |= PCI_BRIDGE_CTL_VGA;
+			/* only route to the active VGA, ignore inactive ones */
+			if  (dev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_VGA_ACTIVE)
+				bus->bridge_ctl |= PCI_BRIDGE_CTL_VGA;
 
 		pdev_sort_resources(dev, &head);
 	}
diff -Nru a/drivers/pci/vga.c b/drivers/pci/vga.c
--- /dev/null	Wed Dec 31 16:00:00 196900
+++ b/drivers/pci/vga.c	2005-01-17 22:40:51 -05:00
@@ -0,0 +1,250 @@
+/*
+ * linux/drivers/pci/vga.c
+ *
+ * (C) Copyright 2004 Jon Smirl <jonsmirl@gmail.com>
+ *
+ * VGA control logic for ensuring only a single enabled VGA device
+ */
+
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/pci.h>
+#include <linux/major.h>
+
+static int vga_initialized = 0;
+static struct pci_dev *vga_active = NULL;
+
+static void bridge_yes(struct pci_dev *pdev)
+{
+	struct pci_dev *bridge;
+	struct pci_bus *bus;
+	
+	/* Make sure the bridges route to us */
+	bus = pdev->bus;
+	while (bus) {
+		bridge = bus->self;
+		if (bridge) {
+			printk(KERN_DEBUG "bridge_yes %p %s\n", bus, pci_name(bridge));
+			bus->bridge_ctl |= PCI_BRIDGE_CTL_VGA;
+			pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, bus->bridge_ctl);
+		}
+		bus = bus->parent;
+	}
+}
+
+static void bridge_no(struct pci_dev *pdev)
+{
+	struct pci_dev *bridge;
+	struct pci_bus *bus;
+	
+	/* Make sure the bridges don't route to us */
+	bus = pdev->bus;
+	while (bus) {
+		bridge = bus->self;
+		if (bridge) {
+			printk(KERN_DEBUG "bridge_no %p %s\n", bus, pci_name(bridge));
+			bus->bridge_ctl &= ~PCI_BRIDGE_CTL_VGA;
+			pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, bus->bridge_ctl);
+		}
+		bus = bus->parent;
+	}
+}
+
+static void vga_enable(struct pci_dev *pdev, unsigned int enable)
+{
+	printk(KERN_DEBUG "vga_enable: enable %d pdev %p %s\n", enable, pdev, pci_name(pdev));
+	
+	bridge_yes(pdev);
+	
+	if (enable) {
+		/* this assumes all other potential VGA devices are disabled */
+		outb(0x01 | inb(0x3C3),  0x3C3);  /* 0 - enable */
+		outb(0x08 | inb(0x46e8), 0x46e8);
+		outb(0x01 | inb(0x102),  0x102);
+		pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_VGA_ACTIVE;
+		vga_active = pdev;
+		return;
+	}
+
+	outb(~0x01 & inb(0x3C3),  0x3C3);
+	outb(~0x08 & inb(0x46e8), 0x46e8);
+	outb(~0x01 & inb(0x102),  0x102);
+	pdev->resource[PCI_ROM_RESOURCE].flags &= ~IORESOURCE_VGA_ACTIVE;
+	if (pdev == vga_active)
+		vga_active = NULL;
+	bridge_no(pdev);
+}
+
+/* echo these values to the sysfs vga attribute on a VGA device */
+enum eEnable {
+	VGA_DISABLE_THIS = 0,	/* If this VGA is enabled, disable it. */
+	VGA_ENABLE_THIS = 1,	/* Disable all VGAs then enable this VGA, mark as active VGA */
+	/* Used while resetting a board, board being reset may not be the active VGA */
+	VGA_DISABLE_ALL = 2,	/* Remember active VGA then disable all VGAa and devices */
+	VGA_ENABLE_ACTIVE = 3,	/* Make sure all VGAs are disabled, then reenable active VGA */
+};
+
+static void set_state(struct pci_dev *pdev, enum eEnable enable)
+{
+	struct pci_dev *pcidev = NULL;
+	unsigned int class;
+
+	printk(KERN_DEBUG "set_state: enable is %d pdev %p %s\n", enable, pdev, pci_name(pdev));
+	
+	if (enable == VGA_DISABLE_THIS)
+		if (vga_active != pdev)
+			return;
+		
+	vga_enable(vga_active, 0);
+
+	/* loop over all devices and make sure no multiple routings */
+	while ((pcidev = pci_get_subsys(PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, pcidev)) != NULL) {
+		class = pcidev->class >> 8;
+
+		if (class == PCI_CLASS_DISPLAY_VGA)
+			bridge_no(pcidev);
+	}
+
+	/* loop over all devices and make sure everyone is disabled */
+	while ((pcidev = pci_get_subsys(PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, pcidev)) != NULL) {
+		class = pcidev->class >> 8;
+
+		if (class == PCI_CLASS_DISPLAY_VGA)
+			vga_enable(pcidev, 0);
+	}
+
+	switch (enable) {
+		case VGA_DISABLE_THIS:
+		case VGA_DISABLE_ALL:
+			break;
+
+		/* Mark us active if requested */
+		case VGA_ENABLE_THIS:
+			vga_enable(pdev, 1);
+			break;
+	
+		/* Restore active device if requested */
+		case VGA_ENABLE_ACTIVE:
+			vga_enable(vga_active, 1);
+			break;
+	}
+}
+
+/* sysfs store for VGA device */
+static ssize_t vga_device_store(struct device *dev, const char *buf, size_t count)
+{
+	char *last;
+	struct pci_dev *pdev = to_pci_dev(dev);
+	enum eEnable enable;
+
+	/* sysfs strings are terminated by \n */
+	enable = simple_strtoul(buf, &last, 0);
+	if (last == buf)
+		return -EINVAL;
+
+	if ((enable < VGA_DISABLE_THIS) || (enable > VGA_ENABLE_ACTIVE))
+		return -EINVAL;
+
+	set_state(pdev, enable);
+
+	return count;
+}
+
+/* sysfs show for VGA device */
+static ssize_t vga_device_show(struct device *dev, char *buf)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	return sprintf(buf, "%d\n", (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_VGA_ACTIVE) != 0);
+}
+
+static struct device_attribute vga_device_attr = __ATTR(vga, S_IRUGO|S_IWUSR, vga_device_show, vga_device_store);
+
+/* sysfs show for VGA routing bridge */
+static ssize_t vga_bridge_show(struct device *dev, char *buf)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	u16 l;
+
+	/* don't trust the shadow PCI_BRIDGE_CTL_VGA in pdev */
+	/* user space may change hardware without telling the kernel */
+	pci_read_config_word(pdev, PCI_BRIDGE_CONTROL, &l);
+	return sprintf(buf, "%d\n", (l & PCI_BRIDGE_CTL_VGA) != 0);
+}
+
+static struct device_attribute vga_bridge_attr = __ATTR(vga, S_IRUGO, vga_bridge_show, NULL);
+
+/* If the device is a VGA or a bridge, add a VGA sysfs attribute */
+int pci_vga_add_device(struct pci_dev *pdev)
+{
+	int class = pdev->class >> 8;
+
+	if (!vga_initialized)
+		return -EACCES;
+
+	if (class == PCI_CLASS_DISPLAY_VGA) {
+		device_create_file(&pdev->dev, &vga_device_attr);
+
+		/* record the active boot device when located */
+		if (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_VGA_ACTIVE)
+			vga_active = pdev;
+		return 0;
+	}
+
+	if ((class == PCI_CLASS_BRIDGE_PCI) || (class == PCI_CLASS_BRIDGE_CARDBUS)) {
+		device_create_file(&pdev->dev, &vga_bridge_attr);
+	}
+	return 0;
+}
+
+/* If the device is a VGA or a bridge, remove the VGA sysfs attribute */
+int pci_vga_remove_device(struct pci_dev *pdev)
+{
+	struct pci_dev *pcidev = NULL;
+	int class = pdev->class >> 8;
+
+	if (!vga_initialized)
+		return -EACCES;
+
+	if (class == PCI_CLASS_DISPLAY_VGA) {
+		device_remove_file(&pdev->dev, &vga_device_attr);
+
+		/* record the active boot device when located */
+		if (vga_active == pdev) {
+			while ((pcidev = pci_get_subsys(PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, pcidev)) != NULL) {
+				class = pcidev->class >> 8;
+				if (class != PCI_CLASS_DISPLAY_VGA)
+					continue;
+				if (pcidev == pdev)
+					continue;
+				set_state(pcidev, VGA_ENABLE_THIS);
+				break;
+			}
+			if (pcidev == NULL)
+				set_state(NULL, VGA_DISABLE_ALL);
+			
+		}
+		return 0;
+	}
+
+	if ((class == PCI_CLASS_BRIDGE_PCI) || (class == PCI_CLASS_BRIDGE_CARDBUS))
+		device_remove_file(&pdev->dev, &vga_bridge_attr);
+
+	return 0;
+}
+
+/* Initialize by scanning all devices */
+static int __init vga_init(void)
+{
+	struct pci_dev *pdev = NULL;
+
+	vga_initialized = 1;
+
+	while ((pdev = pci_get_subsys(PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL)
+		pci_vga_add_device(pdev);
+		
+	return 0;
+}
+
+__initcall(vga_init);
+
diff -Nru a/include/linux/ioport.h b/include/linux/ioport.h
--- a/include/linux/ioport.h	2005-01-17 22:40:51 -05:00
+++ b/include/linux/ioport.h	2005-01-17 22:40:51 -05:00
@@ -41,7 +41,6 @@
 #define IORESOURCE_CACHEABLE	0x00004000
 #define IORESOURCE_RANGELENGTH	0x00008000
 #define IORESOURCE_SHADOWABLE	0x00010000
-#define IORESOURCE_BUS_HAS_VGA	0x00080000
 
 #define IORESOURCE_DISABLED	0x10000000
 #define IORESOURCE_UNSET	0x20000000
@@ -86,6 +85,7 @@
 #define IORESOURCE_ROM_ENABLE		(1<<0)	/* ROM is enabled, same as PCI_ROM_ADDRESS_ENABLE */
 #define IORESOURCE_ROM_SHADOW		(1<<1)	/* ROM is copy at C000:0 */
 #define IORESOURCE_ROM_COPY		(1<<2)	/* ROM is alloc'd copy, resource field overlaid */
+#define IORESOURCE_VGA_ACTIVE		(1<<3)	/* VGA device is active */
 
 /* PC/ISA/whatever - the normal PC address spaces: IO and memory */
 extern struct resource ioport_resource;

             reply	other threads:[~2005-01-18  3:43 UTC|newest]

Thread overview: 31+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2005-01-18  3:43 Jon Smirl [this message]
2005-01-18 17:46 ` Patch to control VGA bus routing and active VGA device Jesse Barnes
2005-01-18 19:38   ` H. Peter Anvin
2005-01-18 21:06     ` Jesse Barnes
2005-01-22 19:04       ` Jon Smirl
2005-01-24 17:25         ` Jesse Barnes
2005-01-24 17:53           ` Jesse Barnes
     [not found] ` <41ED3BD2.1090105@pobox.com>
     [not found]   ` <9e473391050122083822a7f81c@mail.gmail.com>
     [not found]     ` <200501240847.51208.jbarnes@sgi.com>
     [not found]       ` <20050124175131.GM31455@parcelfarce.linux.theplanet.co.uk>
2005-01-24 19:17         ` Fwd: " Jon Smirl
2005-01-24 19:42           ` Jeff Garzik
2005-01-24 19:55             ` Russell King
2005-01-24 23:11               ` Jon Smirl
2005-01-25  4:24               ` Greg KH
2005-01-27  9:59                 ` Jon Smirl
2005-01-27 16:28                   ` Jesse Barnes
2005-01-28 17:32                     ` Grant Grundler
2005-01-28 18:36                       ` Jon Smirl
2005-01-28 19:15                         ` Grant Grundler
2005-01-28 19:26                           ` Jon Smirl
2005-01-28 19:34                             ` Grant Grundler
2005-01-28 18:41                       ` Jesse Barnes
2005-01-28 19:33                         ` Grant Grundler
2005-01-28 19:41                           ` Jesse Barnes
2005-01-28 20:12                             ` Grant Grundler
2005-01-28 20:00                           ` Matthew Wilcox
2005-01-28 20:07                             ` Russell King
2005-01-31 16:01                             ` Alan Cox
2005-02-01  6:38                   ` Greg KH
2005-02-01 16:24                     ` Jon Smirl
2005-01-30  7:51                 ` Jon Smirl
2005-01-24 20:14             ` Matthew Wilcox
2005-01-24 20:22           ` Matthew Wilcox

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=9e47339105011719436a9e5038@mail.gmail.com \
    --to=jonsmirl@gmail.com \
    --cc=alan@lxorguk.ukuu.org.uk \
    --cc=eich@suse.de \
    --cc=jbarnes@engr.sgi.com \
    --cc=linux-kernel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox