From patchwork Fri Sep 20 12:30:14 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laxman Dewangan X-Patchwork-Id: 276389 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from mail-pb0-x23d.google.com (mail-pb0-x23d.google.com [IPv6:2607:f8b0:400e:c01::23d]) (using TLSv1 with cipher ECDHE-RSA-RC4-SHA (128/128 bits)) (Client CN "smtp.gmail.com", Issuer "Google Internet Authority" (not verified)) by ozlabs.org (Postfix) with ESMTPS id 2AE272C026C for ; Fri, 20 Sep 2013 22:09:15 +1000 (EST) Received: by mail-pb0-f61.google.com with SMTP id jt11sf87586pbb.6 for ; Fri, 20 Sep 2013 05:09:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlegroups.com; s=20120806; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:x-original-sender:x-original-authentication-results :reply-to:precedence:mailing-list:list-id:list-post:list-help :list-archive:sender:list-subscribe:list-unsubscribe:content-type; bh=S4h5wR/6LqnnCH4f0hLJ7+fpZiYFkCwPXOZb3BYhAqs=; b=DAWRQyZlivly+TvlEOt/fzEvlFCc5Qisv/wHzKtoPhBDZ1zE6cCEftV1XdNh9PbKb+ frhH0L08O+a6SEF1GeCQ1+tf6DIlTpDBvtNBGuSGXmu+9OQRHYByeJJ6Imh8pU+fjZiY OjOOGqwCpvFN1WNbgkT5Sol0BwzDdzUSU8z8KKBQ8VvonctkO0snBUR0hY7Yc4bpeTCE Q09SX3tGGC3TVRETMWYWFLtbTJMRs0EWFctVmUMaGKUXWnfHXYnAmbOymNq1U9lSuS+n /OIksjS2cBDUhgiUD/7XCmqXLv9IjKsXN6osSokyzBY1pN8y3K9uQLQjiYXvGdExFvtz drYw== X-Received: by 10.49.3.134 with SMTP id c6mr279843qec.0.1379678954066; Fri, 20 Sep 2013 05:09:14 -0700 (PDT) X-BeenThere: rtc-linux@googlegroups.com Received: by 10.49.71.83 with SMTP id s19ls207913qeu.7.gmail; Fri, 20 Sep 2013 05:09:13 -0700 (PDT) X-Received: by 10.236.90.67 with SMTP id d43mr2440737yhf.36.1379678953817; Fri, 20 Sep 2013 05:09:13 -0700 (PDT) Received: from hqemgate16.nvidia.com (hqemgate16.nvidia.com. [216.228.121.65]) by gmr-mx.google.com with ESMTPS id a42si1426123yhj.6.1969.12.31.16.00.00 (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Fri, 20 Sep 2013 05:09:13 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of ldewangan@nvidia.com designates 216.228.121.65 as permitted sender) client-ip=216.228.121.65; Received: from hqnvupgp07.nvidia.com (Not Verified[216.228.121.13]) by hqemgate16.nvidia.com id ; Fri, 20 Sep 2013 05:08:59 -0700 Received: from hqemhub02.nvidia.com ([172.20.12.94]) by hqnvupgp07.nvidia.com (PGP Universal service); Fri, 20 Sep 2013 05:09:12 -0700 X-PGP-Universal: processed; by hqnvupgp07.nvidia.com on Fri, 20 Sep 2013 05:09:12 -0700 Received: from hqnvemgw01.nvidia.com (172.20.150.20) by hqemhub02.nvidia.com (172.20.150.31) with Microsoft SMTP Server id 8.3.327.1; Fri, 20 Sep 2013 05:09:12 -0700 Received: from thelma.nvidia.com (Not Verified[172.16.212.77]) by hqnvemgw01.nvidia.com with MailMarshal (v7,1,2,5326) id ; Fri, 20 Sep 2013 05:09:12 -0700 Received: from ldewangan-ubuntu.nvidia.com (dhcp-10-19-65-30.nvidia.com [10.19.65.30]) by thelma.nvidia.com (8.13.8+Sun/8.8.8) with ESMTP id r8KC8cgs011710; Fri, 20 Sep 2013 05:09:07 -0700 (PDT) From: Laxman Dewangan To: , , , , CC: , , , , , , , , , , , , Laxman Dewangan , Florian Lobmaier Subject: [rtc-linux] [PATCH V2 5/5] drivers/rtc/rtc-as3722: add RTC driver Date: Fri, 20 Sep 2013 18:00:14 +0530 Message-ID: <1379680214-9143-6-git-send-email-ldewangan@nvidia.com> X-Mailer: git-send-email 1.7.1.1 In-Reply-To: <1379680214-9143-1-git-send-email-ldewangan@nvidia.com> References: <1379680214-9143-1-git-send-email-ldewangan@nvidia.com> MIME-Version: 1.0 X-Original-Sender: ldewangan@nvidia.com X-Original-Authentication-Results: gmr-mx.google.com; spf=pass (google.com: best guess record for domain of ldewangan@nvidia.com designates 216.228.121.65 as permitted sender) smtp.mail=ldewangan@nvidia.com Reply-To: rtc-linux@googlegroups.com Precedence: list Mailing-list: list rtc-linux@googlegroups.com; contact rtc-linux+owners@googlegroups.com List-ID: X-Google-Group-Id: 712029733259 List-Post: , List-Help: , List-Archive: Sender: rtc-linux@googlegroups.com List-Subscribe: , List-Unsubscribe: , The AMS AS3722 is a compact system PMU suitable for mobile phones, tablets etc. Add a driver to support accessing the RTC found on the AMS AS3722 PMIC using RTC framework. Signed-off-by: Laxman Dewangan Signed-off-by: Florian Lobmaier --- Changes from V1: - Get rid of clk32k out configuration from RTC. Will add clock driver. drivers/rtc/Kconfig | 10 ++ drivers/rtc/Makefile | 1 + drivers/rtc/rtc-as3722.c | 296 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 307 insertions(+), 0 deletions(-) create mode 100644 drivers/rtc/rtc-as3722.c diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 9654aa3..3a17788 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -153,6 +153,16 @@ config RTC_DRV_88PM80X This driver can also be built as a module. If so, the module will be called rtc-88pm80x. +config RTC_DRV_AS3722 + tristate "AMS AS3722 RTC driver" + depends on MFD_AS3722 + help + If you say yes here you get support for the RTC of AMS AS3722 PMIC + chips. + + This driver can also be built as a module. If so, the module + will be called rtc-as3722. + config RTC_DRV_DS1307 tristate "Dallas/Maxim DS1307/37/38/39/40, ST M41T00, EPSON RX-8025" help diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 2dff3d2..fdb5764 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_RTC_DRV_88PM860X) += rtc-88pm860x.o obj-$(CONFIG_RTC_DRV_88PM80X) += rtc-88pm80x.o obj-$(CONFIG_RTC_DRV_AB3100) += rtc-ab3100.o obj-$(CONFIG_RTC_DRV_AB8500) += rtc-ab8500.o +obj-$(CONFIG_RTC_DRV_AS3722) += rtc-as3722.o obj-$(CONFIG_RTC_DRV_AT32AP700X)+= rtc-at32ap700x.o obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o diff --git a/drivers/rtc/rtc-as3722.c b/drivers/rtc/rtc-as3722.c new file mode 100644 index 0000000..430a927 --- /dev/null +++ b/drivers/rtc/rtc-as3722.c @@ -0,0 +1,296 @@ +/* + * rtc-as3722.c - Real Time Clock driver for ams AS3722 PMICs + * + * Copyright (C) 2013 ams AG + * Copyright (c) 2013, NVIDIA Corporation. All rights reserved. + * + * Author: Florian Lobmaier + * Author: Laxman Dewangan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AS3722_RTC_START_YEAR 2000 +struct as3722_rtc { + struct rtc_device *rtc; + struct device *dev; + struct as3722 *as3722; + int alarm_irq; + bool irq_enable; +}; + +static void as3722_time_to_reg(u8 *rbuff, struct rtc_time *tm) +{ + rbuff[0] = bin2bcd(tm->tm_sec); + rbuff[1] = bin2bcd(tm->tm_min); + rbuff[2] = bin2bcd(tm->tm_hour); + rbuff[3] = bin2bcd(tm->tm_mday); + rbuff[4] = bin2bcd(tm->tm_mon); + rbuff[5] = bin2bcd(tm->tm_year - (AS3722_RTC_START_YEAR - 1900)); +} + +static void as3722_reg_to_time(u8 *rbuff, struct rtc_time *tm) +{ + tm->tm_sec = bcd2bin(rbuff[0] & 0x7F); + tm->tm_min = bcd2bin(rbuff[1] & 0x7F); + tm->tm_hour = bcd2bin(rbuff[2] & 0x3F); + tm->tm_mday = bcd2bin(rbuff[3] & 0x3F); + tm->tm_mon = bcd2bin(rbuff[4] & 0x1F); + tm->tm_year = (AS3722_RTC_START_YEAR - 1900) + bcd2bin(rbuff[5] & 0x7F); + return; +} + +static int as3722_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct as3722_rtc *as3722_rtc = dev_get_drvdata(dev); + struct as3722 *as3722 = as3722_rtc->as3722; + u8 as_time_array[6]; + int ret; + + ret = as3722_block_read(as3722, AS3722_RTC_SECOND_REG, + 6, as_time_array); + if (ret < 0) { + dev_err(dev, "RTC_SECOND reg block read failed %d\n", ret); + return ret; + } + as3722_reg_to_time(as_time_array, tm); + return 0; +} + +static int as3722_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct as3722_rtc *as3722_rtc = dev_get_drvdata(dev); + struct as3722 *as3722 = as3722_rtc->as3722; + u8 as_time_array[6]; + int ret; + + if (tm->tm_year < (AS3722_RTC_START_YEAR - 1900)) + return -EINVAL; + + as3722_time_to_reg(as_time_array, tm); + ret = as3722_block_write(as3722, AS3722_RTC_SECOND_REG, 6, + as_time_array); + if (ret < 0) + dev_err(dev, "RTC_SECOND reg block write failed %d\n", ret); + return ret; +} + +static int as3722_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct as3722_rtc *as3722_rtc = dev_get_drvdata(dev); + + if (enabled && !as3722_rtc->irq_enable) { + enable_irq(as3722_rtc->alarm_irq); + as3722_rtc->irq_enable = true; + } else if (!enabled && as3722_rtc->irq_enable) { + disable_irq(as3722_rtc->alarm_irq); + as3722_rtc->irq_enable = false; + } + return 0; +} + +static int as3722_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct as3722_rtc *as3722_rtc = dev_get_drvdata(dev); + struct as3722 *as3722 = as3722_rtc->as3722; + u8 as_time_array[6]; + int ret; + + ret = as3722_block_read(as3722, AS3722_RTC_ALARM_SECOND_REG, 6, + as_time_array); + if (ret < 0) { + dev_err(dev, "RTC_ALARM_SECOND block read failed %d\n", ret); + return ret; + } + + as3722_reg_to_time(as_time_array, &alrm->time); + return 0; +} + +static int as3722_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct as3722_rtc *as3722_rtc = dev_get_drvdata(dev); + struct as3722 *as3722 = as3722_rtc->as3722; + u8 as_time_array[6]; + int ret; + + if (alrm->time.tm_year < (AS3722_RTC_START_YEAR - 1900)) + return -EINVAL; + + ret = as3722_rtc_alarm_irq_enable(dev, 0); + if (ret < 0) { + dev_err(dev, "Disable RTC alarm failed\n"); + return ret; + } + + as3722_time_to_reg(as_time_array, &alrm->time); + ret = as3722_block_write(as3722, AS3722_RTC_ALARM_SECOND_REG, 6, + as_time_array); + if (ret < 0) { + dev_err(dev, "RTC_ALARM_SECOND block write failed %d\n", ret); + return ret; + } + + if (alrm->enabled) + ret = as3722_rtc_alarm_irq_enable(dev, alrm->enabled); + return ret; +} + +static irqreturn_t as3722_alarm_irq(int irq, void *data) +{ + struct as3722_rtc *as3722_rtc = data; + + rtc_update_irq(as3722_rtc->rtc, 1, RTC_IRQF | RTC_AF); + return IRQ_HANDLED; +} + +static const struct rtc_class_ops as3722_rtc_ops = { + .read_time = as3722_rtc_read_time, + .set_time = as3722_rtc_set_time, + .read_alarm = as3722_rtc_read_alarm, + .set_alarm = as3722_rtc_set_alarm, + .alarm_irq_enable = as3722_rtc_alarm_irq_enable, +}; + +static int as3722_rtc_probe(struct platform_device *pdev) +{ + struct as3722 *as3722 = dev_get_drvdata(pdev->dev.parent); + struct as3722_rtc *as3722_rtc; + int val; + int ret; + + as3722_rtc = devm_kzalloc(&pdev->dev, sizeof(*as3722_rtc), GFP_KERNEL); + if (!as3722_rtc) + return -ENOMEM; + + as3722_rtc->as3722 = as3722; + as3722_rtc->dev = &pdev->dev; + platform_set_drvdata(pdev, as3722_rtc); + + /* Enable the RTC to make sure it is running. */ + ret = as3722_update_bits(as3722, AS3722_RTC_CONTROL_REG, + AS3722_RTC_ON, AS3722_RTC_ON); + if (ret < 0) { + dev_err(&pdev->dev, "RTC_CONTROL reg update failed: %d\n", ret); + return ret; + } + + val = AS3722_RTC_ON | AS3722_RTC_ALARM_WAKEUP_EN; + ret = as3722_write(as3722, AS3722_RTC_CONTROL_REG, val); + if (ret < 0) { + dev_err(&pdev->dev, "RTC_CONTROL reg write failed: %d\n", ret); + return ret; + } + + device_init_wakeup(&pdev->dev, 1); + + as3722_rtc->rtc = rtc_device_register("as3722", &pdev->dev, + &as3722_rtc_ops, THIS_MODULE); + if (IS_ERR(as3722_rtc->rtc)) { + ret = PTR_ERR(as3722_rtc->rtc); + dev_err(&pdev->dev, "RTC register failed: %d\n", ret); + return ret; + } + + as3722_rtc->alarm_irq = platform_get_irq(pdev, 0); + dev_info(&pdev->dev, "RTC interrupt %d\n", as3722_rtc->alarm_irq); + + ret = request_threaded_irq(as3722_rtc->alarm_irq, NULL, + as3722_alarm_irq, IRQF_ONESHOT | IRQF_EARLY_RESUME, + "rtc-alarm", as3722_rtc); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to request alarm IRQ %d: %d\n", + as3722_rtc->alarm_irq, ret); + goto scrub; + } + disable_irq(as3722_rtc->alarm_irq); + return 0; +scrub: + rtc_device_unregister(as3722_rtc->rtc); + return ret; +} + +static int as3722_rtc_remove(struct platform_device *pdev) +{ + struct as3722_rtc *as3722_rtc = platform_get_drvdata(pdev); + + free_irq(as3722_rtc->alarm_irq, as3722_rtc); + rtc_device_unregister(as3722_rtc->rtc); + return 0; +} + +#ifdef CONFIG_PM +static int as3722_rtc_suspend(struct device *dev) +{ + struct as3722_rtc *as3722_rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(as3722_rtc->alarm_irq); + + return 0; +} + +static int as3722_rtc_resume(struct device *dev) +{ + struct as3722_rtc *as3722_rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(as3722_rtc->alarm_irq); + return 0; +} +#endif + +static const struct dev_pm_ops as3722_rtc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(as3722_rtc_suspend, as3722_rtc_resume) +}; + +static const struct of_device_id of_as3722_rtc_match[] = { + { .compatible = "ams,as3722-rtc"}, + { }, +}; +MODULE_DEVICE_TABLE(of, of_as3722_rtc_match); + +static struct platform_driver as3722_rtc_driver = { + .probe = as3722_rtc_probe, + .remove = as3722_rtc_remove, + .driver = { + .name = "as3722-rtc", + .pm = &as3722_rtc_pm_ops, + .of_match_table = of_as3722_rtc_match, + }, +}; +module_platform_driver(as3722_rtc_driver); + +MODULE_DESCRIPTION("RTC driver for AS3722 PMICs"); +MODULE_ALIAS("platform:as3722-rtc"); +MODULE_AUTHOR("Florian Lobmaier "); +MODULE_AUTHOR("Laxman Dewangan "); +MODULE_LICENSE("GPL");