Commit a0a5f766 authored by Chris Chiu's avatar Chris Chiu Committed by Andy Shevchenko

pinctrl: intel: Retain HOSTSW_OWN for requested gpio pin

The touchpad of the ASUS laptops E403NA, X540NA, X541NA are not
responsive after suspend/resume. The following error message
shows after resume.
 i2c_hid i2c-ELAN1200:00: failed to reset device.

On these laptops, the touchpad interrupt is connected via a GPIO
pin which is controlled by Intel pinctrl. After system resumes,
the GPIO is in ACPI mode and no longer works as an IRQ.

This commit saves the HOSTSW_OWN value during suspend, make sure
the HOSTSW_OWN mode remains the same after resume.
Signed-off-by: default avatarChris Chiu <chiu@endlessm.com>
Acked-by: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: default avatarAndy Shevchenko <andriy.shevchenko@linux.intel.com>
parent 2fef3276
...@@ -81,6 +81,7 @@ struct intel_pad_context { ...@@ -81,6 +81,7 @@ struct intel_pad_context {
struct intel_community_context { struct intel_community_context {
u32 *intmask; u32 *intmask;
u32 *hostown;
}; };
struct intel_pinctrl_context { struct intel_pinctrl_context {
...@@ -1284,7 +1285,7 @@ static int intel_pinctrl_pm_init(struct intel_pinctrl *pctrl) ...@@ -1284,7 +1285,7 @@ static int intel_pinctrl_pm_init(struct intel_pinctrl *pctrl)
for (i = 0; i < pctrl->ncommunities; i++) { for (i = 0; i < pctrl->ncommunities; i++) {
struct intel_community *community = &pctrl->communities[i]; struct intel_community *community = &pctrl->communities[i];
u32 *intmask; u32 *intmask, *hostown;
intmask = devm_kcalloc(pctrl->dev, community->ngpps, intmask = devm_kcalloc(pctrl->dev, community->ngpps,
sizeof(*intmask), GFP_KERNEL); sizeof(*intmask), GFP_KERNEL);
...@@ -1292,6 +1293,13 @@ static int intel_pinctrl_pm_init(struct intel_pinctrl *pctrl) ...@@ -1292,6 +1293,13 @@ static int intel_pinctrl_pm_init(struct intel_pinctrl *pctrl)
return -ENOMEM; return -ENOMEM;
communities[i].intmask = intmask; communities[i].intmask = intmask;
hostown = devm_kcalloc(pctrl->dev, community->ngpps,
sizeof(*hostown), GFP_KERNEL);
if (!hostown)
return -ENOMEM;
communities[i].hostown = hostown;
} }
pctrl->context.pads = pads; pctrl->context.pads = pads;
...@@ -1501,6 +1509,10 @@ int intel_pinctrl_suspend_noirq(struct device *dev) ...@@ -1501,6 +1509,10 @@ int intel_pinctrl_suspend_noirq(struct device *dev)
base = community->regs + community->ie_offset; base = community->regs + community->ie_offset;
for (gpp = 0; gpp < community->ngpps; gpp++) for (gpp = 0; gpp < community->ngpps; gpp++)
communities[i].intmask[gpp] = readl(base + gpp * 4); communities[i].intmask[gpp] = readl(base + gpp * 4);
base = community->regs + community->hostown_offset;
for (gpp = 0; gpp < community->ngpps; gpp++)
communities[i].hostown[gpp] = readl(base + gpp * 4);
} }
return 0; return 0;
...@@ -1527,6 +1539,29 @@ static void intel_gpio_irq_init(struct intel_pinctrl *pctrl) ...@@ -1527,6 +1539,29 @@ static void intel_gpio_irq_init(struct intel_pinctrl *pctrl)
} }
} }
static u32
intel_gpio_is_requested(struct gpio_chip *chip, int base, unsigned int size)
{
u32 requested = 0;
unsigned int i;
for (i = 0; i < size; i++)
if (gpiochip_is_requested(chip, base + i))
requested |= BIT(i);
return requested;
}
static u32
intel_gpio_update_pad_mode(void __iomem *hostown, u32 mask, u32 value)
{
u32 curr = readl(hostown);
u32 updated = (curr & ~mask) | (value & mask);
writel(updated, hostown);
return curr;
}
int intel_pinctrl_resume_noirq(struct device *dev) int intel_pinctrl_resume_noirq(struct device *dev)
{ {
struct intel_pinctrl *pctrl = dev_get_drvdata(dev); struct intel_pinctrl *pctrl = dev_get_drvdata(dev);
...@@ -1585,6 +1620,25 @@ int intel_pinctrl_resume_noirq(struct device *dev) ...@@ -1585,6 +1620,25 @@ int intel_pinctrl_resume_noirq(struct device *dev)
dev_dbg(dev, "restored mask %d/%u %#08x\n", i, gpp, dev_dbg(dev, "restored mask %d/%u %#08x\n", i, gpp,
readl(base + gpp * 4)); readl(base + gpp * 4));
} }
base = community->regs + community->hostown_offset;
for (gpp = 0; gpp < community->ngpps; gpp++) {
const struct intel_padgroup *padgrp = &community->gpps[gpp];
u32 requested = 0, value = 0;
u32 saved = communities[i].hostown[gpp];
if (padgrp->gpio_base < 0)
continue;
requested = intel_gpio_is_requested(&pctrl->chip,
padgrp->gpio_base, padgrp->size);
value = intel_gpio_update_pad_mode(base + gpp * 4,
requested, saved);
if ((value ^ saved) & requested) {
dev_warn(dev, "restore hostown %d/%u %#8x->%#8x\n",
i, gpp, value, saved);
}
}
} }
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