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

rtc: brcmstb-waketimer: Add Broadcom STB wake-timer

This adds support for the Broadcom STB wake-timer which is a timer in
the chip's 27Mhz clock domain that offers the ability to wake the system
(wake-up source) from suspend states (S2, S3, S5). It is supported using
the rtc framework allowing us to configure alarms for system wake-up.

Signed-off-by: Brian Norris <computersforpeace@gmail.com>
Signed-off-by: Markus Mayer <mmayer@broadcom.com>
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>

authored by

Brian Norris and committed by
Alexandre Belloni
c4f07ece 3fde00a0

+342
+11
drivers/rtc/Kconfig
··· 197 197 This driver can also be built as a module. If so, the module 198 198 will be called rtc-ac100. 199 199 200 + config RTC_DRV_BRCMSTB 201 + tristate "Broadcom STB wake-timer" 202 + depends on ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST 203 + default ARCH_BRCMSTB || BMIPS_GENERIC 204 + help 205 + If you say yes here you get support for the wake-timer found on 206 + Broadcom STB SoCs (BCM7xxx). 207 + 208 + This driver can also be built as a module. If so, the module will 209 + be called rtc-brcmstb-waketimer. 210 + 200 211 config RTC_DRV_AS3722 201 212 tristate "ams AS3722 RTC driver" 202 213 depends on MFD_AS3722
+1
drivers/rtc/Makefile
··· 36 36 obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o 37 37 obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o 38 38 obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o 39 + obj-$(CONFIG_RTC_DRV_BRCMSTB) += rtc-brcmstb-waketimer.o 39 40 obj-$(CONFIG_RTC_DRV_BQ32K) += rtc-bq32k.o 40 41 obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o 41 42 obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o
+330
drivers/rtc/rtc-brcmstb-waketimer.c
··· 1 + /* 2 + * Copyright © 2014-2017 Broadcom 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License version 2 as 6 + * published by the Free Software Foundation. 7 + * 8 + * This program is distributed in the hope that it will be useful, 9 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 + * GNU General Public License for more details. 12 + */ 13 + 14 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 15 + 16 + #include <linux/clk.h> 17 + #include <linux/device.h> 18 + #include <linux/err.h> 19 + #include <linux/init.h> 20 + #include <linux/interrupt.h> 21 + #include <linux/io.h> 22 + #include <linux/irqreturn.h> 23 + #include <linux/kernel.h> 24 + #include <linux/module.h> 25 + #include <linux/of.h> 26 + #include <linux/platform_device.h> 27 + #include <linux/pm.h> 28 + #include <linux/pm_wakeup.h> 29 + #include <linux/reboot.h> 30 + #include <linux/rtc.h> 31 + #include <linux/stat.h> 32 + #include <linux/suspend.h> 33 + 34 + struct brcmstb_waketmr { 35 + struct rtc_device *rtc; 36 + struct device *dev; 37 + void __iomem *base; 38 + int irq; 39 + struct notifier_block reboot_notifier; 40 + struct clk *clk; 41 + u32 rate; 42 + }; 43 + 44 + #define BRCMSTB_WKTMR_EVENT 0x00 45 + #define BRCMSTB_WKTMR_COUNTER 0x04 46 + #define BRCMSTB_WKTMR_ALARM 0x08 47 + #define BRCMSTB_WKTMR_PRESCALER 0x0C 48 + #define BRCMSTB_WKTMR_PRESCALER_VAL 0x10 49 + 50 + #define BRCMSTB_WKTMR_DEFAULT_FREQ 27000000 51 + 52 + static inline void brcmstb_waketmr_clear_alarm(struct brcmstb_waketmr *timer) 53 + { 54 + writel_relaxed(1, timer->base + BRCMSTB_WKTMR_EVENT); 55 + (void)readl_relaxed(timer->base + BRCMSTB_WKTMR_EVENT); 56 + } 57 + 58 + static void brcmstb_waketmr_set_alarm(struct brcmstb_waketmr *timer, 59 + unsigned int secs) 60 + { 61 + brcmstb_waketmr_clear_alarm(timer); 62 + 63 + writel_relaxed(secs + 1, timer->base + BRCMSTB_WKTMR_ALARM); 64 + } 65 + 66 + static irqreturn_t brcmstb_waketmr_irq(int irq, void *data) 67 + { 68 + struct brcmstb_waketmr *timer = data; 69 + 70 + pm_wakeup_event(timer->dev, 0); 71 + 72 + return IRQ_HANDLED; 73 + } 74 + 75 + struct wktmr_time { 76 + u32 sec; 77 + u32 pre; 78 + }; 79 + 80 + static void wktmr_read(struct brcmstb_waketmr *timer, 81 + struct wktmr_time *t) 82 + { 83 + u32 tmp; 84 + 85 + do { 86 + t->sec = readl_relaxed(timer->base + BRCMSTB_WKTMR_COUNTER); 87 + tmp = readl_relaxed(timer->base + BRCMSTB_WKTMR_PRESCALER_VAL); 88 + } while (tmp >= timer->rate); 89 + 90 + t->pre = timer->rate - tmp; 91 + } 92 + 93 + static int brcmstb_waketmr_prepare_suspend(struct brcmstb_waketmr *timer) 94 + { 95 + struct device *dev = timer->dev; 96 + int ret = 0; 97 + 98 + if (device_may_wakeup(dev)) { 99 + ret = enable_irq_wake(timer->irq); 100 + if (ret) { 101 + dev_err(dev, "failed to enable wake-up interrupt\n"); 102 + return ret; 103 + } 104 + } 105 + 106 + return ret; 107 + } 108 + 109 + /* If enabled as a wakeup-source, arm the timer when powering off */ 110 + static int brcmstb_waketmr_reboot(struct notifier_block *nb, 111 + unsigned long action, void *data) 112 + { 113 + struct brcmstb_waketmr *timer; 114 + 115 + timer = container_of(nb, struct brcmstb_waketmr, reboot_notifier); 116 + 117 + /* Set timer for cold boot */ 118 + if (action == SYS_POWER_OFF) 119 + brcmstb_waketmr_prepare_suspend(timer); 120 + 121 + return NOTIFY_DONE; 122 + } 123 + 124 + static int brcmstb_waketmr_gettime(struct device *dev, 125 + struct rtc_time *tm) 126 + { 127 + struct brcmstb_waketmr *timer = dev_get_drvdata(dev); 128 + struct wktmr_time now; 129 + 130 + wktmr_read(timer, &now); 131 + 132 + rtc_time_to_tm(now.sec, tm); 133 + 134 + return 0; 135 + } 136 + 137 + static int brcmstb_waketmr_settime(struct device *dev, 138 + struct rtc_time *tm) 139 + { 140 + struct brcmstb_waketmr *timer = dev_get_drvdata(dev); 141 + time64_t sec; 142 + 143 + sec = rtc_tm_to_time64(tm); 144 + 145 + if (sec > U32_MAX || sec < 0) 146 + return -EINVAL; 147 + 148 + writel_relaxed(sec, timer->base + BRCMSTB_WKTMR_COUNTER); 149 + 150 + return 0; 151 + } 152 + 153 + static int brcmstb_waketmr_getalarm(struct device *dev, 154 + struct rtc_wkalrm *alarm) 155 + { 156 + struct brcmstb_waketmr *timer = dev_get_drvdata(dev); 157 + time64_t sec; 158 + u32 reg; 159 + 160 + sec = readl_relaxed(timer->base + BRCMSTB_WKTMR_ALARM); 161 + if (sec != 0) { 162 + /* Alarm is enabled */ 163 + alarm->enabled = 1; 164 + rtc_time64_to_tm(sec, &alarm->time); 165 + } 166 + 167 + reg = readl_relaxed(timer->base + BRCMSTB_WKTMR_EVENT); 168 + alarm->pending = !!(reg & 1); 169 + 170 + return 0; 171 + } 172 + 173 + static int brcmstb_waketmr_setalarm(struct device *dev, 174 + struct rtc_wkalrm *alarm) 175 + { 176 + struct brcmstb_waketmr *timer = dev_get_drvdata(dev); 177 + time64_t sec; 178 + 179 + if (alarm->enabled) 180 + sec = rtc_tm_to_time64(&alarm->time); 181 + else 182 + sec = 0; 183 + 184 + if (sec > U32_MAX || sec < 0) 185 + return -EINVAL; 186 + 187 + brcmstb_waketmr_set_alarm(timer, sec); 188 + 189 + return 0; 190 + } 191 + 192 + /* 193 + * Does not do much but keep the RTC class happy. We always support 194 + * alarms. 195 + */ 196 + static int brcmstb_waketmr_alarm_enable(struct device *dev, 197 + unsigned int enabled) 198 + { 199 + return 0; 200 + } 201 + 202 + static const struct rtc_class_ops brcmstb_waketmr_ops = { 203 + .read_time = brcmstb_waketmr_gettime, 204 + .set_time = brcmstb_waketmr_settime, 205 + .read_alarm = brcmstb_waketmr_getalarm, 206 + .set_alarm = brcmstb_waketmr_setalarm, 207 + .alarm_irq_enable = brcmstb_waketmr_alarm_enable, 208 + }; 209 + 210 + static int brcmstb_waketmr_probe(struct platform_device *pdev) 211 + { 212 + struct device *dev = &pdev->dev; 213 + struct brcmstb_waketmr *timer; 214 + struct resource *res; 215 + int ret; 216 + 217 + timer = devm_kzalloc(dev, sizeof(*timer), GFP_KERNEL); 218 + if (!timer) 219 + return -ENOMEM; 220 + 221 + platform_set_drvdata(pdev, timer); 222 + timer->dev = dev; 223 + 224 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 225 + timer->base = devm_ioremap_resource(dev, res); 226 + if (IS_ERR(timer->base)) 227 + return PTR_ERR(timer->base); 228 + 229 + /* 230 + * Set wakeup capability before requesting wakeup interrupt, so we can 231 + * process boot-time "wakeups" (e.g., from S5 soft-off) 232 + */ 233 + device_set_wakeup_capable(dev, true); 234 + device_wakeup_enable(dev); 235 + 236 + timer->irq = platform_get_irq(pdev, 0); 237 + if (timer->irq < 0) 238 + return -ENODEV; 239 + 240 + timer->clk = devm_clk_get(dev, NULL); 241 + if (!IS_ERR(timer->clk)) { 242 + ret = clk_prepare_enable(timer->clk); 243 + if (ret) 244 + return ret; 245 + timer->rate = clk_get_rate(timer->clk); 246 + if (!timer->rate) 247 + timer->rate = BRCMSTB_WKTMR_DEFAULT_FREQ; 248 + } else { 249 + timer->rate = BRCMSTB_WKTMR_DEFAULT_FREQ; 250 + timer->clk = NULL; 251 + } 252 + 253 + ret = devm_request_irq(dev, timer->irq, brcmstb_waketmr_irq, 0, 254 + "brcmstb-waketimer", timer); 255 + if (ret < 0) 256 + return ret; 257 + 258 + timer->reboot_notifier.notifier_call = brcmstb_waketmr_reboot; 259 + register_reboot_notifier(&timer->reboot_notifier); 260 + 261 + timer->rtc = rtc_device_register("brcmstb-waketmr", dev, 262 + &brcmstb_waketmr_ops, THIS_MODULE); 263 + if (IS_ERR(timer->rtc)) { 264 + dev_err(dev, "unable to register device\n"); 265 + unregister_reboot_notifier(&timer->reboot_notifier); 266 + return PTR_ERR(timer->rtc); 267 + } 268 + 269 + dev_info(dev, "registered, with irq %d\n", timer->irq); 270 + 271 + return ret; 272 + } 273 + 274 + static int brcmstb_waketmr_remove(struct platform_device *pdev) 275 + { 276 + struct brcmstb_waketmr *timer = dev_get_drvdata(&pdev->dev); 277 + 278 + unregister_reboot_notifier(&timer->reboot_notifier); 279 + rtc_device_unregister(timer->rtc); 280 + 281 + return 0; 282 + } 283 + 284 + #ifdef CONFIG_PM_SLEEP 285 + static int brcmstb_waketmr_suspend(struct device *dev) 286 + { 287 + struct brcmstb_waketmr *timer = dev_get_drvdata(dev); 288 + 289 + return brcmstb_waketmr_prepare_suspend(timer); 290 + } 291 + 292 + static int brcmstb_waketmr_resume(struct device *dev) 293 + { 294 + struct brcmstb_waketmr *timer = dev_get_drvdata(dev); 295 + int ret; 296 + 297 + if (!device_may_wakeup(dev)) 298 + return 0; 299 + 300 + ret = disable_irq_wake(timer->irq); 301 + 302 + brcmstb_waketmr_clear_alarm(timer); 303 + 304 + return ret; 305 + } 306 + #endif /* CONFIG_PM_SLEEP */ 307 + 308 + static SIMPLE_DEV_PM_OPS(brcmstb_waketmr_pm_ops, 309 + brcmstb_waketmr_suspend, brcmstb_waketmr_resume); 310 + 311 + static const struct of_device_id brcmstb_waketmr_of_match[] = { 312 + { .compatible = "brcm,brcmstb-waketimer" }, 313 + { /* sentinel */ }, 314 + }; 315 + 316 + static struct platform_driver brcmstb_waketmr_driver = { 317 + .probe = brcmstb_waketmr_probe, 318 + .remove = brcmstb_waketmr_remove, 319 + .driver = { 320 + .name = "brcmstb-waketimer", 321 + .pm = &brcmstb_waketmr_pm_ops, 322 + .of_match_table = of_match_ptr(brcmstb_waketmr_of_match), 323 + } 324 + }; 325 + module_platform_driver(brcmstb_waketmr_driver); 326 + 327 + MODULE_LICENSE("GPL v2"); 328 + MODULE_AUTHOR("Brian Norris"); 329 + MODULE_AUTHOR("Markus Mayer"); 330 + MODULE_DESCRIPTION("Wake-up timer driver for STB chips");