Re: [PATCH v3 10/12] watchdog: s3c2410: Support separate source clock
From: Guenter Roeck
Date: Wed Nov 17 2021 - 08:37:09 EST
On Sun, Nov 07, 2021 at 10:29:41PM +0200, Sam Protsenko wrote:
> Right now all devices supported in the driver have the single clock: it
> acts simultaneously as a bus clock (providing register interface
> clocking) and source clock (driving watchdog counter). Some newer Exynos
> chips, like Exynos850, have two separate clocks for that. In that case
> two clocks will be passed to the driver from the resource provider, e.g.
> Device Tree. Provide necessary infrastructure to support that case:
> - use source clock's rate for all timer related calculations
> - use bus clock to gate/ungate the register interface
>
> All devices that use the single clock are kept intact: if only one clock
> is passed from Device Tree, it will be used for both purposes as before.
>
> Signed-off-by: Sam Protsenko <semen.protsenko@xxxxxxxxxx>
Reviewed-by: Guenter Roeck <linux@xxxxxxxxxxxx>
> ---
> Changes in v3:
> - Removed has_src_clk field: clk framework can handle NULL clk; added
> s3c2410wdt_get_freq() function instead, to figure out which clock to
> use for getting the rate
>
> Changes in v2:
> - Reworded commit message to be more formal
> - Used separate "has_src_clk" trait to tell if source clock is present
> - Renamed clock variables to match their purpose
> - Removed caching source clock rate, obtaining it in place each time
> instead
> - Renamed err labels for more consistency
>
> drivers/watchdog/s3c2410_wdt.c | 56 +++++++++++++++++++++++++---------
> 1 file changed, 41 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c
> index f211be8bf976..f31bc765a8a5 100644
> --- a/drivers/watchdog/s3c2410_wdt.c
> +++ b/drivers/watchdog/s3c2410_wdt.c
> @@ -153,7 +153,8 @@ struct s3c2410_wdt_variant {
>
> struct s3c2410_wdt {
> struct device *dev;
> - struct clk *clock;
> + struct clk *bus_clk; /* for register interface (PCLK) */
> + struct clk *src_clk; /* for WDT counter */
> void __iomem *reg_base;
> unsigned int count;
> spinlock_t lock;
> @@ -231,9 +232,14 @@ MODULE_DEVICE_TABLE(platform, s3c2410_wdt_ids);
>
> /* functions */
>
> -static inline unsigned int s3c2410wdt_max_timeout(struct clk *clock)
> +static inline unsigned long s3c2410wdt_get_freq(struct s3c2410_wdt *wdt)
> {
> - unsigned long freq = clk_get_rate(clock);
> + return clk_get_rate(wdt->src_clk ? wdt->src_clk : wdt->bus_clk);
> +}
> +
> +static inline unsigned int s3c2410wdt_max_timeout(struct s3c2410_wdt *wdt)
> +{
> + const unsigned long freq = s3c2410wdt_get_freq(wdt);
>
> return S3C2410_WTCNT_MAXCNT / (freq / (S3C2410_WTCON_PRESCALE_MAX + 1)
> / S3C2410_WTCON_MAXDIV);
> @@ -383,7 +389,7 @@ static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd,
> unsigned int timeout)
> {
> struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
> - unsigned long freq = clk_get_rate(wdt->clock);
> + unsigned long freq = s3c2410wdt_get_freq(wdt);
> unsigned int count;
> unsigned int divisor = 1;
> unsigned long wtcon;
> @@ -632,26 +638,42 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
> goto err;
> }
>
> - wdt->clock = devm_clk_get(dev, "watchdog");
> - if (IS_ERR(wdt->clock)) {
> - dev_err(dev, "failed to find watchdog clock source\n");
> - ret = PTR_ERR(wdt->clock);
> + wdt->bus_clk = devm_clk_get(dev, "watchdog");
> + if (IS_ERR(wdt->bus_clk)) {
> + dev_err(dev, "failed to find bus clock\n");
> + ret = PTR_ERR(wdt->bus_clk);
> goto err;
> }
>
> - ret = clk_prepare_enable(wdt->clock);
> + ret = clk_prepare_enable(wdt->bus_clk);
> if (ret < 0) {
> - dev_err(dev, "failed to enable clock\n");
> + dev_err(dev, "failed to enable bus clock\n");
> return ret;
> }
>
> + /*
> + * "watchdog_src" clock is optional; if it's not present -- just skip it
> + * and use "watchdog" clock as both bus and source clock.
> + */
> + wdt->src_clk = devm_clk_get(dev, "watchdog_src");
> + if (!IS_ERR(wdt->src_clk)) {
> + ret = clk_prepare_enable(wdt->src_clk);
> + if (ret < 0) {
> + dev_err(dev, "failed to enable source clock\n");
> + ret = PTR_ERR(wdt->src_clk);
> + goto err_bus_clk;
> + }
> + } else {
> + wdt->src_clk = NULL;
> + }
> +
> wdt->wdt_device.min_timeout = 1;
> - wdt->wdt_device.max_timeout = s3c2410wdt_max_timeout(wdt->clock);
> + wdt->wdt_device.max_timeout = s3c2410wdt_max_timeout(wdt);
>
> ret = s3c2410wdt_cpufreq_register(wdt);
> if (ret < 0) {
> dev_err(dev, "failed to register cpufreq\n");
> - goto err_clk;
> + goto err_src_clk;
> }
>
> watchdog_set_drvdata(&wdt->wdt_device, wdt);
> @@ -729,8 +751,11 @@ static int s3c2410wdt_probe(struct platform_device *pdev)
> err_cpufreq:
> s3c2410wdt_cpufreq_deregister(wdt);
>
> - err_clk:
> - clk_disable_unprepare(wdt->clock);
> + err_src_clk:
> + clk_disable_unprepare(wdt->src_clk);
> +
> + err_bus_clk:
> + clk_disable_unprepare(wdt->bus_clk);
>
> err:
> return ret;
> @@ -749,7 +774,8 @@ static int s3c2410wdt_remove(struct platform_device *dev)
>
> s3c2410wdt_cpufreq_deregister(wdt);
>
> - clk_disable_unprepare(wdt->clock);
> + clk_disable_unprepare(wdt->src_clk);
> + clk_disable_unprepare(wdt->bus_clk);
>
> return 0;
> }
> --
> 2.30.2
>