13a205b9dSAlexandre Belloni // SPDX-License-Identifier: GPL-2.0+
2f12d8695SLoc Ho /*
3f12d8695SLoc Ho * APM X-Gene SoC Real Time Clock Driver
4f12d8695SLoc Ho *
5f12d8695SLoc Ho * Copyright (c) 2014, Applied Micro Circuits Corporation
6f12d8695SLoc Ho * Author: Rameshwar Prasad Sahu <rsahu@apm.com>
7f12d8695SLoc Ho * Loc Ho <lho@apm.com>
8f12d8695SLoc Ho */
9f12d8695SLoc Ho
10db785341SAlexandre Belloni #include <linux/clk.h>
11db785341SAlexandre Belloni #include <linux/delay.h>
12f12d8695SLoc Ho #include <linux/init.h>
13db785341SAlexandre Belloni #include <linux/io.h>
14f12d8695SLoc Ho #include <linux/module.h>
15f12d8695SLoc Ho #include <linux/of.h>
16f12d8695SLoc Ho #include <linux/platform_device.h>
17f12d8695SLoc Ho #include <linux/rtc.h>
18db785341SAlexandre Belloni #include <linux/slab.h>
19f12d8695SLoc Ho
20f12d8695SLoc Ho /* RTC CSR Registers */
21f12d8695SLoc Ho #define RTC_CCVR 0x00
22f12d8695SLoc Ho #define RTC_CMR 0x04
23f12d8695SLoc Ho #define RTC_CLR 0x08
24f12d8695SLoc Ho #define RTC_CCR 0x0C
25f12d8695SLoc Ho #define RTC_CCR_IE BIT(0)
26f12d8695SLoc Ho #define RTC_CCR_MASK BIT(1)
27f12d8695SLoc Ho #define RTC_CCR_EN BIT(2)
28f12d8695SLoc Ho #define RTC_CCR_WEN BIT(3)
29f12d8695SLoc Ho #define RTC_STAT 0x10
30f12d8695SLoc Ho #define RTC_STAT_BIT BIT(0)
31f12d8695SLoc Ho #define RTC_RSTAT 0x14
32f12d8695SLoc Ho #define RTC_EOI 0x18
33f12d8695SLoc Ho #define RTC_VER 0x1C
34f12d8695SLoc Ho
35f12d8695SLoc Ho struct xgene_rtc_dev {
36f12d8695SLoc Ho struct rtc_device *rtc;
37f12d8695SLoc Ho void __iomem *csr_base;
38f12d8695SLoc Ho struct clk *clk;
39f12d8695SLoc Ho unsigned int irq_wake;
40d0bcd82bSLoc Ho unsigned int irq_enabled;
41f12d8695SLoc Ho };
42f12d8695SLoc Ho
xgene_rtc_read_time(struct device * dev,struct rtc_time * tm)43f12d8695SLoc Ho static int xgene_rtc_read_time(struct device *dev, struct rtc_time *tm)
44f12d8695SLoc Ho {
45f12d8695SLoc Ho struct xgene_rtc_dev *pdata = dev_get_drvdata(dev);
46f12d8695SLoc Ho
4743f327faSAlexandre Belloni rtc_time64_to_tm(readl(pdata->csr_base + RTC_CCVR), tm);
48ab62670eSAlexandre Belloni return 0;
49f12d8695SLoc Ho }
50f12d8695SLoc Ho
xgene_rtc_set_time(struct device * dev,struct rtc_time * tm)5158f88915SAlexandre Belloni static int xgene_rtc_set_time(struct device *dev, struct rtc_time *tm)
52f12d8695SLoc Ho {
53f12d8695SLoc Ho struct xgene_rtc_dev *pdata = dev_get_drvdata(dev);
54f12d8695SLoc Ho
55f12d8695SLoc Ho /*
56f12d8695SLoc Ho * NOTE: After the following write, the RTC_CCVR is only reflected
57f12d8695SLoc Ho * after the update cycle of 1 seconds.
58f12d8695SLoc Ho */
5958f88915SAlexandre Belloni writel((u32)rtc_tm_to_time64(tm), pdata->csr_base + RTC_CLR);
60f12d8695SLoc Ho readl(pdata->csr_base + RTC_CLR); /* Force a barrier */
61f12d8695SLoc Ho
62f12d8695SLoc Ho return 0;
63f12d8695SLoc Ho }
64f12d8695SLoc Ho
xgene_rtc_read_alarm(struct device * dev,struct rtc_wkalrm * alrm)65f12d8695SLoc Ho static int xgene_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
66f12d8695SLoc Ho {
67f12d8695SLoc Ho struct xgene_rtc_dev *pdata = dev_get_drvdata(dev);
68f12d8695SLoc Ho
699a842a7eSAlexandre Belloni /* If possible, CMR should be read here */
7043f327faSAlexandre Belloni rtc_time64_to_tm(0, &alrm->time);
71f12d8695SLoc Ho alrm->enabled = readl(pdata->csr_base + RTC_CCR) & RTC_CCR_IE;
72f12d8695SLoc Ho
73f12d8695SLoc Ho return 0;
74f12d8695SLoc Ho }
75f12d8695SLoc Ho
xgene_rtc_alarm_irq_enable(struct device * dev,u32 enabled)76f12d8695SLoc Ho static int xgene_rtc_alarm_irq_enable(struct device *dev, u32 enabled)
77f12d8695SLoc Ho {
78f12d8695SLoc Ho struct xgene_rtc_dev *pdata = dev_get_drvdata(dev);
79f12d8695SLoc Ho u32 ccr;
80f12d8695SLoc Ho
81f12d8695SLoc Ho ccr = readl(pdata->csr_base + RTC_CCR);
82f12d8695SLoc Ho if (enabled) {
83f12d8695SLoc Ho ccr &= ~RTC_CCR_MASK;
84f12d8695SLoc Ho ccr |= RTC_CCR_IE;
85f12d8695SLoc Ho } else {
86f12d8695SLoc Ho ccr &= ~RTC_CCR_IE;
87f12d8695SLoc Ho ccr |= RTC_CCR_MASK;
88f12d8695SLoc Ho }
89f12d8695SLoc Ho writel(ccr, pdata->csr_base + RTC_CCR);
90f12d8695SLoc Ho
91f12d8695SLoc Ho return 0;
92f12d8695SLoc Ho }
93f12d8695SLoc Ho
xgene_rtc_alarm_irq_enabled(struct device * dev)94d0bcd82bSLoc Ho static int xgene_rtc_alarm_irq_enabled(struct device *dev)
95d0bcd82bSLoc Ho {
96d0bcd82bSLoc Ho struct xgene_rtc_dev *pdata = dev_get_drvdata(dev);
97d0bcd82bSLoc Ho
98d0bcd82bSLoc Ho return readl(pdata->csr_base + RTC_CCR) & RTC_CCR_IE ? 1 : 0;
99d0bcd82bSLoc Ho }
100d0bcd82bSLoc Ho
xgene_rtc_set_alarm(struct device * dev,struct rtc_wkalrm * alrm)101f12d8695SLoc Ho static int xgene_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
102f12d8695SLoc Ho {
103f12d8695SLoc Ho struct xgene_rtc_dev *pdata = dev_get_drvdata(dev);
104f12d8695SLoc Ho
10543f327faSAlexandre Belloni writel((u32)rtc_tm_to_time64(&alrm->time), pdata->csr_base + RTC_CMR);
106f12d8695SLoc Ho
107f12d8695SLoc Ho xgene_rtc_alarm_irq_enable(dev, alrm->enabled);
108f12d8695SLoc Ho
109f12d8695SLoc Ho return 0;
110f12d8695SLoc Ho }
111f12d8695SLoc Ho
112f12d8695SLoc Ho static const struct rtc_class_ops xgene_rtc_ops = {
113f12d8695SLoc Ho .read_time = xgene_rtc_read_time,
11458f88915SAlexandre Belloni .set_time = xgene_rtc_set_time,
115f12d8695SLoc Ho .read_alarm = xgene_rtc_read_alarm,
116f12d8695SLoc Ho .set_alarm = xgene_rtc_set_alarm,
117f12d8695SLoc Ho .alarm_irq_enable = xgene_rtc_alarm_irq_enable,
118f12d8695SLoc Ho };
119f12d8695SLoc Ho
xgene_rtc_interrupt(int irq,void * id)120f12d8695SLoc Ho static irqreturn_t xgene_rtc_interrupt(int irq, void *id)
121f12d8695SLoc Ho {
122db785341SAlexandre Belloni struct xgene_rtc_dev *pdata = id;
123f12d8695SLoc Ho
124f12d8695SLoc Ho /* Check if interrupt asserted */
125f12d8695SLoc Ho if (!(readl(pdata->csr_base + RTC_STAT) & RTC_STAT_BIT))
126f12d8695SLoc Ho return IRQ_NONE;
127f12d8695SLoc Ho
128f12d8695SLoc Ho /* Clear interrupt */
129f12d8695SLoc Ho readl(pdata->csr_base + RTC_EOI);
130f12d8695SLoc Ho
131f12d8695SLoc Ho rtc_update_irq(pdata->rtc, 1, RTC_IRQF | RTC_AF);
132f12d8695SLoc Ho
133f12d8695SLoc Ho return IRQ_HANDLED;
134f12d8695SLoc Ho }
135f12d8695SLoc Ho
xgene_rtc_probe(struct platform_device * pdev)136f12d8695SLoc Ho static int xgene_rtc_probe(struct platform_device *pdev)
137f12d8695SLoc Ho {
138f12d8695SLoc Ho struct xgene_rtc_dev *pdata;
139f12d8695SLoc Ho int ret;
140f12d8695SLoc Ho int irq;
141f12d8695SLoc Ho
142f12d8695SLoc Ho pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
143f12d8695SLoc Ho if (!pdata)
144f12d8695SLoc Ho return -ENOMEM;
145f12d8695SLoc Ho platform_set_drvdata(pdev, pdata);
146f12d8695SLoc Ho
14709ef18bcSYueHaibing pdata->csr_base = devm_platform_ioremap_resource(pdev, 0);
148f12d8695SLoc Ho if (IS_ERR(pdata->csr_base))
149f12d8695SLoc Ho return PTR_ERR(pdata->csr_base);
150f12d8695SLoc Ho
151a652e00eSAlexandre Belloni pdata->rtc = devm_rtc_allocate_device(&pdev->dev);
152a652e00eSAlexandre Belloni if (IS_ERR(pdata->rtc))
153a652e00eSAlexandre Belloni return PTR_ERR(pdata->rtc);
154a652e00eSAlexandre Belloni
155f12d8695SLoc Ho irq = platform_get_irq(pdev, 0);
156faac9102SStephen Boyd if (irq < 0)
157f12d8695SLoc Ho return irq;
158f12d8695SLoc Ho ret = devm_request_irq(&pdev->dev, irq, xgene_rtc_interrupt, 0,
159f12d8695SLoc Ho dev_name(&pdev->dev), pdata);
160f12d8695SLoc Ho if (ret) {
161f12d8695SLoc Ho dev_err(&pdev->dev, "Could not request IRQ\n");
162f12d8695SLoc Ho return ret;
163f12d8695SLoc Ho }
164f12d8695SLoc Ho
165f12d8695SLoc Ho pdata->clk = devm_clk_get(&pdev->dev, NULL);
166f12d8695SLoc Ho if (IS_ERR(pdata->clk)) {
167f12d8695SLoc Ho dev_err(&pdev->dev, "Couldn't get the clock for RTC\n");
168f12d8695SLoc Ho return -ENODEV;
169f12d8695SLoc Ho }
170d0bcd82bSLoc Ho ret = clk_prepare_enable(pdata->clk);
171d0bcd82bSLoc Ho if (ret)
172d0bcd82bSLoc Ho return ret;
173f12d8695SLoc Ho
174f12d8695SLoc Ho /* Turn on the clock and the crystal */
175f12d8695SLoc Ho writel(RTC_CCR_EN, pdata->csr_base + RTC_CCR);
176f12d8695SLoc Ho
177d0bcd82bSLoc Ho ret = device_init_wakeup(&pdev->dev, 1);
178d0bcd82bSLoc Ho if (ret) {
179d0bcd82bSLoc Ho clk_disable_unprepare(pdata->clk);
180d0bcd82bSLoc Ho return ret;
181d0bcd82bSLoc Ho }
182f12d8695SLoc Ho
183a652e00eSAlexandre Belloni pdata->rtc->ops = &xgene_rtc_ops;
184490595abSAlexandre Belloni pdata->rtc->range_max = U32_MAX;
185a652e00eSAlexandre Belloni
186fdcfd854SBartosz Golaszewski ret = devm_rtc_register_device(pdata->rtc);
187a652e00eSAlexandre Belloni if (ret) {
188a652e00eSAlexandre Belloni clk_disable_unprepare(pdata->clk);
189a652e00eSAlexandre Belloni return ret;
190a652e00eSAlexandre Belloni }
191f12d8695SLoc Ho
192f12d8695SLoc Ho return 0;
193f12d8695SLoc Ho }
194f12d8695SLoc Ho
xgene_rtc_remove(struct platform_device * pdev)195*257062d2SUwe Kleine-König static void xgene_rtc_remove(struct platform_device *pdev)
196f12d8695SLoc Ho {
197f12d8695SLoc Ho struct xgene_rtc_dev *pdata = platform_get_drvdata(pdev);
198f12d8695SLoc Ho
199f12d8695SLoc Ho xgene_rtc_alarm_irq_enable(&pdev->dev, 0);
200f12d8695SLoc Ho device_init_wakeup(&pdev->dev, 0);
201f12d8695SLoc Ho clk_disable_unprepare(pdata->clk);
202f12d8695SLoc Ho }
203f12d8695SLoc Ho
xgene_rtc_suspend(struct device * dev)204573e2bf0SArnd Bergmann static int __maybe_unused xgene_rtc_suspend(struct device *dev)
205f12d8695SLoc Ho {
206f12d8695SLoc Ho struct platform_device *pdev = to_platform_device(dev);
207f12d8695SLoc Ho struct xgene_rtc_dev *pdata = platform_get_drvdata(pdev);
208f12d8695SLoc Ho int irq;
209f12d8695SLoc Ho
210f12d8695SLoc Ho irq = platform_get_irq(pdev, 0);
211d0bcd82bSLoc Ho
212d0bcd82bSLoc Ho /*
213d0bcd82bSLoc Ho * If this RTC alarm will be used for waking the system up,
214d0bcd82bSLoc Ho * don't disable it of course. Else we just disable the alarm
215d0bcd82bSLoc Ho * and await suspension.
216d0bcd82bSLoc Ho */
217f12d8695SLoc Ho if (device_may_wakeup(&pdev->dev)) {
218f12d8695SLoc Ho if (!enable_irq_wake(irq))
219f12d8695SLoc Ho pdata->irq_wake = 1;
220f12d8695SLoc Ho } else {
221d0bcd82bSLoc Ho pdata->irq_enabled = xgene_rtc_alarm_irq_enabled(dev);
222f12d8695SLoc Ho xgene_rtc_alarm_irq_enable(dev, 0);
223d0bcd82bSLoc Ho clk_disable_unprepare(pdata->clk);
224f12d8695SLoc Ho }
225f12d8695SLoc Ho return 0;
226f12d8695SLoc Ho }
227f12d8695SLoc Ho
xgene_rtc_resume(struct device * dev)228573e2bf0SArnd Bergmann static int __maybe_unused xgene_rtc_resume(struct device *dev)
229f12d8695SLoc Ho {
230f12d8695SLoc Ho struct platform_device *pdev = to_platform_device(dev);
231f12d8695SLoc Ho struct xgene_rtc_dev *pdata = platform_get_drvdata(pdev);
232f12d8695SLoc Ho int irq;
233d0bcd82bSLoc Ho int rc;
234f12d8695SLoc Ho
235f12d8695SLoc Ho irq = platform_get_irq(pdev, 0);
236d0bcd82bSLoc Ho
237f12d8695SLoc Ho if (device_may_wakeup(&pdev->dev)) {
238f12d8695SLoc Ho if (pdata->irq_wake) {
239f12d8695SLoc Ho disable_irq_wake(irq);
240f12d8695SLoc Ho pdata->irq_wake = 0;
241f12d8695SLoc Ho }
242f12d8695SLoc Ho } else {
243d0bcd82bSLoc Ho rc = clk_prepare_enable(pdata->clk);
244d0bcd82bSLoc Ho if (rc) {
245d0bcd82bSLoc Ho dev_err(dev, "Unable to enable clock error %d\n", rc);
246d0bcd82bSLoc Ho return rc;
247d0bcd82bSLoc Ho }
248d0bcd82bSLoc Ho xgene_rtc_alarm_irq_enable(dev, pdata->irq_enabled);
249f12d8695SLoc Ho }
250f12d8695SLoc Ho
251f12d8695SLoc Ho return 0;
252f12d8695SLoc Ho }
253f12d8695SLoc Ho
254f12d8695SLoc Ho static SIMPLE_DEV_PM_OPS(xgene_rtc_pm_ops, xgene_rtc_suspend, xgene_rtc_resume);
255f12d8695SLoc Ho
256f12d8695SLoc Ho #ifdef CONFIG_OF
257f12d8695SLoc Ho static const struct of_device_id xgene_rtc_of_match[] = {
258f12d8695SLoc Ho {.compatible = "apm,xgene-rtc" },
259f12d8695SLoc Ho { }
260f12d8695SLoc Ho };
261f12d8695SLoc Ho MODULE_DEVICE_TABLE(of, xgene_rtc_of_match);
262f12d8695SLoc Ho #endif
263f12d8695SLoc Ho
264f12d8695SLoc Ho static struct platform_driver xgene_rtc_driver = {
265f12d8695SLoc Ho .probe = xgene_rtc_probe,
266*257062d2SUwe Kleine-König .remove_new = xgene_rtc_remove,
267f12d8695SLoc Ho .driver = {
268f12d8695SLoc Ho .name = "xgene-rtc",
269f12d8695SLoc Ho .pm = &xgene_rtc_pm_ops,
270f12d8695SLoc Ho .of_match_table = of_match_ptr(xgene_rtc_of_match),
271f12d8695SLoc Ho },
272f12d8695SLoc Ho };
273f12d8695SLoc Ho
274f12d8695SLoc Ho module_platform_driver(xgene_rtc_driver);
275f12d8695SLoc Ho
276f12d8695SLoc Ho MODULE_DESCRIPTION("APM X-Gene SoC RTC driver");
277f12d8695SLoc Ho MODULE_AUTHOR("Rameshwar Sahu <rsahu@apm.com>");
278f12d8695SLoc Ho MODULE_LICENSE("GPL");
279