summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--xhfc/base.c135
1 files changed, 131 insertions, 4 deletions
diff --git a/xhfc/base.c b/xhfc/base.c
index f7ad004..5ac5e77 100644
--- a/xhfc/base.c
+++ b/xhfc/base.c
@@ -33,6 +33,7 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/pnp.h>
+#include <acpi/acpi_bus.h>
#ifdef USE_GPIO
#include <gpio/gpio.h>
@@ -49,9 +50,11 @@ MODULE_LICENSE("GPL");
#ifdef USE_GPIO
-#define GPIO_NONE (-1)
+#define GPIO_FROM_PLATFORM_DESC (-2)
+#define GPIO_NONE (-1)
-static int reset_gpio = 27;
+#define XIOH_PROTO_MB_V4_RESET_GPIO 27 // XXX temp hack
+static int reset_gpio = GPIO_FROM_PLATFORM_DESC;
static int port1_ntte_gpio = GPIO_NONE;
static int port2_ntte_gpio = GPIO_NONE;
@@ -73,7 +76,8 @@ module_param(port2_term_gpio, int, S_IRUGO);
module_param(port3_term_gpio, int, S_IRUGO);
module_param(port4_term_gpio, int, S_IRUGO);
-MODULE_PARM_DESC(reset_gpio, "Reset the XHFC using this GPIO");
+MODULE_PARM_DESC(reset_gpio, "Reset the XHFC using this GPIO"
+ "(override ACPI platform description)");
MODULE_PARM_DESC(port1_ntte_gpio, "GPIO used for setting port 1 as NT/TE "
"(default: -1 = none)");
@@ -1295,6 +1299,105 @@ static struct pci_driver xhfc_driver = {
.shutdown = xhfc_shutdown,
};
+/* Note about possible evolution: to allow retro compatible expansion,
+ * you'll have to introduce a new structure with e.g. the dynamical
+ * platform descriptor length in a header.
+ */
+struct xhfc_platform_desc {
+ u8 reset_gpio; // 255 => no gpio for reset
+} __packed;
+
+/* no const: acpi API are not const clean */
+static struct acpi_vendor_uuid xhfc_platform_desc_uuid = {
+ .subtype = 0x42,
+ .data = { 0x34, 0x89, 0xfa, 0xc1, 0xd1, 0xcc, 0xb7, 0x4b,
+ 0xbf, 0xa0, 0x0d, 0x93, 0x8b, 0xf3, 0x8e, 0xd5 },
+};
+
+static int __devinit
+xhfc_acpi_get_platform_desc(acpi_handle handle, struct xhfc_platform_desc *desc)
+{
+ acpi_status status;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct acpi_resource *resource;
+ struct acpi_resource_vendor_typed *vendor;
+ int payload_length;
+
+ if (!handle) {
+ if (DBG)
+ printk(KERN_DEBUG DRIVER_NAME
+ ": %s: xhfc acpi handle missing\n", __func__);
+ return -ENXIO;
+ }
+
+ status = acpi_get_vendor_resource(handle, METHOD_NAME__CRS,
+ &xhfc_platform_desc_uuid, &buffer);
+ if (ACPI_FAILURE(status)) {
+ if (status == AE_NOT_EXIST) {
+ if (DBG)
+ printk(KERN_DEBUG DRIVER_NAME
+ ": %s: xhfc platform descriptor "
+ "not found\n", __func__);
+ return -ENOENT;
+ }
+ printk(KERN_ERR DRIVER_NAME
+ ": %s: acpi_get_vendor_resource() error %s\n",
+ __func__, acpi_format_exception(status));
+ return -EFAULT;
+ }
+
+ resource = buffer.pointer;
+ vendor = &resource->data.vendor_typed;
+
+ payload_length = vendor->byte_length - sizeof (struct acpi_vendor_uuid);
+
+ /* NOTE: modify that simple test if you modify xhfc_platform_desc in
+ * a retrocompatible fashion. */
+ if (payload_length < sizeof (struct xhfc_platform_desc)) {
+ printk(KERN_ERR DRIVER_NAME
+ ": %s: invalid xhfc platform descriptor detected\n",
+ __func__);
+ kfree(buffer.pointer);
+ return -EBADMSG;
+ }
+
+ memcpy(desc, vendor->byte_data, sizeof (struct xhfc_platform_desc));
+ kfree(buffer.pointer);
+ return 0;
+}
+
+/**
+ * xhfc_pnp_get_platform_desc()
+ * @dev: the pnp device
+ * @desc: out param to return the platform desc
+ *
+ * Preconditions: neither @dev nor @desc shall be NULL.
+ *
+ * Returns >= 0 if the platform_desc could be retrieved.
+ * Returns -ENOENT if no descriptor has been found.
+ * Returns an other error code in cases where the caller should
+ * back off. (The caller can also chose to back off on -ENOENT.)
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
+static int __devinit
+xhfc_pnp_get_platform_desc(struct pnp_dev *dev, struct xhfc_platform_desc *desc)
+{
+ acpi_handle handle = DEVICE_ACPI_HANDLE(&(dev->dev));
+ return xhfc_acpi_get_platform_desc(handle, desc);
+}
+#else
+# error not implemented
+#endif
+
+static int __devinit
+xhfc_pd_get_reset_gpio(const struct xhfc_platform_desc *desc)
+{
+ if (desc->reset_gpio == 255)
+ return -1;
+ else
+ return desc->reset_gpio;
+}
+
static int __devinit
xhfc_pnp_init_one(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
{
@@ -1313,9 +1416,33 @@ xhfc_pnp_init_one(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
}
printk(KERN_DEBUG DRIVER_NAME
- ": xhfc_pnp_init_one: pnp device %s (dev_id=%s)\n",
+ ": xhfc_pnp_init_one: pnp device %s (pnp id=%s)\n",
pnp_dev_name(dev), dev_id->id);
+ if (reset_gpio == GPIO_FROM_PLATFORM_DESC) {
+ struct xhfc_platform_desc xhfc_pd;
+ err = xhfc_pnp_get_platform_desc(dev, &xhfc_pd);
+
+ if (err < 0) {
+ printk(KERN_ERR DRIVER_NAME
+ ": %s: xhfc_pnp_get_platform_desc() error %d\n",
+ __func__, err);
+ return err;
+ } else
+ reset_gpio = xhfc_pd_get_reset_gpio(&xhfc_pd);
+
+ if (reset_gpio < 0 && reset_gpio != GPIO_NONE) {
+ printk(KERN_ERR DRIVER_NAME
+ ": %s: unknown reset gpio\n", __func__);
+ return -EIO;
+ }
+ }
+
+ if (DBG) {
+ printk(KERN_DEBUG DRIVER_NAME ": reset gpio = %d\n",
+ reset_gpio);
+ }
+
irq = pnp_irq(dev, 0);
if (irq == (resource_size_t)-1) {
printk(KERN_ERR DRIVER_NAME