19765d2d9SChen-Yu Tsai /* 29765d2d9SChen-Yu Tsai * An RTC driver for Allwinner A31/A23 39765d2d9SChen-Yu Tsai * 49765d2d9SChen-Yu Tsai * Copyright (c) 2014, Chen-Yu Tsai <wens@csie.org> 59765d2d9SChen-Yu Tsai * 69765d2d9SChen-Yu Tsai * based on rtc-sunxi.c 79765d2d9SChen-Yu Tsai * 89765d2d9SChen-Yu Tsai * An RTC driver for Allwinner A10/A20 99765d2d9SChen-Yu Tsai * 109765d2d9SChen-Yu Tsai * Copyright (c) 2013, Carlo Caione <carlo.caione@gmail.com> 119765d2d9SChen-Yu Tsai * 129765d2d9SChen-Yu Tsai * This program is free software; you can redistribute it and/or modify 139765d2d9SChen-Yu Tsai * it under the terms of the GNU General Public License as published by 149765d2d9SChen-Yu Tsai * the Free Software Foundation; either version 2 of the License, or 159765d2d9SChen-Yu Tsai * (at your option) any later version. 169765d2d9SChen-Yu Tsai * 179765d2d9SChen-Yu Tsai * This program is distributed in the hope that it will be useful, but WITHOUT 189765d2d9SChen-Yu Tsai * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 199765d2d9SChen-Yu Tsai * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 209765d2d9SChen-Yu Tsai * more details. 219765d2d9SChen-Yu Tsai */ 229765d2d9SChen-Yu Tsai 239765d2d9SChen-Yu Tsai #include <linux/delay.h> 249765d2d9SChen-Yu Tsai #include <linux/err.h> 259765d2d9SChen-Yu Tsai #include <linux/fs.h> 269765d2d9SChen-Yu Tsai #include <linux/init.h> 279765d2d9SChen-Yu Tsai #include <linux/interrupt.h> 289765d2d9SChen-Yu Tsai #include <linux/io.h> 299765d2d9SChen-Yu Tsai #include <linux/kernel.h> 309765d2d9SChen-Yu Tsai #include <linux/module.h> 319765d2d9SChen-Yu Tsai #include <linux/of.h> 329765d2d9SChen-Yu Tsai #include <linux/of_address.h> 339765d2d9SChen-Yu Tsai #include <linux/of_device.h> 349765d2d9SChen-Yu Tsai #include <linux/platform_device.h> 359765d2d9SChen-Yu Tsai #include <linux/rtc.h> 369765d2d9SChen-Yu Tsai #include <linux/types.h> 379765d2d9SChen-Yu Tsai 389765d2d9SChen-Yu Tsai /* Control register */ 399765d2d9SChen-Yu Tsai #define SUN6I_LOSC_CTRL 0x0000 40*fb61bb82SMaxime Ripard #define SUN6I_LOSC_CTRL_KEY (0x16aa << 16) 419765d2d9SChen-Yu Tsai #define SUN6I_LOSC_CTRL_ALM_DHMS_ACC BIT(9) 429765d2d9SChen-Yu Tsai #define SUN6I_LOSC_CTRL_RTC_HMS_ACC BIT(8) 439765d2d9SChen-Yu Tsai #define SUN6I_LOSC_CTRL_RTC_YMD_ACC BIT(7) 44*fb61bb82SMaxime Ripard #define SUN6I_LOSC_CTRL_EXT_OSC BIT(0) 459765d2d9SChen-Yu Tsai #define SUN6I_LOSC_CTRL_ACC_MASK GENMASK(9, 7) 469765d2d9SChen-Yu Tsai 479765d2d9SChen-Yu Tsai /* RTC */ 489765d2d9SChen-Yu Tsai #define SUN6I_RTC_YMD 0x0010 499765d2d9SChen-Yu Tsai #define SUN6I_RTC_HMS 0x0014 509765d2d9SChen-Yu Tsai 519765d2d9SChen-Yu Tsai /* Alarm 0 (counter) */ 529765d2d9SChen-Yu Tsai #define SUN6I_ALRM_COUNTER 0x0020 539765d2d9SChen-Yu Tsai #define SUN6I_ALRM_CUR_VAL 0x0024 549765d2d9SChen-Yu Tsai #define SUN6I_ALRM_EN 0x0028 559765d2d9SChen-Yu Tsai #define SUN6I_ALRM_EN_CNT_EN BIT(0) 569765d2d9SChen-Yu Tsai #define SUN6I_ALRM_IRQ_EN 0x002c 579765d2d9SChen-Yu Tsai #define SUN6I_ALRM_IRQ_EN_CNT_IRQ_EN BIT(0) 589765d2d9SChen-Yu Tsai #define SUN6I_ALRM_IRQ_STA 0x0030 599765d2d9SChen-Yu Tsai #define SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND BIT(0) 609765d2d9SChen-Yu Tsai 619765d2d9SChen-Yu Tsai /* Alarm 1 (wall clock) */ 629765d2d9SChen-Yu Tsai #define SUN6I_ALRM1_EN 0x0044 639765d2d9SChen-Yu Tsai #define SUN6I_ALRM1_IRQ_EN 0x0048 649765d2d9SChen-Yu Tsai #define SUN6I_ALRM1_IRQ_STA 0x004c 659765d2d9SChen-Yu Tsai #define SUN6I_ALRM1_IRQ_STA_WEEK_IRQ_PEND BIT(0) 669765d2d9SChen-Yu Tsai 679765d2d9SChen-Yu Tsai /* Alarm config */ 689765d2d9SChen-Yu Tsai #define SUN6I_ALARM_CONFIG 0x0050 699765d2d9SChen-Yu Tsai #define SUN6I_ALARM_CONFIG_WAKEUP BIT(0) 709765d2d9SChen-Yu Tsai 719765d2d9SChen-Yu Tsai /* 729765d2d9SChen-Yu Tsai * Get date values 739765d2d9SChen-Yu Tsai */ 749765d2d9SChen-Yu Tsai #define SUN6I_DATE_GET_DAY_VALUE(x) ((x) & 0x0000001f) 759765d2d9SChen-Yu Tsai #define SUN6I_DATE_GET_MON_VALUE(x) (((x) & 0x00000f00) >> 8) 769765d2d9SChen-Yu Tsai #define SUN6I_DATE_GET_YEAR_VALUE(x) (((x) & 0x003f0000) >> 16) 779765d2d9SChen-Yu Tsai #define SUN6I_LEAP_GET_VALUE(x) (((x) & 0x00400000) >> 22) 789765d2d9SChen-Yu Tsai 799765d2d9SChen-Yu Tsai /* 809765d2d9SChen-Yu Tsai * Get time values 819765d2d9SChen-Yu Tsai */ 829765d2d9SChen-Yu Tsai #define SUN6I_TIME_GET_SEC_VALUE(x) ((x) & 0x0000003f) 839765d2d9SChen-Yu Tsai #define SUN6I_TIME_GET_MIN_VALUE(x) (((x) & 0x00003f00) >> 8) 849765d2d9SChen-Yu Tsai #define SUN6I_TIME_GET_HOUR_VALUE(x) (((x) & 0x001f0000) >> 16) 859765d2d9SChen-Yu Tsai 869765d2d9SChen-Yu Tsai /* 879765d2d9SChen-Yu Tsai * Set date values 889765d2d9SChen-Yu Tsai */ 899765d2d9SChen-Yu Tsai #define SUN6I_DATE_SET_DAY_VALUE(x) ((x) & 0x0000001f) 909765d2d9SChen-Yu Tsai #define SUN6I_DATE_SET_MON_VALUE(x) ((x) << 8 & 0x00000f00) 919765d2d9SChen-Yu Tsai #define SUN6I_DATE_SET_YEAR_VALUE(x) ((x) << 16 & 0x003f0000) 929765d2d9SChen-Yu Tsai #define SUN6I_LEAP_SET_VALUE(x) ((x) << 22 & 0x00400000) 939765d2d9SChen-Yu Tsai 949765d2d9SChen-Yu Tsai /* 959765d2d9SChen-Yu Tsai * Set time values 969765d2d9SChen-Yu Tsai */ 979765d2d9SChen-Yu Tsai #define SUN6I_TIME_SET_SEC_VALUE(x) ((x) & 0x0000003f) 989765d2d9SChen-Yu Tsai #define SUN6I_TIME_SET_MIN_VALUE(x) ((x) << 8 & 0x00003f00) 999765d2d9SChen-Yu Tsai #define SUN6I_TIME_SET_HOUR_VALUE(x) ((x) << 16 & 0x001f0000) 1009765d2d9SChen-Yu Tsai 1019765d2d9SChen-Yu Tsai /* 1029765d2d9SChen-Yu Tsai * The year parameter passed to the driver is usually an offset relative to 1039765d2d9SChen-Yu Tsai * the year 1900. This macro is used to convert this offset to another one 1049765d2d9SChen-Yu Tsai * relative to the minimum year allowed by the hardware. 1059765d2d9SChen-Yu Tsai * 1069765d2d9SChen-Yu Tsai * The year range is 1970 - 2033. This range is selected to match Allwinner's 1079765d2d9SChen-Yu Tsai * driver, even though it is somewhat limited. 1089765d2d9SChen-Yu Tsai */ 1099765d2d9SChen-Yu Tsai #define SUN6I_YEAR_MIN 1970 1109765d2d9SChen-Yu Tsai #define SUN6I_YEAR_MAX 2033 1119765d2d9SChen-Yu Tsai #define SUN6I_YEAR_OFF (SUN6I_YEAR_MIN - 1900) 1129765d2d9SChen-Yu Tsai 1139765d2d9SChen-Yu Tsai struct sun6i_rtc_dev { 1149765d2d9SChen-Yu Tsai struct rtc_device *rtc; 1159765d2d9SChen-Yu Tsai struct device *dev; 1169765d2d9SChen-Yu Tsai void __iomem *base; 1179765d2d9SChen-Yu Tsai int irq; 1189765d2d9SChen-Yu Tsai unsigned long alarm; 119a9422a19SMaxime Ripard 120a9422a19SMaxime Ripard spinlock_t lock; 1219765d2d9SChen-Yu Tsai }; 1229765d2d9SChen-Yu Tsai 1239765d2d9SChen-Yu Tsai static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id) 1249765d2d9SChen-Yu Tsai { 1259765d2d9SChen-Yu Tsai struct sun6i_rtc_dev *chip = (struct sun6i_rtc_dev *) id; 126a9422a19SMaxime Ripard irqreturn_t ret = IRQ_NONE; 1279765d2d9SChen-Yu Tsai u32 val; 1289765d2d9SChen-Yu Tsai 129a9422a19SMaxime Ripard spin_lock(&chip->lock); 1309765d2d9SChen-Yu Tsai val = readl(chip->base + SUN6I_ALRM_IRQ_STA); 1319765d2d9SChen-Yu Tsai 1329765d2d9SChen-Yu Tsai if (val & SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND) { 1339765d2d9SChen-Yu Tsai val |= SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND; 1349765d2d9SChen-Yu Tsai writel(val, chip->base + SUN6I_ALRM_IRQ_STA); 1359765d2d9SChen-Yu Tsai 1369765d2d9SChen-Yu Tsai rtc_update_irq(chip->rtc, 1, RTC_AF | RTC_IRQF); 1379765d2d9SChen-Yu Tsai 138a9422a19SMaxime Ripard ret = IRQ_HANDLED; 1399765d2d9SChen-Yu Tsai } 140a9422a19SMaxime Ripard spin_unlock(&chip->lock); 1419765d2d9SChen-Yu Tsai 142a9422a19SMaxime Ripard return ret; 1439765d2d9SChen-Yu Tsai } 1449765d2d9SChen-Yu Tsai 1459765d2d9SChen-Yu Tsai static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip) 1469765d2d9SChen-Yu Tsai { 1479765d2d9SChen-Yu Tsai u32 alrm_val = 0; 1489765d2d9SChen-Yu Tsai u32 alrm_irq_val = 0; 1499765d2d9SChen-Yu Tsai u32 alrm_wake_val = 0; 150a9422a19SMaxime Ripard unsigned long flags; 1519765d2d9SChen-Yu Tsai 1529765d2d9SChen-Yu Tsai if (to) { 1539765d2d9SChen-Yu Tsai alrm_val = SUN6I_ALRM_EN_CNT_EN; 1549765d2d9SChen-Yu Tsai alrm_irq_val = SUN6I_ALRM_IRQ_EN_CNT_IRQ_EN; 1559765d2d9SChen-Yu Tsai alrm_wake_val = SUN6I_ALARM_CONFIG_WAKEUP; 1569765d2d9SChen-Yu Tsai } else { 1579765d2d9SChen-Yu Tsai writel(SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND, 1589765d2d9SChen-Yu Tsai chip->base + SUN6I_ALRM_IRQ_STA); 1599765d2d9SChen-Yu Tsai } 1609765d2d9SChen-Yu Tsai 161a9422a19SMaxime Ripard spin_lock_irqsave(&chip->lock, flags); 1629765d2d9SChen-Yu Tsai writel(alrm_val, chip->base + SUN6I_ALRM_EN); 1639765d2d9SChen-Yu Tsai writel(alrm_irq_val, chip->base + SUN6I_ALRM_IRQ_EN); 1649765d2d9SChen-Yu Tsai writel(alrm_wake_val, chip->base + SUN6I_ALARM_CONFIG); 165a9422a19SMaxime Ripard spin_unlock_irqrestore(&chip->lock, flags); 1669765d2d9SChen-Yu Tsai } 1679765d2d9SChen-Yu Tsai 1689765d2d9SChen-Yu Tsai static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) 1699765d2d9SChen-Yu Tsai { 1709765d2d9SChen-Yu Tsai struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); 1719765d2d9SChen-Yu Tsai u32 date, time; 1729765d2d9SChen-Yu Tsai 1739765d2d9SChen-Yu Tsai /* 1749765d2d9SChen-Yu Tsai * read again in case it changes 1759765d2d9SChen-Yu Tsai */ 1769765d2d9SChen-Yu Tsai do { 1779765d2d9SChen-Yu Tsai date = readl(chip->base + SUN6I_RTC_YMD); 1789765d2d9SChen-Yu Tsai time = readl(chip->base + SUN6I_RTC_HMS); 1799765d2d9SChen-Yu Tsai } while ((date != readl(chip->base + SUN6I_RTC_YMD)) || 1809765d2d9SChen-Yu Tsai (time != readl(chip->base + SUN6I_RTC_HMS))); 1819765d2d9SChen-Yu Tsai 1829765d2d9SChen-Yu Tsai rtc_tm->tm_sec = SUN6I_TIME_GET_SEC_VALUE(time); 1839765d2d9SChen-Yu Tsai rtc_tm->tm_min = SUN6I_TIME_GET_MIN_VALUE(time); 1849765d2d9SChen-Yu Tsai rtc_tm->tm_hour = SUN6I_TIME_GET_HOUR_VALUE(time); 1859765d2d9SChen-Yu Tsai 1869765d2d9SChen-Yu Tsai rtc_tm->tm_mday = SUN6I_DATE_GET_DAY_VALUE(date); 1879765d2d9SChen-Yu Tsai rtc_tm->tm_mon = SUN6I_DATE_GET_MON_VALUE(date); 1889765d2d9SChen-Yu Tsai rtc_tm->tm_year = SUN6I_DATE_GET_YEAR_VALUE(date); 1899765d2d9SChen-Yu Tsai 1909765d2d9SChen-Yu Tsai rtc_tm->tm_mon -= 1; 1919765d2d9SChen-Yu Tsai 1929765d2d9SChen-Yu Tsai /* 1939765d2d9SChen-Yu Tsai * switch from (data_year->min)-relative offset to 1949765d2d9SChen-Yu Tsai * a (1900)-relative one 1959765d2d9SChen-Yu Tsai */ 1969765d2d9SChen-Yu Tsai rtc_tm->tm_year += SUN6I_YEAR_OFF; 1979765d2d9SChen-Yu Tsai 1989765d2d9SChen-Yu Tsai return rtc_valid_tm(rtc_tm); 1999765d2d9SChen-Yu Tsai } 2009765d2d9SChen-Yu Tsai 2019765d2d9SChen-Yu Tsai static int sun6i_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm) 2029765d2d9SChen-Yu Tsai { 2039765d2d9SChen-Yu Tsai struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); 204a9422a19SMaxime Ripard unsigned long flags; 2059765d2d9SChen-Yu Tsai u32 alrm_st; 2069765d2d9SChen-Yu Tsai u32 alrm_en; 2079765d2d9SChen-Yu Tsai 208a9422a19SMaxime Ripard spin_lock_irqsave(&chip->lock, flags); 2099765d2d9SChen-Yu Tsai alrm_en = readl(chip->base + SUN6I_ALRM_IRQ_EN); 2109765d2d9SChen-Yu Tsai alrm_st = readl(chip->base + SUN6I_ALRM_IRQ_STA); 211a9422a19SMaxime Ripard spin_unlock_irqrestore(&chip->lock, flags); 212a9422a19SMaxime Ripard 2139765d2d9SChen-Yu Tsai wkalrm->enabled = !!(alrm_en & SUN6I_ALRM_EN_CNT_EN); 2149765d2d9SChen-Yu Tsai wkalrm->pending = !!(alrm_st & SUN6I_ALRM_EN_CNT_EN); 2159765d2d9SChen-Yu Tsai rtc_time_to_tm(chip->alarm, &wkalrm->time); 2169765d2d9SChen-Yu Tsai 2179765d2d9SChen-Yu Tsai return 0; 2189765d2d9SChen-Yu Tsai } 2199765d2d9SChen-Yu Tsai 2209765d2d9SChen-Yu Tsai static int sun6i_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm) 2219765d2d9SChen-Yu Tsai { 2229765d2d9SChen-Yu Tsai struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); 2239765d2d9SChen-Yu Tsai struct rtc_time *alrm_tm = &wkalrm->time; 2249765d2d9SChen-Yu Tsai struct rtc_time tm_now; 2259765d2d9SChen-Yu Tsai unsigned long time_now = 0; 2269765d2d9SChen-Yu Tsai unsigned long time_set = 0; 2279765d2d9SChen-Yu Tsai unsigned long time_gap = 0; 2289765d2d9SChen-Yu Tsai int ret = 0; 2299765d2d9SChen-Yu Tsai 2309765d2d9SChen-Yu Tsai ret = sun6i_rtc_gettime(dev, &tm_now); 2319765d2d9SChen-Yu Tsai if (ret < 0) { 2329765d2d9SChen-Yu Tsai dev_err(dev, "Error in getting time\n"); 2339765d2d9SChen-Yu Tsai return -EINVAL; 2349765d2d9SChen-Yu Tsai } 2359765d2d9SChen-Yu Tsai 2369765d2d9SChen-Yu Tsai rtc_tm_to_time(alrm_tm, &time_set); 2379765d2d9SChen-Yu Tsai rtc_tm_to_time(&tm_now, &time_now); 2389765d2d9SChen-Yu Tsai if (time_set <= time_now) { 2399765d2d9SChen-Yu Tsai dev_err(dev, "Date to set in the past\n"); 2409765d2d9SChen-Yu Tsai return -EINVAL; 2419765d2d9SChen-Yu Tsai } 2429765d2d9SChen-Yu Tsai 2439765d2d9SChen-Yu Tsai time_gap = time_set - time_now; 2449765d2d9SChen-Yu Tsai 2459765d2d9SChen-Yu Tsai if (time_gap > U32_MAX) { 2469765d2d9SChen-Yu Tsai dev_err(dev, "Date too far in the future\n"); 2479765d2d9SChen-Yu Tsai return -EINVAL; 2489765d2d9SChen-Yu Tsai } 2499765d2d9SChen-Yu Tsai 2509765d2d9SChen-Yu Tsai sun6i_rtc_setaie(0, chip); 2519765d2d9SChen-Yu Tsai writel(0, chip->base + SUN6I_ALRM_COUNTER); 2529765d2d9SChen-Yu Tsai usleep_range(100, 300); 2539765d2d9SChen-Yu Tsai 2549765d2d9SChen-Yu Tsai writel(time_gap, chip->base + SUN6I_ALRM_COUNTER); 2559765d2d9SChen-Yu Tsai chip->alarm = time_set; 2569765d2d9SChen-Yu Tsai 2579765d2d9SChen-Yu Tsai sun6i_rtc_setaie(wkalrm->enabled, chip); 2589765d2d9SChen-Yu Tsai 2599765d2d9SChen-Yu Tsai return 0; 2609765d2d9SChen-Yu Tsai } 2619765d2d9SChen-Yu Tsai 2629765d2d9SChen-Yu Tsai static int sun6i_rtc_wait(struct sun6i_rtc_dev *chip, int offset, 2639765d2d9SChen-Yu Tsai unsigned int mask, unsigned int ms_timeout) 2649765d2d9SChen-Yu Tsai { 2659765d2d9SChen-Yu Tsai const unsigned long timeout = jiffies + msecs_to_jiffies(ms_timeout); 2669765d2d9SChen-Yu Tsai u32 reg; 2679765d2d9SChen-Yu Tsai 2689765d2d9SChen-Yu Tsai do { 2699765d2d9SChen-Yu Tsai reg = readl(chip->base + offset); 2709765d2d9SChen-Yu Tsai reg &= mask; 2719765d2d9SChen-Yu Tsai 2729765d2d9SChen-Yu Tsai if (!reg) 2739765d2d9SChen-Yu Tsai return 0; 2749765d2d9SChen-Yu Tsai 2759765d2d9SChen-Yu Tsai } while (time_before(jiffies, timeout)); 2769765d2d9SChen-Yu Tsai 2779765d2d9SChen-Yu Tsai return -ETIMEDOUT; 2789765d2d9SChen-Yu Tsai } 2799765d2d9SChen-Yu Tsai 2809765d2d9SChen-Yu Tsai static int sun6i_rtc_settime(struct device *dev, struct rtc_time *rtc_tm) 2819765d2d9SChen-Yu Tsai { 2829765d2d9SChen-Yu Tsai struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); 2839765d2d9SChen-Yu Tsai u32 date = 0; 2849765d2d9SChen-Yu Tsai u32 time = 0; 2859765d2d9SChen-Yu Tsai int year; 2869765d2d9SChen-Yu Tsai 2879765d2d9SChen-Yu Tsai year = rtc_tm->tm_year + 1900; 2889765d2d9SChen-Yu Tsai if (year < SUN6I_YEAR_MIN || year > SUN6I_YEAR_MAX) { 2899765d2d9SChen-Yu Tsai dev_err(dev, "rtc only supports year in range %d - %d\n", 2909765d2d9SChen-Yu Tsai SUN6I_YEAR_MIN, SUN6I_YEAR_MAX); 2919765d2d9SChen-Yu Tsai return -EINVAL; 2929765d2d9SChen-Yu Tsai } 2939765d2d9SChen-Yu Tsai 2949765d2d9SChen-Yu Tsai rtc_tm->tm_year -= SUN6I_YEAR_OFF; 2959765d2d9SChen-Yu Tsai rtc_tm->tm_mon += 1; 2969765d2d9SChen-Yu Tsai 2979765d2d9SChen-Yu Tsai date = SUN6I_DATE_SET_DAY_VALUE(rtc_tm->tm_mday) | 2989765d2d9SChen-Yu Tsai SUN6I_DATE_SET_MON_VALUE(rtc_tm->tm_mon) | 2999765d2d9SChen-Yu Tsai SUN6I_DATE_SET_YEAR_VALUE(rtc_tm->tm_year); 3009765d2d9SChen-Yu Tsai 3019765d2d9SChen-Yu Tsai if (is_leap_year(year)) 3029765d2d9SChen-Yu Tsai date |= SUN6I_LEAP_SET_VALUE(1); 3039765d2d9SChen-Yu Tsai 3049765d2d9SChen-Yu Tsai time = SUN6I_TIME_SET_SEC_VALUE(rtc_tm->tm_sec) | 3059765d2d9SChen-Yu Tsai SUN6I_TIME_SET_MIN_VALUE(rtc_tm->tm_min) | 3069765d2d9SChen-Yu Tsai SUN6I_TIME_SET_HOUR_VALUE(rtc_tm->tm_hour); 3079765d2d9SChen-Yu Tsai 3089765d2d9SChen-Yu Tsai /* Check whether registers are writable */ 3099765d2d9SChen-Yu Tsai if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL, 3109765d2d9SChen-Yu Tsai SUN6I_LOSC_CTRL_ACC_MASK, 50)) { 3119765d2d9SChen-Yu Tsai dev_err(dev, "rtc is still busy.\n"); 3129765d2d9SChen-Yu Tsai return -EBUSY; 3139765d2d9SChen-Yu Tsai } 3149765d2d9SChen-Yu Tsai 3159765d2d9SChen-Yu Tsai writel(time, chip->base + SUN6I_RTC_HMS); 3169765d2d9SChen-Yu Tsai 3179765d2d9SChen-Yu Tsai /* 3189765d2d9SChen-Yu Tsai * After writing the RTC HH-MM-SS register, the 3199765d2d9SChen-Yu Tsai * SUN6I_LOSC_CTRL_RTC_HMS_ACC bit is set and it will not 3209765d2d9SChen-Yu Tsai * be cleared until the real writing operation is finished 3219765d2d9SChen-Yu Tsai */ 3229765d2d9SChen-Yu Tsai 3239765d2d9SChen-Yu Tsai if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL, 3249765d2d9SChen-Yu Tsai SUN6I_LOSC_CTRL_RTC_HMS_ACC, 50)) { 3259765d2d9SChen-Yu Tsai dev_err(dev, "Failed to set rtc time.\n"); 3269765d2d9SChen-Yu Tsai return -ETIMEDOUT; 3279765d2d9SChen-Yu Tsai } 3289765d2d9SChen-Yu Tsai 3299765d2d9SChen-Yu Tsai writel(date, chip->base + SUN6I_RTC_YMD); 3309765d2d9SChen-Yu Tsai 3319765d2d9SChen-Yu Tsai /* 3329765d2d9SChen-Yu Tsai * After writing the RTC YY-MM-DD register, the 3339765d2d9SChen-Yu Tsai * SUN6I_LOSC_CTRL_RTC_YMD_ACC bit is set and it will not 3349765d2d9SChen-Yu Tsai * be cleared until the real writing operation is finished 3359765d2d9SChen-Yu Tsai */ 3369765d2d9SChen-Yu Tsai 3379765d2d9SChen-Yu Tsai if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL, 3389765d2d9SChen-Yu Tsai SUN6I_LOSC_CTRL_RTC_YMD_ACC, 50)) { 3399765d2d9SChen-Yu Tsai dev_err(dev, "Failed to set rtc time.\n"); 3409765d2d9SChen-Yu Tsai return -ETIMEDOUT; 3419765d2d9SChen-Yu Tsai } 3429765d2d9SChen-Yu Tsai 3439765d2d9SChen-Yu Tsai return 0; 3449765d2d9SChen-Yu Tsai } 3459765d2d9SChen-Yu Tsai 3469765d2d9SChen-Yu Tsai static int sun6i_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) 3479765d2d9SChen-Yu Tsai { 3489765d2d9SChen-Yu Tsai struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); 3499765d2d9SChen-Yu Tsai 3509765d2d9SChen-Yu Tsai if (!enabled) 3519765d2d9SChen-Yu Tsai sun6i_rtc_setaie(enabled, chip); 3529765d2d9SChen-Yu Tsai 3539765d2d9SChen-Yu Tsai return 0; 3549765d2d9SChen-Yu Tsai } 3559765d2d9SChen-Yu Tsai 3569765d2d9SChen-Yu Tsai static const struct rtc_class_ops sun6i_rtc_ops = { 3579765d2d9SChen-Yu Tsai .read_time = sun6i_rtc_gettime, 3589765d2d9SChen-Yu Tsai .set_time = sun6i_rtc_settime, 3599765d2d9SChen-Yu Tsai .read_alarm = sun6i_rtc_getalarm, 3609765d2d9SChen-Yu Tsai .set_alarm = sun6i_rtc_setalarm, 3619765d2d9SChen-Yu Tsai .alarm_irq_enable = sun6i_rtc_alarm_irq_enable 3629765d2d9SChen-Yu Tsai }; 3639765d2d9SChen-Yu Tsai 3649765d2d9SChen-Yu Tsai static int sun6i_rtc_probe(struct platform_device *pdev) 3659765d2d9SChen-Yu Tsai { 3669765d2d9SChen-Yu Tsai struct sun6i_rtc_dev *chip; 3679765d2d9SChen-Yu Tsai struct resource *res; 3689765d2d9SChen-Yu Tsai int ret; 3699765d2d9SChen-Yu Tsai 3709765d2d9SChen-Yu Tsai chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); 3719765d2d9SChen-Yu Tsai if (!chip) 3729765d2d9SChen-Yu Tsai return -ENOMEM; 373a9422a19SMaxime Ripard spin_lock_init(&chip->lock); 3749765d2d9SChen-Yu Tsai 3759765d2d9SChen-Yu Tsai platform_set_drvdata(pdev, chip); 3769765d2d9SChen-Yu Tsai chip->dev = &pdev->dev; 3779765d2d9SChen-Yu Tsai 3789765d2d9SChen-Yu Tsai res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 3799765d2d9SChen-Yu Tsai chip->base = devm_ioremap_resource(&pdev->dev, res); 3809765d2d9SChen-Yu Tsai if (IS_ERR(chip->base)) 3819765d2d9SChen-Yu Tsai return PTR_ERR(chip->base); 3829765d2d9SChen-Yu Tsai 3839765d2d9SChen-Yu Tsai chip->irq = platform_get_irq(pdev, 0); 3849765d2d9SChen-Yu Tsai if (chip->irq < 0) { 3859765d2d9SChen-Yu Tsai dev_err(&pdev->dev, "No IRQ resource\n"); 3869765d2d9SChen-Yu Tsai return chip->irq; 3879765d2d9SChen-Yu Tsai } 3889765d2d9SChen-Yu Tsai 3899765d2d9SChen-Yu Tsai ret = devm_request_irq(&pdev->dev, chip->irq, sun6i_rtc_alarmirq, 3909765d2d9SChen-Yu Tsai 0, dev_name(&pdev->dev), chip); 3919765d2d9SChen-Yu Tsai if (ret) { 3929765d2d9SChen-Yu Tsai dev_err(&pdev->dev, "Could not request IRQ\n"); 3939765d2d9SChen-Yu Tsai return ret; 3949765d2d9SChen-Yu Tsai } 3959765d2d9SChen-Yu Tsai 3969765d2d9SChen-Yu Tsai /* clear the alarm counter value */ 3979765d2d9SChen-Yu Tsai writel(0, chip->base + SUN6I_ALRM_COUNTER); 3989765d2d9SChen-Yu Tsai 3999765d2d9SChen-Yu Tsai /* disable counter alarm */ 4009765d2d9SChen-Yu Tsai writel(0, chip->base + SUN6I_ALRM_EN); 4019765d2d9SChen-Yu Tsai 4029765d2d9SChen-Yu Tsai /* disable counter alarm interrupt */ 4039765d2d9SChen-Yu Tsai writel(0, chip->base + SUN6I_ALRM_IRQ_EN); 4049765d2d9SChen-Yu Tsai 4059765d2d9SChen-Yu Tsai /* disable week alarm */ 4069765d2d9SChen-Yu Tsai writel(0, chip->base + SUN6I_ALRM1_EN); 4079765d2d9SChen-Yu Tsai 4089765d2d9SChen-Yu Tsai /* disable week alarm interrupt */ 4099765d2d9SChen-Yu Tsai writel(0, chip->base + SUN6I_ALRM1_IRQ_EN); 4109765d2d9SChen-Yu Tsai 4119765d2d9SChen-Yu Tsai /* clear counter alarm pending interrupts */ 4129765d2d9SChen-Yu Tsai writel(SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND, 4139765d2d9SChen-Yu Tsai chip->base + SUN6I_ALRM_IRQ_STA); 4149765d2d9SChen-Yu Tsai 4159765d2d9SChen-Yu Tsai /* clear week alarm pending interrupts */ 4169765d2d9SChen-Yu Tsai writel(SUN6I_ALRM1_IRQ_STA_WEEK_IRQ_PEND, 4179765d2d9SChen-Yu Tsai chip->base + SUN6I_ALRM1_IRQ_STA); 4189765d2d9SChen-Yu Tsai 4199765d2d9SChen-Yu Tsai /* disable alarm wakeup */ 4209765d2d9SChen-Yu Tsai writel(0, chip->base + SUN6I_ALARM_CONFIG); 4219765d2d9SChen-Yu Tsai 422*fb61bb82SMaxime Ripard /* switch to the external, more precise, oscillator */ 423*fb61bb82SMaxime Ripard writel(SUN6I_LOSC_CTRL_KEY | SUN6I_LOSC_CTRL_EXT_OSC, 424*fb61bb82SMaxime Ripard chip->base + SUN6I_LOSC_CTRL); 425*fb61bb82SMaxime Ripard 4269765d2d9SChen-Yu Tsai chip->rtc = rtc_device_register("rtc-sun6i", &pdev->dev, 4279765d2d9SChen-Yu Tsai &sun6i_rtc_ops, THIS_MODULE); 4289765d2d9SChen-Yu Tsai if (IS_ERR(chip->rtc)) { 4299765d2d9SChen-Yu Tsai dev_err(&pdev->dev, "unable to register device\n"); 4309765d2d9SChen-Yu Tsai return PTR_ERR(chip->rtc); 4319765d2d9SChen-Yu Tsai } 4329765d2d9SChen-Yu Tsai 4339765d2d9SChen-Yu Tsai dev_info(&pdev->dev, "RTC enabled\n"); 4349765d2d9SChen-Yu Tsai 4359765d2d9SChen-Yu Tsai return 0; 4369765d2d9SChen-Yu Tsai } 4379765d2d9SChen-Yu Tsai 4389765d2d9SChen-Yu Tsai static int sun6i_rtc_remove(struct platform_device *pdev) 4399765d2d9SChen-Yu Tsai { 4409765d2d9SChen-Yu Tsai struct sun6i_rtc_dev *chip = platform_get_drvdata(pdev); 4419765d2d9SChen-Yu Tsai 4429765d2d9SChen-Yu Tsai rtc_device_unregister(chip->rtc); 4439765d2d9SChen-Yu Tsai 4449765d2d9SChen-Yu Tsai return 0; 4459765d2d9SChen-Yu Tsai } 4469765d2d9SChen-Yu Tsai 4479765d2d9SChen-Yu Tsai static const struct of_device_id sun6i_rtc_dt_ids[] = { 4489765d2d9SChen-Yu Tsai { .compatible = "allwinner,sun6i-a31-rtc" }, 4499765d2d9SChen-Yu Tsai { /* sentinel */ }, 4509765d2d9SChen-Yu Tsai }; 4519765d2d9SChen-Yu Tsai MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids); 4529765d2d9SChen-Yu Tsai 4539765d2d9SChen-Yu Tsai static struct platform_driver sun6i_rtc_driver = { 4549765d2d9SChen-Yu Tsai .probe = sun6i_rtc_probe, 4559765d2d9SChen-Yu Tsai .remove = sun6i_rtc_remove, 4569765d2d9SChen-Yu Tsai .driver = { 4579765d2d9SChen-Yu Tsai .name = "sun6i-rtc", 4589765d2d9SChen-Yu Tsai .of_match_table = sun6i_rtc_dt_ids, 4599765d2d9SChen-Yu Tsai }, 4609765d2d9SChen-Yu Tsai }; 46137539414SMaxime Ripard builtin_platform_driver(sun6i_rtc_driver); 462