From patchwork Fri Dec 6 09:48:04 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arun Kumar K X-Patchwork-Id: 3294241 Return-Path: X-Original-To: patchwork-linux-samsung-soc@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 4385CC0D4A for ; Fri, 6 Dec 2013 09:49:23 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id B89C8204EC for ; Fri, 6 Dec 2013 09:49:21 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 1C118204EB for ; Fri, 6 Dec 2013 09:49:20 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756757Ab3LFJtT (ORCPT ); Fri, 6 Dec 2013 04:49:19 -0500 Received: from mail-pb0-f49.google.com ([209.85.160.49]:53972 "EHLO mail-pb0-f49.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754029Ab3LFJtQ (ORCPT ); Fri, 6 Dec 2013 04:49:16 -0500 Received: by mail-pb0-f49.google.com with SMTP id jt11so775604pbb.36 for ; Fri, 06 Dec 2013 01:49:16 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references; bh=11njEP64hKXsGWzw9I9hB3DLIwjAKLt1H8awbsjvUo0=; b=VodRbjEHAauFRwsWMczF3QSOEdDnmjPTeiKhjuj4+iposuaEkEEJXzCziqwZuBQkYE qQFE0bDV9ndJgTvPxRRarfEXXbhMXhFRm7faax0fl/3exHHXf4NXgjgPzgYgtNNCoTJN 4ozEQIr2ZMv0Vhz+c2MTe1DzFoidHuy5haVxrFVXGD2x+uMjMd08gF5K6XUj+M6ImUqj rNwobxcNa7K45fE987u3PWwUa5QklM6ypZIoclC2njsbzZYrj+N/5Ky3C1de70ikvKA9 j58pvioWhlq7jeoi5eLTIW8Ls7UtDKXcSKO2XwkzhjRxAN2Svj88pII8P6PViJdDBi+e NWRQ== X-Received: by 10.68.133.229 with SMTP id pf5mr3126917pbb.115.1386323356319; Fri, 06 Dec 2013 01:49:16 -0800 (PST) Received: from localhost.localdomain ([115.113.119.130]) by mx.google.com with ESMTPSA id yg3sm175098479pab.16.2013.12.06.01.49.11 for (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Fri, 06 Dec 2013 01:49:15 -0800 (PST) From: Arun Kumar K To: linux-samsung-soc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, cpufreq@vger.kernel.org Cc: mturquette@linaro.org, kgene.kim@samsung.com, rjw@rjwysocki.net, viresh.kumar@linaro.org, tomasz.figa@gmail.com, arjun.kv@samsung.com, abrestic@chromium.org, arunkk.samsung@gmail.com Subject: [PATCH 3/3] cpufreq: exynos: Add exynos5420 cpufreq driver Date: Fri, 6 Dec 2013 15:18:04 +0530 Message-Id: <1386323284-15646-4-git-send-email-arun.kk@samsung.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1386323284-15646-1-git-send-email-arun.kk@samsung.com> References: <1386323284-15646-1-git-send-email-arun.kk@samsung.com> Sender: linux-samsung-soc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-samsung-soc@vger.kernel.org X-Spam-Status: No, score=-7.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI,RP_MATCHES_RCVD,T_DKIM_INVALID,UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: "Arjun.K.V" The patch adds cpufreq driver for exynos5420. Signed-off-by: Arjun.K.V Signed-off-by: Andrew Bresticker Signed-off-by: Arun Kumar K --- drivers/cpufreq/Kconfig.arm | 11 ++ drivers/cpufreq/Makefile | 1 + drivers/cpufreq/exynos-cpufreq.c | 2 + drivers/cpufreq/exynos-cpufreq.h | 8 + drivers/cpufreq/exynos5420-cpufreq.c | 346 ++++++++++++++++++++++++++++++++++ 5 files changed, 368 insertions(+) create mode 100644 drivers/cpufreq/exynos5420-cpufreq.c diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index ce52ed9..96769fe 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -51,6 +51,17 @@ config ARM_EXYNOS5250_CPUFREQ If in doubt, say N. +config ARM_EXYNOS5420_CPUFREQ + bool "SAMSUNG EXYNOS5420" + depends on SOC_EXYNOS5420 + default y + select ARM_EXYNOS_CPUFREQ + help + This adds the CPUFreq driver for Samsung EXYNOS5420 + SoC. + + If in doubt, say N. + config ARM_EXYNOS5440_CPUFREQ bool "SAMSUNG EXYNOS5440" depends on SOC_EXYNOS5440 diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 7494565..802c251 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_ARM_EXYNOS_CPUFREQ) += exynos-cpufreq.o obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o obj-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o obj-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o +obj-$(CONFIG_ARM_EXYNOS5420_CPUFREQ) += exynos5420-cpufreq.o obj-$(CONFIG_ARM_EXYNOS5440_CPUFREQ) += exynos5440-cpufreq.o obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o obj-$(CONFIG_ARM_IMX6Q_CPUFREQ) += imx6q-cpufreq.o diff --git a/drivers/cpufreq/exynos-cpufreq.c b/drivers/cpufreq/exynos-cpufreq.c index f3c2287..ac556eb 100644 --- a/drivers/cpufreq/exynos-cpufreq.c +++ b/drivers/cpufreq/exynos-cpufreq.c @@ -246,6 +246,8 @@ static int __init exynos_cpufreq_init(void) ret = exynos4x12_cpufreq_init(exynos_info); else if (soc_is_exynos5250()) ret = exynos5250_cpufreq_init(exynos_info); + else if (soc_is_exynos5420()) + ret = exynos5420_cpufreq_init(exynos_info); else return 0; diff --git a/drivers/cpufreq/exynos-cpufreq.h b/drivers/cpufreq/exynos-cpufreq.h index 7f25cee..d2f3d1e 100644 --- a/drivers/cpufreq/exynos-cpufreq.h +++ b/drivers/cpufreq/exynos-cpufreq.h @@ -67,3 +67,11 @@ static inline int exynos5250_cpufreq_init(struct exynos_dvfs_info *info) return -EOPNOTSUPP; } #endif +#ifdef CONFIG_ARM_EXYNOS5420_CPUFREQ +extern int exynos5420_cpufreq_init(struct exynos_dvfs_info *); +#else +static inline int exynos5420_cpufreq_init(struct exynos_dvfs_info *info) +{ + return -EOPNOTSUPP; +} +#endif diff --git a/drivers/cpufreq/exynos5420-cpufreq.c b/drivers/cpufreq/exynos5420-cpufreq.c new file mode 100644 index 0000000..728ce71 --- /dev/null +++ b/drivers/cpufreq/exynos5420-cpufreq.c @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * EXYNOS5420 - CPU frequency scaling support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "exynos-cpufreq.h" + +#define CPUFREQ_NUM_LEVELS (L18 + 1) +#define EXYNOS5_CLKDIV_STATCPU0_MASK 0x11111111 +#define EXYNOS5_CLKDIV_STATCPU1_MASK 0x111 + +static struct clk *mout_cpu; +static struct clk *mout_mspll_cpu; +static struct clk *mout_apll; +static struct clk *fout_apll; +static struct clk *fout_spll; + +struct cpufreq_clkdiv { + unsigned int index; + unsigned int clkdiv; + unsigned int clkdiv1; +}; + +static unsigned int exynos5420_volt_table[CPUFREQ_NUM_LEVELS]; +static struct cpufreq_frequency_table exynos5420_freq_table[] = { + {L0, 2000 * 1000}, + {L1, 1900 * 1000}, + {L2, 1800 * 1000}, + {L3, 1700 * 1000}, + {L4, 1600 * 1000}, + {L5, 1500 * 1000}, + {L6, 1400 * 1000}, + {L7, 1300 * 1000}, + {L8, 1200 * 1000}, + {L9, 1100 * 1000}, + {L10, 1000 * 1000}, + {L11, 900 * 1000}, + {L12, 800 * 1000}, + {L13, 700 * 1000}, + {L14, 600 * 1000}, + {L15, 500 * 1000}, + {L16, 400 * 1000}, + {L17, 300 * 1000}, + {L18, 200 * 1000}, + {0, CPUFREQ_TABLE_END}, +}; + +static struct cpufreq_clkdiv exynos5420_clkdiv_table[CPUFREQ_NUM_LEVELS]; + +static unsigned int clkdiv_cpu0_5420[CPUFREQ_NUM_LEVELS][7] = { + /* + * Clock divider values for {CPUD, ATB, PCLK_DBG, APLL, ARM2} + */ + { 2, 7, 7, 3, 0 }, /* ARM L0: 2.0GHz */ + { 2, 7, 7, 3, 0 }, /* ARM L1: 1.9GHz */ + { 2, 7, 7, 3, 0 }, /* ARM L2: 1.8GHz */ + { 2, 7, 7, 3, 0 }, /* ARM L3: 1.7GHz */ + { 2, 7, 7, 3, 0 }, /* ARM L4: 1.6GHz */ + { 2, 7, 7, 3, 0 }, /* ARM L5: 1.5GHz */ + { 2, 7, 7, 3, 0 }, /* ARM L6: 1.4GHz */ + { 2, 7, 7, 3, 0 }, /* ARM L7: 1.3GHz */ + { 2, 7, 7, 3, 0 }, /* ARM L8: 1.2GHz */ + { 2, 7, 7, 3, 0 }, /* ARM L9: 1.1GHz */ + { 2, 6, 6, 3, 0 }, /* ARM L10: 1GHz */ + { 2, 6, 6, 3, 0 }, /* ARM L11: 900MHz */ + { 2, 5, 5, 3, 0 }, /* ARM L12: 800MHz */ + { 2, 5, 5, 3, 0 }, /* ARM L13: 700MHz */ + { 2, 4, 4, 3, 0 }, /* ARM L14: 600MHz */ + { 2, 3, 3, 3, 0 }, /* ARM L15: 500MHz */ + { 2, 3, 3, 3, 0 }, /* ARM L16: 400MHz */ + { 2, 3, 3, 3, 0 }, /* ARM L17: 300MHz */ + { 2, 3, 3, 3, 0 }, /* ARM L18: 200MHz */ +}; + +unsigned int clkdiv_cpu1_5420[CPUFREQ_NUM_LEVELS][2] = { + /* Clock divider values for { copy, HPM } */ + { 7, 7 }, /* ARM L0: 2.0GHz */ + { 7, 7 }, /* ARM L1: 1.9GHz */ + { 7, 7 }, /* ARM L2: 1.8GHz */ + { 7, 7 }, /* ARM L3: 1.7GHz */ + { 7, 7 }, /* ARM L4: 1.6GHz */ + { 7, 7 }, /* ARM L5: 1.5GHz */ + { 7, 7 }, /* ARM L6: 1.4GHz */ + { 7, 7 }, /* ARM L7: 1.3GHz */ + { 7, 7 }, /* ARM L8: 1.2GHz */ + { 7, 7 }, /* ARM L9: 1.1GHz */ + { 7, 7 }, /* ARM L10: 1GHz */ + { 7, 7 }, /* ARM L11: 900MHz */ + { 7, 7 }, /* ARM L12: 800MHz */ + { 7, 7 }, /* ARM L13: 700MHz */ + { 7, 7 }, /* ARM L14: 600MHz */ + { 7, 7 }, /* ARM L15: 500MHz */ + { 7, 7 }, /* ARM L16: 400MHz */ + { 7, 7 }, /* ARM L17: 300MHz */ + { 7, 7 }, /* ARM L18: 200MHz */ +}; + +/* + * Default ASV table + */ +static const unsigned int asv_voltage_5420[CPUFREQ_NUM_LEVELS] = { + 1300000, /* L0 2000 */ + 1300000, /* L1 1900 */ + 1200000, /* L2 1800 */ + 1200000, /* L3 1700 */ + 1200000, /* L4 1600 */ + 1175000, /* L5 1500 */ + 1150000, /* L6 1400 */ + 1125000, /* L7 1300 */ + 1100000, /* L8 1200 */ + 1075000, /* L9 1100 */ + 1050000, /* L10 1000 */ + 1000000, /* L11 900 */ + 950000, /* L12 800 */ + 925000, /* L13 700 */ + 900000, /* L14 600 */ + 900000, /* L15 500 */ + 900000, /* L16 400 */ + 900000, /* L17 300 */ + 900000, /* L18 200 */ +}; + +static void exynos5420_set_clkdiv(unsigned int div_index) +{ + unsigned int tmp; + + /* Change Divider - CPU0 for CMU_CPU */ + tmp = exynos5420_clkdiv_table[div_index].clkdiv; + __raw_writel(tmp, EXYNOS5_CLKDIV_CPU0); + + do { + cpu_relax(); + tmp = __raw_readl(EXYNOS5_CLKDIV_STATCPU0); + } while (tmp & EXYNOS5_CLKDIV_STATCPU0_MASK); + pr_debug("DIV_CPU0[0x%x]\n", __raw_readl(EXYNOS5_CLKDIV_CPU0)); + + /* Change Divider - CPU1 for CMU_CPU */ + tmp = exynos5420_clkdiv_table[div_index].clkdiv1; + __raw_writel(tmp, EXYNOS5_CLKDIV_CPU1); + + do { + cpu_relax(); + tmp = __raw_readl(EXYNOS5_CLKDIV_STATCPU1); + } while (tmp & EXYNOS5_CLKDIV_STATCPU1_MASK); + pr_debug("DIV_CPU1[0x%x]\n", __raw_readl(EXYNOS5_CLKDIV_CPU1)); +} + +static void exynos5420_set_apll(unsigned int new_index, + unsigned int old_index) +{ + unsigned int tmp; + unsigned long rate; + + clk_prepare_enable(fout_spll); + /* 1. MUX_CORE_SEL = MOUT_MSPLL; ARMCLK uses MOUT_MSPLL for lock time */ + if (clk_set_parent(mout_cpu, mout_mspll_cpu)) + pr_err(KERN_ERR "Unable to set parent of clock mout_cpu\n"); + + do { + cpu_relax(); + tmp = __raw_readl(EXYNOS5_CLKMUX_STATCPU); + tmp &= EXYNOS5_CLKMUX_STATCPU_MUXCORE_MASK; + } while (tmp != (0x2 << EXYNOS5_CLKSRC_CPU_MUXCORE_SHIFT)); + + /* 2. Set APLL rate */ + rate = exynos5420_freq_table[new_index].frequency * 1000; + if (clk_set_rate(fout_apll, rate)) + pr_err("Unable to change apll rate to %lu\n", rate); + + /* 3. MUX_CORE_SEL = APLL */ + if (clk_set_parent(mout_cpu, mout_apll)) + pr_err("Unable to set parent of clock mout_cpu\n"); + + do { + cpu_relax(); + tmp = __raw_readl(EXYNOS5_CLKMUX_STATCPU); + tmp &= EXYNOS5_CLKMUX_STATCPU_MUXCORE_MASK; + } while (tmp != (0x1 << EXYNOS5_CLKSRC_CPU_MUXCORE_SHIFT)); + + clk_disable_unprepare(fout_spll); +} + +static bool exynos5420_pms_change(unsigned int old_index, + unsigned int new_index) +{ + /* + * The Exynos cpufreq driver uses this to determine if it can + * avoid changing the CPU voltage and re-parenting the CPU clock + * while chaning the PLL rate. Because we're using CCF to change + * the PLL rate, we no longer have access to the PLL divider table, + * so we can't tell whether or not we can take the fast path from + * here and must always take the slow path. Since this only affects + * a few transitions, there should hopefully be no impact on + * performance. + */ + return (old_index != new_index); +} +static void exynos5420_set_frequency(unsigned int old_index, + unsigned int new_index) +{ + if (old_index > new_index) { + /* 1. Change the system clock divider values */ + exynos5420_set_clkdiv(new_index); + /* 2. Change the apll rate */ + exynos5420_set_apll(new_index, old_index); + } else if (old_index < new_index) { + /* 1. Change the apll rate */ + exynos5420_set_apll(new_index, old_index); + /* 2. Change the system clock divider values */ + exynos5420_set_clkdiv(new_index); + } +} + +static void __init set_volt_table(void) +{ + unsigned int i; + unsigned int asv_volt = 0; + + for (i = 0; i < CPUFREQ_NUM_LEVELS; i++) { +#ifdef CONFIG_ARM_EXYNOS5420_ASV + asv_volt = get_match_volt + (ID_ARM, exynos5420_freq_table[i].frequency); +#endif + if (!asv_volt) + exynos5420_volt_table[i] = asv_voltage_5420[i]; + else + exynos5420_volt_table[i] = asv_volt; + pr_debug("CPUFREQ of CA15 L%d : %d uV\n", i, + exynos5420_volt_table[i]); + } + for (i = L0; i < L2; i++) + exynos5420_freq_table[i].frequency = CPUFREQ_ENTRY_INVALID; + for (i = L14; i <= L18; i++) + exynos5420_freq_table[i].frequency = CPUFREQ_ENTRY_INVALID; +} + +int exynos5420_cpufreq_init(struct exynos_dvfs_info *info) +{ + int i; + unsigned int tmp; + unsigned long rate; + struct clk *sclk_spll; + + set_volt_table(); + + mout_cpu = clk_get(NULL, "mout_cpu"); + if (IS_ERR(mout_cpu)) + return PTR_ERR(mout_cpu); + + mout_mspll_cpu = clk_get(NULL, "mout_mspll_cpu"); + if (IS_ERR(mout_mspll_cpu)) + goto err_mout_mspll_cpu; + + sclk_spll = clk_get(NULL, "mout_spll"); + if (IS_ERR(sclk_spll)) + goto err_sclk_spll; + clk_set_parent(mout_mspll_cpu, sclk_spll); + clk_put(sclk_spll); + + fout_spll = clk_get(NULL, "fout_spll"); + if (IS_ERR(fout_spll)) + goto err_fout_spll; + + rate = clk_get_rate(mout_mspll_cpu) / 1000; + + mout_apll = clk_get(NULL, "mout_apll"); + if (IS_ERR(mout_apll)) + goto err_mout_apll; + + fout_apll = clk_get(NULL, "fout_apll"); + if (IS_ERR(fout_apll)) + goto err_fout_apll; + + for (i = L0; i < CPUFREQ_NUM_LEVELS; i++) { + exynos5420_clkdiv_table[i].index = i; + + tmp = __raw_readl(EXYNOS5_CLKDIV_CPU0); + tmp &= ~(EXYNOS5_CLKDIV_CPU0_CPUD_MASK | + EXYNOS5_CLKDIV_CPU0_ATB_MASK | + EXYNOS5_CLKDIV_CPU0_PCLKDBG_MASK | + EXYNOS5_CLKDIV_CPU0_APLL_MASK | + EXYNOS5_CLKDIV_CPU0_CORE2_MASK); + tmp |= ((clkdiv_cpu0_5420[i][0] << + EXYNOS5_CLKDIV_CPU0_CPUD_SHIFT) | + (clkdiv_cpu0_5420[i][1] << + EXYNOS5_CLKDIV_CPU0_ATB_SHIFT) | + (clkdiv_cpu0_5420[i][2] << + EXYNOS5_CLKDIV_CPU0_PCLKDBG_SHIFT) | + (clkdiv_cpu0_5420[i][3] << + EXYNOS5_CLKDIV_CPU0_APLL_SHIFT) | + (clkdiv_cpu0_5420[i][4] << + EXYNOS5_CLKDIV_CPU0_CORE2_SHIFT)); + + exynos5420_clkdiv_table[i].clkdiv = tmp; + + tmp = __raw_readl(EXYNOS5_CLKDIV_CPU1); + tmp &= ~(EXYNOS5_CLKDIV_CPU1_COPY_MASK | + EXYNOS5_CLKDIV_CPU1_HPM_MASK); + tmp |= ((clkdiv_cpu1_5420[i][0] << + EXYNOS5_CLKDIV_CPU1_COPY_SHIFT) | + (clkdiv_cpu1_5420[i][1] << + EXYNOS5_CLKDIV_CPU1_HPM_SHIFT)); + + exynos5420_clkdiv_table[i].clkdiv1 = tmp; + } + + info->mpll_freq_khz = rate; + info->pll_safe_idx = L12; + info->cpu_clk = fout_apll; + pr_debug("fout_apll[%lu]\n", clk_get_rate(fout_apll)); + info->volt_table = exynos5420_volt_table; + info->freq_table = exynos5420_freq_table; + info->set_freq = exynos5420_set_frequency; + info->need_apll_change = exynos5420_pms_change; + + return 0; + +err_fout_apll: + clk_put(fout_apll); +err_mout_apll: + clk_put(mout_apll); +err_fout_spll: + clk_put(fout_spll); +err_sclk_spll: + clk_put(sclk_spll); +err_mout_mspll_cpu: + clk_put(mout_mspll_cpu); + + pr_err("%s: failed initialization\n", __func__); + return -EINVAL; +}