/* 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;
}
示例#2
0
/* 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;
}
static int reset_q6_trusted(void)
{
	int ret;

	ret = local_src_enable(PLL_4);
	if (ret)
		return ret;

	return auth_and_reset_trusted(PAS_Q6);
}
static int reset_q6_untrusted(void)
{
	int ret;
	u32 reg;

	ret = local_src_enable(PLL_4);
	if (ret)
		goto err;

	/* Put Q6 into reset */
	reg = readl(LCC_Q6_FUNC);
	reg |= Q6SS_SS_ARES | Q6SS_ISDB_ARES | Q6SS_ETM_ARES | STOP_CORE |
		CORE_ARES;
	reg &= ~CORE_GFM4_CLK_EN;
	writel(reg, LCC_Q6_FUNC);

	/* Wait 8 AHB cycles for Q6 to be fully reset (AHB = 1.5Mhz) */
	usleep_range(20, 30);

	/* Turn on Q6 memory */
	reg |= CORE_GFM4_CLK_EN | CORE_L1_MEM_CORE_EN | CORE_TCM_MEM_CORE_EN |
		CORE_TCM_MEM_PERPH_EN;
	writel(reg, LCC_Q6_FUNC);

	/* Turn on Q6 core clocks and take core out of reset */
	reg &= ~(CLAMP_IO | Q6SS_SS_ARES | Q6SS_ISDB_ARES | Q6SS_ETM_ARES |
			CORE_ARES);
	writel(reg, LCC_Q6_FUNC);

	/* Wait for clocks to be enabled */
	dsb();
	/* Program boot address */
	writel((q6_start >> 12) & 0xFFFFF, QDSP6SS_RST_EVB);

	writel(Q6_STRAP_TCM_CONFIG | Q6_STRAP_TCM_BASE,
			QDSP6SS_STRAP_TCM);
	writel(Q6_STRAP_AHB_UPPER | Q6_STRAP_AHB_LOWER,
			QDSP6SS_STRAP_AHB);

	/* Wait for addresses to be programmed before starting Q6 */
	dsb();

	/* Start Q6 instruction execution */
	reg &= ~STOP_CORE;
	writel(reg, LCC_Q6_FUNC);

	return 0;

err:
	return ret;
}
/* 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;
}
示例#7
0
static int reset_q6_untrusted(void)
{
#if defined (CONFIG_USA_MODEL_SGH_I727)
	u32 reg;
    int ret; 

    ret = local_src_enable(PLL_4); 
    if (ret) 
        return ret; 

	printk(KERN_INFO "%s: enter\n", __func__);
	make_q6_proxy_votes();

     /*get the S3B voltage*/ 
         pr_info("%s: PMIC 8901 S3 voltage=%d\n", __func__, pm8901_smps3_get_voltage()); 
	/* Put Q6 into reset */
	reg = readl(LCC_Q6_FUNC);
	reg |= Q6SS_SS_ARES | Q6SS_ISDB_ARES | Q6SS_ETM_ARES | STOP_CORE |
		CORE_ARES;
	reg &= ~CORE_GFM4_CLK_EN;
	writel(reg, LCC_Q6_FUNC);

	/* Wait 8 AHB cycles for Q6 to be fully reset (AHB = 1.5Mhz) */
	usleep_range(20, 30);

	/* Turn on Q6 memory */
	reg |= CORE_GFM4_CLK_EN | CORE_L1_MEM_CORE_EN | CORE_TCM_MEM_CORE_EN |
		CORE_TCM_MEM_PERPH_EN;
	writel(reg, LCC_Q6_FUNC);

	/* Turn on Q6 core clocks and take core out of reset */
	reg &= ~(CLAMP_IO | Q6SS_SS_ARES | Q6SS_ISDB_ARES | Q6SS_ETM_ARES |
			CORE_ARES);
	writel(reg, LCC_Q6_FUNC);

	/* Wait for clocks to be enabled */
	mb();
	/* Program boot address */
	writel((q6_start >> 12) & 0xFFFFF, QDSP6SS_RST_EVB);

	writel(Q6_STRAP_TCM_CONFIG | Q6_STRAP_TCM_BASE, QDSP6SS_STRAP_TCM);
	writel(Q6_STRAP_AHB_UPPER | Q6_STRAP_AHB_LOWER, QDSP6SS_STRAP_AHB);

	/* Wait for addresses to be programmed before starting Q6 */
	mb();

	/* Start Q6 instruction execution */
	reg &= ~STOP_CORE;
	writel(reg, LCC_Q6_FUNC);

     pr_info("%s: PMIC 8901 S3 voltage=%d\n", __func__, pm8901_smps3_get_voltage()); 
     qdsp_clock_register_read(); 

	printk(KERN_INFO "%s: exit\n", __func__);
	return 0;
	
#else
		u32 reg;
	printk(KERN_INFO "%s: enter\n", __func__);
	make_q6_proxy_votes();

	/* Put Q6 into reset */
	reg = readl(LCC_Q6_FUNC);
	reg |= Q6SS_SS_ARES | Q6SS_ISDB_ARES | Q6SS_ETM_ARES | STOP_CORE |
		CORE_ARES;
	reg &= ~CORE_GFM4_CLK_EN;
	writel(reg, LCC_Q6_FUNC);

	/* Wait 8 AHB cycles for Q6 to be fully reset (AHB = 1.5Mhz) */
	usleep_range(20, 30);

	/* Turn on Q6 memory */
	reg |= CORE_GFM4_CLK_EN | CORE_L1_MEM_CORE_EN | CORE_TCM_MEM_CORE_EN |
		CORE_TCM_MEM_PERPH_EN;
	writel(reg, LCC_Q6_FUNC);

	/* Turn on Q6 core clocks and take core out of reset */
	reg &= ~(CLAMP_IO | Q6SS_SS_ARES | Q6SS_ISDB_ARES | Q6SS_ETM_ARES |
			CORE_ARES);
	writel(reg, LCC_Q6_FUNC);

	/* Wait for clocks to be enabled */
	mb();
	/* Program boot address */
	writel((q6_start >> 12) & 0xFFFFF, QDSP6SS_RST_EVB);

	writel(Q6_STRAP_TCM_CONFIG | Q6_STRAP_TCM_BASE, QDSP6SS_STRAP_TCM);
	writel(Q6_STRAP_AHB_UPPER | Q6_STRAP_AHB_LOWER, QDSP6SS_STRAP_AHB);

	/* Wait for addresses to be programmed before starting Q6 */
	mb();

	/* Start Q6 instruction execution */
	reg &= ~STOP_CORE;
	writel(reg, LCC_Q6_FUNC);

	printk(KERN_INFO "%s: exit\n", __func__);
	return 0;
#endif
}
示例#8
0
static void make_q6_proxy_votes(void)
{
	/* Make proxy votes for Q6 and set up timer to disable it. */
	local_src_enable(PLL_4);
	mod_timer(&q6_timer, jiffies + msecs_to_jiffies(PROXY_VOTE_TIMEOUT));
}
/* 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;
}