diff options
author | Gregory CLEMENT <gregory.clement@free-electrons.com> | 2016-12-21 11:28:16 +0100 |
---|---|---|
committer | Alexandre Belloni <alexandre.belloni@free-electrons.com> | 2017-01-11 17:23:03 +0100 |
commit | 844a3073c9896dd252810e388c2fd881efb22d66 (patch) | |
tree | 02d175884e5898de94a400c170f5de2b714b2889 | |
parent | e3dcb74991b792cb9eecae7cd5227b436f8e69ce (diff) | |
download | blackbird-obmc-linux-844a3073c9896dd252810e388c2fd881efb22d66.tar.gz blackbird-obmc-linux-844a3073c9896dd252810e388c2fd881efb22d66.zip |
rtc: armada38x: Follow the new recommendation for errata implementation
According to RES-3124064:
The device supports CPU write and read access to the RTC time register.
However, due to this restriction, read and write from/to internal RTC
register may fail.
Workaround:
General setup:
1. Configure the RTC Mbus Bridge Timing Control register (offset 0x184A0)
to value 0xFD4D4FFF
Write RTC WRCLK Period to its maximum value (0x3FF)
Write RTC WRCLK setup to 0x29
Write RTC WRCLK High Time to 0x53 (default value)
Write RTC Read Output Delay to its maximum value (0x1F)
Mbus - Read All Byte Enable to 0x1 (default value)
2. Configure the RTC Test Configuration Register (offset 0xA381C) bit3
to '1' (Reserved, Marvell internal)
For any RTC register read operation:
1. Read the requested register 100 times.
2. Find the result that appears most frequently and use this result
as the correct value.
For any RTC register write operation:
1. Issue two dummy writes of 0x0 to the RTC Status register (offset
0xA3800).
2. Write the time to the RTC Time register (offset 0xA380C).
This patch is based on the work of Shaker Daibes
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
-rw-r--r-- | drivers/rtc/rtc-armada38x.c | 117 |
1 files changed, 94 insertions, 23 deletions
diff --git a/drivers/rtc/rtc-armada38x.c b/drivers/rtc/rtc-armada38x.c index 9a3f2a6f512e..bc823700a9fe 100644 --- a/drivers/rtc/rtc-armada38x.c +++ b/drivers/rtc/rtc-armada38x.c @@ -29,50 +29,117 @@ #define RTC_TIME 0xC #define RTC_ALARM1 0x10 +#define SOC_RTC_BRIDGE_TIMING_CTL 0x0 +#define SOC_RTC_PERIOD_OFFS 0 +#define SOC_RTC_PERIOD_MASK (0x3FF << SOC_RTC_PERIOD_OFFS) +#define SOC_RTC_READ_DELAY_OFFS 26 +#define SOC_RTC_READ_DELAY_MASK (0x1F << SOC_RTC_READ_DELAY_OFFS) + #define SOC_RTC_INTERRUPT 0x8 #define SOC_RTC_ALARM1 BIT(0) #define SOC_RTC_ALARM2 BIT(1) #define SOC_RTC_ALARM1_MASK BIT(2) #define SOC_RTC_ALARM2_MASK BIT(3) +#define SAMPLE_NR 100 + +struct value_to_freq { + u32 value; + u8 freq; +}; + struct armada38x_rtc { struct rtc_device *rtc_dev; void __iomem *regs; void __iomem *regs_soc; spinlock_t lock; int irq; + struct value_to_freq *val_to_freq; }; /* * According to the datasheet, the OS should wait 5us after every * register write to the RTC hard macro so that the required update * can occur without holding off the system bus + * According to errata RES-3124064, Write to any RTC register + * may fail. As a workaround, before writing to RTC + * register, issue a dummy write of 0x0 twice to RTC Status + * register. */ + static void rtc_delayed_write(u32 val, struct armada38x_rtc *rtc, int offset) { + writel(0, rtc->regs + RTC_STATUS); + writel(0, rtc->regs + RTC_STATUS); writel(val, rtc->regs + offset); udelay(5); } +/* Update RTC-MBUS bridge timing parameters */ +static void rtc_update_mbus_timing_params(struct armada38x_rtc *rtc) +{ + u32 reg; + + reg = readl(rtc->regs_soc + SOC_RTC_BRIDGE_TIMING_CTL); + reg &= ~SOC_RTC_PERIOD_MASK; + reg |= 0x3FF << SOC_RTC_PERIOD_OFFS; /* Maximum value */ + reg &= ~SOC_RTC_READ_DELAY_MASK; + reg |= 0x1F << SOC_RTC_READ_DELAY_OFFS; /* Maximum value */ + writel(reg, rtc->regs_soc + SOC_RTC_BRIDGE_TIMING_CTL); +} + +static u32 read_rtc_register_wa(struct armada38x_rtc *rtc, u8 rtc_reg) +{ + int i, index_max = 0, max = 0; + + for (i = 0; i < SAMPLE_NR; i++) { + rtc->val_to_freq[i].value = readl(rtc->regs + rtc_reg); + rtc->val_to_freq[i].freq = 0; + } + + for (i = 0; i < SAMPLE_NR; i++) { + int j = 0; + u32 value = rtc->val_to_freq[i].value; + + while (rtc->val_to_freq[j].freq) { + if (rtc->val_to_freq[j].value == value) { + rtc->val_to_freq[j].freq++; + break; + } + j++; + } + + if (!rtc->val_to_freq[j].freq) { + rtc->val_to_freq[j].value = value; + rtc->val_to_freq[j].freq = 1; + } + + if (rtc->val_to_freq[j].freq > max) { + index_max = j; + max = rtc->val_to_freq[j].freq; + } + + /* + * If a value already has half of the sample this is the most + * frequent one and we can stop the research right now + */ + if (max > SAMPLE_NR / 2) + break; + } + + return rtc->val_to_freq[index_max].value; +} + static int armada38x_rtc_read_time(struct device *dev, struct rtc_time *tm) { struct armada38x_rtc *rtc = dev_get_drvdata(dev); - unsigned long time, time_check, flags; + unsigned long time, flags; spin_lock_irqsave(&rtc->lock, flags); - time = readl(rtc->regs + RTC_TIME); - /* - * WA for failing time set attempts. As stated in HW ERRATA if - * more than one second between two time reads is detected - * then read once again. - */ - time_check = readl(rtc->regs + RTC_TIME); - if ((time_check - time) > 1) - time_check = readl(rtc->regs + RTC_TIME); - + time = read_rtc_register_wa(rtc, RTC_TIME); spin_unlock_irqrestore(&rtc->lock, flags); - rtc_time_to_tm(time_check, tm); + rtc_time_to_tm(time, tm); return 0; } @@ -87,16 +154,9 @@ static int armada38x_rtc_set_time(struct device *dev, struct rtc_time *tm) if (ret) goto out; - /* - * According to errata FE-3124064, Write to RTC TIME register - * may fail. As a workaround, after writing to RTC TIME - * register, issue a dummy write of 0x0 twice to RTC Status - * register. - */ + spin_lock_irqsave(&rtc->lock, flags); rtc_delayed_write(time, rtc, RTC_TIME); - rtc_delayed_write(0, rtc, RTC_STATUS); - rtc_delayed_write(0, rtc, RTC_STATUS); spin_unlock_irqrestore(&rtc->lock, flags); out: @@ -111,8 +171,8 @@ static int armada38x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) spin_lock_irqsave(&rtc->lock, flags); - time = readl(rtc->regs + RTC_ALARM1); - val = readl(rtc->regs + RTC_IRQ1_CONF) & RTC_IRQ1_AL_EN; + time = read_rtc_register_wa(rtc, RTC_ALARM1); + val = read_rtc_register_wa(rtc, RTC_IRQ1_CONF) & RTC_IRQ1_AL_EN; spin_unlock_irqrestore(&rtc->lock, flags); @@ -182,7 +242,7 @@ static irqreturn_t armada38x_rtc_alarm_irq(int irq, void *data) val = readl(rtc->regs_soc + SOC_RTC_INTERRUPT); writel(val & ~SOC_RTC_ALARM1, rtc->regs_soc + SOC_RTC_INTERRUPT); - val = readl(rtc->regs + RTC_IRQ1_CONF); + val = read_rtc_register_wa(rtc, RTC_IRQ1_CONF); /* disable all the interrupts for alarm 1 */ rtc_delayed_write(0, rtc, RTC_IRQ1_CONF); /* Ack the event */ @@ -221,6 +281,11 @@ static __init int armada38x_rtc_probe(struct platform_device *pdev) if (!rtc) return -ENOMEM; + rtc->val_to_freq = devm_kcalloc(&pdev->dev, SAMPLE_NR, + sizeof(struct value_to_freq), GFP_KERNEL); + if (!rtc->val_to_freq) + return -ENOMEM; + spin_lock_init(&rtc->lock); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rtc"); @@ -253,6 +318,9 @@ static __init int armada38x_rtc_probe(struct platform_device *pdev) if (rtc->irq != -1) device_init_wakeup(&pdev->dev, 1); + /* Update RTC-MBUS bridge timing parameters */ + rtc_update_mbus_timing_params(rtc); + rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name, &armada38x_rtc_ops, THIS_MODULE); if (IS_ERR(rtc->rtc_dev)) { @@ -280,6 +348,9 @@ static int armada38x_rtc_resume(struct device *dev) if (device_may_wakeup(dev)) { struct armada38x_rtc *rtc = dev_get_drvdata(dev); + /* Update RTC-MBUS bridge timing parameters */ + rtc_update_mbus_timing_params(rtc); + return disable_irq_wake(rtc->irq); } |