linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [patch 1/4]  pci: return max reserved busnr
       [not found] <20060116200218.275371000@whizzy>
@ 2006-01-18  0:56 ` Kristen Accardi
  2006-01-18  0:56 ` [patch 2/4] acpiphp: handle dock bridges Kristen Accardi
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 15+ messages in thread
From: Kristen Accardi @ 2006-01-18  0:56 UTC (permalink / raw)
  To: linux-kernel; +Cc: greg, pcihpd-discuss, len.brown, linux-acpi, pavel

Change the semantics of this call to return the max reserved
bus number instead of just the max assigned bus number.

Signed-off-by: Kristen Carlson Accardi <kristen.c.accardi@intel.com> 


 drivers/pci/pci.c   |    5 +++--
 include/linux/pci.h |    1 +
 2 files changed, 4 insertions(+), 2 deletions(-)

--- linux-2.6.15-mm.orig/drivers/pci/pci.c
+++ linux-2.6.15-mm/drivers/pci/pci.c
@@ -19,7 +19,6 @@
 #include <asm/dma.h>	/* isa_dma_bridge_buggy */
 #include "pci.h"
 
-#if 0
 
 /**
  * pci_bus_max_busnr - returns maximum PCI bus number of given bus' children
@@ -34,7 +33,7 @@ pci_bus_max_busnr(struct pci_bus* bus)
 	struct list_head *tmp;
 	unsigned char max, n;
 
-	max = bus->number;
+	max = bus->subordinate;
 	list_for_each(tmp, &bus->children) {
 		n = pci_bus_max_busnr(pci_bus_b(tmp));
 		if(n > max)
@@ -42,7 +41,9 @@ pci_bus_max_busnr(struct pci_bus* bus)
 	}
 	return max;
 }
+EXPORT_SYMBOL_GPL(pci_bus_max_busnr);
 
+#if 0
 /**
  * pci_max_busnr - returns maximum PCI bus number
  *
--- linux-2.6.15-mm.orig/include/linux/pci.h
+++ linux-2.6.15-mm/include/linux/pci.h
@@ -514,6 +514,7 @@ int pci_scan_bridge(struct pci_bus *bus,
 void pci_walk_bus(struct pci_bus *top, void (*cb)(struct pci_dev *, void *),
 		  void *userdata);
 int pci_cfg_space_size(struct pci_dev *dev);
+unsigned char pci_bus_max_busnr(struct pci_bus* bus);
 
 /* kmem_cache style wrapper around pci_alloc_consistent() */
 


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [patch 2/4]  acpiphp: handle dock bridges
       [not found] <20060116200218.275371000@whizzy>
  2006-01-18  0:56 ` [patch 1/4] pci: return max reserved busnr Kristen Accardi
@ 2006-01-18  0:56 ` Kristen Accardi
  2006-01-18  2:26   ` [Pcihpd-discuss] " Kenji Kaneshige
                     ` (4 more replies)
  2006-01-18  0:57 ` [patch 3/4] pci: really fix parent's subordinate busnr Kristen Accardi
  2006-01-18  0:57 ` [patch 4/4] pci: quirk for IBM Dock II cardbus controllers Kristen Accardi
  3 siblings, 5 replies; 15+ messages in thread
From: Kristen Accardi @ 2006-01-18  0:56 UTC (permalink / raw)
  To: linux-kernel; +Cc: greg, pcihpd-discuss, len.brown, linux-acpi, pavel

This patch will modify the acpiphp driver to handle docking and undocking
events and hot adding of the PCI devices on the dock station. It currently 
has a workaround for a problem with acpi where the
acpi threads will deadlock and never return from executing the _DCK method.
As a workaround, I spawn a separate thread to do the dock. In addition, 
I've found that some _DCK methods do some bad things, so have implemented
a fixups section for after docking that will no doubt grow as more laptops
get tested.  This patch CONFLICTS with some acpi plugins that try to do
their own docking support (such as ibm_acpi), so you cannot use this driver 
and the conflicting driver simultaneously. 


Signed-off-by: Kristen Carlson Accardi <kristen.c.accardi@intel.com>


 drivers/pci/hotplug/acpiphp.h      |    2 
 drivers/pci/hotplug/acpiphp_glue.c |  207 ++++++++++++++++++++++++++++++++++++-
 2 files changed, 204 insertions(+), 5 deletions(-)

--- linux-2.6.15-mm.orig/drivers/pci/hotplug/acpiphp.h
+++ linux-2.6.15-mm/drivers/pci/hotplug/acpiphp.h
@@ -189,6 +189,7 @@ struct acpiphp_attention_info
 #define SLOT_POWEREDON		(0x00000001)
 #define SLOT_ENABLED		(0x00000002)
 #define SLOT_MULTIFUNCTION	(0x00000004)
+#define SLOT_DOCKING		(0x00000008)
 
 /* function flags */
 
@@ -198,6 +199,7 @@ struct acpiphp_attention_info
 #define FUNC_HAS_PS1		(0x00000020)
 #define FUNC_HAS_PS2		(0x00000040)
 #define FUNC_HAS_PS3		(0x00000080)
+#define FUNC_HAS_DCK		(0x00000100)
 
 /* function prototypes */
 
--- linux-2.6.15-mm.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-2.6.15-mm/drivers/pci/hotplug/acpiphp_glue.c
@@ -55,12 +55,14 @@
 static LIST_HEAD(bridge_list);
 
 #define MY_NAME "acpiphp_glue"
-
+static struct work_struct dock_task;
+static int enable_device(struct acpiphp_slot *slot);
 static void handle_hotplug_event_bridge (acpi_handle, u32, void *);
 static void handle_hotplug_event_func (acpi_handle, u32, void *);
 static void acpiphp_sanitize_bus(struct pci_bus *bus);
 static void acpiphp_set_hpp_values(acpi_handle handle, struct pci_bus *bus);
-
+static void dock(void *data);
+static unsigned int get_slot_status(struct acpiphp_slot *slot);
 
 /*
  * initialization & terminatation routines
@@ -118,6 +120,30 @@ is_ejectable_slot(acpi_handle handle, u3
 }
 

+static acpi_status handle_dock(struct acpiphp_func *func, int dock)
+{
+	acpi_status status;
+	struct acpi_object_list arg_list;
+	union acpi_object arg;
+	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+
+	dbg("%s: enter\n", __FUNCTION__);
+
+	/* _DCK method has one argument */
+	arg_list.count = 1;
+	arg_list.pointer = &arg;
+	arg.type = ACPI_TYPE_INTEGER;
+	arg.integer.value = dock;
+	status = acpi_evaluate_object(func->handle, "_DCK",
+					&arg_list, &buffer);
+	if (ACPI_FAILURE(status))
+		err("%s: failed to dock!!\n", MY_NAME);
+
+	return status;
+}
+
+
+
 /* callback routine to register each ACPI PCI slot object */
 static acpi_status
 register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
@@ -210,6 +236,12 @@ register_slot(acpi_handle handle, u32 lv
 		slot->flags |= (SLOT_ENABLED | SLOT_POWEREDON);
 	}
 
+	/* install dock notify handler */
+	if (ACPI_SUCCESS(acpi_get_handle(handle, "_DCK", &tmp))) {
+		newfunc->flags |= FUNC_HAS_DCK;
+		INIT_WORK(&dock_task, dock, slot);
+	}
+
 	/* install notify handler */
 	status = acpi_install_notify_handler(handle,
 					     ACPI_SYSTEM_NOTIFY,
@@ -681,6 +713,88 @@ static int acpiphp_configure_ioapics(acp
 	return 0;
 }
 
+/*
+ * the _DCK method can do funny things... and sometimes not
+ * hah-hah funny.
+ */
+static void post_dock_fixups(struct acpiphp_slot *slot,
+			     struct acpiphp_func *func)
+{
+	struct pci_bus *bus = slot->bridge->pci_bus;
+	u32 buses;
+
+	/* fixup bad _DCK function that rewrites
+	 * secondary bridge on slot
+	 */
+	pci_read_config_dword(bus->self,
+				PCI_PRIMARY_BUS,
+				&buses);
+
+	if (((buses >> 8) & 0xff) != bus->secondary) {
+		buses = (buses & 0xff000000)
+	     		| ((unsigned int)(bus->primary)     <<  0)
+	     		| ((unsigned int)(bus->secondary)   <<  8)
+	     		| ((unsigned int)(bus->subordinate) << 16);
+		pci_write_config_dword(bus->self,
+					PCI_PRIMARY_BUS,
+					buses);
+	}
+}
+
+
+static int acpiphp_bus_add(struct acpiphp_func *func)
+{
+	acpi_handle phandle;
+	struct acpi_device *device, *pdevice;
+	int ret_val;
+
+	acpi_get_parent(func->handle, &phandle);
+	if (acpi_bus_get_device(phandle, &pdevice)) {
+		dbg("no parent device, assuming NULL\n");
+		pdevice = NULL;
+	}
+	ret_val = acpi_bus_add(&device, pdevice, func->handle,
+			ACPI_BUS_TYPE_DEVICE);
+	if (ret_val)
+		dbg("cannot add bridge to acpi list\n");
+
+	/*
+	 * try to start anyway.  We could have failed to add
+	 * simply because this bus had previously been added
+	 * on another dock.  Don't bother with the return value
+	 * we just keep going.
+	 */
+	ret_val = acpi_bus_start(device);
+
+	return ret_val;
+}
+
+
+
+static void dock(void *data)
+{
+	struct list_head *l;
+	struct acpiphp_func *func;
+	struct acpiphp_slot *slot = data;
+
+	down(&slot->crit_sect);
+	list_for_each(l, &slot->funcs) {
+		func = list_entry(l, struct acpiphp_func, sibling);
+		if (func->flags & FUNC_HAS_DCK) {
+			handle_dock(func, 1);
+			post_dock_fixups(slot, func);
+			slot->flags |= SLOT_POWEREDON;
+			if (get_slot_status(slot) == ACPI_STA_ALL) {
+				enable_device(slot);
+			}
+		}
+	}
+	slot->flags &= (~SLOT_DOCKING);
+	up(&slot->crit_sect);
+}
+
+
+
 static int power_on_slot(struct acpiphp_slot *slot)
 {
 	acpi_status status;
@@ -705,6 +819,19 @@ static int power_on_slot(struct acpiphp_
 			} else
 				break;
 		}
+
+		if (func->flags & FUNC_HAS_DCK) {
+			dbg("%s: executing _DCK\n", __FUNCTION__);
+			slot->flags |= SLOT_DOCKING;
+			/*
+			 * FIXME - work around for acpi.  Right
+			 * now if we call _DCK from this thread,
+			 * we block forever.
+			 */
+			schedule_work(&dock_task);
+			retval = -1;
+			goto err_exit;
+		}
 	}
 
 	/* TBD: evaluate _STA to check if the slot is enabled */
