434 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright © 2014-2023 Broadcom4 */5 6#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt7 8#include <linux/clk.h>9#include <linux/device.h>10#include <linux/err.h>11#include <linux/init.h>12#include <linux/interrupt.h>13#include <linux/io.h>14#include <linux/irqreturn.h>15#include <linux/kernel.h>16#include <linux/module.h>17#include <linux/of.h>18#include <linux/platform_device.h>19#include <linux/pm.h>20#include <linux/pm_wakeup.h>21#include <linux/reboot.h>22#include <linux/rtc.h>23#include <linux/stat.h>24#include <linux/suspend.h>25 26struct brcmstb_waketmr {27 struct rtc_device *rtc;28 struct device *dev;29 void __iomem *base;30 unsigned int wake_irq;31 unsigned int alarm_irq;32 struct notifier_block reboot_notifier;33 struct clk *clk;34 u32 rate;35 unsigned long rtc_alarm;36 bool alarm_en;37 bool alarm_expired;38};39 40#define BRCMSTB_WKTMR_EVENT 0x0041#define WKTMR_ALARM_EVENT BIT(0)42#define BRCMSTB_WKTMR_COUNTER 0x0443#define BRCMSTB_WKTMR_ALARM 0x0844#define BRCMSTB_WKTMR_PRESCALER 0x0C45#define BRCMSTB_WKTMR_PRESCALER_VAL 0x1046 47#define BRCMSTB_WKTMR_DEFAULT_FREQ 2700000048 49static inline bool brcmstb_waketmr_is_pending(struct brcmstb_waketmr *timer)50{51 u32 reg;52 53 reg = readl_relaxed(timer->base + BRCMSTB_WKTMR_EVENT);54 return !!(reg & WKTMR_ALARM_EVENT);55}56 57static inline void brcmstb_waketmr_clear_alarm(struct brcmstb_waketmr *timer)58{59 u32 reg;60 61 if (timer->alarm_en && timer->alarm_irq)62 disable_irq(timer->alarm_irq);63 timer->alarm_en = false;64 reg = readl_relaxed(timer->base + BRCMSTB_WKTMR_COUNTER);65 writel_relaxed(reg - 1, timer->base + BRCMSTB_WKTMR_ALARM);66 writel_relaxed(WKTMR_ALARM_EVENT, timer->base + BRCMSTB_WKTMR_EVENT);67 (void)readl_relaxed(timer->base + BRCMSTB_WKTMR_EVENT);68 if (timer->alarm_expired) {69 timer->alarm_expired = false;70 /* maintain call balance */71 enable_irq(timer->alarm_irq);72 }73}74 75static void brcmstb_waketmr_set_alarm(struct brcmstb_waketmr *timer,76 unsigned int secs)77{78 unsigned int now;79 80 brcmstb_waketmr_clear_alarm(timer);81 82 /* Make sure we are actually counting in seconds */83 writel_relaxed(timer->rate, timer->base + BRCMSTB_WKTMR_PRESCALER);84 85 writel_relaxed(secs, timer->base + BRCMSTB_WKTMR_ALARM);86 now = readl_relaxed(timer->base + BRCMSTB_WKTMR_COUNTER);87 88 while ((int)(secs - now) <= 0 &&89 !brcmstb_waketmr_is_pending(timer)) {90 secs = now + 1;91 writel_relaxed(secs, timer->base + BRCMSTB_WKTMR_ALARM);92 now = readl_relaxed(timer->base + BRCMSTB_WKTMR_COUNTER);93 }94}95 96static irqreturn_t brcmstb_waketmr_irq(int irq, void *data)97{98 struct brcmstb_waketmr *timer = data;99 100 if (!timer->alarm_irq)101 pm_wakeup_event(timer->dev, 0);102 return IRQ_HANDLED;103}104 105static irqreturn_t brcmstb_alarm_irq(int irq, void *data)106{107 struct brcmstb_waketmr *timer = data;108 109 /* Ignore spurious interrupts */110 if (!brcmstb_waketmr_is_pending(timer))111 return IRQ_HANDLED;112 113 if (timer->alarm_en) {114 if (device_may_wakeup(timer->dev)) {115 disable_irq_nosync(irq);116 timer->alarm_expired = true;117 } else {118 writel_relaxed(WKTMR_ALARM_EVENT,119 timer->base + BRCMSTB_WKTMR_EVENT);120 }121 rtc_update_irq(timer->rtc, 1, RTC_IRQF | RTC_AF);122 } else {123 writel_relaxed(WKTMR_ALARM_EVENT,124 timer->base + BRCMSTB_WKTMR_EVENT);125 }126 127 return IRQ_HANDLED;128}129 130struct wktmr_time {131 u32 sec;132 u32 pre;133};134 135static void wktmr_read(struct brcmstb_waketmr *timer,136 struct wktmr_time *t)137{138 u32 tmp;139 140 do {141 t->sec = readl_relaxed(timer->base + BRCMSTB_WKTMR_COUNTER);142 tmp = readl_relaxed(timer->base + BRCMSTB_WKTMR_PRESCALER_VAL);143 } while (tmp >= timer->rate);144 145 t->pre = timer->rate - tmp;146}147 148static int brcmstb_waketmr_prepare_suspend(struct brcmstb_waketmr *timer)149{150 struct device *dev = timer->dev;151 int ret;152 153 if (device_may_wakeup(dev)) {154 ret = enable_irq_wake(timer->wake_irq);155 if (ret) {156 dev_err(dev, "failed to enable wake-up interrupt\n");157 return ret;158 }159 if (timer->alarm_en && timer->alarm_irq) {160 ret = enable_irq_wake(timer->alarm_irq);161 if (ret) {162 dev_err(dev, "failed to enable rtc interrupt\n");163 disable_irq_wake(timer->wake_irq);164 return ret;165 }166 }167 }168 169 return 0;170}171 172/* If enabled as a wakeup-source, arm the timer when powering off */173static int brcmstb_waketmr_reboot(struct notifier_block *nb,174 unsigned long action, void *data)175{176 struct brcmstb_waketmr *timer;177 178 timer = container_of(nb, struct brcmstb_waketmr, reboot_notifier);179 180 /* Set timer for cold boot */181 if (action == SYS_POWER_OFF)182 brcmstb_waketmr_prepare_suspend(timer);183 184 return NOTIFY_DONE;185}186 187static int brcmstb_waketmr_gettime(struct device *dev,188 struct rtc_time *tm)189{190 struct brcmstb_waketmr *timer = dev_get_drvdata(dev);191 struct wktmr_time now;192 193 wktmr_read(timer, &now);194 195 rtc_time64_to_tm(now.sec, tm);196 197 return 0;198}199 200static int brcmstb_waketmr_settime(struct device *dev,201 struct rtc_time *tm)202{203 struct brcmstb_waketmr *timer = dev_get_drvdata(dev);204 time64_t sec;205 206 sec = rtc_tm_to_time64(tm);207 208 writel_relaxed(sec, timer->base + BRCMSTB_WKTMR_COUNTER);209 210 return 0;211}212 213static int brcmstb_waketmr_getalarm(struct device *dev,214 struct rtc_wkalrm *alarm)215{216 struct brcmstb_waketmr *timer = dev_get_drvdata(dev);217 218 alarm->enabled = timer->alarm_en;219 rtc_time64_to_tm(timer->rtc_alarm, &alarm->time);220 221 alarm->pending = brcmstb_waketmr_is_pending(timer);222 223 return 0;224}225 226static int brcmstb_waketmr_alarm_enable(struct device *dev,227 unsigned int enabled)228{229 struct brcmstb_waketmr *timer = dev_get_drvdata(dev);230 231 if (enabled && !timer->alarm_en) {232 if ((int)(readl_relaxed(timer->base + BRCMSTB_WKTMR_COUNTER) -233 readl_relaxed(timer->base + BRCMSTB_WKTMR_ALARM)) >= 0 &&234 !brcmstb_waketmr_is_pending(timer))235 return -EINVAL;236 timer->alarm_en = true;237 if (timer->alarm_irq) {238 if (timer->alarm_expired) {239 timer->alarm_expired = false;240 /* maintain call balance */241 enable_irq(timer->alarm_irq);242 }243 enable_irq(timer->alarm_irq);244 }245 } else if (!enabled && timer->alarm_en) {246 if (timer->alarm_irq)247 disable_irq(timer->alarm_irq);248 timer->alarm_en = false;249 }250 251 return 0;252}253 254static int brcmstb_waketmr_setalarm(struct device *dev,255 struct rtc_wkalrm *alarm)256{257 struct brcmstb_waketmr *timer = dev_get_drvdata(dev);258 259 timer->rtc_alarm = rtc_tm_to_time64(&alarm->time);260 261 brcmstb_waketmr_set_alarm(timer, timer->rtc_alarm);262 263 return brcmstb_waketmr_alarm_enable(dev, alarm->enabled);264}265 266static const struct rtc_class_ops brcmstb_waketmr_ops = {267 .read_time = brcmstb_waketmr_gettime,268 .set_time = brcmstb_waketmr_settime,269 .read_alarm = brcmstb_waketmr_getalarm,270 .set_alarm = brcmstb_waketmr_setalarm,271 .alarm_irq_enable = brcmstb_waketmr_alarm_enable,272};273 274static int brcmstb_waketmr_probe(struct platform_device *pdev)275{276 struct device *dev = &pdev->dev;277 struct brcmstb_waketmr *timer;278 int ret;279 280 timer = devm_kzalloc(dev, sizeof(*timer), GFP_KERNEL);281 if (!timer)282 return -ENOMEM;283 284 platform_set_drvdata(pdev, timer);285 timer->dev = dev;286 287 timer->base = devm_platform_ioremap_resource(pdev, 0);288 if (IS_ERR(timer->base))289 return PTR_ERR(timer->base);290 291 timer->rtc = devm_rtc_allocate_device(dev);292 if (IS_ERR(timer->rtc))293 return PTR_ERR(timer->rtc);294 295 /*296 * Set wakeup capability before requesting wakeup interrupt, so we can297 * process boot-time "wakeups" (e.g., from S5 soft-off)298 */299 device_init_wakeup(dev, true);300 301 ret = platform_get_irq(pdev, 0);302 if (ret < 0)303 return -ENODEV;304 timer->wake_irq = (unsigned int)ret;305 306 timer->clk = devm_clk_get(dev, NULL);307 if (!IS_ERR(timer->clk)) {308 ret = clk_prepare_enable(timer->clk);309 if (ret)310 return ret;311 timer->rate = clk_get_rate(timer->clk);312 if (!timer->rate)313 timer->rate = BRCMSTB_WKTMR_DEFAULT_FREQ;314 } else {315 timer->rate = BRCMSTB_WKTMR_DEFAULT_FREQ;316 timer->clk = NULL;317 }318 319 ret = devm_request_irq(dev, timer->wake_irq, brcmstb_waketmr_irq, 0,320 "brcmstb-waketimer", timer);321 if (ret < 0)322 goto err_clk;323 324 brcmstb_waketmr_clear_alarm(timer);325 326 /* Attempt to initialize non-wake irq */327 ret = platform_get_irq(pdev, 1);328 if (ret > 0) {329 timer->alarm_irq = (unsigned int)ret;330 ret = devm_request_irq(dev, timer->alarm_irq, brcmstb_alarm_irq,331 IRQF_NO_AUTOEN, "brcmstb-waketimer-rtc",332 timer);333 if (ret < 0)334 timer->alarm_irq = 0;335 }336 337 timer->reboot_notifier.notifier_call = brcmstb_waketmr_reboot;338 register_reboot_notifier(&timer->reboot_notifier);339 340 timer->rtc->ops = &brcmstb_waketmr_ops;341 timer->rtc->range_max = U32_MAX;342 343 ret = devm_rtc_register_device(timer->rtc);344 if (ret)345 goto err_notifier;346 347 return 0;348 349err_notifier:350 unregister_reboot_notifier(&timer->reboot_notifier);351 352err_clk:353 clk_disable_unprepare(timer->clk);354 355 return ret;356}357 358static void brcmstb_waketmr_remove(struct platform_device *pdev)359{360 struct brcmstb_waketmr *timer = dev_get_drvdata(&pdev->dev);361 362 unregister_reboot_notifier(&timer->reboot_notifier);363 clk_disable_unprepare(timer->clk);364}365 366#ifdef CONFIG_PM_SLEEP367static int brcmstb_waketmr_suspend(struct device *dev)368{369 struct brcmstb_waketmr *timer = dev_get_drvdata(dev);370 371 return brcmstb_waketmr_prepare_suspend(timer);372}373 374static int brcmstb_waketmr_suspend_noirq(struct device *dev)375{376 struct brcmstb_waketmr *timer = dev_get_drvdata(dev);377 378 /* Catch any alarms occurring prior to noirq */379 if (timer->alarm_expired && device_may_wakeup(dev))380 return -EBUSY;381 382 return 0;383}384 385static int brcmstb_waketmr_resume(struct device *dev)386{387 struct brcmstb_waketmr *timer = dev_get_drvdata(dev);388 int ret;389 390 if (!device_may_wakeup(dev))391 return 0;392 393 ret = disable_irq_wake(timer->wake_irq);394 if (timer->alarm_en && timer->alarm_irq)395 disable_irq_wake(timer->alarm_irq);396 397 brcmstb_waketmr_clear_alarm(timer);398 399 return ret;400}401#else402#define brcmstb_waketmr_suspend NULL403#define brcmstb_waketmr_suspend_noirq NULL404#define brcmstb_waketmr_resume NULL405#endif /* CONFIG_PM_SLEEP */406 407static const struct dev_pm_ops brcmstb_waketmr_pm_ops = {408 .suspend = brcmstb_waketmr_suspend,409 .suspend_noirq = brcmstb_waketmr_suspend_noirq,410 .resume = brcmstb_waketmr_resume,411};412 413static const __maybe_unused struct of_device_id brcmstb_waketmr_of_match[] = {414 { .compatible = "brcm,brcmstb-waketimer" },415 { /* sentinel */ },416};417 418static struct platform_driver brcmstb_waketmr_driver = {419 .probe = brcmstb_waketmr_probe,420 .remove_new = brcmstb_waketmr_remove,421 .driver = {422 .name = "brcmstb-waketimer",423 .pm = &brcmstb_waketmr_pm_ops,424 .of_match_table = of_match_ptr(brcmstb_waketmr_of_match),425 }426};427module_platform_driver(brcmstb_waketmr_driver);428 429MODULE_LICENSE("GPL v2");430MODULE_AUTHOR("Brian Norris");431MODULE_AUTHOR("Markus Mayer");432MODULE_AUTHOR("Doug Berger");433MODULE_DESCRIPTION("Wake-up timer driver for STB chips");434