/* Perform a hardware rate measurement for a given clock. FOR DEBUG USE ONLY: Measurements take ~15 ms! */ signed soc_clk_measure_rate(unsigned id) { struct clk_local *t = &soc_clk_local_tbl[id]; unsigned long flags; uint32_t regval, prph_web_reg_old; uint64_t raw_count_short, raw_count_full; signed ret; if (t->test_vector == 0) return -EPERM; spin_lock_irqsave(&local_clock_reg_lock, flags); /* Program test vector. */ if (t->test_vector <= 0xFF) { /* Select CLK_TEST_2 */ writel(0x4D40, CLK_TEST_BASE_REG); writel(t->test_vector, CLK_TEST_2_BASE_REG); } else writel(t->test_vector, CLK_TEST_BASE_REG); /* Enable TCXO4 clock branch and root. */ prph_web_reg_old = readl(PRPH_WEB_NS_BASE_REG); regval = prph_web_reg_old | B(9) | B(11); local_src_enable(TCXO); writel(regval, PRPH_WEB_NS_BASE_REG); /* * The ring oscillator counter will not reset if the measured clock * is not running. To detect this, run a short measurement before * the full measurement. If the raw results of the two are the same * then the clock must be off. */ /* Run a short measurement. (~1 ms) */ raw_count_short = run_measurement(0x1000); /* Run a full measurement. (~14 ms) */ raw_count_full = run_measurement(0x10000); /* Disable TCXO4 clock branch and root. */ writel(prph_web_reg_old, PRPH_WEB_NS_BASE_REG); local_src_disable(TCXO); /* Return 0 if the clock is off. */ if (raw_count_full == raw_count_short) ret = 0; else { /* Compute rate in Hz. */ raw_count_full = ((raw_count_full * 10) + 15) * 4800000; do_div(raw_count_full, ((0x10000 * 10) + 35)); ret = (signed)raw_count_full; } spin_unlock_irqrestore(&local_clock_reg_lock, flags); return ret; }
/* 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; }
static int apr_smd_probe(struct platform_device *pdev) { int dest; int clnt; if (pdev->id == APR_DEST_MODEM) { pr_aud_info("apr_tal:Modem Is Up\n"); dest = APR_DEST_MODEM; clnt = APR_CLIENT_VOICE; apr_svc_ch[APR_DL_SMD][dest][clnt].dest_state = 1; wake_up(&apr_svc_ch[APR_DL_SMD][dest][clnt].dest); } else if (pdev->id == APR_DEST_QDSP6) { pr_aud_info("apr_tal:Q6 Is Up\n"); local_src_disable(PLL_4); dest = APR_DEST_QDSP6; clnt = APR_CLIENT_AUDIO; apr_svc_ch[APR_DL_SMD][dest][clnt].dest_state = 1; wake_up(&apr_svc_ch[APR_DL_SMD][dest][clnt].dest); } else pr_aud_err("apr_tal:Invalid Dest Id: %d\n", pdev->id); return 0; }
static void remove_q6_proxy_votes(unsigned long data) { local_src_disable(PLL_4); }
/* Set a clock's frequency. */ static int _local_clk_set_rate(unsigned id, struct clk_freq_tbl *nf) { struct clk_local *clk = &soc_clk_local_tbl[id]; struct clk_freq_tbl *cf; const int32_t *chld = clk->children; int i, rc = 0; unsigned long flags; spin_lock_irqsave(&local_clock_reg_lock, flags); /* Check if frequency is actually changed. */ cf = clk->current_freq; if (nf == cf) goto release_lock; /* Disable branch if clock isn't dual-banked with a glitch-free MUX. */ if (clk->banked_mnd_masks == NULL) { /* Disable all branches to prevent glitches. */ for (i = 0; chld && chld[i] != C(NONE); i++) { struct clk_local *ch = &soc_clk_local_tbl[chld[i]]; /* Don't bother turning off if it is already off. * Checking ch->count is cheaper (cache) than reading * and writing to a register (uncached/unbuffered). */ if (ch->count) local_clk_disable_reg(chld[i]); } if (clk->count) local_clk_disable_reg(id); } if (clk->count) { /* Vote for voltage and source for new freq. */ rc = local_vote_sys_vdd(nf->sys_vdd); if (rc) goto sys_vdd_vote_failed; rc = local_src_enable(nf->src); if (rc) { local_unvote_sys_vdd(nf->sys_vdd); goto src_enable_failed; } } /* Perform clock-specific frequency switch operations. */ BUG_ON(!clk->set_rate); clk->set_rate(clk, nf); /* Release requirements of the old freq. */ if (clk->count) { local_src_disable(cf->src); local_unvote_sys_vdd(cf->sys_vdd); } /* Current freq must be updated before local_clk_enable_reg() * is called to make sure the MNCNTR_EN bit is set correctly. */ clk->current_freq = nf; src_enable_failed: sys_vdd_vote_failed: /* Enable any clocks that were disabled. */ if (clk->banked_mnd_masks == NULL) { if (clk->count) local_clk_enable_reg(id); /* Enable only branches that were ON before. */ for (i = 0; chld && chld[i] != C(NONE); i++) { struct clk_local *ch = &soc_clk_local_tbl[chld[i]]; if (ch->count) local_clk_enable_reg(chld[i]); } } release_lock: spin_unlock_irqrestore(&local_clock_reg_lock, flags); return rc; }