Skip to content

Commit 79d2c8b

Browse files
dsdlinusw
authored andcommitted
pinctrl/amd: save pin registers over suspend/resume
The touchpad in the Asus laptop models X505BA/BP and X542BA/BP is unresponsive after suspend/resume. The following error appears during resume: i2c_hid i2c-ELAN1300:00: failed to reset device. The problem here is that i2c_hid does not notice the interrupt being generated at this point, because the GPIO is no longer configured for interrupts. Fix this by saving pinctrl-amd pin registers during suspend and restoring them at resume time. Based on code from pinctrl-intel. Cc: stable@vger.kernel.org Signed-off-by: Daniel Drake <drake@endlessm.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
1 parent a9a1a48 commit 79d2c8b

File tree

2 files changed

+76
-0
lines changed

2 files changed

+76
-0
lines changed

drivers/pinctrl/pinctrl-amd.c

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include <linux/pinctrl/pinconf.h>
3737
#include <linux/pinctrl/pinconf-generic.h>
3838

39+
#include "core.h"
3940
#include "pinctrl-utils.h"
4041
#include "pinctrl-amd.h"
4142

@@ -725,6 +726,69 @@ static const struct pinconf_ops amd_pinconf_ops = {
725726
.pin_config_group_set = amd_pinconf_group_set,
726727
};
727728

729+
#ifdef CONFIG_PM_SLEEP
730+
static bool amd_gpio_should_save(struct amd_gpio *gpio_dev, unsigned int pin)
731+
{
732+
const struct pin_desc *pd = pin_desc_get(gpio_dev->pctrl, pin);
733+
734+
if (!pd)
735+
return false;
736+
737+
/*
738+
* Only restore the pin if it is actually in use by the kernel (or
739+
* by userspace).
740+
*/
741+
if (pd->mux_owner || pd->gpio_owner ||
742+
gpiochip_line_is_irq(&gpio_dev->gc, pin))
743+
return true;
744+
745+
return false;
746+
}
747+
748+
int amd_gpio_suspend(struct device *dev)
749+
{
750+
struct platform_device *pdev = to_platform_device(dev);
751+
struct amd_gpio *gpio_dev = platform_get_drvdata(pdev);
752+
struct pinctrl_desc *desc = gpio_dev->pctrl->desc;
753+
int i;
754+
755+
for (i = 0; i < desc->npins; i++) {
756+
int pin = desc->pins[i].number;
757+
758+
if (!amd_gpio_should_save(gpio_dev, pin))
759+
continue;
760+
761+
gpio_dev->saved_regs[i] = readl(gpio_dev->base + pin*4);
762+
}
763+
764+
return 0;
765+
}
766+
767+
int amd_gpio_resume(struct device *dev)
768+
{
769+
struct platform_device *pdev = to_platform_device(dev);
770+
struct amd_gpio *gpio_dev = platform_get_drvdata(pdev);
771+
struct pinctrl_desc *desc = gpio_dev->pctrl->desc;
772+
int i;
773+
774+
for (i = 0; i < desc->npins; i++) {
775+
int pin = desc->pins[i].number;
776+
777+
if (!amd_gpio_should_save(gpio_dev, pin))
778+
continue;
779+
780+
writel(gpio_dev->saved_regs[i], gpio_dev->base + pin*4);
781+
}
782+
783+
return 0;
784+
}
785+
786+
static const struct dev_pm_ops amd_gpio_pm_ops = {
787+
SET_LATE_SYSTEM_SLEEP_PM_OPS(amd_gpio_suspend,
788+
amd_gpio_resume)
789+
};
790+
#endif
791+
728792
static struct pinctrl_desc amd_pinctrl_desc = {
729793
.pins = kerncz_pins,
730794
.npins = ARRAY_SIZE(kerncz_pins),
@@ -764,6 +828,14 @@ static int amd_gpio_probe(struct platform_device *pdev)
764828
return irq_base;
765829
}
766830

831+
#ifdef CONFIG_PM_SLEEP
832+
gpio_dev->saved_regs = devm_kcalloc(&pdev->dev, amd_pinctrl_desc.npins,
833+
sizeof(*gpio_dev->saved_regs),
834+
GFP_KERNEL);
835+
if (!gpio_dev->saved_regs)
836+
return -ENOMEM;
837+
#endif
838+
767839
gpio_dev->pdev = pdev;
768840
gpio_dev->gc.direction_input = amd_gpio_direction_input;
769841
gpio_dev->gc.direction_output = amd_gpio_direction_output;
@@ -853,6 +925,9 @@ static struct platform_driver amd_gpio_driver = {
853925
.driver = {
854926
.name = "amd_gpio",
855927
.acpi_match_table = ACPI_PTR(amd_gpio_acpi_match),
928+
#ifdef CONFIG_PM_SLEEP
929+
.pm = &amd_gpio_pm_ops,
930+
#endif
856931
},
857932
.probe = amd_gpio_probe,
858933
.remove = amd_gpio_remove,

drivers/pinctrl/pinctrl-amd.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ struct amd_gpio {
9797
unsigned int hwbank_num;
9898
struct resource *res;
9999
struct platform_device *pdev;
100+
u32 *saved_regs;
100101
};
101102

102103
/* KERNCZ configuration*/

0 commit comments

Comments
 (0)