385 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * PIC32 RTC driver4 *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 0x0022#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 0x1032#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 0x0F0037#define PIC32_RTCALRM_ARPT 0xFF38 39#define PIC32_RTCHOUR 0x2340#define PIC32_RTCMIN 0x2241#define PIC32_RTCSEC 0x2142#define PIC32_RTCYEAR 0x3343#define PIC32_RTCMON 0x3244#define PIC32_RTCDAY 0x3145 46#define PIC32_ALRMTIME 0x4047#define PIC32_ALRMDATE 0x5048 49#define PIC32_ALRMHOUR 0x4350#define PIC32_ALRMMIN 0x4251#define PIC32_ALRMSEC 0x4152#define PIC32_ALRMYEAR 0x5353#define PIC32_ALRMMON 0x5254#define PIC32_ALRMDAY 0x5155 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-update150 * when we read it is to check the second counter, and if it151 * 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 void 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 296static int pic32_rtc_probe(struct platform_device *pdev)297{298 struct pic32_rtc_dev *pdata;299 int ret;300 301 pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);302 if (!pdata)303 return -ENOMEM;304 305 platform_set_drvdata(pdev, pdata);306 307 pdata->alarm_irq = platform_get_irq(pdev, 0);308 if (pdata->alarm_irq < 0)309 return pdata->alarm_irq;310 311 pdata->reg_base = devm_platform_ioremap_resource(pdev, 0);312 if (IS_ERR(pdata->reg_base))313 return PTR_ERR(pdata->reg_base);314 315 pdata->clk = devm_clk_get(&pdev->dev, NULL);316 if (IS_ERR(pdata->clk)) {317 dev_err(&pdev->dev, "failed to find rtc clock source\n");318 ret = PTR_ERR(pdata->clk);319 pdata->clk = NULL;320 return ret;321 }322 323 spin_lock_init(&pdata->alarm_lock);324 325 pdata->rtc = devm_rtc_allocate_device(&pdev->dev);326 if (IS_ERR(pdata->rtc))327 return PTR_ERR(pdata->rtc);328 329 clk_prepare_enable(pdata->clk);330 331 pic32_rtc_enable(pdata, 1);332 333 device_init_wakeup(&pdev->dev, 1);334 335 pdata->rtc->ops = &pic32_rtcops;336 pdata->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;337 pdata->rtc->range_max = RTC_TIMESTAMP_END_2099;338 339 ret = devm_rtc_register_device(pdata->rtc);340 if (ret)341 goto err_nortc;342 343 pdata->rtc->max_user_freq = 128;344 345 pic32_rtc_setfreq(&pdev->dev, 1);346 ret = devm_request_irq(&pdev->dev, pdata->alarm_irq,347 pic32_rtc_alarmirq, 0,348 dev_name(&pdev->dev), pdata);349 if (ret) {350 dev_err(&pdev->dev,351 "IRQ %d error %d\n", pdata->alarm_irq, ret);352 goto err_nortc;353 }354 355 clk_disable(pdata->clk);356 357 return 0;358 359err_nortc:360 pic32_rtc_enable(pdata, 0);361 clk_disable_unprepare(pdata->clk);362 363 return ret;364}365 366static const struct of_device_id pic32_rtc_dt_ids[] = {367 { .compatible = "microchip,pic32mzda-rtc" },368 { /* sentinel */ }369};370MODULE_DEVICE_TABLE(of, pic32_rtc_dt_ids);371 372static struct platform_driver pic32_rtc_driver = {373 .probe = pic32_rtc_probe,374 .remove_new = pic32_rtc_remove,375 .driver = {376 .name = "pic32-rtc",377 .of_match_table = of_match_ptr(pic32_rtc_dt_ids),378 },379};380module_platform_driver(pic32_rtc_driver);381 382MODULE_DESCRIPTION("Microchip PIC32 RTC Driver");383MODULE_AUTHOR("Joshua Henderson <joshua.henderson@microchip.com>");384MODULE_LICENSE("GPL");385