diff options
Diffstat (limited to 'drivers/rtc/rtc-armada38x.c')
-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); } |