162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * RTC driver for the Armada 38x Marvell SoCs
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2015 Marvell
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Gregory Clement <gregory.clement@free-electrons.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/delay.h>
1162306a36Sopenharmony_ci#include <linux/io.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/of.h>
1462306a36Sopenharmony_ci#include <linux/platform_device.h>
1562306a36Sopenharmony_ci#include <linux/rtc.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define RTC_STATUS	    0x0
1862306a36Sopenharmony_ci#define RTC_STATUS_ALARM1	    BIT(0)
1962306a36Sopenharmony_ci#define RTC_STATUS_ALARM2	    BIT(1)
2062306a36Sopenharmony_ci#define RTC_IRQ1_CONF	    0x4
2162306a36Sopenharmony_ci#define RTC_IRQ2_CONF	    0x8
2262306a36Sopenharmony_ci#define RTC_IRQ_AL_EN		    BIT(0)
2362306a36Sopenharmony_ci#define RTC_IRQ_FREQ_EN		    BIT(1)
2462306a36Sopenharmony_ci#define RTC_IRQ_FREQ_1HZ	    BIT(2)
2562306a36Sopenharmony_ci#define RTC_CCR		    0x18
2662306a36Sopenharmony_ci#define RTC_CCR_MODE		    BIT(15)
2762306a36Sopenharmony_ci#define RTC_CONF_TEST	    0x1C
2862306a36Sopenharmony_ci#define RTC_NOMINAL_TIMING	    BIT(13)
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define RTC_TIME	    0xC
3162306a36Sopenharmony_ci#define RTC_ALARM1	    0x10
3262306a36Sopenharmony_ci#define RTC_ALARM2	    0x14
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/* Armada38x SoC registers  */
3562306a36Sopenharmony_ci#define RTC_38X_BRIDGE_TIMING_CTL   0x0
3662306a36Sopenharmony_ci#define RTC_38X_PERIOD_OFFS		0
3762306a36Sopenharmony_ci#define RTC_38X_PERIOD_MASK		(0x3FF << RTC_38X_PERIOD_OFFS)
3862306a36Sopenharmony_ci#define RTC_38X_READ_DELAY_OFFS		26
3962306a36Sopenharmony_ci#define RTC_38X_READ_DELAY_MASK		(0x1F << RTC_38X_READ_DELAY_OFFS)
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/* Armada 7K/8K registers  */
4262306a36Sopenharmony_ci#define RTC_8K_BRIDGE_TIMING_CTL0    0x0
4362306a36Sopenharmony_ci#define RTC_8K_WRCLK_PERIOD_OFFS	0
4462306a36Sopenharmony_ci#define RTC_8K_WRCLK_PERIOD_MASK	(0xFFFF << RTC_8K_WRCLK_PERIOD_OFFS)
4562306a36Sopenharmony_ci#define RTC_8K_WRCLK_SETUP_OFFS		16
4662306a36Sopenharmony_ci#define RTC_8K_WRCLK_SETUP_MASK		(0xFFFF << RTC_8K_WRCLK_SETUP_OFFS)
4762306a36Sopenharmony_ci#define RTC_8K_BRIDGE_TIMING_CTL1   0x4
4862306a36Sopenharmony_ci#define RTC_8K_READ_DELAY_OFFS		0
4962306a36Sopenharmony_ci#define RTC_8K_READ_DELAY_MASK		(0xFFFF << RTC_8K_READ_DELAY_OFFS)
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#define RTC_8K_ISR		    0x10
5262306a36Sopenharmony_ci#define RTC_8K_IMR		    0x14
5362306a36Sopenharmony_ci#define RTC_8K_ALARM2			BIT(0)
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#define SOC_RTC_INTERRUPT	    0x8
5662306a36Sopenharmony_ci#define SOC_RTC_ALARM1			BIT(0)
5762306a36Sopenharmony_ci#define SOC_RTC_ALARM2			BIT(1)
5862306a36Sopenharmony_ci#define SOC_RTC_ALARM1_MASK		BIT(2)
5962306a36Sopenharmony_ci#define SOC_RTC_ALARM2_MASK		BIT(3)
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci#define SAMPLE_NR 100
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistruct value_to_freq {
6462306a36Sopenharmony_ci	u32 value;
6562306a36Sopenharmony_ci	u8 freq;
6662306a36Sopenharmony_ci};
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistruct armada38x_rtc {
6962306a36Sopenharmony_ci	struct rtc_device   *rtc_dev;
7062306a36Sopenharmony_ci	void __iomem	    *regs;
7162306a36Sopenharmony_ci	void __iomem	    *regs_soc;
7262306a36Sopenharmony_ci	spinlock_t	    lock;
7362306a36Sopenharmony_ci	int		    irq;
7462306a36Sopenharmony_ci	bool		    initialized;
7562306a36Sopenharmony_ci	struct value_to_freq *val_to_freq;
7662306a36Sopenharmony_ci	const struct armada38x_rtc_data *data;
7762306a36Sopenharmony_ci};
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci#define ALARM1	0
8062306a36Sopenharmony_ci#define ALARM2	1
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci#define ALARM_REG(base, alarm)	 ((base) + (alarm) * sizeof(u32))
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistruct armada38x_rtc_data {
8562306a36Sopenharmony_ci	/* Initialize the RTC-MBUS bridge timing */
8662306a36Sopenharmony_ci	void (*update_mbus_timing)(struct armada38x_rtc *rtc);
8762306a36Sopenharmony_ci	u32 (*read_rtc_reg)(struct armada38x_rtc *rtc, u8 rtc_reg);
8862306a36Sopenharmony_ci	void (*clear_isr)(struct armada38x_rtc *rtc);
8962306a36Sopenharmony_ci	void (*unmask_interrupt)(struct armada38x_rtc *rtc);
9062306a36Sopenharmony_ci	u32 alarm;
9162306a36Sopenharmony_ci};
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci/*
9462306a36Sopenharmony_ci * According to the datasheet, the OS should wait 5us after every
9562306a36Sopenharmony_ci * register write to the RTC hard macro so that the required update
9662306a36Sopenharmony_ci * can occur without holding off the system bus
9762306a36Sopenharmony_ci * According to errata RES-3124064, Write to any RTC register
9862306a36Sopenharmony_ci * may fail. As a workaround, before writing to RTC
9962306a36Sopenharmony_ci * register, issue a dummy write of 0x0 twice to RTC Status
10062306a36Sopenharmony_ci * register.
10162306a36Sopenharmony_ci */
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic void rtc_delayed_write(u32 val, struct armada38x_rtc *rtc, int offset)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	writel(0, rtc->regs + RTC_STATUS);
10662306a36Sopenharmony_ci	writel(0, rtc->regs + RTC_STATUS);
10762306a36Sopenharmony_ci	writel(val, rtc->regs + offset);
10862306a36Sopenharmony_ci	udelay(5);
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci/* Update RTC-MBUS bridge timing parameters */
11262306a36Sopenharmony_cistatic void rtc_update_38x_mbus_timing_params(struct armada38x_rtc *rtc)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	u32 reg;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	reg = readl(rtc->regs_soc + RTC_38X_BRIDGE_TIMING_CTL);
11762306a36Sopenharmony_ci	reg &= ~RTC_38X_PERIOD_MASK;
11862306a36Sopenharmony_ci	reg |= 0x3FF << RTC_38X_PERIOD_OFFS; /* Maximum value */
11962306a36Sopenharmony_ci	reg &= ~RTC_38X_READ_DELAY_MASK;
12062306a36Sopenharmony_ci	reg |= 0x1F << RTC_38X_READ_DELAY_OFFS; /* Maximum value */
12162306a36Sopenharmony_ci	writel(reg, rtc->regs_soc + RTC_38X_BRIDGE_TIMING_CTL);
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic void rtc_update_8k_mbus_timing_params(struct armada38x_rtc *rtc)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	u32 reg;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	reg = readl(rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL0);
12962306a36Sopenharmony_ci	reg &= ~RTC_8K_WRCLK_PERIOD_MASK;
13062306a36Sopenharmony_ci	reg |= 0x3FF << RTC_8K_WRCLK_PERIOD_OFFS;
13162306a36Sopenharmony_ci	reg &= ~RTC_8K_WRCLK_SETUP_MASK;
13262306a36Sopenharmony_ci	reg |= 0x29 << RTC_8K_WRCLK_SETUP_OFFS;
13362306a36Sopenharmony_ci	writel(reg, rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL0);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	reg = readl(rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL1);
13662306a36Sopenharmony_ci	reg &= ~RTC_8K_READ_DELAY_MASK;
13762306a36Sopenharmony_ci	reg |= 0x3F << RTC_8K_READ_DELAY_OFFS;
13862306a36Sopenharmony_ci	writel(reg, rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL1);
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic u32 read_rtc_register(struct armada38x_rtc *rtc, u8 rtc_reg)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	return readl(rtc->regs + rtc_reg);
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic u32 read_rtc_register_38x_wa(struct armada38x_rtc *rtc, u8 rtc_reg)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	int i, index_max = 0, max = 0;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	for (i = 0; i < SAMPLE_NR; i++) {
15162306a36Sopenharmony_ci		rtc->val_to_freq[i].value = readl(rtc->regs + rtc_reg);
15262306a36Sopenharmony_ci		rtc->val_to_freq[i].freq = 0;
15362306a36Sopenharmony_ci	}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	for (i = 0; i < SAMPLE_NR; i++) {
15662306a36Sopenharmony_ci		int j = 0;
15762306a36Sopenharmony_ci		u32 value = rtc->val_to_freq[i].value;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci		while (rtc->val_to_freq[j].freq) {
16062306a36Sopenharmony_ci			if (rtc->val_to_freq[j].value == value) {
16162306a36Sopenharmony_ci				rtc->val_to_freq[j].freq++;
16262306a36Sopenharmony_ci				break;
16362306a36Sopenharmony_ci			}
16462306a36Sopenharmony_ci			j++;
16562306a36Sopenharmony_ci		}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci		if (!rtc->val_to_freq[j].freq) {
16862306a36Sopenharmony_ci			rtc->val_to_freq[j].value = value;
16962306a36Sopenharmony_ci			rtc->val_to_freq[j].freq = 1;
17062306a36Sopenharmony_ci		}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci		if (rtc->val_to_freq[j].freq > max) {
17362306a36Sopenharmony_ci			index_max = j;
17462306a36Sopenharmony_ci			max = rtc->val_to_freq[j].freq;
17562306a36Sopenharmony_ci		}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci		/*
17862306a36Sopenharmony_ci		 * If a value already has half of the sample this is the most
17962306a36Sopenharmony_ci		 * frequent one and we can stop the research right now
18062306a36Sopenharmony_ci		 */
18162306a36Sopenharmony_ci		if (max > SAMPLE_NR / 2)
18262306a36Sopenharmony_ci			break;
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	return rtc->val_to_freq[index_max].value;
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic void armada38x_clear_isr(struct armada38x_rtc *rtc)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	u32 val = readl(rtc->regs_soc + SOC_RTC_INTERRUPT);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	writel(val & ~SOC_RTC_ALARM1, rtc->regs_soc + SOC_RTC_INTERRUPT);
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic void armada38x_unmask_interrupt(struct armada38x_rtc *rtc)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	u32 val = readl(rtc->regs_soc + SOC_RTC_INTERRUPT);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	writel(val | SOC_RTC_ALARM1_MASK, rtc->regs_soc + SOC_RTC_INTERRUPT);
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic void armada8k_clear_isr(struct armada38x_rtc *rtc)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	writel(RTC_8K_ALARM2, rtc->regs_soc + RTC_8K_ISR);
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic void armada8k_unmask_interrupt(struct armada38x_rtc *rtc)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	writel(RTC_8K_ALARM2, rtc->regs_soc + RTC_8K_IMR);
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic int armada38x_rtc_read_time(struct device *dev, struct rtc_time *tm)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	struct armada38x_rtc *rtc = dev_get_drvdata(dev);
21562306a36Sopenharmony_ci	unsigned long time, flags;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	spin_lock_irqsave(&rtc->lock, flags);
21862306a36Sopenharmony_ci	time = rtc->data->read_rtc_reg(rtc, RTC_TIME);
21962306a36Sopenharmony_ci	spin_unlock_irqrestore(&rtc->lock, flags);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	rtc_time64_to_tm(time, tm);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	return 0;
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic void armada38x_rtc_reset(struct armada38x_rtc *rtc)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	u32 reg;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	reg = rtc->data->read_rtc_reg(rtc, RTC_CONF_TEST);
23162306a36Sopenharmony_ci	/* If bits [7:0] are non-zero, assume RTC was uninitialized */
23262306a36Sopenharmony_ci	if (reg & 0xff) {
23362306a36Sopenharmony_ci		rtc_delayed_write(0, rtc, RTC_CONF_TEST);
23462306a36Sopenharmony_ci		msleep(500); /* Oscillator startup time */
23562306a36Sopenharmony_ci		rtc_delayed_write(0, rtc, RTC_TIME);
23662306a36Sopenharmony_ci		rtc_delayed_write(SOC_RTC_ALARM1 | SOC_RTC_ALARM2, rtc,
23762306a36Sopenharmony_ci				  RTC_STATUS);
23862306a36Sopenharmony_ci		rtc_delayed_write(RTC_NOMINAL_TIMING, rtc, RTC_CCR);
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci	rtc->initialized = true;
24162306a36Sopenharmony_ci}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cistatic int armada38x_rtc_set_time(struct device *dev, struct rtc_time *tm)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	struct armada38x_rtc *rtc = dev_get_drvdata(dev);
24662306a36Sopenharmony_ci	unsigned long time, flags;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	time = rtc_tm_to_time64(tm);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	if (!rtc->initialized)
25162306a36Sopenharmony_ci		armada38x_rtc_reset(rtc);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	spin_lock_irqsave(&rtc->lock, flags);
25462306a36Sopenharmony_ci	rtc_delayed_write(time, rtc, RTC_TIME);
25562306a36Sopenharmony_ci	spin_unlock_irqrestore(&rtc->lock, flags);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	return 0;
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic int armada38x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	struct armada38x_rtc *rtc = dev_get_drvdata(dev);
26362306a36Sopenharmony_ci	unsigned long time, flags;
26462306a36Sopenharmony_ci	u32 reg = ALARM_REG(RTC_ALARM1, rtc->data->alarm);
26562306a36Sopenharmony_ci	u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm);
26662306a36Sopenharmony_ci	u32 val;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	spin_lock_irqsave(&rtc->lock, flags);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	time = rtc->data->read_rtc_reg(rtc, reg);
27162306a36Sopenharmony_ci	val = rtc->data->read_rtc_reg(rtc, reg_irq) & RTC_IRQ_AL_EN;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	spin_unlock_irqrestore(&rtc->lock, flags);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	alrm->enabled = val ? 1 : 0;
27662306a36Sopenharmony_ci	rtc_time64_to_tm(time,  &alrm->time);
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	return 0;
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_cistatic int armada38x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	struct armada38x_rtc *rtc = dev_get_drvdata(dev);
28462306a36Sopenharmony_ci	u32 reg = ALARM_REG(RTC_ALARM1, rtc->data->alarm);
28562306a36Sopenharmony_ci	u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm);
28662306a36Sopenharmony_ci	unsigned long time, flags;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	time = rtc_tm_to_time64(&alrm->time);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	spin_lock_irqsave(&rtc->lock, flags);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	rtc_delayed_write(time, rtc, reg);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	if (alrm->enabled) {
29562306a36Sopenharmony_ci		rtc_delayed_write(RTC_IRQ_AL_EN, rtc, reg_irq);
29662306a36Sopenharmony_ci		rtc->data->unmask_interrupt(rtc);
29762306a36Sopenharmony_ci	}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	spin_unlock_irqrestore(&rtc->lock, flags);
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	return 0;
30262306a36Sopenharmony_ci}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cistatic int armada38x_rtc_alarm_irq_enable(struct device *dev,
30562306a36Sopenharmony_ci					 unsigned int enabled)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	struct armada38x_rtc *rtc = dev_get_drvdata(dev);
30862306a36Sopenharmony_ci	u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm);
30962306a36Sopenharmony_ci	unsigned long flags;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	spin_lock_irqsave(&rtc->lock, flags);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	if (enabled)
31462306a36Sopenharmony_ci		rtc_delayed_write(RTC_IRQ_AL_EN, rtc, reg_irq);
31562306a36Sopenharmony_ci	else
31662306a36Sopenharmony_ci		rtc_delayed_write(0, rtc, reg_irq);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	spin_unlock_irqrestore(&rtc->lock, flags);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	return 0;
32162306a36Sopenharmony_ci}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_cistatic irqreturn_t armada38x_rtc_alarm_irq(int irq, void *data)
32462306a36Sopenharmony_ci{
32562306a36Sopenharmony_ci	struct armada38x_rtc *rtc = data;
32662306a36Sopenharmony_ci	u32 val;
32762306a36Sopenharmony_ci	int event = RTC_IRQF | RTC_AF;
32862306a36Sopenharmony_ci	u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	dev_dbg(&rtc->rtc_dev->dev, "%s:irq(%d)\n", __func__, irq);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	spin_lock(&rtc->lock);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	rtc->data->clear_isr(rtc);
33562306a36Sopenharmony_ci	val = rtc->data->read_rtc_reg(rtc, reg_irq);
33662306a36Sopenharmony_ci	/* disable all the interrupts for alarm*/
33762306a36Sopenharmony_ci	rtc_delayed_write(0, rtc, reg_irq);
33862306a36Sopenharmony_ci	/* Ack the event */
33962306a36Sopenharmony_ci	rtc_delayed_write(1 << rtc->data->alarm, rtc, RTC_STATUS);
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	spin_unlock(&rtc->lock);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	if (val & RTC_IRQ_FREQ_EN) {
34462306a36Sopenharmony_ci		if (val & RTC_IRQ_FREQ_1HZ)
34562306a36Sopenharmony_ci			event |= RTC_UF;
34662306a36Sopenharmony_ci		else
34762306a36Sopenharmony_ci			event |= RTC_PF;
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	rtc_update_irq(rtc->rtc_dev, 1, event);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	return IRQ_HANDLED;
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci/*
35662306a36Sopenharmony_ci * The information given in the Armada 388 functional spec is complex.
35762306a36Sopenharmony_ci * They give two different formulas for calculating the offset value,
35862306a36Sopenharmony_ci * but when considering "Offset" as an 8-bit signed integer, they both
35962306a36Sopenharmony_ci * reduce down to (we shall rename "Offset" as "val" here):
36062306a36Sopenharmony_ci *
36162306a36Sopenharmony_ci *   val = (f_ideal / f_measured - 1) / resolution   where f_ideal = 32768
36262306a36Sopenharmony_ci *
36362306a36Sopenharmony_ci * Converting to time, f = 1/t:
36462306a36Sopenharmony_ci *   val = (t_measured / t_ideal - 1) / resolution   where t_ideal = 1/32768
36562306a36Sopenharmony_ci *
36662306a36Sopenharmony_ci *   =>  t_measured / t_ideal = val * resolution + 1
36762306a36Sopenharmony_ci *
36862306a36Sopenharmony_ci * "offset" in the RTC interface is defined as:
36962306a36Sopenharmony_ci *   t = t0 * (1 + offset * 1e-9)
37062306a36Sopenharmony_ci * where t is the desired period, t0 is the measured period with a zero
37162306a36Sopenharmony_ci * offset, which is t_measured above. With t0 = t_measured and t = t_ideal,
37262306a36Sopenharmony_ci *   offset = (t_ideal / t_measured - 1) / 1e-9
37362306a36Sopenharmony_ci *
37462306a36Sopenharmony_ci *   => t_ideal / t_measured = offset * 1e-9 + 1
37562306a36Sopenharmony_ci *
37662306a36Sopenharmony_ci * so:
37762306a36Sopenharmony_ci *
37862306a36Sopenharmony_ci *   offset * 1e-9 + 1 = 1 / (val * resolution + 1)
37962306a36Sopenharmony_ci *
38062306a36Sopenharmony_ci * We want "resolution" to be an integer, so resolution = R * 1e-9, giving
38162306a36Sopenharmony_ci *   offset = 1e18 / (val * R + 1e9) - 1e9
38262306a36Sopenharmony_ci *   val = (1e18 / (offset + 1e9) - 1e9) / R
38362306a36Sopenharmony_ci * with a common transformation:
38462306a36Sopenharmony_ci *   f(x) = 1e18 / (x + 1e9) - 1e9
38562306a36Sopenharmony_ci *   offset = f(val * R)
38662306a36Sopenharmony_ci *   val = f(offset) / R
38762306a36Sopenharmony_ci *
38862306a36Sopenharmony_ci * Armada 38x supports two modes, fine mode (954ppb) and coarse mode (3815ppb).
38962306a36Sopenharmony_ci */
39062306a36Sopenharmony_cistatic long armada38x_ppb_convert(long ppb)
39162306a36Sopenharmony_ci{
39262306a36Sopenharmony_ci	long div = ppb + 1000000000L;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	return div_s64(1000000000000000000LL + div / 2, div) - 1000000000L;
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistatic int armada38x_rtc_read_offset(struct device *dev, long *offset)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	struct armada38x_rtc *rtc = dev_get_drvdata(dev);
40062306a36Sopenharmony_ci	unsigned long ccr, flags;
40162306a36Sopenharmony_ci	long ppb_cor;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	spin_lock_irqsave(&rtc->lock, flags);
40462306a36Sopenharmony_ci	ccr = rtc->data->read_rtc_reg(rtc, RTC_CCR);
40562306a36Sopenharmony_ci	spin_unlock_irqrestore(&rtc->lock, flags);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	ppb_cor = (ccr & RTC_CCR_MODE ? 3815 : 954) * (s8)ccr;
40862306a36Sopenharmony_ci	/* ppb_cor + 1000000000L can never be zero */
40962306a36Sopenharmony_ci	*offset = armada38x_ppb_convert(ppb_cor);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	return 0;
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_cistatic int armada38x_rtc_set_offset(struct device *dev, long offset)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	struct armada38x_rtc *rtc = dev_get_drvdata(dev);
41762306a36Sopenharmony_ci	unsigned long ccr = 0;
41862306a36Sopenharmony_ci	long ppb_cor, off;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	/*
42162306a36Sopenharmony_ci	 * The maximum ppb_cor is -128 * 3815 .. 127 * 3815, but we
42262306a36Sopenharmony_ci	 * need to clamp the input.  This equates to -484270 .. 488558.
42362306a36Sopenharmony_ci	 * Not only is this to stop out of range "off" but also to
42462306a36Sopenharmony_ci	 * avoid the division by zero in armada38x_ppb_convert().
42562306a36Sopenharmony_ci	 */
42662306a36Sopenharmony_ci	offset = clamp(offset, -484270L, 488558L);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	ppb_cor = armada38x_ppb_convert(offset);
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	/*
43162306a36Sopenharmony_ci	 * Use low update mode where possible, which gives a better
43262306a36Sopenharmony_ci	 * resolution of correction.
43362306a36Sopenharmony_ci	 */
43462306a36Sopenharmony_ci	off = DIV_ROUND_CLOSEST(ppb_cor, 954);
43562306a36Sopenharmony_ci	if (off > 127 || off < -128) {
43662306a36Sopenharmony_ci		ccr = RTC_CCR_MODE;
43762306a36Sopenharmony_ci		off = DIV_ROUND_CLOSEST(ppb_cor, 3815);
43862306a36Sopenharmony_ci	}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	/*
44162306a36Sopenharmony_ci	 * Armada 388 requires a bit pattern in bits 14..8 depending on
44262306a36Sopenharmony_ci	 * the sign bit: { 0, ~S, S, S, S, S, S }
44362306a36Sopenharmony_ci	 */
44462306a36Sopenharmony_ci	ccr |= (off & 0x3fff) ^ 0x2000;
44562306a36Sopenharmony_ci	rtc_delayed_write(ccr, rtc, RTC_CCR);
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	return 0;
44862306a36Sopenharmony_ci}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_cistatic const struct rtc_class_ops armada38x_rtc_ops = {
45162306a36Sopenharmony_ci	.read_time = armada38x_rtc_read_time,
45262306a36Sopenharmony_ci	.set_time = armada38x_rtc_set_time,
45362306a36Sopenharmony_ci	.read_alarm = armada38x_rtc_read_alarm,
45462306a36Sopenharmony_ci	.set_alarm = armada38x_rtc_set_alarm,
45562306a36Sopenharmony_ci	.alarm_irq_enable = armada38x_rtc_alarm_irq_enable,
45662306a36Sopenharmony_ci	.read_offset = armada38x_rtc_read_offset,
45762306a36Sopenharmony_ci	.set_offset = armada38x_rtc_set_offset,
45862306a36Sopenharmony_ci};
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_cistatic const struct armada38x_rtc_data armada38x_data = {
46162306a36Sopenharmony_ci	.update_mbus_timing = rtc_update_38x_mbus_timing_params,
46262306a36Sopenharmony_ci	.read_rtc_reg = read_rtc_register_38x_wa,
46362306a36Sopenharmony_ci	.clear_isr = armada38x_clear_isr,
46462306a36Sopenharmony_ci	.unmask_interrupt = armada38x_unmask_interrupt,
46562306a36Sopenharmony_ci	.alarm = ALARM1,
46662306a36Sopenharmony_ci};
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_cistatic const struct armada38x_rtc_data armada8k_data = {
46962306a36Sopenharmony_ci	.update_mbus_timing = rtc_update_8k_mbus_timing_params,
47062306a36Sopenharmony_ci	.read_rtc_reg = read_rtc_register,
47162306a36Sopenharmony_ci	.clear_isr = armada8k_clear_isr,
47262306a36Sopenharmony_ci	.unmask_interrupt = armada8k_unmask_interrupt,
47362306a36Sopenharmony_ci	.alarm = ALARM2,
47462306a36Sopenharmony_ci};
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_cistatic const struct of_device_id armada38x_rtc_of_match_table[] = {
47762306a36Sopenharmony_ci	{
47862306a36Sopenharmony_ci		.compatible = "marvell,armada-380-rtc",
47962306a36Sopenharmony_ci		.data = &armada38x_data,
48062306a36Sopenharmony_ci	},
48162306a36Sopenharmony_ci	{
48262306a36Sopenharmony_ci		.compatible = "marvell,armada-8k-rtc",
48362306a36Sopenharmony_ci		.data = &armada8k_data,
48462306a36Sopenharmony_ci	},
48562306a36Sopenharmony_ci	{}
48662306a36Sopenharmony_ci};
48762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, armada38x_rtc_of_match_table);
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cistatic __init int armada38x_rtc_probe(struct platform_device *pdev)
49062306a36Sopenharmony_ci{
49162306a36Sopenharmony_ci	struct armada38x_rtc *rtc;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	rtc = devm_kzalloc(&pdev->dev, sizeof(struct armada38x_rtc),
49462306a36Sopenharmony_ci			    GFP_KERNEL);
49562306a36Sopenharmony_ci	if (!rtc)
49662306a36Sopenharmony_ci		return -ENOMEM;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	rtc->data = of_device_get_match_data(&pdev->dev);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	rtc->val_to_freq = devm_kcalloc(&pdev->dev, SAMPLE_NR,
50162306a36Sopenharmony_ci				sizeof(struct value_to_freq), GFP_KERNEL);
50262306a36Sopenharmony_ci	if (!rtc->val_to_freq)
50362306a36Sopenharmony_ci		return -ENOMEM;
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	spin_lock_init(&rtc->lock);
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	rtc->regs = devm_platform_ioremap_resource_byname(pdev, "rtc");
50862306a36Sopenharmony_ci	if (IS_ERR(rtc->regs))
50962306a36Sopenharmony_ci		return PTR_ERR(rtc->regs);
51062306a36Sopenharmony_ci	rtc->regs_soc = devm_platform_ioremap_resource_byname(pdev, "rtc-soc");
51162306a36Sopenharmony_ci	if (IS_ERR(rtc->regs_soc))
51262306a36Sopenharmony_ci		return PTR_ERR(rtc->regs_soc);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	rtc->irq = platform_get_irq(pdev, 0);
51562306a36Sopenharmony_ci	if (rtc->irq < 0)
51662306a36Sopenharmony_ci		return rtc->irq;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev);
51962306a36Sopenharmony_ci	if (IS_ERR(rtc->rtc_dev))
52062306a36Sopenharmony_ci		return PTR_ERR(rtc->rtc_dev);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	if (devm_request_irq(&pdev->dev, rtc->irq, armada38x_rtc_alarm_irq,
52362306a36Sopenharmony_ci				0, pdev->name, rtc) < 0) {
52462306a36Sopenharmony_ci		dev_warn(&pdev->dev, "Interrupt not available.\n");
52562306a36Sopenharmony_ci		rtc->irq = -1;
52662306a36Sopenharmony_ci	}
52762306a36Sopenharmony_ci	platform_set_drvdata(pdev, rtc);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	if (rtc->irq != -1)
53062306a36Sopenharmony_ci		device_init_wakeup(&pdev->dev, 1);
53162306a36Sopenharmony_ci	else
53262306a36Sopenharmony_ci		clear_bit(RTC_FEATURE_ALARM, rtc->rtc_dev->features);
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	/* Update RTC-MBUS bridge timing parameters */
53562306a36Sopenharmony_ci	rtc->data->update_mbus_timing(rtc);
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	rtc->rtc_dev->ops = &armada38x_rtc_ops;
53862306a36Sopenharmony_ci	rtc->rtc_dev->range_max = U32_MAX;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	return devm_rtc_register_device(rtc->rtc_dev);
54162306a36Sopenharmony_ci}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
54462306a36Sopenharmony_cistatic int armada38x_rtc_suspend(struct device *dev)
54562306a36Sopenharmony_ci{
54662306a36Sopenharmony_ci	if (device_may_wakeup(dev)) {
54762306a36Sopenharmony_ci		struct armada38x_rtc *rtc = dev_get_drvdata(dev);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci		return enable_irq_wake(rtc->irq);
55062306a36Sopenharmony_ci	}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	return 0;
55362306a36Sopenharmony_ci}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_cistatic int armada38x_rtc_resume(struct device *dev)
55662306a36Sopenharmony_ci{
55762306a36Sopenharmony_ci	if (device_may_wakeup(dev)) {
55862306a36Sopenharmony_ci		struct armada38x_rtc *rtc = dev_get_drvdata(dev);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci		/* Update RTC-MBUS bridge timing parameters */
56162306a36Sopenharmony_ci		rtc->data->update_mbus_timing(rtc);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci		return disable_irq_wake(rtc->irq);
56462306a36Sopenharmony_ci	}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	return 0;
56762306a36Sopenharmony_ci}
56862306a36Sopenharmony_ci#endif
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(armada38x_rtc_pm_ops,
57162306a36Sopenharmony_ci			 armada38x_rtc_suspend, armada38x_rtc_resume);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_cistatic struct platform_driver armada38x_rtc_driver = {
57462306a36Sopenharmony_ci	.driver		= {
57562306a36Sopenharmony_ci		.name	= "armada38x-rtc",
57662306a36Sopenharmony_ci		.pm	= &armada38x_rtc_pm_ops,
57762306a36Sopenharmony_ci		.of_match_table = armada38x_rtc_of_match_table,
57862306a36Sopenharmony_ci	},
57962306a36Sopenharmony_ci};
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_cimodule_platform_driver_probe(armada38x_rtc_driver, armada38x_rtc_probe);
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ciMODULE_DESCRIPTION("Marvell Armada 38x RTC driver");
58462306a36Sopenharmony_ciMODULE_AUTHOR("Gregory CLEMENT <gregory.clement@free-electrons.com>");
58562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
586