xref: /openbmc/linux/drivers/rtc/rtc-xgene.c (revision 257062d2)
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