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

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
Loading
Loading
Loading
Loading
+55 −1
Original line number Diff line number Diff line
@@ -81,6 +81,7 @@ struct intel_pad_context {

struct intel_community_context {
	u32 *intmask;
	u32 *hostown;
};

struct intel_pinctrl_context {
@@ -1284,7 +1285,7 @@ static int intel_pinctrl_pm_init(struct intel_pinctrl *pctrl)

	for (i = 0; i < pctrl->ncommunities; i++) {
		struct intel_community *community = &pctrl->communities[i];
		u32 *intmask;
		u32 *intmask, *hostown;

		intmask = devm_kcalloc(pctrl->dev, community->ngpps,
				       sizeof(*intmask), GFP_KERNEL);
@@ -1292,6 +1293,13 @@ static int intel_pinctrl_pm_init(struct intel_pinctrl *pctrl)
			return -ENOMEM;

		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;
@@ -1501,6 +1509,10 @@ int intel_pinctrl_suspend_noirq(struct device *dev)
		base = community->regs + community->ie_offset;
		for (gpp = 0; gpp < community->ngpps; gpp++)
			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;
@@ -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)
{
	struct intel_pinctrl *pctrl = dev_get_drvdata(dev);
@@ -1585,6 +1620,25 @@ int intel_pinctrl_resume_noirq(struct device *dev)
			dev_dbg(dev, "restored mask %d/%u %#08x\n", i, gpp,
				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;