[PATCH 1/2] pciehp: add ACPI based slot detection

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

 



There is a problem that some non hot-pluggable PCIe slots are detected
as hot-pluggable by pciehp on some platforms. The immediate cause of
this problem is that hot-plug capable bit in the Slot Capabilities
register is set even for non hot-pluggable slots on those platforms.
It seems a BIOS/hardware problem, but we need workaround about that.

Some of those platforms define hot-pluggable PCIe slots on ACPI
namespace properly, while hot-plug capable bit in the Slot
Capabilities register is set improperly. So using ACPI namespace
information in pciehp to detect PCIe hot-pluggable slots would be a
workaround.

This patch adds 'pciehp_detect_mode' module option. When 'acpi' is
specified, pciehp uses ACPI namespace information to detect PCIe
hot-pluggable slots.

Signed-off-by: Kenji Kaneshige <kaneshige.kenji@xxxxxxxxxxxxxx>

---
 drivers/pci/hotplug/Makefile      |    3 +
 drivers/pci/hotplug/pciehp.h      |   15 ++++-
 drivers/pci/hotplug/pciehp_acpi.c |  114 ++++++++++++++++++++++++++++++++++++++
 drivers/pci/hotplug/pciehp_core.c |    1 
 4 files changed, 132 insertions(+), 1 deletion(-)

