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