Commit a7d4b171 authored by Hans de Goede's avatar Hans de Goede Committed by Dmitry Torokhov

Input: goodix - add support for getting IRQ + reset GPIOs on Cherry Trail devices

On most Cherry Trail (x86, UEFI + ACPI) devices the ACPI tables do not have
a _DSD with a "daffd814-6eba-4d8c-8a91-bc9bbf4aa301" UUID, adding
"irq-gpios" and "reset-gpios" mappings, so we cannot get the GPIOS by name
without first manually adding mappings ourselves.

These devices contain 1 GpioInt and 1 GpioIo resource in their _CRS table:

Method (_CRS, 0, NotSerialized)  // _CRS: Current Resource Settings
{
    Name (RBUF, ResourceTemplate ()
    {
        I2cSerialBusV2 (0x0014, ControllerInitiated, 0x00061A80,
            AddressingMode7Bit, "\\_SB.PCI0.I2C2",
            0x00, ResourceConsumer, , Exclusive,
            )
        GpioInt (Edge, ActiveLow, Shared, PullDefault, 0x0000,
            "\\_SB.GPO1", 0x00, ResourceConsumer, ,
            )
            {   // Pin list
                0x0013
            }
        GpioIo (Shared, PullDefault, 0x0000, 0x0000,
            IoRestrictionOutputOnly,
            "\\_SB.GPO1", 0x00, ResourceConsumer, ,
            )
            {   // Pin list
                0x0019
            }
    })
    Return (RBUF) /* \_SB_.PCI0.I2C2.TCS1._CRS.RBUF */
}

There is no fixed order for these 2. This commit adds code to check that
there is 1 of each as expected and then registers a mapping matching their
order using devm_acpi_dev_add_driver_gpios().

This gives us access to both GPIOs allowing us to properly suspend the
controller during suspend, and making it possible to reset the controller
if necessary.

