diff options
author | Olof Johansson <olof@lixom.net> | 2012-12-17 18:38:55 -0800 |
---|---|---|
committer | Olof Johansson <olof@lixom.net> | 2012-12-17 18:38:55 -0800 |
commit | 10be289d07a5327e2e7764bb3ccbffa215075103 (patch) | |
tree | e1591c2cee3da7e98e0166a60ff6517d63ba28c1 /arch | |
parent | 68136b105cbb798b13e9545b9952e568d04e347c (diff) | |
parent | 9db316b6bf0234d9391f87dd0d28b23f5a44facb (diff) | |
download | linux-stable-10be289d07a5327e2e7764bb3ccbffa215075103.tar.gz linux-stable-10be289d07a5327e2e7764bb3ccbffa215075103.tar.bz2 linux-stable-10be289d07a5327e2e7764bb3ccbffa215075103.zip |
Merge tag 'omap-fixes-a-for-v3.8-window' of git://git.kernel.org/pub/scm/linux/kernel/git/pjw/omap-pending into fixes
From Paul Walmsley per Tony Lindgrens request:
Fix some OMAP4 clock problems, and deal with some sparse warnings from
the OMAP CPUIdle code.
* tag 'omap-fixes-a-for-v3.8-window' of git://git.kernel.org/pub/scm/linux/kernel/git/pjw/omap-pending:
ARM: OMAP3/4: cpuidle: fix sparse and checkpatch warnings
ARM: OMAP4: clock data: DPLLs are missing bypass clocks in their parent lists
ARM: OMAP4: clock data: div_iva_hs_clk is a power-of-two divider
ARM: OMAP4: Fix EMU clock domain always on
ARM: OMAP4460: Workaround ABE DPLL failing to turn-on
ARM: OMAP4: Enhance support for DPLLs with 4X multiplier
ARM: OMAP4: Add function table for non-M4X dplls
ARM: OMAP4: Update timer clock aliases
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mach-omap2/cclock44xx_data.c | 78 | ||||
-rw-r--r-- | arch/arm/mach-omap2/clock.h | 10 | ||||
-rw-r--r-- | arch/arm/mach-omap2/clockdomain.c | 3 | ||||
-rw-r--r-- | arch/arm/mach-omap2/cpuidle34xx.c | 14 | ||||
-rw-r--r-- | arch/arm/mach-omap2/cpuidle44xx.c | 28 | ||||
-rw-r--r-- | arch/arm/mach-omap2/dpll3xxx.c | 46 | ||||
-rw-r--r-- | arch/arm/mach-omap2/dpll44xx.c | 64 |
7 files changed, 189 insertions, 54 deletions
diff --git a/arch/arm/mach-omap2/cclock44xx_data.c b/arch/arm/mach-omap2/cclock44xx_data.c index aa56c3e5bb34..5789a5e25563 100644 --- a/arch/arm/mach-omap2/cclock44xx_data.c +++ b/arch/arm/mach-omap2/cclock44xx_data.c @@ -40,6 +40,14 @@ #define OMAP4430_MODULEMODE_HWCTRL_SHIFT 0 #define OMAP4430_MODULEMODE_SWCTRL_SHIFT 1 +/* + * OMAP4 ABE DPLL default frequency. In OMAP4460 TRM version V, section + * "3.6.3.2.3 CM1_ABE Clock Generator" states that the "DPLL_ABE_X2_CLK + * must be set to 196.608 MHz" and hence, the DPLL locked frequency is + * half of this value. + */ +#define OMAP4_DPLL_ABE_DEFFREQ 98304000 + /* Root clocks */ DEFINE_CLK_FIXED_RATE(extalt_clkin_ck, CLK_IS_ROOT, 59000000, 0x0); @@ -124,6 +132,8 @@ static struct dpll_data dpll_abe_dd = { .enable_mask = OMAP4430_DPLL_EN_MASK, .autoidle_mask = OMAP4430_AUTO_DPLL_MODE_MASK, .idlest_mask = OMAP4430_ST_DPLL_CLK_MASK, + .m4xen_mask = OMAP4430_DPLL_REGM4XEN_MASK, + .lpmode_mask = OMAP4430_DPLL_LPMODE_EN_MASK, .max_multiplier = 2047, .max_divider = 128, .min_divider = 1, @@ -233,7 +243,7 @@ static struct dpll_data dpll_core_dd = { static const char *dpll_core_ck_parents[] = { - "sys_clkin_ck", + "sys_clkin_ck", "core_hsd_byp_clk_mux_ck" }; static struct clk dpll_core_ck; @@ -286,9 +296,9 @@ DEFINE_CLK_DIVIDER(div_core_ck, "dpll_core_m5x2_ck", &dpll_core_m5x2_ck, 0x0, OMAP4430_CM_CLKSEL_CORE, OMAP4430_CLKSEL_CORE_SHIFT, OMAP4430_CLKSEL_CORE_WIDTH, 0x0, NULL); -DEFINE_CLK_OMAP_HSDIVIDER(div_iva_hs_clk, "dpll_core_m5x2_ck", - &dpll_core_m5x2_ck, 0x0, OMAP4430_CM_BYPCLK_DPLL_IVA, - OMAP4430_CLKSEL_0_1_MASK); +DEFINE_CLK_DIVIDER(div_iva_hs_clk, "dpll_core_m5x2_ck", &dpll_core_m5x2_ck, + 0x0, OMAP4430_CM_BYPCLK_DPLL_IVA, OMAP4430_CLKSEL_0_1_SHIFT, + OMAP4430_CLKSEL_0_1_WIDTH, CLK_DIVIDER_POWER_OF_TWO, NULL); DEFINE_CLK_DIVIDER(div_mpu_hs_clk, "dpll_core_m5x2_ck", &dpll_core_m5x2_ck, 0x0, OMAP4430_CM_BYPCLK_DPLL_MPU, OMAP4430_CLKSEL_0_1_SHIFT, @@ -363,8 +373,21 @@ static struct dpll_data dpll_iva_dd = { .min_divider = 1, }; +static const char *dpll_iva_ck_parents[] = { + "sys_clkin_ck", "iva_hsd_byp_clk_mux_ck" +}; + static struct clk dpll_iva_ck; +static const struct clk_ops dpll_ck_ops = { + .enable = &omap3_noncore_dpll_enable, + .disable = &omap3_noncore_dpll_disable, + .recalc_rate = &omap3_dpll_recalc, + .round_rate = &omap2_dpll_round_rate, + .set_rate = &omap3_noncore_dpll_set_rate, + .get_parent = &omap2_init_dpll_parent, +}; + static struct clk_hw_omap dpll_iva_ck_hw = { .hw = { .clk = &dpll_iva_ck, @@ -373,7 +396,7 @@ static struct clk_hw_omap dpll_iva_ck_hw = { .ops = &clkhwops_omap3_dpll, }; -DEFINE_STRUCT_CLK(dpll_iva_ck, dpll_core_ck_parents, dpll_abe_ck_ops); +DEFINE_STRUCT_CLK(dpll_iva_ck, dpll_iva_ck_parents, dpll_ck_ops); static const char *dpll_iva_x2_ck_parents[] = { "dpll_iva_ck", @@ -416,6 +439,10 @@ static struct dpll_data dpll_mpu_dd = { .min_divider = 1, }; +static const char *dpll_mpu_ck_parents[] = { + "sys_clkin_ck", "div_mpu_hs_clk" +}; + static struct clk dpll_mpu_ck; static struct clk_hw_omap dpll_mpu_ck_hw = { @@ -426,7 +453,7 @@ static struct clk_hw_omap dpll_mpu_ck_hw = { .ops = &clkhwops_omap3_dpll, }; -DEFINE_STRUCT_CLK(dpll_mpu_ck, dpll_core_ck_parents, dpll_abe_ck_ops); +DEFINE_STRUCT_CLK(dpll_mpu_ck, dpll_mpu_ck_parents, dpll_ck_ops); DEFINE_CLK_FIXED_FACTOR(mpu_periphclk, "dpll_mpu_ck", &dpll_mpu_ck, 0x0, 1, 2); @@ -464,6 +491,9 @@ static struct dpll_data dpll_per_dd = { .min_divider = 1, }; +static const char *dpll_per_ck_parents[] = { + "sys_clkin_ck", "per_hsd_byp_clk_mux_ck" +}; static struct clk dpll_per_ck; @@ -475,7 +505,7 @@ static struct clk_hw_omap dpll_per_ck_hw = { .ops = &clkhwops_omap3_dpll, }; -DEFINE_STRUCT_CLK(dpll_per_ck, dpll_core_ck_parents, dpll_abe_ck_ops); +DEFINE_STRUCT_CLK(dpll_per_ck, dpll_per_ck_parents, dpll_ck_ops); DEFINE_CLK_DIVIDER(dpll_per_m2_ck, "dpll_per_ck", &dpll_per_ck, 0x0, OMAP4430_CM_DIV_M2_DPLL_PER, OMAP4430_DPLL_CLKOUT_DIV_SHIFT, @@ -559,6 +589,10 @@ static struct dpll_data dpll_usb_dd = { .min_divider = 1, }; +static const char *dpll_usb_ck_parents[] = { + "sys_clkin_ck", "usb_hs_clk_div_ck" +}; + static struct clk dpll_usb_ck; static struct clk_hw_omap dpll_usb_ck_hw = { @@ -569,7 +603,7 @@ static struct clk_hw_omap dpll_usb_ck_hw = { .ops = &clkhwops_omap3_dpll, }; -DEFINE_STRUCT_CLK(dpll_usb_ck, dpll_core_ck_parents, dpll_abe_ck_ops); +DEFINE_STRUCT_CLK(dpll_usb_ck, dpll_usb_ck_parents, dpll_ck_ops); static const char *dpll_usb_clkdcoldo_ck_parents[] = { "dpll_usb_ck", @@ -696,9 +730,13 @@ DEFINE_CLK_DIVIDER(syc_clk_div_ck, "sys_clkin_ck", &sys_clkin_ck, 0x0, OMAP4430_CM_ABE_DSS_SYS_CLKSEL, OMAP4430_CLKSEL_0_0_SHIFT, OMAP4430_CLKSEL_0_0_WIDTH, 0x0, NULL); +static const char *dbgclk_mux_ck_parents[] = { + "sys_clkin_ck" +}; + static struct clk dbgclk_mux_ck; DEFINE_STRUCT_CLK_HW_OMAP(dbgclk_mux_ck, NULL); -DEFINE_STRUCT_CLK(dbgclk_mux_ck, dpll_core_ck_parents, +DEFINE_STRUCT_CLK(dbgclk_mux_ck, dbgclk_mux_ck_parents, dpll_usb_clkdcoldo_ck_ops); /* Leaf clocks controlled by modules */ @@ -1935,10 +1973,10 @@ static struct omap_clk omap44xx_clks[] = { CLK("4803e000.timer", "timer_sys_ck", &sys_clkin_ck, CK_443X), CLK("48086000.timer", "timer_sys_ck", &sys_clkin_ck, CK_443X), CLK("48088000.timer", "timer_sys_ck", &sys_clkin_ck, CK_443X), - CLK("49038000.timer", "timer_sys_ck", &syc_clk_div_ck, CK_443X), - CLK("4903a000.timer", "timer_sys_ck", &syc_clk_div_ck, CK_443X), - CLK("4903c000.timer", "timer_sys_ck", &syc_clk_div_ck, CK_443X), - CLK("4903e000.timer", "timer_sys_ck", &syc_clk_div_ck, CK_443X), + CLK("40138000.timer", "timer_sys_ck", &syc_clk_div_ck, CK_443X), + CLK("4013a000.timer", "timer_sys_ck", &syc_clk_div_ck, CK_443X), + CLK("4013c000.timer", "timer_sys_ck", &syc_clk_div_ck, CK_443X), + CLK("4013e000.timer", "timer_sys_ck", &syc_clk_div_ck, CK_443X), CLK(NULL, "cpufreq_ck", &dpll_mpu_ck, CK_443X), }; @@ -1955,6 +1993,7 @@ int __init omap4xxx_clk_init(void) { u32 cpu_clkflg; struct omap_clk *c; + int rc; if (cpu_is_omap443x()) { cpu_mask = RATE_IN_4430; @@ -1983,5 +2022,18 @@ int __init omap4xxx_clk_init(void) omap2_clk_enable_init_clocks(enable_init_clks, ARRAY_SIZE(enable_init_clks)); + /* + * On OMAP4460 the ABE DPLL fails to turn on if in idle low-power + * state when turning the ABE clock domain. Workaround this by + * locking the ABE DPLL on boot. + */ + if (cpu_is_omap446x()) { + rc = clk_set_parent(&abe_dpll_refclk_mux_ck, &sys_32k_ck); + if (!rc) + rc = clk_set_rate(&dpll_abe_ck, OMAP4_DPLL_ABE_DEFFREQ); + if (rc) + pr_err("%s: failed to configure ABE DPLL!\n", __func__); + } + return 0; } diff --git a/arch/arm/mach-omap2/clock.h b/arch/arm/mach-omap2/clock.h index 9917f793c3b6..b40204837bd7 100644 --- a/arch/arm/mach-omap2/clock.h +++ b/arch/arm/mach-omap2/clock.h @@ -195,6 +195,10 @@ struct clksel { * @enable_mask: mask of the DPLL mode bitfield in @control_reg * @last_rounded_rate: cache of the last rate result of omap2_dpll_round_rate() * @last_rounded_m: cache of the last M result of omap2_dpll_round_rate() + * @last_rounded_m4xen: cache of the last M4X result of + * omap4_dpll_regm4xen_round_rate() + * @last_rounded_lpmode: cache of the last lpmode result of + * omap4_dpll_lpmode_recalc() * @max_multiplier: maximum valid non-bypass multiplier value (actual) * @last_rounded_n: cache of the last N result of omap2_dpll_round_rate() * @min_divider: minimum valid non-bypass divider value (actual) @@ -205,6 +209,8 @@ struct clksel { * @autoidle_mask: mask of the DPLL autoidle mode bitfield in @autoidle_reg * @freqsel_mask: mask of the DPLL jitter correction bitfield in @control_reg * @idlest_mask: mask of the DPLL idle status bitfield in @idlest_reg + * @lpmode_mask: mask of the DPLL low-power mode bitfield in @control_reg + * @m4xen_mask: mask of the DPLL M4X multiplier bitfield in @control_reg * @auto_recal_bit: bitshift of the driftguard enable bit in @control_reg * @recal_en_bit: bitshift of the PRM_IRQENABLE_* bit for recalibration IRQs * @recal_st_bit: bitshift of the PRM_IRQSTATUS_* bit for recalibration IRQs @@ -233,6 +239,8 @@ struct dpll_data { u32 enable_mask; unsigned long last_rounded_rate; u16 last_rounded_m; + u8 last_rounded_m4xen; + u8 last_rounded_lpmode; u16 max_multiplier; u8 last_rounded_n; u8 min_divider; @@ -245,6 +253,8 @@ struct dpll_data { u32 idlest_mask; u32 dco_mask; u32 sddiv_mask; + u32 lpmode_mask; + u32 m4xen_mask; u8 auto_recal_bit; u8 recal_en_bit; u8 recal_st_bit; diff --git a/arch/arm/mach-omap2/clockdomain.c b/arch/arm/mach-omap2/clockdomain.c index 384873580b23..7faf82d4e85c 100644 --- a/arch/arm/mach-omap2/clockdomain.c +++ b/arch/arm/mach-omap2/clockdomain.c @@ -998,7 +998,8 @@ int clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk) spin_lock_irqsave(&clkdm->lock, flags); /* corner case: disabling unused clocks */ - if (__clk_get_enable_count(clk) == 0) + if ((__clk_get_enable_count(clk) == 0) && + (atomic_read(&clkdm->usecount) == 0)) goto ccd_exit; if (atomic_read(&clkdm->usecount) == 0) { diff --git a/arch/arm/mach-omap2/cpuidle34xx.c b/arch/arm/mach-omap2/cpuidle34xx.c index bca7a8885703..22590dbe8f14 100644 --- a/arch/arm/mach-omap2/cpuidle34xx.c +++ b/arch/arm/mach-omap2/cpuidle34xx.c @@ -40,6 +40,8 @@ struct omap3_idle_statedata { u32 core_state; }; +static struct powerdomain *mpu_pd, *core_pd, *per_pd, *cam_pd; + static struct omap3_idle_statedata omap3_idle_data[] = { { .mpu_state = PWRDM_POWER_ON, @@ -71,7 +73,7 @@ static struct omap3_idle_statedata omap3_idle_data[] = { }, }; -static struct powerdomain *mpu_pd, *core_pd, *per_pd, *cam_pd; +/* Private functions */ static int __omap3_enter_idle(struct cpuidle_device *dev, struct cpuidle_driver *drv, @@ -260,11 +262,11 @@ static int omap3_enter_idle_bm(struct cpuidle_device *dev, return ret; } -DEFINE_PER_CPU(struct cpuidle_device, omap3_idle_dev); +static DEFINE_PER_CPU(struct cpuidle_device, omap3_idle_dev); -struct cpuidle_driver omap3_idle_driver = { - .name = "omap3_idle", - .owner = THIS_MODULE, +static struct cpuidle_driver omap3_idle_driver = { + .name = "omap3_idle", + .owner = THIS_MODULE, .states = { { .enter = omap3_enter_idle_bm, @@ -327,6 +329,8 @@ struct cpuidle_driver omap3_idle_driver = { .safe_state_index = 0, }; +/* Public functions */ + /** * omap3_idle_init - Init routine for OMAP3 idle * diff --git a/arch/arm/mach-omap2/cpuidle44xx.c b/arch/arm/mach-omap2/cpuidle44xx.c index 288bee6cbb76..d639aef0deda 100644 --- a/arch/arm/mach-omap2/cpuidle44xx.c +++ b/arch/arm/mach-omap2/cpuidle44xx.c @@ -54,6 +54,8 @@ static struct clockdomain *cpu_clkdm[NR_CPUS]; static atomic_t abort_barrier; static bool cpu_done[NR_CPUS]; +/* Private functions */ + /** * omap4_enter_idle_coupled_[simple/coupled] - OMAP4 cpuidle entry functions * @dev: cpuidle device @@ -161,9 +163,19 @@ fail: return index; } -DEFINE_PER_CPU(struct cpuidle_device, omap4_idle_dev); +/* + * For each cpu, setup the broadcast timer because local timers + * stops for the states above C1. + */ +static void omap_setup_broadcast_timer(void *arg) +{ + int cpu = smp_processor_id(); + clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ON, &cpu); +} + +static DEFINE_PER_CPU(struct cpuidle_device, omap4_idle_dev); -struct cpuidle_driver omap4_idle_driver = { +static struct cpuidle_driver omap4_idle_driver = { .name = "omap4_idle", .owner = THIS_MODULE, .en_core_tk_irqen = 1, @@ -178,7 +190,7 @@ struct cpuidle_driver omap4_idle_driver = { .desc = "MPUSS ON" }, { - /* C2 - CPU0 OFF + CPU1 OFF + MPU CSWR */ + /* C2 - CPU0 OFF + CPU1 OFF + MPU CSWR */ .exit_latency = 328 + 440, .target_residency = 960, .flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_COUPLED, @@ -200,15 +212,7 @@ struct cpuidle_driver omap4_idle_driver = { .safe_state_index = 0, }; -/* - * For each cpu, setup the broadcast timer because local timers - * stops for the states above C1. - */ -static void omap_setup_broadcast_timer(void *arg) -{ - int cpu = smp_processor_id(); - clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ON, &cpu); -} +/* Public functions */ /** * omap4_idle_init - Init routine for OMAP4 idle diff --git a/arch/arm/mach-omap2/dpll3xxx.c b/arch/arm/mach-omap2/dpll3xxx.c index fafb28c0dcbc..2bb18838cba9 100644 --- a/arch/arm/mach-omap2/dpll3xxx.c +++ b/arch/arm/mach-omap2/dpll3xxx.c @@ -291,16 +291,13 @@ static void _lookup_sddiv(struct clk_hw_omap *clk, u8 *sd_div, u16 m, u8 n) /* * _omap3_noncore_dpll_program - set non-core DPLL M,N values directly - * @clk: struct clk * of DPLL to set - * @m: DPLL multiplier to set - * @n: DPLL divider to set - * @freqsel: FREQSEL value to set + * @clk: struct clk * of DPLL to set + * @freqsel: FREQSEL value to set * - * Program the DPLL with the supplied M, N values, and wait for the DPLL to - * lock.. Returns -EINVAL upon error, or 0 upon success. + * Program the DPLL with the last M, N values calculated, and wait for + * the DPLL to lock. Returns -EINVAL upon error, or 0 upon success. */ -static int omap3_noncore_dpll_program(struct clk_hw_omap *clk, u16 m, u8 n, - u16 freqsel) +static int omap3_noncore_dpll_program(struct clk_hw_omap *clk, u16 freqsel) { struct dpll_data *dd = clk->dpll_data; u8 dco, sd_div; @@ -323,23 +320,45 @@ static int omap3_noncore_dpll_program(struct clk_hw_omap *clk, u16 m, u8 n, /* Set DPLL multiplier, divider */ v = __raw_readl(dd->mult_div1_reg); v &= ~(dd->mult_mask | dd->div1_mask); - v |= m << __ffs(dd->mult_mask); - v |= (n - 1) << __ffs(dd->div1_mask); + v |= dd->last_rounded_m << __ffs(dd->mult_mask); + v |= (dd->last_rounded_n - 1) << __ffs(dd->div1_mask); /* Configure dco and sd_div for dplls that have these fields */ if (dd->dco_mask) { - _lookup_dco(clk, &dco, m, n); + _lookup_dco(clk, &dco, dd->last_rounded_m, dd->last_rounded_n); v &= ~(dd->dco_mask); v |= dco << __ffs(dd->dco_mask); } if (dd->sddiv_mask) { - _lookup_sddiv(clk, &sd_div, m, n); + _lookup_sddiv(clk, &sd_div, dd->last_rounded_m, + dd->last_rounded_n); v &= ~(dd->sddiv_mask); v |= sd_div << __ffs(dd->sddiv_mask); } __raw_writel(v, dd->mult_div1_reg); + /* Set 4X multiplier and low-power mode */ + if (dd->m4xen_mask || dd->lpmode_mask) { + v = __raw_readl(dd->control_reg); + + if (dd->m4xen_mask) { + if (dd->last_rounded_m4xen) + v |= dd->m4xen_mask; + else + v &= ~dd->m4xen_mask; + } + + if (dd->lpmode_mask) { + if (dd->last_rounded_lpmode) + v |= dd->lpmode_mask; + else + v &= ~dd->lpmode_mask; + } + + __raw_writel(v, dd->control_reg); + } + /* We let the clock framework set the other output dividers later */ /* REVISIT: Set ramp-up delay? */ @@ -492,8 +511,7 @@ int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate, pr_debug("%s: %s: set rate: locking rate to %lu.\n", __func__, __clk_get_name(hw->clk), rate); - ret = omap3_noncore_dpll_program(clk, dd->last_rounded_m, - dd->last_rounded_n, freqsel); + ret = omap3_noncore_dpll_program(clk, freqsel); if (!ret) new_parent = dd->clk_ref; } diff --git a/arch/arm/mach-omap2/dpll44xx.c b/arch/arm/mach-omap2/dpll44xx.c index d3326c474fdc..d28b0f726715 100644 --- a/arch/arm/mach-omap2/dpll44xx.c +++ b/arch/arm/mach-omap2/dpll44xx.c @@ -20,6 +20,15 @@ #include "clock44xx.h" #include "cm-regbits-44xx.h" +/* + * Maximum DPLL input frequency (FINT) and output frequency (FOUT) that + * can supported when using the DPLL low-power mode. Frequencies are + * defined in OMAP4430/60 Public TRM section 3.6.3.3.2 "Enable Control, + * Status, and Low-Power Operation Mode". + */ +#define OMAP4_DPLL_LP_FINT_MAX 1000000 +#define OMAP4_DPLL_LP_FOUT_MAX 100000000 + /* Supported only on OMAP4 */ int omap4_dpllmx_gatectrl_read(struct clk_hw_omap *clk) { @@ -82,6 +91,31 @@ const struct clk_hw_omap_ops clkhwops_omap4_dpllmx = { }; /** + * omap4_dpll_lpmode_recalc - compute DPLL low-power setting + * @dd: pointer to the dpll data structure + * + * Calculates if low-power mode can be enabled based upon the last + * multiplier and divider values calculated. If low-power mode can be + * enabled, then the bit to enable low-power mode is stored in the + * last_rounded_lpmode variable. This implementation is based upon the + * criteria for enabling low-power mode as described in the OMAP4430/60 + * Public TRM section 3.6.3.3.2 "Enable Control, Status, and Low-Power + * Operation Mode". + */ +static void omap4_dpll_lpmode_recalc(struct dpll_data *dd) +{ + long fint, fout; + + fint = __clk_get_rate(dd->clk_ref) / (dd->last_rounded_n + 1); + fout = fint * dd->last_rounded_m; + + if ((fint < OMAP4_DPLL_LP_FINT_MAX) && (fout < OMAP4_DPLL_LP_FOUT_MAX)) + dd->last_rounded_lpmode = 1; + else + dd->last_rounded_lpmode = 0; +} + +/** * omap4_dpll_regm4xen_recalc - compute DPLL rate, considering REGM4XEN bit * @clk: struct clk * of the DPLL to compute the rate for * @@ -130,7 +164,6 @@ long omap4_dpll_regm4xen_round_rate(struct clk_hw *hw, unsigned long *parent_rate) { struct clk_hw_omap *clk = to_clk_hw_omap(hw); - u32 v; struct dpll_data *dd; long r; @@ -139,18 +172,31 @@ long omap4_dpll_regm4xen_round_rate(struct clk_hw *hw, dd = clk->dpll_data; - /* regm4xen adds a multiplier of 4 to DPLL calculations */ - v = __raw_readl(dd->control_reg) & OMAP4430_DPLL_REGM4XEN_MASK; - - if (v) - target_rate = target_rate / OMAP4430_REGM4XEN_MULT; + dd->last_rounded_m4xen = 0; + /* + * First try to compute the DPLL configuration for + * target rate without using the 4X multiplier. + */ r = omap2_dpll_round_rate(hw, target_rate, NULL); + if (r != ~0) + goto out; + + /* + * If we did not find a valid DPLL configuration, try again, but + * this time see if using the 4X multiplier can help. Enabling the + * 4X multiplier is equivalent to dividing the target rate by 4. + */ + r = omap2_dpll_round_rate(hw, target_rate / OMAP4430_REGM4XEN_MULT, + NULL); if (r == ~0) return r; - if (v) - clk->dpll_data->last_rounded_rate *= OMAP4430_REGM4XEN_MULT; + dd->last_rounded_rate *= OMAP4430_REGM4XEN_MULT; + dd->last_rounded_m4xen = 1; + +out: + omap4_dpll_lpmode_recalc(dd); - return clk->dpll_data->last_rounded_rate; + return dd->last_rounded_rate; } |