diff --git a/drivers/clk/msm/clock-local2.c b/drivers/clk/msm/clock-local2.c index 56ee6d0d7c89..bfa2147ad42c 100644 --- a/drivers/clk/msm/clock-local2.c +++ b/drivers/clk/msm/clock-local2.c @@ -36,6 +36,8 @@ /* For clock without halt checking, wait this long after enables/disables. */ #define HALT_CHECK_DELAY_US 500 +#define RCG_FORCE_DISABLE_DELAY_US 100 + /* * When updating an RCG configuration, check the update bit up to this number * number of times (with a 1 us delay in between) before continuing. @@ -105,14 +107,18 @@ struct div_map { */ static void rcg_update_config(struct rcg_clk *rcg) { - u32 cmd_rcgr_regval, count; + u32 cmd_rcgr_regval; + int count = UPDATE_CHECK_MAX_LOOPS; + + if (rcg->non_local_control_timeout) + count = rcg->non_local_control_timeout; cmd_rcgr_regval = readl_relaxed(CMD_RCGR_REG(rcg)); cmd_rcgr_regval |= CMD_RCGR_CONFIG_UPDATE_BIT; writel_relaxed(cmd_rcgr_regval, CMD_RCGR_REG(rcg)); /* Wait for update to take effect */ - for (count = UPDATE_CHECK_MAX_LOOPS; count > 0; count--) { + for (; count > 0; count--) { if (!(readl_relaxed(CMD_RCGR_REG(rcg)) & CMD_RCGR_CONFIG_UPDATE_BIT)) return; @@ -124,10 +130,13 @@ static void rcg_update_config(struct rcg_clk *rcg) static void rcg_on_check(struct rcg_clk *rcg) { - int count; + int count = UPDATE_CHECK_MAX_LOOPS; + + if (rcg->non_local_control_timeout) + count = rcg->non_local_control_timeout; /* Wait for RCG to turn on */ - for (count = UPDATE_CHECK_MAX_LOOPS; count > 0; count--) { + for (; count > 0; count--) { if (!(readl_relaxed(CMD_RCGR_REG(rcg)) & CMD_RCGR_ROOT_STATUS_BIT)) return; @@ -211,6 +220,8 @@ static void rcg_clear_force_enable(struct rcg_clk *rcg) cmd_rcgr_regval &= ~CMD_RCGR_ROOT_ENABLE_BIT; writel_relaxed(cmd_rcgr_regval, CMD_RCGR_REG(rcg)); spin_unlock_irqrestore(&local_clock_reg_lock, flags); + /* Add a delay of 100usecs to let the RCG disable */ + udelay(RCG_FORCE_DISABLE_DELAY_US); } static int rcg_clk_enable(struct clk *c) @@ -291,25 +302,20 @@ static int rcg_clk_set_rate(struct clk *c, unsigned long rate) BUG_ON(!rcg->set_rate); - /* - * Perform clock-specific frequency switch operations. - * - * For RCGs with non_local_children set to true: - * If this RCG has at least one branch that is controlled by another - * execution entity, ensure that the enable/disable and mux switch - * are staggered. - */ - if (!rcg->non_local_children) { - rcg->set_rate(rcg, nf); - } else if (c->count) { + /* Perform clock-specific frequency switch operations. */ + if ((rcg->non_local_children && c->count) || + rcg->non_local_control_timeout) { /* - * Force enable the RCG here since there could be a disable - * call happening between pre_reparent and set_rate. + * Force enable the RCG here since the clock could be disabled + * between pre_reparent and set_rate. */ rcg_set_force_enable(rcg); rcg->set_rate(rcg, nf); rcg_clear_force_enable(rcg); + } else if (!rcg->non_local_children) { + rcg->set_rate(rcg, nf); } + /* * If non_local_children is set and the RCG is not enabled, * the following operations switch parent in software and cache diff --git a/include/soc/qcom/clock-local2.h b/include/soc/qcom/clock-local2.h index a22c3422104c..81da257fbbbe 100644 --- a/include/soc/qcom/clock-local2.h +++ b/include/soc/qcom/clock-local2.h @@ -57,6 +57,9 @@ struct clk_freq_tbl { * @current_freq: current RCG frequency * @c: generic clock data * @non_local_children: set if RCG has at least one branch owned by a diff EE + * @non_local_control_timeout: configurable RCG timeout needed when all RCG + * children can be controlled by an entity outside of + HLOS. * @force_enable_rcgr: set if RCG needs to be force enabled/disabled during * power sequence * @base: pointer to base address of ioremapped registers. @@ -71,6 +74,7 @@ struct rcg_clk { struct clk c; bool non_local_children; + int non_local_control_timeout; bool force_enable_rcgr; void *const __iomem *base; };