From patchwork Mon Oct 16 15:27:59 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Geert Uytterhoeven X-Patchwork-Id: 826382 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-gpio-owner@vger.kernel.org; receiver=) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yG2KG4gt3z9t3w for ; Tue, 17 Oct 2017 02:28:34 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753812AbdJPP2c (ORCPT ); Mon, 16 Oct 2017 11:28:32 -0400 Received: from baptiste.telenet-ops.be ([195.130.132.51]:56628 "EHLO baptiste.telenet-ops.be" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754173AbdJPP2E (ORCPT ); Mon, 16 Oct 2017 11:28:04 -0400 Received: from ayla.of.borg ([84.195.106.246]) by baptiste.telenet-ops.be with bizsmtp id NFU11w0145JzmfG01FU1ZT; Mon, 16 Oct 2017 17:28:02 +0200 Received: from ramsan.of.borg ([192.168.97.29] helo=ramsan) by ayla.of.borg with esmtp (Exim 4.86_2) (envelope-from ) id 1e47JB-0001Jv-MS; Mon, 16 Oct 2017 17:28:01 +0200 Received: from geert by ramsan with local (Exim 4.86_2) (envelope-from ) id 1e47JB-0005Yz-Kf; Mon, 16 Oct 2017 17:28:01 +0200 From: Geert Uytterhoeven To: Laurent Pinchart , Linus Walleij Cc: =?utf-8?q?Niklas_S=C3=B6derlund?= , linux-renesas-soc@vger.kernel.org, linux-gpio@vger.kernel.org, linux-pm@vger.kernel.org, Geert Uytterhoeven Subject: [PATCH v2 14/14] pinctrl: sh-pfc: Save/restore registers for PSCI system suspend Date: Mon, 16 Oct 2017 17:27:59 +0200 Message-Id: <1508167679-21155-15-git-send-email-geert+renesas@glider.be> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1508167679-21155-1-git-send-email-geert+renesas@glider.be> References: <1508167679-21155-1-git-send-email-geert+renesas@glider.be> Sender: linux-gpio-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org During PSCI system suspend, R-Car Gen3 SoCs are powered down, and their pinctrl register state is lost. Note that as the boot loader skips most initialization after system resume, pinctrl register state differs from the state encountered during normal system boot, too. To fix this, save all GPIO and peripheral function select, module select, drive strength control, bias, and other I/O control registers during system suspend, and restore them during system resume. Note that to avoid overhead on platforms not needing it, the suspend/resume code has a build time dependency on sleep and PSCI support, and a runtime dependency on PSCI. Inspired by a patch in the BSP by Hien Dang. Signed-off-by: Geert Uytterhoeven --- v2: - Rebased. --- drivers/pinctrl/sh-pfc/core.c | 97 +++++++++++++++++++++++++++++++++++++++++ drivers/pinctrl/sh-pfc/sh_pfc.h | 1 + 2 files changed, 98 insertions(+) diff --git a/drivers/pinctrl/sh-pfc/core.c b/drivers/pinctrl/sh-pfc/core.c index 2fe5fd6c5d17adc5..cf4ae4bc91156991 100644 --- a/drivers/pinctrl/sh-pfc/core.c +++ b/drivers/pinctrl/sh-pfc/core.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "core.h" @@ -572,6 +573,97 @@ static const struct of_device_id sh_pfc_of_table[] = { }; #endif +#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_ARM_PSCI_FW) +static void sh_pfc_nop_reg(struct sh_pfc *pfc, u32 reg, unsigned int idx) +{ +} + +static void sh_pfc_save_reg(struct sh_pfc *pfc, u32 reg, unsigned int idx) +{ + pfc->saved_regs[idx] = sh_pfc_read(pfc, reg); +} + +static void sh_pfc_restore_reg(struct sh_pfc *pfc, u32 reg, unsigned int idx) +{ + sh_pfc_write(pfc, reg, pfc->saved_regs[idx]); +} + +static unsigned int sh_pfc_walk_regs(struct sh_pfc *pfc, + void (*do_reg)(struct sh_pfc *pfc, u32 reg, unsigned int idx)) +{ + unsigned int i, n = 0; + + if (pfc->info->cfg_regs) + for (i = 0; pfc->info->cfg_regs[i].reg; i++) + do_reg(pfc, pfc->info->cfg_regs[i].reg, n++); + + if (pfc->info->drive_regs) + for (i = 0; pfc->info->drive_regs[i].reg; i++) + do_reg(pfc, pfc->info->drive_regs[i].reg, n++); + + if (pfc->info->bias_regs) + for (i = 0; pfc->info->bias_regs[i].puen; i++) { + do_reg(pfc, pfc->info->bias_regs[i].puen, n++); + if (pfc->info->bias_regs[i].pud) + do_reg(pfc, pfc->info->bias_regs[i].pud, n++); + } + + if (pfc->info->ioctrl_regs) + for (i = 0; pfc->info->ioctrl_regs[i].reg; i++) + do_reg(pfc, pfc->info->ioctrl_regs[i].reg, n++); + + return n; +} + +static int sh_pfc_suspend_init(struct sh_pfc *pfc) +{ + unsigned int n; + + /* This is the best we can do to check for the presence of PSCI */ + if (!psci_ops.cpu_suspend) + return 0; + + n = sh_pfc_walk_regs(pfc, sh_pfc_nop_reg); + if (!n) + return 0; + + pfc->saved_regs = devm_kmalloc_array(pfc->dev, n, + sizeof(*pfc->saved_regs), + GFP_KERNEL); + if (!pfc->saved_regs) + return -ENOMEM; + + dev_dbg(pfc->dev, "Allocated space to save %u regs\n", n); + return 0; +} + +static int sh_pfc_suspend_noirq(struct device *dev) +{ + struct sh_pfc *pfc = dev_get_drvdata(dev); + + if (pfc->saved_regs) + sh_pfc_walk_regs(pfc, sh_pfc_save_reg); + return 0; +} + +static int sh_pfc_resume_noirq(struct device *dev) +{ + struct sh_pfc *pfc = dev_get_drvdata(dev); + + if (pfc->saved_regs) + sh_pfc_walk_regs(pfc, sh_pfc_restore_reg); + return 0; +} + +static const struct dev_pm_ops sh_pfc_pm = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(sh_pfc_suspend_noirq, sh_pfc_resume_noirq) +}; +#define DEV_PM_OPS &sh_pfc_pm +#else +static int sh_pfc_suspend_init(struct sh_pfc *pfc) { return 0; } +#define DEV_PM_OPS NULL +#endif /* CONFIG_PM_SLEEP && CONFIG_ARM_PSCI_FW */ + static int sh_pfc_probe(struct platform_device *pdev) { #ifdef CONFIG_OF @@ -610,6 +702,10 @@ static int sh_pfc_probe(struct platform_device *pdev) info = pfc->info; } + ret = sh_pfc_suspend_init(pfc); + if (ret) + return ret; + /* Enable dummy states for those platforms without pinctrl support */ if (!of_have_populated_dt()) pinctrl_provide_dummies(); @@ -693,6 +789,7 @@ static struct platform_driver sh_pfc_driver = { .driver = { .name = DRV_NAME, .of_match_table = of_match_ptr(sh_pfc_of_table), + .pm = DEV_PM_OPS, }, }; diff --git a/drivers/pinctrl/sh-pfc/sh_pfc.h b/drivers/pinctrl/sh-pfc/sh_pfc.h index b9bb56c91b6fdaf3..213108a058feefb0 100644 --- a/drivers/pinctrl/sh-pfc/sh_pfc.h +++ b/drivers/pinctrl/sh-pfc/sh_pfc.h @@ -222,6 +222,7 @@ struct sh_pfc { unsigned int nr_gpio_pins; struct sh_pfc_chip *gpio; + u32 *saved_regs; }; struct sh_pfc_soc_operations {