123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499 |
- /*
- * Copyright (c) 2013, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- #define pr_fmt(fmt) "%s: " fmt, __func__
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/io.h>
- #include <linux/delay.h>
- #include <linux/mutex.h>
- #include <linux/spinlock.h>
- #include <linux/errno.h>
- #include <linux/cpufreq.h>
- #include <linux/clk.h>
- #include <linux/platform_device.h>
- #include <linux/iopoll.h>
- #include <mach/board.h>
- #include <mach/msm_iomap.h>
- #include <mach/msm_bus.h>
- #include <mach/msm_bus_board.h>
- #include <mach/rpm-regulator.h>
- #include <mach/clk-provider.h>
- #include <mach/rpm-regulator-smd.h>
- #include "acpuclock.h"
- #include "acpuclock-cortex.h"
- #define POLL_INTERVAL_US 1
- #define APCS_RCG_UPDATE_TIMEOUT_US 20
- static struct acpuclk_drv_data *priv;
- static uint32_t bus_perf_client;
- #ifdef CONFIG_SEC_DEBUG_VERBOSE_SUMMARY_HTML
- extern int cpu_frequency[CONFIG_NR_CPUS];
- extern int cpu_volt[CONFIG_NR_CPUS];
- extern char cpu_state[CONFIG_NR_CPUS][30];
- #endif
- /* Update the bus bandwidth request. */
- static void set_bus_bw(unsigned int bw)
- {
- int ret;
- if (bw >= priv->bus_scale->num_usecases) {
- pr_err("invalid bandwidth request (%d)\n", bw);
- return;
- }
- /* Update bandwidth if request has changed. This may sleep. */
- ret = msm_bus_scale_client_update_request(bus_perf_client, bw);
- if (ret)
- pr_err("bandwidth request failed (%d)\n", ret);
- return;
- }
- /* Apply any voltage increases. */
- static int increase_vdd(unsigned int vdd_cpu, unsigned int vdd_mem)
- {
- int rc = 0;
- if (priv->vdd_mem) {
- /*
- * Increase vdd_mem before vdd_cpu. vdd_mem should
- * be >= vdd_cpu.
- */
- rc = regulator_set_voltage(priv->vdd_mem, vdd_mem,
- priv->vdd_max_mem);
- if (rc) {
- pr_err("vdd_mem increase failed (%d)\n", rc);
- return rc;
- }
- }
- rc = regulator_set_voltage(priv->vdd_cpu, vdd_cpu, priv->vdd_max_cpu);
- if (rc)
- pr_err("vdd_cpu increase failed (%d)\n", rc);
- return rc;
- }
- /* Apply any per-cpu voltage decreases. */
- static void decrease_vdd(unsigned int vdd_cpu, unsigned int vdd_mem)
- {
- int ret;
- /* Update CPU voltage. */
- ret = regulator_set_voltage(priv->vdd_cpu, vdd_cpu, priv->vdd_max_cpu);
- if (ret) {
- pr_err("vdd_cpu decrease failed (%d)\n", ret);
- return;
- }
- if (!priv->vdd_mem)
- return;
- /* Decrease vdd_mem after vdd_cpu. vdd_mem should be >= vdd_cpu. */
- ret = regulator_set_voltage(priv->vdd_mem, vdd_mem, priv->vdd_max_mem);
- if (ret)
- pr_err("vdd_mem decrease failed (%d)\n", ret);
- }
- static void select_clk_source_div(struct acpuclk_drv_data *drv_data,
- struct clkctl_acpu_speed *s)
- {
- u32 regval, rc, src_div;
- void __iomem *apcs_rcg_config = drv_data->apcs_rcg_config;
- void __iomem *apcs_rcg_cmd = drv_data->apcs_rcg_cmd;
- struct acpuclk_reg_data *r = &drv_data->reg_data;
- src_div = s->src_div ? ((2 * s->src_div) - 1) : s->src_div;
- regval = readl_relaxed(apcs_rcg_config);
- regval &= ~r->cfg_src_mask;
- regval |= s->src_sel << r->cfg_src_shift;
- regval &= ~r->cfg_div_mask;
- regval |= src_div << r->cfg_div_shift;
- writel_relaxed(regval, apcs_rcg_config);
- /* Update the configuration */
- regval = readl_relaxed(apcs_rcg_cmd);
- regval |= r->update_mask;
- writel_relaxed(regval, apcs_rcg_cmd);
- /* Wait for the update to take effect */
- rc = readl_poll_timeout_noirq(apcs_rcg_cmd, regval,
- !(regval & r->poll_mask),
- POLL_INTERVAL_US,
- APCS_RCG_UPDATE_TIMEOUT_US);
- if (rc)
- pr_warn("acpu rcg didn't update its configuration\n");
- }
- static struct clkctl_acpu_speed *__init find_cur_cpu_level(void)
- {
- struct clkctl_acpu_speed *f, *max = priv->freq_tbl;
- void __iomem *apcs_rcg_config = priv->apcs_rcg_config;
- struct acpuclk_reg_data *r = &priv->reg_data;
- u32 regval, div, src;
- unsigned long rate;
- struct clk *parent;
- regval = readl_relaxed(apcs_rcg_config);
- src = regval & r->cfg_src_mask;
- src >>= r->cfg_src_shift;
- div = regval & r->cfg_div_mask;
- div >>= r->cfg_div_shift;
- /* No support for half-integer dividers */
- div = div > 1 ? (div + 1) / 2 : 0;
- for (f = priv->freq_tbl; f->khz; f++) {
- if (f->use_for_scaling)
- max = f;
- if (f->src_sel != src || f->src_div != div)
- continue;
- parent = priv->src_clocks[f->src].clk;
- rate = parent->rate / (div ? div : 1);
- if (f->khz * 1000 == rate)
- break;
- }
- if (f->khz)
- return f;
- pr_err("CPUs are running at an unknown rate. Defaulting to %u KHz.\n",
- max->khz);
- /* Change to a safe frequency */
- select_clk_source_div(priv, priv->freq_tbl);
- /* Default to largest frequency */
- return max;
- }
- static int set_speed_atomic(struct clkctl_acpu_speed *tgt_s)
- {
- struct clkctl_acpu_speed *strt_s = priv->current_speed;
- struct clk *strt = priv->src_clocks[strt_s->src].clk;
- struct clk *tgt = priv->src_clocks[tgt_s->src].clk;
- int rc = 0;
- WARN(strt_s->src == ACPUPLL && tgt_s->src == ACPUPLL,
- "can't reprogram ACPUPLL during atomic context\n");
- rc = clk_enable(tgt);
- if (rc)
- return rc;
- select_clk_source_div(priv, tgt_s);
- clk_disable(strt);
- return rc;
- }
- static int set_speed(struct clkctl_acpu_speed *tgt_s)
- {
- int rc = 0;
- unsigned int div = tgt_s->src_div ? tgt_s->src_div : 1;
- unsigned int tgt_freq_hz = tgt_s->khz * 1000 * div;
- struct clkctl_acpu_speed *strt_s = priv->current_speed;
- struct clkctl_acpu_speed *cxo_s = &priv->freq_tbl[0];
- struct clk *strt = priv->src_clocks[strt_s->src].clk;
- struct clk *tgt = priv->src_clocks[tgt_s->src].clk;
- if (strt_s->src == ACPUPLL && tgt_s->src == ACPUPLL) {
- /* Switch to another always on src */
- select_clk_source_div(priv, cxo_s);
- /* Re-program acpu pll */
- clk_disable_unprepare(tgt);
- rc = clk_set_rate(tgt, tgt_freq_hz);
- if (rc)
- pr_err("Failed to set ACPU PLL to %u\n", tgt_freq_hz);
- BUG_ON(clk_prepare_enable(tgt));
- /* Switch back to acpu pll */
- select_clk_source_div(priv, tgt_s);
- } else if (strt_s->src != ACPUPLL && tgt_s->src == ACPUPLL) {
- rc = clk_set_rate(tgt, tgt_freq_hz);
- if (rc) {
- pr_err("Failed to set ACPU PLL to %u\n", tgt_freq_hz);
- return rc;
- }
- rc = clk_prepare_enable(tgt);
- if (rc) {
- pr_err("ACPU PLL enable failed\n");
- return rc;
- }
- select_clk_source_div(priv, tgt_s);
- clk_disable_unprepare(strt);
- } else {
- rc = clk_prepare_enable(tgt);
- if (rc) {
- pr_err("%s enable failed\n",
- priv->src_clocks[tgt_s->src].name);
- return rc;
- }
- select_clk_source_div(priv, tgt_s);
- clk_disable_unprepare(strt);
- }
- return rc;
- }
- static int acpuclk_cortex_set_rate(int cpu, unsigned long rate,
- enum setrate_reason reason)
- {
- struct clkctl_acpu_speed *tgt_s, *strt_s;
- int rc = 0;
- if (reason == SETRATE_CPUFREQ)
- mutex_lock(&priv->lock);
- strt_s = priv->current_speed;
- /* Return early if rate didn't change */
- if (rate == strt_s->khz && reason != SETRATE_INIT)
- goto out;
- /* Find target frequency */
- for (tgt_s = priv->freq_tbl; tgt_s->khz != 0; tgt_s++)
- if (tgt_s->khz == rate)
- break;
- if (tgt_s->khz == 0) {
- rc = -EINVAL;
- goto out;
- }
- /* Increase VDD levels if needed */
- if ((reason == SETRATE_CPUFREQ)
- && (tgt_s->khz > strt_s->khz)) {
- rc = increase_vdd(tgt_s->vdd_cpu, tgt_s->vdd_mem);
- if (rc)
- goto out;
- }
- pr_debug("Switching from CPU rate %u KHz -> %u KHz\n",
- strt_s->khz, tgt_s->khz);
- /* Switch CPU speed. Flag indicates atomic context */
- if (reason == SETRATE_CPUFREQ || reason == SETRATE_INIT)
- rc = set_speed(tgt_s);
- else
- rc = set_speed_atomic(tgt_s);
- if (rc)
- goto out;
- priv->current_speed = tgt_s;
- pr_debug("CPU speed change complete\n");
- /* Nothing else to do for SWFI or power-collapse. */
- if (reason == SETRATE_SWFI || reason == SETRATE_PC)
- goto out;
- /* Update bus bandwith request */
- set_bus_bw(tgt_s->bw_level);
- /* Drop VDD levels if we can. */
- if (tgt_s->khz < strt_s->khz || reason == SETRATE_INIT)
- decrease_vdd(tgt_s->vdd_cpu, tgt_s->vdd_mem);
- #ifdef CONFIG_SEC_DEBUG_VERBOSE_SUMMARY_HTML
- /* add to sec debug variable */
- /* save the voltage level and freq for master core*/
- if(cpu_online(cpu) && cpu_active(cpu))
- strncpy(cpu_state[cpu], "Online", ARRAY_SIZE(cpu_state[cpu]));
- else if(!cpu_online(cpu) && cpu_active(cpu))
- strncpy(cpu_state[cpu], "Migrating", ARRAY_SIZE(cpu_state[cpu]));
- else if(!cpu_online(cpu) && !cpu_active(cpu))
- strncpy(cpu_state[cpu], "Down", ARRAY_SIZE(cpu_state[cpu]));
- else
- strncpy(cpu_state[cpu], "On/NotActive", ARRAY_SIZE(cpu_state[cpu]));
- cpu_frequency[cpu] = priv->current_speed->khz;
- cpu_volt[cpu] = priv->current_speed->vdd_cpu;
- #endif
- out:
- if (reason == SETRATE_CPUFREQ)
- mutex_unlock(&priv->lock);
- return rc;
- }
- static unsigned long acpuclk_cortex_get_rate(int cpu)
- {
- return priv->current_speed->khz;
- }
- #ifdef CONFIG_SEC_DEBUG_VERBOSE_SUMMARY_HTML
- static unsigned int acpuclk_cortex_get_voltage(int cpu)
- {
- return priv->current_speed->vdd_cpu;
- }
- #endif
- #ifdef CONFIG_CPU_FREQ_MSM
- static struct cpufreq_frequency_table freq_table[30];
- static void __init cpufreq_table_init(void)
- {
- int i, freq_cnt = 0;
- /* Construct the freq_table tables from priv->freq_tbl. */
- for (i = 0; priv->freq_tbl[i].khz != 0
- && freq_cnt < ARRAY_SIZE(freq_table) - 1; i++) {
- if (!priv->freq_tbl[i].use_for_scaling)
- continue;
- freq_table[freq_cnt].index = freq_cnt;
- freq_table[freq_cnt].frequency = priv->freq_tbl[i].khz;
- freq_cnt++;
- }
- /* freq_table not big enough to store all usable freqs. */
- BUG_ON(priv->freq_tbl[i].khz != 0);
- freq_table[freq_cnt].index = freq_cnt;
- freq_table[freq_cnt].frequency = CPUFREQ_TABLE_END;
- pr_info("CPU: %d scaling frequencies supported.\n", freq_cnt);
- /* Register table with CPUFreq. */
- for_each_possible_cpu(i)
- cpufreq_frequency_table_get_attr(freq_table, i);
- }
- #else
- static void __init cpufreq_table_init(void) {}
- #endif
- static struct acpuclk_data acpuclk_cortex_data = {
- .set_rate = acpuclk_cortex_set_rate,
- .get_rate = acpuclk_cortex_get_rate,
- #ifdef CONFIG_SEC_DEBUG_VERBOSE_SUMMARY_HTML
- .get_voltage = acpuclk_cortex_get_voltage,
- #endif
- };
- void __init get_speed_bin(void __iomem *base, struct bin_info *bin)
- {
- u32 pte_efuse, redundant_sel;
- pte_efuse = readl_relaxed(base);
- redundant_sel = (pte_efuse >> 24) & 0x7;
- bin->speed = pte_efuse & 0x7;
- if (redundant_sel == 1)
- bin->speed = (pte_efuse >> 27) & 0x7;
- bin->speed_valid = !!(pte_efuse & BIT(3));
- }
- static struct clkctl_acpu_speed *__init select_freq_plan(void)
- {
- struct bin_info bin;
- if (!priv->pte_efuse_base)
- return priv->freq_tbl;
- get_speed_bin(priv->pte_efuse_base, &bin);
- if (bin.speed_valid) {
- pr_info("SPEED BIN: %d\n", bin.speed);
- } else {
- bin.speed = 0;
- pr_warn("SPEED BIN: Defaulting to %d\n",
- bin.speed);
- }
- return priv->pvs_tables[bin.speed];
- }
- int __init acpuclk_cortex_init(struct platform_device *pdev,
- struct acpuclk_drv_data *data)
- {
- int rc;
- int parent;
- priv = data;
- mutex_init(&priv->lock);
- acpuclk_cortex_data.power_collapse_khz = priv->wait_for_irq_khz;
- acpuclk_cortex_data.wait_for_irq_khz = priv->wait_for_irq_khz;
- priv->freq_tbl = select_freq_plan();
- if (!priv->freq_tbl) {
- pr_err("Invalid freq table selected\n");
- BUG();
- }
- bus_perf_client = msm_bus_scale_register_client(priv->bus_scale);
- if (!bus_perf_client) {
- pr_err("Unable to register bus client\n");
- BUG();
- }
- /* Initialize regulators */
- rc = increase_vdd(priv->vdd_max_cpu, priv->vdd_max_mem);
- if (rc)
- return rc;
- if (priv->vdd_mem) {
- rc = regulator_enable(priv->vdd_mem);
- if (rc) {
- dev_err(&pdev->dev, "regulator_enable for mem failed\n");
- return rc;
- }
- }
- rc = regulator_enable(priv->vdd_cpu);
- if (rc) {
- dev_err(&pdev->dev, "regulator_enable for cpu failed\n");
- return rc;
- }
- priv->current_speed = find_cur_cpu_level();
- parent = priv->current_speed->src;
- rc = clk_prepare_enable(priv->src_clocks[parent].clk);
- if (rc) {
- dev_err(&pdev->dev, "handoff: prepare_enable failed\n");
- return rc;
- }
- rc = acpuclk_cortex_set_rate(0, priv->current_speed->khz, SETRATE_INIT);
- if (rc) {
- dev_err(&pdev->dev, "handoff: set rate failed\n");
- return rc;
- }
- acpuclk_register(&acpuclk_cortex_data);
- cpufreq_table_init();
- return 0;
- }
|