/* * Copyright (c) 2011-2012, 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "acpuclock.h" #define REG_CLKSEL_0 (MSM_APCS_GLB_BASE + 0x08) #define REG_CLKDIV_0 (MSM_APCS_GLB_BASE + 0x0C) #define REG_CLKSEL_1 (MSM_APCS_GLB_BASE + 0x10) #define REG_CLKDIV_1 (MSM_APCS_GLB_BASE + 0x14) #define REG_CLKOUTSEL (MSM_APCS_GLB_BASE + 0x18) #define MAX_VDD_MEM 1150000 enum clk_src { SRC_CXO, SRC_PLL0, SRC_PLL8, SRC_PLL9, NUM_SRC, }; struct src_clock { struct clk *clk; const char *name; }; static struct src_clock clocks[NUM_SRC] = { [SRC_PLL0].name = "pll0", [SRC_PLL8].name = "pll8", [SRC_PLL9].name = "pll9", }; struct clkctl_acpu_speed { bool use_for_scaling; unsigned int khz; int src; unsigned int src_sel; unsigned int src_div; unsigned int vdd_cpu; unsigned int vdd_mem; unsigned int bw_level; }; struct acpuclk_state { struct mutex lock; struct clkctl_acpu_speed *current_speed; }; static struct acpuclk_state drv_state = { .current_speed = &(struct clkctl_acpu_speed){ 0 }, }; /* Instantaneous bandwidth requests in MB/s. */ #define BW_MBPS(_bw) \ { \ .vectors = &(struct msm_bus_vectors){ \ .src = MSM_BUS_MASTER_AMPSS_M0, \ .dst = MSM_BUS_SLAVE_EBI_CH0, \ .ib = (_bw) * 1000000UL, \ .ab = 0, \ }, \ .num_paths = 1, \ } static struct msm_bus_paths bw_level_tbl[] = { [0] = BW_MBPS(152), /* At least 19 MHz on bus. */ [1] = BW_MBPS(368), /* At least 46 MHz on bus. */ [2] = BW_MBPS(552), /* At least 69 MHz on bus. */ [3] = BW_MBPS(736), /* At least 92 MHz on bus. */ [4] = BW_MBPS(1064), /* At least 133 MHz on bus. */ [5] = BW_MBPS(1536), /* At least 192 MHz on bus. */ }; static struct msm_bus_scale_pdata bus_client_pdata = { .usecase = bw_level_tbl, .num_usecases = ARRAY_SIZE(bw_level_tbl), .active_only = 1, .name = "acpuclock", }; static uint32_t bus_perf_client; static struct clkctl_acpu_speed acpu_freq_tbl[] = { { 0, 19200, SRC_CXO, 0, 0, RPM_VREG_CORNER_LOW, 1050000, 0 }, { 1, 138000, SRC_PLL0, 6, 1, RPM_VREG_CORNER_LOW, 1050000, 2 }, { 1, 276000, SRC_PLL0, 6, 0, RPM_VREG_CORNER_NOMINAL, 1050000, 2 }, { 1, 384000, SRC_PLL8, 3, 0, RPM_VREG_CORNER_HIGH, 1150000, 4 }, /* The row below may be changed at runtime depending on hw rev. */ { 1, 440000, SRC_PLL9, 2, 0, RPM_VREG_CORNER_HIGH, 1150000, 4 }, { 0 } }; static void select_clk_source_div(struct clkctl_acpu_speed *s) { static void * __iomem const sel_reg[] = {REG_CLKSEL_0, REG_CLKSEL_1}; static void * __iomem const div_reg[] = {REG_CLKDIV_0, REG_CLKDIV_1}; uint32_t next_bank; next_bank = !(readl_relaxed(REG_CLKOUTSEL) & 1); writel_relaxed(s->src_sel, sel_reg[next_bank]); writel_relaxed(s->src_div, div_reg[next_bank]); writel_relaxed(next_bank, REG_CLKOUTSEL); /* Wait for switch to complete. */ mb(); udelay(1); } /* Update the bus bandwidth request. */ static void set_bus_bw(unsigned int bw) { int ret; /* Bounds check. */ if (bw >= ARRAY_SIZE(bw_level_tbl)) { 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 per-cpu voltage increases. */ static int increase_vdd(unsigned int vdd_cpu, unsigned int vdd_mem) { int rc = 0; /* * Increase vdd_mem active-set before vdd_cpu. * vdd_mem should be >= vdd_cpu. */ rc = rpm_vreg_set_voltage(RPM_VREG_ID_PM8018_L9, RPM_VREG_VOTER1, vdd_mem, MAX_VDD_MEM, 0); if (rc) { pr_err("vdd_mem increase failed (%d)\n", rc); return rc; } rc = rpm_vreg_set_voltage(RPM_VREG_ID_PM8018_VDD_DIG_CORNER, RPM_VREG_VOTER1, vdd_cpu, RPM_VREG_CORNER_HIGH, 0); 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 = rpm_vreg_set_voltage(RPM_VREG_ID_PM8018_VDD_DIG_CORNER, RPM_VREG_VOTER1, vdd_cpu, RPM_VREG_CORNER_HIGH, 0); if (ret) { pr_err("vdd_cpu decrease failed (%d)\n", ret); return; } /* * Decrease vdd_mem active-set after vdd_cpu. * vdd_mem should be >= vdd_cpu. */ ret = rpm_vreg_set_voltage(RPM_VREG_ID_PM8018_L9, RPM_VREG_VOTER1, vdd_mem, MAX_VDD_MEM, 0); if (ret) pr_err("vdd_mem decrease failed (%d)\n", ret); } static int acpuclk_9615_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(&drv_state.lock); strt_s = drv_state.current_speed; /* Return early if rate didn't change. */ if (rate == strt_s->khz) goto out; /* Find target frequency. */ for (tgt_s = acpu_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 || reason == SETRATE_INIT) && (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. */ clk_enable(clocks[tgt_s->src].clk); select_clk_source_div(tgt_s); clk_disable(clocks[strt_s->src].clk); drv_state.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) decrease_vdd(tgt_s->vdd_cpu, tgt_s->vdd_mem); out: if (reason == SETRATE_CPUFREQ) mutex_unlock(&drv_state.lock); return rc; } static unsigned long acpuclk_9615_get_rate(int cpu) { return drv_state.current_speed->khz; } #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 acpu_freq_tbl. */ for (i = 0; acpu_freq_tbl[i].khz != 0 && freq_cnt < ARRAY_SIZE(freq_table); i++) { if (acpu_freq_tbl[i].use_for_scaling) { freq_table[freq_cnt].index = freq_cnt; freq_table[freq_cnt].frequency = acpu_freq_tbl[i].khz; freq_cnt++; } } /* freq_table not big enough to store all usable freqs. */ BUG_ON(acpu_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. */ cpufreq_frequency_table_get_attr(freq_table, smp_processor_id()); } #else static void __init cpufreq_table_init(void) {} #endif static struct acpuclk_data acpuclk_9615_data = { .set_rate = acpuclk_9615_set_rate, .get_rate = acpuclk_9615_get_rate, .power_collapse_khz = 19200, .wait_for_irq_khz = 19200, }; static int __init acpuclk_9615_probe(struct platform_device *pdev) { unsigned long max_cpu_khz = 0; int i; mutex_init(&drv_state.lock); bus_perf_client = msm_bus_scale_register_client(&bus_client_pdata); if (!bus_perf_client) { pr_err("Unable to register bus client\n"); BUG(); } for (i = 0; i < NUM_SRC; i++) { if (clocks[i].name) { clocks[i].clk = clk_get_sys("acpu", clocks[i].name); BUG_ON(IS_ERR(clocks[i].clk)); /* * Prepare the PLLs because we enable/disable them * in atomic context during power collapse/restore. */ BUG_ON(clk_prepare(clocks[i].clk)); } } /* Determine the rate of PLL9 and fixup tables accordingly */ if (clk_get_rate(clocks[SRC_PLL9].clk) == 550000000) { for (i = 0; i < ARRAY_SIZE(acpu_freq_tbl); i++) if (acpu_freq_tbl[i].src == SRC_PLL9) { acpu_freq_tbl[i].khz = 550000; acpu_freq_tbl[i].bw_level = 5; } } /* Improve boot time by ramping up CPU immediately. */ for (i = 0; acpu_freq_tbl[i].khz != 0; i++) max_cpu_khz = acpu_freq_tbl[i].khz; acpuclk_9615_set_rate(smp_processor_id(), max_cpu_khz, SETRATE_INIT); acpuclk_register(&acpuclk_9615_data); cpufreq_table_init(); return 0; } static struct platform_driver acpuclk_9615_driver = { .driver = { .name = "acpuclk-9615", .owner = THIS_MODULE, }, }; static int __init acpuclk_9615_init(void) { return platform_driver_probe(&acpuclk_9615_driver, acpuclk_9615_probe); } device_initcall(acpuclk_9615_init);