305 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * RTC driver for Nuvoton MA35D14 *5 * Copyright (C) 2023 Nuvoton Technology Corp.6 */7 8#include <linux/bcd.h>9#include <linux/clk.h>10#include <linux/delay.h>11#include <linux/init.h>12#include <linux/io.h>13#include <linux/module.h>14#include <linux/of.h>15#include <linux/platform_device.h>16#include <linux/rtc.h>17 18/* MA35D1 RTC Control Registers */19#define MA35_REG_RTC_INIT 0x0020#define MA35_REG_RTC_SINFASTS 0x0421#define MA35_REG_RTC_FREQADJ 0x0822#define MA35_REG_RTC_TIME 0x0c23#define MA35_REG_RTC_CAL 0x1024#define MA35_REG_RTC_CLKFMT 0x1425#define MA35_REG_RTC_WEEKDAY 0x1826#define MA35_REG_RTC_TALM 0x1c27#define MA35_REG_RTC_CALM 0x2028#define MA35_REG_RTC_LEAPYEAR 0x2429#define MA35_REG_RTC_INTEN 0x2830#define MA35_REG_RTC_INTSTS 0x2c31 32/* register MA35_REG_RTC_INIT */33#define RTC_INIT_ACTIVE BIT(0)34#define RTC_INIT_MAGIC_CODE 0xa5eb135735 36/* register MA35_REG_RTC_CLKFMT */37#define RTC_CLKFMT_24HEN BIT(0)38#define RTC_CLKFMT_DCOMPEN BIT(16)39 40/* register MA35_REG_RTC_INTEN */41#define RTC_INTEN_ALMIEN BIT(0)42#define RTC_INTEN_UIEN BIT(1)43#define RTC_INTEN_CLKFIEN BIT(24)44#define RTC_INTEN_CLKSTIEN BIT(25)45 46/* register MA35_REG_RTC_INTSTS */47#define RTC_INTSTS_ALMIF BIT(0)48#define RTC_INTSTS_UIF BIT(1)49#define RTC_INTSTS_CLKFIF BIT(24)50#define RTC_INTSTS_CLKSTIF BIT(25)51 52#define RTC_INIT_TIMEOUT 25053 54struct ma35_rtc {55 int irq_num;56 void __iomem *rtc_reg;57 struct rtc_device *rtcdev;58};59 60static u32 rtc_reg_read(struct ma35_rtc *p, u32 offset)61{62 return __raw_readl(p->rtc_reg + offset);63}64 65static inline void rtc_reg_write(struct ma35_rtc *p, u32 offset, u32 value)66{67 __raw_writel(value, p->rtc_reg + offset);68}69 70static irqreturn_t ma35d1_rtc_interrupt(int irq, void *data)71{72 struct ma35_rtc *rtc = (struct ma35_rtc *)data;73 unsigned long events = 0, rtc_irq;74 75 rtc_irq = rtc_reg_read(rtc, MA35_REG_RTC_INTSTS);76 77 if (rtc_irq & RTC_INTSTS_ALMIF) {78 rtc_reg_write(rtc, MA35_REG_RTC_INTSTS, RTC_INTSTS_ALMIF);79 events |= RTC_AF | RTC_IRQF;80 }81 82 rtc_update_irq(rtc->rtcdev, 1, events);83 84 return IRQ_HANDLED;85}86 87static int ma35d1_rtc_init(struct ma35_rtc *rtc, u32 ms_timeout)88{89 const unsigned long timeout = jiffies + msecs_to_jiffies(ms_timeout);90 91 do {92 if (rtc_reg_read(rtc, MA35_REG_RTC_INIT) & RTC_INIT_ACTIVE)93 return 0;94 95 rtc_reg_write(rtc, MA35_REG_RTC_INIT, RTC_INIT_MAGIC_CODE);96 97 mdelay(1);98 99 } while (time_before(jiffies, timeout));100 101 return -ETIMEDOUT;102}103 104static int ma35d1_alarm_irq_enable(struct device *dev, u32 enabled)105{106 struct ma35_rtc *rtc = dev_get_drvdata(dev);107 u32 reg_ien;108 109 reg_ien = rtc_reg_read(rtc, MA35_REG_RTC_INTEN);110 111 if (enabled)112 rtc_reg_write(rtc, MA35_REG_RTC_INTEN, reg_ien | RTC_INTEN_ALMIEN);113 else114 rtc_reg_write(rtc, MA35_REG_RTC_INTEN, reg_ien & ~RTC_INTEN_ALMIEN);115 116 return 0;117}118 119static int ma35d1_rtc_read_time(struct device *dev, struct rtc_time *tm)120{121 struct ma35_rtc *rtc = dev_get_drvdata(dev);122 u32 time, cal, wday;123 124 do {125 time = rtc_reg_read(rtc, MA35_REG_RTC_TIME);126 cal = rtc_reg_read(rtc, MA35_REG_RTC_CAL);127 wday = rtc_reg_read(rtc, MA35_REG_RTC_WEEKDAY);128 } while (time != rtc_reg_read(rtc, MA35_REG_RTC_TIME) ||129 cal != rtc_reg_read(rtc, MA35_REG_RTC_CAL));130 131 tm->tm_mday = bcd2bin(cal >> 0);132 tm->tm_wday = wday;133 tm->tm_mon = bcd2bin(cal >> 8);134 tm->tm_mon = tm->tm_mon - 1;135 tm->tm_year = bcd2bin(cal >> 16) + 100;136 137 tm->tm_sec = bcd2bin(time >> 0);138 tm->tm_min = bcd2bin(time >> 8);139 tm->tm_hour = bcd2bin(time >> 16);140 141 return rtc_valid_tm(tm);142}143 144static int ma35d1_rtc_set_time(struct device *dev, struct rtc_time *tm)145{146 struct ma35_rtc *rtc = dev_get_drvdata(dev);147 u32 val;148 149 val = bin2bcd(tm->tm_mday) << 0 | bin2bcd(tm->tm_mon + 1) << 8 |150 bin2bcd(tm->tm_year - 100) << 16;151 rtc_reg_write(rtc, MA35_REG_RTC_CAL, val);152 153 val = bin2bcd(tm->tm_sec) << 0 | bin2bcd(tm->tm_min) << 8 |154 bin2bcd(tm->tm_hour) << 16;155 rtc_reg_write(rtc, MA35_REG_RTC_TIME, val);156 157 val = tm->tm_wday;158 rtc_reg_write(rtc, MA35_REG_RTC_WEEKDAY, val);159 160 return 0;161}162 163static int ma35d1_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)164{165 struct ma35_rtc *rtc = dev_get_drvdata(dev);166 u32 talm, calm;167 168 talm = rtc_reg_read(rtc, MA35_REG_RTC_TALM);169 calm = rtc_reg_read(rtc, MA35_REG_RTC_CALM);170 171 alrm->time.tm_mday = bcd2bin(calm >> 0);172 alrm->time.tm_mon = bcd2bin(calm >> 8);173 alrm->time.tm_mon = alrm->time.tm_mon - 1;174 175 alrm->time.tm_year = bcd2bin(calm >> 16) + 100;176 177 alrm->time.tm_sec = bcd2bin(talm >> 0);178 alrm->time.tm_min = bcd2bin(talm >> 8);179 alrm->time.tm_hour = bcd2bin(talm >> 16);180 181 return rtc_valid_tm(&alrm->time);182}183 184static int ma35d1_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)185{186 struct ma35_rtc *rtc = dev_get_drvdata(dev);187 unsigned long val;188 189 val = bin2bcd(alrm->time.tm_mday) << 0 | bin2bcd(alrm->time.tm_mon + 1) << 8 |190 bin2bcd(alrm->time.tm_year - 100) << 16;191 rtc_reg_write(rtc, MA35_REG_RTC_CALM, val);192 193 val = bin2bcd(alrm->time.tm_sec) << 0 | bin2bcd(alrm->time.tm_min) << 8 |194 bin2bcd(alrm->time.tm_hour) << 16;195 rtc_reg_write(rtc, MA35_REG_RTC_TALM, val);196 197 ma35d1_alarm_irq_enable(dev, alrm->enabled);198 199 return 0;200}201 202static const struct rtc_class_ops ma35d1_rtc_ops = {203 .read_time = ma35d1_rtc_read_time,204 .set_time = ma35d1_rtc_set_time,205 .read_alarm = ma35d1_rtc_read_alarm,206 .set_alarm = ma35d1_rtc_set_alarm,207 .alarm_irq_enable = ma35d1_alarm_irq_enable,208};209 210static int ma35d1_rtc_probe(struct platform_device *pdev)211{212 struct ma35_rtc *rtc;213 struct clk *clk;214 int ret;215 216 rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);217 if (!rtc)218 return -ENOMEM;219 220 rtc->rtc_reg = devm_platform_ioremap_resource(pdev, 0);221 if (IS_ERR(rtc->rtc_reg))222 return PTR_ERR(rtc->rtc_reg);223 224 clk = of_clk_get(pdev->dev.of_node, 0);225 if (IS_ERR(clk))226 return dev_err_probe(&pdev->dev, PTR_ERR(clk), "failed to find rtc clock\n");227 228 ret = clk_prepare_enable(clk);229 if (ret)230 return ret;231 232 if (!(rtc_reg_read(rtc, MA35_REG_RTC_INIT) & RTC_INIT_ACTIVE)) {233 ret = ma35d1_rtc_init(rtc, RTC_INIT_TIMEOUT);234 if (ret)235 return dev_err_probe(&pdev->dev, ret, "rtc init failed\n");236 }237 238 rtc->irq_num = platform_get_irq(pdev, 0);239 240 ret = devm_request_irq(&pdev->dev, rtc->irq_num, ma35d1_rtc_interrupt,241 IRQF_NO_SUSPEND, "ma35d1rtc", rtc);242 if (ret)243 return dev_err_probe(&pdev->dev, ret, "Failed to request rtc irq\n");244 245 platform_set_drvdata(pdev, rtc);246 247 device_init_wakeup(&pdev->dev, true);248 249 rtc->rtcdev = devm_rtc_allocate_device(&pdev->dev);250 if (IS_ERR(rtc->rtcdev))251 return PTR_ERR(rtc->rtcdev);252 253 rtc->rtcdev->ops = &ma35d1_rtc_ops;254 rtc->rtcdev->range_min = RTC_TIMESTAMP_BEGIN_2000;255 rtc->rtcdev->range_max = RTC_TIMESTAMP_END_2099;256 257 ret = devm_rtc_register_device(rtc->rtcdev);258 if (ret)259 return dev_err_probe(&pdev->dev, ret, "Failed to register rtc device\n");260 261 return 0;262}263 264static int ma35d1_rtc_suspend(struct platform_device *pdev, pm_message_t state)265{266 struct ma35_rtc *rtc = platform_get_drvdata(pdev);267 268 if (device_may_wakeup(&pdev->dev))269 enable_irq_wake(rtc->irq_num);270 271 return 0;272}273 274static int ma35d1_rtc_resume(struct platform_device *pdev)275{276 struct ma35_rtc *rtc = platform_get_drvdata(pdev);277 278 if (device_may_wakeup(&pdev->dev))279 disable_irq_wake(rtc->irq_num);280 281 return 0;282}283 284static const struct of_device_id ma35d1_rtc_of_match[] = {285 { .compatible = "nuvoton,ma35d1-rtc", },286 {},287};288MODULE_DEVICE_TABLE(of, ma35d1_rtc_of_match);289 290static struct platform_driver ma35d1_rtc_driver = {291 .suspend = ma35d1_rtc_suspend,292 .resume = ma35d1_rtc_resume,293 .probe = ma35d1_rtc_probe,294 .driver = {295 .name = "rtc-ma35d1",296 .of_match_table = ma35d1_rtc_of_match,297 },298};299 300module_platform_driver(ma35d1_rtc_driver);301 302MODULE_AUTHOR("Ming-Jen Chen <mjchen@nuvoton.com>");303MODULE_DESCRIPTION("MA35D1 RTC driver");304MODULE_LICENSE("GPL");305