From patchwork Fri May 17 04:33:18 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Doug Anderson X-Patchwork-Id: 2581031 Return-Path: X-Original-To: patchwork-linux-samsung-soc@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by patchwork1.kernel.org (Postfix) with ESMTP id 98A5C3FD4E for ; Fri, 17 May 2013 04:39:20 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751238Ab3EQEjU (ORCPT ); Fri, 17 May 2013 00:39:20 -0400 Received: from mail-gh0-f202.google.com ([209.85.160.202]:64114 "EHLO mail-gh0-f202.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751217Ab3EQEjT (ORCPT ); Fri, 17 May 2013 00:39:19 -0400 Received: by mail-gh0-f202.google.com with SMTP id g24so376772ghb.5 for ; Thu, 16 May 2013 21:39:18 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=x-received:from:to:cc:subject:date:message-id:x-mailer:in-reply-to :references:mime-version:content-type:content-transfer-encoding :x-gm-message-state; bh=Q7kvBD0LjD/0i3Mv0ZAETM3WJmG5wiECJoewXPhwTwY=; b=Atn8H6UwVRtH5a4DsT6FTu+oMMDkEeWuWFsBKOTr1uaDvny1b1ddPi0q4txf7Jr/xg WFFHSHfh9En0olWkP/H89iz8b5fPOS1eYVl4khchOs9ooBi6/cNHBIVRU0asvQL+NPO7 nUz3vtrUc2NjQveDXJQeWfWJcmGbpftdkbWII9PmQzIEEgMOcMbfy6uDw7ysSji9Jl48 78Ci2mFytQlJrR63RiBhRzy6VlhNrf4S/Sobttkj935p6+eKipoSk9U+NHSwfCtysczQ Wo0WOVHkC+um9vyPaI3gRVR930zZrzPyjl9V3XGSzriYJpVp+5/ZssvBwYVR3uiBF+dg Fgmw== X-Received: by 10.236.84.177 with SMTP id s37mr22040202yhe.37.1368765203117; Thu, 16 May 2013 21:33:23 -0700 (PDT) Received: from corp2gmr1-2.hot.corp.google.com (corp2gmr1-2.hot.corp.google.com [172.24.189.93]) by gmr-mx.google.com with ESMTPS id n78si666875yhh.1.2013.05.16.21.33.23 for (version=TLSv1.1 cipher=AES128-SHA bits=128/128); Thu, 16 May 2013 21:33:23 -0700 (PDT) Received: from tictac.mtv.corp.google.com (tictac.mtv.corp.google.com [172.22.162.34]) by corp2gmr1-2.hot.corp.google.com (Postfix) with ESMTP id DFF695A4016; Thu, 16 May 2013 21:33:22 -0700 (PDT) Received: by tictac.mtv.corp.google.com (Postfix, from userid 121310) id 805BF80660; Thu, 16 May 2013 21:33:22 -0700 (PDT) From: Doug Anderson To: Tomasz Figa , Kukjin Kim Cc: =?UTF-8?q?Heiko=20St=C3=BCbner?= , Olof Johansson , Stephen Warren , Thomas Abraham , Linus Walleij , Prathyush K , linux-samsung-soc@vger.kernel.org, Doug Anderson , linux-kernel@vger.kernel.org Subject: [PATCH v3] pinctrl: samsung: fix suspend/resume functionality Date: Thu, 16 May 2013 21:33:18 -0700 Message-Id: <1368765198-25550-1-git-send-email-dianders@chromium.org> X-Mailer: git-send-email 1.8.2.1 In-Reply-To: <1368746388-20194-1-git-send-email-dianders@chromium.org> References: <1368746388-20194-1-git-send-email-dianders@chromium.org> MIME-Version: 1.0 X-Gm-Message-State: ALoCoQkyTNjaaPnERRzQhT1B732ey2f9id4JL4PczV2zbs+3sEfAbCSzqRgG9v/aHovp4oGYOhy3Q+Bnjp27J01sFmqeZrbK0QNLtTf8qlgQC7OSABHXBlBSiNPk7BixHREPDyz/DBpR17WcLynpQTt21rb09ziJNE6jusoC3A/IwOAz+0qrCUEGPeBu7B97EHHg6X2hc9gixEhA3aSkP9L5F2+P+uCy4Q== Sender: linux-samsung-soc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-samsung-soc@vger.kernel.org The GPIO states need to be restored after s2r and this is not currently supported in the pinctrl driver. This patch saves the gpio states before suspend and restores them after resume. Saving and restoring is done very early using syscore_ops and must happen before pins are released from their powerdown state. Patch originally from Prathyush K but rewritten by Doug Anderson . Signed-off-by: Prathyush K Signed-off-by: Doug Anderson Tested-by: Tomasz Figa Acked-by: Tomasz Figa Acked-by: Linus Walleij Acked-by: Kukjin Kim --- Changes in v3: - Skip save and restore for banks with no powerdown config. Changes in v2: - Now uses sycore_ops to make sure we're early enough. - Try to handle two CON registers better. - Should handle s3c24xx better as per Heiko. - Simpler code; no longer tries to avoid glitching lines since we _think_ all current SoCs should have pins in power down state when the restore is called. - Dropped eint patch for now; Tomasz will post his version. drivers/pinctrl/pinctrl-samsung.c | 148 ++++++++++++++++++++++++++++++++++++++ drivers/pinctrl/pinctrl-samsung.h | 5 ++ 2 files changed, 153 insertions(+) diff --git a/drivers/pinctrl/pinctrl-samsung.c b/drivers/pinctrl/pinctrl-samsung.c index 9763668..d45e36f 100644 --- a/drivers/pinctrl/pinctrl-samsung.c +++ b/drivers/pinctrl/pinctrl-samsung.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "core.h" #include "pinctrl-samsung.h" @@ -48,6 +49,9 @@ static struct pin_config { { "samsung,pin-pud-pdn", PINCFG_TYPE_PUD_PDN }, }; +/* Global list of devices (struct samsung_pinctrl_drv_data) */ +LIST_HEAD(drvdata_list); + static unsigned int pin_base; static inline struct samsung_pin_bank *gc_to_pin_bank(struct gpio_chip *gc) @@ -961,9 +965,145 @@ static int samsung_pinctrl_probe(struct platform_device *pdev) ctrl->eint_wkup_init(drvdata); platform_set_drvdata(pdev, drvdata); + + /* Add to the global list */ + list_add_tail(&drvdata->node, &drvdata_list); + return 0; } +#ifdef CONFIG_PM + +/** + * samsung_pinctrl_suspend_dev - save pinctrl state for suspend for a device + * + * Save data for all banks handled by this device. + */ +static void samsung_pinctrl_suspend_dev( + struct samsung_pinctrl_drv_data *drvdata) +{ + struct samsung_pin_ctrl *ctrl = drvdata->ctrl; + void __iomem *virt_base = drvdata->virt_base; + int i; + + for (i = 0; i < ctrl->nr_banks; i++) { + struct samsung_pin_bank *bank = &ctrl->pin_banks[i]; + void __iomem *reg = virt_base + bank->pctl_offset; + + u8 *offs = bank->type->reg_offset; + u8 *widths = bank->type->fld_width; + enum pincfg_type type; + + /* Registers without a powerdown config aren't lost */ + if (!widths[PINCFG_TYPE_CON_PDN]) + continue; + + for (type = 0; type < PINCFG_TYPE_NUM; type++) + if (widths[type]) + bank->pm_save[type] = readl(reg + offs[type]); + + if (widths[PINCFG_TYPE_FUNC] * bank->nr_pins > 32) { + /* Some banks have two config registers */ + bank->pm_save[PINCFG_TYPE_NUM] = + readl(reg + offs[PINCFG_TYPE_FUNC] + 4); + pr_debug("Save %s @ %p (con %#010x %08x)\n", + bank->name, reg, + bank->pm_save[PINCFG_TYPE_FUNC], + bank->pm_save[PINCFG_TYPE_NUM]); + } else { + pr_debug("Save %s @ %p (con %#010x)\n", bank->name, + reg, bank->pm_save[PINCFG_TYPE_FUNC]); + } + } +} + +/** + * samsung_pinctrl_resume_dev - restore pinctrl state from suspend for a device + * + * Restore one of the banks that was saved during suspend. + * + * We don't bother doing anything complicated to avoid glitching lines since + * we're called before pad retention is turned off. + */ +static void samsung_pinctrl_resume_dev(struct samsung_pinctrl_drv_data *drvdata) +{ + struct samsung_pin_ctrl *ctrl = drvdata->ctrl; + void __iomem *virt_base = drvdata->virt_base; + int i; + + for (i = 0; i < ctrl->nr_banks; i++) { + struct samsung_pin_bank *bank = &ctrl->pin_banks[i]; + void __iomem *reg = virt_base + bank->pctl_offset; + + u8 *offs = bank->type->reg_offset; + u8 *widths = bank->type->fld_width; + enum pincfg_type type; + + /* Registers without a powerdown config aren't lost */ + if (!widths[PINCFG_TYPE_CON_PDN]) + continue; + + if (widths[PINCFG_TYPE_FUNC] * bank->nr_pins > 32) { + /* Some banks have two config registers */ + pr_debug("%s @ %p (con %#010x %08x => %#010x %08x)\n", + bank->name, reg, + readl(reg + offs[PINCFG_TYPE_FUNC]), + readl(reg + offs[PINCFG_TYPE_FUNC] + 4), + bank->pm_save[PINCFG_TYPE_FUNC], + bank->pm_save[PINCFG_TYPE_NUM]); + writel(bank->pm_save[PINCFG_TYPE_NUM], + reg + offs[PINCFG_TYPE_FUNC] + 4); + } else { + pr_debug("%s @ %p (con %#010x => %#010x)\n", bank->name, + reg, readl(reg + offs[PINCFG_TYPE_FUNC]), + bank->pm_save[PINCFG_TYPE_FUNC]); + } + for (type = 0; type < PINCFG_TYPE_NUM; type++) + if (widths[type]) + writel(bank->pm_save[type], reg + offs[type]); + } +} + +/** + * samsung_pinctrl_suspend - save pinctrl state for suspend + * + * Save data for all banks across all devices. + */ +static int samsung_pinctrl_suspend(void) +{ + struct samsung_pinctrl_drv_data *drvdata; + + list_for_each_entry(drvdata, &drvdata_list, node) { + samsung_pinctrl_suspend_dev(drvdata); + } + + return 0; +} + +/** + * samsung_pinctrl_resume - restore pinctrl state for suspend + * + * Restore data for all banks across all devices. + */ +static void samsung_pinctrl_resume(void) +{ + struct samsung_pinctrl_drv_data *drvdata; + + list_for_each_entry_reverse(drvdata, &drvdata_list, node) { + samsung_pinctrl_resume_dev(drvdata); + } +} + +#else +#define samsung_pinctrl_suspend NULL +#define samsung_pinctrl_resume NULL +#endif + +static struct syscore_ops samsung_pinctrl_syscore_ops = { + .suspend = samsung_pinctrl_suspend, + .resume = samsung_pinctrl_resume, +}; + static const struct of_device_id samsung_pinctrl_dt_match[] = { #ifdef CONFIG_PINCTRL_EXYNOS { .compatible = "samsung,exynos4210-pinctrl", @@ -992,6 +1132,14 @@ static struct platform_driver samsung_pinctrl_driver = { static int __init samsung_pinctrl_drv_register(void) { + /* + * Register syscore ops for save/restore of registers across suspend. + * It's important to ensure that this driver is running at an earlier + * initcall level than any arch-specific init calls that install syscore + * ops that turn off pad retention (like exynos_pm_resume). + */ + register_syscore_ops(&samsung_pinctrl_syscore_ops); + return platform_driver_register(&samsung_pinctrl_driver); } postcore_initcall(samsung_pinctrl_drv_register); diff --git a/drivers/pinctrl/pinctrl-samsung.h b/drivers/pinctrl/pinctrl-samsung.h index 7c7f9eb..9f5cc81 100644 --- a/drivers/pinctrl/pinctrl-samsung.h +++ b/drivers/pinctrl/pinctrl-samsung.h @@ -127,6 +127,7 @@ struct samsung_pin_bank_type { * @gpio_chip: GPIO chip of the bank. * @grange: linux gpio pin range supported by this bank. * @slock: spinlock protecting bank registers + * @pm_save: saved register values during suspend */ struct samsung_pin_bank { struct samsung_pin_bank_type *type; @@ -144,6 +145,8 @@ struct samsung_pin_bank { struct gpio_chip gpio_chip; struct pinctrl_gpio_range grange; spinlock_t slock; + + u32 pm_save[PINCFG_TYPE_NUM + 1]; /* +1 to handle double CON registers*/ }; /** @@ -189,6 +192,7 @@ struct samsung_pin_ctrl { /** * struct samsung_pinctrl_drv_data: wrapper for holding driver data together. + * @node: global list node * @virt_base: register base address of the controller. * @dev: device instance representing the controller. * @irq: interrpt number used by the controller to notify gpio interrupts. @@ -201,6 +205,7 @@ struct samsung_pin_ctrl { * @nr_function: number of such pin functions. */ struct samsung_pinctrl_drv_data { + struct list_head node; void __iomem *virt_base; struct device *dev; int irq;