提交 3cb0df93 编写于 作者: A Arnd Bergmann

Merge tag 'omap-for-v3.19/clocks-and-pm' of...

Merge tag 'omap-for-v3.19/clocks-and-pm' of git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap into next/soc

Pull "omap soc changes for v3.19" from Tony Lindgren:

SoC related changes for omaps. Mostly to make PM easier to use for
omap4 and later, and to fix clock DPLL fixes by adding determine_rate
and set_rate_and_parent.

* tag 'omap-for-v3.19/clocks-and-pm' of git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap:
  ARM: OMAP2+: hwmod: drop unnecessary list initialization
  ARM: OMAP3+: DPLL: use determine_rate() and set_rate_and_parent()
  ARM: OMAP3: clock: add support for dpll4_set_rate_and_parent
  ARM: OMAP4: clock: add support for determine_rate for omap4 regm4xen DPLL
  ARM: OMAP3: clock: add new rate changing logic support for noncore DPLLs
  ARM: OMAP3: clock: use clk_features flags for omap3 DPLL4 checks
  ARM: OMAP4+: PM: Program CPU logic power state
  ARM: OMAP4+: PM: Centralize static dependency mapping table
  ARM: OMAP4: PM: Only do static dependency configuration in omap4_init_static_deps
Signed-off-by: NArnd Bergmann <arnd@arndb.de>
...@@ -257,6 +257,9 @@ static const struct clk_ops dpll1_ck_ops = { ...@@ -257,6 +257,9 @@ static const struct clk_ops dpll1_ck_ops = {
.get_parent = &omap2_init_dpll_parent, .get_parent = &omap2_init_dpll_parent,
.recalc_rate = &omap3_dpll_recalc, .recalc_rate = &omap3_dpll_recalc,
.set_rate = &omap3_noncore_dpll_set_rate, .set_rate = &omap3_noncore_dpll_set_rate,
.set_parent = &omap3_noncore_dpll_set_parent,
.set_rate_and_parent = &omap3_noncore_dpll_set_rate_and_parent,
.determine_rate = &omap3_noncore_dpll_determine_rate,
.round_rate = &omap2_dpll_round_rate, .round_rate = &omap2_dpll_round_rate,
}; };
...@@ -367,6 +370,9 @@ static const struct clk_ops dpll4_ck_ops = { ...@@ -367,6 +370,9 @@ static const struct clk_ops dpll4_ck_ops = {
.get_parent = &omap2_init_dpll_parent, .get_parent = &omap2_init_dpll_parent,
.recalc_rate = &omap3_dpll_recalc, .recalc_rate = &omap3_dpll_recalc,
.set_rate = &omap3_dpll4_set_rate, .set_rate = &omap3_dpll4_set_rate,
.set_parent = &omap3_noncore_dpll_set_parent,
.set_rate_and_parent = &omap3_dpll4_set_rate_and_parent,
.determine_rate = &omap3_noncore_dpll_determine_rate,
.round_rate = &omap2_dpll_round_rate, .round_rate = &omap2_dpll_round_rate,
}; };
......
...@@ -771,4 +771,8 @@ void __init ti_clk_init_features(void) ...@@ -771,4 +771,8 @@ void __init ti_clk_init_features(void)
ti_clk_features.cm_idlest_val = OMAP24XX_CM_IDLEST_VAL; ti_clk_features.cm_idlest_val = OMAP24XX_CM_IDLEST_VAL;
else if (cpu_is_omap34xx()) else if (cpu_is_omap34xx())
ti_clk_features.cm_idlest_val = OMAP34XX_CM_IDLEST_VAL; ti_clk_features.cm_idlest_val = OMAP34XX_CM_IDLEST_VAL;
/* On OMAP3430 ES1.0, DPLL4 can't be re-programmed */
if (omap_rev() == OMAP3430_REV_ES1_0)
ti_clk_features.flags |= TI_CLK_DPLL4_DENY_REPROGRAM;
} }
...@@ -234,6 +234,7 @@ struct ti_clk_features { ...@@ -234,6 +234,7 @@ struct ti_clk_features {
}; };
#define TI_CLK_DPLL_HAS_FREQSEL (1 << 0) #define TI_CLK_DPLL_HAS_FREQSEL (1 << 0)
#define TI_CLK_DPLL4_DENY_REPROGRAM (1 << 1)
extern struct ti_clk_features ti_clk_features; extern struct ti_clk_features ti_clk_features;
......
...@@ -38,6 +38,18 @@ ...@@ -38,6 +38,18 @@
/* needed by omap3_core_dpll_m2_set_rate() */ /* needed by omap3_core_dpll_m2_set_rate() */
struct clk *sdrc_ick_p, *arm_fck_p; struct clk *sdrc_ick_p, *arm_fck_p;
/**
* omap3_dpll4_set_rate - set rate for omap3 per-dpll
* @hw: clock to change
* @rate: target rate for clock
* @parent_rate: rate of the parent clock
*
* Check if the current SoC supports the per-dpll reprogram operation
* or not, and then do the rate change if supported. Returns -EINVAL
* if not supported, 0 for success, and potential error codes from the
* clock rate change.
*/
int omap3_dpll4_set_rate(struct clk_hw *hw, unsigned long rate, int omap3_dpll4_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate) unsigned long parent_rate)
{ {
...@@ -46,7 +58,7 @@ int omap3_dpll4_set_rate(struct clk_hw *hw, unsigned long rate, ...@@ -46,7 +58,7 @@ int omap3_dpll4_set_rate(struct clk_hw *hw, unsigned long rate,
* on 3430ES1 prevents us from changing DPLL multipliers or dividers * on 3430ES1 prevents us from changing DPLL multipliers or dividers
* on DPLL4. * on DPLL4.
*/ */
if (omap_rev() == OMAP3430_REV_ES1_0) { if (ti_clk_features.flags & TI_CLK_DPLL4_DENY_REPROGRAM) {
pr_err("clock: DPLL4 cannot change rate due to silicon 'Limitation 2.5' on 3430ES1.\n"); pr_err("clock: DPLL4 cannot change rate due to silicon 'Limitation 2.5' on 3430ES1.\n");
return -EINVAL; return -EINVAL;
} }
...@@ -54,6 +66,30 @@ int omap3_dpll4_set_rate(struct clk_hw *hw, unsigned long rate, ...@@ -54,6 +66,30 @@ int omap3_dpll4_set_rate(struct clk_hw *hw, unsigned long rate,
return omap3_noncore_dpll_set_rate(hw, rate, parent_rate); return omap3_noncore_dpll_set_rate(hw, rate, parent_rate);
} }
/**
* omap3_dpll4_set_rate_and_parent - set rate and parent for omap3 per-dpll
* @hw: clock to change
* @rate: target rate for clock
* @parent_rate: rate of the parent clock
* @index: parent index, 0 - reference clock, 1 - bypass clock
*
* Check if the current SoC support the per-dpll reprogram operation
* or not, and then do the rate + parent change if supported. Returns
* -EINVAL if not supported, 0 for success, and potential error codes
* from the clock rate change.
*/
int omap3_dpll4_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate, u8 index)
{
if (ti_clk_features.flags & TI_CLK_DPLL4_DENY_REPROGRAM) {
pr_err("clock: DPLL4 cannot change rate due to silicon 'Limitation 2.5' on 3430ES1.\n");
return -EINVAL;
}
return omap3_noncore_dpll_set_rate_and_parent(hw, rate, parent_rate,
index);
}
void __init omap3_clk_lock_dpll5(void) void __init omap3_clk_lock_dpll5(void)
{ {
struct clk *dpll5_clk; struct clk *dpll5_clk;
......
...@@ -460,25 +460,24 @@ void omap3_noncore_dpll_disable(struct clk_hw *hw) ...@@ -460,25 +460,24 @@ void omap3_noncore_dpll_disable(struct clk_hw *hw)
/* Non-CORE DPLL rate set code */ /* Non-CORE DPLL rate set code */
/** /**
* omap3_noncore_dpll_set_rate - set non-core DPLL rate * omap3_noncore_dpll_determine_rate - determine rate for a DPLL
* @clk: struct clk * of DPLL to set * @hw: pointer to the clock to determine rate for
* @rate: rounded target rate * @rate: target rate for the DPLL
* @best_parent_rate: pointer for returning best parent rate
* @best_parent_clk: pointer for returning best parent clock
* *
* Set the DPLL CLKOUT to the target rate. If the DPLL can enter * Determines which DPLL mode to use for reaching a desired target rate.
* low-power bypass, and the target rate is the bypass source clock * Checks whether the DPLL shall be in bypass or locked mode, and if
* rate, then configure the DPLL for bypass. Otherwise, round the * locked, calculates the M,N values for the DPLL via round-rate.
* target rate if it hasn't been done already, then program and lock * Returns a positive clock rate with success, negative error value
* the DPLL. Returns -EINVAL upon error, or 0 upon success. * in failure.
*/ */
int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate, long omap3_noncore_dpll_determine_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate) unsigned long *best_parent_rate,
struct clk **best_parent_clk)
{ {
struct clk_hw_omap *clk = to_clk_hw_omap(hw); struct clk_hw_omap *clk = to_clk_hw_omap(hw);
struct clk *new_parent = NULL;
unsigned long rrate;
u16 freqsel = 0;
struct dpll_data *dd; struct dpll_data *dd;
int ret;
if (!hw || !rate) if (!hw || !rate)
return -EINVAL; return -EINVAL;
...@@ -489,61 +488,121 @@ int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate, ...@@ -489,61 +488,121 @@ int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate,
if (__clk_get_rate(dd->clk_bypass) == rate && if (__clk_get_rate(dd->clk_bypass) == rate &&
(dd->modes & (1 << DPLL_LOW_POWER_BYPASS))) { (dd->modes & (1 << DPLL_LOW_POWER_BYPASS))) {
pr_debug("%s: %s: set rate: entering bypass.\n", *best_parent_clk = dd->clk_bypass;
__func__, __clk_get_name(hw->clk)); } else {
rate = omap2_dpll_round_rate(hw, rate, best_parent_rate);
*best_parent_clk = dd->clk_ref;
}
*best_parent_rate = rate;
return rate;
}
/**
* omap3_noncore_dpll_set_parent - set parent for a DPLL clock
* @hw: pointer to the clock to set parent for
* @index: parent index to select
*
* Sets parent for a DPLL clock. This sets the DPLL into bypass or
* locked mode. Returns 0 with success, negative error value otherwise.
*/
int omap3_noncore_dpll_set_parent(struct clk_hw *hw, u8 index)
{
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
int ret;
__clk_prepare(dd->clk_bypass); if (!hw)
clk_enable(dd->clk_bypass); return -EINVAL;
if (index)
ret = _omap3_noncore_dpll_bypass(clk); ret = _omap3_noncore_dpll_bypass(clk);
if (!ret) else
new_parent = dd->clk_bypass; ret = _omap3_noncore_dpll_lock(clk);
clk_disable(dd->clk_bypass);
__clk_unprepare(dd->clk_bypass);
} else {
__clk_prepare(dd->clk_ref);
clk_enable(dd->clk_ref);
/* XXX this check is probably pointless in the CCF context */
if (dd->last_rounded_rate != rate) {
rrate = __clk_round_rate(hw->clk, rate);
if (rrate != rate) {
pr_warn("%s: %s: final rate %lu does not match desired rate %lu\n",
__func__, __clk_get_name(hw->clk),
rrate, rate);
rate = rrate;
}
}
if (dd->last_rounded_rate == 0) return ret;
return -EINVAL; }
/* Freqsel is available only on OMAP343X devices */ /**
if (ti_clk_features.flags & TI_CLK_DPLL_HAS_FREQSEL) { * omap3_noncore_dpll_set_rate - set rate for a DPLL clock
freqsel = _omap3_dpll_compute_freqsel(clk, * @hw: pointer to the clock to set parent for
dd->last_rounded_n); * @rate: target rate for the clock
WARN_ON(!freqsel); * @parent_rate: rate of the parent clock
} *
* Sets rate for a DPLL clock. First checks if the clock parent is
* reference clock (in bypass mode, the rate of the clock can't be
* changed) and proceeds with the rate change operation. Returns 0
* with success, negative error value otherwise.
*/
int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
struct dpll_data *dd;
u16 freqsel = 0;
int ret;
if (!hw || !rate)
return -EINVAL;
dd = clk->dpll_data;
if (!dd)
return -EINVAL;
pr_debug("%s: %s: set rate: locking rate to %lu.\n", if (__clk_get_parent(hw->clk) != dd->clk_ref)
__func__, __clk_get_name(hw->clk), rate); return -EINVAL;
if (dd->last_rounded_rate == 0)
return -EINVAL;
ret = omap3_noncore_dpll_program(clk, freqsel); /* Freqsel is available only on OMAP343X devices */
if (!ret) if (ti_clk_features.flags & TI_CLK_DPLL_HAS_FREQSEL) {
new_parent = dd->clk_ref; freqsel = _omap3_dpll_compute_freqsel(clk, dd->last_rounded_n);
clk_disable(dd->clk_ref); WARN_ON(!freqsel);
__clk_unprepare(dd->clk_ref);
} }
/*
* FIXME - this is all wrong. common code handles reparenting and
* migrating prepare/enable counts. dplls should be a multiplexer
* clock and this should be a set_parent operation so that all of that
* stuff is inherited for free
*/
if (!ret && clk_get_parent(hw->clk) != new_parent) pr_debug("%s: %s: set rate: locking rate to %lu.\n", __func__,
__clk_reparent(hw->clk, new_parent); __clk_get_name(hw->clk), rate);
return 0; ret = omap3_noncore_dpll_program(clk, freqsel);
return ret;
}
/**
* omap3_noncore_dpll_set_rate_and_parent - set rate and parent for a DPLL clock
* @hw: pointer to the clock to set rate and parent for
* @rate: target rate for the DPLL
* @parent_rate: clock rate of the DPLL parent
* @index: new parent index for the DPLL, 0 - reference, 1 - bypass
*
* Sets rate and parent for a DPLL clock. If new parent is the bypass
* clock, only selects the parent. Otherwise proceeds with a rate
* change, as this will effectively also change the parent as the
* DPLL is put into locked mode. Returns 0 with success, negative error
* value otherwise.
*/
int omap3_noncore_dpll_set_rate_and_parent(struct clk_hw *hw,
unsigned long rate,
unsigned long parent_rate,
u8 index)
{
int ret;
if (!hw || !rate)
return -EINVAL;
/*
* clk-ref at index[0], in which case we only need to set rate,
* the parent will be changed automatically with the lock sequence.
* With clk-bypass case we only need to change parent.
*/
if (index)
ret = omap3_noncore_dpll_set_parent(hw, index);
else
ret = omap3_noncore_dpll_set_rate(hw, rate, parent_rate);
return ret;
} }
/* DPLL autoidle read/set code */ /* DPLL autoidle read/set code */
......
...@@ -207,3 +207,44 @@ long omap4_dpll_regm4xen_round_rate(struct clk_hw *hw, ...@@ -207,3 +207,44 @@ long omap4_dpll_regm4xen_round_rate(struct clk_hw *hw,
return dd->last_rounded_rate; return dd->last_rounded_rate;
} }
/**
* omap4_dpll_regm4xen_determine_rate - determine rate for a DPLL
* @hw: pointer to the clock to determine rate for
* @rate: target rate for the DPLL
* @best_parent_rate: pointer for returning best parent rate
* @best_parent_clk: pointer for returning best parent clock
*
* Determines which DPLL mode to use for reaching a desired rate.
* Checks whether the DPLL shall be in bypass or locked mode, and if
* locked, calculates the M,N values for the DPLL via round-rate.
* Returns a positive clock rate with success, negative error value
* in failure.
*/
long omap4_dpll_regm4xen_determine_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *best_parent_rate,
struct clk **best_parent_clk)
{
struct clk_hw_omap *clk = to_clk_hw_omap(hw);
struct dpll_data *dd;
if (!hw || !rate)
return -EINVAL;
dd = clk->dpll_data;
if (!dd)
return -EINVAL;
if (__clk_get_rate(dd->clk_bypass) == rate &&
(dd->modes & (1 << DPLL_LOW_POWER_BYPASS))) {
*best_parent_clk = dd->clk_bypass;
} else {
rate = omap4_dpll_regm4xen_round_rate(hw, rate,
best_parent_rate);
*best_parent_clk = dd->clk_ref;
}
*best_parent_rate = rate;
return rate;
}
...@@ -227,7 +227,7 @@ static void __init save_l2x0_context(void) ...@@ -227,7 +227,7 @@ static void __init save_l2x0_context(void)
int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state) int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state)
{ {
struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu); struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu);
unsigned int save_state = 0; unsigned int save_state = 0, cpu_logic_state = PWRDM_POWER_RET;
unsigned int wakeup_cpu; unsigned int wakeup_cpu;
if (omap_rev() == OMAP4430_REV_ES1_0) if (omap_rev() == OMAP4430_REV_ES1_0)
...@@ -239,6 +239,7 @@ int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state) ...@@ -239,6 +239,7 @@ int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state)
save_state = 0; save_state = 0;
break; break;
case PWRDM_POWER_OFF: case PWRDM_POWER_OFF:
cpu_logic_state = PWRDM_POWER_OFF;
save_state = 1; save_state = 1;
break; break;
case PWRDM_POWER_RET: case PWRDM_POWER_RET:
...@@ -270,6 +271,7 @@ int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state) ...@@ -270,6 +271,7 @@ int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state)
cpu_clear_prev_logic_pwrst(cpu); cpu_clear_prev_logic_pwrst(cpu);
pwrdm_set_next_pwrst(pm_info->pwrdm, power_state); pwrdm_set_next_pwrst(pm_info->pwrdm, power_state);
pwrdm_set_logic_retst(pm_info->pwrdm, cpu_logic_state);
set_cpu_wakeup_addr(cpu, virt_to_phys(omap_pm_ops.resume)); set_cpu_wakeup_addr(cpu, virt_to_phys(omap_pm_ops.resume));
omap_pm_ops.scu_prepare(cpu, power_state); omap_pm_ops.scu_prepare(cpu, power_state);
l2x0_pwrst_prepare(cpu, save_state); l2x0_pwrst_prepare(cpu, save_state);
......
...@@ -2832,12 +2832,10 @@ static int __init _add_link(struct omap_hwmod_ocp_if *oi) ...@@ -2832,12 +2832,10 @@ static int __init _add_link(struct omap_hwmod_ocp_if *oi)
_alloc_links(&ml, &sl); _alloc_links(&ml, &sl);
ml->ocp_if = oi; ml->ocp_if = oi;
INIT_LIST_HEAD(&ml->node);
list_add(&ml->node, &oi->master->master_ports); list_add(&ml->node, &oi->master->master_ports);
oi->master->masters_cnt++; oi->master->masters_cnt++;
sl->ocp_if = oi; sl->ocp_if = oi;
INIT_LIST_HEAD(&sl->node);
list_add(&sl->node, &oi->slave->slave_ports); list_add(&sl->node, &oi->slave->slave_ports);
oi->slave->slaves_cnt++; oi->slave->slaves_cnt++;
......
...@@ -37,6 +37,16 @@ struct power_state { ...@@ -37,6 +37,16 @@ struct power_state {
struct list_head node; struct list_head node;
}; };
/**
* struct static_dep_map - Static dependency map
* @from: from clockdomain
* @to: to clockdomain
*/
struct static_dep_map {
const char *from;
const char *to;
};
static u32 cpu_suspend_state = PWRDM_POWER_OFF; static u32 cpu_suspend_state = PWRDM_POWER_OFF;
static LIST_HEAD(pwrst_list); static LIST_HEAD(pwrst_list);
...@@ -148,94 +158,61 @@ static void omap_default_idle(void) ...@@ -148,94 +158,61 @@ static void omap_default_idle(void)
omap_do_wfi(); omap_do_wfi();
} }
/** /*
* omap4_init_static_deps - Add OMAP4 static dependencies * The dynamic dependency between MPUSS -> MEMIF and
* * MPUSS -> L4_PER/L3_* and DUCATI -> L3_* doesn't work as
* Add needed static clockdomain dependencies on OMAP4 devices. * expected. The hardware recommendation is to enable static
* Return: 0 on success or 'err' on failures * dependencies for these to avoid system lock ups or random crashes.
* The L4 wakeup depedency is added to workaround the OCP sync hardware
* BUG with 32K synctimer which lead to incorrect timer value read
* from the 32K counter. The BUG applies for GPTIMER1 and WDT2 which
* are part of L4 wakeup clockdomain.
*/ */
static inline int omap4_init_static_deps(void) static const struct static_dep_map omap4_static_dep_map[] = {
{ {.from = "mpuss_clkdm", .to = "l3_emif_clkdm"},
struct clockdomain *emif_clkdm, *mpuss_clkdm, *l3_1_clkdm; {.from = "mpuss_clkdm", .to = "l3_1_clkdm"},
struct clockdomain *ducati_clkdm, *l3_2_clkdm; {.from = "mpuss_clkdm", .to = "l3_2_clkdm"},
int ret = 0; {.from = "ducati_clkdm", .to = "l3_1_clkdm"},
{.from = "ducati_clkdm", .to = "l3_2_clkdm"},
if (omap_rev() == OMAP4430_REV_ES1_0) { {.from = NULL} /* TERMINATION */
WARN(1, "Power Management not supported on OMAP4430 ES1.0\n"); };
return -ENODEV;
}
pr_err("Power Management for TI OMAP4.\n");
/*
* OMAP4 chip PM currently works only with certain (newer)
* versions of bootloaders. This is due to missing code in the
* kernel to properly reset and initialize some devices.
* http://www.spinics.net/lists/arm-kernel/msg218641.html
*/
pr_warn("OMAP4 PM: u-boot >= v2012.07 is required for full PM support\n");
ret = pwrdm_for_each(pwrdms_setup, NULL);
if (ret) {
pr_err("Failed to setup powerdomains\n");
return ret;
}
/*
* The dynamic dependency between MPUSS -> MEMIF and
* MPUSS -> L4_PER/L3_* and DUCATI -> L3_* doesn't work as
* expected. The hardware recommendation is to enable static
* dependencies for these to avoid system lock ups or random crashes.
* The L4 wakeup depedency is added to workaround the OCP sync hardware
* BUG with 32K synctimer which lead to incorrect timer value read
* from the 32K counter. The BUG applies for GPTIMER1 and WDT2 which
* are part of L4 wakeup clockdomain.
*/
mpuss_clkdm = clkdm_lookup("mpuss_clkdm");
emif_clkdm = clkdm_lookup("l3_emif_clkdm");
l3_1_clkdm = clkdm_lookup("l3_1_clkdm");
l3_2_clkdm = clkdm_lookup("l3_2_clkdm");
ducati_clkdm = clkdm_lookup("ducati_clkdm");
if ((!mpuss_clkdm) || (!emif_clkdm) || (!l3_1_clkdm) ||
(!l3_2_clkdm) || (!ducati_clkdm))
return -EINVAL;
ret = clkdm_add_wkdep(mpuss_clkdm, emif_clkdm);
ret |= clkdm_add_wkdep(mpuss_clkdm, l3_1_clkdm);
ret |= clkdm_add_wkdep(mpuss_clkdm, l3_2_clkdm);
ret |= clkdm_add_wkdep(ducati_clkdm, l3_1_clkdm);
ret |= clkdm_add_wkdep(ducati_clkdm, l3_2_clkdm);
if (ret) {
pr_err("Failed to add MPUSS -> L3/EMIF/L4PER, DUCATI -> L3 wakeup dependency\n");
return -EINVAL;
}
return ret; static const struct static_dep_map omap5_dra7_static_dep_map[] = {
} {.from = "mpu_clkdm", .to = "emif_clkdm"},
{.from = NULL} /* TERMINATION */
};
/** /**
* omap5_dra7_init_static_deps - Init static clkdm dependencies on OMAP5 and * omap4plus_init_static_deps() - Initialize a static dependency map
* DRA7 * @map: Mapping of clock domains
*
* The dynamic dependency between MPUSS -> EMIF is broken and has
* not worked as expected. The hardware recommendation is to
* enable static dependencies for these to avoid system
* lock ups or random crashes.
*/ */
static inline int omap5_dra7_init_static_deps(void) static inline int omap4plus_init_static_deps(const struct static_dep_map *map)
{ {
struct clockdomain *mpuss_clkdm, *emif_clkdm;
int ret; int ret;
struct clockdomain *from, *to;
if (!map)
return 0;
mpuss_clkdm = clkdm_lookup("mpu_clkdm"); while (map->from) {
emif_clkdm = clkdm_lookup("emif_clkdm"); from = clkdm_lookup(map->from);
if (!mpuss_clkdm || !emif_clkdm) to = clkdm_lookup(map->to);
return -EINVAL; if (!from || !to) {
pr_err("Failed lookup %s or %s for wakeup dependency\n",
map->from, map->to);
return -EINVAL;
}
ret = clkdm_add_wkdep(from, to);
if (ret) {
pr_err("Failed to add %s -> %s wakeup dependency(%d)\n",
map->from, map->to, ret);
return ret;
}
ret = clkdm_add_wkdep(mpuss_clkdm, emif_clkdm); map++;
if (ret) };
pr_err("Failed to add MPUSS -> EMIF wakeup dependency\n");
return ret; return 0;
} }
/** /**
...@@ -272,6 +249,15 @@ int __init omap4_pm_init(void) ...@@ -272,6 +249,15 @@ int __init omap4_pm_init(void)
pr_info("Power Management for TI OMAP4+ devices.\n"); pr_info("Power Management for TI OMAP4+ devices.\n");
/*
* OMAP4 chip PM currently works only with certain (newer)
* versions of bootloaders. This is due to missing code in the
* kernel to properly reset and initialize some devices.
* http://www.spinics.net/lists/arm-kernel/msg218641.html
*/
if (cpu_is_omap44xx())
pr_warn("OMAP4 PM: u-boot >= v2012.07 is required for full PM support\n");
ret = pwrdm_for_each(pwrdms_setup, NULL); ret = pwrdm_for_each(pwrdms_setup, NULL);
if (ret) { if (ret) {
pr_err("Failed to setup powerdomains.\n"); pr_err("Failed to setup powerdomains.\n");
...@@ -279,9 +265,9 @@ int __init omap4_pm_init(void) ...@@ -279,9 +265,9 @@ int __init omap4_pm_init(void)
} }
if (cpu_is_omap44xx()) if (cpu_is_omap44xx())
ret = omap4_init_static_deps(); ret = omap4plus_init_static_deps(omap4_static_dep_map);
else if (soc_is_omap54xx() || soc_is_dra7xx()) else if (soc_is_omap54xx() || soc_is_dra7xx())
ret = omap5_dra7_init_static_deps(); ret = omap4plus_init_static_deps(omap5_dra7_static_dep_map);
if (ret) { if (ret) {
pr_err("Failed to initialise static dependencies.\n"); pr_err("Failed to initialise static dependencies.\n");
......
...@@ -33,6 +33,9 @@ static const struct clk_ops dpll_m4xen_ck_ops = { ...@@ -33,6 +33,9 @@ static const struct clk_ops dpll_m4xen_ck_ops = {
.recalc_rate = &omap4_dpll_regm4xen_recalc, .recalc_rate = &omap4_dpll_regm4xen_recalc,
.round_rate = &omap4_dpll_regm4xen_round_rate, .round_rate = &omap4_dpll_regm4xen_round_rate,
.set_rate = &omap3_noncore_dpll_set_rate, .set_rate = &omap3_noncore_dpll_set_rate,
.set_parent = &omap3_noncore_dpll_set_parent,
.set_rate_and_parent = &omap3_noncore_dpll_set_rate_and_parent,
.determine_rate = &omap4_dpll_regm4xen_determine_rate,
.get_parent = &omap2_init_dpll_parent, .get_parent = &omap2_init_dpll_parent,
}; };
#else #else
...@@ -53,6 +56,9 @@ static const struct clk_ops dpll_ck_ops = { ...@@ -53,6 +56,9 @@ static const struct clk_ops dpll_ck_ops = {
.recalc_rate = &omap3_dpll_recalc, .recalc_rate = &omap3_dpll_recalc,
.round_rate = &omap2_dpll_round_rate, .round_rate = &omap2_dpll_round_rate,
.set_rate = &omap3_noncore_dpll_set_rate, .set_rate = &omap3_noncore_dpll_set_rate,
.set_parent = &omap3_noncore_dpll_set_parent,
.set_rate_and_parent = &omap3_noncore_dpll_set_rate_and_parent,
.determine_rate = &omap3_noncore_dpll_determine_rate,
.get_parent = &omap2_init_dpll_parent, .get_parent = &omap2_init_dpll_parent,
}; };
...@@ -61,6 +67,9 @@ static const struct clk_ops dpll_no_gate_ck_ops = { ...@@ -61,6 +67,9 @@ static const struct clk_ops dpll_no_gate_ck_ops = {
.get_parent = &omap2_init_dpll_parent, .get_parent = &omap2_init_dpll_parent,
.round_rate = &omap2_dpll_round_rate, .round_rate = &omap2_dpll_round_rate,
.set_rate = &omap3_noncore_dpll_set_rate, .set_rate = &omap3_noncore_dpll_set_rate,
.set_parent = &omap3_noncore_dpll_set_parent,
.set_rate_and_parent = &omap3_noncore_dpll_set_rate_and_parent,
.determine_rate = &omap3_noncore_dpll_determine_rate,
}; };
#else #else
static const struct clk_ops dpll_core_ck_ops = {}; static const struct clk_ops dpll_core_ck_ops = {};
...@@ -97,6 +106,9 @@ static const struct clk_ops omap3_dpll_ck_ops = { ...@@ -97,6 +106,9 @@ static const struct clk_ops omap3_dpll_ck_ops = {
.get_parent = &omap2_init_dpll_parent, .get_parent = &omap2_init_dpll_parent,
.recalc_rate = &omap3_dpll_recalc, .recalc_rate = &omap3_dpll_recalc,
.set_rate = &omap3_noncore_dpll_set_rate, .set_rate = &omap3_noncore_dpll_set_rate,
.set_parent = &omap3_noncore_dpll_set_parent,
.set_rate_and_parent = &omap3_noncore_dpll_set_rate_and_parent,
.determine_rate = &omap3_noncore_dpll_determine_rate,
.round_rate = &omap2_dpll_round_rate, .round_rate = &omap2_dpll_round_rate,
}; };
...@@ -106,6 +118,9 @@ static const struct clk_ops omap3_dpll_per_ck_ops = { ...@@ -106,6 +118,9 @@ static const struct clk_ops omap3_dpll_per_ck_ops = {
.get_parent = &omap2_init_dpll_parent, .get_parent = &omap2_init_dpll_parent,
.recalc_rate = &omap3_dpll_recalc, .recalc_rate = &omap3_dpll_recalc,
.set_rate = &omap3_dpll4_set_rate, .set_rate = &omap3_dpll4_set_rate,
.set_parent = &omap3_noncore_dpll_set_parent,
.set_rate_and_parent = &omap3_dpll4_set_rate_and_parent,
.determine_rate = &omap3_noncore_dpll_determine_rate,
.round_rate = &omap2_dpll_round_rate, .round_rate = &omap2_dpll_round_rate,
}; };
#endif #endif
......
...@@ -254,13 +254,26 @@ extern const struct clk_ops ti_clk_mux_ops; ...@@ -254,13 +254,26 @@ extern const struct clk_ops ti_clk_mux_ops;
void omap2_init_clk_hw_omap_clocks(struct clk *clk); void omap2_init_clk_hw_omap_clocks(struct clk *clk);
int omap3_noncore_dpll_enable(struct clk_hw *hw); int omap3_noncore_dpll_enable(struct clk_hw *hw);
void omap3_noncore_dpll_disable(struct clk_hw *hw); void omap3_noncore_dpll_disable(struct clk_hw *hw);
int omap3_noncore_dpll_set_parent(struct clk_hw *hw, u8 index);
int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate, int omap3_noncore_dpll_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate); unsigned long parent_rate);
int omap3_noncore_dpll_set_rate_and_parent(struct clk_hw *hw,
unsigned long rate,
unsigned long parent_rate,
u8 index);
long omap3_noncore_dpll_determine_rate(struct clk_hw *hw,
unsigned long rate,
unsigned long *best_parent_rate,
struct clk **best_parent_clk);
unsigned long omap4_dpll_regm4xen_recalc(struct clk_hw *hw, unsigned long omap4_dpll_regm4xen_recalc(struct clk_hw *hw,
unsigned long parent_rate); unsigned long parent_rate);
long omap4_dpll_regm4xen_round_rate(struct clk_hw *hw, long omap4_dpll_regm4xen_round_rate(struct clk_hw *hw,
unsigned long target_rate, unsigned long target_rate,
unsigned long *parent_rate); unsigned long *parent_rate);
long omap4_dpll_regm4xen_determine_rate(struct clk_hw *hw,
unsigned long rate,
unsigned long *best_parent_rate,
struct clk **best_parent_clk);
u8 omap2_init_dpll_parent(struct clk_hw *hw); u8 omap2_init_dpll_parent(struct clk_hw *hw);
unsigned long omap3_dpll_recalc(struct clk_hw *hw, unsigned long parent_rate); unsigned long omap3_dpll_recalc(struct clk_hw *hw, unsigned long parent_rate);
long omap2_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate, long omap2_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate,
...@@ -278,6 +291,8 @@ int omap2_clk_disable_autoidle_all(void); ...@@ -278,6 +291,8 @@ int omap2_clk_disable_autoidle_all(void);
void omap2_clk_enable_init_clocks(const char **clk_names, u8 num_clocks); void omap2_clk_enable_init_clocks(const char **clk_names, u8 num_clocks);
int omap3_dpll4_set_rate(struct clk_hw *clk, unsigned long rate, int omap3_dpll4_set_rate(struct clk_hw *clk, unsigned long rate,
unsigned long parent_rate); unsigned long parent_rate);
int omap3_dpll4_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate, u8 index);
int omap2_dflt_clk_enable(struct clk_hw *hw); int omap2_dflt_clk_enable(struct clk_hw *hw);
void omap2_dflt_clk_disable(struct clk_hw *hw); void omap2_dflt_clk_disable(struct clk_hw *hw);
int omap2_dflt_clk_is_enabled(struct clk_hw *hw); int omap2_dflt_clk_is_enabled(struct clk_hw *hw);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册