[Acked] [Vivid][SRU][PATCH] pinctrl: cherryview: Save and restore pin configs over system sleep

Andy Whitcroft apw at canonical.com
Mon Dec 21 11:05:19 UTC 2015


On Mon, Dec 21, 2015 at 01:41:56PM +0800, Phidias Chiang wrote:
> From: Mika Westerberg <mika.westerberg at linux.intel.com>
> 
> BugLink: http://bugs.launchpad.net/bugs/1518855
> 
> Before resuming from system sleep BIOS restores its view of pin
> configuration. If we have configured some pins differently from that, for
> instance some driver requested a pin as a GPIO but it was not in GPIO mode
> originally, our view of the pin configuration will not match the hardware
> state anymore.
> 
> This patch saves the pin configuration and interrupt mask registers on
> suspend and restores them on exit. This should make sure that the
> previously configured state is still in effect.
> 
> Signed-off-by: Mika Westerberg <mika.westerberg at linux.intel.com>
> Signed-off-by: Linus Walleij <linus.walleij at linaro.org>
> (cherry picked from commit 9eb457b547cc731bc2fc251bd79891a60c64fc3e)
> Signed-off-by: Phidias Chiang <phidias.chiang at canonical.com>
> Reviewed-By: AceLan Kao <acelan.kao at canonical.com>
> 
> ---
>  drivers/pinctrl/intel/pinctrl-cherryview.c | 104 +++++++++++++++++++++++++++++
>  1 file changed, 104 insertions(+)
> 
> diff --git a/drivers/pinctrl/intel/pinctrl-cherryview.c b/drivers/pinctrl/intel/pinctrl-cherryview.c
> index e9f8b39..dde67d4 100644
> --- a/drivers/pinctrl/intel/pinctrl-cherryview.c
> +++ b/drivers/pinctrl/intel/pinctrl-cherryview.c
> @@ -148,6 +148,11 @@ struct chv_community {
>  	size_t ngpios;
>  };
>  
> +struct chv_pin_context {
> +	u32 padctrl0;
> +	u32 padctrl1;
> +};
> +
>  /**
>   * struct chv_pinctrl - CHV pinctrl private structure
>   * @dev: Pointer to the parent device
> @@ -172,6 +177,8 @@ struct chv_pinctrl {
>  	spinlock_t lock;
>  	unsigned intr_lines[16];
>  	const struct chv_community *community;
> +	u32 saved_intmask;
> +	struct chv_pin_context *saved_pin_context;
>  };
>  
>  #define gpiochip_to_pinctrl(c) container_of(c, struct chv_pinctrl, chip)
> @@ -1443,6 +1450,14 @@ static int chv_pinctrl_probe(struct platform_device *pdev)
>  	spin_lock_init(&pctrl->lock);
>  	pctrl->dev = &pdev->dev;
>  
> +#ifdef CONFIG_PM_SLEEP
> +	pctrl->saved_pin_context = devm_kcalloc(pctrl->dev,
> +		pctrl->community->npins, sizeof(*pctrl->saved_pin_context),
> +		GFP_KERNEL);
> +	if (!pctrl->saved_pin_context)
> +		return -ENOMEM;
> +#endif
> +
>  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>  	pctrl->regs = devm_ioremap_resource(&pdev->dev, res);
>  	if (IS_ERR(pctrl->regs))
> @@ -1486,6 +1501,94 @@ static int chv_pinctrl_remove(struct platform_device *pdev)
>  	return 0;
>  }
>  
> +#ifdef CONFIG_PM_SLEEP
> +static int chv_pinctrl_suspend(struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct chv_pinctrl *pctrl = platform_get_drvdata(pdev);
> +	int i;
> +
> +	pctrl->saved_intmask = readl(pctrl->regs + CHV_INTMASK);
> +
> +	for (i = 0; i < pctrl->community->npins; i++) {
> +		const struct pinctrl_pin_desc *desc;
> +		struct chv_pin_context *ctx;
> +		void __iomem *reg;
> +
> +		desc = &pctrl->community->pins[i];
> +		if (chv_pad_locked(pctrl, desc->number))
> +			continue;
> +
> +		ctx = &pctrl->saved_pin_context[i];
> +
> +		reg = chv_padreg(pctrl, desc->number, CHV_PADCTRL0);
> +		ctx->padctrl0 = readl(reg) & ~CHV_PADCTRL0_GPIORXSTATE;
> +
> +		reg = chv_padreg(pctrl, desc->number, CHV_PADCTRL1);
> +		ctx->padctrl1 = readl(reg);
> +	}
> +
> +	return 0;
> +}
> +
> +static int chv_pinctrl_resume(struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct chv_pinctrl *pctrl = platform_get_drvdata(pdev);
> +	int i;
> +
> +	/*
> +	 * Mask all interrupts before restoring per-pin configuration
> +	 * registers because we don't know in which state BIOS left them
> +	 * upon exiting suspend.
> +	 */
> +	chv_writel(0, pctrl->regs + CHV_INTMASK);
> +
> +	for (i = 0; i < pctrl->community->npins; i++) {
> +		const struct pinctrl_pin_desc *desc;
> +		const struct chv_pin_context *ctx;
> +		void __iomem *reg;
> +		u32 val;
> +
> +		desc = &pctrl->community->pins[i];
> +		if (chv_pad_locked(pctrl, desc->number))
> +			continue;
> +
> +		ctx = &pctrl->saved_pin_context[i];
> +
> +		/* Only restore if our saved state differs from the current */
> +		reg = chv_padreg(pctrl, desc->number, CHV_PADCTRL0);
> +		val = readl(reg) & ~CHV_PADCTRL0_GPIORXSTATE;
> +		if (ctx->padctrl0 != val) {
> +			chv_writel(ctx->padctrl0, reg);
> +			dev_dbg(pctrl->dev, "restored pin %2u ctrl0 0x%08x\n",
> +				desc->number, readl(reg));
> +		}
> +
> +		reg = chv_padreg(pctrl, desc->number, CHV_PADCTRL1);
> +		val = readl(reg);
> +		if (ctx->padctrl1 != val) {
> +			chv_writel(ctx->padctrl1, reg);
> +			dev_dbg(pctrl->dev, "restored pin %2u ctrl1 0x%08x\n",
> +				desc->number, readl(reg));
> +		}
> +	}
> +
> +	/*
> +	 * Now that all pins are restored to known state, we can restore
> +	 * the interrupt mask register as well.
> +	 */
> +	chv_writel(0xffff, pctrl->regs + CHV_INTSTAT);
> +	chv_writel(pctrl->saved_intmask, pctrl->regs + CHV_INTMASK);
> +
> +	return 0;
> +}
> +#endif
> +
> +static const struct dev_pm_ops chv_pinctrl_pm_ops = {
> +	SET_LATE_SYSTEM_SLEEP_PM_OPS(chv_pinctrl_suspend, chv_pinctrl_resume)
> +};
> +
>  static const struct acpi_device_id chv_pinctrl_acpi_match[] = {
>  	{ "INT33FF" },
>  	{ }
> @@ -1498,6 +1601,7 @@ static struct platform_driver chv_pinctrl_driver = {
>  	.driver = {
>  		.name = "cherryview-pinctrl",
>  		.owner = THIS_MODULE,
> +		.pm = &chv_pinctrl_pm_ops,
>  		.acpi_match_table = chv_pinctrl_acpi_match,
>  	},
>  };

Looks to do what is claimed.  Clean cherry-pick.

Acked-by: Andy Whitcroft <apw at canonical.com>

-apw




More information about the kernel-team mailing list