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

rtc: add rtc-lpc24xx driver

Add driver for the RTC found on NXP LPC178x/18xx/408x/43xx devices.
The RTC provides calendar and clock functionality together with
alarm interrupt support.

Signed-off-by: Joachim Eastwood <manabian@gmail.com>
Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>

authored by

Joachim Eastwood and committed by
Alexandre Belloni
c28b42e3 dcb9372b

+323
+12
drivers/rtc/Kconfig
··· 1456 1456 This driver can also be buillt as a module. If so, the module 1457 1457 will be called rtc-jz4740. 1458 1458 1459 + config RTC_DRV_LPC24XX 1460 + tristate "NXP RTC for LPC178x/18xx/408x/43xx" 1461 + depends on ARCH_LPC18XX || COMPILE_TEST 1462 + depends on OF && HAS_IOMEM 1463 + help 1464 + This enables support for the NXP RTC found which can be found on 1465 + NXP LPC178x/18xx/408x/43xx devices. 1466 + 1467 + If you have one of the devices above enable this driver to use 1468 + the hardware RTC. This driver can also be buillt as a module. If 1469 + so, the module will be called rtc-lpc24xx. 1470 + 1459 1471 config RTC_DRV_LPC32XX 1460 1472 depends on ARCH_LPC32XX 1461 1473 tristate "NXP LPC32XX RTC"
+1
drivers/rtc/Makefile
··· 74 74 obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o 75 75 obj-$(CONFIG_RTC_DRV_JZ4740) += rtc-jz4740.o 76 76 obj-$(CONFIG_RTC_DRV_LP8788) += rtc-lp8788.o 77 + obj-$(CONFIG_RTC_DRV_LPC24XX) += rtc-lpc24xx.o 77 78 obj-$(CONFIG_RTC_DRV_LPC32XX) += rtc-lpc32xx.o 78 79 obj-$(CONFIG_RTC_DRV_LOONGSON1) += rtc-ls1x.o 79 80 obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o
+310
drivers/rtc/rtc-lpc24xx.c
··· 1 + /* 2 + * RTC driver for NXP LPC178x/18xx/43xx Real-Time Clock (RTC) 3 + * 4 + * Copyright (C) 2011 NXP Semiconductors 5 + * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com> 6 + * 7 + * This program is free software; you can redistribute it and/or modify 8 + * it under the terms of the GNU General Public License as published by 9 + * the Free Software Foundation; either version 2 of the License, or 10 + * (at your option) any later version. 11 + * 12 + */ 13 + 14 + #include <linux/clk.h> 15 + #include <linux/io.h> 16 + #include <linux/kernel.h> 17 + #include <linux/module.h> 18 + #include <linux/of.h> 19 + #include <linux/of_device.h> 20 + #include <linux/platform_device.h> 21 + #include <linux/rtc.h> 22 + 23 + /* LPC24xx RTC register offsets and bits */ 24 + #define LPC24XX_ILR 0x00 25 + #define LPC24XX_RTCCIF BIT(0) 26 + #define LPC24XX_RTCALF BIT(1) 27 + #define LPC24XX_CTC 0x04 28 + #define LPC24XX_CCR 0x08 29 + #define LPC24XX_CLKEN BIT(0) 30 + #define LPC178X_CCALEN BIT(4) 31 + #define LPC24XX_CIIR 0x0c 32 + #define LPC24XX_AMR 0x10 33 + #define LPC24XX_ALARM_DISABLE 0xff 34 + #define LPC24XX_CTIME0 0x14 35 + #define LPC24XX_CTIME1 0x18 36 + #define LPC24XX_CTIME2 0x1c 37 + #define LPC24XX_SEC 0x20 38 + #define LPC24XX_MIN 0x24 39 + #define LPC24XX_HOUR 0x28 40 + #define LPC24XX_DOM 0x2c 41 + #define LPC24XX_DOW 0x30 42 + #define LPC24XX_DOY 0x34 43 + #define LPC24XX_MONTH 0x38 44 + #define LPC24XX_YEAR 0x3c 45 + #define LPC24XX_ALSEC 0x60 46 + #define LPC24XX_ALMIN 0x64 47 + #define LPC24XX_ALHOUR 0x68 48 + #define LPC24XX_ALDOM 0x6c 49 + #define LPC24XX_ALDOW 0x70 50 + #define LPC24XX_ALDOY 0x74 51 + #define LPC24XX_ALMON 0x78 52 + #define LPC24XX_ALYEAR 0x7c 53 + 54 + /* Macros to read fields in consolidated time (CT) registers */ 55 + #define CT0_SECS(x) (((x) >> 0) & 0x3f) 56 + #define CT0_MINS(x) (((x) >> 8) & 0x3f) 57 + #define CT0_HOURS(x) (((x) >> 16) & 0x1f) 58 + #define CT0_DOW(x) (((x) >> 24) & 0x07) 59 + #define CT1_DOM(x) (((x) >> 0) & 0x1f) 60 + #define CT1_MONTH(x) (((x) >> 8) & 0x0f) 61 + #define CT1_YEAR(x) (((x) >> 16) & 0xfff) 62 + #define CT2_DOY(x) (((x) >> 0) & 0xfff) 63 + 64 + #define rtc_readl(dev, reg) readl((dev)->rtc_base + (reg)) 65 + #define rtc_writel(dev, reg, val) writel((val), (dev)->rtc_base + (reg)) 66 + 67 + struct lpc24xx_rtc { 68 + void __iomem *rtc_base; 69 + struct rtc_device *rtc; 70 + struct clk *clk_rtc; 71 + struct clk *clk_reg; 72 + }; 73 + 74 + static int lpc24xx_rtc_set_time(struct device *dev, struct rtc_time *tm) 75 + { 76 + struct lpc24xx_rtc *rtc = dev_get_drvdata(dev); 77 + 78 + /* Disable RTC during update */ 79 + rtc_writel(rtc, LPC24XX_CCR, LPC178X_CCALEN); 80 + 81 + rtc_writel(rtc, LPC24XX_SEC, tm->tm_sec); 82 + rtc_writel(rtc, LPC24XX_MIN, tm->tm_min); 83 + rtc_writel(rtc, LPC24XX_HOUR, tm->tm_hour); 84 + rtc_writel(rtc, LPC24XX_DOW, tm->tm_wday); 85 + rtc_writel(rtc, LPC24XX_DOM, tm->tm_mday); 86 + rtc_writel(rtc, LPC24XX_DOY, tm->tm_yday); 87 + rtc_writel(rtc, LPC24XX_MONTH, tm->tm_mon); 88 + rtc_writel(rtc, LPC24XX_YEAR, tm->tm_year); 89 + 90 + rtc_writel(rtc, LPC24XX_CCR, LPC24XX_CLKEN | LPC178X_CCALEN); 91 + 92 + return 0; 93 + } 94 + 95 + static int lpc24xx_rtc_read_time(struct device *dev, struct rtc_time *tm) 96 + { 97 + struct lpc24xx_rtc *rtc = dev_get_drvdata(dev); 98 + u32 ct0, ct1, ct2; 99 + 100 + ct0 = rtc_readl(rtc, LPC24XX_CTIME0); 101 + ct1 = rtc_readl(rtc, LPC24XX_CTIME1); 102 + ct2 = rtc_readl(rtc, LPC24XX_CTIME2); 103 + 104 + tm->tm_sec = CT0_SECS(ct0); 105 + tm->tm_min = CT0_MINS(ct0); 106 + tm->tm_hour = CT0_HOURS(ct0); 107 + tm->tm_wday = CT0_DOW(ct0); 108 + tm->tm_mon = CT1_MONTH(ct1); 109 + tm->tm_mday = CT1_DOM(ct1); 110 + tm->tm_year = CT1_YEAR(ct1); 111 + tm->tm_yday = CT2_DOY(ct2); 112 + 113 + return rtc_valid_tm(tm); 114 + } 115 + 116 + static int lpc24xx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) 117 + { 118 + struct lpc24xx_rtc *rtc = dev_get_drvdata(dev); 119 + struct rtc_time *tm = &wkalrm->time; 120 + 121 + tm->tm_sec = rtc_readl(rtc, LPC24XX_ALSEC); 122 + tm->tm_min = rtc_readl(rtc, LPC24XX_ALMIN); 123 + tm->tm_hour = rtc_readl(rtc, LPC24XX_ALHOUR); 124 + tm->tm_mday = rtc_readl(rtc, LPC24XX_ALDOM); 125 + tm->tm_wday = rtc_readl(rtc, LPC24XX_ALDOW); 126 + tm->tm_yday = rtc_readl(rtc, LPC24XX_ALDOY); 127 + tm->tm_mon = rtc_readl(rtc, LPC24XX_ALMON); 128 + tm->tm_year = rtc_readl(rtc, LPC24XX_ALYEAR); 129 + 130 + wkalrm->enabled = rtc_readl(rtc, LPC24XX_AMR) == 0; 131 + wkalrm->pending = !!(rtc_readl(rtc, LPC24XX_ILR) & LPC24XX_RTCCIF); 132 + 133 + return rtc_valid_tm(&wkalrm->time); 134 + } 135 + 136 + static int lpc24xx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) 137 + { 138 + struct lpc24xx_rtc *rtc = dev_get_drvdata(dev); 139 + struct rtc_time *tm = &wkalrm->time; 140 + 141 + /* Disable alarm irq during update */ 142 + rtc_writel(rtc, LPC24XX_AMR, LPC24XX_ALARM_DISABLE); 143 + 144 + rtc_writel(rtc, LPC24XX_ALSEC, tm->tm_sec); 145 + rtc_writel(rtc, LPC24XX_ALMIN, tm->tm_min); 146 + rtc_writel(rtc, LPC24XX_ALHOUR, tm->tm_hour); 147 + rtc_writel(rtc, LPC24XX_ALDOM, tm->tm_mday); 148 + rtc_writel(rtc, LPC24XX_ALDOW, tm->tm_wday); 149 + rtc_writel(rtc, LPC24XX_ALDOY, tm->tm_yday); 150 + rtc_writel(rtc, LPC24XX_ALMON, tm->tm_mon); 151 + rtc_writel(rtc, LPC24XX_ALYEAR, tm->tm_year); 152 + 153 + if (wkalrm->enabled) 154 + rtc_writel(rtc, LPC24XX_AMR, 0); 155 + 156 + return 0; 157 + } 158 + 159 + static int lpc24xx_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) 160 + { 161 + struct lpc24xx_rtc *rtc = dev_get_drvdata(dev); 162 + 163 + if (enable) 164 + rtc_writel(rtc, LPC24XX_AMR, 0); 165 + else 166 + rtc_writel(rtc, LPC24XX_AMR, LPC24XX_ALARM_DISABLE); 167 + 168 + return 0; 169 + } 170 + 171 + static irqreturn_t lpc24xx_rtc_interrupt(int irq, void *data) 172 + { 173 + unsigned long events = RTC_IRQF; 174 + struct lpc24xx_rtc *rtc = data; 175 + u32 rtc_iir; 176 + 177 + /* Check interrupt cause */ 178 + rtc_iir = rtc_readl(rtc, LPC24XX_ILR); 179 + if (rtc_iir & LPC24XX_RTCALF) { 180 + events |= RTC_AF; 181 + rtc_writel(rtc, LPC24XX_AMR, LPC24XX_ALARM_DISABLE); 182 + } 183 + 184 + /* Clear interrupt status and report event */ 185 + rtc_writel(rtc, LPC24XX_ILR, rtc_iir); 186 + rtc_update_irq(rtc->rtc, 1, events); 187 + 188 + return IRQ_HANDLED; 189 + } 190 + 191 + static const struct rtc_class_ops lpc24xx_rtc_ops = { 192 + .read_time = lpc24xx_rtc_read_time, 193 + .set_time = lpc24xx_rtc_set_time, 194 + .read_alarm = lpc24xx_rtc_read_alarm, 195 + .set_alarm = lpc24xx_rtc_set_alarm, 196 + .alarm_irq_enable = lpc24xx_rtc_alarm_irq_enable, 197 + }; 198 + 199 + static int lpc24xx_rtc_probe(struct platform_device *pdev) 200 + { 201 + struct lpc24xx_rtc *rtc; 202 + struct resource *res; 203 + int irq, ret; 204 + 205 + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); 206 + if (!rtc) 207 + return -ENOMEM; 208 + 209 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 210 + rtc->rtc_base = devm_ioremap_resource(&pdev->dev, res); 211 + if (IS_ERR(rtc->rtc_base)) 212 + return PTR_ERR(rtc->rtc_base); 213 + 214 + irq = platform_get_irq(pdev, 0); 215 + if (irq < 0) { 216 + dev_warn(&pdev->dev, "can't get interrupt resource\n"); 217 + return irq; 218 + } 219 + 220 + rtc->clk_rtc = devm_clk_get(&pdev->dev, "rtc"); 221 + if (IS_ERR(rtc->clk_rtc)) { 222 + dev_err(&pdev->dev, "error getting rtc clock\n"); 223 + return PTR_ERR(rtc->clk_rtc); 224 + } 225 + 226 + rtc->clk_reg = devm_clk_get(&pdev->dev, "reg"); 227 + if (IS_ERR(rtc->clk_reg)) { 228 + dev_err(&pdev->dev, "error getting reg clock\n"); 229 + return PTR_ERR(rtc->clk_reg); 230 + } 231 + 232 + ret = clk_prepare_enable(rtc->clk_rtc); 233 + if (ret) { 234 + dev_err(&pdev->dev, "unable to enable rtc clock\n"); 235 + return ret; 236 + } 237 + 238 + ret = clk_prepare_enable(rtc->clk_reg); 239 + if (ret) { 240 + dev_err(&pdev->dev, "unable to enable reg clock\n"); 241 + goto disable_rtc_clk; 242 + } 243 + 244 + platform_set_drvdata(pdev, rtc); 245 + 246 + /* Clear any pending interrupts */ 247 + rtc_writel(rtc, LPC24XX_ILR, LPC24XX_RTCCIF | LPC24XX_RTCALF); 248 + 249 + /* Enable RTC count */ 250 + rtc_writel(rtc, LPC24XX_CCR, LPC24XX_CLKEN | LPC178X_CCALEN); 251 + 252 + ret = devm_request_irq(&pdev->dev, irq, lpc24xx_rtc_interrupt, 0, 253 + pdev->name, rtc); 254 + if (ret < 0) { 255 + dev_warn(&pdev->dev, "can't request interrupt\n"); 256 + goto disable_clks; 257 + } 258 + 259 + rtc->rtc = devm_rtc_device_register(&pdev->dev, "lpc24xx-rtc", 260 + &lpc24xx_rtc_ops, THIS_MODULE); 261 + if (IS_ERR(rtc->rtc)) { 262 + dev_err(&pdev->dev, "can't register rtc device\n"); 263 + ret = PTR_ERR(rtc->rtc); 264 + goto disable_clks; 265 + } 266 + 267 + return 0; 268 + 269 + disable_clks: 270 + clk_disable_unprepare(rtc->clk_reg); 271 + disable_rtc_clk: 272 + clk_disable_unprepare(rtc->clk_rtc); 273 + return ret; 274 + } 275 + 276 + static int lpc24xx_rtc_remove(struct platform_device *pdev) 277 + { 278 + struct lpc24xx_rtc *rtc = platform_get_drvdata(pdev); 279 + 280 + /* Ensure all interrupt sources are masked */ 281 + rtc_writel(rtc, LPC24XX_AMR, LPC24XX_ALARM_DISABLE); 282 + rtc_writel(rtc, LPC24XX_CIIR, 0); 283 + 284 + rtc_writel(rtc, LPC24XX_CCR, LPC178X_CCALEN); 285 + 286 + clk_disable_unprepare(rtc->clk_rtc); 287 + clk_disable_unprepare(rtc->clk_reg); 288 + 289 + return 0; 290 + } 291 + 292 + static const struct of_device_id lpc24xx_rtc_match[] = { 293 + { .compatible = "nxp,lpc1788-rtc" }, 294 + { } 295 + }; 296 + MODULE_DEVICE_TABLE(of, lpc24xx_rtc_match); 297 + 298 + static struct platform_driver lpc24xx_rtc_driver = { 299 + .probe = lpc24xx_rtc_probe, 300 + .remove = lpc24xx_rtc_remove, 301 + .driver = { 302 + .name = "lpc24xx-rtc", 303 + .of_match_table = lpc24xx_rtc_match, 304 + }, 305 + }; 306 + module_platform_driver(lpc24xx_rtc_driver); 307 + 308 + MODULE_AUTHOR("Kevin Wells <wellsk40@gmail.com>"); 309 + MODULE_DESCRIPTION("RTC driver for the LPC178x/18xx/408x/43xx SoCs"); 310 + MODULE_LICENSE("GPL");