[PATCH v2 5/5] ARM: S5PV210: Initial CPUFREQ Support
MyungJoo Ham
myungjoo.ham at samsung.com
Sun Jul 18 22:50:28 EDT 2010
On Fri, Jul 16, 2010 at 8:02 PM, Maurus Cuelenaere
<mcuelenaere at gmail.com> wrote:
> Op 16-07-10 10:01, MyungJoo Ham schreef:
>> S5PV210 CPUFREQ Support.
>>
>> This CPUFREQ may work without PMIC's DVS support. However, it is not
>> as effective without DVS support as supposed. AVS is not supported in
>> this version.
>>
>> Note that CLK_SRC of some clocks including ARMCLK, G3D, G2D, MFC,
>> and ONEDRAM are modified directly without updating clksrc_src
>> representations of the clocks. Because CPUFREQ reverts the CLK_SRC
>> to the supposed values, this does not affect the consistency as long as
>> there are no other modules that modifies clock sources of ARMCLK, G3D,
>> G2D, MFC, and ONEDRAM (and only CPUFREQ should have the control). As
>> soon as clock framework is settled, we may update source clocks
>> (.parent field) of those clocks accordingly later.
>>
>> --
>> v2:
>> - Ramp-up delay is removed. (let regulator framework do the job)
>> - Provide proper max values for regulator_set_voltage
>> - Removed unneccesary #ifdef's.
>> - Removed unnecessary initialiser for CLK_OUT
>>
>> Signed-off-by: MyungJoo Ham <myungjoo.ham at samsung.com>
>> Signed-off-by: Kyungmin Park <kyungmin.park at samsung.com>
>> ---
>> arch/arm/Kconfig | 1 +
>> arch/arm/mach-s5pv210/Makefile | 3 +
>> arch/arm/mach-s5pv210/cpufreq-s5pv210.c | 776 +++++++++++++++++++++++++
>> arch/arm/mach-s5pv210/include/mach/cpu-freq.h | 50 ++
>> 4 files changed, 830 insertions(+), 0 deletions(-)
>> create mode 100644 arch/arm/mach-s5pv210/cpufreq-s5pv210.c
>> create mode 100644 arch/arm/mach-s5pv210/include/mach/cpu-freq.h
>>
(snip)
>> +
>> +struct s5pc11x_dvs_conf {
>> + const unsigned long lvl; /* DVFS level : L0,L1,L2,L3... */
>
> Why add this when you don't use it? You could use the level as the lookup key instead.
>
Thank you for pointing out. I'll correct them (including pr_* and
duplicated info messages) in the next version of patches.
>> + unsigned long arm_volt; /* uV */
>> + unsigned long int_volt; /* uV */
>> +};
>> +
>> +#ifdef CONFIG_CPU_S5PC110_EVT0_ERRATA
>> +const unsigned long arm_volt_max = 1350000;
>> +const unsigned long int_volt_max = 1250000;
>> +#else
>> +const unsigned long arm_volt_max = 1300000;
>> +const unsigned long int_volt_max = 1200000;
>> +#endif
>> +
>> +static struct s5pc11x_dvs_conf s5pv210_dvs_conf[] = {
>> +#ifdef CONFIG_CPU_S5PC110_EVT0_ERRATA
>> + {
>> + .lvl = L0,
>> + .arm_volt = 1300000,
>> + .int_volt = 1200000,
>> + }, {
>> + .lvl = L1,
>> + .arm_volt = 1250000,
>> + .int_volt = 1200000,
>> +
>> + }, {
>> + .lvl = L2,
>> + .arm_volt = 1250000,
>> + .int_volt = 1200000,
>> +
>> + }, {
>> + .lvl = L3,
>> + .arm_volt = 1250000,
>> + .int_volt = 1200000,
>> + }, {
>> + .lvl = L4,
>> + .arm_volt = 1250000,
>> + .int_volt = 1200000,
>> + },
>> +#else
>> + {
>> + .lvl = L0,
>> + .arm_volt = 1250000,
>> + .int_volt = 1100000,
>> + }, {
>> + .lvl = L1,
>> + .arm_volt = 1200000,
>> + .int_volt = 1100000,
>> +
>> + }, {
>> + .lvl = L2,
>> + .arm_volt = 1050000,
>> + .int_volt = 1100000,
>> +
>> + }, {
>> + .lvl = L3,
>> + .arm_volt = 950000,
>> + .int_volt = 1100000,
>> + }, {
>> + .lvl = L4,
>> + .arm_volt = 950000,
>> + .int_volt = 1000000,
>> + },
>> +#endif
>> +};
>> +
>> +static u32 clkdiv_val[5][11] = {
>> + /*{ APLL, A2M, HCLK_MSYS, PCLK_MSYS,
>> + * HCLK_DSYS, PCLK_DSYS, HCLK_PSYS, PCLK_PSYS, ONEDRAM,
>> + * MFC, G3D }
>> + */
>> + /* L0 : [1000/200/200/100][166/83][133/66][200/200] */
>> + {0, 4, 4, 1, 3, 1, 4, 1, 3, 0, 0},
>> + /* L1 : [800/200/200/100][166/83][133/66][200/200] */
>> + {0, 3, 3, 1, 3, 1, 4, 1, 3, 0, 0},
>> + /* L2 : [400/200/200/100][166/83][133/66][200/200] */
>> + {1, 3, 1, 1, 3, 1, 4, 1, 3, 0, 0},
>> + /* L3 : [200/200/200/100][166/83][133/66][200/200] */
>> + {3, 3, 0, 1, 3, 1, 4, 1, 3, 0, 0},
>> + /* L4 : [100/100/100/100][83/83][66/66][100/100] */
>> + {7, 7, 0, 0, 7, 0, 9, 0, 7, 0, 0},
>> +};
>> +
>> +static struct s3c_freq s5pv210_clk_info[] = {
>> + [L0] = { /* L0: 1GHz */
>> + .fclk = 1000000,
>> + .armclk = 1000000,
>> + .hclk_tns = 0,
>> + .hclk = 133000,
>> + .pclk = 66000,
>> + .hclk_msys = 200000,
>> + .pclk_msys = 100000,
>> + .hclk_dsys = 166750,
>> + .pclk_dsys = 83375,
>> + },
>> + [L1] = { /* L1: 800MHz */
>> + .fclk = 800000,
>> + .armclk = 800000,
>> + .hclk_tns = 0,
>> + .hclk = 133000,
>> + .pclk = 66000,
>> + .hclk_msys = 200000,
>> + .pclk_msys = 100000,
>> + .hclk_dsys = 166750,
>> + .pclk_dsys = 83375,
>> + },
>> + [L2] = { /* L2: 400MHz */
>> + .fclk = 800000,
>> + .armclk = 400000,
>> + .hclk_tns = 0,
>> + .hclk = 133000,
>> + .pclk = 66000,
>> + .hclk_msys = 200000,
>> + .pclk_msys = 100000,
>> + .hclk_dsys = 166750,
>> + .pclk_dsys = 83375,
>> + },
>> + [L3] = { /* L3: 200MHz */
>> + .fclk = 800000,
>> + .armclk = 200000,
>> + .hclk_tns = 0,
>> + .hclk = 133000,
>> + .pclk = 66000,
>> + .hclk_msys = 200000,
>> + .pclk_msys = 100000,
>> + .hclk_dsys = 166750,
>> + .pclk_dsys = 83375,
>> + },
>> + [L4] = { /* L4: 100MHz */
>> + .fclk = 800000,
>> + .armclk = 100000,
>> + .hclk_tns = 0,
>> + .hclk = 66000,
>> + .pclk = 66000,
>> + .hclk_msys = 100000,
>> + .pclk_msys = 100000,
>> + .hclk_dsys = 83375,
>> + .pclk_dsys = 83375,
>> + },
>> +};
>> +
>> +int s5pv210_verify_speed(struct cpufreq_policy *policy)
>> +{
>> +
>> + if (policy->cpu)
>> + return -EINVAL;
>> +
>> + return cpufreq_frequency_table_verify(policy, s5pv210_freq_table);
>> +}
>> +
>> +unsigned int s5pv210_getspeed(unsigned int cpu)
>> +{
>> + unsigned long rate;
>> +
>> + if (cpu)
>> + return 0;
>> +
>> + rate = clk_get_rate(mpu_clk) / KHZ_T;
>> +
>> + return rate;
>> +}
>> +
>> +static void s5pv210_target_APLL2MPLL(unsigned int index,
>> + unsigned int bus_speed_changing)
>> +{
>> + unsigned int reg;
>> +
>> + /* 1. Temporarily change divider for MFC and G3D
>> + * SCLKA2M(200/1=200)->(200/4=50)MHz
>> + **/
>> + reg = __raw_readl(S5P_CLK_DIV2);
>> + reg &= ~(S5P_CLKDIV2_G3D_MASK | S5P_CLKDIV2_MFC_MASK);
>> + reg |= (0x3 << S5P_CLKDIV2_G3D_SHIFT) |
>> + (0x3 << S5P_CLKDIV2_MFC_SHIFT);
>> +#ifndef CONFIG_CPU_S5PC110_EVT0_ERRATA
>> + reg &= ~S5P_CLKDIV2_G2D_MASK;
>> + reg |= (0x2 << S5P_CLKDIV2_G2D_SHIFT);
>> +#endif
>> + __raw_writel(reg, S5P_CLK_DIV2);
>> +
>> + /* For MFC, G3D dividing */
>> + do {
>> + reg = __raw_readl(S5P_CLK_DIV_STAT0);
>> + } while (reg & ((1 << 16) | (1 << 17)));
>> +
>> + /* 2. Change SCLKA2M(200MHz) to SCLKMPLL in MFC_MUX, G3D MUX
>> + * (100/4=50)->(667/4=166)MHz
>> + **/
>> + reg = __raw_readl(S5P_CLK_SRC2);
>> + reg &= ~(S5P_CLKSRC2_G3D_MASK | S5P_CLKSRC2_MFC_MASK);
>> + reg |= (0x1 << S5P_CLKSRC2_G3D_SHIFT) |
>> + (0x1 << S5P_CLKSRC2_MFC_SHIFT);
>> +#ifndef CONFIG_CPU_S5PC110_EVT0_ERRATA
>> + reg &= ~S5P_CLKSRC2_G2D_MASK;
>> + reg |= (0x1 << S5P_CLKSRC2_G2D_SHIFT);
>> +#endif
>> + __raw_writel(reg, S5P_CLK_SRC2);
>> +
>> + do {
>> + reg = __raw_readl(S5P_CLK_MUX_STAT1);
>> + } while (reg & ((1 << 7) | (1 << 3)));
>> +
>> + /* 3. DMC1 refresh count for 133MHz if (index == L4) is true
>> + * refresh counter is already programmed in the outer/upper
>> + * code. 0x287 at 83MHz
>> + **/
>> + if (!bus_speed_changing)
>> + __raw_writel(0x40d, S5P_VA_DMC1 + 0x30);
>> +
>> + /* 4. SCLKAPLL -> SCLKMPLL */
>> + reg = __raw_readl(S5P_CLK_SRC0);
>> + reg &= ~(S5P_CLKSRC0_MUX200_MASK);
>> + reg |= (0x1 << S5P_CLKSRC0_MUX200_SHIFT);
>> + __raw_writel(reg, S5P_CLK_SRC0);
>> +
>> + do {
>> + reg = __raw_readl(S5P_CLK_MUX_STAT0);
>> + } while (reg & (0x1 << 18));
>> +
>> +#if defined(CONFIG_S5PC110_H_TYPE)
>> + /* DMC0 source clock: SCLKA2M -> SCLKMPLL */
>> + __raw_writel(0x50e, S5P_VA_DMC0 + 0x30);
>> +
>> + reg = __raw_readl(S5P_CLK_DIV6);
>> + reg &= ~(S5P_CLKDIV6_ONEDRAM_MASK);
>> + reg |= (0x3 << S5P_CLKDIV6_ONEDRAM_SHIFT);
>> + __raw_writel(reg, S5P_CLK_DIV6);
>> +
>> + do {
>> + reg = __raw_readl(S5P_CLK_DIV_STAT1);
>> + } while (reg & (1 << 15));
>> +
>> + reg = __raw_readl(S5P_CLK_SRC6);
>> + reg &= ~(S5P_CLKSRC6_ONEDRAM_MASK);
>> + reg |= (0x1 << S5P_CLKSRC6_ONEDRAM_SHIFT);
>> + __raw_writel(reg, S5P_CLK_SRC6);
>> +
>> + do {
>> + reg = __raw_readl(S5P_CLK_MUX_STAT1);
>> + } while (reg & (1 << 11));
>> +#endif
>> +}
>> +
>> +static void s5pv210_target_MPLL2APLL(unsigned int index,
>> + unsigned int bus_speed_changing)
>> +{
>> + unsigned int reg;
>> +
>> + /* 7. Set Lock time = 30us*24MHz = 02cf (EVT1) */
>> +#ifdef CONFIG_CPU_S5PC110_EVT0_ERRATA
>> + /* Lock time = 300us*24Mhz = 7200(0x1c20)*/
>> + __raw_writel(0x1c20, S5P_APLL_LOCK);
>> +#else
>> + /* Lock time = 30us*24Mhz = 0x2cf*/
>> + __raw_writel(0x2cf, S5P_APLL_LOCK);
>> +#endif
>> +
>> + /* 8. Turn on APLL
>> + * 8-1. Set PMS values
>> + * 8-3. Wait until the PLL is locked
>> + **/
>> + if (index == L0)
>> + /* APLL FOUT becomes 1000 Mhz */
>> + __raw_writel(PLL45XX_APLL_VAL_1000, S5P_APLL_CON);
>> + else
>> + /* APLL FOUT becomes 800 Mhz */
>> + __raw_writel(PLL45XX_APLL_VAL_800, S5P_APLL_CON);
>> +
>> + do {
>> + reg = __raw_readl(S5P_APLL_CON);
>> + } while (!(reg & (0x1 << 29)));
>> +
>> + /* 9. Change source clock from SCLKMPLL(667MHz)
>> + * to SCLKA2M(200MHz) in MFC_MUX and G3D_MUX
>> + * (667/4=166)->(200/4=50)MHz
>> + **/
>> + reg = __raw_readl(S5P_CLK_SRC2);
>> + reg &= ~(S5P_CLKSRC2_G3D_MASK | S5P_CLKSRC2_MFC_MASK);
>> + reg |= (0 << S5P_CLKSRC2_G3D_SHIFT) | (0 << S5P_CLKSRC2_MFC_SHIFT);
>> +#ifndef CONFIG_CPU_S5PC110_EVT0_ERRATA
>> + reg &= ~S5P_CLKSRC2_G2D_MASK;
>> + reg |= 0x1 << S5P_CLKSRC2_G2D_SHIFT;
>> +#endif
>> + __raw_writel(reg, S5P_CLK_SRC2);
>> +
>> + do {
>> + reg = __raw_readl(S5P_CLK_MUX_STAT1);
>> + } while (reg & ((1 << 7) | (1 << 3)));
>> +
>> + /* 10. Change divider for MFC and G3D
>> + * (200/4=50)->(200/1=200)MHz
>> + **/
>> + reg = __raw_readl(S5P_CLK_DIV2);
>> + reg &= ~(S5P_CLKDIV2_G3D_MASK | S5P_CLKDIV2_MFC_MASK);
>> + reg |= (clkdiv_val[index][10] << S5P_CLKDIV2_G3D_SHIFT) |
>> + (clkdiv_val[index][9] << S5P_CLKDIV2_MFC_SHIFT);
>> +#ifndef CONFIG_CPU_S5PC110_EVT0_ERRATA
>> + reg &= ~S5P_CLKDIV2_G2D_MASK;
>> + reg |= 0x2 << S5P_CLKDIV2_G2D_SHIFT;
>> +#endif
>> + __raw_writel(reg, S5P_CLK_DIV2);
>> +
>> + do {
>> + reg = __raw_readl(S5P_CLK_DIV_STAT0);
>> + } while (reg & ((1 << 16) | (1 << 17)));
>> +
>> + /* 11. Change MPLL to APLL in MSYS_MUX */
>> + reg = __raw_readl(S5P_CLK_SRC0);
>> + reg &= ~(S5P_CLKSRC0_MUX200_MASK);
>> + reg |= (0x0 << S5P_CLKSRC0_MUX200_SHIFT); /* SCLKMPLL -> SCLKAPLL */
>> + __raw_writel(reg, S5P_CLK_SRC0);
>> +
>> + do {
>> + reg = __raw_readl(S5P_CLK_MUX_STAT0);
>> + } while (reg & (0x1 << 18));
>> +
>> + /* 12. DMC1 refresh counter
>> + * L4: DMC1 = 100MHz 7.8us/(1/100) = 0x30c
>> + * Others: DMC1 = 200MHz 7.8us/(1/200) = 0x618
>> + **/
>> + if (!bus_speed_changing)
>> + __raw_writel(0x618, S5P_VA_DMC1 + 0x30);
>> +
>> +#if defined(CONFIG_S5PC110_H_TYPE)
>> + /* DMC0 source clock: SCLKMPLL -> SCLKA2M */
>> + reg = __raw_readl(S5P_CLK_SRC6);
>> + reg &= ~(S5P_CLKSRC6_ONEDRAM_MASK);
>> + reg |= (0x0 << S5P_CLKSRC6_ONEDRAM_SHIFT);
>> + __raw_writel(reg, S5P_CLK_SRC6);
>> +
>> + do {
>> + reg = __raw_readl(S5P_CLK_MUX_STAT1);
>> + } while (reg & (1 << 11));
>> +
>> + reg = __raw_readl(S5P_CLK_DIC6);
>> + reg &= ~(S5P_CLKDIV6_ONEDRAM_MASK);
>> + reg |= (0x0 << S5P_CLKDIV6_ONEDRAM_SHIFT);
>> + __raw_writel(reg, S5P_CLK_DIV6);
>> +
>> + do {
>> + reg = __raw_readl(S5P_CLK_DIV_STAT1);
>> + } while (reg & (1 << 15));
>> + __raw_writel(0x618, S5P_VA_DMC0 + 0x30);
>> +#endif
>> +}
>> +
>> +#ifdef CONFIG_PM
>> +static int no_cpufreq_access;
>> +/* s5pv210_target: relation has an additional symantics other than
>> + * the standard
>> + * [0x30]:
>> + * 1: disable further access to target until being re-enabled.
>> + * 2: re-enable access to target */
>> +#endif
>> +static int s5pv210_target(struct cpufreq_policy *policy,
>> + unsigned int target_freq,
>> + unsigned int relation)
>> +{
>> + static int initialized;
>> + int ret = 0;
>> + unsigned long arm_clk;
>> + unsigned int index, reg, arm_volt, int_volt;
>> + unsigned int pll_changing = 0;
>> + unsigned int bus_speed_changing = 0;
>> +
>> +#ifdef CONFIG_CPU_FREQ_DEBUG
>> + printk(KERN_INFO "cpufreq: Entering for %dkHz\n", target_freq);
>> +#endif
>
> cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, <prefix>, ...)
>
>> +#ifdef CONFIG_PM
>> + if ((relation & ENABLE_FURTHER_CPUFREQ) &&
>> + (relation & DISABLE_FURTHER_CPUFREQ)) {
>> + /* Invalidate both if both marked */
>> + relation &= ~ENABLE_FURTHER_CPUFREQ;
>> + relation &= ~DISABLE_FURTHER_CPUFREQ;
>> + printk(KERN_ERR "%s:%d denied marking \"FURTHER_CPUFREQ\""
>> + " as both marked.\n",
>> + __FILE__, __LINE__);
>> + }
>> + if (relation & ENABLE_FURTHER_CPUFREQ)
>> + no_cpufreq_access = 0;
>> + if (no_cpufreq_access == 1) {
>> +#ifdef CONFIG_PM_VERBOSE
>> + printk(KERN_ERR "%s:%d denied access to %s as it is disabled"
>> + " temporarily\n", __FILE__, __LINE__, __func__);
>
> Use pr_err(...)
>
>> +#endif
>> + ret = -EINVAL;
>> + goto out;
>> + }
>> + if (relation & DISABLE_FURTHER_CPUFREQ)
>> + no_cpufreq_access = 1;
>> + relation &= ~MASK_FURTHER_CPUFREQ;
>> +#endif
>> +
>> + s3c_freqs.freqs.old = s5pv210_getspeed(0);
>> +
>> + if (cpufreq_frequency_table_target(policy, s5pv210_freq_table,
>> + target_freq, relation, &index)) {
>> + ret = -EINVAL;
>> + goto out;
>> + }
>> +#ifdef CPUFREQ_DISABLE_100MHZ
>> + if (index == L4)
>> + index = L3;
>> +#endif
>> +#ifdef CPUFREQ_DISABLE_1GHZ
>> + if (index == L0)
>> + index = L1;
>> +#endif
>> +
>> + arm_clk = s5pv210_freq_table[index].frequency;
>> +
>> + s3c_freqs.freqs.new = arm_clk;
>> + s3c_freqs.freqs.cpu = 0;
>> +
>> + if (s3c_freqs.freqs.new == s3c_freqs.freqs.old &&
>> + initialized >= 2)
>> + return 0;
>> + else if (initialized < 2)
>> + initialized++;
>> +
>> + arm_volt = s5pv210_dvs_conf[index].arm_volt;
>> + int_volt = s5pv210_dvs_conf[index].int_volt;
>> +
>> + /* iNew clock information update */
>> + memcpy(&s3c_freqs.new, &s5pv210_clk_info[index],
>> + sizeof(struct s3c_freq));
>> +
>> + if (s3c_freqs.freqs.new >= s3c_freqs.freqs.old) {
>> + /* Voltage up code: increase ARM first */
>> + if (!IS_ERR_OR_NULL(arm_regulator) &&
>> + !IS_ERR_OR_NULL(internal_regulator)) {
>> + regulator_set_voltage(arm_regulator,
>> + arm_volt, arm_volt_max);
>> + regulator_set_voltage(internal_regulator,
>> + int_volt, int_volt_max);
>> + }
>> + }
>> + cpufreq_notify_transition(&s3c_freqs.freqs, CPUFREQ_PRECHANGE);
>> +
>> + if (s3c_freqs.new.fclk != s3c_freqs.old.fclk || initialized < 2)
>> + pll_changing = 1;
>> +
>> + if (s3c_freqs.new.hclk_msys != s3c_freqs.old.hclk_msys ||
>> + initialized < 2)
>> + bus_speed_changing = 1;
>> +
>> + if (bus_speed_changing) {
>> + /* Reconfigure DRAM refresh counter value for minimum
>> + * temporary clock while changing divider.
>> + * expected clock is 83MHz: 7.8usec/(1/83MHz) = 0x287
>> + **/
>> + if (pll_changing)
>> + __raw_writel(0x287, S5P_VA_DMC1 + 0x30);
>> + else
>> + __raw_writel(0x30c, S5P_VA_DMC1 + 0x30);
>> +
>> + __raw_writel(0x287, S5P_VA_DMC0 + 0x30);
>> + }
>> +
>> + /* APLL should be changed in this level
>> + * APLL -> MPLL(for stable transition) -> APLL
>> + * Some clock source's clock API are not prepared. Do not use clock API
>> + * in below code.
>> + */
>> + if (pll_changing)
>> + s5pv210_target_APLL2MPLL(index, bus_speed_changing);
>> +
>> + /* ARM MCS value changed */
>> + if (index != L4) {
>> + reg = __raw_readl(S5P_ARM_MCS_CON);
>> + reg &= ~0x3;
>> + reg |= 0x1;
>> + __raw_writel(reg, S5P_ARM_MCS_CON);
>> + }
>> +
>> + reg = __raw_readl(S5P_CLK_DIV0);
>> +
>> + reg &= ~(S5P_CLKDIV0_APLL_MASK | S5P_CLKDIV0_A2M_MASK
>> + | S5P_CLKDIV0_HCLK200_MASK | S5P_CLKDIV0_PCLK100_MASK
>> + | S5P_CLKDIV0_HCLK166_MASK | S5P_CLKDIV0_PCLK83_MASK
>> + | S5P_CLKDIV0_HCLK133_MASK | S5P_CLKDIV0_PCLK66_MASK);
>> +
>> + reg |= ((clkdiv_val[index][0]<<S5P_CLKDIV0_APLL_SHIFT)
>> + | (clkdiv_val[index][1] << S5P_CLKDIV0_A2M_SHIFT)
>> + | (clkdiv_val[index][2] << S5P_CLKDIV0_HCLK200_SHIFT)
>> + | (clkdiv_val[index][3] << S5P_CLKDIV0_PCLK100_SHIFT)
>> + | (clkdiv_val[index][4] << S5P_CLKDIV0_HCLK166_SHIFT)
>> + | (clkdiv_val[index][5] << S5P_CLKDIV0_PCLK83_SHIFT)
>> + | (clkdiv_val[index][6] << S5P_CLKDIV0_HCLK133_SHIFT)
>> + | (clkdiv_val[index][7] << S5P_CLKDIV0_PCLK66_SHIFT));
>> +
>> + __raw_writel(reg, S5P_CLK_DIV0);
>> +
>> + do {
>> + reg = __raw_readl(S5P_CLK_DIV_STAT0);
>> + } while (reg & 0xff);
>> +
>> + /* ARM MCS value changed */
>> + if (index == L4) {
>> + reg = __raw_readl(S5P_ARM_MCS_CON);
>> + reg &= ~0x3;
>> + reg |= 0x3;
>> + __raw_writel(reg, S5P_ARM_MCS_CON);
>> + }
>> +
>> + if (pll_changing)
>> + s5pv210_target_MPLL2APLL(index, bus_speed_changing);
>> +
>> + /* L4 level need to change memory bus speed,
>> + * hence onedram clock divier and
>> + * memory refresh parameter should be changed
>> + */
>> + if (bus_speed_changing) {
>> + reg = __raw_readl(S5P_CLK_DIV6);
>> + reg &= ~S5P_CLKDIV6_ONEDRAM_MASK;
>> + reg |= (clkdiv_val[index][8] << S5P_CLKDIV6_ONEDRAM_SHIFT);
>> + /* ONEDRAM Clock Divider Ratio: 7 for L4, 3 for Others */
>> + __raw_writel(reg, S5P_CLK_DIV6);
>> +
>> + do {
>> + reg = __raw_readl(S5P_CLK_DIV_STAT1);
>> + } while (reg & (1 << 15));
>> +
>> + /* Reconfigure DRAM refresh counter value */
>> + if (index != L4) {
>> + /* DMC0: 166MHz
>> + * DMC1: 200MHz
>> + **/
>> + __raw_writel(0x618, S5P_VA_DMC1 + 0x30);
>> +#if !defined(CONFIG_S5PC110_H_TYPE)
>> + __raw_writel(0x50e, S5P_VA_DMC0 + 0x30);
>> +#else
>> + __raw_writel(0x618, S5P_VA_DMC0 + 0x30);
>> +#endif
>> + } else {
>> + /* DMC0: 83MHz
>> + * DMC1: 100MHz
>> + **/
>> + __raw_writel(0x30c, S5P_VA_DMC1 + 0x30);
>> + __raw_writel(0x287, S5P_VA_DMC0 + 0x30);
>> + }
>> + }
>> +
>> + if (s3c_freqs.freqs.new < s3c_freqs.freqs.old) {
>> + /* Voltage down: decrease INT first.*/
>> + if (!IS_ERR_OR_NULL(arm_regulator) &&
>> + !IS_ERR_OR_NULL(internal_regulator)) {
>> + regulator_set_voltage(internal_regulator,
>> + int_volt, int_volt_max);
>> + regulator_set_voltage(arm_regulator,
>> + arm_volt, arm_volt_max);
>> + }
>> + }
>> + cpufreq_notify_transition(&s3c_freqs.freqs, CPUFREQ_POSTCHANGE);
>> +
>> + memcpy(&s3c_freqs.old, &s3c_freqs.new, sizeof(struct s3c_freq));
>> +#ifdef CONFIG_CPU_FREQ_DEBUG
>> + printk(KERN_INFO "cpufreq: Performance changed[L%d]\n", index);
>
> pr_info
>
>> +#endif
>> + previous_arm_volt = s5pv210_dvs_conf[index].arm_volt;
>> +out:
>> + return ret;
>> +}
>> +
>> +
>> +#ifdef CONFIG_PM
>> +static int previous_frequency;
>> +
>> +static int s5pv210_cpufreq_suspend(struct cpufreq_policy *policy,
>> + pm_message_t pmsg)
>> +{
>> + int ret = 0;
>> + printk(KERN_INFO "cpufreq: Entering suspend.\n");
>
> pr_info
>
>> +
>> + previous_frequency = cpufreq_get(0);
>> + ret = __cpufreq_driver_target(cpufreq_cpu_get(0), SLEEP_FREQ,
>> + DISABLE_FURTHER_CPUFREQ);
>> + return ret;
>> +}
>> +
>> +static int s5pv210_cpufreq_resume(struct cpufreq_policy *policy)
>> +{
>> + int ret = 0;
>> + u32 rate;
>> + int level = CPUFREQ_TABLE_END;
>> + int i;
>> +
>> + printk(KERN_INFO "cpufreq: Waking up from a suspend.\n");
>
> pr_info
>
>> +
>> + __cpufreq_driver_target(cpufreq_cpu_get(0), previous_frequency,
>> + ENABLE_FURTHER_CPUFREQ);
>> +
>> + /* Clock information update with wakeup value */
>> + rate = clk_get_rate(mpu_clk);
>> +
>> + i = 0;
>> + while (s5pv210_freq_table[i].frequency != CPUFREQ_TABLE_END) {
>> + if (s5pv210_freq_table[i].frequency * 1000 == rate) {
>> + level = s5pv210_freq_table[i].index;
>> + break;
>> + }
>> + i++;
>> + }
>> +
>> + if (level == CPUFREQ_TABLE_END) { /* Not found */
>> + printk(KERN_ERR "[%s:%d] clock speed does not match: "
>> + "%d. Using L1 of 800MHz.\n",
>> + __FILE__, __LINE__, rate);
>
> pr_err
>
>> + level = L1;
>> + }
>> +
>> + memcpy(&s3c_freqs.old, &s5pv210_clk_info[level],
>> + sizeof(struct s3c_freq));
>> + previous_arm_volt = s5pv210_dvs_conf[level].arm_volt;
>> + return ret;
>> +}
>> +#endif
>> +
>> +
>> +static int __init s5pv210_cpu_init(struct cpufreq_policy *policy)
>> +{
>> + u32 rate ;
>> + int i, level = CPUFREQ_TABLE_END;
>> +
>> + printk(KERN_INFO "S5PV210 CPUFREQ %s:%d\n", __FILE__, __LINE__);
>
> pr_info
>
>> +#ifdef CONFIG_PM
>> + no_cpufreq_access = 0;
>> +#endif
>> +#ifdef CLK_OUT_PROBING
>> + reg = __raw_readl(S5P_CLK_OUT);
>> + reg &= ~(0x1f << 12 | 0xf << 20); /* CLKSEL and DIVVAL*/
>> + reg |= (0xf << 12 | 0x1 << 20); /* CLKSEL = ARMCLK/4, DIVVAL = 1 */
>> + /* Result = ARMCLK / 4 / ( 1 + 1 ) */
>> + __raw_writel(reg, S5P_CLK_OUT);
>> +#endif
>> + mpu_clk = clk_get(NULL, MPU_CLK);
>> + if (IS_ERR(mpu_clk)) {
>> + printk(KERN_ERR "S5PV210 CPUFREQ cannot get MPU_CLK(%s)\n",
>> + MPU_CLK);
>
> pr_err
>
>> + return PTR_ERR(mpu_clk);
>> + }
>> +
>> + if (policy->cpu != 0) {
>> + printk(KERN_ERR "S5PV210 CPUFREQ cannot get proper cpu(%d)\n",
>> + policy->cpu);
>
> pr_err
>
>> + return -EINVAL;
>> + }
>> + policy->cur = policy->min = policy->max = s5pv210_getspeed(0);
>> +
>> + cpufreq_frequency_table_get_attr(s5pv210_freq_table, policy->cpu);
>> +
>> + policy->cpuinfo.transition_latency = 40000; /*1us*/
>> +
>> + rate = clk_get_rate(mpu_clk);
>> + i = 0;
>> +
>> + while (s5pv210_freq_table[i].frequency != CPUFREQ_TABLE_END) {
>> + if (s5pv210_freq_table[i].frequency * 1000 == rate) {
>> + level = s5pv210_freq_table[i].index;
>> + break;
>> + }
>> + i++;
>> + }
>> +
>> + if (level == CPUFREQ_TABLE_END) { /* Not found */
>> + printk(KERN_ERR "[%s:%d] clock speed does not match: "
>> + "%d. Using L1 of 800MHz.\n",
>> + __FILE__, __LINE__, rate);
>> + level = L1;
>> + }
>> +
>> + printk(KERN_INFO "S5PV210 CPUFREQ Initialized.\n");
>
> pr_info
>
>> +
>> + memcpy(&s3c_freqs.old, &s5pv210_clk_info[level],
>> + sizeof(struct s3c_freq));
>> + previous_arm_volt = s5pv210_dvs_conf[level].arm_volt;
>> +
>> + return cpufreq_frequency_table_cpuinfo(policy, s5pv210_freq_table);
>> +}
>> +
>> +static struct cpufreq_driver s5pv210_driver = {
>> + .flags = CPUFREQ_STICKY,
>> + .verify = s5pv210_verify_speed,
>> + .target = s5pv210_target,
>> + .get = s5pv210_getspeed,
>> + .init = s5pv210_cpu_init,
>> + .name = "s5pv210",
>> +#ifdef CONFIG_PM
>> + .suspend = s5pv210_cpufreq_suspend,
>> + .resume = s5pv210_cpufreq_resume,
>> +#endif
>> +};
>> +
>> +static int __init s5pv210_cpufreq_init(void)
>> +{
>> + printk(KERN_INFO "S5PV210 CPUFREQ Init.\n");
>
> pr_info
> Also, I guess one banner is enough?
>
>> + arm_regulator = regulator_get_exclusive(NULL, "vddarm");
>> + if (IS_ERR(arm_regulator)) {
>> + printk(KERN_ERR "failed to get regulater resource vddarm\n");
>> + goto error;
>> + }
>> + internal_regulator = regulator_get_exclusive(NULL, "vddint");
>> + if (IS_ERR(internal_regulator)) {
>> + printk(KERN_ERR "failed to get regulater resource vddint\n");
>> + goto error;
>> + }
>> + goto finish;
>> +error:
>> + printk(KERN_WARNING "Cannot get vddarm or vddint. CPUFREQ Will not"
>> + " change the voltage.\n");
>
> pr_warn
>
>> +finish:
>> + return cpufreq_register_driver(&s5pv210_driver);
>> +}
>> +
>> +late_initcall(s5pv210_cpufreq_init);
>> diff --git a/arch/arm/mach-s5pv210/include/mach/cpu-freq.h b/arch/arm/mach-s5pv210/include/mach/cpu-freq.h
>> new file mode 100644
>> index 0000000..d3c31b2
>> --- /dev/null
>> +++ b/arch/arm/mach-s5pv210/include/mach/cpu-freq.h
>> @@ -0,0 +1,50 @@
>> +/* arch/arm/mach-s5pv210/include/mach/cpu-freq.h
>> + *
>> + * Copyright (c) 2010 Samsung Electronics
>> + *
>> + * MyungJoo Ham <myungjoo.ham at samsung.com>
>> + *
>> + * S5PV210/S5PC110 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.
>> +*/
>> +
>> +#ifndef _ARCH_ARM_MACH_S5PV210_INCLUDE_MACH_CPU_FREQ_H_
>> +#define _ARCH_ARM_MACH_S5PV210_INCLUDE_MACH_CPU_FREQ_H_
>> +
>> +#include <linux/cpufreq.h>
>> +
>> +#ifdef CONFIG_CPU_S5PV210
>> +
>> +#define USE_FREQ_TABLE
>> +
>> +#define KHZ_T 1000
>> +
>> +#define MPU_CLK "armclk"
>> +
>> +enum perf_level {
>> + L0 = 0,
>> + L1,
>> + L2,
>> + L3,
>> + L4,
>> +};
>> +
>> +#define CLK_DIV0_MASK ((0x7<<0)|(0x7<<8)|(0x7<<12)) /* APLL,HCLK_MSYS,PCLK_MSYS mask value */
>> +
>> +#ifdef CONFIG_PM
>> +#define SLEEP_FREQ (800 * 1000) /* Use 800MHz when entering sleep */
>> +
>> +/* additional symantics for "relation" in cpufreq with pm */
>> +#define DISABLE_FURTHER_CPUFREQ 0x10
>> +#define ENABLE_FURTHER_CPUFREQ 0x20
>> +#define MASK_FURTHER_CPUFREQ 0x30
>> +/* With 0x00(NOCHANGE), it depends on the previous "further" status */
>> +
>> +#endif
>> +
>> +
>> +#endif /* CONFIG_CPU_S5PV210 */
>> +#endif /* _ARCH_ARM_MACH_S5PV210_INCLUDE_MACH_CPU_FREQ_H_ */
>
>
> --
> Maurus Cuelenaere
>
--
MyungJoo Ham (함명주), Ph.D.
Mobile Software Platform Lab,
Digital Media and Communications (DMC) Business
Samsung Electronics
cell: 82-10-6714-2858
More information about the linux-arm-kernel
mailing list