Staging: sm750fb: fix build warning with proc_panDisplay
[linux/fpc-iii.git] / drivers / rtc / rtc-armada38x.c
blob43e04af39e0964e3dccd24e251f484d04a680bc9
1 /*
2 * RTC driver for the Armada 38x Marvell SoCs
4 * Copyright (C) 2015 Marvell
6 * Gregory Clement <gregory.clement@free-electrons.com>
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) any later version.
15 #include <linux/delay.h>
16 #include <linux/io.h>
17 #include <linux/module.h>
18 #include <linux/of.h>
19 #include <linux/platform_device.h>
20 #include <linux/rtc.h>
22 #define RTC_STATUS 0x0
23 #define RTC_STATUS_ALARM1 BIT(0)
24 #define RTC_STATUS_ALARM2 BIT(1)
25 #define RTC_IRQ1_CONF 0x4
26 #define RTC_IRQ1_AL_EN BIT(0)
27 #define RTC_IRQ1_FREQ_EN BIT(1)
28 #define RTC_IRQ1_FREQ_1HZ BIT(2)
29 #define RTC_TIME 0xC
30 #define RTC_ALARM1 0x10
32 #define SOC_RTC_INTERRUPT 0x8
33 #define SOC_RTC_ALARM1 BIT(0)
34 #define SOC_RTC_ALARM2 BIT(1)
35 #define SOC_RTC_ALARM1_MASK BIT(2)
36 #define SOC_RTC_ALARM2_MASK BIT(3)
38 struct armada38x_rtc {
39 struct rtc_device *rtc_dev;
40 void __iomem *regs;
41 void __iomem *regs_soc;
42 spinlock_t lock;
43 int irq;
47 * According to the datasheet, the OS should wait 5us after every
48 * register write to the RTC hard macro so that the required update
49 * can occur without holding off the system bus
51 static void rtc_delayed_write(u32 val, struct armada38x_rtc *rtc, int offset)
53 writel(val, rtc->regs + offset);
54 udelay(5);
57 static int armada38x_rtc_read_time(struct device *dev, struct rtc_time *tm)
59 struct armada38x_rtc *rtc = dev_get_drvdata(dev);
60 unsigned long time, time_check, flags;
62 spin_lock_irqsave(&rtc->lock, flags);
64 time = readl(rtc->regs + RTC_TIME);
66 * WA for failing time set attempts. As stated in HW ERRATA if
67 * more than one second between two time reads is detected
68 * then read once again.
70 time_check = readl(rtc->regs + RTC_TIME);
71 if ((time_check - time) > 1)
72 time_check = readl(rtc->regs + RTC_TIME);
74 spin_unlock_irqrestore(&rtc->lock, flags);
76 rtc_time_to_tm(time_check, tm);
78 return 0;
81 static int armada38x_rtc_set_time(struct device *dev, struct rtc_time *tm)
83 struct armada38x_rtc *rtc = dev_get_drvdata(dev);
84 int ret = 0;
85 unsigned long time, flags;
87 ret = rtc_tm_to_time(tm, &time);
89 if (ret)
90 goto out;
92 * Setting the RTC time not always succeeds. According to the
93 * errata we need to first write on the status register and
94 * then wait for 100ms before writing to the time register to be
95 * sure that the data will be taken into account.
97 spin_lock_irqsave(&rtc->lock, flags);
99 rtc_delayed_write(0, rtc, RTC_STATUS);
101 spin_unlock_irqrestore(&rtc->lock, flags);
103 msleep(100);
105 spin_lock_irqsave(&rtc->lock, flags);
107 rtc_delayed_write(time, rtc, RTC_TIME);
109 spin_unlock_irqrestore(&rtc->lock, flags);
110 out:
111 return ret;
114 static int armada38x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
116 struct armada38x_rtc *rtc = dev_get_drvdata(dev);
117 unsigned long time, flags;
118 u32 val;
120 spin_lock_irqsave(&rtc->lock, flags);
122 time = readl(rtc->regs + RTC_ALARM1);
123 val = readl(rtc->regs + RTC_IRQ1_CONF) & RTC_IRQ1_AL_EN;
125 spin_unlock_irqrestore(&rtc->lock, flags);
127 alrm->enabled = val ? 1 : 0;
128 rtc_time_to_tm(time, &alrm->time);
130 return 0;
133 static int armada38x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
135 struct armada38x_rtc *rtc = dev_get_drvdata(dev);
136 unsigned long time, flags;
137 int ret = 0;
138 u32 val;
140 ret = rtc_tm_to_time(&alrm->time, &time);
142 if (ret)
143 goto out;
145 spin_lock_irqsave(&rtc->lock, flags);
147 rtc_delayed_write(time, rtc, RTC_ALARM1);
149 if (alrm->enabled) {
150 rtc_delayed_write(RTC_IRQ1_AL_EN, rtc, RTC_IRQ1_CONF);
151 val = readl(rtc->regs_soc + SOC_RTC_INTERRUPT);
152 writel(val | SOC_RTC_ALARM1_MASK,
153 rtc->regs_soc + SOC_RTC_INTERRUPT);
156 spin_unlock_irqrestore(&rtc->lock, flags);
158 out:
159 return ret;
162 static int armada38x_rtc_alarm_irq_enable(struct device *dev,
163 unsigned int enabled)
165 struct armada38x_rtc *rtc = dev_get_drvdata(dev);
166 unsigned long flags;
168 spin_lock_irqsave(&rtc->lock, flags);
170 if (enabled)
171 rtc_delayed_write(RTC_IRQ1_AL_EN, rtc, RTC_IRQ1_CONF);
172 else
173 rtc_delayed_write(0, rtc, RTC_IRQ1_CONF);
175 spin_unlock_irqrestore(&rtc->lock, flags);
177 return 0;
180 static irqreturn_t armada38x_rtc_alarm_irq(int irq, void *data)
182 struct armada38x_rtc *rtc = data;
183 u32 val;
184 int event = RTC_IRQF | RTC_AF;
186 dev_dbg(&rtc->rtc_dev->dev, "%s:irq(%d)\n", __func__, irq);
188 spin_lock(&rtc->lock);
190 val = readl(rtc->regs_soc + SOC_RTC_INTERRUPT);
192 writel(val & ~SOC_RTC_ALARM1, rtc->regs_soc + SOC_RTC_INTERRUPT);
193 val = readl(rtc->regs + RTC_IRQ1_CONF);
194 /* disable all the interrupts for alarm 1 */
195 rtc_delayed_write(0, rtc, RTC_IRQ1_CONF);
196 /* Ack the event */
197 rtc_delayed_write(RTC_STATUS_ALARM1, rtc, RTC_STATUS);
199 spin_unlock(&rtc->lock);
201 if (val & RTC_IRQ1_FREQ_EN) {
202 if (val & RTC_IRQ1_FREQ_1HZ)
203 event |= RTC_UF;
204 else
205 event |= RTC_PF;
208 rtc_update_irq(rtc->rtc_dev, 1, event);
210 return IRQ_HANDLED;
213 static struct rtc_class_ops armada38x_rtc_ops = {
214 .read_time = armada38x_rtc_read_time,
215 .set_time = armada38x_rtc_set_time,
216 .read_alarm = armada38x_rtc_read_alarm,
217 .set_alarm = armada38x_rtc_set_alarm,
218 .alarm_irq_enable = armada38x_rtc_alarm_irq_enable,
221 static __init int armada38x_rtc_probe(struct platform_device *pdev)
223 struct resource *res;
224 struct armada38x_rtc *rtc;
225 int ret;
227 rtc = devm_kzalloc(&pdev->dev, sizeof(struct armada38x_rtc),
228 GFP_KERNEL);
229 if (!rtc)
230 return -ENOMEM;
232 spin_lock_init(&rtc->lock);
234 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rtc");
235 rtc->regs = devm_ioremap_resource(&pdev->dev, res);
236 if (IS_ERR(rtc->regs))
237 return PTR_ERR(rtc->regs);
238 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rtc-soc");
239 rtc->regs_soc = devm_ioremap_resource(&pdev->dev, res);
240 if (IS_ERR(rtc->regs_soc))
241 return PTR_ERR(rtc->regs_soc);
243 rtc->irq = platform_get_irq(pdev, 0);
245 if (rtc->irq < 0) {
246 dev_err(&pdev->dev, "no irq\n");
247 return rtc->irq;
249 if (devm_request_irq(&pdev->dev, rtc->irq, armada38x_rtc_alarm_irq,
250 0, pdev->name, rtc) < 0) {
251 dev_warn(&pdev->dev, "Interrupt not available.\n");
252 rtc->irq = -1;
254 * If there is no interrupt available then we can't
255 * use the alarm
257 armada38x_rtc_ops.set_alarm = NULL;
258 armada38x_rtc_ops.alarm_irq_enable = NULL;
260 platform_set_drvdata(pdev, rtc);
261 if (rtc->irq != -1)
262 device_init_wakeup(&pdev->dev, 1);
264 rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name,
265 &armada38x_rtc_ops, THIS_MODULE);
266 if (IS_ERR(rtc->rtc_dev)) {
267 ret = PTR_ERR(rtc->rtc_dev);
268 dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
269 return ret;
271 return 0;
274 #ifdef CONFIG_PM_SLEEP
275 static int armada38x_rtc_suspend(struct device *dev)
277 if (device_may_wakeup(dev)) {
278 struct armada38x_rtc *rtc = dev_get_drvdata(dev);
280 return enable_irq_wake(rtc->irq);
283 return 0;
286 static int armada38x_rtc_resume(struct device *dev)
288 if (device_may_wakeup(dev)) {
289 struct armada38x_rtc *rtc = dev_get_drvdata(dev);
291 return disable_irq_wake(rtc->irq);
294 return 0;
296 #endif
298 static SIMPLE_DEV_PM_OPS(armada38x_rtc_pm_ops,
299 armada38x_rtc_suspend, armada38x_rtc_resume);
301 #ifdef CONFIG_OF
302 static const struct of_device_id armada38x_rtc_of_match_table[] = {
303 { .compatible = "marvell,armada-380-rtc", },
306 #endif
308 static struct platform_driver armada38x_rtc_driver = {
309 .driver = {
310 .name = "armada38x-rtc",
311 .pm = &armada38x_rtc_pm_ops,
312 .of_match_table = of_match_ptr(armada38x_rtc_of_match_table),
316 module_platform_driver_probe(armada38x_rtc_driver, armada38x_rtc_probe);
318 MODULE_DESCRIPTION("Marvell Armada 38x RTC driver");
319 MODULE_AUTHOR("Gregory CLEMENT <gregory.clement@free-electrons.com>");
320 MODULE_LICENSE("GPL");