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=199207 Signed-off-by: Hans de Goede <hdegoede@redhat.com> Reviewed-by: Bastien Nocera <hadess@hadess.net> Link: https://lore.kernel.org/r/20200307121505.3707-4-hdegoede@redhat.com Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
This commit is contained in:
parent
1921dacef7
commit
a7d4b17166
|
@ -34,6 +34,7 @@ struct goodix_ts_data;
|
|||
enum goodix_irq_pin_access_method {
|
||||
IRQ_PIN_ACCESS_NONE,
|
||||
IRQ_PIN_ACCESS_GPIO,
|
||||
IRQ_PIN_ACCESS_ACPI_GPIO,
|
||||
};
|
||||
|
||||
struct goodix_chip_data {
|
||||
|
@ -53,6 +54,8 @@ struct goodix_ts_data {
|
|||
struct regulator *vddio;
|
||||
struct gpio_desc *gpiod_int;
|
||||
struct gpio_desc *gpiod_rst;
|
||||
int gpio_count;
|
||||
int gpio_int_idx;
|
||||
u16 id;
|
||||
u16 version;
|
||||
const char *cfg_name;
|
||||
|
@ -537,6 +540,12 @@ static int goodix_irq_direction_output(struct goodix_ts_data *ts,
|
|||
return -EINVAL;
|
||||
case IRQ_PIN_ACCESS_GPIO:
|
||||
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 */
|
||||
|
@ -551,6 +560,7 @@ static int goodix_irq_direction_input(struct goodix_ts_data *ts)
|
|||
__func__);
|
||||
return -EINVAL;
|
||||
case IRQ_PIN_ACCESS_GPIO:
|
||||
case IRQ_PIN_ACCESS_ACPI_GPIO:
|
||||
return gpiod_direction_input(ts->gpiod_int);
|
||||
}
|
||||
|
||||
|
@ -615,6 +625,94 @@ static int goodix_reset(struct goodix_ts_data *ts)
|
|||
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
|
||||
*
|
||||
|
@ -625,6 +723,7 @@ static int goodix_get_gpio_config(struct goodix_ts_data *ts)
|
|||
int error;
|
||||
struct device *dev;
|
||||
struct gpio_desc *gpiod;
|
||||
bool added_acpi_mappings = false;
|
||||
|
||||
if (!ts->client)
|
||||
return -EINVAL;
|
||||
|
@ -648,6 +747,7 @@ static int goodix_get_gpio_config(struct goodix_ts_data *ts)
|
|||
return error;
|
||||
}
|
||||
|
||||
retry_get_irq_gpio:
|
||||
/* Get the interrupt GPIO pin number */
|
||||
gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_INT_NAME, GPIOD_IN);
|
||||
if (IS_ERR(gpiod)) {
|
||||
|
@ -657,6 +757,11 @@ static int goodix_get_gpio_config(struct goodix_ts_data *ts)
|
|||
GOODIX_GPIO_INT_NAME, 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;
|
||||
|
||||
|
@ -672,10 +777,25 @@ static int goodix_get_gpio_config(struct goodix_ts_data *ts)
|
|||
|
||||
ts->gpiod_rst = gpiod;
|
||||
|
||||
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;
|
||||
switch (ts->irq_pin_access_method) {
|
||||
case IRQ_PIN_ACCESS_ACPI_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;
|
||||
|
|
Loading…
Reference in New Issue