Index: 20081212/drivers/pci/hotplug/pciehp.h
===================================================================
--- 20081212.orig/drivers/pci/hotplug/pciehp.h
+++ 20081212/drivers/pci/hotplug/pciehp.h
@@ -220,11 +220,23 @@ struct hpc_ops {
 #include <acpi/actypes.h>
 #include <linux/pci-acpi.h>
 
+extern void __init pciehp_acpi_slot_detection_init(void);
+extern int pciehp_acpi_slot_detection_check(struct pci_dev *dev);
+
+static inline void pciehp_firmware_init(void)
+{
+	pciehp_acpi_slot_detection_init();
+}
+
 static inline int pciehp_get_hp_hw_control_from_firmware(struct pci_dev *dev)
 {
+	int retval;
 	u32 flags = (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL |
 		     OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
-	return acpi_get_hp_hw_control_from_firmware(dev, flags);
+	retval = acpi_get_hp_hw_control_from_firmware(dev, flags);
+	if (retval)
+		return retval;
+	return pciehp_acpi_slot_detection_check(dev);
 }
 
 static inline int pciehp_get_hp_params_from_firmware(struct pci_dev *dev,
@@ -235,6 +247,7 @@ static inline int pciehp_get_hp_params_f
 	return 0;
 }
 #else
+#define pciehp_firmware_init()				do {} while (0)
 #define pciehp_get_hp_hw_control_from_firmware(dev) 	0
 #define pciehp_get_hp_params_from_firmware(dev, hpp)    (-ENODEV)
 #endif 				/* CONFIG_ACPI */
Index: 20081212/drivers/pci/hotplug/pciehp_acpi.c
===================================================================
--- /dev/null
+++ 20081212/drivers/pci/hotplug/pciehp_acpi.c
@@ -0,0 +1,114 @@
+/*
+ * ACPI related functions for PCI Express Hot Plug driver.
+ *
+ * Copyright (C) 2008 Kenji Kaneshige
+ * Copyright (C) 2008 Fujitsu Limited.
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT.  See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/acpi.h>
+#include "pciehp.h"
+
+#define PCIEHP_DETECT_PCIE	(0)
+#define PCIEHP_DETECT_ACPI	(1)
+#define PCIEHP_DETECT_DEFAULT	PCIEHP_DETECT_PCIE
+
+static int slot_detection_mode;
+static char *pciehp_detect_mode;
+module_param(pciehp_detect_mode, charp, 0444);
+MODULE_PARM_DESC(pciehp_detect_mode,
+		 "Slot detection mode: pcie, acpi\n"
+		 "  pcie - Use PCIe based slot detection (default)\n"
+		 "  acpi - Use ACPI for slot detection\n");
+
+static int is_ejectable(acpi_handle handle)
+{
+	acpi_status status;
+	acpi_handle tmp;
+	unsigned long long removable;
+	status = acpi_get_handle(handle, "_ADR", &tmp);
+	if (ACPI_FAILURE(status))
+		return 0;
+	status = acpi_get_handle(handle, "_EJ0", &tmp);
+	if (ACPI_SUCCESS(status))
+		return 1;
+	status = acpi_evaluate_integer(handle, "_RMV", NULL, &removable);
+	if (ACPI_SUCCESS(status) && removable)
+		return 1;
+	return 0;
+}
+
+static acpi_status
+check_hotplug(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+	int *found = (int *)context;
+	if (is_ejectable(handle)) {
+		*found = 1;
+		return AE_CTRL_TERMINATE;
+	}
+	return AE_OK;
+}
+
+static int pciehp_detect_acpi_slot(struct pci_bus *pbus)
+{
+	acpi_handle handle;
+	struct pci_dev *pdev = pbus->self;
+	int found = 0;
+
+	if (!pdev){
+		int seg = pci_domain_nr(pbus), busnr = pbus->number;
+		handle = acpi_get_pci_rootbridge_handle(seg, busnr);
+	} else
+		handle = DEVICE_ACPI_HANDLE(&(pdev->dev));
+
+	if (!handle)
+		return 0;
+
+	acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
+			    check_hotplug, (void *)&found, NULL);
+	return found;
+}
+
+int pciehp_acpi_slot_detection_check(struct pci_dev *dev)
+{
+	if (slot_detection_mode != PCIEHP_DETECT_ACPI)
+		return 0;
+	if (pciehp_detect_acpi_slot(dev->subordinate))
+		return 0;
+	return -ENODEV;
+}
+
+static int __init parse_detect_mode(void)
+{
+	if (!pciehp_detect_mode)
+		return PCIEHP_DETECT_DEFAULT;
+	if (!strcmp(pciehp_detect_mode, "pcie"))
+		return PCIEHP_DETECT_PCIE;
+	if (!strcmp(pciehp_detect_mode, "acpi"))
+		return PCIEHP_DETECT_ACPI;
+	warn("bad specifier '%s' for pciehp_detect_mode. Use default\n",
+	     pciehp_detect_mode);
+	return PCIEHP_DETECT_DEFAULT;
+}
+
+void __init pciehp_acpi_slot_detection_init(void)
+{
+	slot_detection_mode = parse_detect_mode();
+}
Index: 20081212/drivers/pci/hotplug/Makefile
===================================================================
--- 20081212.orig/drivers/pci/hotplug/Makefile
+++ 20081212/drivers/pci/hotplug/Makefile
@@ -55,6 +55,9 @@ pciehp-objs		:=	pciehp_core.o	\
 				pciehp_ctrl.o	\
 				pciehp_pci.o	\
 				pciehp_hpc.o
+ifdef CONFIG_ACPI
+pciehp-objs		+=	pciehp_acpi.o
+endif
 
 shpchp-objs		:=	shpchp_core.o	\
 				shpchp_ctrl.o	\
Index: 20081212/drivers/pci/hotplug/pciehp_core.c
===================================================================
--- 20081212.orig/drivers/pci/hotplug/pciehp_core.c
+++ 20081212/drivers/pci/hotplug/pciehp_core.c
@@ -522,6 +522,7 @@ static int __init pcied_init(void)
 {
 	int retval = 0;
 
+	pciehp_firmware_init();
 	retval = pcie_port_service_register(&hpdriver_portdrv);
  	dbg("pcie_port_service_register = %d\n", retval);
   	info(DRIVER_DESC " version: " DRIVER_VERSION "\n");


--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [DMA Engine]     [Linux Coverity]     [Linux USB]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Greybus]

  Powered by Linux