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