xref: /linux/drivers/rtc/rtc-armada38x.c (revision f6e3d773e10bc4c7c19083cfdd3e275cfece58de)
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>
1975faea91SGregory 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
2734f54f57SGregory CLEMENT #define RTC_IRQ2_CONF	    0x8
2875faea91SGregory CLEMENT #define RTC_IRQ_AL_EN		    BIT(0)
2975faea91SGregory CLEMENT #define RTC_IRQ_FREQ_EN		    BIT(1)
3075faea91SGregory CLEMENT #define RTC_IRQ_FREQ_1HZ	    BIT(2)
31f94ffbc2SRussell King #define RTC_CCR		    0x18
32f94ffbc2SRussell King #define RTC_CCR_MODE		    BIT(15)
331a990fefSBaruch Siach #define RTC_CONF_TEST	    0x1C
341a990fefSBaruch Siach #define RTC_NOMINAL_TIMING	    BIT(13)
3575faea91SGregory CLEMENT 
36a3a42806SGregory CLEMENT #define RTC_TIME	    0xC
37a3a42806SGregory CLEMENT #define RTC_ALARM1	    0x10
3834f54f57SGregory CLEMENT #define RTC_ALARM2	    0x14
3934f54f57SGregory CLEMENT 
4034f54f57SGregory CLEMENT /* Armada38x SoC registers  */
4175faea91SGregory CLEMENT #define RTC_38X_BRIDGE_TIMING_CTL   0x0
4275faea91SGregory CLEMENT #define RTC_38X_PERIOD_OFFS		0
4375faea91SGregory CLEMENT #define RTC_38X_PERIOD_MASK		(0x3FF << RTC_38X_PERIOD_OFFS)
4475faea91SGregory CLEMENT #define RTC_38X_READ_DELAY_OFFS		26
4575faea91SGregory CLEMENT #define RTC_38X_READ_DELAY_MASK		(0x1F << RTC_38X_READ_DELAY_OFFS)
46844a3073SGregory CLEMENT 
4734f54f57SGregory CLEMENT /* Armada 7K/8K registers  */
4834f54f57SGregory CLEMENT #define RTC_8K_BRIDGE_TIMING_CTL0    0x0
4934f54f57SGregory CLEMENT #define RTC_8K_WRCLK_PERIOD_OFFS	0
5034f54f57SGregory CLEMENT #define RTC_8K_WRCLK_PERIOD_MASK	(0xFFFF << RTC_8K_WRCLK_PERIOD_OFFS)
5134f54f57SGregory CLEMENT #define RTC_8K_WRCLK_SETUP_OFFS		16
5234f54f57SGregory CLEMENT #define RTC_8K_WRCLK_SETUP_MASK		(0xFFFF << RTC_8K_WRCLK_SETUP_OFFS)
5334f54f57SGregory CLEMENT #define RTC_8K_BRIDGE_TIMING_CTL1   0x4
5434f54f57SGregory CLEMENT #define RTC_8K_READ_DELAY_OFFS		0
5534f54f57SGregory CLEMENT #define RTC_8K_READ_DELAY_MASK		(0xFFFF << RTC_8K_READ_DELAY_OFFS)
5634f54f57SGregory CLEMENT 
5734f54f57SGregory CLEMENT #define RTC_8K_ISR		    0x10
5834f54f57SGregory CLEMENT #define RTC_8K_IMR		    0x14
5934f54f57SGregory CLEMENT #define RTC_8K_ALARM2			BIT(0)
6034f54f57SGregory CLEMENT 
61a3a42806SGregory CLEMENT #define SOC_RTC_INTERRUPT	    0x8
62a3a42806SGregory CLEMENT #define SOC_RTC_ALARM1			BIT(0)
63a3a42806SGregory CLEMENT #define SOC_RTC_ALARM2			BIT(1)
64a3a42806SGregory CLEMENT #define SOC_RTC_ALARM1_MASK		BIT(2)
65a3a42806SGregory CLEMENT #define SOC_RTC_ALARM2_MASK		BIT(3)
66a3a42806SGregory CLEMENT 
67844a3073SGregory CLEMENT #define SAMPLE_NR 100
68844a3073SGregory CLEMENT 
69844a3073SGregory CLEMENT struct value_to_freq {
70844a3073SGregory CLEMENT 	u32 value;
71844a3073SGregory CLEMENT 	u8 freq;
72844a3073SGregory CLEMENT };
73844a3073SGregory CLEMENT 
74a3a42806SGregory CLEMENT struct armada38x_rtc {
75a3a42806SGregory CLEMENT 	struct rtc_device   *rtc_dev;
76a3a42806SGregory CLEMENT 	void __iomem	    *regs;
77a3a42806SGregory CLEMENT 	void __iomem	    *regs_soc;
78a3a42806SGregory CLEMENT 	spinlock_t	    lock;
79a3a42806SGregory CLEMENT 	int		    irq;
801a990fefSBaruch Siach 	bool		    initialized;
81844a3073SGregory CLEMENT 	struct value_to_freq *val_to_freq;
8275faea91SGregory CLEMENT 	struct armada38x_rtc_data *data;
8375faea91SGregory CLEMENT };
8475faea91SGregory CLEMENT 
8575faea91SGregory CLEMENT #define ALARM1	0
8634f54f57SGregory CLEMENT #define ALARM2	1
8734f54f57SGregory CLEMENT 
8875faea91SGregory CLEMENT #define ALARM_REG(base, alarm)	 ((base) + (alarm) * sizeof(u32))
8975faea91SGregory CLEMENT 
9075faea91SGregory CLEMENT struct armada38x_rtc_data {
9175faea91SGregory CLEMENT 	/* Initialize the RTC-MBUS bridge timing */
9275faea91SGregory CLEMENT 	void (*update_mbus_timing)(struct armada38x_rtc *rtc);
9375faea91SGregory CLEMENT 	u32 (*read_rtc_reg)(struct armada38x_rtc *rtc, u8 rtc_reg);
9475faea91SGregory CLEMENT 	void (*clear_isr)(struct armada38x_rtc *rtc);
9575faea91SGregory CLEMENT 	void (*unmask_interrupt)(struct armada38x_rtc *rtc);
9675faea91SGregory CLEMENT 	u32 alarm;
97a3a42806SGregory CLEMENT };
98a3a42806SGregory CLEMENT 
99a3a42806SGregory CLEMENT /*
100a3a42806SGregory CLEMENT  * According to the datasheet, the OS should wait 5us after every
101a3a42806SGregory CLEMENT  * register write to the RTC hard macro so that the required update
102a3a42806SGregory CLEMENT  * can occur without holding off the system bus
103844a3073SGregory CLEMENT  * According to errata RES-3124064, Write to any RTC register
104844a3073SGregory CLEMENT  * may fail. As a workaround, before writing to RTC
105844a3073SGregory CLEMENT  * register, issue a dummy write of 0x0 twice to RTC Status
106844a3073SGregory CLEMENT  * register.
107a3a42806SGregory CLEMENT  */
108844a3073SGregory CLEMENT 
109a3a42806SGregory CLEMENT static void rtc_delayed_write(u32 val, struct armada38x_rtc *rtc, int offset)
110a3a42806SGregory CLEMENT {
111844a3073SGregory CLEMENT 	writel(0, rtc->regs + RTC_STATUS);
112844a3073SGregory CLEMENT 	writel(0, rtc->regs + RTC_STATUS);
113a3a42806SGregory CLEMENT 	writel(val, rtc->regs + offset);
114a3a42806SGregory CLEMENT 	udelay(5);
115a3a42806SGregory CLEMENT }
116a3a42806SGregory CLEMENT 
117844a3073SGregory CLEMENT /* Update RTC-MBUS bridge timing parameters */
11875faea91SGregory CLEMENT static void rtc_update_38x_mbus_timing_params(struct armada38x_rtc *rtc)
119844a3073SGregory CLEMENT {
120844a3073SGregory CLEMENT 	u32 reg;
121844a3073SGregory CLEMENT 
12275faea91SGregory CLEMENT 	reg = readl(rtc->regs_soc + RTC_38X_BRIDGE_TIMING_CTL);
12375faea91SGregory CLEMENT 	reg &= ~RTC_38X_PERIOD_MASK;
12475faea91SGregory CLEMENT 	reg |= 0x3FF << RTC_38X_PERIOD_OFFS; /* Maximum value */
12575faea91SGregory CLEMENT 	reg &= ~RTC_38X_READ_DELAY_MASK;
12675faea91SGregory CLEMENT 	reg |= 0x1F << RTC_38X_READ_DELAY_OFFS; /* Maximum value */
12775faea91SGregory CLEMENT 	writel(reg, rtc->regs_soc + RTC_38X_BRIDGE_TIMING_CTL);
128844a3073SGregory CLEMENT }
129844a3073SGregory CLEMENT 
13034f54f57SGregory CLEMENT static void rtc_update_8k_mbus_timing_params(struct armada38x_rtc *rtc)
13134f54f57SGregory CLEMENT {
13234f54f57SGregory CLEMENT 	u32 reg;
13334f54f57SGregory CLEMENT 
13434f54f57SGregory CLEMENT 	reg = readl(rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL0);
13534f54f57SGregory CLEMENT 	reg &= ~RTC_8K_WRCLK_PERIOD_MASK;
13634f54f57SGregory CLEMENT 	reg |= 0x3FF << RTC_8K_WRCLK_PERIOD_OFFS;
13734f54f57SGregory CLEMENT 	reg &= ~RTC_8K_WRCLK_SETUP_MASK;
13834f54f57SGregory CLEMENT 	reg |= 0x29 << RTC_8K_WRCLK_SETUP_OFFS;
13934f54f57SGregory CLEMENT 	writel(reg, rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL0);
14034f54f57SGregory CLEMENT 
14134f54f57SGregory CLEMENT 	reg = readl(rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL1);
14234f54f57SGregory CLEMENT 	reg &= ~RTC_8K_READ_DELAY_MASK;
14334f54f57SGregory CLEMENT 	reg |= 0x3F << RTC_8K_READ_DELAY_OFFS;
14434f54f57SGregory CLEMENT 	writel(reg, rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL1);
14534f54f57SGregory CLEMENT }
14634f54f57SGregory CLEMENT 
14734f54f57SGregory CLEMENT static u32 read_rtc_register(struct armada38x_rtc *rtc, u8 rtc_reg)
14834f54f57SGregory CLEMENT {
14934f54f57SGregory CLEMENT 	return readl(rtc->regs + rtc_reg);
15034f54f57SGregory CLEMENT }
15134f54f57SGregory CLEMENT 
15275faea91SGregory CLEMENT static u32 read_rtc_register_38x_wa(struct armada38x_rtc *rtc, u8 rtc_reg)
153844a3073SGregory CLEMENT {
154844a3073SGregory CLEMENT 	int i, index_max = 0, max = 0;
155844a3073SGregory CLEMENT 
156844a3073SGregory CLEMENT 	for (i = 0; i < SAMPLE_NR; i++) {
157844a3073SGregory CLEMENT 		rtc->val_to_freq[i].value = readl(rtc->regs + rtc_reg);
158844a3073SGregory CLEMENT 		rtc->val_to_freq[i].freq = 0;
159844a3073SGregory CLEMENT 	}
160844a3073SGregory CLEMENT 
161844a3073SGregory CLEMENT 	for (i = 0; i < SAMPLE_NR; i++) {
162844a3073SGregory CLEMENT 		int j = 0;
163844a3073SGregory CLEMENT 		u32 value = rtc->val_to_freq[i].value;
164844a3073SGregory CLEMENT 
165844a3073SGregory CLEMENT 		while (rtc->val_to_freq[j].freq) {
166844a3073SGregory CLEMENT 			if (rtc->val_to_freq[j].value == value) {
167844a3073SGregory CLEMENT 				rtc->val_to_freq[j].freq++;
168844a3073SGregory CLEMENT 				break;
169844a3073SGregory CLEMENT 			}
170844a3073SGregory CLEMENT 			j++;
171844a3073SGregory CLEMENT 		}
172844a3073SGregory CLEMENT 
173844a3073SGregory CLEMENT 		if (!rtc->val_to_freq[j].freq) {
174844a3073SGregory CLEMENT 			rtc->val_to_freq[j].value = value;
175844a3073SGregory CLEMENT 			rtc->val_to_freq[j].freq = 1;
176844a3073SGregory CLEMENT 		}
177844a3073SGregory CLEMENT 
178844a3073SGregory CLEMENT 		if (rtc->val_to_freq[j].freq > max) {
179844a3073SGregory CLEMENT 			index_max = j;
180844a3073SGregory CLEMENT 			max = rtc->val_to_freq[j].freq;
181844a3073SGregory CLEMENT 		}
182844a3073SGregory CLEMENT 
183844a3073SGregory CLEMENT 		/*
184844a3073SGregory CLEMENT 		 * If a value already has half of the sample this is the most
185844a3073SGregory CLEMENT 		 * frequent one and we can stop the research right now
186844a3073SGregory CLEMENT 		 */
187844a3073SGregory CLEMENT 		if (max > SAMPLE_NR / 2)
188844a3073SGregory CLEMENT 			break;
189844a3073SGregory CLEMENT 	}
190844a3073SGregory CLEMENT 
191844a3073SGregory CLEMENT 	return rtc->val_to_freq[index_max].value;
192844a3073SGregory CLEMENT }
193844a3073SGregory CLEMENT 
19475faea91SGregory CLEMENT static void armada38x_clear_isr(struct armada38x_rtc *rtc)
19575faea91SGregory CLEMENT {
19675faea91SGregory CLEMENT 	u32 val = readl(rtc->regs_soc + SOC_RTC_INTERRUPT);
19775faea91SGregory CLEMENT 
19875faea91SGregory CLEMENT 	writel(val & ~SOC_RTC_ALARM1, rtc->regs_soc + SOC_RTC_INTERRUPT);
19975faea91SGregory CLEMENT }
20075faea91SGregory CLEMENT 
20175faea91SGregory CLEMENT static void armada38x_unmask_interrupt(struct armada38x_rtc *rtc)
20275faea91SGregory CLEMENT {
20375faea91SGregory CLEMENT 	u32 val = readl(rtc->regs_soc + SOC_RTC_INTERRUPT);
20475faea91SGregory CLEMENT 
20575faea91SGregory CLEMENT 	writel(val | SOC_RTC_ALARM1_MASK, rtc->regs_soc + SOC_RTC_INTERRUPT);
20675faea91SGregory CLEMENT }
20734f54f57SGregory CLEMENT 
20834f54f57SGregory CLEMENT static void armada8k_clear_isr(struct armada38x_rtc *rtc)
20934f54f57SGregory CLEMENT {
21034f54f57SGregory CLEMENT 	writel(RTC_8K_ALARM2, rtc->regs_soc + RTC_8K_ISR);
21134f54f57SGregory CLEMENT }
21234f54f57SGregory CLEMENT 
21334f54f57SGregory CLEMENT static void armada8k_unmask_interrupt(struct armada38x_rtc *rtc)
21434f54f57SGregory CLEMENT {
21534f54f57SGregory CLEMENT 	writel(RTC_8K_ALARM2, rtc->regs_soc + RTC_8K_IMR);
21634f54f57SGregory CLEMENT }
21734f54f57SGregory CLEMENT 
218a3a42806SGregory CLEMENT static int armada38x_rtc_read_time(struct device *dev, struct rtc_time *tm)
219a3a42806SGregory CLEMENT {
220a3a42806SGregory CLEMENT 	struct armada38x_rtc *rtc = dev_get_drvdata(dev);
221844a3073SGregory CLEMENT 	unsigned long time, flags;
222a3a42806SGregory CLEMENT 
2230c6e7183SNadav Haklai 	spin_lock_irqsave(&rtc->lock, flags);
22475faea91SGregory CLEMENT 	time = rtc->data->read_rtc_reg(rtc, RTC_TIME);
2250c6e7183SNadav Haklai 	spin_unlock_irqrestore(&rtc->lock, flags);
226a3a42806SGregory CLEMENT 
227*f6e3d773SAlexandre Belloni 	rtc_time64_to_tm(time, tm);
228a3a42806SGregory CLEMENT 
229a3a42806SGregory CLEMENT 	return 0;
230a3a42806SGregory CLEMENT }
231a3a42806SGregory CLEMENT 
2321a990fefSBaruch Siach static void armada38x_rtc_reset(struct armada38x_rtc *rtc)
2331a990fefSBaruch Siach {
2341a990fefSBaruch Siach 	u32 reg;
2351a990fefSBaruch Siach 
2361a990fefSBaruch Siach 	reg = rtc->data->read_rtc_reg(rtc, RTC_CONF_TEST);
2371a990fefSBaruch Siach 	/* If bits [7:0] are non-zero, assume RTC was uninitialized */
2381a990fefSBaruch Siach 	if (reg & 0xff) {
2391a990fefSBaruch Siach 		rtc_delayed_write(0, rtc, RTC_CONF_TEST);
2401a990fefSBaruch Siach 		msleep(500); /* Oscillator startup time */
2411a990fefSBaruch Siach 		rtc_delayed_write(0, rtc, RTC_TIME);
2421a990fefSBaruch Siach 		rtc_delayed_write(SOC_RTC_ALARM1 | SOC_RTC_ALARM2, rtc,
2431a990fefSBaruch Siach 				  RTC_STATUS);
2441a990fefSBaruch Siach 		rtc_delayed_write(RTC_NOMINAL_TIMING, rtc, RTC_CCR);
2451a990fefSBaruch Siach 	}
2461a990fefSBaruch Siach 	rtc->initialized = true;
2471a990fefSBaruch Siach }
2481a990fefSBaruch Siach 
249a3a42806SGregory CLEMENT static int armada38x_rtc_set_time(struct device *dev, struct rtc_time *tm)
250a3a42806SGregory CLEMENT {
251a3a42806SGregory CLEMENT 	struct armada38x_rtc *rtc = dev_get_drvdata(dev);
2520c6e7183SNadav Haklai 	unsigned long time, flags;
253a3a42806SGregory CLEMENT 
254*f6e3d773SAlexandre Belloni 	time = rtc_tm_to_time64(tm);
255844a3073SGregory CLEMENT 
2561a990fefSBaruch Siach 	if (!rtc->initialized)
2571a990fefSBaruch Siach 		armada38x_rtc_reset(rtc);
2581a990fefSBaruch Siach 
2590c6e7183SNadav Haklai 	spin_lock_irqsave(&rtc->lock, flags);
260a3a42806SGregory CLEMENT 	rtc_delayed_write(time, rtc, RTC_TIME);
2610c6e7183SNadav Haklai 	spin_unlock_irqrestore(&rtc->lock, flags);
262a3a42806SGregory CLEMENT 
263*f6e3d773SAlexandre Belloni 	return 0;
264a3a42806SGregory CLEMENT }
265a3a42806SGregory CLEMENT 
266a3a42806SGregory CLEMENT static int armada38x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
267a3a42806SGregory CLEMENT {
268a3a42806SGregory CLEMENT 	struct armada38x_rtc *rtc = dev_get_drvdata(dev);
269a3a42806SGregory CLEMENT 	unsigned long time, flags;
27075faea91SGregory CLEMENT 	u32 reg = ALARM_REG(RTC_ALARM1, rtc->data->alarm);
27175faea91SGregory CLEMENT 	u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm);
272a3a42806SGregory CLEMENT 	u32 val;
273a3a42806SGregory CLEMENT 
274a3a42806SGregory CLEMENT 	spin_lock_irqsave(&rtc->lock, flags);
275a3a42806SGregory CLEMENT 
27675faea91SGregory CLEMENT 	time = rtc->data->read_rtc_reg(rtc, reg);
27775faea91SGregory CLEMENT 	val = rtc->data->read_rtc_reg(rtc, reg_irq) & RTC_IRQ_AL_EN;
278a3a42806SGregory CLEMENT 
279a3a42806SGregory CLEMENT 	spin_unlock_irqrestore(&rtc->lock, flags);
280a3a42806SGregory CLEMENT 
281a3a42806SGregory CLEMENT 	alrm->enabled = val ? 1 : 0;
282*f6e3d773SAlexandre Belloni 	rtc_time64_to_tm(time,  &alrm->time);
283a3a42806SGregory CLEMENT 
284a3a42806SGregory CLEMENT 	return 0;
285a3a42806SGregory CLEMENT }
286a3a42806SGregory CLEMENT 
287a3a42806SGregory CLEMENT static int armada38x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
288a3a42806SGregory CLEMENT {
289a3a42806SGregory CLEMENT 	struct armada38x_rtc *rtc = dev_get_drvdata(dev);
29075faea91SGregory CLEMENT 	u32 reg = ALARM_REG(RTC_ALARM1, rtc->data->alarm);
29175faea91SGregory CLEMENT 	u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm);
292a3a42806SGregory CLEMENT 	unsigned long time, flags;
293a3a42806SGregory CLEMENT 
294*f6e3d773SAlexandre Belloni 	time = rtc_tm_to_time64(&alrm->time);
295a3a42806SGregory CLEMENT 
296a3a42806SGregory CLEMENT 	spin_lock_irqsave(&rtc->lock, flags);
297a3a42806SGregory CLEMENT 
29875faea91SGregory CLEMENT 	rtc_delayed_write(time, rtc, reg);
299a3a42806SGregory CLEMENT 
300a3a42806SGregory CLEMENT 	if (alrm->enabled) {
30175faea91SGregory CLEMENT 		rtc_delayed_write(RTC_IRQ_AL_EN, rtc, reg_irq);
30275faea91SGregory CLEMENT 		rtc->data->unmask_interrupt(rtc);
303a3a42806SGregory CLEMENT 	}
304a3a42806SGregory CLEMENT 
305a3a42806SGregory CLEMENT 	spin_unlock_irqrestore(&rtc->lock, flags);
306a3a42806SGregory CLEMENT 
307*f6e3d773SAlexandre Belloni 	return 0;
308a3a42806SGregory CLEMENT }
309a3a42806SGregory CLEMENT 
310a3a42806SGregory CLEMENT static int armada38x_rtc_alarm_irq_enable(struct device *dev,
311a3a42806SGregory CLEMENT 					 unsigned int enabled)
312a3a42806SGregory CLEMENT {
313a3a42806SGregory CLEMENT 	struct armada38x_rtc *rtc = dev_get_drvdata(dev);
31475faea91SGregory CLEMENT 	u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm);
315a3a42806SGregory CLEMENT 	unsigned long flags;
316a3a42806SGregory CLEMENT 
317a3a42806SGregory CLEMENT 	spin_lock_irqsave(&rtc->lock, flags);
318a3a42806SGregory CLEMENT 
319a3a42806SGregory CLEMENT 	if (enabled)
32075faea91SGregory CLEMENT 		rtc_delayed_write(RTC_IRQ_AL_EN, rtc, reg_irq);
321a3a42806SGregory CLEMENT 	else
32275faea91SGregory CLEMENT 		rtc_delayed_write(0, rtc, reg_irq);
323a3a42806SGregory CLEMENT 
324a3a42806SGregory CLEMENT 	spin_unlock_irqrestore(&rtc->lock, flags);
325a3a42806SGregory CLEMENT 
326a3a42806SGregory CLEMENT 	return 0;
327a3a42806SGregory CLEMENT }
328a3a42806SGregory CLEMENT 
329a3a42806SGregory CLEMENT static irqreturn_t armada38x_rtc_alarm_irq(int irq, void *data)
330a3a42806SGregory CLEMENT {
331a3a42806SGregory CLEMENT 	struct armada38x_rtc *rtc = data;
332a3a42806SGregory CLEMENT 	u32 val;
333a3a42806SGregory CLEMENT 	int event = RTC_IRQF | RTC_AF;
33475faea91SGregory CLEMENT 	u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm);
335a3a42806SGregory CLEMENT 
336a3a42806SGregory CLEMENT 	dev_dbg(&rtc->rtc_dev->dev, "%s:irq(%d)\n", __func__, irq);
337a3a42806SGregory CLEMENT 
338a3a42806SGregory CLEMENT 	spin_lock(&rtc->lock);
339a3a42806SGregory CLEMENT 
34075faea91SGregory CLEMENT 	rtc->data->clear_isr(rtc);
34175faea91SGregory CLEMENT 	val = rtc->data->read_rtc_reg(rtc, reg_irq);
34275faea91SGregory CLEMENT 	/* disable all the interrupts for alarm*/
34375faea91SGregory CLEMENT 	rtc_delayed_write(0, rtc, reg_irq);
344a3a42806SGregory CLEMENT 	/* Ack the event */
34575faea91SGregory CLEMENT 	rtc_delayed_write(1 << rtc->data->alarm, rtc, RTC_STATUS);
346a3a42806SGregory CLEMENT 
347a3a42806SGregory CLEMENT 	spin_unlock(&rtc->lock);
348a3a42806SGregory CLEMENT 
34975faea91SGregory CLEMENT 	if (val & RTC_IRQ_FREQ_EN) {
35075faea91SGregory CLEMENT 		if (val & RTC_IRQ_FREQ_1HZ)
351a3a42806SGregory CLEMENT 			event |= RTC_UF;
352a3a42806SGregory CLEMENT 		else
353a3a42806SGregory CLEMENT 			event |= RTC_PF;
354a3a42806SGregory CLEMENT 	}
355a3a42806SGregory CLEMENT 
356a3a42806SGregory CLEMENT 	rtc_update_irq(rtc->rtc_dev, 1, event);
357a3a42806SGregory CLEMENT 
358a3a42806SGregory CLEMENT 	return IRQ_HANDLED;
359a3a42806SGregory CLEMENT }
360a3a42806SGregory CLEMENT 
361f94ffbc2SRussell King /*
362f94ffbc2SRussell King  * The information given in the Armada 388 functional spec is complex.
363f94ffbc2SRussell King  * They give two different formulas for calculating the offset value,
364f94ffbc2SRussell King  * but when considering "Offset" as an 8-bit signed integer, they both
365f94ffbc2SRussell King  * reduce down to (we shall rename "Offset" as "val" here):
366f94ffbc2SRussell King  *
367f94ffbc2SRussell King  *   val = (f_ideal / f_measured - 1) / resolution   where f_ideal = 32768
368f94ffbc2SRussell King  *
369f94ffbc2SRussell King  * Converting to time, f = 1/t:
370f94ffbc2SRussell King  *   val = (t_measured / t_ideal - 1) / resolution   where t_ideal = 1/32768
371f94ffbc2SRussell King  *
372f94ffbc2SRussell King  *   =>  t_measured / t_ideal = val * resolution + 1
373f94ffbc2SRussell King  *
374f94ffbc2SRussell King  * "offset" in the RTC interface is defined as:
375f94ffbc2SRussell King  *   t = t0 * (1 + offset * 1e-9)
376f94ffbc2SRussell King  * where t is the desired period, t0 is the measured period with a zero
377f94ffbc2SRussell King  * offset, which is t_measured above. With t0 = t_measured and t = t_ideal,
378f94ffbc2SRussell King  *   offset = (t_ideal / t_measured - 1) / 1e-9
379f94ffbc2SRussell King  *
380f94ffbc2SRussell King  *   => t_ideal / t_measured = offset * 1e-9 + 1
381f94ffbc2SRussell King  *
382f94ffbc2SRussell King  * so:
383f94ffbc2SRussell King  *
384f94ffbc2SRussell King  *   offset * 1e-9 + 1 = 1 / (val * resolution + 1)
385f94ffbc2SRussell King  *
386f94ffbc2SRussell King  * We want "resolution" to be an integer, so resolution = R * 1e-9, giving
387f94ffbc2SRussell King  *   offset = 1e18 / (val * R + 1e9) - 1e9
388f94ffbc2SRussell King  *   val = (1e18 / (offset + 1e9) - 1e9) / R
389f94ffbc2SRussell King  * with a common transformation:
390f94ffbc2SRussell King  *   f(x) = 1e18 / (x + 1e9) - 1e9
391f94ffbc2SRussell King  *   offset = f(val * R)
392f94ffbc2SRussell King  *   val = f(offset) / R
393f94ffbc2SRussell King  *
394f94ffbc2SRussell King  * Armada 38x supports two modes, fine mode (954ppb) and coarse mode (3815ppb).
395f94ffbc2SRussell King  */
396f94ffbc2SRussell King static long armada38x_ppb_convert(long ppb)
397f94ffbc2SRussell King {
398f94ffbc2SRussell King 	long div = ppb + 1000000000L;
399f94ffbc2SRussell King 
400f94ffbc2SRussell King 	return div_s64(1000000000000000000LL + div / 2, div) - 1000000000L;
401f94ffbc2SRussell King }
402f94ffbc2SRussell King 
403f94ffbc2SRussell King static int armada38x_rtc_read_offset(struct device *dev, long *offset)
404f94ffbc2SRussell King {
405f94ffbc2SRussell King 	struct armada38x_rtc *rtc = dev_get_drvdata(dev);
406f94ffbc2SRussell King 	unsigned long ccr, flags;
407f94ffbc2SRussell King 	long ppb_cor;
408f94ffbc2SRussell King 
409f94ffbc2SRussell King 	spin_lock_irqsave(&rtc->lock, flags);
410f94ffbc2SRussell King 	ccr = rtc->data->read_rtc_reg(rtc, RTC_CCR);
411f94ffbc2SRussell King 	spin_unlock_irqrestore(&rtc->lock, flags);
412f94ffbc2SRussell King 
413f94ffbc2SRussell King 	ppb_cor = (ccr & RTC_CCR_MODE ? 3815 : 954) * (s8)ccr;
414f94ffbc2SRussell King 	/* ppb_cor + 1000000000L can never be zero */
415f94ffbc2SRussell King 	*offset = armada38x_ppb_convert(ppb_cor);
416f94ffbc2SRussell King 
417f94ffbc2SRussell King 	return 0;
418f94ffbc2SRussell King }
419f94ffbc2SRussell King 
420f94ffbc2SRussell King static int armada38x_rtc_set_offset(struct device *dev, long offset)
421f94ffbc2SRussell King {
422f94ffbc2SRussell King 	struct armada38x_rtc *rtc = dev_get_drvdata(dev);
423f94ffbc2SRussell King 	unsigned long ccr = 0;
424f94ffbc2SRussell King 	long ppb_cor, off;
425f94ffbc2SRussell King 
426f94ffbc2SRussell King 	/*
427f94ffbc2SRussell King 	 * The maximum ppb_cor is -128 * 3815 .. 127 * 3815, but we
428f94ffbc2SRussell King 	 * need to clamp the input.  This equates to -484270 .. 488558.
429f94ffbc2SRussell King 	 * Not only is this to stop out of range "off" but also to
430f94ffbc2SRussell King 	 * avoid the division by zero in armada38x_ppb_convert().
431f94ffbc2SRussell King 	 */
432f94ffbc2SRussell King 	offset = clamp(offset, -484270L, 488558L);
433f94ffbc2SRussell King 
434f94ffbc2SRussell King 	ppb_cor = armada38x_ppb_convert(offset);
435f94ffbc2SRussell King 
436f94ffbc2SRussell King 	/*
437f94ffbc2SRussell King 	 * Use low update mode where possible, which gives a better
438f94ffbc2SRussell King 	 * resolution of correction.
439f94ffbc2SRussell King 	 */
440f94ffbc2SRussell King 	off = DIV_ROUND_CLOSEST(ppb_cor, 954);
441f94ffbc2SRussell King 	if (off > 127 || off < -128) {
442f94ffbc2SRussell King 		ccr = RTC_CCR_MODE;
443f94ffbc2SRussell King 		off = DIV_ROUND_CLOSEST(ppb_cor, 3815);
444f94ffbc2SRussell King 	}
445f94ffbc2SRussell King 
446f94ffbc2SRussell King 	/*
447f94ffbc2SRussell King 	 * Armada 388 requires a bit pattern in bits 14..8 depending on
448f94ffbc2SRussell King 	 * the sign bit: { 0, ~S, S, S, S, S, S }
449f94ffbc2SRussell King 	 */
450f94ffbc2SRussell King 	ccr |= (off & 0x3fff) ^ 0x2000;
451f94ffbc2SRussell King 	rtc_delayed_write(ccr, rtc, RTC_CCR);
452f94ffbc2SRussell King 
453f94ffbc2SRussell King 	return 0;
454f94ffbc2SRussell King }
455f94ffbc2SRussell King 
456d748c981SRussell King static const struct rtc_class_ops armada38x_rtc_ops = {
457a3a42806SGregory CLEMENT 	.read_time = armada38x_rtc_read_time,
458a3a42806SGregory CLEMENT 	.set_time = armada38x_rtc_set_time,
459a3a42806SGregory CLEMENT 	.read_alarm = armada38x_rtc_read_alarm,
460a3a42806SGregory CLEMENT 	.set_alarm = armada38x_rtc_set_alarm,
461a3a42806SGregory CLEMENT 	.alarm_irq_enable = armada38x_rtc_alarm_irq_enable,
462f94ffbc2SRussell King 	.read_offset = armada38x_rtc_read_offset,
463f94ffbc2SRussell King 	.set_offset = armada38x_rtc_set_offset,
464a3a42806SGregory CLEMENT };
465a3a42806SGregory CLEMENT 
466d748c981SRussell King static const struct rtc_class_ops armada38x_rtc_ops_noirq = {
467d748c981SRussell King 	.read_time = armada38x_rtc_read_time,
468d748c981SRussell King 	.set_time = armada38x_rtc_set_time,
469d748c981SRussell King 	.read_alarm = armada38x_rtc_read_alarm,
470f94ffbc2SRussell King 	.read_offset = armada38x_rtc_read_offset,
471f94ffbc2SRussell King 	.set_offset = armada38x_rtc_set_offset,
472d748c981SRussell King };
473d748c981SRussell King 
47475faea91SGregory CLEMENT static const struct armada38x_rtc_data armada38x_data = {
47575faea91SGregory CLEMENT 	.update_mbus_timing = rtc_update_38x_mbus_timing_params,
47675faea91SGregory CLEMENT 	.read_rtc_reg = read_rtc_register_38x_wa,
47775faea91SGregory CLEMENT 	.clear_isr = armada38x_clear_isr,
47875faea91SGregory CLEMENT 	.unmask_interrupt = armada38x_unmask_interrupt,
47975faea91SGregory CLEMENT 	.alarm = ALARM1,
48075faea91SGregory CLEMENT };
48175faea91SGregory CLEMENT 
48234f54f57SGregory CLEMENT static const struct armada38x_rtc_data armada8k_data = {
48334f54f57SGregory CLEMENT 	.update_mbus_timing = rtc_update_8k_mbus_timing_params,
48434f54f57SGregory CLEMENT 	.read_rtc_reg = read_rtc_register,
48534f54f57SGregory CLEMENT 	.clear_isr = armada8k_clear_isr,
48634f54f57SGregory CLEMENT 	.unmask_interrupt = armada8k_unmask_interrupt,
48734f54f57SGregory CLEMENT 	.alarm = ALARM2,
48834f54f57SGregory CLEMENT };
48934f54f57SGregory CLEMENT 
49075faea91SGregory CLEMENT #ifdef CONFIG_OF
49175faea91SGregory CLEMENT static const struct of_device_id armada38x_rtc_of_match_table[] = {
49275faea91SGregory CLEMENT 	{
49375faea91SGregory CLEMENT 		.compatible = "marvell,armada-380-rtc",
49475faea91SGregory CLEMENT 		.data = &armada38x_data,
49575faea91SGregory CLEMENT 	},
49634f54f57SGregory CLEMENT 	{
49734f54f57SGregory CLEMENT 		.compatible = "marvell,armada-8k-rtc",
49834f54f57SGregory CLEMENT 		.data = &armada8k_data,
49934f54f57SGregory CLEMENT 	},
50075faea91SGregory CLEMENT 	{}
50175faea91SGregory CLEMENT };
50275faea91SGregory CLEMENT MODULE_DEVICE_TABLE(of, armada38x_rtc_of_match_table);
50375faea91SGregory CLEMENT #endif
50475faea91SGregory CLEMENT 
505a3a42806SGregory CLEMENT static __init int armada38x_rtc_probe(struct platform_device *pdev)
506a3a42806SGregory CLEMENT {
507a3a42806SGregory CLEMENT 	struct resource *res;
508a3a42806SGregory CLEMENT 	struct armada38x_rtc *rtc;
50975faea91SGregory CLEMENT 	const struct of_device_id *match;
510a3a42806SGregory CLEMENT 	int ret;
511a3a42806SGregory CLEMENT 
51275faea91SGregory CLEMENT 	match = of_match_device(armada38x_rtc_of_match_table, &pdev->dev);
51375faea91SGregory CLEMENT 	if (!match)
51475faea91SGregory CLEMENT 		return -ENODEV;
51575faea91SGregory CLEMENT 
516a3a42806SGregory CLEMENT 	rtc = devm_kzalloc(&pdev->dev, sizeof(struct armada38x_rtc),
517a3a42806SGregory CLEMENT 			    GFP_KERNEL);
518a3a42806SGregory CLEMENT 	if (!rtc)
519a3a42806SGregory CLEMENT 		return -ENOMEM;
520a3a42806SGregory CLEMENT 
521844a3073SGregory CLEMENT 	rtc->val_to_freq = devm_kcalloc(&pdev->dev, SAMPLE_NR,
522844a3073SGregory CLEMENT 				sizeof(struct value_to_freq), GFP_KERNEL);
523844a3073SGregory CLEMENT 	if (!rtc->val_to_freq)
524844a3073SGregory CLEMENT 		return -ENOMEM;
525844a3073SGregory CLEMENT 
526a3a42806SGregory CLEMENT 	spin_lock_init(&rtc->lock);
527a3a42806SGregory CLEMENT 
528a3a42806SGregory CLEMENT 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rtc");
529a3a42806SGregory CLEMENT 	rtc->regs = devm_ioremap_resource(&pdev->dev, res);
530a3a42806SGregory CLEMENT 	if (IS_ERR(rtc->regs))
531a3a42806SGregory CLEMENT 		return PTR_ERR(rtc->regs);
532a3a42806SGregory CLEMENT 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rtc-soc");
533a3a42806SGregory CLEMENT 	rtc->regs_soc = devm_ioremap_resource(&pdev->dev, res);
534a3a42806SGregory CLEMENT 	if (IS_ERR(rtc->regs_soc))
535a3a42806SGregory CLEMENT 		return PTR_ERR(rtc->regs_soc);
536a3a42806SGregory CLEMENT 
537a3a42806SGregory CLEMENT 	rtc->irq = platform_get_irq(pdev, 0);
538a3a42806SGregory CLEMENT 
539a3a42806SGregory CLEMENT 	if (rtc->irq < 0) {
540a3a42806SGregory CLEMENT 		dev_err(&pdev->dev, "no irq\n");
541a3a42806SGregory CLEMENT 		return rtc->irq;
542a3a42806SGregory CLEMENT 	}
5437d61cbb9SAlexandre Belloni 
5447d61cbb9SAlexandre Belloni 	rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev);
5457d61cbb9SAlexandre Belloni 	if (IS_ERR(rtc->rtc_dev))
5467d61cbb9SAlexandre Belloni 		return PTR_ERR(rtc->rtc_dev);
5477d61cbb9SAlexandre Belloni 
548a3a42806SGregory CLEMENT 	if (devm_request_irq(&pdev->dev, rtc->irq, armada38x_rtc_alarm_irq,
549a3a42806SGregory CLEMENT 				0, pdev->name, rtc) < 0) {
550a3a42806SGregory CLEMENT 		dev_warn(&pdev->dev, "Interrupt not available.\n");
551a3a42806SGregory CLEMENT 		rtc->irq = -1;
552d748c981SRussell King 	}
553d748c981SRussell King 	platform_set_drvdata(pdev, rtc);
554d748c981SRussell King 
555d748c981SRussell King 	if (rtc->irq != -1) {
556d748c981SRussell King 		device_init_wakeup(&pdev->dev, 1);
5577d61cbb9SAlexandre Belloni 		rtc->rtc_dev->ops = &armada38x_rtc_ops;
558d748c981SRussell King 	} else {
559a3a42806SGregory CLEMENT 		/*
560a3a42806SGregory CLEMENT 		 * If there is no interrupt available then we can't
561a3a42806SGregory CLEMENT 		 * use the alarm
562a3a42806SGregory CLEMENT 		 */
5637d61cbb9SAlexandre Belloni 		rtc->rtc_dev->ops = &armada38x_rtc_ops_noirq;
564a3a42806SGregory CLEMENT 	}
56575faea91SGregory CLEMENT 	rtc->data = (struct armada38x_rtc_data *)match->data;
56675faea91SGregory CLEMENT 
567844a3073SGregory CLEMENT 	/* Update RTC-MBUS bridge timing parameters */
56875faea91SGregory CLEMENT 	rtc->data->update_mbus_timing(rtc);
569844a3073SGregory CLEMENT 
570ef2a7176SAlexandre Belloni 	rtc->rtc_dev->range_max = U32_MAX;
571ef2a7176SAlexandre Belloni 
5727d61cbb9SAlexandre Belloni 	ret = rtc_register_device(rtc->rtc_dev);
5737d61cbb9SAlexandre Belloni 	if (ret)
574a3a42806SGregory CLEMENT 		dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
5757d61cbb9SAlexandre Belloni 
576a3a42806SGregory CLEMENT 	return ret;
577a3a42806SGregory CLEMENT }
578a3a42806SGregory CLEMENT 
579a3a42806SGregory CLEMENT #ifdef CONFIG_PM_SLEEP
580a3a42806SGregory CLEMENT static int armada38x_rtc_suspend(struct device *dev)
581a3a42806SGregory CLEMENT {
582a3a42806SGregory CLEMENT 	if (device_may_wakeup(dev)) {
583a3a42806SGregory CLEMENT 		struct armada38x_rtc *rtc = dev_get_drvdata(dev);
584a3a42806SGregory CLEMENT 
585a3a42806SGregory CLEMENT 		return enable_irq_wake(rtc->irq);
586a3a42806SGregory CLEMENT 	}
587a3a42806SGregory CLEMENT 
588a3a42806SGregory CLEMENT 	return 0;
589a3a42806SGregory CLEMENT }
590a3a42806SGregory CLEMENT 
591a3a42806SGregory CLEMENT static int armada38x_rtc_resume(struct device *dev)
592a3a42806SGregory CLEMENT {
593a3a42806SGregory CLEMENT 	if (device_may_wakeup(dev)) {
594a3a42806SGregory CLEMENT 		struct armada38x_rtc *rtc = dev_get_drvdata(dev);
595a3a42806SGregory CLEMENT 
596844a3073SGregory CLEMENT 		/* Update RTC-MBUS bridge timing parameters */
59775faea91SGregory CLEMENT 		rtc->data->update_mbus_timing(rtc);
598844a3073SGregory CLEMENT 
599a3a42806SGregory CLEMENT 		return disable_irq_wake(rtc->irq);
600a3a42806SGregory CLEMENT 	}
601a3a42806SGregory CLEMENT 
602a3a42806SGregory CLEMENT 	return 0;
603a3a42806SGregory CLEMENT }
604a3a42806SGregory CLEMENT #endif
605a3a42806SGregory CLEMENT 
606a3a42806SGregory CLEMENT static SIMPLE_DEV_PM_OPS(armada38x_rtc_pm_ops,
607a3a42806SGregory CLEMENT 			 armada38x_rtc_suspend, armada38x_rtc_resume);
608a3a42806SGregory CLEMENT 
609a3a42806SGregory CLEMENT static struct platform_driver armada38x_rtc_driver = {
610a3a42806SGregory CLEMENT 	.driver		= {
611a3a42806SGregory CLEMENT 		.name	= "armada38x-rtc",
612a3a42806SGregory CLEMENT 		.pm	= &armada38x_rtc_pm_ops,
613a3a42806SGregory CLEMENT 		.of_match_table = of_match_ptr(armada38x_rtc_of_match_table),
614a3a42806SGregory CLEMENT 	},
615a3a42806SGregory CLEMENT };
616a3a42806SGregory CLEMENT 
617a3a42806SGregory CLEMENT module_platform_driver_probe(armada38x_rtc_driver, armada38x_rtc_probe);
618a3a42806SGregory CLEMENT 
619a3a42806SGregory CLEMENT MODULE_DESCRIPTION("Marvell Armada 38x RTC driver");
620a3a42806SGregory CLEMENT MODULE_AUTHOR("Gregory CLEMENT <gregory.clement@free-electrons.com>");
621a3a42806SGregory CLEMENT MODULE_LICENSE("GPL");
622