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 {
|
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 {
|
||||||
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,
|
||||||
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)
|
||||||
__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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
|
|
||||||
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;
|
||||||
|
|
Loading…
Reference in New Issue