BugLink: https://bugzilla.redhat.com/show_bug.cgi?id=1786317
BugLink: https://github.com/nexus511/gpd-ubuntu-packages/issues/10
BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=199207Signed-off-by: default avatarHans de Goede <hdegoede@redhat.com>
Reviewed-by: default avatarBastien Nocera <hadess@hadess.net>
Link: https://lore.kernel.org/r/20200307121505.3707-4-hdegoede@redhat.comSigned-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
parent 1921dace
...@@ -34,6 +34,7 @@ struct goodix_ts_data; ...@@ -34,6 +34,7 @@ struct goodix_ts_data;
enum goodix_irq_pin_access_method { enum goodix_irq_pin_access_method {
IRQ_PIN_ACCESS_NONE, IRQ_PIN_ACCESS_NONE,
IRQ_PIN_ACCESS_GPIO, IRQ_PIN_ACCESS_GPIO,
IRQ_PIN_ACCESS_ACPI_GPIO,
}; };
struct goodix_chip_data { struct goodix_chip_data {
...@@ -53,6 +54,8 @@ struct goodix_ts_data { ...@@ -53,6 +54,8 @@ struct goodix_ts_data {
struct regulator *vddio; struct regulator *vddio;
struct gpio_desc *gpiod_int; struct gpio_desc *gpiod_int;
struct gpio_desc *gpiod_rst; struct gpio_desc *gpiod_rst;
int gpio_count;
int gpio_int_idx;
u16 id; u16 id;
u16 version; u16 version;
const char *cfg_name; const char *cfg_name;
...@@ -537,6 +540,12 @@ static int goodix_irq_direction_output(struct goodix_ts_data *ts, ...@@ -537,6 +540,12 @@ static int goodix_irq_direction_output(struct goodix_ts_data *ts,
return -EINVAL; return -EINVAL;
case IRQ_PIN_ACCESS_GPIO: case IRQ_PIN_ACCESS_GPIO:
return gpiod_direction_output(ts->gpiod_int, value); return gpiod_direction_output(ts->gpiod_int, value);
case IRQ_PIN_ACCESS_ACPI_GPIO:
/*
* The IRQ pin triggers on a falling edge, so its gets marked
* as active-low, use output_raw to avoid the value inversion.
*/
return gpiod_direction_output_raw(ts->gpiod_int, value);
} }
return -EINVAL; /* Never reached */ return -EINVAL; /* Never reached */
...@@ -551,6 +560,7 @@ static int goodix_irq_direction_input(struct goodix_ts_data *ts) ...@@ -551,6 +560,7 @@ static int goodix_irq_direction_input(struct goodix_ts_data *ts)
__func__); __func__);
return -EINVAL; return -EINVAL;
case IRQ_PIN_ACCESS_GPIO: case IRQ_PIN_ACCESS_GPIO:
case IRQ_PIN_ACCESS_ACPI_GPIO:
return gpiod_direction_input(ts->gpiod_int); return gpiod_direction_input(ts->gpiod_int);
} }
...@@ -615,6 +625,94 @@ static int goodix_reset(struct goodix_ts_data *ts) ...@@ -615,6 +625,94 @@ static int goodix_reset(struct goodix_ts_data *ts)
return 0; return 0;
} }
#if defined CONFIG_X86 && defined CONFIG_ACPI
static const struct acpi_gpio_params first_gpio = { 0, 0, false };
static const struct acpi_gpio_params second_gpio = { 1, 0, false };
static const struct acpi_gpio_mapping acpi_goodix_int_first_gpios[] = {
{ GOODIX_GPIO_INT_NAME "-gpios", &first_gpio, 1 },
{ GOODIX_GPIO_RST_NAME "-gpios", &second_gpio, 1 },
{ },
};
static const struct acpi_gpio_mapping acpi_goodix_int_last_gpios[] = {
{ GOODIX_GPIO_RST_NAME "-gpios", &first_gpio, 1 },
{ GOODIX_GPIO_INT_NAME "-gpios", &second_gpio, 1 },
{ },
};
static int goodix_resource(struct acpi_resource *ares, void *data)
{
struct goodix_ts_data *ts = data;
struct device *dev = &ts->client->dev;
struct acpi_resource_gpio *gpio;
switch (ares->type) {
case ACPI_RESOURCE_TYPE_GPIO:
gpio = &ares->data.gpio;
if (gpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT) {
if (ts->gpio_int_idx == -1) {
ts->gpio_int_idx = ts->gpio_count;
} else {
dev_err(dev, "More then one GpioInt resource, ignoring ACPI GPIO resources\n");
ts->gpio_int_idx = -2;
}
}
ts->gpio_count++;
break;
default:
break;
}
return 0;
}
/*
* This function gets called in case we fail to get the irq GPIO directly
* because the ACPI tables lack GPIO-name to APCI _CRS index mappings
* (no _DSD UUID daffd814-6eba-4d8c-8a91-bc9bbf4aa301 data).
* In that case we add our own mapping and then goodix_get_gpio_config()
* retries to get the GPIOs based on the added mapping.
*/
static int goodix_add_acpi_gpio_mappings(struct goodix_ts_data *ts)
{
const struct acpi_gpio_mapping *gpio_mapping = NULL;
struct device *dev = &ts->client->dev;
LIST_HEAD(resources);
int ret;
ts->gpio_count = 0;
ts->gpio_int_idx = -1;
ret = acpi_dev_get_resources(ACPI_COMPANION(dev), &resources,
goodix_resource, ts);
if (ret < 0) {
dev_err(dev, "Error getting ACPI resources: %d\n", ret);
return ret;
}
acpi_dev_free_resource_list(&resources);
if (ts->gpio_count == 2 && ts->gpio_int_idx == 0) {
ts->irq_pin_access_method = IRQ_PIN_ACCESS_ACPI_GPIO;
gpio_mapping = acpi_goodix_int_first_gpios;
} else if (ts->gpio_count == 2 && ts->gpio_int_idx == 1) {
ts->irq_pin_access_method = IRQ_PIN_ACCESS_ACPI_GPIO;
gpio_mapping = acpi_goodix_int_last_gpios;
} else {
dev_warn(dev, "Unexpected ACPI resources: gpio_count %d, gpio_int_idx %d\n",
ts->gpio_count, ts->gpio_int_idx);
return -EINVAL;
}
return devm_acpi_dev_add_driver_gpios(dev, gpio_mapping);
}
#else
static int goodix_add_acpi_gpio_mappings(struct goodix_ts_data *ts)
{
return -EINVAL;
}
#endif /* CONFIG_X86 && CONFIG_ACPI */
/** /**
* goodix_get_gpio_config - Get GPIO config from ACPI/DT * goodix_get_gpio_config - Get GPIO config from ACPI/DT
* *
...@@ -625,6 +723,7 @@ static int goodix_get_gpio_config(struct goodix_ts_data *ts) ...@@ -625,6 +723,7 @@ static int goodix_get_gpio_config(struct goodix_ts_data *ts)
int error; int error;
struct device *dev; struct device *dev;
struct gpio_desc *gpiod; struct gpio_desc *gpiod;
bool added_acpi_mappings = false;
if (!ts->client) if (!ts->client)
return -EINVAL; return -EINVAL;
...@@ -648,6 +747,7 @@ static int goodix_get_gpio_config(struct goodix_ts_data *ts) ...@@ -648,6 +747,7 @@ static int goodix_get_gpio_config(struct goodix_ts_data *ts)
return error; return error;
} }
retry_get_irq_gpio:
/* Get the interrupt GPIO pin number */ /* Get the interrupt GPIO pin number */
gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_INT_NAME, GPIOD_IN); gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_INT_NAME, GPIOD_IN);
if (IS_ERR(gpiod)) { if (IS_ERR(gpiod)) {
...@@ -657,6 +757,11 @@ static int goodix_get_gpio_config(struct goodix_ts_data *ts) ...@@ -657,6 +757,11 @@ static int goodix_get_gpio_config(struct goodix_ts_data *ts)
GOODIX_GPIO_INT_NAME, error); GOODIX_GPIO_INT_NAME, error);
return error; return error;
} }
if (!gpiod && has_acpi_companion(dev) && !added_acpi_mappings) {
added_acpi_mappings = true;
if (goodix_add_acpi_gpio_mappings(ts) == 0)
goto retry_get_irq_gpio;
}
ts->gpiod_int = gpiod; ts->gpiod_int = gpiod;
...@@ -672,10 +777,25 @@ static int goodix_get_gpio_config(struct goodix_ts_data *ts) ...@@ -672,10 +777,25 @@ static int goodix_get_gpio_config(struct goodix_ts_data *ts)
ts->gpiod_rst = gpiod; ts->gpiod_rst = gpiod;
if (ts->gpiod_int && ts->gpiod_rst) { switch (ts->irq_pin_access_method) {
ts->reset_controller_at_probe = true; case IRQ_PIN_ACCESS_ACPI_GPIO:
ts->load_cfg_from_disk = true; /*
ts->irq_pin_access_method = IRQ_PIN_ACCESS_GPIO; * We end up here if goodix_add_acpi_gpio_mappings() has
* called devm_acpi_dev_add_driver_gpios() because the ACPI
* tables did not contain name to index mappings.
* Check that we successfully got both GPIOs after we've
* added our own acpi_gpio_mapping and if we did not get both
* GPIOs reset irq_pin_access_method to IRQ_PIN_ACCESS_NONE.
*/
if (!ts->gpiod_int || !ts->gpiod_rst)
ts->irq_pin_access_method = IRQ_PIN_ACCESS_NONE;
break;
default:
if (ts->gpiod_int && ts->gpiod_rst) {
ts->reset_controller_at_probe = true;
ts->load_cfg_from_disk = true;
ts->irq_pin_access_method = IRQ_PIN_ACCESS_GPIO;
}
} }
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment