From 0e35b075585186e22d268379f92852369fc0ea88 Mon Sep 17 00:00:00 2001 From: Ranjani Vaidyanathan Date: Thu, 9 May 2019 16:10:39 -0500 Subject: [PATCH] MLK-21690 clk: imx8qm/imx8qxp: Store clock rate in SCU clock driver Linux clock framework does not work correctly when the SCFW API returns the rate of the clock based on the HW power mode (LP vs ON). This causes issues in two ways: 1. Linux clock framework always calls clk_recalc() after set_rate() as a mechanism to get the actual rate set by the HW. This does not work on iMX8QM/iMX8QXP when the resource is in LP mode, as clocks are sourced from 24MHz when resource is in LP mode. 2. On iMX8QM/iMX8QXP multiple resources share the same power domain. As resources enter runtime idle, the iMXQM/QXP power domain driver in Linux sets the resources in LP mode which can result in the entire power domain entering LP mode. If a clock get_rate()/recalc_rate() is executed as part of probing a new driver, the rate returned by SCFW API will not match the rate requested by the driver. To fix the above two problems, store the clock rate in SCU driver and return the stored clock rate if the resource is in LP mode. Signed-off-by: Ranjani Vaidyanathan --- drivers/clk/imx/clk-divider-scu.c | 19 +++++++++++++++---- drivers/clk/imx/clk-gate-scu.c | 14 +++++++++++--- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/drivers/clk/imx/clk-divider-scu.c b/drivers/clk/imx/clk-divider-scu.c index b8ad05050ec0..1ea53b64c83a 100644 --- a/drivers/clk/imx/clk-divider-scu.c +++ b/drivers/clk/imx/clk-divider-scu.c @@ -27,6 +27,7 @@ struct clk_divider_scu { struct clk_divider div; sc_rsrc_t rsrc_id; sc_pm_clk_t clk_type; + uint32_t rate; }; struct clk_divider3_scu { @@ -53,14 +54,21 @@ static unsigned long clk_divider_scu_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clk_divider_scu *clk = to_clk_divider_scu(hw); - sc_err_t sci_err; + sc_err_t sci_err = SC_ERR_NONE; sc_pm_clock_rate_t rate = 0; + sc_pm_power_mode_t cur_mode; if (!ccm_ipc_handle) return 0; - sci_err = sc_pm_get_clock_rate(ccm_ipc_handle, clk->rsrc_id, - clk->clk_type, &rate); + sc_pm_get_resource_power_mode(ccm_ipc_handle, clk->rsrc_id, &cur_mode); + + if (cur_mode == SC_PM_PW_MODE_ON) { + sci_err = sc_pm_get_clock_rate(ccm_ipc_handle, clk->rsrc_id, + clk->clk_type, &rate); + clk->rate = rate; + } else + rate = clk->rate; return sci_err ? 0 : rate; } @@ -78,7 +86,7 @@ static int clk_divider_scu_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct clk_divider_scu *clk = to_clk_divider_scu(hw); - sc_err_t sci_err; + sc_err_t sci_err = SC_ERR_NONE; if (!ccm_ipc_handle) { return -EAGAIN; @@ -87,6 +95,9 @@ static int clk_divider_scu_set_rate(struct clk_hw *hw, unsigned long rate, sci_err = sc_pm_set_clock_rate(ccm_ipc_handle, clk->rsrc_id, clk->clk_type, (sc_pm_clock_rate_t *)&rate); + if (sci_err == SC_ERR_NONE) + clk->rate = rate; + return sci_err ? -EINVAL : 0; } diff --git a/drivers/clk/imx/clk-gate-scu.c b/drivers/clk/imx/clk-gate-scu.c index abcda5f9a952..7bd3bc6e32d4 100644 --- a/drivers/clk/imx/clk-gate-scu.c +++ b/drivers/clk/imx/clk-gate-scu.c @@ -47,6 +47,7 @@ struct clk_gate_scu { spinlock_t *lock; sc_rsrc_t rsrc_id; sc_pm_clk_t clk_type; + uint32_t rate; }; struct clk_gate2_scu { @@ -147,14 +148,21 @@ static unsigned long clk_gate_scu_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clk_gate_scu *clk = to_clk_gate_scu(hw); - sc_err_t sci_err; + sc_err_t sci_err = SC_ERR_NONE; sc_pm_clock_rate_t rate = 0; + sc_pm_power_mode_t cur_mode; if (!ccm_ipc_handle) return 0; - sci_err = sc_pm_get_clock_rate(ccm_ipc_handle, clk->rsrc_id, - clk->clk_type, &rate); + sc_pm_get_resource_power_mode(ccm_ipc_handle, clk->rsrc_id, &cur_mode); + + if (cur_mode == SC_PM_PW_MODE_ON) { + sci_err = sc_pm_get_clock_rate(ccm_ipc_handle, clk->rsrc_id, + clk->clk_type, &rate); + clk->rate = rate; + } else + rate = parent_rate; return sci_err ? 0 : rate; } -- 2.17.1