Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

rtc: sun6i: Add sun6i RTC driver

This patch introduces the driver for the RTC in the Allwinner A31 and
A23 SoCs.

Unlike the RTC found in A10/A20 SoCs, which was part of the timer, the
RTC in A31/A23 are a separate hardware block, which also contain a few
controls for the RTC block hardware (a regulator and RTC block GPIO pin
latches), while also having separate interrupts for the alarms.

The hardware is different enough to make a different driver for it.

Signed-off-by: Chen-Yu Tsai <wens@csie.org>
Reviewed-by: Varka Bhadram <varkabhadram@gmail.com>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>

authored by

Chen-Yu Tsai and committed by
Maxime Ripard
9765d2d9 7d1311b9

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