/* Enable a clock with no locking, enabling parent clocks as needed. */ static int local_clk_enable_nolock(unsigned id) { struct clk_local *clk = &soc_clk_local_tbl[id]; int rc = 0; if (clk->type == RESET) return -EPERM; if (!clk->count) { rc = local_vote_sys_vdd(clk->current_freq->sys_vdd); if (rc) goto err_vdd; if (clk->parent != C(NONE)) { rc = local_clk_enable_nolock(clk->parent); if (rc) goto err_par; } rc = local_src_enable(clk->current_freq->src); if (rc) goto err_src; local_clk_enable_reg(id); } clk->count++; return rc; err_src: if (clk->parent != C(NONE)) rc = local_clk_disable_nolock(clk->parent); err_par: local_unvote_sys_vdd(clk->current_freq->sys_vdd); err_vdd: return rc; }
/* Enable a clock and any related power rail. */ int local_clk_enable(unsigned id) { int rc = 0; unsigned long flags; spin_lock_irqsave(&local_clock_reg_lock, flags); rc = local_clk_enable_nolock(id); spin_unlock_irqrestore(&local_clock_reg_lock, flags); return rc; }
/* Enable a clock with no locking, enabling parent clocks as needed. */ static int local_clk_enable_nolock(unsigned id) { struct clk_local *clk = &soc_clk_local_tbl[id]; int rc = 0; if (clk->type == RESET) return -EPERM; if (!clk->count) { rc = local_vote_sys_vdd(clk->current_freq->sys_vdd); if (rc) goto err_vdd; if (clk->parent != C(NONE)) { rc = local_clk_enable_nolock(clk->parent); if (rc) goto err_par; } rc = local_src_enable(clk->current_freq->src); if (rc) goto err_src; local_clk_enable_reg(id); /* * With remote rail control, the remote processor might modify * the clock control register when the rail is enabled/disabled. * Enable the rail inside the lock to protect against this. */ rc = soc_set_pwr_rail(id, 1); if (rc) goto err_pwr; } clk->count++; return rc; err_pwr: local_clk_disable_reg(id); err_src: if (clk->parent != C(NONE)) rc = local_clk_disable_nolock(clk->parent); err_par: local_unvote_sys_vdd(clk->current_freq->sys_vdd); err_vdd: return rc; }
/* Disable a clock with no locking, disabling unused parents, too. */ static int local_clk_disable_nolock(unsigned id) { struct clk_local *clk = &soc_clk_local_tbl[id]; int rc = 0; if (clk->count > 0) clk->count--; else { pr_warning("%s: Reference counts are incorrect for clock %d!\n", __func__, id); return rc; } if (clk->count == 0) { soc_set_pwr_rail(id, 0); local_clk_disable_reg(id); rc = local_src_disable(clk->current_freq->src); if (rc) goto err_src; if (clk->parent != C(NONE)) { rc = local_clk_disable_nolock(clk->parent); if (rc) goto err_par; } rc = local_unvote_sys_vdd(clk->current_freq->sys_vdd); if (rc) goto err_vdd; } return rc; err_vdd: if (clk->parent != C(NONE)) rc = local_clk_enable_nolock(clk->parent); err_par: local_src_enable(clk->current_freq->src); err_src: local_clk_enable_reg(id); clk->count++; return rc; }
/* Enable a clock and any related power rail. */ int local_clk_enable(unsigned id) { int rc = 0; unsigned long flags; spin_lock_irqsave(&local_clock_reg_lock, flags); rc = local_clk_enable_nolock(id); if (rc) goto unlock; /* * With remote rail control, the remote processor might modify * the clock control register when the rail is enabled/disabled. * Enable the rail inside the lock to protect against this. */ rc = soc_set_pwr_rail(id, 1); if (rc) local_clk_disable_nolock(id); unlock: spin_unlock_irqrestore(&local_clock_reg_lock, flags); return rc; }