1a3a42806SGregory CLEMENT /* 2a3a42806SGregory CLEMENT * RTC driver for the Armada 38x Marvell SoCs 3a3a42806SGregory CLEMENT * 4a3a42806SGregory CLEMENT * Copyright (C) 2015 Marvell 5a3a42806SGregory CLEMENT * 6a3a42806SGregory CLEMENT * Gregory Clement <gregory.clement@free-electrons.com> 7a3a42806SGregory CLEMENT * 8a3a42806SGregory CLEMENT * This program is free software; you can redistribute it and/or 9a3a42806SGregory CLEMENT * modify it under the terms of the GNU General Public License as 10a3a42806SGregory CLEMENT * published by the Free Software Foundation; either version 2 of the 11a3a42806SGregory CLEMENT * License, or (at your option) any later version. 12a3a42806SGregory CLEMENT * 13a3a42806SGregory CLEMENT */ 14a3a42806SGregory CLEMENT 15a3a42806SGregory CLEMENT #include <linux/delay.h> 16a3a42806SGregory CLEMENT #include <linux/io.h> 17a3a42806SGregory CLEMENT #include <linux/module.h> 18a3a42806SGregory CLEMENT #include <linux/of.h> 1975faea91SGregory CLEMENT #include <linux/of_device.h> 20a3a42806SGregory CLEMENT #include <linux/platform_device.h> 21a3a42806SGregory CLEMENT #include <linux/rtc.h> 22a3a42806SGregory CLEMENT 23a3a42806SGregory CLEMENT #define RTC_STATUS 0x0 24a3a42806SGregory CLEMENT #define RTC_STATUS_ALARM1 BIT(0) 25a3a42806SGregory CLEMENT #define RTC_STATUS_ALARM2 BIT(1) 26a3a42806SGregory CLEMENT #define RTC_IRQ1_CONF 0x4 2734f54f57SGregory CLEMENT #define RTC_IRQ2_CONF 0x8 2875faea91SGregory CLEMENT #define RTC_IRQ_AL_EN BIT(0) 2975faea91SGregory CLEMENT #define RTC_IRQ_FREQ_EN BIT(1) 3075faea91SGregory CLEMENT #define RTC_IRQ_FREQ_1HZ BIT(2) 31f94ffbc2SRussell King #define RTC_CCR 0x18 32f94ffbc2SRussell King #define RTC_CCR_MODE BIT(15) 331a990fefSBaruch Siach #define RTC_CONF_TEST 0x1C 341a990fefSBaruch Siach #define RTC_NOMINAL_TIMING BIT(13) 3575faea91SGregory CLEMENT 36a3a42806SGregory CLEMENT #define RTC_TIME 0xC 37a3a42806SGregory CLEMENT #define RTC_ALARM1 0x10 3834f54f57SGregory CLEMENT #define RTC_ALARM2 0x14 3934f54f57SGregory CLEMENT 4034f54f57SGregory CLEMENT /* Armada38x SoC registers */ 4175faea91SGregory CLEMENT #define RTC_38X_BRIDGE_TIMING_CTL 0x0 4275faea91SGregory CLEMENT #define RTC_38X_PERIOD_OFFS 0 4375faea91SGregory CLEMENT #define RTC_38X_PERIOD_MASK (0x3FF << RTC_38X_PERIOD_OFFS) 4475faea91SGregory CLEMENT #define RTC_38X_READ_DELAY_OFFS 26 4575faea91SGregory CLEMENT #define RTC_38X_READ_DELAY_MASK (0x1F << RTC_38X_READ_DELAY_OFFS) 46844a3073SGregory CLEMENT 4734f54f57SGregory CLEMENT /* Armada 7K/8K registers */ 4834f54f57SGregory CLEMENT #define RTC_8K_BRIDGE_TIMING_CTL0 0x0 4934f54f57SGregory CLEMENT #define RTC_8K_WRCLK_PERIOD_OFFS 0 5034f54f57SGregory CLEMENT #define RTC_8K_WRCLK_PERIOD_MASK (0xFFFF << RTC_8K_WRCLK_PERIOD_OFFS) 5134f54f57SGregory CLEMENT #define RTC_8K_WRCLK_SETUP_OFFS 16 5234f54f57SGregory CLEMENT #define RTC_8K_WRCLK_SETUP_MASK (0xFFFF << RTC_8K_WRCLK_SETUP_OFFS) 5334f54f57SGregory CLEMENT #define RTC_8K_BRIDGE_TIMING_CTL1 0x4 5434f54f57SGregory CLEMENT #define RTC_8K_READ_DELAY_OFFS 0 5534f54f57SGregory CLEMENT #define RTC_8K_READ_DELAY_MASK (0xFFFF << RTC_8K_READ_DELAY_OFFS) 5634f54f57SGregory CLEMENT 5734f54f57SGregory CLEMENT #define RTC_8K_ISR 0x10 5834f54f57SGregory CLEMENT #define RTC_8K_IMR 0x14 5934f54f57SGregory CLEMENT #define RTC_8K_ALARM2 BIT(0) 6034f54f57SGregory CLEMENT 61a3a42806SGregory CLEMENT #define SOC_RTC_INTERRUPT 0x8 62a3a42806SGregory CLEMENT #define SOC_RTC_ALARM1 BIT(0) 63a3a42806SGregory CLEMENT #define SOC_RTC_ALARM2 BIT(1) 64a3a42806SGregory CLEMENT #define SOC_RTC_ALARM1_MASK BIT(2) 65a3a42806SGregory CLEMENT #define SOC_RTC_ALARM2_MASK BIT(3) 66a3a42806SGregory CLEMENT 67844a3073SGregory CLEMENT #define SAMPLE_NR 100 68844a3073SGregory CLEMENT 69844a3073SGregory CLEMENT struct value_to_freq { 70844a3073SGregory CLEMENT u32 value; 71844a3073SGregory CLEMENT u8 freq; 72844a3073SGregory CLEMENT }; 73844a3073SGregory CLEMENT 74a3a42806SGregory CLEMENT struct armada38x_rtc { 75a3a42806SGregory CLEMENT struct rtc_device *rtc_dev; 76a3a42806SGregory CLEMENT void __iomem *regs; 77a3a42806SGregory CLEMENT void __iomem *regs_soc; 78a3a42806SGregory CLEMENT spinlock_t lock; 79a3a42806SGregory CLEMENT int irq; 801a990fefSBaruch Siach bool initialized; 81844a3073SGregory CLEMENT struct value_to_freq *val_to_freq; 8275faea91SGregory CLEMENT struct armada38x_rtc_data *data; 8375faea91SGregory CLEMENT }; 8475faea91SGregory CLEMENT 8575faea91SGregory CLEMENT #define ALARM1 0 8634f54f57SGregory CLEMENT #define ALARM2 1 8734f54f57SGregory CLEMENT 8875faea91SGregory CLEMENT #define ALARM_REG(base, alarm) ((base) + (alarm) * sizeof(u32)) 8975faea91SGregory CLEMENT 9075faea91SGregory CLEMENT struct armada38x_rtc_data { 9175faea91SGregory CLEMENT /* Initialize the RTC-MBUS bridge timing */ 9275faea91SGregory CLEMENT void (*update_mbus_timing)(struct armada38x_rtc *rtc); 9375faea91SGregory CLEMENT u32 (*read_rtc_reg)(struct armada38x_rtc *rtc, u8 rtc_reg); 9475faea91SGregory CLEMENT void (*clear_isr)(struct armada38x_rtc *rtc); 9575faea91SGregory CLEMENT void (*unmask_interrupt)(struct armada38x_rtc *rtc); 9675faea91SGregory CLEMENT u32 alarm; 97a3a42806SGregory CLEMENT }; 98a3a42806SGregory CLEMENT 99a3a42806SGregory CLEMENT /* 100a3a42806SGregory CLEMENT * According to the datasheet, the OS should wait 5us after every 101a3a42806SGregory CLEMENT * register write to the RTC hard macro so that the required update 102a3a42806SGregory CLEMENT * can occur without holding off the system bus 103844a3073SGregory CLEMENT * According to errata RES-3124064, Write to any RTC register 104844a3073SGregory CLEMENT * may fail. As a workaround, before writing to RTC 105844a3073SGregory CLEMENT * register, issue a dummy write of 0x0 twice to RTC Status 106844a3073SGregory CLEMENT * register. 107a3a42806SGregory CLEMENT */ 108844a3073SGregory CLEMENT 109a3a42806SGregory CLEMENT static void rtc_delayed_write(u32 val, struct armada38x_rtc *rtc, int offset) 110a3a42806SGregory CLEMENT { 111844a3073SGregory CLEMENT writel(0, rtc->regs + RTC_STATUS); 112844a3073SGregory CLEMENT writel(0, rtc->regs + RTC_STATUS); 113a3a42806SGregory CLEMENT writel(val, rtc->regs + offset); 114a3a42806SGregory CLEMENT udelay(5); 115a3a42806SGregory CLEMENT } 116a3a42806SGregory CLEMENT 117844a3073SGregory CLEMENT /* Update RTC-MBUS bridge timing parameters */ 11875faea91SGregory CLEMENT static void rtc_update_38x_mbus_timing_params(struct armada38x_rtc *rtc) 119844a3073SGregory CLEMENT { 120844a3073SGregory CLEMENT u32 reg; 121844a3073SGregory CLEMENT 12275faea91SGregory CLEMENT reg = readl(rtc->regs_soc + RTC_38X_BRIDGE_TIMING_CTL); 12375faea91SGregory CLEMENT reg &= ~RTC_38X_PERIOD_MASK; 12475faea91SGregory CLEMENT reg |= 0x3FF << RTC_38X_PERIOD_OFFS; /* Maximum value */ 12575faea91SGregory CLEMENT reg &= ~RTC_38X_READ_DELAY_MASK; 12675faea91SGregory CLEMENT reg |= 0x1F << RTC_38X_READ_DELAY_OFFS; /* Maximum value */ 12775faea91SGregory CLEMENT writel(reg, rtc->regs_soc + RTC_38X_BRIDGE_TIMING_CTL); 128844a3073SGregory CLEMENT } 129844a3073SGregory CLEMENT 13034f54f57SGregory CLEMENT static void rtc_update_8k_mbus_timing_params(struct armada38x_rtc *rtc) 13134f54f57SGregory CLEMENT { 13234f54f57SGregory CLEMENT u32 reg; 13334f54f57SGregory CLEMENT 13434f54f57SGregory CLEMENT reg = readl(rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL0); 13534f54f57SGregory CLEMENT reg &= ~RTC_8K_WRCLK_PERIOD_MASK; 13634f54f57SGregory CLEMENT reg |= 0x3FF << RTC_8K_WRCLK_PERIOD_OFFS; 13734f54f57SGregory CLEMENT reg &= ~RTC_8K_WRCLK_SETUP_MASK; 13834f54f57SGregory CLEMENT reg |= 0x29 << RTC_8K_WRCLK_SETUP_OFFS; 13934f54f57SGregory CLEMENT writel(reg, rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL0); 14034f54f57SGregory CLEMENT 14134f54f57SGregory CLEMENT reg = readl(rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL1); 14234f54f57SGregory CLEMENT reg &= ~RTC_8K_READ_DELAY_MASK; 14334f54f57SGregory CLEMENT reg |= 0x3F << RTC_8K_READ_DELAY_OFFS; 14434f54f57SGregory CLEMENT writel(reg, rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL1); 14534f54f57SGregory CLEMENT } 14634f54f57SGregory CLEMENT 14734f54f57SGregory CLEMENT static u32 read_rtc_register(struct armada38x_rtc *rtc, u8 rtc_reg) 14834f54f57SGregory CLEMENT { 14934f54f57SGregory CLEMENT return readl(rtc->regs + rtc_reg); 15034f54f57SGregory CLEMENT } 15134f54f57SGregory CLEMENT 15275faea91SGregory CLEMENT static u32 read_rtc_register_38x_wa(struct armada38x_rtc *rtc, u8 rtc_reg) 153844a3073SGregory CLEMENT { 154844a3073SGregory CLEMENT int i, index_max = 0, max = 0; 155844a3073SGregory CLEMENT 156844a3073SGregory CLEMENT for (i = 0; i < SAMPLE_NR; i++) { 157844a3073SGregory CLEMENT rtc->val_to_freq[i].value = readl(rtc->regs + rtc_reg); 158844a3073SGregory CLEMENT rtc->val_to_freq[i].freq = 0; 159844a3073SGregory CLEMENT } 160844a3073SGregory CLEMENT 161844a3073SGregory CLEMENT for (i = 0; i < SAMPLE_NR; i++) { 162844a3073SGregory CLEMENT int j = 0; 163844a3073SGregory CLEMENT u32 value = rtc->val_to_freq[i].value; 164844a3073SGregory CLEMENT 165844a3073SGregory CLEMENT while (rtc->val_to_freq[j].freq) { 166844a3073SGregory CLEMENT if (rtc->val_to_freq[j].value == value) { 167844a3073SGregory CLEMENT rtc->val_to_freq[j].freq++; 168844a3073SGregory CLEMENT break; 169844a3073SGregory CLEMENT } 170844a3073SGregory CLEMENT j++; 171844a3073SGregory CLEMENT } 172844a3073SGregory CLEMENT 173844a3073SGregory CLEMENT if (!rtc->val_to_freq[j].freq) { 174844a3073SGregory CLEMENT rtc->val_to_freq[j].value = value; 175844a3073SGregory CLEMENT rtc->val_to_freq[j].freq = 1; 176844a3073SGregory CLEMENT } 177844a3073SGregory CLEMENT 178844a3073SGregory CLEMENT if (rtc->val_to_freq[j].freq > max) { 179844a3073SGregory CLEMENT index_max = j; 180844a3073SGregory CLEMENT max = rtc->val_to_freq[j].freq; 181844a3073SGregory CLEMENT } 182844a3073SGregory CLEMENT 183844a3073SGregory CLEMENT /* 184844a3073SGregory CLEMENT * If a value already has half of the sample this is the most 185844a3073SGregory CLEMENT * frequent one and we can stop the research right now 186844a3073SGregory CLEMENT */ 187844a3073SGregory CLEMENT if (max > SAMPLE_NR / 2) 188844a3073SGregory CLEMENT break; 189844a3073SGregory CLEMENT } 190844a3073SGregory CLEMENT 191844a3073SGregory CLEMENT return rtc->val_to_freq[index_max].value; 192844a3073SGregory CLEMENT } 193844a3073SGregory CLEMENT 19475faea91SGregory CLEMENT static void armada38x_clear_isr(struct armada38x_rtc *rtc) 19575faea91SGregory CLEMENT { 19675faea91SGregory CLEMENT u32 val = readl(rtc->regs_soc + SOC_RTC_INTERRUPT); 19775faea91SGregory CLEMENT 19875faea91SGregory CLEMENT writel(val & ~SOC_RTC_ALARM1, rtc->regs_soc + SOC_RTC_INTERRUPT); 19975faea91SGregory CLEMENT } 20075faea91SGregory CLEMENT 20175faea91SGregory CLEMENT static void armada38x_unmask_interrupt(struct armada38x_rtc *rtc) 20275faea91SGregory CLEMENT { 20375faea91SGregory CLEMENT u32 val = readl(rtc->regs_soc + SOC_RTC_INTERRUPT); 20475faea91SGregory CLEMENT 20575faea91SGregory CLEMENT writel(val | SOC_RTC_ALARM1_MASK, rtc->regs_soc + SOC_RTC_INTERRUPT); 20675faea91SGregory CLEMENT } 20734f54f57SGregory CLEMENT 20834f54f57SGregory CLEMENT static void armada8k_clear_isr(struct armada38x_rtc *rtc) 20934f54f57SGregory CLEMENT { 21034f54f57SGregory CLEMENT writel(RTC_8K_ALARM2, rtc->regs_soc + RTC_8K_ISR); 21134f54f57SGregory CLEMENT } 21234f54f57SGregory CLEMENT 21334f54f57SGregory CLEMENT static void armada8k_unmask_interrupt(struct armada38x_rtc *rtc) 21434f54f57SGregory CLEMENT { 21534f54f57SGregory CLEMENT writel(RTC_8K_ALARM2, rtc->regs_soc + RTC_8K_IMR); 21634f54f57SGregory CLEMENT } 21734f54f57SGregory CLEMENT 218a3a42806SGregory CLEMENT static int armada38x_rtc_read_time(struct device *dev, struct rtc_time *tm) 219a3a42806SGregory CLEMENT { 220a3a42806SGregory CLEMENT struct armada38x_rtc *rtc = dev_get_drvdata(dev); 221844a3073SGregory CLEMENT unsigned long time, flags; 222a3a42806SGregory CLEMENT 2230c6e7183SNadav Haklai spin_lock_irqsave(&rtc->lock, flags); 22475faea91SGregory CLEMENT time = rtc->data->read_rtc_reg(rtc, RTC_TIME); 2250c6e7183SNadav Haklai spin_unlock_irqrestore(&rtc->lock, flags); 226a3a42806SGregory CLEMENT 227*f6e3d773SAlexandre Belloni rtc_time64_to_tm(time, tm); 228a3a42806SGregory CLEMENT 229a3a42806SGregory CLEMENT return 0; 230a3a42806SGregory CLEMENT } 231a3a42806SGregory CLEMENT 2321a990fefSBaruch Siach static void armada38x_rtc_reset(struct armada38x_rtc *rtc) 2331a990fefSBaruch Siach { 2341a990fefSBaruch Siach u32 reg; 2351a990fefSBaruch Siach 2361a990fefSBaruch Siach reg = rtc->data->read_rtc_reg(rtc, RTC_CONF_TEST); 2371a990fefSBaruch Siach /* If bits [7:0] are non-zero, assume RTC was uninitialized */ 2381a990fefSBaruch Siach if (reg & 0xff) { 2391a990fefSBaruch Siach rtc_delayed_write(0, rtc, RTC_CONF_TEST); 2401a990fefSBaruch Siach msleep(500); /* Oscillator startup time */ 2411a990fefSBaruch Siach rtc_delayed_write(0, rtc, RTC_TIME); 2421a990fefSBaruch Siach rtc_delayed_write(SOC_RTC_ALARM1 | SOC_RTC_ALARM2, rtc, 2431a990fefSBaruch Siach RTC_STATUS); 2441a990fefSBaruch Siach rtc_delayed_write(RTC_NOMINAL_TIMING, rtc, RTC_CCR); 2451a990fefSBaruch Siach } 2461a990fefSBaruch Siach rtc->initialized = true; 2471a990fefSBaruch Siach } 2481a990fefSBaruch Siach 249a3a42806SGregory CLEMENT static int armada38x_rtc_set_time(struct device *dev, struct rtc_time *tm) 250a3a42806SGregory CLEMENT { 251a3a42806SGregory CLEMENT struct armada38x_rtc *rtc = dev_get_drvdata(dev); 2520c6e7183SNadav Haklai unsigned long time, flags; 253a3a42806SGregory CLEMENT 254*f6e3d773SAlexandre Belloni time = rtc_tm_to_time64(tm); 255844a3073SGregory CLEMENT 2561a990fefSBaruch Siach if (!rtc->initialized) 2571a990fefSBaruch Siach armada38x_rtc_reset(rtc); 2581a990fefSBaruch Siach 2590c6e7183SNadav Haklai spin_lock_irqsave(&rtc->lock, flags); 260a3a42806SGregory CLEMENT rtc_delayed_write(time, rtc, RTC_TIME); 2610c6e7183SNadav Haklai spin_unlock_irqrestore(&rtc->lock, flags); 262a3a42806SGregory CLEMENT 263*f6e3d773SAlexandre Belloni return 0; 264a3a42806SGregory CLEMENT } 265a3a42806SGregory CLEMENT 266a3a42806SGregory CLEMENT static int armada38x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) 267a3a42806SGregory CLEMENT { 268a3a42806SGregory CLEMENT struct armada38x_rtc *rtc = dev_get_drvdata(dev); 269a3a42806SGregory CLEMENT unsigned long time, flags; 27075faea91SGregory CLEMENT u32 reg = ALARM_REG(RTC_ALARM1, rtc->data->alarm); 27175faea91SGregory CLEMENT u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm); 272a3a42806SGregory CLEMENT u32 val; 273a3a42806SGregory CLEMENT 274a3a42806SGregory CLEMENT spin_lock_irqsave(&rtc->lock, flags); 275a3a42806SGregory CLEMENT 27675faea91SGregory CLEMENT time = rtc->data->read_rtc_reg(rtc, reg); 27775faea91SGregory CLEMENT val = rtc->data->read_rtc_reg(rtc, reg_irq) & RTC_IRQ_AL_EN; 278a3a42806SGregory CLEMENT 279a3a42806SGregory CLEMENT spin_unlock_irqrestore(&rtc->lock, flags); 280a3a42806SGregory CLEMENT 281a3a42806SGregory CLEMENT alrm->enabled = val ? 1 : 0; 282*f6e3d773SAlexandre Belloni rtc_time64_to_tm(time, &alrm->time); 283a3a42806SGregory CLEMENT 284a3a42806SGregory CLEMENT return 0; 285a3a42806SGregory CLEMENT } 286a3a42806SGregory CLEMENT 287a3a42806SGregory CLEMENT static int armada38x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) 288a3a42806SGregory CLEMENT { 289a3a42806SGregory CLEMENT struct armada38x_rtc *rtc = dev_get_drvdata(dev); 29075faea91SGregory CLEMENT u32 reg = ALARM_REG(RTC_ALARM1, rtc->data->alarm); 29175faea91SGregory CLEMENT u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm); 292a3a42806SGregory CLEMENT unsigned long time, flags; 293a3a42806SGregory CLEMENT 294*f6e3d773SAlexandre Belloni time = rtc_tm_to_time64(&alrm->time); 295a3a42806SGregory CLEMENT 296a3a42806SGregory CLEMENT spin_lock_irqsave(&rtc->lock, flags); 297a3a42806SGregory CLEMENT 29875faea91SGregory CLEMENT rtc_delayed_write(time, rtc, reg); 299a3a42806SGregory CLEMENT 300a3a42806SGregory CLEMENT if (alrm->enabled) { 30175faea91SGregory CLEMENT rtc_delayed_write(RTC_IRQ_AL_EN, rtc, reg_irq); 30275faea91SGregory CLEMENT rtc->data->unmask_interrupt(rtc); 303a3a42806SGregory CLEMENT } 304a3a42806SGregory CLEMENT 305a3a42806SGregory CLEMENT spin_unlock_irqrestore(&rtc->lock, flags); 306a3a42806SGregory CLEMENT 307*f6e3d773SAlexandre Belloni return 0; 308a3a42806SGregory CLEMENT } 309a3a42806SGregory CLEMENT 310a3a42806SGregory CLEMENT static int armada38x_rtc_alarm_irq_enable(struct device *dev, 311a3a42806SGregory CLEMENT unsigned int enabled) 312a3a42806SGregory CLEMENT { 313a3a42806SGregory CLEMENT struct armada38x_rtc *rtc = dev_get_drvdata(dev); 31475faea91SGregory CLEMENT u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm); 315a3a42806SGregory CLEMENT unsigned long flags; 316a3a42806SGregory CLEMENT 317a3a42806SGregory CLEMENT spin_lock_irqsave(&rtc->lock, flags); 318a3a42806SGregory CLEMENT 319a3a42806SGregory CLEMENT if (enabled) 32075faea91SGregory CLEMENT rtc_delayed_write(RTC_IRQ_AL_EN, rtc, reg_irq); 321a3a42806SGregory CLEMENT else 32275faea91SGregory CLEMENT rtc_delayed_write(0, rtc, reg_irq); 323a3a42806SGregory CLEMENT 324a3a42806SGregory CLEMENT spin_unlock_irqrestore(&rtc->lock, flags); 325a3a42806SGregory CLEMENT 326a3a42806SGregory CLEMENT return 0; 327a3a42806SGregory CLEMENT } 328a3a42806SGregory CLEMENT 329a3a42806SGregory CLEMENT static irqreturn_t armada38x_rtc_alarm_irq(int irq, void *data) 330a3a42806SGregory CLEMENT { 331a3a42806SGregory CLEMENT struct armada38x_rtc *rtc = data; 332a3a42806SGregory CLEMENT u32 val; 333a3a42806SGregory CLEMENT int event = RTC_IRQF | RTC_AF; 33475faea91SGregory CLEMENT u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm); 335a3a42806SGregory CLEMENT 336a3a42806SGregory CLEMENT dev_dbg(&rtc->rtc_dev->dev, "%s:irq(%d)\n", __func__, irq); 337a3a42806SGregory CLEMENT 338a3a42806SGregory CLEMENT spin_lock(&rtc->lock); 339a3a42806SGregory CLEMENT 34075faea91SGregory CLEMENT rtc->data->clear_isr(rtc); 34175faea91SGregory CLEMENT val = rtc->data->read_rtc_reg(rtc, reg_irq); 34275faea91SGregory CLEMENT /* disable all the interrupts for alarm*/ 34375faea91SGregory CLEMENT rtc_delayed_write(0, rtc, reg_irq); 344a3a42806SGregory CLEMENT /* Ack the event */ 34575faea91SGregory CLEMENT rtc_delayed_write(1 << rtc->data->alarm, rtc, RTC_STATUS); 346a3a42806SGregory CLEMENT 347a3a42806SGregory CLEMENT spin_unlock(&rtc->lock); 348a3a42806SGregory CLEMENT 34975faea91SGregory CLEMENT if (val & RTC_IRQ_FREQ_EN) { 35075faea91SGregory CLEMENT if (val & RTC_IRQ_FREQ_1HZ) 351a3a42806SGregory CLEMENT event |= RTC_UF; 352a3a42806SGregory CLEMENT else 353a3a42806SGregory CLEMENT event |= RTC_PF; 354a3a42806SGregory CLEMENT } 355a3a42806SGregory CLEMENT 356a3a42806SGregory CLEMENT rtc_update_irq(rtc->rtc_dev, 1, event); 357a3a42806SGregory CLEMENT 358a3a42806SGregory CLEMENT return IRQ_HANDLED; 359a3a42806SGregory CLEMENT } 360a3a42806SGregory CLEMENT 361f94ffbc2SRussell King /* 362f94ffbc2SRussell King * The information given in the Armada 388 functional spec is complex. 363f94ffbc2SRussell King * They give two different formulas for calculating the offset value, 364f94ffbc2SRussell King * but when considering "Offset" as an 8-bit signed integer, they both 365f94ffbc2SRussell King * reduce down to (we shall rename "Offset" as "val" here): 366f94ffbc2SRussell King * 367f94ffbc2SRussell King * val = (f_ideal / f_measured - 1) / resolution where f_ideal = 32768 368f94ffbc2SRussell King * 369f94ffbc2SRussell King * Converting to time, f = 1/t: 370f94ffbc2SRussell King * val = (t_measured / t_ideal - 1) / resolution where t_ideal = 1/32768 371f94ffbc2SRussell King * 372f94ffbc2SRussell King * => t_measured / t_ideal = val * resolution + 1 373f94ffbc2SRussell King * 374f94ffbc2SRussell King * "offset" in the RTC interface is defined as: 375f94ffbc2SRussell King * t = t0 * (1 + offset * 1e-9) 376f94ffbc2SRussell King * where t is the desired period, t0 is the measured period with a zero 377f94ffbc2SRussell King * offset, which is t_measured above. With t0 = t_measured and t = t_ideal, 378f94ffbc2SRussell King * offset = (t_ideal / t_measured - 1) / 1e-9 379f94ffbc2SRussell King * 380f94ffbc2SRussell King * => t_ideal / t_measured = offset * 1e-9 + 1 381f94ffbc2SRussell King * 382f94ffbc2SRussell King * so: 383f94ffbc2SRussell King * 384f94ffbc2SRussell King * offset * 1e-9 + 1 = 1 / (val * resolution + 1) 385f94ffbc2SRussell King * 386f94ffbc2SRussell King * We want "resolution" to be an integer, so resolution = R * 1e-9, giving 387f94ffbc2SRussell King * offset = 1e18 / (val * R + 1e9) - 1e9 388f94ffbc2SRussell King * val = (1e18 / (offset + 1e9) - 1e9) / R 389f94ffbc2SRussell King * with a common transformation: 390f94ffbc2SRussell King * f(x) = 1e18 / (x + 1e9) - 1e9 391f94ffbc2SRussell King * offset = f(val * R) 392f94ffbc2SRussell King * val = f(offset) / R 393f94ffbc2SRussell King * 394f94ffbc2SRussell King * Armada 38x supports two modes, fine mode (954ppb) and coarse mode (3815ppb). 395f94ffbc2SRussell King */ 396f94ffbc2SRussell King static long armada38x_ppb_convert(long ppb) 397f94ffbc2SRussell King { 398f94ffbc2SRussell King long div = ppb + 1000000000L; 399f94ffbc2SRussell King 400f94ffbc2SRussell King return div_s64(1000000000000000000LL + div / 2, div) - 1000000000L; 401f94ffbc2SRussell King } 402f94ffbc2SRussell King 403f94ffbc2SRussell King static int armada38x_rtc_read_offset(struct device *dev, long *offset) 404f94ffbc2SRussell King { 405f94ffbc2SRussell King struct armada38x_rtc *rtc = dev_get_drvdata(dev); 406f94ffbc2SRussell King unsigned long ccr, flags; 407f94ffbc2SRussell King long ppb_cor; 408f94ffbc2SRussell King 409f94ffbc2SRussell King spin_lock_irqsave(&rtc->lock, flags); 410f94ffbc2SRussell King ccr = rtc->data->read_rtc_reg(rtc, RTC_CCR); 411f94ffbc2SRussell King spin_unlock_irqrestore(&rtc->lock, flags); 412f94ffbc2SRussell King 413f94ffbc2SRussell King ppb_cor = (ccr & RTC_CCR_MODE ? 3815 : 954) * (s8)ccr; 414f94ffbc2SRussell King /* ppb_cor + 1000000000L can never be zero */ 415f94ffbc2SRussell King *offset = armada38x_ppb_convert(ppb_cor); 416f94ffbc2SRussell King 417f94ffbc2SRussell King return 0; 418f94ffbc2SRussell King } 419f94ffbc2SRussell King 420f94ffbc2SRussell King static int armada38x_rtc_set_offset(struct device *dev, long offset) 421f94ffbc2SRussell King { 422f94ffbc2SRussell King struct armada38x_rtc *rtc = dev_get_drvdata(dev); 423f94ffbc2SRussell King unsigned long ccr = 0; 424f94ffbc2SRussell King long ppb_cor, off; 425f94ffbc2SRussell King 426f94ffbc2SRussell King /* 427f94ffbc2SRussell King * The maximum ppb_cor is -128 * 3815 .. 127 * 3815, but we 428f94ffbc2SRussell King * need to clamp the input. This equates to -484270 .. 488558. 429f94ffbc2SRussell King * Not only is this to stop out of range "off" but also to 430f94ffbc2SRussell King * avoid the division by zero in armada38x_ppb_convert(). 431f94ffbc2SRussell King */ 432f94ffbc2SRussell King offset = clamp(offset, -484270L, 488558L); 433f94ffbc2SRussell King 434f94ffbc2SRussell King ppb_cor = armada38x_ppb_convert(offset); 435f94ffbc2SRussell King 436f94ffbc2SRussell King /* 437f94ffbc2SRussell King * Use low update mode where possible, which gives a better 438f94ffbc2SRussell King * resolution of correction. 439f94ffbc2SRussell King */ 440f94ffbc2SRussell King off = DIV_ROUND_CLOSEST(ppb_cor, 954); 441f94ffbc2SRussell King if (off > 127 || off < -128) { 442f94ffbc2SRussell King ccr = RTC_CCR_MODE; 443f94ffbc2SRussell King off = DIV_ROUND_CLOSEST(ppb_cor, 3815); 444f94ffbc2SRussell King } 445f94ffbc2SRussell King 446f94ffbc2SRussell King /* 447f94ffbc2SRussell King * Armada 388 requires a bit pattern in bits 14..8 depending on 448f94ffbc2SRussell King * the sign bit: { 0, ~S, S, S, S, S, S } 449f94ffbc2SRussell King */ 450f94ffbc2SRussell King ccr |= (off & 0x3fff) ^ 0x2000; 451f94ffbc2SRussell King rtc_delayed_write(ccr, rtc, RTC_CCR); 452f94ffbc2SRussell King 453f94ffbc2SRussell King return 0; 454f94ffbc2SRussell King } 455f94ffbc2SRussell King 456d748c981SRussell King static const struct rtc_class_ops armada38x_rtc_ops = { 457a3a42806SGregory CLEMENT .read_time = armada38x_rtc_read_time, 458a3a42806SGregory CLEMENT .set_time = armada38x_rtc_set_time, 459a3a42806SGregory CLEMENT .read_alarm = armada38x_rtc_read_alarm, 460a3a42806SGregory CLEMENT .set_alarm = armada38x_rtc_set_alarm, 461a3a42806SGregory CLEMENT .alarm_irq_enable = armada38x_rtc_alarm_irq_enable, 462f94ffbc2SRussell King .read_offset = armada38x_rtc_read_offset, 463f94ffbc2SRussell King .set_offset = armada38x_rtc_set_offset, 464a3a42806SGregory CLEMENT }; 465a3a42806SGregory CLEMENT 466d748c981SRussell King static const struct rtc_class_ops armada38x_rtc_ops_noirq = { 467d748c981SRussell King .read_time = armada38x_rtc_read_time, 468d748c981SRussell King .set_time = armada38x_rtc_set_time, 469d748c981SRussell King .read_alarm = armada38x_rtc_read_alarm, 470f94ffbc2SRussell King .read_offset = armada38x_rtc_read_offset, 471f94ffbc2SRussell King .set_offset = armada38x_rtc_set_offset, 472d748c981SRussell King }; 473d748c981SRussell King 47475faea91SGregory CLEMENT static const struct armada38x_rtc_data armada38x_data = { 47575faea91SGregory CLEMENT .update_mbus_timing = rtc_update_38x_mbus_timing_params, 47675faea91SGregory CLEMENT .read_rtc_reg = read_rtc_register_38x_wa, 47775faea91SGregory CLEMENT .clear_isr = armada38x_clear_isr, 47875faea91SGregory CLEMENT .unmask_interrupt = armada38x_unmask_interrupt, 47975faea91SGregory CLEMENT .alarm = ALARM1, 48075faea91SGregory CLEMENT }; 48175faea91SGregory CLEMENT 48234f54f57SGregory CLEMENT static const struct armada38x_rtc_data armada8k_data = { 48334f54f57SGregory CLEMENT .update_mbus_timing = rtc_update_8k_mbus_timing_params, 48434f54f57SGregory CLEMENT .read_rtc_reg = read_rtc_register, 48534f54f57SGregory CLEMENT .clear_isr = armada8k_clear_isr, 48634f54f57SGregory CLEMENT .unmask_interrupt = armada8k_unmask_interrupt, 48734f54f57SGregory CLEMENT .alarm = ALARM2, 48834f54f57SGregory CLEMENT }; 48934f54f57SGregory CLEMENT 49075faea91SGregory CLEMENT #ifdef CONFIG_OF 49175faea91SGregory CLEMENT static const struct of_device_id armada38x_rtc_of_match_table[] = { 49275faea91SGregory CLEMENT { 49375faea91SGregory CLEMENT .compatible = "marvell,armada-380-rtc", 49475faea91SGregory CLEMENT .data = &armada38x_data, 49575faea91SGregory CLEMENT }, 49634f54f57SGregory CLEMENT { 49734f54f57SGregory CLEMENT .compatible = "marvell,armada-8k-rtc", 49834f54f57SGregory CLEMENT .data = &armada8k_data, 49934f54f57SGregory CLEMENT }, 50075faea91SGregory CLEMENT {} 50175faea91SGregory CLEMENT }; 50275faea91SGregory CLEMENT MODULE_DEVICE_TABLE(of, armada38x_rtc_of_match_table); 50375faea91SGregory CLEMENT #endif 50475faea91SGregory CLEMENT 505a3a42806SGregory CLEMENT static __init int armada38x_rtc_probe(struct platform_device *pdev) 506a3a42806SGregory CLEMENT { 507a3a42806SGregory CLEMENT struct resource *res; 508a3a42806SGregory CLEMENT struct armada38x_rtc *rtc; 50975faea91SGregory CLEMENT const struct of_device_id *match; 510a3a42806SGregory CLEMENT int ret; 511a3a42806SGregory CLEMENT 51275faea91SGregory CLEMENT match = of_match_device(armada38x_rtc_of_match_table, &pdev->dev); 51375faea91SGregory CLEMENT if (!match) 51475faea91SGregory CLEMENT return -ENODEV; 51575faea91SGregory CLEMENT 516a3a42806SGregory CLEMENT rtc = devm_kzalloc(&pdev->dev, sizeof(struct armada38x_rtc), 517a3a42806SGregory CLEMENT GFP_KERNEL); 518a3a42806SGregory CLEMENT if (!rtc) 519a3a42806SGregory CLEMENT return -ENOMEM; 520a3a42806SGregory CLEMENT 521844a3073SGregory CLEMENT rtc->val_to_freq = devm_kcalloc(&pdev->dev, SAMPLE_NR, 522844a3073SGregory CLEMENT sizeof(struct value_to_freq), GFP_KERNEL); 523844a3073SGregory CLEMENT if (!rtc->val_to_freq) 524844a3073SGregory CLEMENT return -ENOMEM; 525844a3073SGregory CLEMENT 526a3a42806SGregory CLEMENT spin_lock_init(&rtc->lock); 527a3a42806SGregory CLEMENT 528a3a42806SGregory CLEMENT res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rtc"); 529a3a42806SGregory CLEMENT rtc->regs = devm_ioremap_resource(&pdev->dev, res); 530a3a42806SGregory CLEMENT if (IS_ERR(rtc->regs)) 531a3a42806SGregory CLEMENT return PTR_ERR(rtc->regs); 532a3a42806SGregory CLEMENT res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rtc-soc"); 533a3a42806SGregory CLEMENT rtc->regs_soc = devm_ioremap_resource(&pdev->dev, res); 534a3a42806SGregory CLEMENT if (IS_ERR(rtc->regs_soc)) 535a3a42806SGregory CLEMENT return PTR_ERR(rtc->regs_soc); 536a3a42806SGregory CLEMENT 537a3a42806SGregory CLEMENT rtc->irq = platform_get_irq(pdev, 0); 538a3a42806SGregory CLEMENT 539a3a42806SGregory CLEMENT if (rtc->irq < 0) { 540a3a42806SGregory CLEMENT dev_err(&pdev->dev, "no irq\n"); 541a3a42806SGregory CLEMENT return rtc->irq; 542a3a42806SGregory CLEMENT } 5437d61cbb9SAlexandre Belloni 5447d61cbb9SAlexandre Belloni rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev); 5457d61cbb9SAlexandre Belloni if (IS_ERR(rtc->rtc_dev)) 5467d61cbb9SAlexandre Belloni return PTR_ERR(rtc->rtc_dev); 5477d61cbb9SAlexandre Belloni 548a3a42806SGregory CLEMENT if (devm_request_irq(&pdev->dev, rtc->irq, armada38x_rtc_alarm_irq, 549a3a42806SGregory CLEMENT 0, pdev->name, rtc) < 0) { 550a3a42806SGregory CLEMENT dev_warn(&pdev->dev, "Interrupt not available.\n"); 551a3a42806SGregory CLEMENT rtc->irq = -1; 552d748c981SRussell King } 553d748c981SRussell King platform_set_drvdata(pdev, rtc); 554d748c981SRussell King 555d748c981SRussell King if (rtc->irq != -1) { 556d748c981SRussell King device_init_wakeup(&pdev->dev, 1); 5577d61cbb9SAlexandre Belloni rtc->rtc_dev->ops = &armada38x_rtc_ops; 558d748c981SRussell King } else { 559a3a42806SGregory CLEMENT /* 560a3a42806SGregory CLEMENT * If there is no interrupt available then we can't 561a3a42806SGregory CLEMENT * use the alarm 562a3a42806SGregory CLEMENT */ 5637d61cbb9SAlexandre Belloni rtc->rtc_dev->ops = &armada38x_rtc_ops_noirq; 564a3a42806SGregory CLEMENT } 56575faea91SGregory CLEMENT rtc->data = (struct armada38x_rtc_data *)match->data; 56675faea91SGregory CLEMENT 567844a3073SGregory CLEMENT /* Update RTC-MBUS bridge timing parameters */ 56875faea91SGregory CLEMENT rtc->data->update_mbus_timing(rtc); 569844a3073SGregory CLEMENT 570ef2a7176SAlexandre Belloni rtc->rtc_dev->range_max = U32_MAX; 571ef2a7176SAlexandre Belloni 5727d61cbb9SAlexandre Belloni ret = rtc_register_device(rtc->rtc_dev); 5737d61cbb9SAlexandre Belloni if (ret) 574a3a42806SGregory CLEMENT dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret); 5757d61cbb9SAlexandre Belloni 576a3a42806SGregory CLEMENT return ret; 577a3a42806SGregory CLEMENT } 578a3a42806SGregory CLEMENT 579a3a42806SGregory CLEMENT #ifdef CONFIG_PM_SLEEP 580a3a42806SGregory CLEMENT static int armada38x_rtc_suspend(struct device *dev) 581a3a42806SGregory CLEMENT { 582a3a42806SGregory CLEMENT if (device_may_wakeup(dev)) { 583a3a42806SGregory CLEMENT struct armada38x_rtc *rtc = dev_get_drvdata(dev); 584a3a42806SGregory CLEMENT 585a3a42806SGregory CLEMENT return enable_irq_wake(rtc->irq); 586a3a42806SGregory CLEMENT } 587a3a42806SGregory CLEMENT 588a3a42806SGregory CLEMENT return 0; 589a3a42806SGregory CLEMENT } 590a3a42806SGregory CLEMENT 591a3a42806SGregory CLEMENT static int armada38x_rtc_resume(struct device *dev) 592a3a42806SGregory CLEMENT { 593a3a42806SGregory CLEMENT if (device_may_wakeup(dev)) { 594a3a42806SGregory CLEMENT struct armada38x_rtc *rtc = dev_get_drvdata(dev); 595a3a42806SGregory CLEMENT 596844a3073SGregory CLEMENT /* Update RTC-MBUS bridge timing parameters */ 59775faea91SGregory CLEMENT rtc->data->update_mbus_timing(rtc); 598844a3073SGregory CLEMENT 599a3a42806SGregory CLEMENT return disable_irq_wake(rtc->irq); 600a3a42806SGregory CLEMENT } 601a3a42806SGregory CLEMENT 602a3a42806SGregory CLEMENT return 0; 603a3a42806SGregory CLEMENT } 604a3a42806SGregory CLEMENT #endif 605a3a42806SGregory CLEMENT 606a3a42806SGregory CLEMENT static SIMPLE_DEV_PM_OPS(armada38x_rtc_pm_ops, 607a3a42806SGregory CLEMENT armada38x_rtc_suspend, armada38x_rtc_resume); 608a3a42806SGregory CLEMENT 609a3a42806SGregory CLEMENT static struct platform_driver armada38x_rtc_driver = { 610a3a42806SGregory CLEMENT .driver = { 611a3a42806SGregory CLEMENT .name = "armada38x-rtc", 612a3a42806SGregory CLEMENT .pm = &armada38x_rtc_pm_ops, 613a3a42806SGregory CLEMENT .of_match_table = of_match_ptr(armada38x_rtc_of_match_table), 614a3a42806SGregory CLEMENT }, 615a3a42806SGregory CLEMENT }; 616a3a42806SGregory CLEMENT 617a3a42806SGregory CLEMENT module_platform_driver_probe(armada38x_rtc_driver, armada38x_rtc_probe); 618a3a42806SGregory CLEMENT 619a3a42806SGregory CLEMENT MODULE_DESCRIPTION("Marvell Armada 38x RTC driver"); 620a3a42806SGregory CLEMENT MODULE_AUTHOR("Gregory CLEMENT <gregory.clement@free-electrons.com>"); 621a3a42806SGregory CLEMENT MODULE_LICENSE("GPL"); 622