aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThierry Reding <treding@nvidia.com>2017-01-12 11:07:43 -0500
committerAlexandre Belloni <alexandre.belloni@free-electrons.com>2017-01-22 18:46:55 -0500
commit5fa4086987506b2ab8c92f8f99f2295db9918856 (patch)
treea36631cced680db7ad525464c6fc55d8fc689195
parent0ae20595e334edb0ccd4a421bcf299c762299a09 (diff)
rtc: tegra: Implement clock handling
Accessing the registers of the RTC block on Tegra requires the module clock to be enabled. This only works because the RTC module clock will be enabled by default during early boot. However, because the clock is unused, the CCF will disable it at late_init time. This causes the RTC to become unusable afterwards. This can easily be reproduced by trying to use the RTC: $ hwclock --rtc /dev/rtc1 This will hang the system. I ran into this by following up on a report by Martin Michlmayr that reboot wasn't working on Tegra210 systems. It turns out that the rtc-tegra driver's ->shutdown() implementation will hang the CPU, because of the disabled clock, before the system can be rebooted. What confused me for a while is that the same driver is used on prior Tegra generations where the hang can not be observed. However, as Peter De Schrijver pointed out, this is because on 32-bit Tegra chips the RTC clock is enabled by the tegra20_timer.c clocksource driver, which uses the RTC to provide a persistent clock. This code is never enabled on 64-bit Tegra because the persistent clock infrastructure does not exist on 64-bit ARM. The proper fix for this is to add proper clock handling to the RTC driver in order to ensure that the clock is enabled when the driver requires it. All device trees contain the clock already, therefore no additional changes are required. Reported-by: Martin Michlmayr <tbm@cyrius.com> Acked-By Peter De Schrijver <pdeschrijver@nvidia.com> Signed-off-by: Thierry Reding <treding@nvidia.com> Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
-rw-r--r--drivers/rtc/rtc-tegra.c28
1 files changed, 26 insertions, 2 deletions
diff --git a/drivers/rtc/rtc-tegra.c b/drivers/rtc/rtc-tegra.c
index 38e662ff1a70..d30d57b048d3 100644
--- a/drivers/rtc/rtc-tegra.c
+++ b/drivers/rtc/rtc-tegra.c
@@ -18,6 +18,7 @@
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 */ 19 */
20 20
21#include <linux/clk.h>
21#include <linux/delay.h> 22#include <linux/delay.h>
22#include <linux/init.h> 23#include <linux/init.h>
23#include <linux/io.h> 24#include <linux/io.h>
@@ -60,6 +61,7 @@ struct tegra_rtc_info {
60 struct platform_device *pdev; 61 struct platform_device *pdev;
61 struct rtc_device *rtc_dev; 62 struct rtc_device *rtc_dev;
62 void __iomem *rtc_base; /* NULL if not initialized. */ 63 void __iomem *rtc_base; /* NULL if not initialized. */
64 struct clk *clk;
63 int tegra_rtc_irq; /* alarm and periodic irq */ 65 int tegra_rtc_irq; /* alarm and periodic irq */
64 spinlock_t tegra_rtc_lock; 66 spinlock_t tegra_rtc_lock;
65}; 67};
@@ -327,6 +329,14 @@ static int __init tegra_rtc_probe(struct platform_device *pdev)
327 if (info->tegra_rtc_irq <= 0) 329 if (info->tegra_rtc_irq <= 0)
328 return -EBUSY; 330 return -EBUSY;
329 331
332 info->clk = devm_clk_get(&pdev->dev, NULL);
333 if (IS_ERR(info->clk))
334 return PTR_ERR(info->clk);
335
336 ret = clk_prepare_enable(info->clk);
337 if (ret < 0)
338 return ret;
339
330 /* set context info. */ 340 /* set context info. */
331 info->pdev = pdev; 341 info->pdev = pdev;
332 spin_lock_init(&info->tegra_rtc_lock); 342 spin_lock_init(&info->tegra_rtc_lock);
@@ -347,7 +357,7 @@ static int __init tegra_rtc_probe(struct platform_device *pdev)
347 ret = PTR_ERR(info->rtc_dev); 357 ret = PTR_ERR(info->rtc_dev);
348 dev_err(&pdev->dev, "Unable to register device (err=%d).\n", 358 dev_err(&pdev->dev, "Unable to register device (err=%d).\n",
349 ret); 359 ret);
350 return ret; 360 goto disable_clk;
351 } 361 }
352 362
353 ret = devm_request_irq(&pdev->dev, info->tegra_rtc_irq, 363 ret = devm_request_irq(&pdev->dev, info->tegra_rtc_irq,
@@ -357,12 +367,25 @@ static int __init tegra_rtc_probe(struct platform_device *pdev)
357 dev_err(&pdev->dev, 367 dev_err(&pdev->dev,
358 "Unable to request interrupt for device (err=%d).\n", 368 "Unable to request interrupt for device (err=%d).\n",
359 ret); 369 ret);
360 return ret; 370 goto disable_clk;
361 } 371 }
362 372
363 dev_notice(&pdev->dev, "Tegra internal Real Time Clock\n"); 373 dev_notice(&pdev->dev, "Tegra internal Real Time Clock\n");
364 374
365 return 0; 375 return 0;
376
377disable_clk:
378 clk_disable_unprepare(info->clk);
379 return ret;
380}
381
382static int tegra_rtc_remove(struct platform_device *pdev)
383{
384 struct tegra_rtc_info *info = platform_get_drvdata(pdev);
385
386 clk_disable_unprepare(info->clk);
387
388 return 0;
366} 389}
367 390
368#ifdef CONFIG_PM_SLEEP 391#ifdef CONFIG_PM_SLEEP
@@ -414,6 +437,7 @@ static void tegra_rtc_shutdown(struct platform_device *pdev)
414 437
415MODULE_ALIAS("platform:tegra_rtc"); 438MODULE_ALIAS("platform:tegra_rtc");
416static struct platform_driver tegra_rtc_driver = { 439static struct platform_driver tegra_rtc_driver = {
440 .remove = tegra_rtc_remove,
417 .shutdown = tegra_rtc_shutdown, 441 .shutdown = tegra_rtc_shutdown,
418 .driver = { 442 .driver = {
419 .name = "tegra_rtc", 443 .name = "tegra_rtc",