lkml.org 
[lkml]   [2017]   [Apr]   [19]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.9 62/69] rtc: tegra: Implement clock handling
    Date
    4.9-stable review patch.  If anyone has any objections, please let me know.

    ------------------

    From: Thierry Reding <treding@nvidia.com>

    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>
    ---
    drivers/rtc/rtc-tegra.c | 28 ++++++++++++++++++++++++++--
    1 file changed, 26 insertions(+), 2 deletions(-)

    --- a/drivers/rtc/rtc-tegra.c
    +++ b/drivers/rtc/rtc-tegra.c
    @@ -18,6 +18,7 @@
    * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    */
    #include <linux/kernel.h>
    +#include <linux/clk.h>
    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/slab.h>
    @@ -59,6 +60,7 @@ struct tegra_rtc_info {
    struct platform_device *pdev;
    struct rtc_device *rtc_dev;
    void __iomem *rtc_base; /* NULL if not initialized. */
    + struct clk *clk;
    int tegra_rtc_irq; /* alarm and periodic irq */
    spinlock_t tegra_rtc_lock;
    };
    @@ -326,6 +328,14 @@ static int __init tegra_rtc_probe(struct
    if (info->tegra_rtc_irq <= 0)
    return -EBUSY;

    + info->clk = devm_clk_get(&pdev->dev, NULL);
    + if (IS_ERR(info->clk))
    + return PTR_ERR(info->clk);
    +
    + ret = clk_prepare_enable(info->clk);
    + if (ret < 0)
    + return ret;
    +
    /* set context info. */
    info->pdev = pdev;
    spin_lock_init(&info->tegra_rtc_lock);
    @@ -346,7 +356,7 @@ static int __init tegra_rtc_probe(struct
    ret = PTR_ERR(info->rtc_dev);
    dev_err(&pdev->dev, "Unable to register device (err=%d).\n",
    ret);
    - return ret;
    + goto disable_clk;
    }

    ret = devm_request_irq(&pdev->dev, info->tegra_rtc_irq,
    @@ -356,12 +366,25 @@ static int __init tegra_rtc_probe(struct
    dev_err(&pdev->dev,
    "Unable to request interrupt for device (err=%d).\n",
    ret);
    - return ret;
    + goto disable_clk;
    }

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

    return 0;
    +
    +disable_clk:
    + clk_disable_unprepare(info->clk);
    + return ret;
    +}
    +
    +static int tegra_rtc_remove(struct platform_device *pdev)
    +{
    + struct tegra_rtc_info *info = platform_get_drvdata(pdev);
    +
    + clk_disable_unprepare(info->clk);
    +
    + return 0;
    }

    #ifdef CONFIG_PM_SLEEP
    @@ -413,6 +436,7 @@ static void tegra_rtc_shutdown(struct pl

    MODULE_ALIAS("platform:tegra_rtc");
    static struct platform_driver tegra_rtc_driver = {
    + .remove = tegra_rtc_remove,
    .shutdown = tegra_rtc_shutdown,
    .driver = {
    .name = "tegra_rtc",

    \
     
     \ /
      Last update: 2017-04-19 16:46    [W:4.228 / U:0.024 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site