From patchwork Mon Oct 28 18:12:35 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Josh Cartwright X-Patchwork-Id: 3104181 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 43A33BF924 for ; Mon, 28 Oct 2013 19:45:08 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 8D16A2039D for ; Mon, 28 Oct 2013 19:45:06 +0000 (UTC) Received: from casper.infradead.org (casper.infradead.org [85.118.1.10]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id AB78720395 for ; Mon, 28 Oct 2013 19:45:04 +0000 (UTC) Received: from merlin.infradead.org ([2001:4978:20e::2]) by casper.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1VasJL-0000NC-1M; Mon, 28 Oct 2013 19:17:12 +0000 Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1VasIR-0008Gp-KA; Mon, 28 Oct 2013 19:16:15 +0000 Received: from smtp.codeaurora.org ([198.145.11.231]) by merlin.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1VasH6-00086y-8E for linux-arm-kernel@lists.infradead.org; Mon, 28 Oct 2013 19:15:06 +0000 Received: from smtp.codeaurora.org (localhost [127.0.0.1]) by smtp.codeaurora.org (Postfix) with ESMTP id 5989213F28D; Mon, 28 Oct 2013 19:14:10 +0000 (UTC) Received: by smtp.codeaurora.org (Postfix, from userid 486) id 452A813F2AE; Mon, 28 Oct 2013 19:14:10 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Spam-Level: X-Spam-Status: No, score=-4.7 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from joshc.qualcomm.com (rrcs-67-52-129-61.west.biz.rr.com [67.52.129.61]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) (Authenticated sender: joshc@smtp.codeaurora.org) by smtp.codeaurora.org (Postfix) with ESMTPSA id E965513F2A5; Mon, 28 Oct 2013 19:14:08 +0000 (UTC) Received: by joshc.qualcomm.com (Postfix, from userid 1000) id DC10B60D04; Mon, 28 Oct 2013 14:12:55 -0500 (CDT) Message-Id: <1392ea90487793f9cbbdfb7f8d148f45122a52c2.1382985169.git.joshc@codeaurora.org> In-Reply-To: References: From: Josh Cartwright Date: Mon, 28 Oct 2013 13:12:35 -0500 To: Greg Kroah-Hartman , Alessandro Zummo , David Brown , Daniel Walker , Bryan Huntsman Subject: [PATCH v3 10/10] rtc: pm8xxx: add support for pm8941 X-Virus-Scanned: ClamAV using ClamSMTP X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20131028_151453_267676_BDD0CF6A X-CRM114-Status: GOOD ( 33.19 ) X-Spam-Score: -2.4 (--) Cc: rtc-linux@googlegroups.com, linux-arm-msm@vger.kernel.org, Gilad Avidov , linux-kernel@vger.kernel.org, Michael Bohan , Sagar Dharia , linux-arm-kernel@lists.infradead.org X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.15 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-Virus-Scanned: ClamAV using ClamSMTP Adapt the existing pm8xxx driver to work with the RTC available on Qualcomm's 8941 PMIC. In order to do this, rework the register access functions to use a regmap provided by the parent driver. Account for slight differences in the RTC address space by parameterizing addresses and providing a chip-specific initialization function. Signed-off-by: Josh Cartwright --- drivers/rtc/Kconfig | 1 - drivers/rtc/rtc-pm8xxx.c | 229 +++++++++++++++++++++++++++++------------------ 2 files changed, 143 insertions(+), 87 deletions(-) diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 9654aa3..19b89ee 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1177,7 +1177,6 @@ config RTC_DRV_LPC32XX config RTC_DRV_PM8XXX tristate "Qualcomm PMIC8XXX RTC" - depends on MFD_PM8XXX help If you say yes here you get support for the Qualcomm PMIC8XXX RTC. diff --git a/drivers/rtc/rtc-pm8xxx.c b/drivers/rtc/rtc-pm8xxx.c index 03f8f75..a9044d4 100644 --- a/drivers/rtc/rtc-pm8xxx.c +++ b/drivers/rtc/rtc-pm8xxx.c @@ -1,4 +1,5 @@ /* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * Copyright (c) 2013, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -13,35 +14,33 @@ #include #include #include +#include +#include #include +#include #include #include #include #include - -/* RTC Register offsets from RTC CTRL REG */ -#define PM8XXX_ALARM_CTRL_OFFSET 0x01 -#define PM8XXX_RTC_WRITE_OFFSET 0x02 -#define PM8XXX_RTC_READ_OFFSET 0x06 -#define PM8XXX_ALARM_RW_OFFSET 0x0A - /* RTC_CTRL register bit fields */ #define PM8xxx_RTC_ENABLE BIT(7) #define PM8xxx_RTC_ALARM_ENABLE BIT(1) #define PM8xxx_RTC_ALARM_CLEAR BIT(0) #define NUM_8_BIT_RTC_REGS 0x4 - /** * struct pm8xxx_rtc - rtc driver internal structure * @rtc: rtc device for this driver. * @rtc_alarm_irq: rtc alarm irq number. - * @rtc_base: address of rtc control register. + * @rtc_control_reg: address of control register. * @rtc_read_base: base address of read registers. * @rtc_write_base: base address of write registers. * @alarm_rw_base: base address of alarm registers. + * @alarm_ctrl1: address of alarm ctrl1 register. + * @alarm_ctrl2: address of alarm ctrl2 register (only used on pm8941). + * @alarm_clear: RTC-specific callback to clear alarm interrupt. * @ctrl_reg: rtc control register. * @rtc_dev: device structure. * @ctrl_reg_lock: spinlock protecting access to ctrl_reg. @@ -49,51 +48,34 @@ struct pm8xxx_rtc { struct rtc_device *rtc; int rtc_alarm_irq; - int rtc_base; + int rtc_control_reg; int rtc_read_base; int rtc_write_base; int alarm_rw_base; - u8 ctrl_reg; + int alarm_ctrl1; + int alarm_ctrl2; + int (*alarm_clear)(struct pm8xxx_rtc *); + u8 ctrl_reg; struct device *rtc_dev; spinlock_t ctrl_reg_lock; + struct regmap *regmap; }; /* * The RTC registers need to be read/written one byte at a time. This is a * hardware limitation. */ -static int pm8xxx_read_wrapper(struct pm8xxx_rtc *rtc_dd, u8 *rtc_val, - int base, int count) +static inline int pm8xxx_read_wrapper(struct pm8xxx_rtc *rtc_dd, u8 *rtc_val, + int base, int count) { - int i, rc; - struct device *parent = rtc_dd->rtc_dev->parent; - - for (i = 0; i < count; i++) { - rc = pm8xxx_readb(parent, base + i, &rtc_val[i]); - if (rc < 0) { - dev_err(rtc_dd->rtc_dev, "PMIC read failed\n"); - return rc; - } - } - - return 0; + return regmap_bulk_read(rtc_dd->regmap, base, rtc_val, count); } -static int pm8xxx_write_wrapper(struct pm8xxx_rtc *rtc_dd, u8 *rtc_val, - int base, int count) +static inline int pm8xxx_write_wrapper(struct pm8xxx_rtc *rtc_dd, + const u8 *rtc_val, + int base, int count) { - int i, rc; - struct device *parent = rtc_dd->rtc_dev->parent; - - for (i = 0; i < count; i++) { - rc = pm8xxx_writeb(parent, base + i, rtc_val[i]); - if (rc < 0) { - dev_err(rtc_dd->rtc_dev, "PMIC write failed\n"); - return rc; - } - } - - return 0; + return regmap_bulk_write(rtc_dd->regmap, base, rtc_val, count); } /* @@ -125,8 +107,8 @@ static int pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm) if (ctrl_reg & PM8xxx_RTC_ALARM_ENABLE) { alarm_enabled = 1; ctrl_reg &= ~PM8xxx_RTC_ALARM_ENABLE; - rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, - 1); + rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, + rtc_dd->rtc_control_reg, 1); if (rc < 0) { dev_err(dev, "Write to RTC control register " "failed\n"); @@ -161,8 +143,8 @@ static int pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm) if (alarm_enabled) { ctrl_reg |= PM8xxx_RTC_ALARM_ENABLE; - rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, - 1); + rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, + rtc_dd->rtc_control_reg, 1); if (rc < 0) { dev_err(dev, "Write to RTC control register " "failed\n"); @@ -255,7 +237,8 @@ static int pm8xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) ctrl_reg = alarm->enabled ? (ctrl_reg | PM8xxx_RTC_ALARM_ENABLE) : (ctrl_reg & ~PM8xxx_RTC_ALARM_ENABLE); - rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, 1); + rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, + rtc_dd->rtc_control_reg, 1); if (rc < 0) { dev_err(dev, "Write to RTC control register failed\n"); goto rtc_rw_fail; @@ -316,7 +299,8 @@ static int pm8xxx_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) ctrl_reg = (enable) ? (ctrl_reg | PM8xxx_RTC_ALARM_ENABLE) : (ctrl_reg & ~PM8xxx_RTC_ALARM_ENABLE); - rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, 1); + rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, + rtc_dd->rtc_control_reg, 1); if (rc < 0) { dev_err(dev, "Write to RTC control register failed\n"); goto rtc_rw_fail; @@ -351,7 +335,8 @@ static irqreturn_t pm8xxx_alarm_trigger(int irq, void *dev_id) ctrl_reg = rtc_dd->ctrl_reg; ctrl_reg &= ~PM8xxx_RTC_ALARM_ENABLE; - rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, 1); + rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, + rtc_dd->rtc_control_reg, 1); if (rc < 0) { spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags); dev_err(rtc_dd->rtc_dev, "Write to RTC control register " @@ -363,37 +348,105 @@ static irqreturn_t pm8xxx_alarm_trigger(int irq, void *dev_id) spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags); /* Clear RTC alarm register */ - rc = pm8xxx_read_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base + - PM8XXX_ALARM_CTRL_OFFSET, 1); + rc = rtc_dd->alarm_clear(rtc_dd); + if (rc < 0) + dev_err(rtc_dd->rtc_dev, "failed to clear RTC alarm\n"); + +rtc_alarm_handled: + return IRQ_HANDLED; +} + +static int pm8941_alarm_clear(struct pm8xxx_rtc *rtc_dd) +{ + u8 ctrl = PM8xxx_RTC_ALARM_CLEAR; + return pm8xxx_write_wrapper(rtc_dd, &ctrl, rtc_dd->alarm_ctrl2, 1); +} + +static int pm8941_chip_init(struct platform_device *pdev, + struct pm8xxx_rtc *rtc) +{ + u32 addr[2]; + int err; + + err = of_property_read_u32_array(pdev->dev.of_node, + "reg", addr, 2); + if (err || addr[0] > 0xFFFF || addr[1] > 0xFFFF) { + dev_err(&pdev->dev, "RTC IO resources absent or invalid\n"); + return err; + } + + rtc->alarm_clear = pm8941_alarm_clear; + + rtc->rtc_control_reg = addr[0] + 0x46; + rtc->rtc_write_base = addr[0] + 0x40; + rtc->rtc_read_base = addr[0] + 0x48; + rtc->alarm_rw_base = addr[1] + 0x40; + rtc->alarm_ctrl1 = addr[1] + 0x46; + rtc->alarm_ctrl2 = addr[1] + 0x48; + return 0; +} + +static int pm8xxx_alarm_clear(struct pm8xxx_rtc *rtc_dd) +{ + u8 ctrl_reg; + int rc; + + rc = pm8xxx_read_wrapper(rtc_dd, &ctrl_reg, rtc_dd->alarm_ctrl1, 1); if (rc < 0) { dev_err(rtc_dd->rtc_dev, "RTC Alarm control register read " "failed\n"); - goto rtc_alarm_handled; + return rc; } ctrl_reg &= ~PM8xxx_RTC_ALARM_CLEAR; - rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base + - PM8XXX_ALARM_CTRL_OFFSET, 1); - if (rc < 0) - dev_err(rtc_dd->rtc_dev, "Write to RTC Alarm control register" - " failed\n"); + return pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->alarm_ctrl1, 1); +} -rtc_alarm_handled: - return IRQ_HANDLED; +static int pm8xxx_chip_init(struct platform_device *pdev, + struct pm8xxx_rtc *rtc) +{ + const struct pm8xxx_rtc_platform_data *pdata = + dev_get_platdata(&pdev->dev); + struct resource *res; + + if (!pdata) { + dev_err(&pdev->dev, "Platform data not initialized.\n"); + return -ENXIO; + } + + if (pdata->rtc_write_enable) + pm8xxx_rtc_ops.set_time = pm8xxx_rtc_set_time; + + res = platform_get_resource_byname(pdev, IORESOURCE_IO, + "pmic_rtc_base"); + if (!res) { + dev_err(&pdev->dev, "RTC IO resource absent\n"); + return -ENXIO; + } + + rtc->rtc_control_reg = res->start; + rtc->rtc_write_base = res->start + 0x02; + rtc->rtc_read_base = res->start + 0x06; + rtc->alarm_rw_base = res->start + 0x0A; + rtc->alarm_ctrl1 = res->start + 0x01; + + rtc->alarm_clear = pm8xxx_alarm_clear; + return 0; } +static const struct of_device_id pm8xxx_rtc_idtable[] = { + { .compatible = "qcom,pm8941-rtc", pm8941_chip_init }, + {}, +}; +MODULE_DEVICE_TABLE(of, pm8xxx_rtc_idtable); + static int pm8xxx_rtc_probe(struct platform_device *pdev) { - int rc; - u8 ctrl_reg; - bool rtc_write_enable = false; + int (*chip_init)(struct platform_device *pdev, struct pm8xxx_rtc *rtc); + const struct device_node *node = pdev->dev.of_node; struct pm8xxx_rtc *rtc_dd; - struct resource *rtc_resource; - const struct pm8xxx_rtc_platform_data *pdata = - dev_get_platdata(&pdev->dev); - - if (pdata != NULL) - rtc_write_enable = pdata->rtc_write_enable; + u8 ctrl_reg; + int rc; rtc_dd = devm_kzalloc(&pdev->dev, sizeof(*rtc_dd), GFP_KERNEL); if (rtc_dd == NULL) { @@ -401,33 +454,40 @@ static int pm8xxx_rtc_probe(struct platform_device *pdev) return -ENOMEM; } - /* Initialise spinlock to protect RTC control register */ - spin_lock_init(&rtc_dd->ctrl_reg_lock); + chip_init = pm8xxx_chip_init; + if (node) { + const + struct of_device_id *id = of_match_node(pm8xxx_rtc_idtable, + pdev->dev.of_node); + if (id && id->data) + chip_init = id->data; + } - rtc_dd->rtc_alarm_irq = platform_get_irq(pdev, 0); - if (rtc_dd->rtc_alarm_irq < 0) { - dev_err(&pdev->dev, "Alarm IRQ resource absent!\n"); + rc = chip_init(pdev, rtc_dd); + if (rc) { + dev_err(&pdev->dev, "Failed to initialize chip.\n"); return -ENXIO; } - rtc_resource = platform_get_resource_byname(pdev, IORESOURCE_IO, - "pmic_rtc_base"); - if (!(rtc_resource && rtc_resource->start)) { - dev_err(&pdev->dev, "RTC IO resource absent!\n"); + rtc_dd->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!rtc_dd->regmap) { + dev_err(&pdev->dev, "Unable to get regmap.\n"); return -ENXIO; } - rtc_dd->rtc_base = rtc_resource->start; + /* Initialise spinlock to protect RTC control register */ + spin_lock_init(&rtc_dd->ctrl_reg_lock); - /* Setup RTC register addresses */ - rtc_dd->rtc_write_base = rtc_dd->rtc_base + PM8XXX_RTC_WRITE_OFFSET; - rtc_dd->rtc_read_base = rtc_dd->rtc_base + PM8XXX_RTC_READ_OFFSET; - rtc_dd->alarm_rw_base = rtc_dd->rtc_base + PM8XXX_ALARM_RW_OFFSET; + rtc_dd->rtc_alarm_irq = platform_get_irq(pdev, 0); + if (rtc_dd->rtc_alarm_irq < 0) { + dev_err(&pdev->dev, "Alarm IRQ resource absent!\n"); + return -ENXIO; + } rtc_dd->rtc_dev = &pdev->dev; /* Check if the RTC is on, else turn it on */ - rc = pm8xxx_read_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, 1); + rc = pm8xxx_read_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_control_reg, 1); if (rc < 0) { dev_err(&pdev->dev, "RTC control register read failed!\n"); return rc; @@ -435,8 +495,8 @@ static int pm8xxx_rtc_probe(struct platform_device *pdev) if (!(ctrl_reg & PM8xxx_RTC_ENABLE)) { ctrl_reg |= PM8xxx_RTC_ENABLE; - rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, rtc_dd->rtc_base, - 1); + rc = pm8xxx_write_wrapper(rtc_dd, &ctrl_reg, + rtc_dd->rtc_control_reg, 1); if (rc < 0) { dev_err(&pdev->dev, "Write to RTC control register " "failed\n"); @@ -444,10 +504,6 @@ static int pm8xxx_rtc_probe(struct platform_device *pdev) } } - rtc_dd->ctrl_reg = ctrl_reg; - if (rtc_write_enable == true) - pm8xxx_rtc_ops.set_time = pm8xxx_rtc_set_time; - platform_set_drvdata(pdev, rtc_dd); /* Register the RTC device */ @@ -516,6 +572,7 @@ static struct platform_driver pm8xxx_rtc_driver = { .name = PM8XXX_RTC_DEV_NAME, .owner = THIS_MODULE, .pm = &pm8xxx_rtc_pm_ops, + .of_match_table = pm8xxx_rtc_idtable, }, };