xref: /linux/drivers/rtc/rtc-armada38x.c (revision 75faea9179a775fbc3380e918ac90dc9991dc450)
1a3a42806SGregory CLEMENT /*
2a3a42806SGregory CLEMENT  * RTC driver for the Armada 38x Marvell SoCs
3a3a42806SGregory CLEMENT  *
4a3a42806SGregory CLEMENT  * Copyright (C) 2015 Marvell
5a3a42806SGregory CLEMENT  *
6a3a42806SGregory CLEMENT  * Gregory Clement <gregory.clement@free-electrons.com>
7a3a42806SGregory CLEMENT  *
8a3a42806SGregory CLEMENT  * This program is free software; you can redistribute it and/or
9a3a42806SGregory CLEMENT  * modify it under the terms of the GNU General Public License as
10a3a42806SGregory CLEMENT  * published by the Free Software Foundation; either version 2 of the
11a3a42806SGregory CLEMENT  * License, or (at your option) any later version.
12a3a42806SGregory CLEMENT  *
13a3a42806SGregory CLEMENT  */
14a3a42806SGregory CLEMENT 
15a3a42806SGregory CLEMENT #include <linux/delay.h>
16a3a42806SGregory CLEMENT #include <linux/io.h>
17a3a42806SGregory CLEMENT #include <linux/module.h>
18a3a42806SGregory CLEMENT #include <linux/of.h>
19*75faea91SGregory CLEMENT #include <linux/of_device.h>
20a3a42806SGregory CLEMENT #include <linux/platform_device.h>
21a3a42806SGregory CLEMENT #include <linux/rtc.h>
22a3a42806SGregory CLEMENT 
23a3a42806SGregory CLEMENT #define RTC_STATUS	    0x0
24a3a42806SGregory CLEMENT #define RTC_STATUS_ALARM1	    BIT(0)
25a3a42806SGregory CLEMENT #define RTC_STATUS_ALARM2	    BIT(1)
26a3a42806SGregory CLEMENT #define RTC_IRQ1_CONF	    0x4
27*75faea91SGregory CLEMENT #define RTC_IRQ_AL_EN		    BIT(0)
28*75faea91SGregory CLEMENT #define RTC_IRQ_FREQ_EN		    BIT(1)
29*75faea91SGregory CLEMENT #define RTC_IRQ_FREQ_1HZ	    BIT(2)
30*75faea91SGregory CLEMENT 
31a3a42806SGregory CLEMENT #define RTC_TIME	    0xC
32a3a42806SGregory CLEMENT #define RTC_ALARM1	    0x10
33*75faea91SGregory CLEMENT #define RTC_38X_BRIDGE_TIMING_CTL   0x0
34*75faea91SGregory CLEMENT #define RTC_38X_PERIOD_OFFS		0
35*75faea91SGregory CLEMENT #define RTC_38X_PERIOD_MASK		(0x3FF << RTC_38X_PERIOD_OFFS)
36*75faea91SGregory CLEMENT #define RTC_38X_READ_DELAY_OFFS		26
37*75faea91SGregory CLEMENT #define RTC_38X_READ_DELAY_MASK		(0x1F << RTC_38X_READ_DELAY_OFFS)
38844a3073SGregory CLEMENT 
39a3a42806SGregory CLEMENT #define SOC_RTC_INTERRUPT	    0x8
40a3a42806SGregory CLEMENT #define SOC_RTC_ALARM1			BIT(0)
41a3a42806SGregory CLEMENT #define SOC_RTC_ALARM2			BIT(1)
42a3a42806SGregory CLEMENT #define SOC_RTC_ALARM1_MASK		BIT(2)
43a3a42806SGregory CLEMENT #define SOC_RTC_ALARM2_MASK		BIT(3)
44a3a42806SGregory CLEMENT 
45844a3073SGregory CLEMENT #define SAMPLE_NR 100
46844a3073SGregory CLEMENT 
47844a3073SGregory CLEMENT struct value_to_freq {
48844a3073SGregory CLEMENT 	u32 value;
49844a3073SGregory CLEMENT 	u8 freq;
50844a3073SGregory CLEMENT };
51844a3073SGregory CLEMENT 
52a3a42806SGregory CLEMENT struct armada38x_rtc {
53a3a42806SGregory CLEMENT 	struct rtc_device   *rtc_dev;
54a3a42806SGregory CLEMENT 	void __iomem	    *regs;
55a3a42806SGregory CLEMENT 	void __iomem	    *regs_soc;
56a3a42806SGregory CLEMENT 	spinlock_t	    lock;
57a3a42806SGregory CLEMENT 	int		    irq;
58844a3073SGregory CLEMENT 	struct value_to_freq *val_to_freq;
59*75faea91SGregory CLEMENT 	struct armada38x_rtc_data *data;
60*75faea91SGregory CLEMENT };
61*75faea91SGregory CLEMENT 
62*75faea91SGregory CLEMENT #define ALARM1	0
63*75faea91SGregory CLEMENT #define ALARM_REG(base, alarm)	 ((base) + (alarm) * sizeof(u32))
64*75faea91SGregory CLEMENT 
65*75faea91SGregory CLEMENT struct armada38x_rtc_data {
66*75faea91SGregory CLEMENT 	/* Initialize the RTC-MBUS bridge timing */
67*75faea91SGregory CLEMENT 	void (*update_mbus_timing)(struct armada38x_rtc *rtc);
68*75faea91SGregory CLEMENT 	u32 (*read_rtc_reg)(struct armada38x_rtc *rtc, u8 rtc_reg);
69*75faea91SGregory CLEMENT 	void (*clear_isr)(struct armada38x_rtc *rtc);
70*75faea91SGregory CLEMENT 	void (*unmask_interrupt)(struct armada38x_rtc *rtc);
71*75faea91SGregory CLEMENT 	u32 alarm;
72a3a42806SGregory CLEMENT };
73a3a42806SGregory CLEMENT 
74a3a42806SGregory CLEMENT /*
75a3a42806SGregory CLEMENT  * According to the datasheet, the OS should wait 5us after every
76a3a42806SGregory CLEMENT  * register write to the RTC hard macro so that the required update
77a3a42806SGregory CLEMENT  * can occur without holding off the system bus
78844a3073SGregory CLEMENT  * According to errata RES-3124064, Write to any RTC register
79844a3073SGregory CLEMENT  * may fail. As a workaround, before writing to RTC
80844a3073SGregory CLEMENT  * register, issue a dummy write of 0x0 twice to RTC Status
81844a3073SGregory CLEMENT  * register.
82a3a42806SGregory CLEMENT  */
83844a3073SGregory CLEMENT 
84a3a42806SGregory CLEMENT static void rtc_delayed_write(u32 val, struct armada38x_rtc *rtc, int offset)
85a3a42806SGregory CLEMENT {
86844a3073SGregory CLEMENT 	writel(0, rtc->regs + RTC_STATUS);
87844a3073SGregory CLEMENT 	writel(0, rtc->regs + RTC_STATUS);
88a3a42806SGregory CLEMENT 	writel(val, rtc->regs + offset);
89a3a42806SGregory CLEMENT 	udelay(5);
90a3a42806SGregory CLEMENT }
91a3a42806SGregory CLEMENT 
92844a3073SGregory CLEMENT /* Update RTC-MBUS bridge timing parameters */
93*75faea91SGregory CLEMENT static void rtc_update_38x_mbus_timing_params(struct armada38x_rtc *rtc)
94844a3073SGregory CLEMENT {
95844a3073SGregory CLEMENT 	u32 reg;
96844a3073SGregory CLEMENT 
97*75faea91SGregory CLEMENT 	reg = readl(rtc->regs_soc + RTC_38X_BRIDGE_TIMING_CTL);
98*75faea91SGregory CLEMENT 	reg &= ~RTC_38X_PERIOD_MASK;
99*75faea91SGregory CLEMENT 	reg |= 0x3FF << RTC_38X_PERIOD_OFFS; /* Maximum value */
100*75faea91SGregory CLEMENT 	reg &= ~RTC_38X_READ_DELAY_MASK;
101*75faea91SGregory CLEMENT 	reg |= 0x1F << RTC_38X_READ_DELAY_OFFS; /* Maximum value */
102*75faea91SGregory CLEMENT 	writel(reg, rtc->regs_soc + RTC_38X_BRIDGE_TIMING_CTL);
103844a3073SGregory CLEMENT }
104844a3073SGregory CLEMENT 
105*75faea91SGregory CLEMENT static u32 read_rtc_register_38x_wa(struct armada38x_rtc *rtc, u8 rtc_reg)
106844a3073SGregory CLEMENT {
107844a3073SGregory CLEMENT 	int i, index_max = 0, max = 0;
108844a3073SGregory CLEMENT 
109844a3073SGregory CLEMENT 	for (i = 0; i < SAMPLE_NR; i++) {
110844a3073SGregory CLEMENT 		rtc->val_to_freq[i].value = readl(rtc->regs + rtc_reg);
111844a3073SGregory CLEMENT 		rtc->val_to_freq[i].freq = 0;
112844a3073SGregory CLEMENT 	}
113844a3073SGregory CLEMENT 
114844a3073SGregory CLEMENT 	for (i = 0; i < SAMPLE_NR; i++) {
115844a3073SGregory CLEMENT 		int j = 0;
116844a3073SGregory CLEMENT 		u32 value = rtc->val_to_freq[i].value;
117844a3073SGregory CLEMENT 
118844a3073SGregory CLEMENT 		while (rtc->val_to_freq[j].freq) {
119844a3073SGregory CLEMENT 			if (rtc->val_to_freq[j].value == value) {
120844a3073SGregory CLEMENT 				rtc->val_to_freq[j].freq++;
121844a3073SGregory CLEMENT 				break;
122844a3073SGregory CLEMENT 			}
123844a3073SGregory CLEMENT 			j++;
124844a3073SGregory CLEMENT 		}
125844a3073SGregory CLEMENT 
126844a3073SGregory CLEMENT 		if (!rtc->val_to_freq[j].freq) {
127844a3073SGregory CLEMENT 			rtc->val_to_freq[j].value = value;
128844a3073SGregory CLEMENT 			rtc->val_to_freq[j].freq = 1;
129844a3073SGregory CLEMENT 		}
130844a3073SGregory CLEMENT 
131844a3073SGregory CLEMENT 		if (rtc->val_to_freq[j].freq > max) {
132844a3073SGregory CLEMENT 			index_max = j;
133844a3073SGregory CLEMENT 			max = rtc->val_to_freq[j].freq;
134844a3073SGregory CLEMENT 		}
135844a3073SGregory CLEMENT 
136844a3073SGregory CLEMENT 		/*
137844a3073SGregory CLEMENT 		 * If a value already has half of the sample this is the most
138844a3073SGregory CLEMENT 		 * frequent one and we can stop the research right now
139844a3073SGregory CLEMENT 		 */
140844a3073SGregory CLEMENT 		if (max > SAMPLE_NR / 2)
141844a3073SGregory CLEMENT 			break;
142844a3073SGregory CLEMENT 	}
143844a3073SGregory CLEMENT 
144844a3073SGregory CLEMENT 	return rtc->val_to_freq[index_max].value;
145844a3073SGregory CLEMENT }
146844a3073SGregory CLEMENT 
147*75faea91SGregory CLEMENT static void armada38x_clear_isr(struct armada38x_rtc *rtc)
148*75faea91SGregory CLEMENT {
149*75faea91SGregory CLEMENT 	u32 val = readl(rtc->regs_soc + SOC_RTC_INTERRUPT);
150*75faea91SGregory CLEMENT 
151*75faea91SGregory CLEMENT 	writel(val & ~SOC_RTC_ALARM1, rtc->regs_soc + SOC_RTC_INTERRUPT);
152*75faea91SGregory CLEMENT }
153*75faea91SGregory CLEMENT 
154*75faea91SGregory CLEMENT static void armada38x_unmask_interrupt(struct armada38x_rtc *rtc)
155*75faea91SGregory CLEMENT {
156*75faea91SGregory CLEMENT 	u32 val = readl(rtc->regs_soc + SOC_RTC_INTERRUPT);
157*75faea91SGregory CLEMENT 
158*75faea91SGregory CLEMENT 	writel(val | SOC_RTC_ALARM1_MASK, rtc->regs_soc + SOC_RTC_INTERRUPT);
159*75faea91SGregory CLEMENT }
160a3a42806SGregory CLEMENT static int armada38x_rtc_read_time(struct device *dev, struct rtc_time *tm)
161a3a42806SGregory CLEMENT {
162a3a42806SGregory CLEMENT 	struct armada38x_rtc *rtc = dev_get_drvdata(dev);
163844a3073SGregory CLEMENT 	unsigned long time, flags;
164a3a42806SGregory CLEMENT 
1650c6e7183SNadav Haklai 	spin_lock_irqsave(&rtc->lock, flags);
166*75faea91SGregory CLEMENT 	time = rtc->data->read_rtc_reg(rtc, RTC_TIME);
1670c6e7183SNadav Haklai 	spin_unlock_irqrestore(&rtc->lock, flags);
168a3a42806SGregory CLEMENT 
169844a3073SGregory CLEMENT 	rtc_time_to_tm(time, tm);
170a3a42806SGregory CLEMENT 
171a3a42806SGregory CLEMENT 	return 0;
172a3a42806SGregory CLEMENT }
173a3a42806SGregory CLEMENT 
174a3a42806SGregory CLEMENT static int armada38x_rtc_set_time(struct device *dev, struct rtc_time *tm)
175a3a42806SGregory CLEMENT {
176a3a42806SGregory CLEMENT 	struct armada38x_rtc *rtc = dev_get_drvdata(dev);
177a3a42806SGregory CLEMENT 	int ret = 0;
1780c6e7183SNadav Haklai 	unsigned long time, flags;
179a3a42806SGregory CLEMENT 
180a3a42806SGregory CLEMENT 	ret = rtc_tm_to_time(tm, &time);
181a3a42806SGregory CLEMENT 
182a3a42806SGregory CLEMENT 	if (ret)
183a3a42806SGregory CLEMENT 		goto out;
184844a3073SGregory CLEMENT 
1850c6e7183SNadav Haklai 	spin_lock_irqsave(&rtc->lock, flags);
186a3a42806SGregory CLEMENT 	rtc_delayed_write(time, rtc, RTC_TIME);
1870c6e7183SNadav Haklai 	spin_unlock_irqrestore(&rtc->lock, flags);
188a3a42806SGregory CLEMENT 
189a3a42806SGregory CLEMENT out:
190a3a42806SGregory CLEMENT 	return ret;
191a3a42806SGregory CLEMENT }
192a3a42806SGregory CLEMENT 
193a3a42806SGregory CLEMENT static int armada38x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
194a3a42806SGregory CLEMENT {
195a3a42806SGregory CLEMENT 	struct armada38x_rtc *rtc = dev_get_drvdata(dev);
196a3a42806SGregory CLEMENT 	unsigned long time, flags;
197*75faea91SGregory CLEMENT 	u32 reg = ALARM_REG(RTC_ALARM1, rtc->data->alarm);
198*75faea91SGregory CLEMENT 	u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm);
199a3a42806SGregory CLEMENT 	u32 val;
200a3a42806SGregory CLEMENT 
201a3a42806SGregory CLEMENT 	spin_lock_irqsave(&rtc->lock, flags);
202a3a42806SGregory CLEMENT 
203*75faea91SGregory CLEMENT 	time = rtc->data->read_rtc_reg(rtc, reg);
204*75faea91SGregory CLEMENT 	val = rtc->data->read_rtc_reg(rtc, reg_irq) & RTC_IRQ_AL_EN;
205a3a42806SGregory CLEMENT 
206a3a42806SGregory CLEMENT 	spin_unlock_irqrestore(&rtc->lock, flags);
207a3a42806SGregory CLEMENT 
208a3a42806SGregory CLEMENT 	alrm->enabled = val ? 1 : 0;
209a3a42806SGregory CLEMENT 	rtc_time_to_tm(time,  &alrm->time);
210a3a42806SGregory CLEMENT 
211a3a42806SGregory CLEMENT 	return 0;
212a3a42806SGregory CLEMENT }
213a3a42806SGregory CLEMENT 
214a3a42806SGregory CLEMENT static int armada38x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
215a3a42806SGregory CLEMENT {
216a3a42806SGregory CLEMENT 	struct armada38x_rtc *rtc = dev_get_drvdata(dev);
217*75faea91SGregory CLEMENT 	u32 reg = ALARM_REG(RTC_ALARM1, rtc->data->alarm);
218*75faea91SGregory CLEMENT 	u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm);
219a3a42806SGregory CLEMENT 	unsigned long time, flags;
220a3a42806SGregory CLEMENT 	int ret = 0;
221a3a42806SGregory CLEMENT 
222a3a42806SGregory CLEMENT 	ret = rtc_tm_to_time(&alrm->time, &time);
223a3a42806SGregory CLEMENT 
224a3a42806SGregory CLEMENT 	if (ret)
225a3a42806SGregory CLEMENT 		goto out;
226a3a42806SGregory CLEMENT 
227a3a42806SGregory CLEMENT 	spin_lock_irqsave(&rtc->lock, flags);
228a3a42806SGregory CLEMENT 
229*75faea91SGregory CLEMENT 	rtc_delayed_write(time, rtc, reg);
230a3a42806SGregory CLEMENT 
231a3a42806SGregory CLEMENT 	if (alrm->enabled) {
232*75faea91SGregory CLEMENT 		rtc_delayed_write(RTC_IRQ_AL_EN, rtc, reg_irq);
233*75faea91SGregory CLEMENT 		rtc->data->unmask_interrupt(rtc);
234a3a42806SGregory CLEMENT 	}
235a3a42806SGregory CLEMENT 
236a3a42806SGregory CLEMENT 	spin_unlock_irqrestore(&rtc->lock, flags);
237a3a42806SGregory CLEMENT 
238a3a42806SGregory CLEMENT out:
239a3a42806SGregory CLEMENT 	return ret;
240a3a42806SGregory CLEMENT }
241a3a42806SGregory CLEMENT 
242a3a42806SGregory CLEMENT static int armada38x_rtc_alarm_irq_enable(struct device *dev,
243a3a42806SGregory CLEMENT 					 unsigned int enabled)
244a3a42806SGregory CLEMENT {
245a3a42806SGregory CLEMENT 	struct armada38x_rtc *rtc = dev_get_drvdata(dev);
246*75faea91SGregory CLEMENT 	u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm);
247a3a42806SGregory CLEMENT 	unsigned long flags;
248a3a42806SGregory CLEMENT 
249a3a42806SGregory CLEMENT 	spin_lock_irqsave(&rtc->lock, flags);
250a3a42806SGregory CLEMENT 
251a3a42806SGregory CLEMENT 	if (enabled)
252*75faea91SGregory CLEMENT 		rtc_delayed_write(RTC_IRQ_AL_EN, rtc, reg_irq);
253a3a42806SGregory CLEMENT 	else
254*75faea91SGregory CLEMENT 		rtc_delayed_write(0, rtc, reg_irq);
255a3a42806SGregory CLEMENT 
256a3a42806SGregory CLEMENT 	spin_unlock_irqrestore(&rtc->lock, flags);
257a3a42806SGregory CLEMENT 
258a3a42806SGregory CLEMENT 	return 0;
259a3a42806SGregory CLEMENT }
260a3a42806SGregory CLEMENT 
261a3a42806SGregory CLEMENT static irqreturn_t armada38x_rtc_alarm_irq(int irq, void *data)
262a3a42806SGregory CLEMENT {
263a3a42806SGregory CLEMENT 	struct armada38x_rtc *rtc = data;
264a3a42806SGregory CLEMENT 	u32 val;
265a3a42806SGregory CLEMENT 	int event = RTC_IRQF | RTC_AF;
266*75faea91SGregory CLEMENT 	u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm);
267a3a42806SGregory CLEMENT 
268a3a42806SGregory CLEMENT 	dev_dbg(&rtc->rtc_dev->dev, "%s:irq(%d)\n", __func__, irq);
269a3a42806SGregory CLEMENT 
270a3a42806SGregory CLEMENT 	spin_lock(&rtc->lock);
271a3a42806SGregory CLEMENT 
272*75faea91SGregory CLEMENT 	rtc->data->clear_isr(rtc);
273*75faea91SGregory CLEMENT 	val = rtc->data->read_rtc_reg(rtc, reg_irq);
274*75faea91SGregory CLEMENT 	/* disable all the interrupts for alarm*/
275*75faea91SGregory CLEMENT 	rtc_delayed_write(0, rtc, reg_irq);
276a3a42806SGregory CLEMENT 	/* Ack the event */
277*75faea91SGregory CLEMENT 	rtc_delayed_write(1 << rtc->data->alarm, rtc, RTC_STATUS);
278a3a42806SGregory CLEMENT 
279a3a42806SGregory CLEMENT 	spin_unlock(&rtc->lock);
280a3a42806SGregory CLEMENT 
281*75faea91SGregory CLEMENT 	if (val & RTC_IRQ_FREQ_EN) {
282*75faea91SGregory CLEMENT 		if (val & RTC_IRQ_FREQ_1HZ)
283a3a42806SGregory CLEMENT 			event |= RTC_UF;
284a3a42806SGregory CLEMENT 		else
285a3a42806SGregory CLEMENT 			event |= RTC_PF;
286a3a42806SGregory CLEMENT 	}
287a3a42806SGregory CLEMENT 
288a3a42806SGregory CLEMENT 	rtc_update_irq(rtc->rtc_dev, 1, event);
289a3a42806SGregory CLEMENT 
290a3a42806SGregory CLEMENT 	return IRQ_HANDLED;
291a3a42806SGregory CLEMENT }
292a3a42806SGregory CLEMENT 
293d748c981SRussell King static const struct rtc_class_ops armada38x_rtc_ops = {
294a3a42806SGregory CLEMENT 	.read_time = armada38x_rtc_read_time,
295a3a42806SGregory CLEMENT 	.set_time = armada38x_rtc_set_time,
296a3a42806SGregory CLEMENT 	.read_alarm = armada38x_rtc_read_alarm,
297a3a42806SGregory CLEMENT 	.set_alarm = armada38x_rtc_set_alarm,
298a3a42806SGregory CLEMENT 	.alarm_irq_enable = armada38x_rtc_alarm_irq_enable,
299a3a42806SGregory CLEMENT };
300a3a42806SGregory CLEMENT 
301d748c981SRussell King static const struct rtc_class_ops armada38x_rtc_ops_noirq = {
302d748c981SRussell King 	.read_time = armada38x_rtc_read_time,
303d748c981SRussell King 	.set_time = armada38x_rtc_set_time,
304d748c981SRussell King 	.read_alarm = armada38x_rtc_read_alarm,
305d748c981SRussell King };
306d748c981SRussell King 
307*75faea91SGregory CLEMENT static const struct armada38x_rtc_data armada38x_data = {
308*75faea91SGregory CLEMENT 	.update_mbus_timing = rtc_update_38x_mbus_timing_params,
309*75faea91SGregory CLEMENT 	.read_rtc_reg = read_rtc_register_38x_wa,
310*75faea91SGregory CLEMENT 	.clear_isr = armada38x_clear_isr,
311*75faea91SGregory CLEMENT 	.unmask_interrupt = armada38x_unmask_interrupt,
312*75faea91SGregory CLEMENT 	.alarm = ALARM1,
313*75faea91SGregory CLEMENT };
314*75faea91SGregory CLEMENT 
315*75faea91SGregory CLEMENT #ifdef CONFIG_OF
316*75faea91SGregory CLEMENT static const struct of_device_id armada38x_rtc_of_match_table[] = {
317*75faea91SGregory CLEMENT 	{
318*75faea91SGregory CLEMENT 		.compatible = "marvell,armada-380-rtc",
319*75faea91SGregory CLEMENT 		.data = &armada38x_data,
320*75faea91SGregory CLEMENT 	},
321*75faea91SGregory CLEMENT 	{}
322*75faea91SGregory CLEMENT };
323*75faea91SGregory CLEMENT MODULE_DEVICE_TABLE(of, armada38x_rtc_of_match_table);
324*75faea91SGregory CLEMENT #endif
325*75faea91SGregory CLEMENT 
326a3a42806SGregory CLEMENT static __init int armada38x_rtc_probe(struct platform_device *pdev)
327a3a42806SGregory CLEMENT {
328d748c981SRussell King 	const struct rtc_class_ops *ops;
329a3a42806SGregory CLEMENT 	struct resource *res;
330a3a42806SGregory CLEMENT 	struct armada38x_rtc *rtc;
331*75faea91SGregory CLEMENT 	const struct of_device_id *match;
332a3a42806SGregory CLEMENT 	int ret;
333a3a42806SGregory CLEMENT 
334*75faea91SGregory CLEMENT 	match = of_match_device(armada38x_rtc_of_match_table, &pdev->dev);
335*75faea91SGregory CLEMENT 	if (!match)
336*75faea91SGregory CLEMENT 		return -ENODEV;
337*75faea91SGregory CLEMENT 
338a3a42806SGregory CLEMENT 	rtc = devm_kzalloc(&pdev->dev, sizeof(struct armada38x_rtc),
339a3a42806SGregory CLEMENT 			    GFP_KERNEL);
340a3a42806SGregory CLEMENT 	if (!rtc)
341a3a42806SGregory CLEMENT 		return -ENOMEM;
342a3a42806SGregory CLEMENT 
343844a3073SGregory CLEMENT 	rtc->val_to_freq = devm_kcalloc(&pdev->dev, SAMPLE_NR,
344844a3073SGregory CLEMENT 				sizeof(struct value_to_freq), GFP_KERNEL);
345844a3073SGregory CLEMENT 	if (!rtc->val_to_freq)
346844a3073SGregory CLEMENT 		return -ENOMEM;
347844a3073SGregory CLEMENT 
348a3a42806SGregory CLEMENT 	spin_lock_init(&rtc->lock);
349a3a42806SGregory CLEMENT 
350a3a42806SGregory CLEMENT 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rtc");
351a3a42806SGregory CLEMENT 	rtc->regs = devm_ioremap_resource(&pdev->dev, res);
352a3a42806SGregory CLEMENT 	if (IS_ERR(rtc->regs))
353a3a42806SGregory CLEMENT 		return PTR_ERR(rtc->regs);
354a3a42806SGregory CLEMENT 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rtc-soc");
355a3a42806SGregory CLEMENT 	rtc->regs_soc = devm_ioremap_resource(&pdev->dev, res);
356a3a42806SGregory CLEMENT 	if (IS_ERR(rtc->regs_soc))
357a3a42806SGregory CLEMENT 		return PTR_ERR(rtc->regs_soc);
358a3a42806SGregory CLEMENT 
359a3a42806SGregory CLEMENT 	rtc->irq = platform_get_irq(pdev, 0);
360a3a42806SGregory CLEMENT 
361a3a42806SGregory CLEMENT 	if (rtc->irq < 0) {
362a3a42806SGregory CLEMENT 		dev_err(&pdev->dev, "no irq\n");
363a3a42806SGregory CLEMENT 		return rtc->irq;
364a3a42806SGregory CLEMENT 	}
365a3a42806SGregory CLEMENT 	if (devm_request_irq(&pdev->dev, rtc->irq, armada38x_rtc_alarm_irq,
366a3a42806SGregory CLEMENT 				0, pdev->name, rtc) < 0) {
367a3a42806SGregory CLEMENT 		dev_warn(&pdev->dev, "Interrupt not available.\n");
368a3a42806SGregory CLEMENT 		rtc->irq = -1;
369d748c981SRussell King 	}
370d748c981SRussell King 	platform_set_drvdata(pdev, rtc);
371d748c981SRussell King 
372d748c981SRussell King 	if (rtc->irq != -1) {
373d748c981SRussell King 		device_init_wakeup(&pdev->dev, 1);
374d748c981SRussell King 		ops = &armada38x_rtc_ops;
375d748c981SRussell King 	} else {
376a3a42806SGregory CLEMENT 		/*
377a3a42806SGregory CLEMENT 		 * If there is no interrupt available then we can't
378a3a42806SGregory CLEMENT 		 * use the alarm
379a3a42806SGregory CLEMENT 		 */
380d748c981SRussell King 		ops = &armada38x_rtc_ops_noirq;
381a3a42806SGregory CLEMENT 	}
382*75faea91SGregory CLEMENT 	rtc->data = (struct armada38x_rtc_data *)match->data;
383*75faea91SGregory CLEMENT 
384a3a42806SGregory CLEMENT 
385844a3073SGregory CLEMENT 	/* Update RTC-MBUS bridge timing parameters */
386*75faea91SGregory CLEMENT 	rtc->data->update_mbus_timing(rtc);
387844a3073SGregory CLEMENT 
388a3a42806SGregory CLEMENT 	rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name,
389d748c981SRussell King 						ops, THIS_MODULE);
390a3a42806SGregory CLEMENT 	if (IS_ERR(rtc->rtc_dev)) {
391a3a42806SGregory CLEMENT 		ret = PTR_ERR(rtc->rtc_dev);
392a3a42806SGregory CLEMENT 		dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
393a3a42806SGregory CLEMENT 		return ret;
394a3a42806SGregory CLEMENT 	}
395a3a42806SGregory CLEMENT 	return 0;
396a3a42806SGregory CLEMENT }
397a3a42806SGregory CLEMENT 
398a3a42806SGregory CLEMENT #ifdef CONFIG_PM_SLEEP
399a3a42806SGregory CLEMENT static int armada38x_rtc_suspend(struct device *dev)
400a3a42806SGregory CLEMENT {
401a3a42806SGregory CLEMENT 	if (device_may_wakeup(dev)) {
402a3a42806SGregory CLEMENT 		struct armada38x_rtc *rtc = dev_get_drvdata(dev);
403a3a42806SGregory CLEMENT 
404a3a42806SGregory CLEMENT 		return enable_irq_wake(rtc->irq);
405a3a42806SGregory CLEMENT 	}
406a3a42806SGregory CLEMENT 
407a3a42806SGregory CLEMENT 	return 0;
408a3a42806SGregory CLEMENT }
409a3a42806SGregory CLEMENT 
410a3a42806SGregory CLEMENT static int armada38x_rtc_resume(struct device *dev)
411a3a42806SGregory CLEMENT {
412a3a42806SGregory CLEMENT 	if (device_may_wakeup(dev)) {
413a3a42806SGregory CLEMENT 		struct armada38x_rtc *rtc = dev_get_drvdata(dev);
414a3a42806SGregory CLEMENT 
415844a3073SGregory CLEMENT 		/* Update RTC-MBUS bridge timing parameters */
416*75faea91SGregory CLEMENT 		rtc->data->update_mbus_timing(rtc);
417844a3073SGregory CLEMENT 
418a3a42806SGregory CLEMENT 		return disable_irq_wake(rtc->irq);
419a3a42806SGregory CLEMENT 	}
420a3a42806SGregory CLEMENT 
421a3a42806SGregory CLEMENT 	return 0;
422a3a42806SGregory CLEMENT }
423a3a42806SGregory CLEMENT #endif
424a3a42806SGregory CLEMENT 
425a3a42806SGregory CLEMENT static SIMPLE_DEV_PM_OPS(armada38x_rtc_pm_ops,
426a3a42806SGregory CLEMENT 			 armada38x_rtc_suspend, armada38x_rtc_resume);
427a3a42806SGregory CLEMENT 
428a3a42806SGregory CLEMENT static struct platform_driver armada38x_rtc_driver = {
429a3a42806SGregory CLEMENT 	.driver		= {
430a3a42806SGregory CLEMENT 		.name	= "armada38x-rtc",
431a3a42806SGregory CLEMENT 		.pm	= &armada38x_rtc_pm_ops,
432a3a42806SGregory CLEMENT 		.of_match_table = of_match_ptr(armada38x_rtc_of_match_table),
433a3a42806SGregory CLEMENT 	},
434a3a42806SGregory CLEMENT };
435a3a42806SGregory CLEMENT 
436a3a42806SGregory CLEMENT module_platform_driver_probe(armada38x_rtc_driver, armada38x_rtc_probe);
437a3a42806SGregory CLEMENT 
438a3a42806SGregory CLEMENT MODULE_DESCRIPTION("Marvell Armada 38x RTC driver");
439a3a42806SGregory CLEMENT MODULE_AUTHOR("Gregory CLEMENT <gregory.clement@free-electrons.com>");
440a3a42806SGregory CLEMENT MODULE_LICENSE("GPL");
441