From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752771AbbGaJ7k (ORCPT ); Fri, 31 Jul 2015 05:59:40 -0400 Received: from mail-bl2on0055.outbound.protection.outlook.com ([65.55.169.55]:3680 "EHLO na01-bl2-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1751441AbbGaJ7f (ORCPT ); Fri, 31 Jul 2015 05:59:35 -0400 Authentication-Results: spf=pass (sender IP is 149.199.60.100) smtp.mailfrom=xilinx.com; towertech.it; dkim=none (message not signed) header.d=none; From: Suneel Garapati To: , , CC: , , Alessandro Zummo , Suneel Garapati Subject: [PATCH v2 2/2] drivers: rtc: add xilinx zynqmp rtc driver Date: Fri, 31 Jul 2015 15:29:17 +0530 Message-ID: <1438336757-23404-2-git-send-email-suneel.garapati@xilinx.com> X-Mailer: git-send-email 2.1.2 In-Reply-To: <1438336757-23404-1-git-send-email-suneel.garapati@xilinx.com> References: <1438336757-23404-1-git-send-email-suneel.garapati@xilinx.com> X-RCIS-Action: ALLOW X-TM-AS-Product-Ver: IMSS-7.1.0.1224-8.0.0.1202-21714.005 X-TM-AS-User-Approved-Sender: Yes;Yes X-EOPAttributedMessage: 0 X-Microsoft-Exchange-Diagnostics: 1;BY2FFO11FD048;1:JontzUJAQDbGeW/HKgWfP70L8k2lr4Ah2q4wteaWst7HdUR2OE5WcbLYsEB6WIFi9d5uC+S5QSAd4v4vP0vr7Kq1f4doatjKvWqsX/1yZ5s+iSxMcUXNbWB9YyhLKCzHq6qyaQ6QCgaUyxZ3MvcMxH1HkPEh26+ToPV9juo56cLPzIGWSeYcQR91jWMYdKAvgqiyE6hGq+C8gE+7CATqeCXKc5t4Qf1UiAf6Gfi3Em4os2zNBay9fGm8TLZrmB2p1FBGZpYrhVuCLoH6AlzKbRSE8ZZhMJnUSfL2mUjOyuGONwZI457sR4Ytb14/MOW7 X-Forefront-Antispam-Report: CIP:149.199.60.100;CTRY:US;IPV:NLI;EFV:NLI;SFV:NSPM;SFS:(10009020)(6009001)(2980300002)(438002)(199003)(189002)(107886002)(189998001)(47776003)(19580405001)(77096005)(5001960100002)(5001920100001)(50226001)(19580395003)(36756003)(6806004)(2201001)(76176999)(50986999)(229853001)(106466001)(87936001)(5001770100001)(48376002)(5003600100002)(50466002)(36386004)(92566002)(46102003)(33646002)(86362001)(77156002)(2950100001)(575784001)(15975445007)(63266004)(62966003)(107986001)(4001430100001)(2101003);DIR:OUT;SFP:1101;SCL:1;SRVR:BY2FFO11HUB019;H:xsj-pvapsmtpgw02;FPR:;SPF:Pass;MLV:sfv;A:1;MX:1;LANG:en; MIME-Version: 1.0 Content-Type: text/plain X-Microsoft-Exchange-Diagnostics: 1;BY2FFO11HUB019;2:r/mLNjRUiVcbkE5cYZxBxa6ZIxeAfiuoCZWFYE0t8H4W23ai+MQvF6RGn66IT0Z+LDb50U+PgybA4gZDmsVqUwXFqhhDDGP+qOS0CsclOmEf24bGJDomMcaK35yA1HR7kchPRSUZdregVRCFZze18eOR0kC7WGN1CYSeZYv/314=;3:MNDN1W+6Ht0EKGNu5cmEZyEqPN/BBdhhDD8UUsshbra5E7NHH5cPnq0jPeCM/8FPP2Sb+a/Xyni71elTLvi54C+DxdpNnOZGFawSYrE/Nk1tXLwh4HjnPjmOYAYVZVxoUKGB6yo7P5lX3vJpgWd+13tACl3jpcLcbUUMsS+g1ey4AnL67Dqk1aFE9p1NMPiBgZiXWBDT370WLxxDO7nRC1dsXDEoRa+b91drH32pNrutGJ+MiDJ0Sqo44kD4PlCT;25:mmKvp9vZI6DTe6YbcurZlr5hJ2I4/epUPNEVFqEZngeHEA2V5G9goj+KGZiRfIvJ+tEawcsI1GbheFEs6Ex8RLO5j4z+Bvi1xWVYO/S1exbBDHIbSFuGzzLiyFnqpayc+qnzTTxNls5WITmVnM/JNgrGVGkfaqqbHw1ZQx6pEKkK18SkGQvTreodu3VPLuZPOsK885w5VoW7bhWOf8itsbFLqhWGHhR2zzL4BooCAS7medRJs/nJ985MbZsctpFMw63FbI7FnFWzHs/34dd0qw== X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:BY2FFO11HUB019; X-Microsoft-Exchange-Diagnostics: 1;BY2FFO11HUB019;20:Mf5S2QXzwxjzLmE0NZE7dixR32tthA5HmkaUwZCULjnryCURR6FDzmJmzVpmDeB3c91B7FuolgeCZ6yQKLqT+OfDE655oRMNnu2GQuzJ+/9kBG9FotyF1wnjPX4L32rzEz8QAA15G2dX1U3Jj0Ng1RZsKRA7qIYwJYaSLj51s5vDpuRS/tmo+HOICbNSEyiRpdjU+zaGbnO6geGIi38+M8/0ae9TZH/4b6Qt1yWeXiJ0v/WbW5A08eWT+O+GwFP/zE6hNTSSfrTFJ5DhdUhvfFnmF+vYnFMV+iBXjXHVxlzR5IEzfdf+9MHFYCPfxje9MUyrnPDDg42ADtZbhSj4Pg+fvt30H02vo/iMUWVxSM4bhrT/XFNE5foLkXq2FpPuqkdnycjYq07ZW6piCe9m3QVpKKSnAHkrafFQ1l1r72rk2NeIMp7N/Xex+3BW9GuPAnRPnZ7Cumq1DQpGaxrPtUJAZ8sfeMPS9GuczZPMMRU1U4LSHwFDEFQnriUtLu/T;4:hm+S1JwoueE3tyzTL33eeXo1XloCr9sOBY23KYmJHnpe2rcJmcHHQIqz3ZwqPmjOd/i/T37YyDNSmkSBdfN3xyYuFHsOzL66jf48zvwMtICCcahMormuvTf6DO5/vYEhU0a1q76oIwVTAScRaQHfDUiOUptWarnUDNAxEUJsBGSdBfAkbYHVUL6TwpiahzPvtaTGU1kLsJs/EElurqW1AAQ2XZmc8j000Ko3jazSja4Btp9M19JaJZNupbgyVb3wKiOqfhWqMwV+zSzEEtRn1h6Fa/aIiNoNS9v32yZNmV8= BY2FFO11HUB019: X-MS-Exchange-Organization-RulesExecuted X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:; X-Exchange-Antispam-Report-CFA-Test: BCL:0;PCL:0;RULEID:(601004)(5005006)(3002001);SRVR:BY2FFO11HUB019;BCL:0;PCL:0;RULEID:;SRVR:BY2FFO11HUB019; X-Forefront-PRVS: 0654257CF5 X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1;BY2FFO11HUB019;23:b1u9EFF4+8j94+gHgwN6Dmm6EZGmhXz/ThVNZBz1?= =?us-ascii?Q?Hpqk4E/JakhYOY5P21g/zDZfI1jWRX2dmtIN1CPr+r2rMgNRhcsmRh4os8Vs?= =?us-ascii?Q?gxBKrPeh+AjtP4HDaE0pg17dOZz6kHF+XCnoRrYbMVnsI518HMs9UQzdvklp?= =?us-ascii?Q?ZSUhjs8Mbuj1qtv7blZnjdwsENnlJPzVATHlq7BxahYOiFWB02GF3mwydJHp?= =?us-ascii?Q?x2ndw6BA+QnFfpQC6MuxBdCNM6Rvy5MeXoqs+9VxyQp3Nkl6PLMeix0w2PF/?= =?us-ascii?Q?/ZxitLZ2gbBvZzrENMViSUTWII7UUpUC9Y6vtt4IM6Dh+lGPz/QtgDw6/0jj?= =?us-ascii?Q?Sekmnj6WGq9ubHFT9Tv6RfaH7lDRpvIihcZKPTiN71a3c4684SqxCV5M6pZc?= =?us-ascii?Q?B7vr24RY5rTxZIRb7ULpOHBWAnXncxxj8rflN9Vinx7SyoiGSUWmpz0d3agg?= =?us-ascii?Q?IHmr1y16liHmoitMrlbIVEDN4RIjQYZ+5bKrZmLMlW/1krm98egMtlJe2s9u?= =?us-ascii?Q?iiECmKQrSQA44xfhWhPlOrViYv0GHOvzasynjyFeJNj2dLsQWD1dunnJm4XY?= =?us-ascii?Q?QAcDYMYqcBvcITi4om8Z3FHXa8+mC4OlHzQNahNM9LdX0x6ZWa6w/Zk2H1Gn?= =?us-ascii?Q?pTQS3+AIfB5cId2b7R+9ltQVNuhW4dPs5vlBDWgTWY6Q7y4iBuxBpUYQ8i/v?= =?us-ascii?Q?0oco771gIrTgQSgUVfTiRE+lYNIKLJ1pa4NB/HAKIQ5MpY1puqg5CzwEAPj4?= =?us-ascii?Q?tQFjvS2nPyuMaAZISownxmVYK7pjjIxA13uavEh3lWSJU3oRqEsQjygi6r0Q?= =?us-ascii?Q?pEjONa7MssuL3rMIRjnfo79T9EqTystCSoFR8jZo6MlCQGNTqRyXI3GAsVJF?= =?us-ascii?Q?vw3FU0P7NY4q/bhfGf1veL6p9WUiqrdFn+i3Pi3MKpf1rG+kFHj/l6FtFNQJ?= =?us-ascii?Q?2Lq1BlMF2ai9vJXTbdmAkOLEaf3Yo9oCJtzRqLJGkbjILoHfpSp5qv5UWZ/M?= =?us-ascii?Q?2X0ErQx5UOsXBb2QOOSCW+VGazXKehrKMAvi3gCs39ZG0XglCMm1v1iLZgdy?= =?us-ascii?Q?5b45nRiLeIsknpwfwgFhEsH8lSp5?= X-Microsoft-Exchange-Diagnostics: 1;BY2FFO11HUB019;5:gGNyldPASmH6xZ8tnWmCDKMe5JSuVqZqni4WywaHqgrymEva25ZdDXrIlG0zWtro3DV9cBl72u3P8YZGrvuUt/IxxjdWCUCaArZPtBKoImxMZvOIuy9toCfPts225ADOi2dVJMW3hvBu42o1mIKCrQ==;24:L7CKVpI6im/AdGeGSfOQMPyNOzYrOGne1gx1iVMFrx8M/luzqFdrQ+kIbo5ih5rkqskox4gxLhjDCyyUxCqbu2yiRfIRokqFm8sCygUxyCU= X-OriginatorOrg: xilinx.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 31 Jul 2015 09:59:32.9516 (UTC) X-MS-Exchange-CrossTenant-Id: 657af505-d5df-48d0-8300-c31994686c5c X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=657af505-d5df-48d0-8300-c31994686c5c;Ip=[149.199.60.100];Helo=[xsj-pvapsmtpgw02] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: BY2FFO11HUB019 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org adds support for RTC controller found on Xilinx Zynq Ultrascale+ MPSoC platform. Signed-off-by: Suneel Garapati --- Changes v2 - change alm to alarm --- drivers/rtc/Kconfig | 7 ++ drivers/rtc/Makefile | 1 + drivers/rtc/rtc-zynqmp.c | 278 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 286 insertions(+) create mode 100644 drivers/rtc/rtc-zynqmp.c diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index e132ccb..e44705b 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1116,6 +1116,13 @@ config RTC_DRV_OPAL This driver can also be built as a module. If so, the module will be called rtc-opal. +config RTC_DRV_ZYNQMP + tristate "Xilinx Zynq Ultrascale+ MPSoC RTC" + depends on OF + help + If you say yes here you get support for the RTC controller found on + Xilinx Zynq Ultrascale+ MPSoC. + comment "on-CPU RTC drivers" config RTC_DRV_DAVINCI diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 2797384..e491eb5 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -159,3 +159,4 @@ obj-$(CONFIG_RTC_DRV_WM831X) += rtc-wm831x.o obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o obj-$(CONFIG_RTC_DRV_XGENE) += rtc-xgene.o +obj-$(CONFIG_RTC_DRV_ZYNQMP) += rtc-zynqmp.o diff --git a/drivers/rtc/rtc-zynqmp.c b/drivers/rtc/rtc-zynqmp.c new file mode 100644 index 0000000..e442746 --- /dev/null +++ b/drivers/rtc/rtc-zynqmp.c @@ -0,0 +1,278 @@ +/* + * Xilinx Zynq Ultrascale+ MPSoC Real Time Clock Driver + * + * Copyright (C) 2015 Xilinx, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include + +/* RTC Registers */ +#define RTC_SET_TM_WR 0x00 +#define RTC_SET_TM_RD 0x04 +#define RTC_CALIB_WR 0x08 +#define RTC_CALIB_RD 0x0C +#define RTC_CUR_TM 0x10 +#define RTC_CUR_TICK 0x14 +#define RTC_ALRM 0x18 +#define RTC_INT_STS 0x20 +#define RTC_INT_MASK 0x24 +#define RTC_INT_EN 0x28 +#define RTC_INT_DIS 0x2C +#define RTC_CTRL 0x40 + +#define RTC_FR_EN BIT(20) +#define RTC_FR_DATSHIFT 16 +#define RTC_TICK_MASK 0xFFFF +#define RTC_INT_SEC BIT(0) +#define RTC_INT_ALRM BIT(1) +#define RTC_OSC_EN BIT(24) + +#define RTC_CALIB_DEF 0x198233 +#define RTC_CALIB_MASK 0x1FFFFF + +struct xlnx_rtc_dev { + struct rtc_device *rtc; + void __iomem *reg_base; + int alarm_irq; + int sec_irq; +}; + +static int xlnx_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev); + unsigned long new_time; + + rtc_tm_to_time(tm, &new_time); + writel(new_time, xrtcdev->reg_base + RTC_SET_TM_WR); + + return 0; +} + +static int xlnx_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev); + + rtc_time_to_tm(readl(xrtcdev->reg_base + RTC_CUR_TM), tm); + + return rtc_valid_tm(tm); +} + +static int xlnx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev); + + rtc_time_to_tm(readl(xrtcdev->reg_base + RTC_ALRM), &alrm->time); + alrm->enabled = readl(xrtcdev->reg_base + RTC_INT_MASK) & RTC_INT_ALRM; + + return 0; +} + +static int xlnx_rtc_alarm_irq_enable(struct device *dev, u32 enabled) +{ + struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev); + + if (enabled) + writel(RTC_INT_ALRM, xrtcdev->reg_base + RTC_INT_EN); + else + writel(RTC_INT_ALRM, xrtcdev->reg_base + RTC_INT_DIS); + + return 0; +} + +static int xlnx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev); + unsigned long alarm_time; + + rtc_tm_to_time(&alrm->time, &alarm_time); + + writel((u32) alarm_time, (xrtcdev->reg_base + RTC_ALRM)); + + xlnx_rtc_alarm_irq_enable(dev, alrm->enabled); + + return 0; +} + +static void xlnx_init_rtc(struct xlnx_rtc_dev *xrtcdev, u32 calibval) +{ + /* + * Based on crystal freq of 33.330 KHz + * set the secounds counter and enable, set fractions counter + * to default value suggested as per design spec + * to correct RTC delay in frequency over period of time. + */ + calibval &= RTC_CALIB_MASK; + writel(calibval, (xrtcdev->reg_base + RTC_CALIB_WR)); +} + +static const struct rtc_class_ops xlnx_rtc_ops = { + .set_time = xlnx_rtc_set_time, + .read_time = xlnx_rtc_read_time, + .read_alarm = xlnx_rtc_read_alarm, + .set_alarm = xlnx_rtc_set_alarm, + .alarm_irq_enable = xlnx_rtc_alarm_irq_enable, +}; + +static irqreturn_t xlnx_rtc_interrupt(int irq, void *id) +{ + struct xlnx_rtc_dev *xrtcdev = (struct xlnx_rtc_dev *) id; + unsigned int status; + + status = readl(xrtcdev->reg_base + RTC_INT_STS); + /* Check if interrupt asserted */ + if (!(status & (RTC_INT_SEC | RTC_INT_ALRM))) + return IRQ_NONE; + + /* Clear interrupt */ + writel(status, xrtcdev->reg_base + RTC_INT_STS); + + if (status & RTC_INT_SEC) + rtc_update_irq(xrtcdev->rtc, 1, RTC_IRQF | RTC_UF); + if (status & RTC_INT_ALRM) + rtc_update_irq(xrtcdev->rtc, 1, RTC_IRQF | RTC_AF); + if (status & RTC_INT_ALRM) + printk("alarm interrupt\n"); + return IRQ_HANDLED; +} + +static int xlnx_rtc_probe(struct platform_device *pdev) +{ + struct xlnx_rtc_dev *xrtcdev; + struct resource *res; + int ret; + unsigned int calibvalue; + + xrtcdev = devm_kzalloc(&pdev->dev, sizeof(*xrtcdev), GFP_KERNEL); + if (!xrtcdev) + return -ENOMEM; + + platform_set_drvdata(pdev, xrtcdev); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + xrtcdev->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(xrtcdev->reg_base)) + return PTR_ERR(xrtcdev->reg_base); + + xrtcdev->alarm_irq = platform_get_irq_byname(pdev, "alarm"); + if (xrtcdev->alarm_irq < 0) { + dev_err(&pdev->dev, "no irq resource\n"); + return xrtcdev->alarm_irq; + } + ret = devm_request_irq(&pdev->dev, xrtcdev->alarm_irq, + xlnx_rtc_interrupt, 0, + dev_name(&pdev->dev), xrtcdev); + if (ret) { + dev_err(&pdev->dev, "request irq failed\n"); + return ret; + } + + xrtcdev->sec_irq = platform_get_irq_byname(pdev, "sec"); + if (xrtcdev->sec_irq < 0) { + dev_err(&pdev->dev, "no irq resource\n"); + return xrtcdev->sec_irq; + } + ret = devm_request_irq(&pdev->dev, xrtcdev->sec_irq, + xlnx_rtc_interrupt, 0, + dev_name(&pdev->dev), xrtcdev); + if (ret) { + dev_err(&pdev->dev, "request irq failed\n"); + return ret; + } + + ret = of_property_read_u32(pdev->dev.of_node, "calibration", + &calibvalue); + if (ret) + calibvalue = RTC_CALIB_DEF; + + xlnx_init_rtc(xrtcdev, calibvalue); + + device_init_wakeup(&pdev->dev, 1); + + xrtcdev->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, + &xlnx_rtc_ops, THIS_MODULE); + if (IS_ERR(xrtcdev->rtc)) { + return PTR_ERR(xrtcdev->rtc); + } + + return 0; +} + +static int xlnx_rtc_remove(struct platform_device *pdev) +{ + xlnx_rtc_alarm_irq_enable(&pdev->dev, 0); + device_init_wakeup(&pdev->dev, 0); + + return 0; +} + +static int __maybe_unused xlnx_rtc_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct xlnx_rtc_dev *xrtcdev = platform_get_drvdata(pdev); + + if (device_may_wakeup(&pdev->dev)) { + enable_irq_wake(xrtcdev->alarm_irq); + } else { + xlnx_rtc_alarm_irq_enable(dev, 0); + } + + return 0; +} + +static int __maybe_unused xlnx_rtc_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct xlnx_rtc_dev *xrtcdev = platform_get_drvdata(pdev); + + if (device_may_wakeup(&pdev->dev)) { + disable_irq_wake(xrtcdev->alarm_irq); + } else { + xlnx_rtc_alarm_irq_enable(dev, 1); + } + + return 0; +} + +static SIMPLE_DEV_PM_OPS(xlnx_rtc_pm_ops, xlnx_rtc_suspend, xlnx_rtc_resume); + +static const struct of_device_id xlnx_rtc_of_match[] = { + {.compatible = "xlnx,zynqmp-rtc" }, + { } +}; +MODULE_DEVICE_TABLE(of, xlnx_rtc_of_match); + +static struct platform_driver xlnx_rtc_driver = { + .probe = xlnx_rtc_probe, + .remove = xlnx_rtc_remove, + .driver = { + .name = KBUILD_MODNAME, + .pm = &xlnx_rtc_pm_ops, + .of_match_table = xlnx_rtc_of_match, + }, +}; + +module_platform_driver(xlnx_rtc_driver); + +MODULE_DESCRIPTION("Xilinx Zynq MPSoC RTC driver"); +MODULE_AUTHOR("Xilinx Inc."); +MODULE_LICENSE("GPL v2"); -- 2.1.2