On Fri, Jul 16, 2010 at 8:02 PM, Maurus Cuelenaere <mcuelenaere@xxxxxxxxx> 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@xxxxxxxxxxx> >> Signed-off-by: Kyungmin Park <kyungmin.park@xxxxxxxxxxx> >> --- >> 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@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@xxxxxxxxxxx> >> + * >> + * 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 -- To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html