1*9765d2d9SChen-Yu Tsai /* 2*9765d2d9SChen-Yu Tsai * An RTC driver for Allwinner A31/A23 3*9765d2d9SChen-Yu Tsai * 4*9765d2d9SChen-Yu Tsai * Copyright (c) 2014, Chen-Yu Tsai <wens@csie.org> 5*9765d2d9SChen-Yu Tsai * 6*9765d2d9SChen-Yu Tsai * based on rtc-sunxi.c 7*9765d2d9SChen-Yu Tsai * 8*9765d2d9SChen-Yu Tsai * An RTC driver for Allwinner A10/A20 9*9765d2d9SChen-Yu Tsai * 10*9765d2d9SChen-Yu Tsai * Copyright (c) 2013, Carlo Caione <carlo.caione@gmail.com> 11*9765d2d9SChen-Yu Tsai * 12*9765d2d9SChen-Yu Tsai * This program is free software; you can redistribute it and/or modify 13*9765d2d9SChen-Yu Tsai * it under the terms of the GNU General Public License as published by 14*9765d2d9SChen-Yu Tsai * the Free Software Foundation; either version 2 of the License, or 15*9765d2d9SChen-Yu Tsai * (at your option) any later version. 16*9765d2d9SChen-Yu Tsai * 17*9765d2d9SChen-Yu Tsai * This program is distributed in the hope that it will be useful, but WITHOUT 18*9765d2d9SChen-Yu Tsai * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 19*9765d2d9SChen-Yu Tsai * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 20*9765d2d9SChen-Yu Tsai * more details. 21*9765d2d9SChen-Yu Tsai */ 22*9765d2d9SChen-Yu Tsai 23*9765d2d9SChen-Yu Tsai #include <linux/delay.h> 24*9765d2d9SChen-Yu Tsai #include <linux/err.h> 25*9765d2d9SChen-Yu Tsai #include <linux/fs.h> 26*9765d2d9SChen-Yu Tsai #include <linux/init.h> 27*9765d2d9SChen-Yu Tsai #include <linux/interrupt.h> 28*9765d2d9SChen-Yu Tsai #include <linux/io.h> 29*9765d2d9SChen-Yu Tsai #include <linux/kernel.h> 30*9765d2d9SChen-Yu Tsai #include <linux/module.h> 31*9765d2d9SChen-Yu Tsai #include <linux/of.h> 32*9765d2d9SChen-Yu Tsai #include <linux/of_address.h> 33*9765d2d9SChen-Yu Tsai #include <linux/of_device.h> 34*9765d2d9SChen-Yu Tsai #include <linux/platform_device.h> 35*9765d2d9SChen-Yu Tsai #include <linux/rtc.h> 36*9765d2d9SChen-Yu Tsai #include <linux/types.h> 37*9765d2d9SChen-Yu Tsai 38*9765d2d9SChen-Yu Tsai /* Control register */ 39*9765d2d9SChen-Yu Tsai #define SUN6I_LOSC_CTRL 0x0000 40*9765d2d9SChen-Yu Tsai #define SUN6I_LOSC_CTRL_ALM_DHMS_ACC BIT(9) 41*9765d2d9SChen-Yu Tsai #define SUN6I_LOSC_CTRL_RTC_HMS_ACC BIT(8) 42*9765d2d9SChen-Yu Tsai #define SUN6I_LOSC_CTRL_RTC_YMD_ACC BIT(7) 43*9765d2d9SChen-Yu Tsai #define SUN6I_LOSC_CTRL_ACC_MASK GENMASK(9, 7) 44*9765d2d9SChen-Yu Tsai 45*9765d2d9SChen-Yu Tsai /* RTC */ 46*9765d2d9SChen-Yu Tsai #define SUN6I_RTC_YMD 0x0010 47*9765d2d9SChen-Yu Tsai #define SUN6I_RTC_HMS 0x0014 48*9765d2d9SChen-Yu Tsai 49*9765d2d9SChen-Yu Tsai /* Alarm 0 (counter) */ 50*9765d2d9SChen-Yu Tsai #define SUN6I_ALRM_COUNTER 0x0020 51*9765d2d9SChen-Yu Tsai #define SUN6I_ALRM_CUR_VAL 0x0024 52*9765d2d9SChen-Yu Tsai #define SUN6I_ALRM_EN 0x0028 53*9765d2d9SChen-Yu Tsai #define SUN6I_ALRM_EN_CNT_EN BIT(0) 54*9765d2d9SChen-Yu Tsai #define SUN6I_ALRM_IRQ_EN 0x002c 55*9765d2d9SChen-Yu Tsai #define SUN6I_ALRM_IRQ_EN_CNT_IRQ_EN BIT(0) 56*9765d2d9SChen-Yu Tsai #define SUN6I_ALRM_IRQ_STA 0x0030 57*9765d2d9SChen-Yu Tsai #define SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND BIT(0) 58*9765d2d9SChen-Yu Tsai 59*9765d2d9SChen-Yu Tsai /* Alarm 1 (wall clock) */ 60*9765d2d9SChen-Yu Tsai #define SUN6I_ALRM1_EN 0x0044 61*9765d2d9SChen-Yu Tsai #define SUN6I_ALRM1_IRQ_EN 0x0048 62*9765d2d9SChen-Yu Tsai #define SUN6I_ALRM1_IRQ_STA 0x004c 63*9765d2d9SChen-Yu Tsai #define SUN6I_ALRM1_IRQ_STA_WEEK_IRQ_PEND BIT(0) 64*9765d2d9SChen-Yu Tsai 65*9765d2d9SChen-Yu Tsai /* Alarm config */ 66*9765d2d9SChen-Yu Tsai #define SUN6I_ALARM_CONFIG 0x0050 67*9765d2d9SChen-Yu Tsai #define SUN6I_ALARM_CONFIG_WAKEUP BIT(0) 68*9765d2d9SChen-Yu Tsai 69*9765d2d9SChen-Yu Tsai /* 70*9765d2d9SChen-Yu Tsai * Get date values 71*9765d2d9SChen-Yu Tsai */ 72*9765d2d9SChen-Yu Tsai #define SUN6I_DATE_GET_DAY_VALUE(x) ((x) & 0x0000001f) 73*9765d2d9SChen-Yu Tsai #define SUN6I_DATE_GET_MON_VALUE(x) (((x) & 0x00000f00) >> 8) 74*9765d2d9SChen-Yu Tsai #define SUN6I_DATE_GET_YEAR_VALUE(x) (((x) & 0x003f0000) >> 16) 75*9765d2d9SChen-Yu Tsai #define SUN6I_LEAP_GET_VALUE(x) (((x) & 0x00400000) >> 22) 76*9765d2d9SChen-Yu Tsai 77*9765d2d9SChen-Yu Tsai /* 78*9765d2d9SChen-Yu Tsai * Get time values 79*9765d2d9SChen-Yu Tsai */ 80*9765d2d9SChen-Yu Tsai #define SUN6I_TIME_GET_SEC_VALUE(x) ((x) & 0x0000003f) 81*9765d2d9SChen-Yu Tsai #define SUN6I_TIME_GET_MIN_VALUE(x) (((x) & 0x00003f00) >> 8) 82*9765d2d9SChen-Yu Tsai #define SUN6I_TIME_GET_HOUR_VALUE(x) (((x) & 0x001f0000) >> 16) 83*9765d2d9SChen-Yu Tsai 84*9765d2d9SChen-Yu Tsai /* 85*9765d2d9SChen-Yu Tsai * Set date values 86*9765d2d9SChen-Yu Tsai */ 87*9765d2d9SChen-Yu Tsai #define SUN6I_DATE_SET_DAY_VALUE(x) ((x) & 0x0000001f) 88*9765d2d9SChen-Yu Tsai #define SUN6I_DATE_SET_MON_VALUE(x) ((x) << 8 & 0x00000f00) 89*9765d2d9SChen-Yu Tsai #define SUN6I_DATE_SET_YEAR_VALUE(x) ((x) << 16 & 0x003f0000) 90*9765d2d9SChen-Yu Tsai #define SUN6I_LEAP_SET_VALUE(x) ((x) << 22 & 0x00400000) 91*9765d2d9SChen-Yu Tsai 92*9765d2d9SChen-Yu Tsai /* 93*9765d2d9SChen-Yu Tsai * Set time values 94*9765d2d9SChen-Yu Tsai */ 95*9765d2d9SChen-Yu Tsai #define SUN6I_TIME_SET_SEC_VALUE(x) ((x) & 0x0000003f) 96*9765d2d9SChen-Yu Tsai #define SUN6I_TIME_SET_MIN_VALUE(x) ((x) << 8 & 0x00003f00) 97*9765d2d9SChen-Yu Tsai #define SUN6I_TIME_SET_HOUR_VALUE(x) ((x) << 16 & 0x001f0000) 98*9765d2d9SChen-Yu Tsai 99*9765d2d9SChen-Yu Tsai /* 100*9765d2d9SChen-Yu Tsai * The year parameter passed to the driver is usually an offset relative to 101*9765d2d9SChen-Yu Tsai * the year 1900. This macro is used to convert this offset to another one 102*9765d2d9SChen-Yu Tsai * relative to the minimum year allowed by the hardware. 103*9765d2d9SChen-Yu Tsai * 104*9765d2d9SChen-Yu Tsai * The year range is 1970 - 2033. This range is selected to match Allwinner's 105*9765d2d9SChen-Yu Tsai * driver, even though it is somewhat limited. 106*9765d2d9SChen-Yu Tsai */ 107*9765d2d9SChen-Yu Tsai #define SUN6I_YEAR_MIN 1970 108*9765d2d9SChen-Yu Tsai #define SUN6I_YEAR_MAX 2033 109*9765d2d9SChen-Yu Tsai #define SUN6I_YEAR_OFF (SUN6I_YEAR_MIN - 1900) 110*9765d2d9SChen-Yu Tsai 111*9765d2d9SChen-Yu Tsai struct sun6i_rtc_dev { 112*9765d2d9SChen-Yu Tsai struct rtc_device *rtc; 113*9765d2d9SChen-Yu Tsai struct device *dev; 114*9765d2d9SChen-Yu Tsai void __iomem *base; 115*9765d2d9SChen-Yu Tsai int irq; 116*9765d2d9SChen-Yu Tsai unsigned long alarm; 117*9765d2d9SChen-Yu Tsai }; 118*9765d2d9SChen-Yu Tsai 119*9765d2d9SChen-Yu Tsai static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id) 120*9765d2d9SChen-Yu Tsai { 121*9765d2d9SChen-Yu Tsai struct sun6i_rtc_dev *chip = (struct sun6i_rtc_dev *) id; 122*9765d2d9SChen-Yu Tsai u32 val; 123*9765d2d9SChen-Yu Tsai 124*9765d2d9SChen-Yu Tsai val = readl(chip->base + SUN6I_ALRM_IRQ_STA); 125*9765d2d9SChen-Yu Tsai 126*9765d2d9SChen-Yu Tsai if (val & SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND) { 127*9765d2d9SChen-Yu Tsai val |= SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND; 128*9765d2d9SChen-Yu Tsai writel(val, chip->base + SUN6I_ALRM_IRQ_STA); 129*9765d2d9SChen-Yu Tsai 130*9765d2d9SChen-Yu Tsai rtc_update_irq(chip->rtc, 1, RTC_AF | RTC_IRQF); 131*9765d2d9SChen-Yu Tsai 132*9765d2d9SChen-Yu Tsai return IRQ_HANDLED; 133*9765d2d9SChen-Yu Tsai } 134*9765d2d9SChen-Yu Tsai 135*9765d2d9SChen-Yu Tsai return IRQ_NONE; 136*9765d2d9SChen-Yu Tsai } 137*9765d2d9SChen-Yu Tsai 138*9765d2d9SChen-Yu Tsai static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip) 139*9765d2d9SChen-Yu Tsai { 140*9765d2d9SChen-Yu Tsai u32 alrm_val = 0; 141*9765d2d9SChen-Yu Tsai u32 alrm_irq_val = 0; 142*9765d2d9SChen-Yu Tsai u32 alrm_wake_val = 0; 143*9765d2d9SChen-Yu Tsai 144*9765d2d9SChen-Yu Tsai if (to) { 145*9765d2d9SChen-Yu Tsai alrm_val = SUN6I_ALRM_EN_CNT_EN; 146*9765d2d9SChen-Yu Tsai alrm_irq_val = SUN6I_ALRM_IRQ_EN_CNT_IRQ_EN; 147*9765d2d9SChen-Yu Tsai alrm_wake_val = SUN6I_ALARM_CONFIG_WAKEUP; 148*9765d2d9SChen-Yu Tsai } else { 149*9765d2d9SChen-Yu Tsai writel(SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND, 150*9765d2d9SChen-Yu Tsai chip->base + SUN6I_ALRM_IRQ_STA); 151*9765d2d9SChen-Yu Tsai } 152*9765d2d9SChen-Yu Tsai 153*9765d2d9SChen-Yu Tsai writel(alrm_val, chip->base + SUN6I_ALRM_EN); 154*9765d2d9SChen-Yu Tsai writel(alrm_irq_val, chip->base + SUN6I_ALRM_IRQ_EN); 155*9765d2d9SChen-Yu Tsai writel(alrm_wake_val, chip->base + SUN6I_ALARM_CONFIG); 156*9765d2d9SChen-Yu Tsai } 157*9765d2d9SChen-Yu Tsai 158*9765d2d9SChen-Yu Tsai static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) 159*9765d2d9SChen-Yu Tsai { 160*9765d2d9SChen-Yu Tsai struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); 161*9765d2d9SChen-Yu Tsai u32 date, time; 162*9765d2d9SChen-Yu Tsai 163*9765d2d9SChen-Yu Tsai /* 164*9765d2d9SChen-Yu Tsai * read again in case it changes 165*9765d2d9SChen-Yu Tsai */ 166*9765d2d9SChen-Yu Tsai do { 167*9765d2d9SChen-Yu Tsai date = readl(chip->base + SUN6I_RTC_YMD); 168*9765d2d9SChen-Yu Tsai time = readl(chip->base + SUN6I_RTC_HMS); 169*9765d2d9SChen-Yu Tsai } while ((date != readl(chip->base + SUN6I_RTC_YMD)) || 170*9765d2d9SChen-Yu Tsai (time != readl(chip->base + SUN6I_RTC_HMS))); 171*9765d2d9SChen-Yu Tsai 172*9765d2d9SChen-Yu Tsai rtc_tm->tm_sec = SUN6I_TIME_GET_SEC_VALUE(time); 173*9765d2d9SChen-Yu Tsai rtc_tm->tm_min = SUN6I_TIME_GET_MIN_VALUE(time); 174*9765d2d9SChen-Yu Tsai rtc_tm->tm_hour = SUN6I_TIME_GET_HOUR_VALUE(time); 175*9765d2d9SChen-Yu Tsai 176*9765d2d9SChen-Yu Tsai rtc_tm->tm_mday = SUN6I_DATE_GET_DAY_VALUE(date); 177*9765d2d9SChen-Yu Tsai rtc_tm->tm_mon = SUN6I_DATE_GET_MON_VALUE(date); 178*9765d2d9SChen-Yu Tsai rtc_tm->tm_year = SUN6I_DATE_GET_YEAR_VALUE(date); 179*9765d2d9SChen-Yu Tsai 180*9765d2d9SChen-Yu Tsai rtc_tm->tm_mon -= 1; 181*9765d2d9SChen-Yu Tsai 182*9765d2d9SChen-Yu Tsai /* 183*9765d2d9SChen-Yu Tsai * switch from (data_year->min)-relative offset to 184*9765d2d9SChen-Yu Tsai * a (1900)-relative one 185*9765d2d9SChen-Yu Tsai */ 186*9765d2d9SChen-Yu Tsai rtc_tm->tm_year += SUN6I_YEAR_OFF; 187*9765d2d9SChen-Yu Tsai 188*9765d2d9SChen-Yu Tsai return rtc_valid_tm(rtc_tm); 189*9765d2d9SChen-Yu Tsai } 190*9765d2d9SChen-Yu Tsai 191*9765d2d9SChen-Yu Tsai static int sun6i_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm) 192*9765d2d9SChen-Yu Tsai { 193*9765d2d9SChen-Yu Tsai struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); 194*9765d2d9SChen-Yu Tsai u32 alrm_st; 195*9765d2d9SChen-Yu Tsai u32 alrm_en; 196*9765d2d9SChen-Yu Tsai 197*9765d2d9SChen-Yu Tsai alrm_en = readl(chip->base + SUN6I_ALRM_IRQ_EN); 198*9765d2d9SChen-Yu Tsai alrm_st = readl(chip->base + SUN6I_ALRM_IRQ_STA); 199*9765d2d9SChen-Yu Tsai wkalrm->enabled = !!(alrm_en & SUN6I_ALRM_EN_CNT_EN); 200*9765d2d9SChen-Yu Tsai wkalrm->pending = !!(alrm_st & SUN6I_ALRM_EN_CNT_EN); 201*9765d2d9SChen-Yu Tsai rtc_time_to_tm(chip->alarm, &wkalrm->time); 202*9765d2d9SChen-Yu Tsai 203*9765d2d9SChen-Yu Tsai return 0; 204*9765d2d9SChen-Yu Tsai } 205*9765d2d9SChen-Yu Tsai 206*9765d2d9SChen-Yu Tsai static int sun6i_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm) 207*9765d2d9SChen-Yu Tsai { 208*9765d2d9SChen-Yu Tsai struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); 209*9765d2d9SChen-Yu Tsai struct rtc_time *alrm_tm = &wkalrm->time; 210*9765d2d9SChen-Yu Tsai struct rtc_time tm_now; 211*9765d2d9SChen-Yu Tsai unsigned long time_now = 0; 212*9765d2d9SChen-Yu Tsai unsigned long time_set = 0; 213*9765d2d9SChen-Yu Tsai unsigned long time_gap = 0; 214*9765d2d9SChen-Yu Tsai int ret = 0; 215*9765d2d9SChen-Yu Tsai 216*9765d2d9SChen-Yu Tsai ret = sun6i_rtc_gettime(dev, &tm_now); 217*9765d2d9SChen-Yu Tsai if (ret < 0) { 218*9765d2d9SChen-Yu Tsai dev_err(dev, "Error in getting time\n"); 219*9765d2d9SChen-Yu Tsai return -EINVAL; 220*9765d2d9SChen-Yu Tsai } 221*9765d2d9SChen-Yu Tsai 222*9765d2d9SChen-Yu Tsai rtc_tm_to_time(alrm_tm, &time_set); 223*9765d2d9SChen-Yu Tsai rtc_tm_to_time(&tm_now, &time_now); 224*9765d2d9SChen-Yu Tsai if (time_set <= time_now) { 225*9765d2d9SChen-Yu Tsai dev_err(dev, "Date to set in the past\n"); 226*9765d2d9SChen-Yu Tsai return -EINVAL; 227*9765d2d9SChen-Yu Tsai } 228*9765d2d9SChen-Yu Tsai 229*9765d2d9SChen-Yu Tsai time_gap = time_set - time_now; 230*9765d2d9SChen-Yu Tsai 231*9765d2d9SChen-Yu Tsai if (time_gap > U32_MAX) { 232*9765d2d9SChen-Yu Tsai dev_err(dev, "Date too far in the future\n"); 233*9765d2d9SChen-Yu Tsai return -EINVAL; 234*9765d2d9SChen-Yu Tsai } 235*9765d2d9SChen-Yu Tsai 236*9765d2d9SChen-Yu Tsai sun6i_rtc_setaie(0, chip); 237*9765d2d9SChen-Yu Tsai writel(0, chip->base + SUN6I_ALRM_COUNTER); 238*9765d2d9SChen-Yu Tsai usleep_range(100, 300); 239*9765d2d9SChen-Yu Tsai 240*9765d2d9SChen-Yu Tsai writel(time_gap, chip->base + SUN6I_ALRM_COUNTER); 241*9765d2d9SChen-Yu Tsai chip->alarm = time_set; 242*9765d2d9SChen-Yu Tsai 243*9765d2d9SChen-Yu Tsai sun6i_rtc_setaie(wkalrm->enabled, chip); 244*9765d2d9SChen-Yu Tsai 245*9765d2d9SChen-Yu Tsai return 0; 246*9765d2d9SChen-Yu Tsai } 247*9765d2d9SChen-Yu Tsai 248*9765d2d9SChen-Yu Tsai static int sun6i_rtc_wait(struct sun6i_rtc_dev *chip, int offset, 249*9765d2d9SChen-Yu Tsai unsigned int mask, unsigned int ms_timeout) 250*9765d2d9SChen-Yu Tsai { 251*9765d2d9SChen-Yu Tsai const unsigned long timeout = jiffies + msecs_to_jiffies(ms_timeout); 252*9765d2d9SChen-Yu Tsai u32 reg; 253*9765d2d9SChen-Yu Tsai 254*9765d2d9SChen-Yu Tsai do { 255*9765d2d9SChen-Yu Tsai reg = readl(chip->base + offset); 256*9765d2d9SChen-Yu Tsai reg &= mask; 257*9765d2d9SChen-Yu Tsai 258*9765d2d9SChen-Yu Tsai if (!reg) 259*9765d2d9SChen-Yu Tsai return 0; 260*9765d2d9SChen-Yu Tsai 261*9765d2d9SChen-Yu Tsai } while (time_before(jiffies, timeout)); 262*9765d2d9SChen-Yu Tsai 263*9765d2d9SChen-Yu Tsai return -ETIMEDOUT; 264*9765d2d9SChen-Yu Tsai } 265*9765d2d9SChen-Yu Tsai 266*9765d2d9SChen-Yu Tsai static int sun6i_rtc_settime(struct device *dev, struct rtc_time *rtc_tm) 267*9765d2d9SChen-Yu Tsai { 268*9765d2d9SChen-Yu Tsai struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); 269*9765d2d9SChen-Yu Tsai u32 date = 0; 270*9765d2d9SChen-Yu Tsai u32 time = 0; 271*9765d2d9SChen-Yu Tsai int year; 272*9765d2d9SChen-Yu Tsai 273*9765d2d9SChen-Yu Tsai year = rtc_tm->tm_year + 1900; 274*9765d2d9SChen-Yu Tsai if (year < SUN6I_YEAR_MIN || year > SUN6I_YEAR_MAX) { 275*9765d2d9SChen-Yu Tsai dev_err(dev, "rtc only supports year in range %d - %d\n", 276*9765d2d9SChen-Yu Tsai SUN6I_YEAR_MIN, SUN6I_YEAR_MAX); 277*9765d2d9SChen-Yu Tsai return -EINVAL; 278*9765d2d9SChen-Yu Tsai } 279*9765d2d9SChen-Yu Tsai 280*9765d2d9SChen-Yu Tsai rtc_tm->tm_year -= SUN6I_YEAR_OFF; 281*9765d2d9SChen-Yu Tsai rtc_tm->tm_mon += 1; 282*9765d2d9SChen-Yu Tsai 283*9765d2d9SChen-Yu Tsai date = SUN6I_DATE_SET_DAY_VALUE(rtc_tm->tm_mday) | 284*9765d2d9SChen-Yu Tsai SUN6I_DATE_SET_MON_VALUE(rtc_tm->tm_mon) | 285*9765d2d9SChen-Yu Tsai SUN6I_DATE_SET_YEAR_VALUE(rtc_tm->tm_year); 286*9765d2d9SChen-Yu Tsai 287*9765d2d9SChen-Yu Tsai if (is_leap_year(year)) 288*9765d2d9SChen-Yu Tsai date |= SUN6I_LEAP_SET_VALUE(1); 289*9765d2d9SChen-Yu Tsai 290*9765d2d9SChen-Yu Tsai time = SUN6I_TIME_SET_SEC_VALUE(rtc_tm->tm_sec) | 291*9765d2d9SChen-Yu Tsai SUN6I_TIME_SET_MIN_VALUE(rtc_tm->tm_min) | 292*9765d2d9SChen-Yu Tsai SUN6I_TIME_SET_HOUR_VALUE(rtc_tm->tm_hour); 293*9765d2d9SChen-Yu Tsai 294*9765d2d9SChen-Yu Tsai /* Check whether registers are writable */ 295*9765d2d9SChen-Yu Tsai if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL, 296*9765d2d9SChen-Yu Tsai SUN6I_LOSC_CTRL_ACC_MASK, 50)) { 297*9765d2d9SChen-Yu Tsai dev_err(dev, "rtc is still busy.\n"); 298*9765d2d9SChen-Yu Tsai return -EBUSY; 299*9765d2d9SChen-Yu Tsai } 300*9765d2d9SChen-Yu Tsai 301*9765d2d9SChen-Yu Tsai writel(time, chip->base + SUN6I_RTC_HMS); 302*9765d2d9SChen-Yu Tsai 303*9765d2d9SChen-Yu Tsai /* 304*9765d2d9SChen-Yu Tsai * After writing the RTC HH-MM-SS register, the 305*9765d2d9SChen-Yu Tsai * SUN6I_LOSC_CTRL_RTC_HMS_ACC bit is set and it will not 306*9765d2d9SChen-Yu Tsai * be cleared until the real writing operation is finished 307*9765d2d9SChen-Yu Tsai */ 308*9765d2d9SChen-Yu Tsai 309*9765d2d9SChen-Yu Tsai if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL, 310*9765d2d9SChen-Yu Tsai SUN6I_LOSC_CTRL_RTC_HMS_ACC, 50)) { 311*9765d2d9SChen-Yu Tsai dev_err(dev, "Failed to set rtc time.\n"); 312*9765d2d9SChen-Yu Tsai return -ETIMEDOUT; 313*9765d2d9SChen-Yu Tsai } 314*9765d2d9SChen-Yu Tsai 315*9765d2d9SChen-Yu Tsai writel(date, chip->base + SUN6I_RTC_YMD); 316*9765d2d9SChen-Yu Tsai 317*9765d2d9SChen-Yu Tsai /* 318*9765d2d9SChen-Yu Tsai * After writing the RTC YY-MM-DD register, the 319*9765d2d9SChen-Yu Tsai * SUN6I_LOSC_CTRL_RTC_YMD_ACC bit is set and it will not 320*9765d2d9SChen-Yu Tsai * be cleared until the real writing operation is finished 321*9765d2d9SChen-Yu Tsai */ 322*9765d2d9SChen-Yu Tsai 323*9765d2d9SChen-Yu Tsai if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL, 324*9765d2d9SChen-Yu Tsai SUN6I_LOSC_CTRL_RTC_YMD_ACC, 50)) { 325*9765d2d9SChen-Yu Tsai dev_err(dev, "Failed to set rtc time.\n"); 326*9765d2d9SChen-Yu Tsai return -ETIMEDOUT; 327*9765d2d9SChen-Yu Tsai } 328*9765d2d9SChen-Yu Tsai 329*9765d2d9SChen-Yu Tsai return 0; 330*9765d2d9SChen-Yu Tsai } 331*9765d2d9SChen-Yu Tsai 332*9765d2d9SChen-Yu Tsai static int sun6i_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) 333*9765d2d9SChen-Yu Tsai { 334*9765d2d9SChen-Yu Tsai struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); 335*9765d2d9SChen-Yu Tsai 336*9765d2d9SChen-Yu Tsai if (!enabled) 337*9765d2d9SChen-Yu Tsai sun6i_rtc_setaie(enabled, chip); 338*9765d2d9SChen-Yu Tsai 339*9765d2d9SChen-Yu Tsai return 0; 340*9765d2d9SChen-Yu Tsai } 341*9765d2d9SChen-Yu Tsai 342*9765d2d9SChen-Yu Tsai static const struct rtc_class_ops sun6i_rtc_ops = { 343*9765d2d9SChen-Yu Tsai .read_time = sun6i_rtc_gettime, 344*9765d2d9SChen-Yu Tsai .set_time = sun6i_rtc_settime, 345*9765d2d9SChen-Yu Tsai .read_alarm = sun6i_rtc_getalarm, 346*9765d2d9SChen-Yu Tsai .set_alarm = sun6i_rtc_setalarm, 347*9765d2d9SChen-Yu Tsai .alarm_irq_enable = sun6i_rtc_alarm_irq_enable 348*9765d2d9SChen-Yu Tsai }; 349*9765d2d9SChen-Yu Tsai 350*9765d2d9SChen-Yu Tsai static int sun6i_rtc_probe(struct platform_device *pdev) 351*9765d2d9SChen-Yu Tsai { 352*9765d2d9SChen-Yu Tsai struct sun6i_rtc_dev *chip; 353*9765d2d9SChen-Yu Tsai struct resource *res; 354*9765d2d9SChen-Yu Tsai int ret; 355*9765d2d9SChen-Yu Tsai 356*9765d2d9SChen-Yu Tsai chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); 357*9765d2d9SChen-Yu Tsai if (!chip) 358*9765d2d9SChen-Yu Tsai return -ENOMEM; 359*9765d2d9SChen-Yu Tsai 360*9765d2d9SChen-Yu Tsai platform_set_drvdata(pdev, chip); 361*9765d2d9SChen-Yu Tsai chip->dev = &pdev->dev; 362*9765d2d9SChen-Yu Tsai 363*9765d2d9SChen-Yu Tsai res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 364*9765d2d9SChen-Yu Tsai chip->base = devm_ioremap_resource(&pdev->dev, res); 365*9765d2d9SChen-Yu Tsai if (IS_ERR(chip->base)) 366*9765d2d9SChen-Yu Tsai return PTR_ERR(chip->base); 367*9765d2d9SChen-Yu Tsai 368*9765d2d9SChen-Yu Tsai chip->irq = platform_get_irq(pdev, 0); 369*9765d2d9SChen-Yu Tsai if (chip->irq < 0) { 370*9765d2d9SChen-Yu Tsai dev_err(&pdev->dev, "No IRQ resource\n"); 371*9765d2d9SChen-Yu Tsai return chip->irq; 372*9765d2d9SChen-Yu Tsai } 373*9765d2d9SChen-Yu Tsai 374*9765d2d9SChen-Yu Tsai ret = devm_request_irq(&pdev->dev, chip->irq, sun6i_rtc_alarmirq, 375*9765d2d9SChen-Yu Tsai 0, dev_name(&pdev->dev), chip); 376*9765d2d9SChen-Yu Tsai if (ret) { 377*9765d2d9SChen-Yu Tsai dev_err(&pdev->dev, "Could not request IRQ\n"); 378*9765d2d9SChen-Yu Tsai return ret; 379*9765d2d9SChen-Yu Tsai } 380*9765d2d9SChen-Yu Tsai 381*9765d2d9SChen-Yu Tsai /* clear the alarm counter value */ 382*9765d2d9SChen-Yu Tsai writel(0, chip->base + SUN6I_ALRM_COUNTER); 383*9765d2d9SChen-Yu Tsai 384*9765d2d9SChen-Yu Tsai /* disable counter alarm */ 385*9765d2d9SChen-Yu Tsai writel(0, chip->base + SUN6I_ALRM_EN); 386*9765d2d9SChen-Yu Tsai 387*9765d2d9SChen-Yu Tsai /* disable counter alarm interrupt */ 388*9765d2d9SChen-Yu Tsai writel(0, chip->base + SUN6I_ALRM_IRQ_EN); 389*9765d2d9SChen-Yu Tsai 390*9765d2d9SChen-Yu Tsai /* disable week alarm */ 391*9765d2d9SChen-Yu Tsai writel(0, chip->base + SUN6I_ALRM1_EN); 392*9765d2d9SChen-Yu Tsai 393*9765d2d9SChen-Yu Tsai /* disable week alarm interrupt */ 394*9765d2d9SChen-Yu Tsai writel(0, chip->base + SUN6I_ALRM1_IRQ_EN); 395*9765d2d9SChen-Yu Tsai 396*9765d2d9SChen-Yu Tsai /* clear counter alarm pending interrupts */ 397*9765d2d9SChen-Yu Tsai writel(SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND, 398*9765d2d9SChen-Yu Tsai chip->base + SUN6I_ALRM_IRQ_STA); 399*9765d2d9SChen-Yu Tsai 400*9765d2d9SChen-Yu Tsai /* clear week alarm pending interrupts */ 401*9765d2d9SChen-Yu Tsai writel(SUN6I_ALRM1_IRQ_STA_WEEK_IRQ_PEND, 402*9765d2d9SChen-Yu Tsai chip->base + SUN6I_ALRM1_IRQ_STA); 403*9765d2d9SChen-Yu Tsai 404*9765d2d9SChen-Yu Tsai /* disable alarm wakeup */ 405*9765d2d9SChen-Yu Tsai writel(0, chip->base + SUN6I_ALARM_CONFIG); 406*9765d2d9SChen-Yu Tsai 407*9765d2d9SChen-Yu Tsai chip->rtc = rtc_device_register("rtc-sun6i", &pdev->dev, 408*9765d2d9SChen-Yu Tsai &sun6i_rtc_ops, THIS_MODULE); 409*9765d2d9SChen-Yu Tsai if (IS_ERR(chip->rtc)) { 410*9765d2d9SChen-Yu Tsai dev_err(&pdev->dev, "unable to register device\n"); 411*9765d2d9SChen-Yu Tsai return PTR_ERR(chip->rtc); 412*9765d2d9SChen-Yu Tsai } 413*9765d2d9SChen-Yu Tsai 414*9765d2d9SChen-Yu Tsai dev_info(&pdev->dev, "RTC enabled\n"); 415*9765d2d9SChen-Yu Tsai 416*9765d2d9SChen-Yu Tsai return 0; 417*9765d2d9SChen-Yu Tsai } 418*9765d2d9SChen-Yu Tsai 419*9765d2d9SChen-Yu Tsai static int sun6i_rtc_remove(struct platform_device *pdev) 420*9765d2d9SChen-Yu Tsai { 421*9765d2d9SChen-Yu Tsai struct sun6i_rtc_dev *chip = platform_get_drvdata(pdev); 422*9765d2d9SChen-Yu Tsai 423*9765d2d9SChen-Yu Tsai rtc_device_unregister(chip->rtc); 424*9765d2d9SChen-Yu Tsai 425*9765d2d9SChen-Yu Tsai return 0; 426*9765d2d9SChen-Yu Tsai } 427*9765d2d9SChen-Yu Tsai 428*9765d2d9SChen-Yu Tsai static const struct of_device_id sun6i_rtc_dt_ids[] = { 429*9765d2d9SChen-Yu Tsai { .compatible = "allwinner,sun6i-a31-rtc" }, 430*9765d2d9SChen-Yu Tsai { /* sentinel */ }, 431*9765d2d9SChen-Yu Tsai }; 432*9765d2d9SChen-Yu Tsai MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids); 433*9765d2d9SChen-Yu Tsai 434*9765d2d9SChen-Yu Tsai static struct platform_driver sun6i_rtc_driver = { 435*9765d2d9SChen-Yu Tsai .probe = sun6i_rtc_probe, 436*9765d2d9SChen-Yu Tsai .remove = sun6i_rtc_remove, 437*9765d2d9SChen-Yu Tsai .driver = { 438*9765d2d9SChen-Yu Tsai .name = "sun6i-rtc", 439*9765d2d9SChen-Yu Tsai .of_match_table = sun6i_rtc_dt_ids, 440*9765d2d9SChen-Yu Tsai }, 441*9765d2d9SChen-Yu Tsai }; 442*9765d2d9SChen-Yu Tsai 443*9765d2d9SChen-Yu Tsai module_platform_driver(sun6i_rtc_driver); 444*9765d2d9SChen-Yu Tsai 445*9765d2d9SChen-Yu Tsai MODULE_DESCRIPTION("sun6i RTC driver"); 446*9765d2d9SChen-Yu Tsai MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>"); 447*9765d2d9SChen-Yu Tsai MODULE_LICENSE("GPL"); 448