summaryrefslogtreecommitdiff
path: root/arch/arm/plat-omap
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/plat-omap')
-rw-r--r--arch/arm/plat-omap/clock.c99
-rw-r--r--arch/arm/plat-omap/dmtimer.c4
-rw-r--r--arch/arm/plat-omap/include/plat/clock.h27
-rw-r--r--arch/arm/plat-omap/include/plat/omap_hwmod.h3
-rw-r--r--arch/arm/plat-omap/omap_device.c36
5 files changed, 124 insertions, 45 deletions
diff --git a/arch/arm/plat-omap/clock.c b/arch/arm/plat-omap/clock.c
index fc62fb5fc20b..c9122dd6ee8d 100644
--- a/arch/arm/plat-omap/clock.c
+++ b/arch/arm/plat-omap/clock.c
@@ -37,14 +37,16 @@ static struct clk_functions *arch_clock;
int clk_enable(struct clk *clk)
{
unsigned long flags;
- int ret = 0;
+ int ret;
if (clk == NULL || IS_ERR(clk))
return -EINVAL;
+ if (!arch_clock || !arch_clock->clk_enable)
+ return -EINVAL;
+
spin_lock_irqsave(&clockfw_lock, flags);
- if (arch_clock->clk_enable)
- ret = arch_clock->clk_enable(clk);
+ ret = arch_clock->clk_enable(clk);
spin_unlock_irqrestore(&clockfw_lock, flags);
return ret;
@@ -58,6 +60,9 @@ void clk_disable(struct clk *clk)
if (clk == NULL || IS_ERR(clk))
return;
+ if (!arch_clock || !arch_clock->clk_disable)
+ return;
+
spin_lock_irqsave(&clockfw_lock, flags);
if (clk->usecount == 0) {
pr_err("Trying disable clock %s with 0 usecount\n",
@@ -66,8 +71,7 @@ void clk_disable(struct clk *clk)
goto out;
}
- if (arch_clock->clk_disable)
- arch_clock->clk_disable(clk);
+ arch_clock->clk_disable(clk);
out:
spin_unlock_irqrestore(&clockfw_lock, flags);
@@ -77,7 +81,7 @@ EXPORT_SYMBOL(clk_disable);
unsigned long clk_get_rate(struct clk *clk)
{
unsigned long flags;
- unsigned long ret = 0;
+ unsigned long ret;
if (clk == NULL || IS_ERR(clk))
return 0;
@@ -97,14 +101,16 @@ EXPORT_SYMBOL(clk_get_rate);
long clk_round_rate(struct clk *clk, unsigned long rate)
{
unsigned long flags;
- long ret = 0;
+ long ret;
if (clk == NULL || IS_ERR(clk))
- return ret;
+ return 0;
+
+ if (!arch_clock || !arch_clock->clk_round_rate)
+ return 0;
spin_lock_irqsave(&clockfw_lock, flags);
- if (arch_clock->clk_round_rate)
- ret = arch_clock->clk_round_rate(clk, rate);
+ ret = arch_clock->clk_round_rate(clk, rate);
spin_unlock_irqrestore(&clockfw_lock, flags);
return ret;
@@ -119,14 +125,13 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
if (clk == NULL || IS_ERR(clk))
return ret;
+ if (!arch_clock || !arch_clock->clk_set_rate)
+ return ret;
+
spin_lock_irqsave(&clockfw_lock, flags);
- if (arch_clock->clk_set_rate)
- ret = arch_clock->clk_set_rate(clk, rate);
- if (ret == 0) {
- if (clk->recalc)
- clk->rate = clk->recalc(clk);
+ ret = arch_clock->clk_set_rate(clk, rate);
+ if (ret == 0)
propagate_rate(clk);
- }
spin_unlock_irqrestore(&clockfw_lock, flags);
return ret;
@@ -141,15 +146,14 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
if (clk == NULL || IS_ERR(clk) || parent == NULL || IS_ERR(parent))
return ret;
+ if (!arch_clock || !arch_clock->clk_set_parent)
+ return ret;
+
spin_lock_irqsave(&clockfw_lock, flags);
if (clk->usecount == 0) {
- if (arch_clock->clk_set_parent)
- ret = arch_clock->clk_set_parent(clk, parent);
- if (ret == 0) {
- if (clk->recalc)
- clk->rate = clk->recalc(clk);
+ ret = arch_clock->clk_set_parent(clk, parent);
+ if (ret == 0)
propagate_rate(clk);
- }
} else
ret = -EBUSY;
spin_unlock_irqrestore(&clockfw_lock, flags);
@@ -335,6 +339,38 @@ struct clk *omap_clk_get_by_name(const char *name)
return ret;
}
+int omap_clk_enable_autoidle_all(void)
+{
+ struct clk *c;
+ unsigned long flags;
+
+ spin_lock_irqsave(&clockfw_lock, flags);
+
+ list_for_each_entry(c, &clocks, node)
+ if (c->ops->allow_idle)
+ c->ops->allow_idle(c);
+
+ spin_unlock_irqrestore(&clockfw_lock, flags);
+
+ return 0;
+}
+
+int omap_clk_disable_autoidle_all(void)
+{
+ struct clk *c;
+ unsigned long flags;
+
+ spin_lock_irqsave(&clockfw_lock, flags);
+
+ list_for_each_entry(c, &clocks, node)
+ if (c->ops->deny_idle)
+ c->ops->deny_idle(c);
+
+ spin_unlock_irqrestore(&clockfw_lock, flags);
+
+ return 0;
+}
+
/*
* Low level helpers
*/
@@ -367,9 +403,11 @@ void clk_init_cpufreq_table(struct cpufreq_frequency_table **table)
{
unsigned long flags;
+ if (!arch_clock || !arch_clock->clk_init_cpufreq_table)
+ return;
+
spin_lock_irqsave(&clockfw_lock, flags);
- if (arch_clock->clk_init_cpufreq_table)
- arch_clock->clk_init_cpufreq_table(table);
+ arch_clock->clk_init_cpufreq_table(table);
spin_unlock_irqrestore(&clockfw_lock, flags);
}
@@ -377,9 +415,11 @@ void clk_exit_cpufreq_table(struct cpufreq_frequency_table **table)
{
unsigned long flags;
+ if (!arch_clock || !arch_clock->clk_exit_cpufreq_table)
+ return;
+
spin_lock_irqsave(&clockfw_lock, flags);
- if (arch_clock->clk_exit_cpufreq_table)
- arch_clock->clk_exit_cpufreq_table(table);
+ arch_clock->clk_exit_cpufreq_table(table);
spin_unlock_irqrestore(&clockfw_lock, flags);
}
#endif
@@ -397,6 +437,9 @@ static int __init clk_disable_unused(void)
struct clk *ck;
unsigned long flags;
+ if (!arch_clock || !arch_clock->clk_disable_unused)
+ return 0;
+
pr_info("clock: disabling unused clocks to save power\n");
list_for_each_entry(ck, &clocks, node) {
if (ck->ops == &clkops_null)
@@ -406,14 +449,14 @@ static int __init clk_disable_unused(void)
continue;
spin_lock_irqsave(&clockfw_lock, flags);
- if (arch_clock->clk_disable_unused)
- arch_clock->clk_disable_unused(ck);
+ arch_clock->clk_disable_unused(ck);
spin_unlock_irqrestore(&clockfw_lock, flags);
}
return 0;
}
late_initcall(clk_disable_unused);
+late_initcall(omap_clk_enable_autoidle_all);
#endif
int __init clk_init(struct clk_functions * custom_clocks)
diff --git a/arch/arm/plat-omap/dmtimer.c b/arch/arm/plat-omap/dmtimer.c
index 1d706cf63ca0..ee9f6ebba29b 100644
--- a/arch/arm/plat-omap/dmtimer.c
+++ b/arch/arm/plat-omap/dmtimer.c
@@ -342,6 +342,10 @@ static void omap_dm_timer_reset(struct omap_dm_timer *timer)
l |= 0x02 << 3; /* Set to smart-idle mode */
l |= 0x2 << 8; /* Set clock activity to perserve f-clock on idle */
+ /* Enable autoidle on OMAP2 / OMAP3 */
+ if (cpu_is_omap24xx() || cpu_is_omap34xx())
+ l |= 0x1 << 0;
+
/*
* Enable wake-up on OMAP2 CPUs.
*/
diff --git a/arch/arm/plat-omap/include/plat/clock.h b/arch/arm/plat-omap/include/plat/clock.h
index d43e6234dbbb..006e599c6613 100644
--- a/arch/arm/plat-omap/include/plat/clock.h
+++ b/arch/arm/plat-omap/include/plat/clock.h
@@ -25,6 +25,8 @@ struct clockdomain;
* @disable: fn ptr that enables the current clock in hardware
* @find_idlest: function returning the IDLEST register for the clock's IP blk
* @find_companion: function returning the "companion" clk reg for the clock
+ * @allow_idle: fn ptr that enables autoidle for the current clock in hardware
+ * @deny_idle: fn ptr that disables autoidle for the current clock in hardware
*
* A "companion" clk is an accompanying clock to the one being queried
* that must be enabled for the IP module connected to the clock to
@@ -42,6 +44,8 @@ struct clkops {
u8 *, u8 *);
void (*find_companion)(struct clk *, void __iomem **,
u8 *);
+ void (*allow_idle)(struct clk *);
+ void (*deny_idle)(struct clk *);
};
#ifdef CONFIG_ARCH_OMAP2PLUS
@@ -105,7 +109,6 @@ struct clksel {
* @clk_ref: struct clk pointer to the clock's reference clock input
* @control_reg: register containing the DPLL mode bitfield
* @enable_mask: mask of the DPLL mode bitfield in @control_reg
- * @rate_tolerance: maximum variance allowed from target rate (in Hz)
* @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()
* @max_multiplier: maximum valid non-bypass multiplier value (actual)
@@ -131,12 +134,9 @@ struct clksel {
* XXX Some DPLLs have multiple bypass inputs, so it's not technically
* correct to only have one @clk_bypass pointer.
*
- * XXX @rate_tolerance should probably be deprecated - currently there
- * don't seem to be any usecases for DPLL rounding that is not exact.
- *
* XXX The runtime-variable fields (@last_rounded_rate, @last_rounded_m,
* @last_rounded_n) should be separated from the runtime-fixed fields
- * and placed into a differenct structure, so that the runtime-fixed data
+ * and placed into a different structure, so that the runtime-fixed data
* can be placed into read-only space.
*/
struct dpll_data {
@@ -147,7 +147,6 @@ struct dpll_data {
struct clk *clk_ref;
void __iomem *control_reg;
u32 enable_mask;
- unsigned int rate_tolerance;
unsigned long last_rounded_rate;
u16 last_rounded_m;
u16 max_multiplier;
@@ -172,12 +171,24 @@ struct dpll_data {
#endif
-/* struct clk.flags possibilities */
+/*
+ * struct clk.flags possibilities
+ *
+ * XXX document the rest of the clock flags here
+ *
+ * CLOCK_CLKOUTX2: (OMAP4 only) DPLL CLKOUT and CLKOUTX2 GATE_CTRL
+ * bits share the same register. This flag allows the
+ * omap4_dpllmx*() code to determine which GATE_CTRL bit field
+ * should be used. This is a temporary solution - a better approach
+ * would be to associate clock type-specific data with the clock,
+ * similar to the struct dpll_data approach.
+ */
#define ENABLE_REG_32BIT (1 << 0) /* Use 32-bit access */
#define CLOCK_IDLE_CONTROL (1 << 1)
#define CLOCK_NO_IDLE_PARENT (1 << 2)
#define ENABLE_ON_INIT (1 << 3) /* Enable upon framework init */
#define INVERT_ENABLE (1 << 4) /* 0 enables, 1 disables */
+#define CLOCK_CLKOUTX2 (1 << 5)
/**
* struct clk - OMAP struct clk
@@ -293,6 +304,8 @@ extern void clk_init_cpufreq_table(struct cpufreq_frequency_table **table);
extern void clk_exit_cpufreq_table(struct cpufreq_frequency_table **table);
#endif
extern struct clk *omap_clk_get_by_name(const char *name);
+extern int omap_clk_enable_autoidle_all(void);
+extern int omap_clk_disable_autoidle_all(void);
extern const struct clkops clkops_null;
diff --git a/arch/arm/plat-omap/include/plat/omap_hwmod.h b/arch/arm/plat-omap/include/plat/omap_hwmod.h
index 97aa8e763e16..8a1368fbbbd3 100644
--- a/arch/arm/plat-omap/include/plat/omap_hwmod.h
+++ b/arch/arm/plat-omap/include/plat/omap_hwmod.h
@@ -561,6 +561,7 @@ int omap_hwmod_enable_clocks(struct omap_hwmod *oh);
int omap_hwmod_disable_clocks(struct omap_hwmod *oh);
int omap_hwmod_set_slave_idlemode(struct omap_hwmod *oh, u8 idlemode);
+int omap_hwmod_set_ocp_autoidle(struct omap_hwmod *oh, u8 autoidle);
int omap_hwmod_reset(struct omap_hwmod *oh);
void omap_hwmod_ocp_barrier(struct omap_hwmod *oh);
@@ -595,6 +596,8 @@ int omap_hwmod_for_each_by_class(const char *classname,
int omap_hwmod_set_postsetup_state(struct omap_hwmod *oh, u8 state);
u32 omap_hwmod_get_context_loss_count(struct omap_hwmod *oh);
+int omap_hwmod_no_setup_reset(struct omap_hwmod *oh);
+
/*
* Chip variant-specific hwmod init routines - XXX should be converted
* to use initcalls once the initial boot ordering is straightened out
diff --git a/arch/arm/plat-omap/omap_device.c b/arch/arm/plat-omap/omap_device.c
index 57adb270767b..9bbda9acb73b 100644
--- a/arch/arm/plat-omap/omap_device.c
+++ b/arch/arm/plat-omap/omap_device.c
@@ -83,9 +83,11 @@
#include <linux/err.h>
#include <linux/io.h>
#include <linux/clk.h>
+#include <linux/clkdev.h>
#include <plat/omap_device.h>
#include <plat/omap_hwmod.h>
+#include <plat/clock.h>
/* These parameters are passed to _omap_device_{de,}activate() */
#define USE_WAKEUP_LAT 0
@@ -239,12 +241,12 @@ static inline struct omap_device *_find_by_pdev(struct platform_device *pdev)
}
/**
- * _add_optional_clock_alias - Add clock alias for hwmod optional clocks
+ * _add_optional_clock_clkdev - Add clkdev entry for hwmod optional clocks
* @od: struct omap_device *od
*
* For every optional clock present per hwmod per omap_device, this function
- * adds an entry in the clocks list of the form <dev-id=dev_name, con-id=role>
- * if an entry is already present in it with the form <dev-id=NULL, con-id=role>
+ * adds an entry in the clkdev table of the form <dev-id=dev_name, con-id=role>
+ * if it does not exist already.
*
* The function is called from inside omap_device_build_ss(), after
* omap_device_register.
@@ -254,25 +256,39 @@ static inline struct omap_device *_find_by_pdev(struct platform_device *pdev)
*
* No return value.
*/
-static void _add_optional_clock_alias(struct omap_device *od,
+static void _add_optional_clock_clkdev(struct omap_device *od,
struct omap_hwmod *oh)
{
int i;
for (i = 0; i < oh->opt_clks_cnt; i++) {
struct omap_hwmod_opt_clk *oc;
- int r;
+ struct clk *r;
+ struct clk_lookup *l;
oc = &oh->opt_clks[i];
if (!oc->_clk)
continue;
- r = clk_add_alias(oc->role, dev_name(&od->pdev.dev),
- (char *)oc->clk, &od->pdev.dev);
- if (r)
- pr_err("omap_device: %s: clk_add_alias for %s failed\n",
+ r = clk_get_sys(dev_name(&od->pdev.dev), oc->role);
+ if (!IS_ERR(r))
+ continue; /* clkdev entry exists */
+
+ r = omap_clk_get_by_name((char *)oc->clk);
+ if (IS_ERR(r)) {
+ pr_err("omap_device: %s: omap_clk_get_by_name for %s failed\n",
+ dev_name(&od->pdev.dev), oc->clk);
+ continue;
+ }
+
+ l = clkdev_alloc(r, oc->role, dev_name(&od->pdev.dev));
+ if (!l) {
+ pr_err("omap_device: %s: clkdev_alloc for %s failed\n",
dev_name(&od->pdev.dev), oc->role);
+ return;
+ }
+ clkdev_add(l);
}
}
@@ -480,7 +496,7 @@ struct omap_device *omap_device_build_ss(const char *pdev_name, int pdev_id,
for (i = 0; i < oh_cnt; i++) {
hwmods[i]->od = od;
- _add_optional_clock_alias(od, hwmods[i]);
+ _add_optional_clock_clkdev(od, hwmods[i]);
}
if (ret)