From patchwork Thu Oct 9 19:06:31 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Johan Hovold X-Patchwork-Id: 5060791 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id D13A19F30B for ; Thu, 9 Oct 2014 19:21:18 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id B39AD2022A for ; Thu, 9 Oct 2014 19:21:17 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id A926B2024C for ; Thu, 9 Oct 2014 19:21:16 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1XcJEl-0003HC-0z; Thu, 09 Oct 2014 19:18:55 +0000 Received: from mail-lb0-x22c.google.com ([2a00:1450:4010:c04::22c]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1XcJ8t-0006W4-Ne for linux-arm-kernel@lists.infradead.org; Thu, 09 Oct 2014 19:12:59 +0000 Received: by mail-lb0-f172.google.com with SMTP id b6so1831605lbj.3 for ; Thu, 09 Oct 2014 12:12:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references; bh=PEhwNc+8wYov4TV/IkafZay4zyC7t2y+w/nPUYj728o=; b=SlQilyVkjC0Mgi7Skni2Ei9pkxEYtFgoJ7Jtn9JeCyDYDCGfaDDPvMbEqH5cPWbvIf FyghW9U6Ln847KDmco9TjRvWio08e7Aunvk3lrbk97gPLAE0KcoOVs00O3VBpEcIzjbi +x0lsKeN2h0JubwiaEj73QVYo3OX/X6IGUp+vrCCxIlO1pHmnQH5yX2E6b7EJ7XN7sj9 fxi3dd7r1r25NL9xzz4cbPbfGznMP85qAhsZf8BinHdI9cAwOOjSnhU4sKYf6Evxl0a2 pMQNLajm3iJDXCEq6C/FH1vZycpeJj6zbiGmY/K8BY+eU7I3C54eeazMa4twefajs1XN OMRA== X-Received: by 10.152.22.7 with SMTP id z7mr20782538lae.6.1412881949340; Thu, 09 Oct 2014 12:12:29 -0700 (PDT) Received: from xi.terra (s83-177-171-8.cust.tele2.se. [83.177.171.8]) by mx.google.com with ESMTPSA id a9sm1199717laa.3.2014.10.09.12.12.26 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 09 Oct 2014 12:12:28 -0700 (PDT) X-Google-Original-Sender: Received: from johan by xi.terra with local (Exim 4.84) (envelope-from ) id 1XcJ5p-0006hm-1N; Thu, 09 Oct 2014 21:09:41 +0200 From: Johan Hovold To: Alessandro Zummo , Tony Lindgren , =?UTF-8?q?Beno=C3=AEt=20Cousson?= Subject: [PATCH 09/12] rtc: omap: add support for pmic_power_en Date: Thu, 9 Oct 2014 21:06:31 +0200 Message-Id: <1412881594-25678-10-git-send-email-johan@kernel.org> X-Mailer: git-send-email 2.0.4 In-Reply-To: <1412881594-25678-1-git-send-email-johan@kernel.org> References: <1412881594-25678-1-git-send-email-johan@kernel.org> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20141009_121252_175783_008CAC4B X-CRM114-Status: GOOD ( 22.80 ) X-Spam-Score: 0.0 (/) Cc: devicetree@vger.kernel.org, rtc-linux@googlegroups.com, Colin Foe-Parker , Lokesh Vutla , j-keerthy@ti.com, nsekhar@ti.com, linux-kernel@vger.kernel.org, Felipe Balbi , Johan Hovold , t-kristo@ti.com, linux-arm-kernel@lists.infradead.org, AnilKumar Ch , Andrew Morton , linux-omap@vger.kernel.org, Guenter Roeck X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-1.8 required=5.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_NONE, T_DKIM_INVALID, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=no version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add new property "ti,system-power-controller" to register the RTC as a power-off handler. Some RTC IP revisions can control an external PMIC via the pmic_power_en pin, which can be configured to transition to OFF on ALARM2 events and back to ON on subsequent ALARM (wakealarm) events. This is based on earlier work by Colin Foe-Parker and AnilKumar Ch. [1] [1] https://www.mail-archive.com/linux-omap@vger.kernel.org/msg82127.html Signed-off-by: Johan Hovold --- Documentation/devicetree/bindings/rtc/rtc-omap.txt | 9 +- drivers/rtc/rtc-omap.c | 107 ++++++++++++++++++++- 2 files changed, 110 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/rtc/rtc-omap.txt b/Documentation/devicetree/bindings/rtc/rtc-omap.txt index 5a0f02d34d95..750efd40c72e 100644 --- a/Documentation/devicetree/bindings/rtc/rtc-omap.txt +++ b/Documentation/devicetree/bindings/rtc/rtc-omap.txt @@ -5,11 +5,17 @@ Required properties: - "ti,da830-rtc" - for RTC IP used similar to that on DA8xx SoC family. - "ti,am3352-rtc" - for RTC IP used similar to that on AM335x SoC family. This RTC IP has special WAKE-EN Register to enable - Wakeup generation for event Alarm. + Wakeup generation for event Alarm. It can also be + used to control an external PMIC via the + pmic_power_en pin. - reg: Address range of rtc register set - interrupts: rtc timer, alarm interrupts in order - interrupt-parent: phandle for the interrupt controller +Optional properties: +- ti,system-power-controller: whether the rtc is controlling the system power + through pmic_power_en + Example: rtc@1c23000 { @@ -18,4 +24,5 @@ rtc@1c23000 { interrupts = <19 19>; interrupt-parent = <&intc>; + ti,system-power-controller; }; diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c index 62e2e9a9887a..b0319864dd42 100644 --- a/drivers/rtc/rtc-omap.c +++ b/drivers/rtc/rtc-omap.c @@ -70,6 +70,15 @@ #define OMAP_RTC_IRQWAKEEN 0x7c +#define OMAP_RTC_ALARM2_SECONDS_REG 0x80 +#define OMAP_RTC_ALARM2_MINUTES_REG 0x84 +#define OMAP_RTC_ALARM2_HOURS_REG 0x88 +#define OMAP_RTC_ALARM2_DAYS_REG 0x8c +#define OMAP_RTC_ALARM2_MONTHS_REG 0x90 +#define OMAP_RTC_ALARM2_YEARS_REG 0x94 + +#define OMAP_RTC_PMIC_REG 0x98 + /* OMAP_RTC_CTRL_REG bit fields: */ #define OMAP_RTC_CTRL_SPLIT BIT(7) #define OMAP_RTC_CTRL_DISABLE BIT(6) @@ -82,6 +91,7 @@ /* OMAP_RTC_STATUS_REG bit fields: */ #define OMAP_RTC_STATUS_POWER_UP BIT(7) +#define OMAP_RTC_STATUS_ALARM2 BIT(7) #define OMAP_RTC_STATUS_ALARM BIT(6) #define OMAP_RTC_STATUS_1D_EVENT BIT(5) #define OMAP_RTC_STATUS_1H_EVENT BIT(4) @@ -91,6 +101,7 @@ #define OMAP_RTC_STATUS_BUSY BIT(0) /* OMAP_RTC_INTERRUPTS_REG bit fields: */ +#define OMAP_RTC_INTERRUPTS_IT_ALARM2 BIT(4) #define OMAP_RTC_INTERRUPTS_IT_ALARM BIT(3) #define OMAP_RTC_INTERRUPTS_IT_TIMER BIT(2) @@ -100,6 +111,9 @@ /* OMAP_RTC_IRQWAKEEN bit fields: */ #define OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN BIT(1) +/* OMAP_RTC_PMIC bit fields: */ +#define OMAP_RTC_PMIC_POWER_EN_EN BIT(16) + /* OMAP_RTC_KICKER values */ #define KICK0_VALUE 0x83e70b13 #define KICK1_VALUE 0x95a4f1e0 @@ -124,11 +138,18 @@ */ #define OMAP_RTC_HAS_POWER_UP_RESET BIT(3) +/* + * Some RTC IP revisions can control an external PMIC via the pmic_power_en + * pin. + */ +#define OMAP_RTC_HAS_PMIC_MODE BIT(4) + static void __iomem *rtc_base; #define rtc_read(addr) readb(rtc_base + (addr)) #define rtc_write(val, addr) writeb(val, rtc_base + (addr)) +#define rtc_readl(addr) readl(rtc_base + (addr)) #define rtc_writel(val, addr) writel(val, rtc_base + (addr)) @@ -338,6 +359,61 @@ static int omap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) return 0; } +static struct platform_device *omap_rtc_power_off_dev; + +/* + * omap_rtc_poweroff: RTC-controlled power off + * + * The RTC can be used to control an external PMIC via the pmic_power_en pin, + * which can be configured to transition to OFF on ALARM2 events. + * + * Notes: + * The two-second alarm offset is the shortest offset possible as the alarm + * registers must be set before the next timer update and the offset + * calculation is to heavy for everything to be done within a single access + * period (~15us). + * + * Called with local interrupts disabled. + */ +static void omap_rtc_power_off(void) +{ + struct rtc_time tm; + unsigned long now; + u32 val; + + /* enable pmic_power_en control */ + val = rtc_readl(OMAP_RTC_PMIC_REG); + rtc_writel(val | OMAP_RTC_PMIC_POWER_EN_EN, OMAP_RTC_PMIC_REG); + + /* set alarm two seconds from now */ + omap_rtc_read_time(&omap_rtc_power_off_dev->dev, &tm); + rtc_tm_to_time(&tm, &now); + rtc_time_to_tm(now + 2, &tm); + + if (tm2bcd(&tm) < 0) { + pr_err("%s - power off failed\n", __func__); + return; + } + + rtc_wait_not_busy(); + + rtc_write(tm.tm_sec, OMAP_RTC_ALARM2_SECONDS_REG); + rtc_write(tm.tm_min, OMAP_RTC_ALARM2_MINUTES_REG); + rtc_write(tm.tm_hour, OMAP_RTC_ALARM2_HOURS_REG); + rtc_write(tm.tm_mday, OMAP_RTC_ALARM2_DAYS_REG); + rtc_write(tm.tm_mon, OMAP_RTC_ALARM2_MONTHS_REG); + rtc_write(tm.tm_year, OMAP_RTC_ALARM2_YEARS_REG); + + /* + * enable ALARM2 interrupt + * + * NOTE: this fails on AM3352 if rtc_write (writeb) is used + */ + val = rtc_read(OMAP_RTC_INTERRUPTS_REG); + rtc_writel(val | OMAP_RTC_INTERRUPTS_IT_ALARM2, + OMAP_RTC_INTERRUPTS_REG); +} + static struct rtc_class_ops omap_rtc_ops = { .read_time = omap_rtc_read_time, .set_time = omap_rtc_set_time, @@ -360,7 +436,7 @@ static struct platform_device_id omap_rtc_devtype[] = { [OMAP_RTC_DATA_AM3352_IDX] = { .name = "am3352-rtc", .driver_data = OMAP_RTC_HAS_KICKER | OMAP_RTC_HAS_IRQWAKEEN | - OMAP_RTC_HAS_32KCLK_EN, + OMAP_RTC_HAS_32KCLK_EN | OMAP_RTC_HAS_PMIC_MODE, }, [OMAP_RTC_DATA_DA830_IDX] = { .name = "da830-rtc", @@ -386,12 +462,16 @@ static int __init omap_rtc_probe(struct platform_device *pdev) struct resource *res; struct rtc_device *rtc; u8 reg, mask, new_ctrl; + bool pm_off = false; const struct platform_device_id *id_entry; const struct of_device_id *of_id; of_id = of_match_device(omap_rtc_of_match, &pdev->dev); - if (of_id) + if (of_id) { pdev->id_entry = of_id->data; + pm_off = of_property_read_bool(pdev->dev.of_node, + "ti,system-power-controller"); + } id_entry = platform_get_device_id(pdev); if (!id_entry) { @@ -425,10 +505,12 @@ static int __init omap_rtc_probe(struct platform_device *pdev) rtc_writel(KICK1_VALUE, OMAP_RTC_KICK1_REG); } - /* clear pending irqs, and set 1/second periodic, - * which we'll use instead of update irqs + /* + * disable interrupts + * + * NOTE: ALARM2 is not cleared on AM3352 if rtc_write (writeb) is used */ - rtc_write(0, OMAP_RTC_INTERRUPTS_REG); + rtc_writel(0, OMAP_RTC_INTERRUPTS_REG); /* enable RTC functional clock */ if (id_entry->driver_data & OMAP_RTC_HAS_32KCLK_EN) { @@ -440,6 +522,8 @@ static int __init omap_rtc_probe(struct platform_device *pdev) reg = rtc_read(OMAP_RTC_STATUS_REG); mask = OMAP_RTC_STATUS_ALARM; + if (id_entry->driver_data & OMAP_RTC_HAS_PMIC_MODE) + mask |= OMAP_RTC_STATUS_ALARM2; if (id_entry->driver_data & OMAP_RTC_HAS_POWER_UP_RESET) { mask |= OMAP_RTC_STATUS_POWER_UP; if (reg & OMAP_RTC_STATUS_POWER_UP) @@ -502,6 +586,13 @@ static int __init omap_rtc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, rtc); + if (id_entry->driver_data & OMAP_RTC_HAS_PMIC_MODE) { + if (pm_off && !pm_power_off) { + omap_rtc_power_off_dev = pdev; + pm_power_off = omap_rtc_power_off; + } + } + return 0; fail0: @@ -518,6 +609,12 @@ static int __exit omap_rtc_remove(struct platform_device *pdev) const struct platform_device_id *id_entry = platform_get_device_id(pdev); + if (pm_power_off == omap_rtc_power_off && + omap_rtc_power_off_dev == pdev) { + pm_power_off = NULL; + omap_rtc_power_off_dev = NULL; + } + device_init_wakeup(&pdev->dev, 0); /* leave rtc running, but disable irqs */