/* 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;
}
Exemple #4
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;
}