Skip to content

Commit ba02781

Browse files
thierryredinggregkh
authored andcommitted
rtc: tegra: Implement clock handling
commit 5fa4086987506b2ab8c92f8f99f2295db9918856 upstream. 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> [bwh: Backported to 4.9: adjust context] Signed-off-by: Ben Hutchings <ben@decadent.org.uk> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent ccf0904 commit ba02781

1 file changed

Lines changed: 26 additions & 2 deletions

File tree

drivers/rtc/rtc-tegra.c

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
1919
*/
2020
#include <linux/kernel.h>
21+
#include <linux/clk.h>
2122
#include <linux/init.h>
2223
#include <linux/module.h>
2324
#include <linux/slab.h>
@@ -59,6 +60,7 @@ struct tegra_rtc_info {
5960
struct platform_device *pdev;
6061
struct rtc_device *rtc_dev;
6162
void __iomem *rtc_base; /* NULL if not initialized. */
63+
struct clk *clk;
6264
int tegra_rtc_irq; /* alarm and periodic irq */
6365
spinlock_t tegra_rtc_lock;
6466
};
@@ -332,6 +334,14 @@ static int __init tegra_rtc_probe(struct platform_device *pdev)
332334
if (info->tegra_rtc_irq <= 0)
333335
return -EBUSY;
334336

337+
info->clk = devm_clk_get(&pdev->dev, NULL);
338+
if (IS_ERR(info->clk))
339+
return PTR_ERR(info->clk);
340+
341+
ret = clk_prepare_enable(info->clk);
342+
if (ret < 0)
343+
return ret;
344+
335345
/* set context info. */
336346
info->pdev = pdev;
337347
spin_lock_init(&info->tegra_rtc_lock);
@@ -352,7 +362,7 @@ static int __init tegra_rtc_probe(struct platform_device *pdev)
352362
ret = PTR_ERR(info->rtc_dev);
353363
dev_err(&pdev->dev, "Unable to register device (err=%d).\n",
354364
ret);
355-
return ret;
365+
goto disable_clk;
356366
}
357367

358368
ret = devm_request_irq(&pdev->dev, info->tegra_rtc_irq,
@@ -362,11 +372,24 @@ static int __init tegra_rtc_probe(struct platform_device *pdev)
362372
dev_err(&pdev->dev,
363373
"Unable to request interrupt for device (err=%d).\n",
364374
ret);
365-
return ret;
375+
goto disable_clk;
366376
}
367377

368378
dev_notice(&pdev->dev, "Tegra internal Real Time Clock\n");
369379

380+
return 0;
381+
382+
disable_clk:
383+
clk_disable_unprepare(info->clk);
384+
return ret;
385+
}
386+
387+
static int tegra_rtc_remove(struct platform_device *pdev)
388+
{
389+
struct tegra_rtc_info *info = platform_get_drvdata(pdev);
390+
391+
clk_disable_unprepare(info->clk);
392+
370393
return 0;
371394
}
372395

@@ -419,6 +442,7 @@ static void tegra_rtc_shutdown(struct platform_device *pdev)
419442

420443
MODULE_ALIAS("platform:tegra_rtc");
421444
static struct platform_driver tegra_rtc_driver = {
445+
.remove = tegra_rtc_remove,
422446
.shutdown = tegra_rtc_shutdown,
423447
.driver = {
424448
.name = "tegra_rtc",

0 commit comments

Comments
 (0)