|
@@ -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);
|
|
|
}
|
|
|
|