static void switch_sc_speed(int cpu, struct clkctl_acpu_speed *tgt_s) { struct clkctl_acpu_speed *strt_s = drv_state.current_speed[cpu]; if (strt_s->pll != ACPU_SCPLL && tgt_s->pll != ACPU_SCPLL) { select_clk_source_div(cpu, tgt_s); /* Select core source because target may be AFAB. */ select_core_source(cpu, tgt_s->core_src_sel); } else if (strt_s->pll != ACPU_SCPLL && tgt_s->pll == ACPU_SCPLL) { scpll_enable(cpu, tgt_s->sc_l_val); mb(); select_core_source(cpu, tgt_s->core_src_sel); } else if (strt_s->pll == ACPU_SCPLL && tgt_s->pll != ACPU_SCPLL) { select_clk_source_div(cpu, tgt_s); select_core_source(cpu, tgt_s->core_src_sel); mb(); scpll_disable(cpu); } else scpll_change_freq(cpu, tgt_s->sc_l_val); /* Update the driver state with the new clock freq */ drv_state.current_speed[cpu] = tgt_s; /* Adjust lpj for the new clock speed. */ /* XXX Temporary hack until udelay() is fixed to not care about lpj: * Update the global loops_per_jiffy variable used by udelay() to be * the max of the two CPUs' values. */ #ifdef CONFIG_SMP per_cpu(cpu_data, cpu).loops_per_jiffy = tgt_s->lpj; #endif loops_per_jiffy = tgt_s->lpj; for_each_online_cpu(cpu) if (drv_state.current_speed[cpu]->lpj > loops_per_jiffy) loops_per_jiffy = drv_state.current_speed[cpu]->lpj; }
static void l2_set_speed(struct clkctl_acpu_speed *tgt_s) { if (drv_state.current_l2_speed->l2_src_sel == 1 && tgt_s->l2_src_sel == 1) scpll_change_freq(L2, tgt_s->l2_l_val); else { if (tgt_s->l2_src_sel == 1) { scpll_enable(L2, tgt_s->l2_l_val); mb(); select_core_source(L2, tgt_s->l2_src_sel); } else { select_core_source(L2, tgt_s->l2_src_sel); mb(); scpll_disable(L2); } } drv_state.current_l2_speed = tgt_s; }
static void __init scpll_init(void) { uint32_t regval; dprintk("Initializing PLL 3\n"); /* Clear calibration LUT registers containing max frequency entry. * LUT registers are only writeable in debug mode. */ writel(SCPLL_DEBUG_FULL, SCPLL_DEBUG_ADDR); writel(0x0, SCPLL_LUT_A_HW_MAX_ADDR); writel(SCPLL_DEBUG_NONE, SCPLL_DEBUG_ADDR); /* Power-up SCPLL into standby mode. */ writel(SCPLL_STANDBY, SCPLL_CTL_ADDR); udelay(10); /* Calibrate the SCPLL to the maximum range supported by the h/w. We * might not use the full range of calibrated frequencies, but this * simplifies changes required for future increases in max CPU freq. */ regval = (L_VAL_SCPLL_CAL_MAX << 24) | (L_VAL_SCPLL_CAL_MIN << 16); writel(regval, SCPLL_CAL_ADDR); /* Start calibration */ writel(SCPLL_FULL_CAL, SCPLL_CTL_ADDR); /* Wait for proof that calibration has started before checking the * 'calibration done' bit in the status register. Waiting for the * LUT register we cleared to contain data accomplishes this. * This is required since the 'calibration done' bit takes time to * transition from 'done' to 'not done' when starting a calibration. */ while (readl(SCPLL_LUT_A_HW_MAX_ADDR) == 0) cpu_relax(); /* Wait for calibration to complete. */ while (readl(SCPLL_STATUS_ADDR) & 0x2) cpu_relax(); /* Power-down SCPLL */ scpll_enable(0, NULL); }
int acpuclk_set_rate(int cpu, unsigned long rate, enum setrate_reason reason) { struct clkctl_acpu_speed *tgt_s, *strt_s; int res, rc = 0; if (reason == SETRATE_CPUFREQ) mutex_lock(&drv_state.lock); strt_s = drv_state.current_speed; if (rate == strt_s->acpuclk_khz) goto out; for (tgt_s = acpu_freq_tbl; tgt_s->acpuclk_khz != 0; tgt_s++) if (tgt_s->acpuclk_khz == rate) break; if (tgt_s->acpuclk_khz == 0) { rc = -EINVAL; goto out; } if (reason == SETRATE_CPUFREQ) { /* Increase VDD if needed. */ if (tgt_s->vdd > strt_s->vdd) { rc = acpuclk_set_vdd_level(tgt_s->vdd); if (rc) { pr_err("Unable to increase ACPU vdd (%d)\n", rc); goto out; } } } dprintk("Switching from ACPU rate %u KHz -> %u KHz\n", strt_s->acpuclk_khz, tgt_s->acpuclk_khz); if (strt_s->pll != ACPU_PLL_3 && tgt_s->pll != ACPU_PLL_3) { select_clk_source(tgt_s); /* Select core source because target may be AXI. */ select_core_source(tgt_s->core_src_sel); } else if (strt_s->pll != ACPU_PLL_3 && tgt_s->pll == ACPU_PLL_3) { scpll_enable(1, tgt_s); mb(); select_core_source(tgt_s->core_src_sel); } else if (strt_s->pll == ACPU_PLL_3 && tgt_s->pll != ACPU_PLL_3) { select_clk_source(tgt_s); select_core_source(tgt_s->core_src_sel); mb(); scpll_enable(0, NULL); } else { scpll_change_freq(tgt_s->l_value); } /* Update the driver state with the new clock freq */ drv_state.current_speed = tgt_s; /* Re-adjust lpj for the new clock speed. */ loops_per_jiffy = tgt_s->lpj; /* Nothing else to do for SWFI. */ if (reason == SETRATE_SWFI) goto out; if (strt_s->ebi1clk_khz != tgt_s->ebi1clk_khz) { res = ebi1_clk_set_min_rate(CLKVOTE_ACPUCLK, tgt_s->ebi1clk_khz * 1000); if (res < 0) pr_warning("Setting EBI1/AXI min rate failed (%d)\n", res); } /* Nothing else to do for power collapse */ if (reason == SETRATE_PC) goto out; /* Drop VDD level if we can. */ if (tgt_s->vdd < strt_s->vdd) { res = acpuclk_set_vdd_level(tgt_s->vdd); if (res) pr_warning("Unable to drop ACPU vdd (%d)\n", res); } dprintk("ACPU speed change complete\n"); out: if (reason == SETRATE_CPUFREQ) mutex_unlock(&drv_state.lock); return rc; }