From a485923efbb83056b7fb79e4fd2fee05c990ad5e Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 23 Feb 2015 15:52:45 +0200 Subject: HID: i2c-hid: Add support for ACPI GPIO interrupts The HID over I2C specification allows to have the interrupt for a HID device to be GPIO instead of directly connected to the IO-APIC. Add support for this so that when the driver does not find proper interrupt number from the I2C client structure we check if it has ACPI GpioInt() resource listed in _CRS. If it is found we convert it to an interrupt number and use it instead. Signed-off-by: Mika Westerberg Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 68 +++++++++++++++++++++++++++++++------------ 1 file changed, 50 insertions(+), 18 deletions(-) (limited to 'drivers/hid/i2c-hid/i2c-hid.c') diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 36053f33d6d9..ab4dd952b6ba 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -37,6 +37,7 @@ #include #include #include +#include #include @@ -144,6 +145,8 @@ struct i2c_hid { unsigned long flags; /* device flags */ wait_queue_head_t wait; /* For waiting the interrupt */ + struct gpio_desc *desc; + int irq; struct i2c_hid_platform_data pdata; }; @@ -785,16 +788,16 @@ static int i2c_hid_init_irq(struct i2c_client *client) struct i2c_hid *ihid = i2c_get_clientdata(client); int ret; - dev_dbg(&client->dev, "Requesting IRQ: %d\n", client->irq); + dev_dbg(&client->dev, "Requesting IRQ: %d\n", ihid->irq); - ret = request_threaded_irq(client->irq, NULL, i2c_hid_irq, + ret = request_threaded_irq(ihid->irq, NULL, i2c_hid_irq, IRQF_TRIGGER_LOW | IRQF_ONESHOT, client->name, ihid); if (ret < 0) { dev_warn(&client->dev, "Could not register for %s interrupt, irq = %d," " ret = %d\n", - client->name, client->irq, ret); + client->name, ihid->irq, ret); return ret; } @@ -841,6 +844,14 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid) } #ifdef CONFIG_ACPI + +/* Default GPIO mapping */ +static const struct acpi_gpio_params i2c_hid_irq_gpio = { 0, 0, true }; +static const struct acpi_gpio_mapping i2c_hid_acpi_gpios[] = { + { "gpios", &i2c_hid_irq_gpio, 1 }, + { }, +}; + static int i2c_hid_acpi_pdata(struct i2c_client *client, struct i2c_hid_platform_data *pdata) { @@ -866,7 +877,7 @@ static int i2c_hid_acpi_pdata(struct i2c_client *client, pdata->hid_descriptor_address = obj->integer.value; ACPI_FREE(obj); - return 0; + return acpi_dev_add_driver_gpios(adev, i2c_hid_acpi_gpios); } static const struct acpi_device_id i2c_hid_acpi_match[] = { @@ -930,12 +941,6 @@ static int i2c_hid_probe(struct i2c_client *client, dbg_hid("HID probe called for i2c 0x%02x\n", client->addr); - if (!client->irq) { - dev_err(&client->dev, - "HID over i2c has not been provided an Int IRQ\n"); - return -EINVAL; - } - ihid = kzalloc(sizeof(struct i2c_hid), GFP_KERNEL); if (!ihid) return -ENOMEM; @@ -955,6 +960,23 @@ static int i2c_hid_probe(struct i2c_client *client, ihid->pdata = *platform_data; } + if (client->irq > 0) { + ihid->irq = client->irq; + } else if (ACPI_COMPANION(&client->dev)) { + ihid->desc = gpiod_get(&client->dev, NULL, GPIOD_IN); + if (IS_ERR(ihid->desc)) { + dev_err(&client->dev, "Failed to get GPIO interrupt\n"); + return PTR_ERR(ihid->desc); + } + + ihid->irq = gpiod_to_irq(ihid->desc); + if (ihid->irq < 0) { + gpiod_put(ihid->desc); + dev_err(&client->dev, "Failed to convert GPIO to IRQ\n"); + return ihid->irq; + } + } + i2c_set_clientdata(client, ihid); ihid->client = client; @@ -1017,13 +1039,16 @@ err_mem_free: hid_destroy_device(hid); err_irq: - free_irq(client->irq, ihid); + free_irq(ihid->irq, ihid); err_pm: pm_runtime_put_noidle(&client->dev); pm_runtime_disable(&client->dev); err: + if (ihid->desc) + gpiod_put(ihid->desc); + i2c_hid_free_buffers(ihid); kfree(ihid); return ret; @@ -1042,13 +1067,18 @@ static int i2c_hid_remove(struct i2c_client *client) hid = ihid->hid; hid_destroy_device(hid); - free_irq(client->irq, ihid); + free_irq(ihid->irq, ihid); if (ihid->bufsize) i2c_hid_free_buffers(ihid); + if (ihid->desc) + gpiod_put(ihid->desc); + kfree(ihid); + acpi_dev_remove_driver_gpios(ACPI_COMPANION(&client->dev)); + return 0; } @@ -1060,9 +1090,9 @@ static int i2c_hid_suspend(struct device *dev) struct hid_device *hid = ihid->hid; int ret = 0; - disable_irq(client->irq); + disable_irq(ihid->irq); if (device_may_wakeup(&client->dev)) - enable_irq_wake(client->irq); + enable_irq_wake(ihid->irq); if (hid->driver && hid->driver->suspend) ret = hid->driver->suspend(hid, PMSG_SUSPEND); @@ -1080,13 +1110,13 @@ static int i2c_hid_resume(struct device *dev) struct i2c_hid *ihid = i2c_get_clientdata(client); struct hid_device *hid = ihid->hid; - enable_irq(client->irq); + enable_irq(ihid->irq); ret = i2c_hid_hwreset(client); if (ret) return ret; if (device_may_wakeup(&client->dev)) - disable_irq_wake(client->irq); + disable_irq_wake(ihid->irq); if (hid->driver && hid->driver->reset_resume) { ret = hid->driver->reset_resume(hid); @@ -1101,17 +1131,19 @@ static int i2c_hid_resume(struct device *dev) static int i2c_hid_runtime_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); + struct i2c_hid *ihid = i2c_get_clientdata(client); i2c_hid_set_power(client, I2C_HID_PWR_SLEEP); - disable_irq(client->irq); + disable_irq(ihid->irq); return 0; } static int i2c_hid_runtime_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); + struct i2c_hid *ihid = i2c_get_clientdata(client); - enable_irq(client->irq); + enable_irq(ihid->irq); i2c_hid_set_power(client, I2C_HID_PWR_ON); return 0; } -- cgit v1.2.3 From a7d2bf25a4837e6514a2747380fd4539b63ee20c Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 14 Apr 2015 17:35:12 +0300 Subject: HID: i2c-hid: Do not fail probing if gpiolib is not enabled Using GPIOs and gpiolib is optional. If the kernel is compiled without GPIO support the driver should not fail if it finds the interrupt using normal methods. However, commit a485923efbb8 ("HID: i2c-hid: Add support for ACPI GPIO interrupts") did not take into account that acpi_dev_add_driver_gpios() returns -ENXIO when !CONFIG_GPIOLIB. Fix this by checking the return value against -ENXIO and 0 and only in that case fail the probe. Reported-by: Gabriele Mazzotta Signed-off-by: Mika Westerberg Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/hid/i2c-hid/i2c-hid.c') diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index ab4dd952b6ba..92d6cdf02460 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -862,6 +862,7 @@ static int i2c_hid_acpi_pdata(struct i2c_client *client, union acpi_object *obj; struct acpi_device *adev; acpi_handle handle; + int ret; handle = ACPI_HANDLE(&client->dev); if (!handle || acpi_bus_get_device(handle, &adev)) @@ -877,7 +878,9 @@ static int i2c_hid_acpi_pdata(struct i2c_client *client, pdata->hid_descriptor_address = obj->integer.value; ACPI_FREE(obj); - return acpi_dev_add_driver_gpios(adev, i2c_hid_acpi_gpios); + /* GPIOs are optional */ + ret = acpi_dev_add_driver_gpios(adev, i2c_hid_acpi_gpios); + return ret < 0 && ret != -ENXIO ? ret : 0; } static const struct acpi_device_id i2c_hid_acpi_match[] = { -- cgit v1.2.3 From c8fd51dc12c6e22bb97d5e273babcf339770aba5 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 14 May 2015 11:33:30 +0300 Subject: HID: i2c-hid: fix harmless test_bit() issue These defines are used like this: if (test_bit(I2C_HID_STARTED, &ihid->flags)) The intent was to use bits 0, 1, and 2 but because of the extra shifts we're using bits 1, 2, and 4. It's harmless becuase it's done consistently but it's not the intent and static checkers will complain. Fixes: 4a200c3b9a40 ('HID: i2c-hid: introduce HID over i2c specification implementation') Signed-off-by: Dan Carpenter Reviewed-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/hid/i2c-hid/i2c-hid.c') diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index ab4dd952b6ba..ea5ce752cb47 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -42,9 +42,9 @@ #include /* flags */ -#define I2C_HID_STARTED (1 << 0) -#define I2C_HID_RESET_PENDING (1 << 1) -#define I2C_HID_READ_PENDING (1 << 2) +#define I2C_HID_STARTED 0 +#define I2C_HID_RESET_PENDING 1 +#define I2C_HID_READ_PENDING 2 #define I2C_HID_PWR_ON 0x00 #define I2C_HID_PWR_SLEEP 0x01 -- cgit v1.2.3 From 3e48138c2c66d8ff7d96c87a0535cd50078c742f Mon Sep 17 00:00:00 2001 From: Andrew Duggan Date: Tue, 2 Jun 2015 14:46:20 -0700 Subject: HID: i2c-hid: Do not set the ACPI companion field in the HID device The HID device does not need to know about the ACPI device associated with the underlying i2c device. Setting the ACPI companion field in the HID device also has the side effect of causing HID to be set as wake capable, since acpi_bind_one uses's the companion ACPI device's wakeup flags to set the device as wake capable. Which results in power/wakeup files in sysfs for the HID device which do not do anything. Signed-off-by: Andrew Duggan Reviewed-by: Benson Leung Tested-by: Benson Leung Acked-by: Mika Westerberg Signed-off-by: Jiri Kosina --- drivers/hid/i2c-hid/i2c-hid.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/hid/i2c-hid/i2c-hid.c') diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index 92d6cdf02460..0dfb5d170af2 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -1019,7 +1019,6 @@ static int i2c_hid_probe(struct i2c_client *client, hid->driver_data = client; hid->ll_driver = &i2c_hid_ll_driver; hid->dev.parent = &client->dev; - ACPI_COMPANION_SET(&hid->dev, ACPI_COMPANION(&client->dev)); hid->bus = BUS_I2C; hid->version = le16_to_cpu(ihid->hdesc.bcdVersion); hid->vendor = le16_to_cpu(ihid->hdesc.wVendorID); -- cgit v1.2.3