From: Shaker Daibes <[email protected]> According to FE-3124064: The device supports CPU write and read access to the RTC time register. However, due to this errata, read from RTC TIME register may fail.
Workaround: General configuration: 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 0x53 (default value ) 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). [[email protected]: cosmetic changes and fix issues for interrupt in original patch] Reviewed-by: Lior Amsalem <[email protected]> Signed-off-by: Gregory CLEMENT <[email protected]> --- drivers/rtc/rtc-armada38x.c | 109 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 85 insertions(+), 24 deletions(-) diff --git a/drivers/rtc/rtc-armada38x.c b/drivers/rtc/rtc-armada38x.c index 9a3f2a6f512e..a0859286a4c4 100644 --- a/drivers/rtc/rtc-armada38x.c +++ b/drivers/rtc/rtc-armada38x.c @@ -29,12 +29,21 @@ #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 armada38x_rtc { struct rtc_device *rtc_dev; void __iomem *regs; @@ -47,32 +56,85 @@ struct armada38x_rtc { * 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 FE-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) +{ + uint32_t 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); +} + +struct str_value_to_freq { + unsigned long value; + u8 freq; +} __packed; + +static unsigned long read_rtc_register_wa(struct armada38x_rtc *rtc, u8 rtc_reg) +{ + unsigned long value_array[SAMPLE_NR], i, j, value; + unsigned long max = 0, index_max = SAMPLE_NR - 1; + struct str_value_to_freq value_to_freq[SAMPLE_NR]; + + for (i = 0; i < SAMPLE_NR; i++) { + value_to_freq[i].freq = 0; + value_array[i] = readl(rtc->regs + rtc_reg); + } + for (i = 0; i < SAMPLE_NR; i++) { + value = value_array[i]; + /* + * if value appears in value_to_freq array so add the + * counter of value, if didn't appear yet in counters + * array then allocate new member of value_to_freq + * array with counter = 1 + */ + for (j = 0; j < SAMPLE_NR; j++) { + if (value_to_freq[j].freq == 0 || + value_to_freq[j].value == value) + break; + if (j == (SAMPLE_NR - 1)) + break; + } + if (value_to_freq[j].freq == 0) + value_to_freq[j].value = value; + value_to_freq[j].freq++; + /* find the most common result */ + if (max < value_to_freq[j].freq) { + index_max = j; + max = value_to_freq[j].freq; + } + } + return value_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 +149,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 +166,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 +237,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 */ @@ -196,7 +251,6 @@ static irqreturn_t armada38x_rtc_alarm_irq(int irq, void *data) else event |= RTC_PF; } - rtc_update_irq(rtc->rtc_dev, 1, event); return IRQ_HANDLED; @@ -253,6 +307,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)) { @@ -260,6 +317,7 @@ static __init int armada38x_rtc_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret); return ret; } + return 0; } @@ -280,6 +338,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); } -- 2.10.2 -- You received this message because you are subscribed to "rtc-linux". Membership options at http://groups.google.com/group/rtc-linux . Please read http://groups.google.com/group/rtc-linux/web/checklist before submitting a driver. --- You received this message because you are subscribed to the Google Groups "rtc-linux" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. For more options, visit https://groups.google.com/d/optout.
