From c90029311ad5a4cb0db76d2b1025586ad3133cb8 Mon Sep 17 00:00:00 2001 From: Guillaume Knispel Date: Mon, 13 Feb 2012 19:53:48 +0100 Subject: now extract xhfc reset gpio from DSDT (vendor specific descriptor) --- xhfc/base.c | 135 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file 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 #include #include +#include #ifdef USE_GPIO #include @@ -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 -- cgit v1.2.3