@@ -730,7 +857,11 @@ static int power_off_slot(struct acpiphp
 
 	list_for_each (l, &slot->funcs) {
 		func = list_entry(l, struct acpiphp_func, sibling);
-
+		if (func->flags & FUNC_HAS_DCK) {
+			dbg("%s: undock commencing\n", __FUNCTION__);
+			handle_dock(func, 0);
+			dbg("%s: undock complete\n", __FUNCTION__);
+		}
 		if (func->flags & FUNC_HAS_PS3) {
 			status = acpi_evaluate_object(func->handle, "_PS3", NULL, NULL);
 			if (ACPI_FAILURE(status)) {
@@ -752,6 +883,63 @@ static int power_off_slot(struct acpiphp
 

 /**
+ * get_func - given pci_dev & slot, get the matching acpiphp_func
+ * @slot: slot to be scanned.
+ * @dev: pci_dev to match
+ *
+ * This function will check the list of acpiphp functions for
+ * this slot and return the one that represents the given
+ * pci_dev structure.
+ */
+static struct acpiphp_func * get_func(struct acpiphp_slot *slot,
+					struct pci_dev *dev)
+{
+	struct list_head *l;
+	struct acpiphp_func *func;
+	struct pci_bus *bus = slot->bridge->pci_bus;
+
+	list_for_each (l, &slot->funcs) {
+		func = list_entry(l, struct acpiphp_func, sibling);
+		if (pci_get_slot(bus, PCI_DEVFN(slot->device,
+					func->function)) == dev)
+			return func;
+	}
+	return NULL;
+}
+
+
+
+/** acpiphp_max_busnr - find the max reserved busnr for this bus
+ *  @bus: the bus to scan
+ */
+static unsigned char
+acpiphp_max_busnr(struct pci_bus *bus)
+{
+	struct list_head *tmp;
+	unsigned char max, n;
+
+	/*
+	 * pci_bus_max_busnr will return the highest
+	 * reserved busnr for all these children.
+ 	 * that is equivalent to the bus->subordinate
+	 * value.  We don't want to use the parent's
+	 * bus->subordinate value because it could have
+	 * padding in it.
+	 */
+	max = bus->secondary;
+
+	list_for_each(tmp, &bus->children) {
+		n = pci_bus_max_busnr(pci_bus_b(tmp));
+		if (n > max)
+			max = n;
+	}
+	return max;
+}
+
+
+
+
+/**
  * enable_device - enable, configure a slot
  * @slot: slot to be enabled
  *
@@ -788,7 +976,7 @@ static int enable_device(struct acpiphp_
 		goto err_exit;
 	}
 
-	max = bus->secondary;
+	max = acpiphp_max_busnr(bus);
 	for (pass = 0; pass < 2; pass++) {
 		list_for_each_entry(dev, &bus->devices, bus_list) {
 			if (PCI_SLOT(dev->devfn) != slot->device)
@@ -796,8 +984,12 @@ static int enable_device(struct acpiphp_
 			if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
 			    dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) {
 				max = pci_scan_bridge(bus, dev, max, pass);
-				if (pass && dev->subordinate)
+				if (pass && dev->subordinate) {
 					pci_bus_size_bridges(dev->subordinate);
+					func = get_func(slot, dev);
+					if (func)
+						acpiphp_bus_add(func);
+				}
 			}
 		}
 	}
@@ -1231,6 +1423,11 @@ static void handle_hotplug_event_func(ac
 
 	case ACPI_NOTIFY_EJECT_REQUEST:
 		/* request device eject */
+		if (func->slot->flags & SLOT_DOCKING) {
+			/* ignore if we are in the middle of docking */
+			dbg("eject request in the middle of a dock\n");
+			break;
+		}
 		dbg("%s: Device eject notify on %s\n", __FUNCTION__, objname);
 		if (!(acpiphp_disable_slot(func->slot)))
 			acpiphp_eject_slot(func->slot);


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [patch 3/4] pci: really fix parent's subordinate busnr
       [not found] <20060116200218.275371000@whizzy>
  2006-01-18  0:56 ` [patch 1/4] pci: return max reserved busnr Kristen Accardi
  2006-01-18  0:56 ` [patch 2/4] acpiphp: handle dock bridges Kristen Accardi
@ 2006-01-18  0:57 ` Kristen Accardi
  2006-01-18  0:57 ` [patch 4/4] pci: quirk for IBM Dock II cardbus controllers Kristen Accardi
  3 siblings, 0 replies; 15+ messages in thread
From: Kristen Accardi @ 2006-01-18  0:57 UTC (permalink / raw)
  To: linux-kernel; +Cc: greg, pcihpd-discuss, len.brown, linux-acpi, pavel

After you find the maximum value of the subordinate buses below the child
bus, you must fix the parent's subordinate bus number again, otherwise
it may be too small.

Signed-off-by: Kristen Carlson Accardi <kristen.c.accardi@intel.com> 


 drivers/pci/probe.c |    5 +++++
 1 files changed, 5 insertions(+)

--- linux-2.6.15-mm.orig/drivers/pci/probe.c
+++ linux-2.6.15-mm/drivers/pci/probe.c
@@ -537,6 +537,11 @@ int __devinit pci_scan_bridge(struct pci
 			pci_fixup_parent_subordinate_busnr(child, max);
 			/* Now we can scan all subordinate buses... */
 			max = pci_scan_child_bus(child);
+			/*
+			 * now fix it up again since we have found
+			 * the real value of max.
+			 */
+			pci_fixup_parent_subordinate_busnr(child, max);
 		} else {
 			/*
 			 * For CardBus bridges, we leave 4 bus numbers


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [patch 4/4]  pci: quirk for IBM Dock II cardbus controllers
       [not found] <20060116200218.275371000@whizzy>
                   ` (2 preceding siblings ...)
  2006-01-18  0:57 ` [patch 3/4] pci: really fix parent's subordinate busnr Kristen Accardi
@ 2006-01-18  0:57 ` Kristen Accardi
  3 siblings, 0 replies; 15+ messages in thread
From: Kristen Accardi @ 2006-01-18  0:57 UTC (permalink / raw)
  To: linux-kernel; +Cc: greg, pcihpd-discuss, len.brown, linux-acpi, pavel

The IBM Dock II cardbus bridges require some extra configuration
before Yenta is loaded in order to setup the Interrupts to be
routed properly. 

Signed-off-by: Kristen Carlson Accardi <kristen.c.accardi@intel.com>

 drivers/pci/quirks.c |   27 +++++++++++++++++++++++++++
 1 files changed, 27 insertions(+)

--- linux-2.6.15-mm.orig/drivers/pci/quirks.c
+++ linux-2.6.15-mm/drivers/pci/quirks.c
@@ -1242,6 +1242,33 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_IN
 DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_PXHV,	quirk_pcie_pxh);
 

+/*
+ * Fixup the cardbus bridges on the IBM Dock II docking station
+ */
+static void __devinit quirk_ibm_dock2_cardbus(struct pci_dev *dev)
+{
+	u32 val;
+
+	/*
+	 * tie the 2 interrupt pins to INTA, and configure the
+	 * multifunction routing register to handle this.
+	 */
+	if ((dev->subsystem_vendor == PCI_VENDOR_ID_IBM) &&
+		(dev->subsystem_device == 0x0148)) {
+		printk(KERN_INFO "PCI: Found IBM Dock II Cardbus Bridge "
+			"applying quirk\n");
+		pci_read_config_dword(dev, 0x8c, &val);
+		val = ((val & 0xffffff00) | 0x1002);
+		pci_write_config_dword(dev, 0x8c, val);
+		pci_read_config_dword(dev, 0x80, &val);
+		val = ((val & 0x00ffff00) | 0x2864c077);
+		pci_write_config_dword(dev, 0x80, val);
+	}
+}
+
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_1420,
+				quirk_ibm_dock2_cardbus);
+
 static void __devinit quirk_netmos(struct pci_dev *dev)
 {
 	unsigned int num_parallel = (dev->subsystem_device & 0xf0) >> 4;


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [Pcihpd-discuss] [patch 2/4]  acpiphp: handle dock bridges
  2006-01-18  0:56 ` [patch 2/4] acpiphp: handle dock bridges Kristen Accardi
@ 2006-01-18  2:26   ` Kenji Kaneshige
  2006-01-18  5:11     ` Greg KH
  2006-01-18  3:55   ` Dmitry Torokhov
                     ` (3 subsequent siblings)
  4 siblings, 1 reply; 15+ messages in thread
From: Kenji Kaneshige @ 2006-01-18  2:26 UTC (permalink / raw)
  To: Kristen Accardi
  Cc: linux-kernel, greg, pcihpd-discuss, len.brown, linux-acpi, pavel

Hi,

> +static struct acpiphp_func * get_func(struct acpiphp_slot *slot,
> +					struct pci_dev *dev)
> +{
> +	struct list_head *l;
> +	struct acpiphp_func *func;
> +	struct pci_bus *bus = slot->bridge->pci_bus;
> +
> +	list_for_each (l, &slot->funcs) {
> +		func = list_entry(l, struct acpiphp_func, sibling);
> +		if (pci_get_slot(bus, PCI_DEVFN(slot->device,
> +					func->function)) == dev)
> +			return func;
> +	}

This seems to leak reference counter of pci_dev. I think you
must call pci_dev_put() also.

Thanks,
Kenji Kaneshige

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [patch 2/4]  acpiphp: handle dock bridges
  2006-01-18  0:56 ` [patch 2/4] acpiphp: handle dock bridges Kristen Accardi
  2006-01-18  2:26   ` [Pcihpd-discuss] " Kenji Kaneshige
@ 2006-01-18  3:55   ` Dmitry Torokhov
  2006-01-18  4:43   ` [Pcihpd-discuss] " Kenji Kaneshige
                     ` (2 subsequent siblings)
  4 siblings, 0 replies; 15+ messages in thread
From: Dmitry Torokhov @ 2006-01-18  3:55 UTC (permalink / raw)
  To: Kristen Accardi
  Cc: linux-kernel, greg, pcihpd-discuss, len.brown, linux-acpi, pavel

On Tuesday 17 January 2006 19:56, Kristen Accardi wrote:

> +
> +	down(&slot->crit_sect);
> +	list_for_each(l, &slot->funcs) {
> +		func = list_entry(l, struct acpiphp_func, sibling);

list_for_each_entry() maybe?

> +static struct acpiphp_func * get_func(struct acpiphp_slot *slot,
> +					struct pci_dev *dev)
> +{
> +	struct list_head *l;
> +	struct acpiphp_func *func;
> +	struct pci_bus *bus = slot->bridge->pci_bus;
> +
> +	list_for_each (l, &slot->funcs) {
> +		func = list_entry(l, struct acpiphp_func, sibling);

And here?

-- 
Dmitry

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [Pcihpd-discuss] [patch 2/4]  acpiphp: handle dock bridges
  2006-01-18  0:56 ` [patch 2/4] acpiphp: handle dock bridges Kristen Accardi
  2006-01-18  2:26   ` [Pcihpd-discuss] " Kenji Kaneshige
  2006-01-18  3:55   ` Dmitry Torokhov
@ 2006-01-18  4:43   ` Kenji Kaneshige
  2006-01-20  3:24   ` MUNEDA Takahiro
  2006-01-21  1:55   ` [PATCH] acpiphp: treat dck separate from dock bridge Kristen Accardi
  4 siblings, 0 replies; 15+ messages in thread
From: Kenji Kaneshige @ 2006-01-18  4:43 UTC (permalink / raw)
  To: Kristen Accardi
  Cc: linux-kernel, greg, pcihpd-discuss, len.brown, linux-acpi, pavel

Kristen Accardi wrote:
> +static acpi_status handle_dock(struct acpiphp_func *func, int dock)
> +{
> +	acpi_status status;
> +	struct acpi_object_list arg_list;
> +	union acpi_object arg;
> +	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
> +
> +	dbg("%s: enter\n", __FUNCTION__);
> +
> +	/* _DCK method has one argument */
> +	arg_list.count = 1;
> +	arg_list.pointer = &arg;
> +	arg.type = ACPI_TYPE_INTEGER;
> +	arg.integer.value = dock;
> +	status = acpi_evaluate_object(func->handle, "_DCK",
> +					&arg_list, &buffer);
> +	if (ACPI_FAILURE(status))
> +		err("%s: failed to dock!!\n", MY_NAME);
> +
> +	return status;
> +}

I think you need to add acpi_os_free() for freeing buffer.pointer.

Thanks,
Kenji Kaneshige

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [Pcihpd-discuss] [patch 2/4]  acpiphp: handle dock bridges
  2006-01-18  2:26   ` [Pcihpd-discuss] " Kenji Kaneshige
@ 2006-01-18  5:11     ` Greg KH
  0 siblings, 0 replies; 15+ messages in thread
From: Greg KH @ 2006-01-18  5:11 UTC (permalink / raw)
  To: Kenji Kaneshige
  Cc: Kristen Accardi, linux-kernel, pcihpd-discuss, len.brown,
	linux-acpi, pavel

On Wed, Jan 18, 2006 at 11:26:41AM +0900, Kenji Kaneshige wrote:
> Hi,
> 
> > +static struct acpiphp_func * get_func(struct acpiphp_slot *slot,
> > +					struct pci_dev *dev)
> > +{
> > +	struct list_head *l;
> > +	struct acpiphp_func *func;
> > +	struct pci_bus *bus = slot->bridge->pci_bus;
> > +
> > +	list_for_each (l, &slot->funcs) {
> > +		func = list_entry(l, struct acpiphp_func, sibling);
> > +		if (pci_get_slot(bus, PCI_DEVFN(slot->device,
> > +					func->function)) == dev)
> > +			return func;
> > +	}
> 
> This seems to leak reference counter of pci_dev. I think you
> must call pci_dev_put() also.

Yes, good catch.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [Pcihpd-discuss] [patch 2/4] acpiphp: handle dock bridges
  2006-01-18  0:56 ` [patch 2/4] acpiphp: handle dock bridges Kristen Accardi
                     ` (2 preceding siblings ...)
  2006-01-18  4:43   ` [Pcihpd-discuss] " Kenji Kaneshige
@ 2006-01-20  3:24   ` MUNEDA Takahiro
  2006-01-20 17:56     ` Kristen Accardi
  2006-01-21  1:55   ` [PATCH] acpiphp: treat dck separate from dock bridge Kristen Accardi
  4 siblings, 1 reply; 15+ messages in thread
From: MUNEDA Takahiro @ 2006-01-20  3:24 UTC (permalink / raw)
  To: Kristen Accardi
  Cc: linux-kernel, greg, pcihpd-discuss, len.brown, linux-acpi, pavel

Hi,

At Tue, 17 Jan 2006 16:56:59 -0800,
Kristen Accardi <kristen.c.accardi@intel.com> wrote:
> 
> @@ -681,6 +713,88 @@ static int acpiphp_configure_ioapics(acp
(snip)
> +static int acpiphp_bus_add(struct acpiphp_func *func)
> +{
> +	acpi_handle phandle;
> +	struct acpi_device *device, *pdevice;
> +	int ret_val;
> +
> +	acpi_get_parent(func->handle, &phandle);
> +	if (acpi_bus_get_device(phandle, &pdevice)) {
> +		dbg("no parent device, assuming NULL\n");
> +		pdevice = NULL;
> +	}
> +	ret_val = acpi_bus_add(&device, pdevice, func->handle,
> +			ACPI_BUS_TYPE_DEVICE);
> +	if (ret_val)
> +		dbg("cannot add bridge to acpi list\n");
> +
> +	/*
> +	 * try to start anyway.  We could have failed to add
> +	 * simply because this bus had previously been added
> +	 * on another dock.  Don't bother with the return value
> +	 * we just keep going.
> +	 */
> +	ret_val = acpi_bus_start(device);
> +
> +	return ret_val;
> +}
> +
> +
> +

When the device is docked, acpi_bus_add() is called to register
the device into acpi list. But if the device is undocked,
acpi_bus_trim() doesn't called.
Can you hot-add/remove the dock station repeatedly?

Thanks,
MUNE

-- 
MUNEDA Takahiro <muneda.takahiro@jp.fujitsu.com>

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [Pcihpd-discuss] [patch 2/4] acpiphp: handle dock bridges
  2006-01-20  3:24   ` MUNEDA Takahiro
@ 2006-01-20 17:56     ` Kristen Accardi
  0 siblings, 0 replies; 15+ messages in thread
From: Kristen Accardi @ 2006-01-20 17:56 UTC (permalink / raw)
  To: MUNEDA Takahiro
  Cc: linux-kernel, greg, pcihpd-discuss, len.brown, linux-acpi, pavel

On Fri, 2006-01-20 at 12:24 +0900, MUNEDA Takahiro wrote:
> Hi,
> 
> At Tue, 17 Jan 2006 16:56:59 -0800,
> Kristen Accardi <kristen.c.accardi@intel.com> wrote:
> > 
> > @@ -681,6 +713,88 @@ static int acpiphp_configure_ioapics(acp
> (snip)
> > +static int acpiphp_bus_add(struct acpiphp_func *func)
> > +{
> > +	acpi_handle phandle;
> > +	struct acpi_device *device, *pdevice;
> > +	int ret_val;
> > +
> > +	acpi_get_parent(func->handle, &phandle);
> > +	if (acpi_bus_get_device(phandle, &pdevice)) {
> > +		dbg("no parent device, assuming NULL\n");
> > +		pdevice = NULL;
> > +	}
> > +	ret_val = acpi_bus_add(&device, pdevice, func->handle,
> > +			ACPI_BUS_TYPE_DEVICE);
> > +	if (ret_val)
> > +		dbg("cannot add bridge to acpi list\n");
> > +
> > +	/*
> > +	 * try to start anyway.  We could have failed to add
> > +	 * simply because this bus had previously been added
> > +	 * on another dock.  Don't bother with the return value
> > +	 * we just keep going.
> > +	 */
> > +	ret_val = acpi_bus_start(device);
> > +
> > +	return ret_val;
> > +}
> > +
> > +
> > +
> 
> When the device is docked, acpi_bus_add() is called to register
> the device into acpi list. But if the device is undocked,
> acpi_bus_trim() doesn't called.
> Can you hot-add/remove the dock station repeatedly?
> 
> Thanks,
> MUNE
> 

Yes you can.  The reason for this is because even if we fail the call to
acpi_bus_add(), we ignore the return value and call acpi_bus_start()
anyway.


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH] acpiphp: treat dck separate from dock bridge
  2006-01-18  0:56 ` [patch 2/4] acpiphp: handle dock bridges Kristen Accardi
                     ` (3 preceding siblings ...)
  2006-01-20  3:24   ` MUNEDA Takahiro
@ 2006-01-21  1:55   ` Kristen Accardi
  2006-01-21 11:27     ` Pavel Machek
                       ` (2 more replies)
  4 siblings, 3 replies; 15+ messages in thread
From: Kristen Accardi @ 2006-01-21  1:55 UTC (permalink / raw)
  To: linux-kernel; +Cc: greg, pcihpd-discuss, len.brown, linux-acpi, pavel

Here's an addition to the acpiphp dock station patch for interested
people to try.  It has a problem that I've not solved yet that will
cause the driver to oops if you boot your laptop docked, then load the
driver, then try to undock.  But, it works for me if you boot undocked,
then load the driver, then dock/undock etc.  This patch is different
from the original in that I no longer assume that _DCK is defined under
the actual p2p dock bridge.  However, the solution I came up with is not
actually very good.  I have to somehow find out which device is the
actual p2p bridge, and what I try to do here is:
1) find the device that _DCK is defined under
2) if this is a bridged device, we are done (i.e. is has _ADR and is
ejectable)
3) if not, then walk the namespace looking for _EJD.  If you find one,
see if the dependency is on the dock device.  The dock bridge should be
dependent on the dock device.  

	- when you find something that is dependent on the dock device,
	  see if it has an _ADR and a _PRT entry.  If so, then it is the
	  p2p dock bridge.

So, this is likely a wrong assumption, so I will be thinking some more
about how to tell which acpi device is the dock bridge.  But meanwhile,
this patch could be tested/reviewed to see if it gets us a step further
in the right direction.

I patched against 2.6.16-rc1-mm2 because I wasn't sure if it was safe to
just drop my other patch (since actually trying to just revert the
broken out patch didn't actually work).  So this patches the file with
my original patch applied.

Signed-off-by: Kristen Carlson Accardi <kristen.c.accardi@intel.com>

 drivers/pci/hotplug/acpiphp.h      |   11 +
 drivers/pci/hotplug/acpiphp_glue.c |  354 +++++++++++++++++++++++++++----------
 2 files changed, 272 insertions(+), 93 deletions(-)

--- linux-2.6.16-rc1.orig/drivers/pci/hotplug/acpiphp.h
+++ linux-2.6.16-rc1/drivers/pci/hotplug/acpiphp.h
@@ -148,6 +148,17 @@ struct acpiphp_func {
 	u32		flags;		/* see below */
 };
 
+
+struct acpiphp_dock_bridge {
+	acpi_handle dock_bridge_handle;
+	acpi_handle dck_handle;
+	struct acpiphp_func *func;
+	struct acpiphp_slot *slot;
+	u32 last_dock_time;
+	u32 flags;
+};
+
+
 /**
  * struct acpiphp_attention_info - device specific attention registration
  *
--- linux-2.6.16-rc1.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-2.6.16-rc1/drivers/pci/hotplug/acpiphp_glue.c
@@ -53,15 +53,14 @@
 #include "acpiphp.h"
 
 static LIST_HEAD(bridge_list);
+static struct acpiphp_dock_bridge dock_bridge;
 
 #define MY_NAME "acpiphp_glue"
-static struct work_struct dock_task;
 static int enable_device(struct acpiphp_slot *slot);
 static void handle_hotplug_event_bridge (acpi_handle, u32, void *);
 static void handle_hotplug_event_func (acpi_handle, u32, void *);
 static void acpiphp_sanitize_bus(struct pci_bus *bus);
 static void acpiphp_set_hpp_values(acpi_handle handle, struct pci_bus *bus);
-static void dock(void *data);
 static unsigned int get_slot_status(struct acpiphp_slot *slot);
 
 /*
@@ -104,13 +103,26 @@ static int is_ejectable(acpi_handle hand
 }
 
 
+
+static int is_dock(acpi_handle handle)
+{
+	acpi_status status;
+	acpi_handle tmp;
+
+	status = acpi_get_handle(handle, "_DCK", &tmp);
+	if (ACPI_FAILURE(status)) {
+		return 0;
+	}
+	return 1;
+}
+
 /* callback routine to check the existence of ejectable slots */
 static acpi_status
 is_ejectable_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
 {
 	int *count = (int *)context;
 
-	if (is_ejectable(handle)) {
+	if (is_ejectable(handle) || (handle == dock_bridge.dock_bridge_handle)) {
 		(*count)++;
 		/* only one ejectable slot is enough */
 		return AE_CTRL_TERMINATE;
@@ -120,7 +132,9 @@ is_ejectable_slot(acpi_handle handle, u3
 }
 
 
-static acpi_status handle_dock(struct acpiphp_func *func, int dock)
+
+
+static acpi_status handle_dock(int dock)
 {
 	acpi_status status;
 	struct acpi_object_list arg_list;
@@ -134,10 +148,13 @@ static acpi_status handle_dock(struct ac
 	arg_list.pointer = &arg;
 	arg.type = ACPI_TYPE_INTEGER;
 	arg.integer.value = dock;
-	status = acpi_evaluate_object(func->handle, "_DCK",
+	status = acpi_evaluate_object(dock_bridge.dck_handle, "_DCK",
 					&arg_list, &buffer);
 	if (ACPI_FAILURE(status))
 		err("%s: failed to dock!!\n", MY_NAME);
+	acpi_os_free(buffer.pointer);
+
+	dbg("%s: exit\n", __FUNCTION__);
 
 	return status;
 }
@@ -157,6 +174,7 @@ register_slot(acpi_handle handle, u32 lv
 	int device, function;
 	static int num_slots = 0;	/* XXX if we support I/O node hotplug... */
 
+
 	status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr);
 
 	if (ACPI_FAILURE(status))
@@ -164,9 +182,10 @@ register_slot(acpi_handle handle, u32 lv
 
 	status = acpi_get_handle(handle, "_EJ0", &tmp);
 
-	if (ACPI_FAILURE(status))
+	if ((handle != dock_bridge.dock_bridge_handle) && ACPI_FAILURE(status))
 		return AE_OK;
 
+
 	device = (adr >> 16) & 0xffff;
 	function = adr & 0xffff;
 
@@ -178,7 +197,8 @@ register_slot(acpi_handle handle, u32 lv
 	INIT_LIST_HEAD(&newfunc->sibling);
 	newfunc->handle = handle;
 	newfunc->function = function;
-	newfunc->flags = FUNC_HAS_EJ0;
+	if (ACPI_SUCCESS(status))
+		newfunc->flags = FUNC_HAS_EJ0;
 
 	if (ACPI_SUCCESS(acpi_get_handle(handle, "_STA", &tmp)))
 		newfunc->flags |= FUNC_HAS_STA;
@@ -189,6 +209,9 @@ register_slot(acpi_handle handle, u32 lv
 	if (ACPI_SUCCESS(acpi_get_handle(handle, "_PS3", &tmp)))
 		newfunc->flags |= FUNC_HAS_PS3;
 
+	if (ACPI_SUCCESS(acpi_get_handle(handle, "_DCK", &tmp)))
+		newfunc->flags |= FUNC_HAS_DCK;
+
 	status = acpi_evaluate_integer(handle, "_SUN", NULL, &sun);
 	if (ACPI_FAILURE(status))
 		sun = -1;
@@ -236,24 +259,22 @@ register_slot(acpi_handle handle, u32 lv
 		slot->flags |= (SLOT_ENABLED | SLOT_POWEREDON);
 	}
 
-	/* install dock notify handler */
-	if (ACPI_SUCCESS(acpi_get_handle(handle, "_DCK", &tmp))) {
-		newfunc->flags |= FUNC_HAS_DCK;
-		INIT_WORK(&dock_task, dock, slot);
-	}
 
 	/* install notify handler */
-	status = acpi_install_notify_handler(handle,
+	if (!(newfunc->flags & FUNC_HAS_DCK)) {
+		status = acpi_install_notify_handler(handle,
 					     ACPI_SYSTEM_NOTIFY,
 					     handle_hotplug_event_func,
 					     newfunc);
-
-	if (ACPI_FAILURE(status)) {
-		err("failed to register interrupt notify handler\n");
-		return status;
+		if (ACPI_FAILURE(status))
+			err("failed to register interrupt notify handler\n");
+	} else if (handle == dock_bridge.dock_bridge_handle) {
+		dock_bridge.func = newfunc;
+		dock_bridge.slot = slot;
+		status = AE_OK;
 	}
 
-	return AE_OK;
+	return status;
 }
 
 
@@ -544,11 +565,13 @@ static void cleanup_bridge(struct acpiph
 		list_for_each_safe (list, tmp, &slot->funcs) {
 			struct acpiphp_func *func;
 			func = list_entry(list, struct acpiphp_func, sibling);
-			status = acpi_remove_notify_handler(func->handle,
+			if (!(func->flags & FUNC_HAS_DCK)) {
+				status = acpi_remove_notify_handler(func->handle,
 						ACPI_SYSTEM_NOTIFY,
 						handle_hotplug_event_func);
-			if (ACPI_FAILURE(status))
-				err("failed to remove notify handler\n");
+				if (ACPI_FAILURE(status))
+					err("failed to remove notify handler\n");
+			}
 			pci_dev_put(func->pci_dev);
 			list_del(list);
 			kfree(func);
@@ -713,33 +736,6 @@ static int acpiphp_configure_ioapics(acp
 	return 0;
 }
 
-/*
- * the _DCK method can do funny things... and sometimes not
- * hah-hah funny.
- */
-static void post_dock_fixups(struct acpiphp_slot *slot,
-			     struct acpiphp_func *func)
-{
-	struct pci_bus *bus = slot->bridge->pci_bus;
-	u32 buses;
-
-	/* fixup bad _DCK function that rewrites
-	 * secondary bridge on slot
-	 */
-	pci_read_config_dword(bus->self,
-				PCI_PRIMARY_BUS,
-				&buses);
-
-	if (((buses >> 8) & 0xff) != bus->secondary) {
-		buses = (buses & 0xff000000)
-	     		| ((unsigned int)(bus->primary)     <<  0)
-	     		| ((unsigned int)(bus->secondary)   <<  8)
-	     		| ((unsigned int)(bus->subordinate) << 16);
-		pci_write_config_dword(bus->self,
-					PCI_PRIMARY_BUS,
-					buses);
-	}
-}
 
 
 static int acpiphp_bus_add(struct acpiphp_func *func)
@@ -771,26 +767,32 @@ static int acpiphp_bus_add(struct acpiph
 
 
 
-static void dock(void *data)
+
+/*
+ * the _DCK method can do funny things... and sometimes not
+ * hah-hah funny.
+ */
+static void post_dock_fixups(void)
 {
-	struct list_head *l;
-	struct acpiphp_func *func;
-	struct acpiphp_slot *slot = data;
+	struct pci_bus *bus = dock_bridge.slot->bridge->pci_bus;
+	u32 buses;
 
-	mutex_lock(&slot->crit_sect);
-	list_for_each(l, &slot->funcs) {
-		func = list_entry(l, struct acpiphp_func, sibling);
-		if (func->flags & FUNC_HAS_DCK) {
-			handle_dock(func, 1);
-			post_dock_fixups(slot, func);
-			slot->flags |= SLOT_POWEREDON;
-			if (get_slot_status(slot) == ACPI_STA_ALL) {
-				enable_device(slot);
-			}
-		}
+	/* fixup bad _DCK function that rewrites
+	 * secondary bridge on slot
+	 */
+	pci_read_config_dword(bus->self,
+				PCI_PRIMARY_BUS,
+				&buses);
+
+	if (((buses >> 8) & 0xff) != bus->secondary) {
+		buses = (buses & 0xff000000)
+	     		| ((unsigned int)(bus->primary)     <<  0)
+	     		| ((unsigned int)(bus->secondary)   <<  8)
+	     		| ((unsigned int)(bus->subordinate) << 16);
+		pci_write_config_dword(bus->self,
+					PCI_PRIMARY_BUS,
+					buses);
 	}
-	slot->flags &= (~SLOT_DOCKING);
-	mutex_unlock(&slot->crit_sect);
 }
 
 
@@ -819,19 +821,6 @@ static int power_on_slot(struct acpiphp_
 			} else
 				break;
 		}
-
-		if (func->flags & FUNC_HAS_DCK) {
-			dbg("%s: executing _DCK\n", __FUNCTION__);
-			slot->flags |= SLOT_DOCKING;
-			/*
-			 * FIXME - work around for acpi.  Right
-			 * now if we call _DCK from this thread,
-			 * we block forever.
-			 */
-			schedule_work(&dock_task);
-			retval = -1;
-			goto err_exit;
-		}
 	}
 
 	/* TBD: evaluate _STA to check if the slot is enabled */
@@ -857,11 +846,6 @@ static int power_off_slot(struct acpiphp
 
 	list_for_each (l, &slot->funcs) {
 		func = list_entry(l, struct acpiphp_func, sibling);
-		if (func->flags & FUNC_HAS_DCK) {
-			dbg("%s: undock commencing\n", __FUNCTION__);
-			handle_dock(func, 0);
-			dbg("%s: undock complete\n", __FUNCTION__);
-		}
 		if (func->flags & FUNC_HAS_PS3) {
 			status = acpi_evaluate_object(func->handle, "_PS3", NULL, NULL);
 			if (ACPI_FAILURE(status)) {
@@ -871,6 +855,7 @@ static int power_off_slot(struct acpiphp
 			} else
 				break;
 		}
+
 	}
 
 	/* TBD: evaluate _STA to check if the slot is disabled */
@@ -882,6 +867,8 @@ static int power_off_slot(struct acpiphp
 }
 
 
+
+
 /**
  * get_func - given pci_dev & slot, get the matching acpiphp_func
  * @slot: slot to be scanned.
@@ -889,22 +876,27 @@ static int power_off_slot(struct acpiphp
  *
  * This function will check the list of acpiphp functions for
  * this slot and return the one that represents the given
- * pci_dev structure.
+ * pci_dev structure.  This function will incremente the ref
+ * pointer of the pci_dev struct as a side effect, so the caller
+ * must call pci_dev_put when they are done.
  */
 static struct acpiphp_func * get_func(struct acpiphp_slot *slot,
 					struct pci_dev *dev)
 {
-	struct list_head *l;
-	struct acpiphp_func *func;
+	struct acpiphp_func *func = NULL;
 	struct pci_bus *bus = slot->bridge->pci_bus;
+	struct pci_dev *pdev;
 
-	list_for_each (l, &slot->funcs) {
-		func = list_entry(l, struct acpiphp_func, sibling);
-		if (pci_get_slot(bus, PCI_DEVFN(slot->device,
-					func->function)) == dev)
-			return func;
+	list_for_each_entry (func, &slot->funcs, sibling) {
+		pdev = pci_get_slot(bus, PCI_DEVFN(slot->device,
+					func->function));
+		if (pdev) {
+			if (pdev == dev)
+				break;
+			pci_dev_put(pdev);
+		}
 	}
-	return NULL;
+	return func;
 }
 
 
@@ -987,8 +979,11 @@ static int enable_device(struct acpiphp_
 				if (pass && dev->subordinate) {
 					pci_bus_size_bridges(dev->subordinate);
 					func = get_func(slot, dev);
-					if (func)
+					if (func) {
 						acpiphp_bus_add(func);
+						/* side effect of get_func() */
+						pci_dev_put(dev);
+					}
 				}
 			}
 		}
@@ -1306,6 +1301,7 @@ static void handle_bridge_insertion(acpi
  * ACPI event handlers
  */
 
+
 /**
  * handle_hotplug_event_bridge - handle ACPI event on bridges
  *
@@ -1439,6 +1435,47 @@ static void handle_hotplug_event_func(ac
 	}
 }
 
+
+
+static void
+handle_hotplug_event_dock(acpi_handle handle, u32 type, void *context)
+{
+	acpi_handle dock = dock_bridge.dock_bridge_handle;
+
+	switch (type) {
+		case ACPI_NOTIFY_BUS_CHECK:
+			dbg("%s: Bus check notify\n",
+				__FUNCTION__);
+			dock_bridge.flags |= SLOT_DOCKING;
+			handle_dock(1);
+			if (dock) {
+				post_dock_fixups();
+				handle_hotplug_event_func(dock,
+					type, dock_bridge.func);
+			}
+			dock_bridge.flags &= ~(SLOT_DOCKING);
+			dock_bridge.last_dock_time = jiffies;
+			break;
+		case ACPI_NOTIFY_EJECT_REQUEST:
+			if (dock_bridge.flags & SLOT_DOCKING ||
+				dock_bridge.last_dock_time == jiffies) {
+				dbg("%s: Ignore bogus eject request\n",
+					__FUNCTION__);
+			} else {
+				dbg("%s: Eject notify\n", __FUNCTION__);
+				handle_dock(0);
+				handle_hotplug_event_func(dock,
+					type, dock_bridge.func);
+			}
+			break;
+		default:
+			warn("%s: unknown event type 0x%x\n",
+				__FUNCTION__, type);
+	}
+}
+
+
+
 static int is_root_bridge(acpi_handle handle)
 {
 	acpi_status status;
@@ -1468,6 +1505,118 @@ static int is_root_bridge(acpi_handle ha
 	return 0;
 }
 
+
+static acpi_status
+find_dock_ejd(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+	acpi_status status;
+	acpi_handle tmp;
+	acpi_handle dck_handle = (acpi_handle) context;
+	char objname[64];
+	char ejd_objname[64];
+	struct acpi_buffer buffer = { .length = sizeof(objname),
+				      .pointer = objname };
+	struct acpi_buffer ejd_buffer = { .length = sizeof(ejd_objname),
+					  .pointer = ejd_objname };
+	union acpi_object *ejd_obj;
+	union acpi_object *dck_obj;
+
+	status = acpi_get_handle(handle, "_EJD", &tmp);
+	if (ACPI_FAILURE(status)) {
+		return 0;
+	}
+
+	/* make sure we are dependent on the dock device */
+	acpi_get_name(dck_handle, ACPI_FULL_PATHNAME, &buffer);
+	status = acpi_evaluate_object(handle, "_EJD", NULL, &ejd_buffer);
+	if (ACPI_FAILURE(status)) {
+		err("Unable to execute _EJD!\n");
+		return 0;
+	}
+
+	ejd_obj = ejd_buffer.pointer;
+	dck_obj = buffer.pointer;
+	if (!strncmp(ejd_obj->string.pointer, dck_obj->string.pointer,
+		ejd_obj->string.length)) {
+		/* ok, this device is dependent on the dock device,
+		 * if it was the actual dock bridge, it would have
+		 * a PRT associated with it.
+		 */
+		status = acpi_get_handle(handle, "_PRT", &tmp);
+		if (ACPI_FAILURE(status))
+			return 0;
+
+		/* yippee, we found the dock bridge! */
+		*(rv) = handle;
+		return AE_CTRL_TERMINATE;
+	}
+	return AE_OK;
+}
+
+
+static acpi_handle
+get_dock_handle(acpi_handle handle)
+{
+	acpi_handle dock_bridge_handle = NULL;
+
+	/*
+	 * first see if we are the dock bridge.
+	 */
+	if (is_ejectable(handle))
+		return handle;
+
+	/*
+	 * otherwise, we are going to have to find
+	 * the dock bridge by checking the _EJD list.
+	 */
+	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+		ACPI_UINT32_MAX, find_dock_ejd, handle, &dock_bridge_handle);
+
+	return dock_bridge_handle;
+}
+
+
+
+static acpi_status
+find_dock(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+	int *count = (int *)context;
+
+	if (is_dock(handle)) {
+		/* found a dock.  Now we have to determine if
+		 * the _DCK method is within the scope of the
+		 * dock bridge, or outside it (as in the IBM x-series)
+		 */
+		dock_bridge.dock_bridge_handle = get_dock_handle(handle);
+		dock_bridge.dck_handle = handle;
+		acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
+			handle_hotplug_event_dock, NULL);
+		(*count)++;
+	}
+
+	return AE_OK;
+}
+
+
+
+
+static int
+find_dock_bridge(void)
+{
+	int num = 0;
+
+	/* start from the root object, because some laptops define
+	 * _DCK methods outside the scope of PCI (IBM x-series laptop)
+	 */
+	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+			ACPI_UINT32_MAX, find_dock, &num, NULL);
+
+	return num;
+}
+
+
+
+
 static acpi_status
 find_root_bridges(acpi_handle handle, u32 lvl, void *context, void **rv)
 {
@@ -1494,6 +1643,8 @@ int __init acpiphp_glue_init(void)
 {
 	int num = 0;
 
+	num = find_dock_bridge();
+
 	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
 			ACPI_UINT32_MAX, find_root_bridges, &num, NULL);
 
@@ -1513,6 +1664,17 @@ int __init acpiphp_glue_init(void)
  */
 void __exit acpiphp_glue_exit(void)
 {
+	acpi_handle handle = dock_bridge.dck_handle;
+
+	/* if we have a dock station handle, we should
+	 * remove the notify handler
+	 */
+	if (handle) {
+		if (ACPI_FAILURE(acpi_remove_notify_handler(handle,
+			ACPI_SYSTEM_NOTIFY, handle_hotplug_event_dock)))
+			err("failed to remove dock notify handler\n");
+	}
+
 	acpi_pci_unregister_driver(&acpi_pci_hp_driver);
 }
 
@@ -1537,6 +1699,12 @@ int __init acpiphp_get_num_slots(void)
 		num_slots += bridge->nr_slots;
 	}
 
+	/* it's possible to have a dock station that doesn't actually
+	 * use a pci dock bridge.  For now, continue to allow this
+	 * to be handled by this driver.
+	 */
+	if (dock_bridge.dck_handle && !dock_bridge.dock_bridge_handle)
+		num_slots++;
 	dbg("Total %d slots\n", num_slots);
 	return num_slots;
 }


^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] acpiphp: treat dck separate from dock bridge
  2006-01-21  1:55   ` [PATCH] acpiphp: treat dck separate from dock bridge Kristen Accardi
@ 2006-01-21 11:27     ` Pavel Machek
  2006-01-21 11:29     ` Pavel Machek
  2006-01-21 12:10     ` Pavel Machek
  2 siblings, 0 replies; 15+ messages in thread
From: Pavel Machek @ 2006-01-21 11:27 UTC (permalink / raw)
  To: Kristen Accardi; +Cc: linux-kernel, greg, pcihpd-discuss, len.brown, linux-acpi

Hi!
On Pá 20-01-06 17:55:06, Kristen Accardi wrote:
> Here's an addition to the acpiphp dock station patch for interested
> people to try.  It has a problem that I've not solved yet that will
> cause the driver to oops if you boot your laptop docked, then load the
> driver, then try to undock.  But, it works for me if you boot undocked,
> then load the driver, then dock/undock etc.  This patch is different
> from the original in that I no longer assume that _DCK is defined under
> the actual p2p dock bridge.  However, the solution I came up with is not
> actually very good.  I have to somehow find out which device is the
> actual p2p bridge, and what I try to do here is:
> 1) find the device that _DCK is defined under
> 2) if this is a bridged device, we are done (i.e. is has _ADR and is
> ejectable)
> 3) if not, then walk the namespace looking for _EJD.  If you find one,
> see if the dependency is on the dock device.  The dock bridge should be
> dependent on the dock device.  
> 
> 	- when you find something that is dependent on the dock device,
> 	  see if it has an _ADR and a _PRT entry.  If so, then it is the
> 	  p2p dock bridge.
> 
> So, this is likely a wrong assumption, so I will be thinking some more
> about how to tell which acpi device is the dock bridge.  But meanwhile,
> this patch could be tested/reviewed to see if it gets us a step further
> in the right direction.
> 
> I patched against 2.6.16-rc1-mm2 because I wasn't sure if it was safe to
> just drop my other patch (since actually trying to just revert the
> broken out patch didn't actually work).  So this patches the file with
> my original patch applied.

It oopsed on me while insmoding it: ... will try to investigate.

root@amd:/data/l/linux-mm/drivers/pci/hotplug# insmod acpiphp.ko
BUG: unable to handle kernel NULL pointer dereference at virtual address 00000044
 printing eip:
*pde = 00000000
Oops: 0000 [#1]
last sysfs file: /devices/system/cpu/cpu0/cpufreq/scaling_governor
Modules linked in: acpiphp
CPU:    0
EIP:    0060:[<f9990d20>]    Not tainted VLI
EFLAGS: 00010286   (2.6.16-rc1-mm2 #2)
EIP is at acpiphp_get_power_status+0x0/0x10 [acpiphp]
eax: 00000000   ebx: f796bbe0   ecx: f9995384   edx: f7edc2c0
esi: f796bca0   edi: 00000014   ebp: f9992c35   esp: f7b35e14
ds: 007b   es: 007b   ss: 0068
Process insmod (pid: 1520, threadinfo=f7b34000 task=c1f36a50)
Stack: <0>f887e13a f9992e5c f9992c18 00000246 22222222 fffffff4 00000000 f784e000
       f784e4ac f784e4d0 f9995380 c0135636 f99953c8 c05be852 f999538c 00000008
       00000000 f999538c 0000001c 00000018 0000001c 00000018 f998bba8 f998ba90
Call Trace:
 [<f887e13a>] acpiphp_init+0x13a/0x290 [acpiphp]
 [<c0135636>] sys_init_module+0x146/0x1630
 [<c010f1b0>] acpi_register_ioapic+0x0/0x10
 [<c013fd87>] generic_file_aio_read+0x47/0x70
 [<c0168b93>] open_namei+0x83/0x560
 [<c012e930>] autoremove_wake_function+0x0/0x50
 [<c014d544>] do_brk+0x204/0x210
 [<c016f4fa>] dput_recursive+0x2a/0x1d0
 [<c015a891>] __fput+0xe1/0x140
 [<c017304b>] mntput_no_expire+0x1b/0x70
 [<c0102d1d>] syscall_call+0x7/0xb
Code: b9 ff ff ff ff b8 06 00 00 00 89 74 24 08 89 5c 24 04 c7 04 24 f0 0a 99 f9 e8 a6 f0 90 c6 8b 54 24 0c eb 9a 8d b4 26 00 00 00 00 <0f> b6 40 44 83 e0 01 c3 90 8d b4 26 00 00 00 00 e8 fb fc ff ff
 Segmentation fault
root@amd:/data/l/linux-mm/drivers/pci/hotplug#


-- 
Thanks, Sharp!

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] acpiphp: treat dck separate from dock bridge
  2006-01-21  1:55   ` [PATCH] acpiphp: treat dck separate from dock bridge Kristen Accardi
  2006-01-21 11:27     ` Pavel Machek
@ 2006-01-21 11:29     ` Pavel Machek
  2006-01-21 12:10     ` Pavel Machek
  2 siblings, 0 replies; 15+ messages in thread
From: Pavel Machek @ 2006-01-21 11:29 UTC (permalink / raw)
  To: Kristen Accardi; +Cc: linux-kernel, greg, pcihpd-discuss, len.brown, linux-acpi

Hi!

> So, this is likely a wrong assumption, so I will be thinking some more
> about how to tell which acpi device is the dock bridge.  But meanwhile,
> this patch could be tested/reviewed to see if it gets us a step further
> in the right direction.
> 
> I patched against 2.6.16-rc1-mm2 because I wasn't sure if it was safe to
> just drop my other patch (since actually trying to just revert the
> broken out patch didn't actually work).  So this patches the file with
> my original patch applied.

Oops, there were preceding warnings before that oops...:

								Pavel

acpiphp: ACPI Hot Plug PCI Controller Driver version: 0.5
acpiphp_glue: get_slot_from_id: no object for id 0
BUG: warning at drivers/pci/hotplug/acpiphp_glue.c:1757/get_slot_from_id()
 [<f9990ec3>] get_slot_from_id+0x83/0x90 [acpiphp]
 [<f887e12c>] acpiphp_init+0x12c/0x290 [acpiphp]
 [<c0135636>] sys_init_module+0x146/0x1630
 [<c010f1b0>] acpi_register_ioapic+0x0/0x10
 [<c013fd87>] generic_file_aio_read+0x47/0x70
 [<c0168b93>] open_namei+0x83/0x560
 [<c012e930>] autoremove_wake_function+0x0/0x50
 [<c014d544>] do_brk+0x204/0x210
 [<c016f4fa>] dput_recursive+0x2a/0x1d0
 [<c015a891>] __fput+0xe1/0x140
 [<c017304b>] mntput_no_expire+0x1b/0x70
 [<c0102d1d>] syscall_call+0x7/0xb
BUG: unable to handle kernel NULL pointer dereference at virtual address 00000044
 printing eip:
f9990d20
*pde = 00000000
Oops: 0000 [#1]
last sysfs file: /devices/system/cpu/cpu0/cpufreq/scaling_governor
Modules linked in: acpiphp
CPU:    0
EIP:    0060:[<f9990d20>]    Not tainted VLI
EFLAGS: 00010286   (2.6.16-rc1-mm2 #2)
EIP is at acpiphp_get_power_status+0x0/0x10 [acpiphp]
eax: 00000000   ebx: f796bbe0   ecx: f9995384   edx: f7edc2c0
esi: f796bca0   edi: 00000014   ebp: f9992c35   esp: f7b35e14
ds: 007b   es: 007b   ss: 0068
Process insmod (pid: 1520, threadinfo=f7b34000 task=c1f36a50)
Stack: <0>f887e13a f9992e5c f9992c18 00000246 22222222 fffffff4 00000000 f784e000
       f784e4ac f784e4d0 f9995380 c0135636 f99953c8 c05be852 f999538c 00000008
       00000000 f999538c 0000001c 00000018 0000001c 00000018 f998bba8 f998ba90
Call Trace:
 [<f887e13a>] acpiphp_init+0x13a/0x290 [acpiphp]
 [<c0135636>] sys_init_module+0x146/0x1630
 [<c010f1b0>] acpi_register_ioapic+0x0/0x10
 [<c013fd87>] generic_file_aio_read+0x47/0x70
 [<c0168b93>] open_namei+0x83/0x560
 [<c012e930>] autoremove_wake_function+0x0/0x50
 [<c014d544>] do_brk+0x204/0x210
 [<c016f4fa>] dput_recursive+0x2a/0x1d0
 [<c015a891>] __fput+0xe1/0x140
 [<c017304b>] mntput_no_expire+0x1b/0x70
 [<c0102d1d>] syscall_call+0x7/0xb

-- 
Thanks, Sharp!

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] acpiphp: treat dck separate from dock bridge
  2006-01-21  1:55   ` [PATCH] acpiphp: treat dck separate from dock bridge Kristen Accardi
  2006-01-21 11:27     ` Pavel Machek
  2006-01-21 11:29     ` Pavel Machek
@ 2006-01-21 12:10     ` Pavel Machek
  2006-01-23 19:20       ` Kristen Accardi
  2 siblings, 1 reply; 15+ messages in thread
From: Pavel Machek @ 2006-01-21 12:10 UTC (permalink / raw)
  To: Kristen Accardi; +Cc: linux-kernel, greg, pcihpd-discuss, len.brown, linux-acpi

Hi!

> actual p2p bridge, and what I try to do here is:
> 1) find the device that _DCK is defined under
> 2) if this is a bridged device, we are done (i.e. is has _ADR and is
> ejectable)
> 3) if not, then walk the namespace looking for _EJD.  If you find one,
> see if the dependency is on the dock device.  The dock bridge should be
> dependent on the dock device.  
> 
> 	- when you find something that is dependent on the dock device,
> 	  see if it has an _ADR and a _PRT entry.  If so, then it is the
> 	  p2p dock bridge.
> 
> So, this is likely a wrong assumption, so I will be thinking some more
> about how to tell which acpi device is the dock bridge.  But meanwhile,
> this patch could be tested/reviewed to see if it gets us a step further
> in the right direction.

Oopsen seems to be called because add_p2p_bridge is never called. I
think it should be called for this device:

0000:00:1e.0 PCI bridge: Intel Corporation 82801 Mobile PCI Bridge (rev 81)

--- clean-mm//drivers/pci/hotplug/acpiphp_glue.c	2006-01-21 11:38:51.000000000 +0100
+++ linux-mm/drivers/pci/hotplug/acpiphp_glue.c	2006-01-21 12:53:28.000000000 +0100
@@ -454,16 +457,22 @@
 		return AE_OK;
 	}
 
+	printk("ADR ok\n");
+
 	device = (tmp >> 16) & 0xffff;
 	function = tmp & 0xffff;
 
 	dev = pci_get_slot(pci_bus, PCI_DEVFN(device, function));
 
+	printk("device, function = %lx\n", tmp);
+
 	if (!dev || !dev->subordinate)
 		goto out;
 
+	printk("detect slots\n");
+
 	/* check if this bridge has ejectable slots */
-	if (detect_ejectable_slots(handle) > 0) {
+	if ((tmp == 0x1e0000) || (detect_ejectable_slots(handle) > 0)) {
 		dbg("found PCI-to-PCI bridge at PCI %s\n", pci_name(dev));
 		add_p2p_bridge(handle, dev);
 	}

...but then bridge->slots is still empty. I tried forcing
is_ejectable(), but then I get:

Jan 21 13:09:52 amd kernel: acpiphp: ACPI Hot Plug PCI Controller
Driver version: 0.5
Jan 21 13:09:53 amd kernel: add_bridge
Jan 21 13:09:53 amd kernel: find_p2p_bridge
Jan 21 13:09:53 amd kernel: ADR ok
Jan 21 13:09:53 amd kernel: device, function = 1f0000
Jan 21 13:09:53 amd kernel: find_p2p_bridge
Jan 21 13:09:53 amd kernel: ADR ok
Jan 21 13:09:53 amd kernel: device, function = 10000
Jan 21 13:09:53 amd kernel: detect slots
Jan 21 13:09:53 amd kernel: find_p2p_bridge
Jan 21 13:09:53 amd kernel: ADR ok
Jan 21 13:09:53 amd kernel: device, function = 1e0000
Jan 21 13:09:53 amd kernel: detect slots
Jan 21 13:09:53 amd kernel: acpiphp_glue: found PCI-to-PCI bridge at
PCI 0000:00:1e.0
Jan 21 13:09:53 amd kernel: add_p2p_bridge
Jan 21 13:09:53 amd kernel: acpiphp_glue: _HPP evaluation failed
Jan 21 13:09:53 amd kernel: find_p2p_bridge
Jan 21 13:09:53 amd kernel: ADR ok
Jan 21 13:09:53 amd kernel: device, function = 1f0001
Jan 21 13:09:53 amd kernel: find_p2p_bridge
Jan 21 13:09:53 amd kernel: ADR ok
Jan 21 13:09:53 amd kernel: device, function = 1d0000
Jan 21 13:09:53 amd kernel: find_p2p_bridge
Jan 21 13:09:53 amd kernel: ADR ok
Jan 21 13:09:53 amd kernel: device, function = 1d0001
Jan 21 13:09:53 amd kernel: find_p2p_bridge
Jan 21 13:09:53 amd kernel: ADR ok
Jan 21 13:09:53 amd kernel: device, function = 1d0002
Jan 21 13:09:53 amd kernel: find_p2p_bridge
Jan 21 13:09:53 amd kernel: ADR ok
Jan 21 13:09:53 amd kernel: device, function = 1d0007
Jan 21 13:09:53 amd kernel: find_p2p_bridge
Jan 21 13:09:53 amd kernel: ADR ok
Jan 21 13:09:53 amd kernel: device, function = 1f0006
Jan 21 13:09:53 amd kernel: acpiphp_glue: Bus 0000:02 has 0 slots
Jan 21 13:09:53 amd kernel: acpiphp_glue: Total 0 slots
Jan 21 13:10:05 amd pam_limits[1345]: wrong limit value 'unlimited'

								Pavel
-- 
Thanks, Sharp!

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] acpiphp: treat dck separate from dock bridge
  2006-01-21 12:10     ` Pavel Machek
@ 2006-01-23 19:20       ` Kristen Accardi
  0 siblings, 0 replies; 15+ messages in thread
From: Kristen Accardi @ 2006-01-23 19:20 UTC (permalink / raw)
  To: Pavel Machek; +Cc: linux-kernel, greg, pcihpd-discuss, len.brown, linux-acpi

On Sat, 2006-01-21 at 13:10 +0100, Pavel Machek wrote:
<snip>
> 
> Oopsen seems to be called because add_p2p_bridge is never called. I
> think it should be called for this device:
> 
> 0000:00:1e.0 PCI bridge: Intel Corporation 82801 Mobile PCI Bridge (rev 81)
> 
> --- clean-mm//drivers/pci/hotplug/acpiphp_glue.c	2006-01-21 11:38:51.000000000 +0100
> +++ linux-mm/drivers/pci/hotplug/acpiphp_glue.c	2006-01-21 12:53:28.000000000 +0100
> @@ -454,16 +457,22 @@
>  		return AE_OK;
>  	}
>  
> +	printk("ADR ok\n");
> +
>  	device = (tmp >> 16) & 0xffff;
>  	function = tmp & 0xffff;
>  
>  	dev = pci_get_slot(pci_bus, PCI_DEVFN(device, function));
>  
> +	printk("device, function = %lx\n", tmp);
> +
>  	if (!dev || !dev->subordinate)
>  		goto out;
>  
> +	printk("detect slots\n");
> +
>  	/* check if this bridge has ejectable slots */
> -	if (detect_ejectable_slots(handle) > 0) {
> +	if ((tmp == 0x1e0000) || (detect_ejectable_slots(handle) > 0)) {
>  		dbg("found PCI-to-PCI bridge at PCI %s\n", pci_name(dev));
>  		add_p2p_bridge(handle, dev);
>  	}
> 
> ...but then bridge->slots is still empty. I tried forcing
> is_ejectable(), but then I get:
> 
> Jan 21 13:09:52 amd kernel: acpiphp: ACPI Hot Plug PCI Controller
> Driver version: 0.5
> Jan 21 13:09:53 amd kernel: add_bridge
> Jan 21 13:09:53 amd kernel: find_p2p_bridge
> Jan 21 13:09:53 amd kernel: ADR ok
> Jan 21 13:09:53 amd kernel: device, function = 1f0000
> Jan 21 13:09:53 amd kernel: find_p2p_bridge
> Jan 21 13:09:53 amd kernel: ADR ok
> Jan 21 13:09:53 amd kernel: device, function = 10000
> Jan 21 13:09:53 amd kernel: detect slots
> Jan 21 13:09:53 amd kernel: find_p2p_bridge
> Jan 21 13:09:53 amd kernel: ADR ok
> Jan 21 13:09:53 amd kernel: device, function = 1e0000
> Jan 21 13:09:53 amd kernel: detect slots
> Jan 21 13:09:53 amd kernel: acpiphp_glue: found PCI-to-PCI bridge at
> PCI 0000:00:1e.0
> Jan 21 13:09:53 amd kernel: add_p2p_bridge
> Jan 21 13:09:53 amd kernel: acpiphp_glue: _HPP evaluation failed
> Jan 21 13:09:53 amd kernel: find_p2p_bridge
> Jan 21 13:09:53 amd kernel: ADR ok
> Jan 21 13:09:53 amd kernel: device, function = 1f0001
> Jan 21 13:09:53 amd kernel: find_p2p_bridge
> Jan 21 13:09:53 amd kernel: ADR ok
> Jan 21 13:09:53 amd kernel: device, function = 1d0000
> Jan 21 13:09:53 amd kernel: find_p2p_bridge
> Jan 21 13:09:53 amd kernel: ADR ok
> Jan 21 13:09:53 amd kernel: device, function = 1d0001
> Jan 21 13:09:53 amd kernel: find_p2p_bridge
> Jan 21 13:09:53 amd kernel: ADR ok
> Jan 21 13:09:53 amd kernel: device, function = 1d0002
> Jan 21 13:09:53 amd kernel: find_p2p_bridge
> Jan 21 13:09:53 amd kernel: ADR ok
> Jan 21 13:09:53 amd kernel: device, function = 1d0007
> Jan 21 13:09:53 amd kernel: find_p2p_bridge
> Jan 21 13:09:53 amd kernel: ADR ok
> Jan 21 13:09:53 amd kernel: device, function = 1f0006
> Jan 21 13:09:53 amd kernel: acpiphp_glue: Bus 0000:02 has 0 slots
> Jan 21 13:09:53 amd kernel: acpiphp_glue: Total 0 slots
> Jan 21 13:10:05 amd pam_limits[1345]: wrong limit value 'unlimited'
> 
> 								Pavel


Well, let's back up a bit.  I see a few things wrong.  Starting with
just the basics, it doesn't seem like we have even found your _DCK
method, otherwise find_num_slots should have reported 1.  If we had been
able to find the dock bridge, then adding the p2p bridge would have
worked as well.  Let's debug this problem first - Can you please try
this patch, making sure to load the module with debugging enabled:

modprobe acpiphp debug=1

and then send me the output of dmesg -s 10000?  Thanks for helping me
debug, I appreciate it.  This patch just adds some extra debugging info
to the code path that I would expect to be taken while trying to find
your _DCK method.  Other than that, it is unchanged.

Kristen

 drivers/pci/hotplug/acpiphp.h      |   11 +
 drivers/pci/hotplug/acpiphp_glue.c |  366 +++++++++++++++++++++++++++----------
 2 files changed, 284 insertions(+), 93 deletions(-)

--- linux-2.6.16-rc1.orig/drivers/pci/hotplug/acpiphp.h
+++ linux-2.6.16-rc1/drivers/pci/hotplug/acpiphp.h
@@ -148,6 +148,17 @@ struct acpiphp_func {
 	u32		flags;		/* see below */
 };
 
+
+struct acpiphp_dock_bridge {
+	acpi_handle dock_bridge_handle;
+	acpi_handle dck_handle;
+	struct acpiphp_func *func;
+	struct acpiphp_slot *slot;
+	u32 last_dock_time;
+	u32 flags;
+};
+
+
 /**
  * struct acpiphp_attention_info - device specific attention registration
  *
--- linux-2.6.16-rc1.orig/drivers/pci/hotplug/acpiphp_glue.c
+++ linux-2.6.16-rc1/drivers/pci/hotplug/acpiphp_glue.c
@@ -53,15 +53,14 @@
 #include "acpiphp.h"
 
 static LIST_HEAD(bridge_list);
+static struct acpiphp_dock_bridge dock_bridge;
 
 #define MY_NAME "acpiphp_glue"
-static struct work_struct dock_task;
 static int enable_device(struct acpiphp_slot *slot);
 static void handle_hotplug_event_bridge (acpi_handle, u32, void *);
 static void handle_hotplug_event_func (acpi_handle, u32, void *);
 static void acpiphp_sanitize_bus(struct pci_bus *bus);
 static void acpiphp_set_hpp_values(acpi_handle handle, struct pci_bus *bus);
-static void dock(void *data);
 static unsigned int get_slot_status(struct acpiphp_slot *slot);
 
 /*
@@ -104,13 +103,26 @@ static int is_ejectable(acpi_handle hand
 }
 
 
+
+static int is_dock(acpi_handle handle)
+{
+	acpi_status status;
+	acpi_handle tmp;
+
+	status = acpi_get_handle(handle, "_DCK", &tmp);
+	if (ACPI_FAILURE(status)) {
+		return 0;
+	}
+	return 1;
+}
+
 /* callback routine to check the existence of ejectable slots */
 static acpi_status
 is_ejectable_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
 {
 	int *count = (int *)context;
 
-	if (is_ejectable(handle)) {
+	if (is_ejectable(handle) || (handle == dock_bridge.dock_bridge_handle)) {
 		(*count)++;
 		/* only one ejectable slot is enough */
 		return AE_CTRL_TERMINATE;
@@ -120,7 +132,9 @@ is_ejectable_slot(acpi_handle handle, u3
 }
 
 
-static acpi_status handle_dock(struct acpiphp_func *func, int dock)
+
+
+static acpi_status handle_dock(int dock)
 {
 	acpi_status status;
 	struct acpi_object_list arg_list;
@@ -134,10 +148,13 @@ static acpi_status handle_dock(struct ac
 	arg_list.pointer = &arg;
 	arg.type = ACPI_TYPE_INTEGER;
 	arg.integer.value = dock;
-	status = acpi_evaluate_object(func->handle, "_DCK",
+	status = acpi_evaluate_object(dock_bridge.dck_handle, "_DCK",
 					&arg_list, &buffer);
 	if (ACPI_FAILURE(status))
 		err("%s: failed to dock!!\n", MY_NAME);
+	acpi_os_free(buffer.pointer);
+
+	dbg("%s: exit\n", __FUNCTION__);
 
 	return status;
 }
@@ -157,6 +174,7 @@ register_slot(acpi_handle handle, u32 lv
 	int device, function;
 	static int num_slots = 0;	/* XXX if we support I/O node hotplug... */
 
+
 	status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr);
 
 	if (ACPI_FAILURE(status))
@@ -164,9 +182,10 @@ register_slot(acpi_handle handle, u32 lv
 
 	status = acpi_get_handle(handle, "_EJ0", &tmp);
 
-	if (ACPI_FAILURE(status))
+	if ((handle != dock_bridge.dock_bridge_handle) && ACPI_FAILURE(status))
 		return AE_OK;
 
+
 	device = (adr >> 16) & 0xffff;
 	function = adr & 0xffff;
 
@@ -178,7 +197,8 @@ register_slot(acpi_handle handle, u32 lv
 	INIT_LIST_HEAD(&newfunc->sibling);
 	newfunc->handle = handle;
 	newfunc->function = function;
-	newfunc->flags = FUNC_HAS_EJ0;
+	if (ACPI_SUCCESS(status))
+		newfunc->flags = FUNC_HAS_EJ0;
 
 	if (ACPI_SUCCESS(acpi_get_handle(handle, "_STA", &tmp)))
 		newfunc->flags |= FUNC_HAS_STA;
@@ -189,6 +209,9 @@ register_slot(acpi_handle handle, u32 lv
 	if (ACPI_SUCCESS(acpi_get_handle(handle, "_PS3", &tmp)))
 		newfunc->flags |= FUNC_HAS_PS3;
 
+	if (ACPI_SUCCESS(acpi_get_handle(handle, "_DCK", &tmp)))
+		newfunc->flags |= FUNC_HAS_DCK;
+
 	status = acpi_evaluate_integer(handle, "_SUN", NULL, &sun);
 	if (ACPI_FAILURE(status))
 		sun = -1;
@@ -236,24 +259,22 @@ register_slot(acpi_handle handle, u32 lv
 		slot->flags |= (SLOT_ENABLED | SLOT_POWEREDON);
 	}
 
-	/* install dock notify handler */
-	if (ACPI_SUCCESS(acpi_get_handle(handle, "_DCK", &tmp))) {
-		newfunc->flags |= FUNC_HAS_DCK;
-		INIT_WORK(&dock_task, dock, slot);
-	}
 
 	/* install notify handler */
-	status = acpi_install_notify_handler(handle,
+	if (!(newfunc->flags & FUNC_HAS_DCK)) {
+		status = acpi_install_notify_handler(handle,
 					     ACPI_SYSTEM_NOTIFY,
 					     handle_hotplug_event_func,
 					     newfunc);
-
-	if (ACPI_FAILURE(status)) {
-		err("failed to register interrupt notify handler\n");
-		return status;
+		if (ACPI_FAILURE(status))
+			err("failed to register interrupt notify handler\n");
+	} else if (handle == dock_bridge.dock_bridge_handle) {
+		dock_bridge.func = newfunc;
+		dock_bridge.slot = slot;
+		status = AE_OK;
 	}
 
-	return AE_OK;
+	return status;
 }
 
 
@@ -544,11 +565,13 @@ static void cleanup_bridge(struct acpiph
 		list_for_each_safe (list, tmp, &slot->funcs) {
 			struct acpiphp_func *func;
 			func = list_entry(list, struct acpiphp_func, sibling);
-			status = acpi_remove_notify_handler(func->handle,
+			if (!(func->flags & FUNC_HAS_DCK)) {
+				status = acpi_remove_notify_handler(func->handle,
 						ACPI_SYSTEM_NOTIFY,
 						handle_hotplug_event_func);
-			if (ACPI_FAILURE(status))
-				err("failed to remove notify handler\n");
+				if (ACPI_FAILURE(status))
+					err("failed to remove notify handler\n");
+			}
 			pci_dev_put(func->pci_dev);
 			list_del(list);
 			kfree(func);
@@ -713,33 +736,6 @@ static int acpiphp_configure_ioapics(acp
 	return 0;
 }
 
-/*
- * the _DCK method can do funny things... and sometimes not
- * hah-hah funny.
- */
-static void post_dock_fixups(struct acpiphp_slot *slot,
-			     struct acpiphp_func *func)
-{
-	struct pci_bus *bus = slot->bridge->pci_bus;
-	u32 buses;
-
-	/* fixup bad _DCK function that rewrites
-	 * secondary bridge on slot
-	 */
-	pci_read_config_dword(bus->self,
-				PCI_PRIMARY_BUS,
-				&buses);
-
-	if (((buses >> 8) & 0xff) != bus->secondary) {
-		buses = (buses & 0xff000000)
-	     		| ((unsigned int)(bus->primary)     <<  0)
-	     		| ((unsigned int)(bus->secondary)   <<  8)
-	     		| ((unsigned int)(bus->subordinate) << 16);
-		pci_write_config_dword(bus->self,
-					PCI_PRIMARY_BUS,
-					buses);
-	}
-}
 
 
 static int acpiphp_bus_add(struct acpiphp_func *func)
@@ -771,26 +767,32 @@ static int acpiphp_bus_add(struct acpiph
 
 
 
-static void dock(void *data)
+
+/*
+ * the _DCK method can do funny things... and sometimes not
+ * hah-hah funny.
+ */
+static void post_dock_fixups(void)
 {
-	struct list_head *l;
-	struct acpiphp_func *func;
-	struct acpiphp_slot *slot = data;
+	struct pci_bus *bus = dock_bridge.slot->bridge->pci_bus;
+	u32 buses;
 
-	mutex_lock(&slot->crit_sect);
-	list_for_each(l, &slot->funcs) {
-		func = list_entry(l, struct acpiphp_func, sibling);
-		if (func->flags & FUNC_HAS_DCK) {
-			handle_dock(func, 1);
-			post_dock_fixups(slot, func);
-			slot->flags |= SLOT_POWEREDON;
-			if (get_slot_status(slot) == ACPI_STA_ALL) {
-				enable_device(slot);
-			}
-		}
+	/* fixup bad _DCK function that rewrites
+	 * secondary bridge on slot
+	 */
+	pci_read_config_dword(bus->self,
+				PCI_PRIMARY_BUS,
+				&buses);
+
+	if (((buses >> 8) & 0xff) != bus->secondary) {
+		buses = (buses & 0xff000000)
+	     		| ((unsigned int)(bus->primary)     <<  0)
+	     		| ((unsigned int)(bus->secondary)   <<  8)
+	     		| ((unsigned int)(bus->subordinate) << 16);
+		pci_write_config_dword(bus->self,
+					PCI_PRIMARY_BUS,
+					buses);
 	}
-	slot->flags &= (~SLOT_DOCKING);
-	mutex_unlock(&slot->crit_sect);
 }
 
 
@@ -819,19 +821,6 @@ static int power_on_slot(struct acpiphp_
 			} else
 				break;
 		}
-
-		if (func->flags & FUNC_HAS_DCK) {
-			dbg("%s: executing _DCK\n", __FUNCTION__);
-			slot->flags |= SLOT_DOCKING;
-			/*
-			 * FIXME - work around for acpi.  Right
-			 * now if we call _DCK from this thread,
-			 * we block forever.
-			 */
-			schedule_work(&dock_task);
-			retval = -1;
-			goto err_exit;
-		}
 	}
 
 	/* TBD: evaluate _STA to check if the slot is enabled */
@@ -857,11 +846,6 @@ static int power_off_slot(struct acpiphp
 
 	list_for_each (l, &slot->funcs) {
 		func = list_entry(l, struct acpiphp_func, sibling);
-		if (func->flags & FUNC_HAS_DCK) {
-			dbg("%s: undock commencing\n", __FUNCTION__);
-			handle_dock(func, 0);
-			dbg("%s: undock complete\n", __FUNCTION__);
-		}
 		if (func->flags & FUNC_HAS_PS3) {
 			status = acpi_evaluate_object(func->handle, "_PS3", NULL, NULL);
 			if (ACPI_FAILURE(status)) {
@@ -871,6 +855,7 @@ static int power_off_slot(struct acpiphp
 			} else
 				break;
 		}
+
 	}
 
 	/* TBD: evaluate _STA to check if the slot is disabled */
@@ -882,6 +867,8 @@ static int power_off_slot(struct acpiphp
 }
 
 
+
+
 /**
  * get_func - given pci_dev & slot, get the matching acpiphp_func
  * @slot: slot to be scanned.
@@ -889,22 +876,27 @@ static int power_off_slot(struct acpiphp
  *
  * This function will check the list of acpiphp functions for
  * this slot and return the one that represents the given
- * pci_dev structure.
+ * pci_dev structure.  This function will incremente the ref
+ * pointer of the pci_dev struct as a side effect, so the caller
+ * must call pci_dev_put when they are done.
  */
 static struct acpiphp_func * get_func(struct acpiphp_slot *slot,
 					struct pci_dev *dev)
 {
-	struct list_head *l;
-	struct acpiphp_func *func;
+	struct acpiphp_func *func = NULL;
 	struct pci_bus *bus = slot->bridge->pci_bus;
+	struct pci_dev *pdev;
 
-	list_for_each (l, &slot->funcs) {
-		func = list_entry(l, struct acpiphp_func, sibling);
-		if (pci_get_slot(bus, PCI_DEVFN(slot->device,
-					func->function)) == dev)
-			return func;
+	list_for_each_entry (func, &slot->funcs, sibling) {
+		pdev = pci_get_slot(bus, PCI_DEVFN(slot->device,
+					func->function));
+		if (pdev) {
+			if (pdev == dev)
+				break;
+			pci_dev_put(pdev);
+		}
 	}
-	return NULL;
+	return func;
 }
 
 
@@ -987,8 +979,11 @@ static int enable_device(struct acpiphp_
 				if (pass && dev->subordinate) {
 					pci_bus_size_bridges(dev->subordinate);
 					func = get_func(slot, dev);
-					if (func)
+					if (func) {
 						acpiphp_bus_add(func);
+						/* side effect of get_func() */
+						pci_dev_put(dev);
+					}
 				}
 			}
 		}
@@ -1306,6 +1301,7 @@ static void handle_bridge_insertion(acpi
  * ACPI event handlers
  */
 
+
 /**
  * handle_hotplug_event_bridge - handle ACPI event on bridges
  *
@@ -1439,6 +1435,47 @@ static void handle_hotplug_event_func(ac
 	}
 }
 
+
+
+static void
+handle_hotplug_event_dock(acpi_handle handle, u32 type, void *context)
+{
+	acpi_handle dock = dock_bridge.dock_bridge_handle;
+
+	switch (type) {
+		case ACPI_NOTIFY_BUS_CHECK:
+			dbg("%s: Bus check notify\n",
+				__FUNCTION__);
+			dock_bridge.flags |= SLOT_DOCKING;
+			handle_dock(1);
+			if (dock) {
+				post_dock_fixups();
+				handle_hotplug_event_func(dock,
+					type, dock_bridge.func);
+			}
+			dock_bridge.flags &= ~(SLOT_DOCKING);
+			dock_bridge.last_dock_time = jiffies;
+			break;
+		case ACPI_NOTIFY_EJECT_REQUEST:
+			if (dock_bridge.flags & SLOT_DOCKING ||
+				dock_bridge.last_dock_time == jiffies) {
+				dbg("%s: Ignore bogus eject request\n",
+					__FUNCTION__);
+			} else {
+				dbg("%s: Eject notify\n", __FUNCTION__);
+				handle_dock(0);
+				handle_hotplug_event_func(dock,
+					type, dock_bridge.func);
+			}
+			break;
+		default:
+			warn("%s: unknown event type 0x%x\n",
+				__FUNCTION__, type);
+	}
+}
+
+
+
 static int is_root_bridge(acpi_handle handle)
 {
 	acpi_status status;
@@ -1468,6 +1505,130 @@ static int is_root_bridge(acpi_handle ha
 	return 0;
 }
 
+
+static acpi_status
+find_dock_ejd(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+	acpi_status status;
+	acpi_handle tmp;
+	acpi_handle dck_handle = (acpi_handle) context;
+	char objname[64];
+	char ejd_objname[64];
+	struct acpi_buffer buffer = { .length = sizeof(objname),
+				      .pointer = objname };
+	struct acpi_buffer ejd_buffer = { .length = sizeof(ejd_objname),
+					  .pointer = ejd_objname };
+	union acpi_object *ejd_obj;
+	union acpi_object *dck_obj;
+
+	dbg("%s: enter\n", __FUNCTION__);
+
+	status = acpi_get_handle(handle, "_EJD", &tmp);
+	if (ACPI_FAILURE(status)) {
+		dbg("%s: no _EJD methods found\n", __FUNCTION__);
+		return 0;
+	}
+
+	/* make sure we are dependent on the dock device */
+	acpi_get_name(dck_handle, ACPI_FULL_PATHNAME, &buffer);
+	status = acpi_evaluate_object(handle, "_EJD", NULL, &ejd_buffer);
+	if (ACPI_FAILURE(status)) {
+		err("Unable to execute _EJD!\n");
+		return 0;
+	}
+
+	ejd_obj = ejd_buffer.pointer;
+	dck_obj = buffer.pointer;
+	if (!strncmp(ejd_obj->string.pointer, dck_obj->string.pointer,
+		ejd_obj->string.length)) {
+		dbg("%s: found device dependent on dock\n", __FUNCTION__);
+		/* ok, this device is dependent on the dock device,
+		 * if it was the actual dock bridge, it would have
+		 * a PRT associated with it.
+		 */
+		status = acpi_get_handle(handle, "_PRT", &tmp);
+		if (ACPI_FAILURE(status))
+			return 0;
+
+		/* yippee, we found the dock bridge! */
+		dbg("%s: found dock bridge\n", __FUNCTION__);
+		*(rv) = handle;
+		return AE_CTRL_TERMINATE;
+	}
+	return AE_OK;
+}
+
+
+static acpi_handle
+get_dock_handle(acpi_handle handle)
+{
+	acpi_handle dock_bridge_handle = NULL;
+
+	dbg("%s: enter\n", __FUNCTION__);
+
+	/*
+	 * first see if we are the dock bridge.
+	 */
+	if (is_ejectable(handle))
+		return handle;
+
+	dbg("%s: not ejectable\n", __FUNCTION__);
+
+	/*
+	 * otherwise, we are going to have to find
+	 * the dock bridge by checking the _EJD list.
+	 */
+	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+		ACPI_UINT32_MAX, find_dock_ejd, handle, &dock_bridge_handle);
+
+	return dock_bridge_handle;
+}
+
+
+
+static acpi_status
+find_dock(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+	int *count = (int *)context;
+
+	dbg("%s: enter\n", __FUNCTION__);
+
+	if (is_dock(handle)) {
+		/* found a dock.  Now we have to determine if
+		 * the _DCK method is within the scope of the
+		 * dock bridge, or outside it (as in the IBM x-series)
+		 */
+		dbg("%s: found dock\n", __FUNCTION__);
+		dock_bridge.dock_bridge_handle = get_dock_handle(handle);
+		dock_bridge.dck_handle = handle;
+		acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
+			handle_hotplug_event_dock, NULL);
+		(*count)++;
+	}
+
+	return AE_OK;
+}
+
+
+
+
+static int
+find_dock_bridge(void)
+{
+	int num = 0;
+
+	/* start from the root object, because some laptops define
+	 * _DCK methods outside the scope of PCI (IBM x-series laptop)
+	 */
+	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+			ACPI_UINT32_MAX, find_dock, &num, NULL);
+
+	return num;
+}
+
+
+
+
 static acpi_status
 find_root_bridges(acpi_handle handle, u32 lvl, void *context, void **rv)
 {
@@ -1494,6 +1655,8 @@ int __init acpiphp_glue_init(void)
 {
 	int num = 0;
 
+	num = find_dock_bridge();
+
 	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
 			ACPI_UINT32_MAX, find_root_bridges, &num, NULL);
 
@@ -1513,6 +1676,17 @@ int __init acpiphp_glue_init(void)
  */
 void __exit acpiphp_glue_exit(void)
 {
+	acpi_handle handle = dock_bridge.dck_handle;
+
+	/* if we have a dock station handle, we should
+	 * remove the notify handler
+	 */
+	if (handle) {
+		if (ACPI_FAILURE(acpi_remove_notify_handler(handle,
+			ACPI_SYSTEM_NOTIFY, handle_hotplug_event_dock)))
+			err("failed to remove dock notify handler\n");
+	}
+
 	acpi_pci_unregister_driver(&acpi_pci_hp_driver);
 }
 
@@ -1537,6 +1711,12 @@ int __init acpiphp_get_num_slots(void)
 		num_slots += bridge->nr_slots;
 	}
 
+	/* it's possible to have a dock station that doesn't actually
+	 * use a pci dock bridge.  For now, continue to allow this
+	 * to be handled by this driver.
+	 */
+	if (dock_bridge.dck_handle && !dock_bridge.dock_bridge_handle)
+		num_slots++;
 	dbg("Total %d slots\n", num_slots);
 	return num_slots;
 }


^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2006-01-23 19:17 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <20060116200218.275371000@whizzy>
2006-01-18  0:56 ` [patch 1/4] pci: return max reserved busnr Kristen Accardi
2006-01-18  0:56 ` [patch 2/4] acpiphp: handle dock bridges Kristen Accardi
2006-01-18  2:26   ` [Pcihpd-discuss] " Kenji Kaneshige
2006-01-18  5:11     ` Greg KH
2006-01-18  3:55   ` Dmitry Torokhov
2006-01-18  4:43   ` [Pcihpd-discuss] " Kenji Kaneshige
2006-01-20  3:24   ` MUNEDA Takahiro
2006-01-20 17:56     ` Kristen Accardi
2006-01-21  1:55   ` [PATCH] acpiphp: treat dck separate from dock bridge Kristen Accardi
2006-01-21 11:27     ` Pavel Machek
2006-01-21 11:29     ` Pavel Machek
2006-01-21 12:10     ` Pavel Machek
2006-01-23 19:20       ` Kristen Accardi
2006-01-18  0:57 ` [patch 3/4] pci: really fix parent's subordinate busnr Kristen Accardi
2006-01-18  0:57 ` [patch 4/4] pci: quirk for IBM Dock II cardbus controllers Kristen Accardi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).