rtc-pic32.c (9805B)
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * PIC32 RTC driver 4 * 5 * Joshua Henderson <joshua.henderson@microchip.com> 6 * Copyright (C) 2016 Microchip Technology Inc. All rights reserved. 7 * 8 */ 9#include <linux/init.h> 10#include <linux/module.h> 11#include <linux/of.h> 12#include <linux/platform_device.h> 13#include <linux/io.h> 14#include <linux/slab.h> 15#include <linux/clk.h> 16#include <linux/rtc.h> 17#include <linux/bcd.h> 18 19#include <asm/mach-pic32/pic32.h> 20 21#define PIC32_RTCCON 0x00 22#define PIC32_RTCCON_ON BIT(15) 23#define PIC32_RTCCON_SIDL BIT(13) 24#define PIC32_RTCCON_RTCCLKSEL (3 << 9) 25#define PIC32_RTCCON_RTCCLKON BIT(6) 26#define PIC32_RTCCON_RTCWREN BIT(3) 27#define PIC32_RTCCON_RTCSYNC BIT(2) 28#define PIC32_RTCCON_HALFSEC BIT(1) 29#define PIC32_RTCCON_RTCOE BIT(0) 30 31#define PIC32_RTCALRM 0x10 32#define PIC32_RTCALRM_ALRMEN BIT(15) 33#define PIC32_RTCALRM_CHIME BIT(14) 34#define PIC32_RTCALRM_PIV BIT(13) 35#define PIC32_RTCALRM_ALARMSYNC BIT(12) 36#define PIC32_RTCALRM_AMASK 0x0F00 37#define PIC32_RTCALRM_ARPT 0xFF 38 39#define PIC32_RTCHOUR 0x23 40#define PIC32_RTCMIN 0x22 41#define PIC32_RTCSEC 0x21 42#define PIC32_RTCYEAR 0x33 43#define PIC32_RTCMON 0x32 44#define PIC32_RTCDAY 0x31 45 46#define PIC32_ALRMTIME 0x40 47#define PIC32_ALRMDATE 0x50 48 49#define PIC32_ALRMHOUR 0x43 50#define PIC32_ALRMMIN 0x42 51#define PIC32_ALRMSEC 0x41 52#define PIC32_ALRMYEAR 0x53 53#define PIC32_ALRMMON 0x52 54#define PIC32_ALRMDAY 0x51 55 56struct pic32_rtc_dev { 57 struct rtc_device *rtc; 58 void __iomem *reg_base; 59 struct clk *clk; 60 spinlock_t alarm_lock; 61 int alarm_irq; 62 bool alarm_clk_enabled; 63}; 64 65static void pic32_rtc_alarm_clk_enable(struct pic32_rtc_dev *pdata, 66 bool enable) 67{ 68 unsigned long flags; 69 70 spin_lock_irqsave(&pdata->alarm_lock, flags); 71 if (enable) { 72 if (!pdata->alarm_clk_enabled) { 73 clk_enable(pdata->clk); 74 pdata->alarm_clk_enabled = true; 75 } 76 } else { 77 if (pdata->alarm_clk_enabled) { 78 clk_disable(pdata->clk); 79 pdata->alarm_clk_enabled = false; 80 } 81 } 82 spin_unlock_irqrestore(&pdata->alarm_lock, flags); 83} 84 85static irqreturn_t pic32_rtc_alarmirq(int irq, void *id) 86{ 87 struct pic32_rtc_dev *pdata = (struct pic32_rtc_dev *)id; 88 89 clk_enable(pdata->clk); 90 rtc_update_irq(pdata->rtc, 1, RTC_AF | RTC_IRQF); 91 clk_disable(pdata->clk); 92 93 pic32_rtc_alarm_clk_enable(pdata, false); 94 95 return IRQ_HANDLED; 96} 97 98static int pic32_rtc_setaie(struct device *dev, unsigned int enabled) 99{ 100 struct pic32_rtc_dev *pdata = dev_get_drvdata(dev); 101 void __iomem *base = pdata->reg_base; 102 103 clk_enable(pdata->clk); 104 105 writel(PIC32_RTCALRM_ALRMEN, 106 base + (enabled ? PIC32_SET(PIC32_RTCALRM) : 107 PIC32_CLR(PIC32_RTCALRM))); 108 109 clk_disable(pdata->clk); 110 111 pic32_rtc_alarm_clk_enable(pdata, enabled); 112 113 return 0; 114} 115 116static int pic32_rtc_setfreq(struct device *dev, int freq) 117{ 118 struct pic32_rtc_dev *pdata = dev_get_drvdata(dev); 119 void __iomem *base = pdata->reg_base; 120 121 clk_enable(pdata->clk); 122 123 writel(PIC32_RTCALRM_AMASK, base + PIC32_CLR(PIC32_RTCALRM)); 124 writel(freq << 8, base + PIC32_SET(PIC32_RTCALRM)); 125 writel(PIC32_RTCALRM_CHIME, base + PIC32_SET(PIC32_RTCALRM)); 126 127 clk_disable(pdata->clk); 128 129 return 0; 130} 131 132static int pic32_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) 133{ 134 struct pic32_rtc_dev *pdata = dev_get_drvdata(dev); 135 void __iomem *base = pdata->reg_base; 136 unsigned int tries = 0; 137 138 clk_enable(pdata->clk); 139 140 do { 141 rtc_tm->tm_hour = readb(base + PIC32_RTCHOUR); 142 rtc_tm->tm_min = readb(base + PIC32_RTCMIN); 143 rtc_tm->tm_mon = readb(base + PIC32_RTCMON); 144 rtc_tm->tm_mday = readb(base + PIC32_RTCDAY); 145 rtc_tm->tm_year = readb(base + PIC32_RTCYEAR); 146 rtc_tm->tm_sec = readb(base + PIC32_RTCSEC); 147 148 /* 149 * The only way to work out whether the system was mid-update 150 * when we read it is to check the second counter, and if it 151 * is zero, then we re-try the entire read. 152 */ 153 tries += 1; 154 } while (rtc_tm->tm_sec == 0 && tries < 2); 155 156 rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec); 157 rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min); 158 rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour); 159 rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday); 160 rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon) - 1; 161 rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year); 162 163 rtc_tm->tm_year += 100; 164 165 dev_dbg(dev, "read time %ptR\n", rtc_tm); 166 167 clk_disable(pdata->clk); 168 return 0; 169} 170 171static int pic32_rtc_settime(struct device *dev, struct rtc_time *tm) 172{ 173 struct pic32_rtc_dev *pdata = dev_get_drvdata(dev); 174 void __iomem *base = pdata->reg_base; 175 176 dev_dbg(dev, "set time %ptR\n", tm); 177 178 clk_enable(pdata->clk); 179 writeb(bin2bcd(tm->tm_sec), base + PIC32_RTCSEC); 180 writeb(bin2bcd(tm->tm_min), base + PIC32_RTCMIN); 181 writeb(bin2bcd(tm->tm_hour), base + PIC32_RTCHOUR); 182 writeb(bin2bcd(tm->tm_mday), base + PIC32_RTCDAY); 183 writeb(bin2bcd(tm->tm_mon + 1), base + PIC32_RTCMON); 184 writeb(bin2bcd(tm->tm_year - 100), base + PIC32_RTCYEAR); 185 clk_disable(pdata->clk); 186 187 return 0; 188} 189 190static int pic32_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) 191{ 192 struct pic32_rtc_dev *pdata = dev_get_drvdata(dev); 193 struct rtc_time *alm_tm = &alrm->time; 194 void __iomem *base = pdata->reg_base; 195 unsigned int alm_en; 196 197 clk_enable(pdata->clk); 198 alm_tm->tm_sec = readb(base + PIC32_ALRMSEC); 199 alm_tm->tm_min = readb(base + PIC32_ALRMMIN); 200 alm_tm->tm_hour = readb(base + PIC32_ALRMHOUR); 201 alm_tm->tm_mon = readb(base + PIC32_ALRMMON); 202 alm_tm->tm_mday = readb(base + PIC32_ALRMDAY); 203 alm_tm->tm_year = readb(base + PIC32_ALRMYEAR); 204 205 alm_en = readb(base + PIC32_RTCALRM); 206 207 alrm->enabled = (alm_en & PIC32_RTCALRM_ALRMEN) ? 1 : 0; 208 209 dev_dbg(dev, "getalarm: %d, %ptR\n", alm_en, alm_tm); 210 211 alm_tm->tm_sec = bcd2bin(alm_tm->tm_sec); 212 alm_tm->tm_min = bcd2bin(alm_tm->tm_min); 213 alm_tm->tm_hour = bcd2bin(alm_tm->tm_hour); 214 alm_tm->tm_mday = bcd2bin(alm_tm->tm_mday); 215 alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon) - 1; 216 alm_tm->tm_year = bcd2bin(alm_tm->tm_year); 217 218 clk_disable(pdata->clk); 219 return 0; 220} 221 222static int pic32_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) 223{ 224 struct pic32_rtc_dev *pdata = dev_get_drvdata(dev); 225 struct rtc_time *tm = &alrm->time; 226 void __iomem *base = pdata->reg_base; 227 228 clk_enable(pdata->clk); 229 dev_dbg(dev, "setalarm: %d, %ptR\n", alrm->enabled, tm); 230 231 writel(0x00, base + PIC32_ALRMTIME); 232 writel(0x00, base + PIC32_ALRMDATE); 233 234 pic32_rtc_setaie(dev, alrm->enabled); 235 236 clk_disable(pdata->clk); 237 return 0; 238} 239 240static int pic32_rtc_proc(struct device *dev, struct seq_file *seq) 241{ 242 struct pic32_rtc_dev *pdata = dev_get_drvdata(dev); 243 void __iomem *base = pdata->reg_base; 244 unsigned int repeat; 245 246 clk_enable(pdata->clk); 247 248 repeat = readw(base + PIC32_RTCALRM); 249 repeat &= PIC32_RTCALRM_ARPT; 250 seq_printf(seq, "periodic_IRQ\t: %s\n", repeat ? "yes" : "no"); 251 252 clk_disable(pdata->clk); 253 return 0; 254} 255 256static const struct rtc_class_ops pic32_rtcops = { 257 .read_time = pic32_rtc_gettime, 258 .set_time = pic32_rtc_settime, 259 .read_alarm = pic32_rtc_getalarm, 260 .set_alarm = pic32_rtc_setalarm, 261 .proc = pic32_rtc_proc, 262 .alarm_irq_enable = pic32_rtc_setaie, 263}; 264 265static void pic32_rtc_enable(struct pic32_rtc_dev *pdata, int en) 266{ 267 void __iomem *base = pdata->reg_base; 268 269 if (!base) 270 return; 271 272 clk_enable(pdata->clk); 273 if (!en) { 274 writel(PIC32_RTCCON_ON, base + PIC32_CLR(PIC32_RTCCON)); 275 } else { 276 pic32_syskey_unlock(); 277 278 writel(PIC32_RTCCON_RTCWREN, base + PIC32_SET(PIC32_RTCCON)); 279 writel(3 << 9, base + PIC32_CLR(PIC32_RTCCON)); 280 281 if (!(readl(base + PIC32_RTCCON) & PIC32_RTCCON_ON)) 282 writel(PIC32_RTCCON_ON, base + PIC32_SET(PIC32_RTCCON)); 283 } 284 clk_disable(pdata->clk); 285} 286 287static int pic32_rtc_remove(struct platform_device *pdev) 288{ 289 struct pic32_rtc_dev *pdata = platform_get_drvdata(pdev); 290 291 pic32_rtc_setaie(&pdev->dev, 0); 292 clk_unprepare(pdata->clk); 293 pdata->clk = NULL; 294 295 return 0; 296} 297 298static int pic32_rtc_probe(struct platform_device *pdev) 299{ 300 struct pic32_rtc_dev *pdata; 301 int ret; 302 303 pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); 304 if (!pdata) 305 return -ENOMEM; 306 307 platform_set_drvdata(pdev, pdata); 308 309 pdata->alarm_irq = platform_get_irq(pdev, 0); 310 if (pdata->alarm_irq < 0) 311 return pdata->alarm_irq; 312 313 pdata->reg_base = devm_platform_ioremap_resource(pdev, 0); 314 if (IS_ERR(pdata->reg_base)) 315 return PTR_ERR(pdata->reg_base); 316 317 pdata->clk = devm_clk_get(&pdev->dev, NULL); 318 if (IS_ERR(pdata->clk)) { 319 dev_err(&pdev->dev, "failed to find rtc clock source\n"); 320 ret = PTR_ERR(pdata->clk); 321 pdata->clk = NULL; 322 return ret; 323 } 324 325 spin_lock_init(&pdata->alarm_lock); 326 327 clk_prepare_enable(pdata->clk); 328 329 pic32_rtc_enable(pdata, 1); 330 331 device_init_wakeup(&pdev->dev, 1); 332 333 pdata->rtc = devm_rtc_allocate_device(&pdev->dev); 334 if (IS_ERR(pdata->rtc)) 335 return PTR_ERR(pdata->rtc); 336 337 pdata->rtc->ops = &pic32_rtcops; 338 pdata->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; 339 pdata->rtc->range_max = RTC_TIMESTAMP_END_2099; 340 341 ret = devm_rtc_register_device(pdata->rtc); 342 if (ret) 343 goto err_nortc; 344 345 pdata->rtc->max_user_freq = 128; 346 347 pic32_rtc_setfreq(&pdev->dev, 1); 348 ret = devm_request_irq(&pdev->dev, pdata->alarm_irq, 349 pic32_rtc_alarmirq, 0, 350 dev_name(&pdev->dev), pdata); 351 if (ret) { 352 dev_err(&pdev->dev, 353 "IRQ %d error %d\n", pdata->alarm_irq, ret); 354 goto err_nortc; 355 } 356 357 clk_disable(pdata->clk); 358 359 return 0; 360 361err_nortc: 362 pic32_rtc_enable(pdata, 0); 363 clk_disable_unprepare(pdata->clk); 364 365 return ret; 366} 367 368static const struct of_device_id pic32_rtc_dt_ids[] = { 369 { .compatible = "microchip,pic32mzda-rtc" }, 370 { /* sentinel */ } 371}; 372MODULE_DEVICE_TABLE(of, pic32_rtc_dt_ids); 373 374static struct platform_driver pic32_rtc_driver = { 375 .probe = pic32_rtc_probe, 376 .remove = pic32_rtc_remove, 377 .driver = { 378 .name = "pic32-rtc", 379 .of_match_table = of_match_ptr(pic32_rtc_dt_ids), 380 }, 381}; 382module_platform_driver(pic32_rtc_driver); 383 384MODULE_DESCRIPTION("Microchip PIC32 RTC Driver"); 385MODULE_AUTHOR("Joshua Henderson <joshua.henderson@microchip.com>"); 386MODULE_LICENSE("GPL");