xref: /linux/drivers/rtc/rtc-armada38x.c (revision 03c11eb3b16dc0058589751dfd91f254be2be613)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2a3a42806SGregory CLEMENT /*
3a3a42806SGregory CLEMENT  * RTC driver for the Armada 38x Marvell SoCs
4a3a42806SGregory CLEMENT  *
5a3a42806SGregory CLEMENT  * Copyright (C) 2015 Marvell
6a3a42806SGregory CLEMENT  *
7a3a42806SGregory CLEMENT  * Gregory Clement <gregory.clement@free-electrons.com>
8a3a42806SGregory CLEMENT  */
9a3a42806SGregory CLEMENT 
10a3a42806SGregory CLEMENT #include <linux/delay.h>
11a3a42806SGregory CLEMENT #include <linux/io.h>
12a3a42806SGregory CLEMENT #include <linux/module.h>
13a3a42806SGregory CLEMENT #include <linux/of.h>
14a3a42806SGregory CLEMENT #include <linux/platform_device.h>
15a3a42806SGregory CLEMENT #include <linux/rtc.h>
16a3a42806SGregory CLEMENT 
17a3a42806SGregory CLEMENT #define RTC_STATUS	    0x0
18a3a42806SGregory CLEMENT #define RTC_STATUS_ALARM1	    BIT(0)
19a3a42806SGregory CLEMENT #define RTC_STATUS_ALARM2	    BIT(1)
20a3a42806SGregory CLEMENT #define RTC_IRQ1_CONF	    0x4
2134f54f57SGregory CLEMENT #define RTC_IRQ2_CONF	    0x8
2275faea91SGregory CLEMENT #define RTC_IRQ_AL_EN		    BIT(0)
2375faea91SGregory CLEMENT #define RTC_IRQ_FREQ_EN		    BIT(1)
2475faea91SGregory CLEMENT #define RTC_IRQ_FREQ_1HZ	    BIT(2)
25f94ffbc2SRussell King #define RTC_CCR		    0x18
26f94ffbc2SRussell King #define RTC_CCR_MODE		    BIT(15)
271a990fefSBaruch Siach #define RTC_CONF_TEST	    0x1C
281a990fefSBaruch Siach #define RTC_NOMINAL_TIMING	    BIT(13)
2975faea91SGregory CLEMENT 
30a3a42806SGregory CLEMENT #define RTC_TIME	    0xC
31a3a42806SGregory CLEMENT #define RTC_ALARM1	    0x10
3234f54f57SGregory CLEMENT #define RTC_ALARM2	    0x14
3334f54f57SGregory CLEMENT 
3434f54f57SGregory CLEMENT /* Armada38x SoC registers  */
3575faea91SGregory CLEMENT #define RTC_38X_BRIDGE_TIMING_CTL   0x0
3675faea91SGregory CLEMENT #define RTC_38X_PERIOD_OFFS		0
3775faea91SGregory CLEMENT #define RTC_38X_PERIOD_MASK		(0x3FF << RTC_38X_PERIOD_OFFS)
3875faea91SGregory CLEMENT #define RTC_38X_READ_DELAY_OFFS		26
3975faea91SGregory CLEMENT #define RTC_38X_READ_DELAY_MASK		(0x1F << RTC_38X_READ_DELAY_OFFS)
40844a3073SGregory CLEMENT 
4134f54f57SGregory CLEMENT /* Armada 7K/8K registers  */
4234f54f57SGregory CLEMENT #define RTC_8K_BRIDGE_TIMING_CTL0    0x0
4334f54f57SGregory CLEMENT #define RTC_8K_WRCLK_PERIOD_OFFS	0
4434f54f57SGregory CLEMENT #define RTC_8K_WRCLK_PERIOD_MASK	(0xFFFF << RTC_8K_WRCLK_PERIOD_OFFS)
4534f54f57SGregory CLEMENT #define RTC_8K_WRCLK_SETUP_OFFS		16
4634f54f57SGregory CLEMENT #define RTC_8K_WRCLK_SETUP_MASK		(0xFFFF << RTC_8K_WRCLK_SETUP_OFFS)
4734f54f57SGregory CLEMENT #define RTC_8K_BRIDGE_TIMING_CTL1   0x4
4834f54f57SGregory CLEMENT #define RTC_8K_READ_DELAY_OFFS		0
4934f54f57SGregory CLEMENT #define RTC_8K_READ_DELAY_MASK		(0xFFFF << RTC_8K_READ_DELAY_OFFS)
5034f54f57SGregory CLEMENT 
5134f54f57SGregory CLEMENT #define RTC_8K_ISR		    0x10
5234f54f57SGregory CLEMENT #define RTC_8K_IMR		    0x14
5334f54f57SGregory CLEMENT #define RTC_8K_ALARM2			BIT(0)
5434f54f57SGregory CLEMENT 
55a3a42806SGregory CLEMENT #define SOC_RTC_INTERRUPT	    0x8
56a3a42806SGregory CLEMENT #define SOC_RTC_ALARM1			BIT(0)
57a3a42806SGregory CLEMENT #define SOC_RTC_ALARM2			BIT(1)
58a3a42806SGregory CLEMENT #define SOC_RTC_ALARM1_MASK		BIT(2)
59a3a42806SGregory CLEMENT #define SOC_RTC_ALARM2_MASK		BIT(3)
60a3a42806SGregory CLEMENT 
61844a3073SGregory CLEMENT #define SAMPLE_NR 100
62844a3073SGregory CLEMENT 
63844a3073SGregory CLEMENT struct value_to_freq {
64844a3073SGregory CLEMENT 	u32 value;
65844a3073SGregory CLEMENT 	u8 freq;
66844a3073SGregory CLEMENT };
67844a3073SGregory CLEMENT 
68a3a42806SGregory CLEMENT struct armada38x_rtc {
69a3a42806SGregory CLEMENT 	struct rtc_device   *rtc_dev;
70a3a42806SGregory CLEMENT 	void __iomem	    *regs;
71a3a42806SGregory CLEMENT 	void __iomem	    *regs_soc;
72a3a42806SGregory CLEMENT 	spinlock_t	    lock;
73a3a42806SGregory CLEMENT 	int		    irq;
741a990fefSBaruch Siach 	bool		    initialized;
75844a3073SGregory CLEMENT 	struct value_to_freq *val_to_freq;
76cd7629b2SStephen Boyd 	const struct armada38x_rtc_data *data;
7775faea91SGregory CLEMENT };
7875faea91SGregory CLEMENT 
7975faea91SGregory CLEMENT #define ALARM1	0
8034f54f57SGregory CLEMENT #define ALARM2	1
8134f54f57SGregory CLEMENT 
8275faea91SGregory CLEMENT #define ALARM_REG(base, alarm)	 ((base) + (alarm) * sizeof(u32))
8375faea91SGregory CLEMENT 
8475faea91SGregory CLEMENT struct armada38x_rtc_data {
8575faea91SGregory CLEMENT 	/* Initialize the RTC-MBUS bridge timing */
8675faea91SGregory CLEMENT 	void (*update_mbus_timing)(struct armada38x_rtc *rtc);
8775faea91SGregory CLEMENT 	u32 (*read_rtc_reg)(struct armada38x_rtc *rtc, u8 rtc_reg);
8875faea91SGregory CLEMENT 	void (*clear_isr)(struct armada38x_rtc *rtc);
8975faea91SGregory CLEMENT 	void (*unmask_interrupt)(struct armada38x_rtc *rtc);
9075faea91SGregory CLEMENT 	u32 alarm;
91a3a42806SGregory CLEMENT };
92a3a42806SGregory CLEMENT 
93a3a42806SGregory CLEMENT /*
94a3a42806SGregory CLEMENT  * According to the datasheet, the OS should wait 5us after every
95a3a42806SGregory CLEMENT  * register write to the RTC hard macro so that the required update
96a3a42806SGregory CLEMENT  * can occur without holding off the system bus
97844a3073SGregory CLEMENT  * According to errata RES-3124064, Write to any RTC register
98844a3073SGregory CLEMENT  * may fail. As a workaround, before writing to RTC
99844a3073SGregory CLEMENT  * register, issue a dummy write of 0x0 twice to RTC Status
100844a3073SGregory CLEMENT  * register.
101a3a42806SGregory CLEMENT  */
102844a3073SGregory CLEMENT 
rtc_delayed_write(u32 val,struct armada38x_rtc * rtc,int offset)103a3a42806SGregory CLEMENT static void rtc_delayed_write(u32 val, struct armada38x_rtc *rtc, int offset)
104a3a42806SGregory CLEMENT {
105844a3073SGregory CLEMENT 	writel(0, rtc->regs + RTC_STATUS);
106844a3073SGregory CLEMENT 	writel(0, rtc->regs + RTC_STATUS);
107a3a42806SGregory CLEMENT 	writel(val, rtc->regs + offset);
108a3a42806SGregory CLEMENT 	udelay(5);
109a3a42806SGregory CLEMENT }
110a3a42806SGregory CLEMENT 
111844a3073SGregory CLEMENT /* Update RTC-MBUS bridge timing parameters */
rtc_update_38x_mbus_timing_params(struct armada38x_rtc * rtc)11275faea91SGregory CLEMENT static void rtc_update_38x_mbus_timing_params(struct armada38x_rtc *rtc)
113844a3073SGregory CLEMENT {
114844a3073SGregory CLEMENT 	u32 reg;
115844a3073SGregory CLEMENT 
11675faea91SGregory CLEMENT 	reg = readl(rtc->regs_soc + RTC_38X_BRIDGE_TIMING_CTL);
11775faea91SGregory CLEMENT 	reg &= ~RTC_38X_PERIOD_MASK;
11875faea91SGregory CLEMENT 	reg |= 0x3FF << RTC_38X_PERIOD_OFFS; /* Maximum value */
11975faea91SGregory CLEMENT 	reg &= ~RTC_38X_READ_DELAY_MASK;
12075faea91SGregory CLEMENT 	reg |= 0x1F << RTC_38X_READ_DELAY_OFFS; /* Maximum value */
12175faea91SGregory CLEMENT 	writel(reg, rtc->regs_soc + RTC_38X_BRIDGE_TIMING_CTL);
122844a3073SGregory CLEMENT }
123844a3073SGregory CLEMENT 
rtc_update_8k_mbus_timing_params(struct armada38x_rtc * rtc)12434f54f57SGregory CLEMENT static void rtc_update_8k_mbus_timing_params(struct armada38x_rtc *rtc)
12534f54f57SGregory CLEMENT {
12634f54f57SGregory CLEMENT 	u32 reg;
12734f54f57SGregory CLEMENT 
12834f54f57SGregory CLEMENT 	reg = readl(rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL0);
12934f54f57SGregory CLEMENT 	reg &= ~RTC_8K_WRCLK_PERIOD_MASK;
13034f54f57SGregory CLEMENT 	reg |= 0x3FF << RTC_8K_WRCLK_PERIOD_OFFS;
13134f54f57SGregory CLEMENT 	reg &= ~RTC_8K_WRCLK_SETUP_MASK;
13234f54f57SGregory CLEMENT 	reg |= 0x29 << RTC_8K_WRCLK_SETUP_OFFS;
13334f54f57SGregory CLEMENT 	writel(reg, rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL0);
13434f54f57SGregory CLEMENT 
13534f54f57SGregory CLEMENT 	reg = readl(rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL1);
13634f54f57SGregory CLEMENT 	reg &= ~RTC_8K_READ_DELAY_MASK;
13734f54f57SGregory CLEMENT 	reg |= 0x3F << RTC_8K_READ_DELAY_OFFS;
13834f54f57SGregory CLEMENT 	writel(reg, rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL1);
13934f54f57SGregory CLEMENT }
14034f54f57SGregory CLEMENT 
read_rtc_register(struct armada38x_rtc * rtc,u8 rtc_reg)14134f54f57SGregory CLEMENT static u32 read_rtc_register(struct armada38x_rtc *rtc, u8 rtc_reg)
14234f54f57SGregory CLEMENT {
14334f54f57SGregory CLEMENT 	return readl(rtc->regs + rtc_reg);
14434f54f57SGregory CLEMENT }
14534f54f57SGregory CLEMENT 
read_rtc_register_38x_wa(struct armada38x_rtc * rtc,u8 rtc_reg)14675faea91SGregory CLEMENT static u32 read_rtc_register_38x_wa(struct armada38x_rtc *rtc, u8 rtc_reg)
147844a3073SGregory CLEMENT {
148844a3073SGregory CLEMENT 	int i, index_max = 0, max = 0;
149844a3073SGregory CLEMENT 
150844a3073SGregory CLEMENT 	for (i = 0; i < SAMPLE_NR; i++) {
151844a3073SGregory CLEMENT 		rtc->val_to_freq[i].value = readl(rtc->regs + rtc_reg);
152844a3073SGregory CLEMENT 		rtc->val_to_freq[i].freq = 0;
153844a3073SGregory CLEMENT 	}
154844a3073SGregory CLEMENT 
155844a3073SGregory CLEMENT 	for (i = 0; i < SAMPLE_NR; i++) {
156844a3073SGregory CLEMENT 		int j = 0;
157844a3073SGregory CLEMENT 		u32 value = rtc->val_to_freq[i].value;
158844a3073SGregory CLEMENT 
159844a3073SGregory CLEMENT 		while (rtc->val_to_freq[j].freq) {
160844a3073SGregory CLEMENT 			if (rtc->val_to_freq[j].value == value) {
161844a3073SGregory CLEMENT 				rtc->val_to_freq[j].freq++;
162844a3073SGregory CLEMENT 				break;
163844a3073SGregory CLEMENT 			}
164844a3073SGregory CLEMENT 			j++;
165844a3073SGregory CLEMENT 		}
166844a3073SGregory CLEMENT 
167844a3073SGregory CLEMENT 		if (!rtc->val_to_freq[j].freq) {
168844a3073SGregory CLEMENT 			rtc->val_to_freq[j].value = value;
169844a3073SGregory CLEMENT 			rtc->val_to_freq[j].freq = 1;
170844a3073SGregory CLEMENT 		}
171844a3073SGregory CLEMENT 
172844a3073SGregory CLEMENT 		if (rtc->val_to_freq[j].freq > max) {
173844a3073SGregory CLEMENT 			index_max = j;
174844a3073SGregory CLEMENT 			max = rtc->val_to_freq[j].freq;
175844a3073SGregory CLEMENT 		}
176844a3073SGregory CLEMENT 
177844a3073SGregory CLEMENT 		/*
178844a3073SGregory CLEMENT 		 * If a value already has half of the sample this is the most
179844a3073SGregory CLEMENT 		 * frequent one and we can stop the research right now
180844a3073SGregory CLEMENT 		 */
181844a3073SGregory CLEMENT 		if (max > SAMPLE_NR / 2)
182844a3073SGregory CLEMENT 			break;
183844a3073SGregory CLEMENT 	}
184844a3073SGregory CLEMENT 
185844a3073SGregory CLEMENT 	return rtc->val_to_freq[index_max].value;
186844a3073SGregory CLEMENT }
187844a3073SGregory CLEMENT 
armada38x_clear_isr(struct armada38x_rtc * rtc)18875faea91SGregory CLEMENT static void armada38x_clear_isr(struct armada38x_rtc *rtc)
18975faea91SGregory CLEMENT {
19075faea91SGregory CLEMENT 	u32 val = readl(rtc->regs_soc + SOC_RTC_INTERRUPT);
19175faea91SGregory CLEMENT 
19275faea91SGregory CLEMENT 	writel(val & ~SOC_RTC_ALARM1, rtc->regs_soc + SOC_RTC_INTERRUPT);
19375faea91SGregory CLEMENT }
19475faea91SGregory CLEMENT 
armada38x_unmask_interrupt(struct armada38x_rtc * rtc)19575faea91SGregory CLEMENT static void armada38x_unmask_interrupt(struct armada38x_rtc *rtc)
19675faea91SGregory CLEMENT {
19775faea91SGregory CLEMENT 	u32 val = readl(rtc->regs_soc + SOC_RTC_INTERRUPT);
19875faea91SGregory CLEMENT 
19975faea91SGregory CLEMENT 	writel(val | SOC_RTC_ALARM1_MASK, rtc->regs_soc + SOC_RTC_INTERRUPT);
20075faea91SGregory CLEMENT }
20134f54f57SGregory CLEMENT 
armada8k_clear_isr(struct armada38x_rtc * rtc)20234f54f57SGregory CLEMENT static void armada8k_clear_isr(struct armada38x_rtc *rtc)
20334f54f57SGregory CLEMENT {
20434f54f57SGregory CLEMENT 	writel(RTC_8K_ALARM2, rtc->regs_soc + RTC_8K_ISR);
20534f54f57SGregory CLEMENT }
20634f54f57SGregory CLEMENT 
armada8k_unmask_interrupt(struct armada38x_rtc * rtc)20734f54f57SGregory CLEMENT static void armada8k_unmask_interrupt(struct armada38x_rtc *rtc)
20834f54f57SGregory CLEMENT {
20934f54f57SGregory CLEMENT 	writel(RTC_8K_ALARM2, rtc->regs_soc + RTC_8K_IMR);
21034f54f57SGregory CLEMENT }
21134f54f57SGregory CLEMENT 
armada38x_rtc_read_time(struct device * dev,struct rtc_time * tm)212a3a42806SGregory CLEMENT static int armada38x_rtc_read_time(struct device *dev, struct rtc_time *tm)
213a3a42806SGregory CLEMENT {
214a3a42806SGregory CLEMENT 	struct armada38x_rtc *rtc = dev_get_drvdata(dev);
215844a3073SGregory CLEMENT 	unsigned long time, flags;
216a3a42806SGregory CLEMENT 
2170c6e7183SNadav Haklai 	spin_lock_irqsave(&rtc->lock, flags);
21875faea91SGregory CLEMENT 	time = rtc->data->read_rtc_reg(rtc, RTC_TIME);
2190c6e7183SNadav Haklai 	spin_unlock_irqrestore(&rtc->lock, flags);
220a3a42806SGregory CLEMENT 
221f6e3d773SAlexandre Belloni 	rtc_time64_to_tm(time, tm);
222a3a42806SGregory CLEMENT 
223a3a42806SGregory CLEMENT 	return 0;
224a3a42806SGregory CLEMENT }
225a3a42806SGregory CLEMENT 
armada38x_rtc_reset(struct armada38x_rtc * rtc)2261a990fefSBaruch Siach static void armada38x_rtc_reset(struct armada38x_rtc *rtc)
2271a990fefSBaruch Siach {
2281a990fefSBaruch Siach 	u32 reg;
2291a990fefSBaruch Siach 
2301a990fefSBaruch Siach 	reg = rtc->data->read_rtc_reg(rtc, RTC_CONF_TEST);
2311a990fefSBaruch Siach 	/* If bits [7:0] are non-zero, assume RTC was uninitialized */
2321a990fefSBaruch Siach 	if (reg & 0xff) {
2331a990fefSBaruch Siach 		rtc_delayed_write(0, rtc, RTC_CONF_TEST);
2341a990fefSBaruch Siach 		msleep(500); /* Oscillator startup time */
2351a990fefSBaruch Siach 		rtc_delayed_write(0, rtc, RTC_TIME);
2361a990fefSBaruch Siach 		rtc_delayed_write(SOC_RTC_ALARM1 | SOC_RTC_ALARM2, rtc,
2371a990fefSBaruch Siach 				  RTC_STATUS);
2381a990fefSBaruch Siach 		rtc_delayed_write(RTC_NOMINAL_TIMING, rtc, RTC_CCR);
2391a990fefSBaruch Siach 	}
2401a990fefSBaruch Siach 	rtc->initialized = true;
2411a990fefSBaruch Siach }
2421a990fefSBaruch Siach 
armada38x_rtc_set_time(struct device * dev,struct rtc_time * tm)243a3a42806SGregory CLEMENT static int armada38x_rtc_set_time(struct device *dev, struct rtc_time *tm)
244a3a42806SGregory CLEMENT {
245a3a42806SGregory CLEMENT 	struct armada38x_rtc *rtc = dev_get_drvdata(dev);
2460c6e7183SNadav Haklai 	unsigned long time, flags;
247a3a42806SGregory CLEMENT 
248f6e3d773SAlexandre Belloni 	time = rtc_tm_to_time64(tm);
249844a3073SGregory CLEMENT 
2501a990fefSBaruch Siach 	if (!rtc->initialized)
2511a990fefSBaruch Siach 		armada38x_rtc_reset(rtc);
2521a990fefSBaruch Siach 
2530c6e7183SNadav Haklai 	spin_lock_irqsave(&rtc->lock, flags);
254a3a42806SGregory CLEMENT 	rtc_delayed_write(time, rtc, RTC_TIME);
2550c6e7183SNadav Haklai 	spin_unlock_irqrestore(&rtc->lock, flags);
256a3a42806SGregory CLEMENT 
257f6e3d773SAlexandre Belloni 	return 0;
258a3a42806SGregory CLEMENT }
259a3a42806SGregory CLEMENT 
armada38x_rtc_read_alarm(struct device * dev,struct rtc_wkalrm * alrm)260a3a42806SGregory CLEMENT static int armada38x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
261a3a42806SGregory CLEMENT {
262a3a42806SGregory CLEMENT 	struct armada38x_rtc *rtc = dev_get_drvdata(dev);
263a3a42806SGregory CLEMENT 	unsigned long time, flags;
26475faea91SGregory CLEMENT 	u32 reg = ALARM_REG(RTC_ALARM1, rtc->data->alarm);
26575faea91SGregory CLEMENT 	u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm);
266a3a42806SGregory CLEMENT 	u32 val;
267a3a42806SGregory CLEMENT 
268a3a42806SGregory CLEMENT 	spin_lock_irqsave(&rtc->lock, flags);
269a3a42806SGregory CLEMENT 
27075faea91SGregory CLEMENT 	time = rtc->data->read_rtc_reg(rtc, reg);
27175faea91SGregory CLEMENT 	val = rtc->data->read_rtc_reg(rtc, reg_irq) & RTC_IRQ_AL_EN;
272a3a42806SGregory CLEMENT 
273a3a42806SGregory CLEMENT 	spin_unlock_irqrestore(&rtc->lock, flags);
274a3a42806SGregory CLEMENT 
275a3a42806SGregory CLEMENT 	alrm->enabled = val ? 1 : 0;
276f6e3d773SAlexandre Belloni 	rtc_time64_to_tm(time,  &alrm->time);
277a3a42806SGregory CLEMENT 
278a3a42806SGregory CLEMENT 	return 0;
279a3a42806SGregory CLEMENT }
280a3a42806SGregory CLEMENT 
armada38x_rtc_set_alarm(struct device * dev,struct rtc_wkalrm * alrm)281a3a42806SGregory CLEMENT static int armada38x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
282a3a42806SGregory CLEMENT {
283a3a42806SGregory CLEMENT 	struct armada38x_rtc *rtc = dev_get_drvdata(dev);
28475faea91SGregory CLEMENT 	u32 reg = ALARM_REG(RTC_ALARM1, rtc->data->alarm);
28575faea91SGregory CLEMENT 	u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm);
286a3a42806SGregory CLEMENT 	unsigned long time, flags;
287a3a42806SGregory CLEMENT 
288f6e3d773SAlexandre Belloni 	time = rtc_tm_to_time64(&alrm->time);
289a3a42806SGregory CLEMENT 
290a3a42806SGregory CLEMENT 	spin_lock_irqsave(&rtc->lock, flags);
291a3a42806SGregory CLEMENT 
29275faea91SGregory CLEMENT 	rtc_delayed_write(time, rtc, reg);
293a3a42806SGregory CLEMENT 
294a3a42806SGregory CLEMENT 	if (alrm->enabled) {
29575faea91SGregory CLEMENT 		rtc_delayed_write(RTC_IRQ_AL_EN, rtc, reg_irq);
29675faea91SGregory CLEMENT 		rtc->data->unmask_interrupt(rtc);
297a3a42806SGregory CLEMENT 	}
298a3a42806SGregory CLEMENT 
299a3a42806SGregory CLEMENT 	spin_unlock_irqrestore(&rtc->lock, flags);
300a3a42806SGregory CLEMENT 
301f6e3d773SAlexandre Belloni 	return 0;
302a3a42806SGregory CLEMENT }
303a3a42806SGregory CLEMENT 
armada38x_rtc_alarm_irq_enable(struct device * dev,unsigned int enabled)304a3a42806SGregory CLEMENT static int armada38x_rtc_alarm_irq_enable(struct device *dev,
305a3a42806SGregory CLEMENT 					 unsigned int enabled)
306a3a42806SGregory CLEMENT {
307a3a42806SGregory CLEMENT 	struct armada38x_rtc *rtc = dev_get_drvdata(dev);
30875faea91SGregory CLEMENT 	u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm);
309a3a42806SGregory CLEMENT 	unsigned long flags;
310a3a42806SGregory CLEMENT 
311a3a42806SGregory CLEMENT 	spin_lock_irqsave(&rtc->lock, flags);
312a3a42806SGregory CLEMENT 
313a3a42806SGregory CLEMENT 	if (enabled)
31475faea91SGregory CLEMENT 		rtc_delayed_write(RTC_IRQ_AL_EN, rtc, reg_irq);
315a3a42806SGregory CLEMENT 	else
31675faea91SGregory CLEMENT 		rtc_delayed_write(0, rtc, reg_irq);
317a3a42806SGregory CLEMENT 
318a3a42806SGregory CLEMENT 	spin_unlock_irqrestore(&rtc->lock, flags);
319a3a42806SGregory CLEMENT 
320a3a42806SGregory CLEMENT 	return 0;
321a3a42806SGregory CLEMENT }
322a3a42806SGregory CLEMENT 
armada38x_rtc_alarm_irq(int irq,void * data)323a3a42806SGregory CLEMENT static irqreturn_t armada38x_rtc_alarm_irq(int irq, void *data)
324a3a42806SGregory CLEMENT {
325a3a42806SGregory CLEMENT 	struct armada38x_rtc *rtc = data;
326a3a42806SGregory CLEMENT 	u32 val;
327a3a42806SGregory CLEMENT 	int event = RTC_IRQF | RTC_AF;
32875faea91SGregory CLEMENT 	u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm);
329a3a42806SGregory CLEMENT 
330a3a42806SGregory CLEMENT 	dev_dbg(&rtc->rtc_dev->dev, "%s:irq(%d)\n", __func__, irq);
331a3a42806SGregory CLEMENT 
332a3a42806SGregory CLEMENT 	spin_lock(&rtc->lock);
333a3a42806SGregory CLEMENT 
33475faea91SGregory CLEMENT 	rtc->data->clear_isr(rtc);
33575faea91SGregory CLEMENT 	val = rtc->data->read_rtc_reg(rtc, reg_irq);
33675faea91SGregory CLEMENT 	/* disable all the interrupts for alarm*/
33775faea91SGregory CLEMENT 	rtc_delayed_write(0, rtc, reg_irq);
338a3a42806SGregory CLEMENT 	/* Ack the event */
33975faea91SGregory CLEMENT 	rtc_delayed_write(1 << rtc->data->alarm, rtc, RTC_STATUS);
340a3a42806SGregory CLEMENT 
341a3a42806SGregory CLEMENT 	spin_unlock(&rtc->lock);
342a3a42806SGregory CLEMENT 
34375faea91SGregory CLEMENT 	if (val & RTC_IRQ_FREQ_EN) {
34475faea91SGregory CLEMENT 		if (val & RTC_IRQ_FREQ_1HZ)
345a3a42806SGregory CLEMENT 			event |= RTC_UF;
346a3a42806SGregory CLEMENT 		else
347a3a42806SGregory CLEMENT 			event |= RTC_PF;
348a3a42806SGregory CLEMENT 	}
349a3a42806SGregory CLEMENT 
350a3a42806SGregory CLEMENT 	rtc_update_irq(rtc->rtc_dev, 1, event);
351a3a42806SGregory CLEMENT 
352a3a42806SGregory CLEMENT 	return IRQ_HANDLED;
353a3a42806SGregory CLEMENT }
354a3a42806SGregory CLEMENT 
355f94ffbc2SRussell King /*
356f94ffbc2SRussell King  * The information given in the Armada 388 functional spec is complex.
357f94ffbc2SRussell King  * They give two different formulas for calculating the offset value,
358f94ffbc2SRussell King  * but when considering "Offset" as an 8-bit signed integer, they both
359f94ffbc2SRussell King  * reduce down to (we shall rename "Offset" as "val" here):
360f94ffbc2SRussell King  *
361f94ffbc2SRussell King  *   val = (f_ideal / f_measured - 1) / resolution   where f_ideal = 32768
362f94ffbc2SRussell King  *
363f94ffbc2SRussell King  * Converting to time, f = 1/t:
364f94ffbc2SRussell King  *   val = (t_measured / t_ideal - 1) / resolution   where t_ideal = 1/32768
365f94ffbc2SRussell King  *
366f94ffbc2SRussell King  *   =>  t_measured / t_ideal = val * resolution + 1
367f94ffbc2SRussell King  *
368f94ffbc2SRussell King  * "offset" in the RTC interface is defined as:
369f94ffbc2SRussell King  *   t = t0 * (1 + offset * 1e-9)
370f94ffbc2SRussell King  * where t is the desired period, t0 is the measured period with a zero
371f94ffbc2SRussell King  * offset, which is t_measured above. With t0 = t_measured and t = t_ideal,
372f94ffbc2SRussell King  *   offset = (t_ideal / t_measured - 1) / 1e-9
373f94ffbc2SRussell King  *
374f94ffbc2SRussell King  *   => t_ideal / t_measured = offset * 1e-9 + 1
375f94ffbc2SRussell King  *
376f94ffbc2SRussell King  * so:
377f94ffbc2SRussell King  *
378f94ffbc2SRussell King  *   offset * 1e-9 + 1 = 1 / (val * resolution + 1)
379f94ffbc2SRussell King  *
380f94ffbc2SRussell King  * We want "resolution" to be an integer, so resolution = R * 1e-9, giving
381f94ffbc2SRussell King  *   offset = 1e18 / (val * R + 1e9) - 1e9
382f94ffbc2SRussell King  *   val = (1e18 / (offset + 1e9) - 1e9) / R
383f94ffbc2SRussell King  * with a common transformation:
384f94ffbc2SRussell King  *   f(x) = 1e18 / (x + 1e9) - 1e9
385f94ffbc2SRussell King  *   offset = f(val * R)
386f94ffbc2SRussell King  *   val = f(offset) / R
387f94ffbc2SRussell King  *
388f94ffbc2SRussell King  * Armada 38x supports two modes, fine mode (954ppb) and coarse mode (3815ppb).
389f94ffbc2SRussell King  */
armada38x_ppb_convert(long ppb)390f94ffbc2SRussell King static long armada38x_ppb_convert(long ppb)
391f94ffbc2SRussell King {
392f94ffbc2SRussell King 	long div = ppb + 1000000000L;
393f94ffbc2SRussell King 
394f94ffbc2SRussell King 	return div_s64(1000000000000000000LL + div / 2, div) - 1000000000L;
395f94ffbc2SRussell King }
396f94ffbc2SRussell King 
armada38x_rtc_read_offset(struct device * dev,long * offset)397f94ffbc2SRussell King static int armada38x_rtc_read_offset(struct device *dev, long *offset)
398f94ffbc2SRussell King {
399f94ffbc2SRussell King 	struct armada38x_rtc *rtc = dev_get_drvdata(dev);
400f94ffbc2SRussell King 	unsigned long ccr, flags;
401f94ffbc2SRussell King 	long ppb_cor;
402f94ffbc2SRussell King 
403f94ffbc2SRussell King 	spin_lock_irqsave(&rtc->lock, flags);
404f94ffbc2SRussell King 	ccr = rtc->data->read_rtc_reg(rtc, RTC_CCR);
405f94ffbc2SRussell King 	spin_unlock_irqrestore(&rtc->lock, flags);
406f94ffbc2SRussell King 
407f94ffbc2SRussell King 	ppb_cor = (ccr & RTC_CCR_MODE ? 3815 : 954) * (s8)ccr;
408f94ffbc2SRussell King 	/* ppb_cor + 1000000000L can never be zero */
409f94ffbc2SRussell King 	*offset = armada38x_ppb_convert(ppb_cor);
410f94ffbc2SRussell King 
411f94ffbc2SRussell King 	return 0;
412f94ffbc2SRussell King }
413f94ffbc2SRussell King 
armada38x_rtc_set_offset(struct device * dev,long offset)414f94ffbc2SRussell King static int armada38x_rtc_set_offset(struct device *dev, long offset)
415f94ffbc2SRussell King {
416f94ffbc2SRussell King 	struct armada38x_rtc *rtc = dev_get_drvdata(dev);
417f94ffbc2SRussell King 	unsigned long ccr = 0;
418f94ffbc2SRussell King 	long ppb_cor, off;
419f94ffbc2SRussell King 
420f94ffbc2SRussell King 	/*
421f94ffbc2SRussell King 	 * The maximum ppb_cor is -128 * 3815 .. 127 * 3815, but we
422f94ffbc2SRussell King 	 * need to clamp the input.  This equates to -484270 .. 488558.
423f94ffbc2SRussell King 	 * Not only is this to stop out of range "off" but also to
424f94ffbc2SRussell King 	 * avoid the division by zero in armada38x_ppb_convert().
425f94ffbc2SRussell King 	 */
426f94ffbc2SRussell King 	offset = clamp(offset, -484270L, 488558L);
427f94ffbc2SRussell King 
428f94ffbc2SRussell King 	ppb_cor = armada38x_ppb_convert(offset);
429f94ffbc2SRussell King 
430f94ffbc2SRussell King 	/*
431f94ffbc2SRussell King 	 * Use low update mode where possible, which gives a better
432f94ffbc2SRussell King 	 * resolution of correction.
433f94ffbc2SRussell King 	 */
434f94ffbc2SRussell King 	off = DIV_ROUND_CLOSEST(ppb_cor, 954);
435f94ffbc2SRussell King 	if (off > 127 || off < -128) {
436f94ffbc2SRussell King 		ccr = RTC_CCR_MODE;
437f94ffbc2SRussell King 		off = DIV_ROUND_CLOSEST(ppb_cor, 3815);
438f94ffbc2SRussell King 	}
439f94ffbc2SRussell King 
440f94ffbc2SRussell King 	/*
441f94ffbc2SRussell King 	 * Armada 388 requires a bit pattern in bits 14..8 depending on
442f94ffbc2SRussell King 	 * the sign bit: { 0, ~S, S, S, S, S, S }
443f94ffbc2SRussell King 	 */
444f94ffbc2SRussell King 	ccr |= (off & 0x3fff) ^ 0x2000;
445f94ffbc2SRussell King 	rtc_delayed_write(ccr, rtc, RTC_CCR);
446f94ffbc2SRussell King 
447f94ffbc2SRussell King 	return 0;
448f94ffbc2SRussell King }
449f94ffbc2SRussell King 
450d748c981SRussell King static const struct rtc_class_ops armada38x_rtc_ops = {
451a3a42806SGregory CLEMENT 	.read_time = armada38x_rtc_read_time,
452a3a42806SGregory CLEMENT 	.set_time = armada38x_rtc_set_time,
453a3a42806SGregory CLEMENT 	.read_alarm = armada38x_rtc_read_alarm,
454a3a42806SGregory CLEMENT 	.set_alarm = armada38x_rtc_set_alarm,
455a3a42806SGregory CLEMENT 	.alarm_irq_enable = armada38x_rtc_alarm_irq_enable,
456f94ffbc2SRussell King 	.read_offset = armada38x_rtc_read_offset,
457f94ffbc2SRussell King 	.set_offset = armada38x_rtc_set_offset,
458a3a42806SGregory CLEMENT };
459a3a42806SGregory CLEMENT 
46075faea91SGregory CLEMENT static const struct armada38x_rtc_data armada38x_data = {
46175faea91SGregory CLEMENT 	.update_mbus_timing = rtc_update_38x_mbus_timing_params,
46275faea91SGregory CLEMENT 	.read_rtc_reg = read_rtc_register_38x_wa,
46375faea91SGregory CLEMENT 	.clear_isr = armada38x_clear_isr,
46475faea91SGregory CLEMENT 	.unmask_interrupt = armada38x_unmask_interrupt,
46575faea91SGregory CLEMENT 	.alarm = ALARM1,
46675faea91SGregory CLEMENT };
46775faea91SGregory CLEMENT 
46834f54f57SGregory CLEMENT static const struct armada38x_rtc_data armada8k_data = {
46934f54f57SGregory CLEMENT 	.update_mbus_timing = rtc_update_8k_mbus_timing_params,
47034f54f57SGregory CLEMENT 	.read_rtc_reg = read_rtc_register,
47134f54f57SGregory CLEMENT 	.clear_isr = armada8k_clear_isr,
47234f54f57SGregory CLEMENT 	.unmask_interrupt = armada8k_unmask_interrupt,
47334f54f57SGregory CLEMENT 	.alarm = ALARM2,
47434f54f57SGregory CLEMENT };
47534f54f57SGregory CLEMENT 
47675faea91SGregory CLEMENT static const struct of_device_id armada38x_rtc_of_match_table[] = {
47775faea91SGregory CLEMENT 	{
47875faea91SGregory CLEMENT 		.compatible = "marvell,armada-380-rtc",
47975faea91SGregory CLEMENT 		.data = &armada38x_data,
48075faea91SGregory CLEMENT 	},
48134f54f57SGregory CLEMENT 	{
48234f54f57SGregory CLEMENT 		.compatible = "marvell,armada-8k-rtc",
48334f54f57SGregory CLEMENT 		.data = &armada8k_data,
48434f54f57SGregory CLEMENT 	},
48575faea91SGregory CLEMENT 	{}
48675faea91SGregory CLEMENT };
48775faea91SGregory CLEMENT MODULE_DEVICE_TABLE(of, armada38x_rtc_of_match_table);
48875faea91SGregory CLEMENT 
armada38x_rtc_probe(struct platform_device * pdev)489a3a42806SGregory CLEMENT static __init int armada38x_rtc_probe(struct platform_device *pdev)
490a3a42806SGregory CLEMENT {
491a3a42806SGregory CLEMENT 	struct armada38x_rtc *rtc;
49275faea91SGregory CLEMENT 
493a3a42806SGregory CLEMENT 	rtc = devm_kzalloc(&pdev->dev, sizeof(struct armada38x_rtc),
494a3a42806SGregory CLEMENT 			    GFP_KERNEL);
495a3a42806SGregory CLEMENT 	if (!rtc)
496a3a42806SGregory CLEMENT 		return -ENOMEM;
497a3a42806SGregory CLEMENT 
498cd7629b2SStephen Boyd 	rtc->data = of_device_get_match_data(&pdev->dev);
499cd7629b2SStephen Boyd 
500844a3073SGregory CLEMENT 	rtc->val_to_freq = devm_kcalloc(&pdev->dev, SAMPLE_NR,
501844a3073SGregory CLEMENT 				sizeof(struct value_to_freq), GFP_KERNEL);
502844a3073SGregory CLEMENT 	if (!rtc->val_to_freq)
503844a3073SGregory CLEMENT 		return -ENOMEM;
504844a3073SGregory CLEMENT 
505a3a42806SGregory CLEMENT 	spin_lock_init(&rtc->lock);
506a3a42806SGregory CLEMENT 
507e99ab4abSYe Xingchen 	rtc->regs = devm_platform_ioremap_resource_byname(pdev, "rtc");
508a3a42806SGregory CLEMENT 	if (IS_ERR(rtc->regs))
509a3a42806SGregory CLEMENT 		return PTR_ERR(rtc->regs);
510e99ab4abSYe Xingchen 	rtc->regs_soc = devm_platform_ioremap_resource_byname(pdev, "rtc-soc");
511a3a42806SGregory CLEMENT 	if (IS_ERR(rtc->regs_soc))
512a3a42806SGregory CLEMENT 		return PTR_ERR(rtc->regs_soc);
513a3a42806SGregory CLEMENT 
514a3a42806SGregory CLEMENT 	rtc->irq = platform_get_irq(pdev, 0);
515faac9102SStephen Boyd 	if (rtc->irq < 0)
516a3a42806SGregory CLEMENT 		return rtc->irq;
5177d61cbb9SAlexandre Belloni 
5187d61cbb9SAlexandre Belloni 	rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev);
5197d61cbb9SAlexandre Belloni 	if (IS_ERR(rtc->rtc_dev))
5207d61cbb9SAlexandre Belloni 		return PTR_ERR(rtc->rtc_dev);
5217d61cbb9SAlexandre Belloni 
522a3a42806SGregory CLEMENT 	if (devm_request_irq(&pdev->dev, rtc->irq, armada38x_rtc_alarm_irq,
523a3a42806SGregory CLEMENT 				0, pdev->name, rtc) < 0) {
524a3a42806SGregory CLEMENT 		dev_warn(&pdev->dev, "Interrupt not available.\n");
525a3a42806SGregory CLEMENT 		rtc->irq = -1;
526d748c981SRussell King 	}
527d748c981SRussell King 	platform_set_drvdata(pdev, rtc);
528d748c981SRussell King 
52995151801SAlexandre Belloni 	if (rtc->irq != -1)
530d748c981SRussell King 		device_init_wakeup(&pdev->dev, 1);
53195151801SAlexandre Belloni 	else
53295151801SAlexandre Belloni 		clear_bit(RTC_FEATURE_ALARM, rtc->rtc_dev->features);
53375faea91SGregory CLEMENT 
534844a3073SGregory CLEMENT 	/* Update RTC-MBUS bridge timing parameters */
53575faea91SGregory CLEMENT 	rtc->data->update_mbus_timing(rtc);
536844a3073SGregory CLEMENT 
53795151801SAlexandre Belloni 	rtc->rtc_dev->ops = &armada38x_rtc_ops;
538ef2a7176SAlexandre Belloni 	rtc->rtc_dev->range_max = U32_MAX;
539ef2a7176SAlexandre Belloni 
540fdcfd854SBartosz Golaszewski 	return devm_rtc_register_device(rtc->rtc_dev);
541a3a42806SGregory CLEMENT }
542a3a42806SGregory CLEMENT 
543a3a42806SGregory CLEMENT #ifdef CONFIG_PM_SLEEP
armada38x_rtc_suspend(struct device * dev)544a3a42806SGregory CLEMENT static int armada38x_rtc_suspend(struct device *dev)
545a3a42806SGregory CLEMENT {
546a3a42806SGregory CLEMENT 	if (device_may_wakeup(dev)) {
547a3a42806SGregory CLEMENT 		struct armada38x_rtc *rtc = dev_get_drvdata(dev);
548a3a42806SGregory CLEMENT 
549a3a42806SGregory CLEMENT 		return enable_irq_wake(rtc->irq);
550a3a42806SGregory CLEMENT 	}
551a3a42806SGregory CLEMENT 
552a3a42806SGregory CLEMENT 	return 0;
553a3a42806SGregory CLEMENT }
554a3a42806SGregory CLEMENT 
armada38x_rtc_resume(struct device * dev)555a3a42806SGregory CLEMENT static int armada38x_rtc_resume(struct device *dev)
556a3a42806SGregory CLEMENT {
557a3a42806SGregory CLEMENT 	if (device_may_wakeup(dev)) {
558a3a42806SGregory CLEMENT 		struct armada38x_rtc *rtc = dev_get_drvdata(dev);
559a3a42806SGregory CLEMENT 
560844a3073SGregory CLEMENT 		/* Update RTC-MBUS bridge timing parameters */
56175faea91SGregory CLEMENT 		rtc->data->update_mbus_timing(rtc);
562844a3073SGregory CLEMENT 
563a3a42806SGregory CLEMENT 		return disable_irq_wake(rtc->irq);
564a3a42806SGregory CLEMENT 	}
565a3a42806SGregory CLEMENT 
566a3a42806SGregory CLEMENT 	return 0;
567a3a42806SGregory CLEMENT }
568a3a42806SGregory CLEMENT #endif
569a3a42806SGregory CLEMENT 
570a3a42806SGregory CLEMENT static SIMPLE_DEV_PM_OPS(armada38x_rtc_pm_ops,
571a3a42806SGregory CLEMENT 			 armada38x_rtc_suspend, armada38x_rtc_resume);
572a3a42806SGregory CLEMENT 
573a3a42806SGregory CLEMENT static struct platform_driver armada38x_rtc_driver = {
574a3a42806SGregory CLEMENT 	.driver		= {
575a3a42806SGregory CLEMENT 		.name	= "armada38x-rtc",
576a3a42806SGregory CLEMENT 		.pm	= &armada38x_rtc_pm_ops,
577*4f3688dcSZhu Wang 		.of_match_table = armada38x_rtc_of_match_table,
578a3a42806SGregory CLEMENT 	},
579a3a42806SGregory CLEMENT };
580a3a42806SGregory CLEMENT 
581a3a42806SGregory CLEMENT module_platform_driver_probe(armada38x_rtc_driver, armada38x_rtc_probe);
582a3a42806SGregory CLEMENT 
583a3a42806SGregory CLEMENT MODULE_DESCRIPTION("Marvell Armada 38x RTC driver");
584a3a42806SGregory CLEMENT MODULE_AUTHOR("Gregory CLEMENT <gregory.clement@free-electrons.com>");
585a3a42806SGregory CLEMENT MODULE_LICENSE("GPL");
586