* [PATCH 05/10] cpufreq: OMAP: move clk name decision to init
From: Kevin Hilman @ 2011-09-22 21:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1316725648-26710-1-git-send-email-khilman@ti.com>
From: Nishanth Menon <nm@ti.com>
Clk name does'nt need to dynamically detected during clk init.
move them off to driver initialization, if we dont have a clk name,
there is no point in registering the driver anyways. The actual clk
get and put is left at cpu_init and exit functions.
Signed-off-by: Nishanth Menon <nm@ti.com>
Signed-off-by: Kevin Hilman <khilman@ti.com>
---
drivers/cpufreq/omap-cpufreq.c | 20 +++++++++++++-------
1 files changed, 13 insertions(+), 7 deletions(-)
diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c
index 724d36c..07e835d 100644
--- a/drivers/cpufreq/omap-cpufreq.c
+++ b/drivers/cpufreq/omap-cpufreq.c
@@ -48,6 +48,7 @@ static struct lpj_info global_lpj_ref;
static struct cpufreq_frequency_table *freq_table;
static struct clk *mpu_clk;
+static char *mpu_clk_name;
static int omap_verify_speed(struct cpufreq_policy *policy)
{
@@ -152,13 +153,7 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy)
int result = 0;
struct device *mpu_dev;
- if (cpu_is_omap24xx())
- mpu_clk = clk_get(NULL, "virt_prcm_set");
- else if (cpu_is_omap34xx())
- mpu_clk = clk_get(NULL, "dpll1_ck");
- else if (cpu_is_omap44xx())
- mpu_clk = clk_get(NULL, "dpll_mpu_ck");
-
+ mpu_clk = clk_get(NULL, mpu_clk_name);
if (IS_ERR(mpu_clk))
return PTR_ERR(mpu_clk);
@@ -232,6 +227,17 @@ static struct cpufreq_driver omap_driver = {
static int __init omap_cpufreq_init(void)
{
+ if (cpu_is_omap24xx())
+ mpu_clk_name = "virt_prcm_set";
+ else if (cpu_is_omap34xx())
+ mpu_clk_name = "dpll1_ck";
+ else if (cpu_is_omap44xx())
+ mpu_clk_name = "dpll_mpu_ck";
+
+ if (!mpu_clk_name) {
+ pr_err("%s: unsupported Silicon?\n", __func__);
+ return -EINVAL;
+ }
return cpufreq_register_driver(&omap_driver);
}
--
1.7.6
^ permalink raw reply related
* [PATCH 06/10] cpufreq: OMAP: deny initialization if no mpudev
From: Kevin Hilman @ 2011-09-22 21:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1316725648-26710-1-git-send-email-khilman@ti.com>
From: Nishanth Menon <nm@ti.com>
if we do not have mpu_dev we normally fail in cpu_init. It is better
to fail driver registration if the devices are not available.
Signed-off-by: Nishanth Menon <nm@ti.com>
Signed-off-by: Kevin Hilman <khilman@ti.com>
---
drivers/cpufreq/omap-cpufreq.c | 15 ++++++++-------
1 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c
index 07e835d..fe943b3 100644
--- a/drivers/cpufreq/omap-cpufreq.c
+++ b/drivers/cpufreq/omap-cpufreq.c
@@ -49,6 +49,7 @@ static struct lpj_info global_lpj_ref;
static struct cpufreq_frequency_table *freq_table;
static struct clk *mpu_clk;
static char *mpu_clk_name;
+static struct device *mpu_dev;
static int omap_verify_speed(struct cpufreq_policy *policy)
{
@@ -151,7 +152,6 @@ static int omap_target(struct cpufreq_policy *policy,
static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy)
{
int result = 0;
- struct device *mpu_dev;
mpu_clk = clk_get(NULL, mpu_clk_name);
if (IS_ERR(mpu_clk))
@@ -161,12 +161,6 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy)
return -EINVAL;
policy->cur = policy->min = policy->max = omap_getspeed(policy->cpu);
- mpu_dev = omap2_get_mpuss_device();
-
- if (!mpu_dev) {
- pr_warning("%s: unable to get the mpu device\n", __func__);
- return -EINVAL;
- }
opp_init_cpufreq_table(mpu_dev, &freq_table);
if (freq_table) {
@@ -238,6 +232,13 @@ static int __init omap_cpufreq_init(void)
pr_err("%s: unsupported Silicon?\n", __func__);
return -EINVAL;
}
+
+ mpu_dev = omap2_get_mpuss_device();
+ if (!mpu_dev) {
+ pr_warning("%s: unable to get the mpu device\n", __func__);
+ return -EINVAL;
+ }
+
return cpufreq_register_driver(&omap_driver);
}
--
1.7.6
^ permalink raw reply related
* [PATCH 07/10] cpufreq: OMAP: dont support !freq_table
From: Kevin Hilman @ 2011-09-22 21:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1316725648-26710-1-git-send-email-khilman@ti.com>
From: Nishanth Menon <nm@ti.com>
OMAP2+ all have frequency tables, hence the hacks we had for older
silicon do not need to be carried forward. As part of this change,
use cpufreq_frequency_table_target to find the best match for
frequency requested.
Signed-off-by: Nishanth Menon <nm@ti.com>
Signed-off-by: Kevin Hilman <khilman@ti.com>
---
drivers/cpufreq/omap-cpufreq.c | 67 +++++++++++++++++++--------------------
1 files changed, 33 insertions(+), 34 deletions(-)
diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c
index fe943b3..635778d 100644
--- a/drivers/cpufreq/omap-cpufreq.c
+++ b/drivers/cpufreq/omap-cpufreq.c
@@ -34,8 +34,6 @@
#include <mach/hardware.h>
-#define VERY_HI_RATE 900000000
-
#ifdef CONFIG_SMP
struct lpj_info {
unsigned long ref;
@@ -53,20 +51,9 @@ static struct device *mpu_dev;
static int omap_verify_speed(struct cpufreq_policy *policy)
{
- if (freq_table)
- return cpufreq_frequency_table_verify(policy, freq_table);
-
- if (policy->cpu)
+ if (!freq_table)
return -EINVAL;
-
- cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
- policy->cpuinfo.max_freq);
-
- policy->min = clk_round_rate(mpu_clk, policy->min * 1000) / 1000;
- policy->max = clk_round_rate(mpu_clk, policy->max * 1000) / 1000;
- cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
- policy->cpuinfo.max_freq);
- return 0;
+ return cpufreq_frequency_table_verify(policy, freq_table);
}
static unsigned int omap_getspeed(unsigned int cpu)
@@ -84,18 +71,31 @@ static int omap_target(struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation)
{
- int i, ret = 0;
+ unsigned int i;
+ int ret = 0;
struct cpufreq_freqs freqs;
- /* Ensure desired rate is within allowed range. Some govenors
- * (ondemand) will just pass target_freq=0 to get the minimum. */
- if (target_freq < policy->min)
- target_freq = policy->min;
- if (target_freq > policy->max)
- target_freq = policy->max;
+ if (!freq_table) {
+ dev_err(mpu_dev, "%s: cpu%d: no freq table!\n", __func__,
+ policy->cpu);
+ return -EINVAL;
+ }
+
+ ret = cpufreq_frequency_table_target(policy, freq_table, target_freq,
+ relation, &i);
+ if (ret) {
+ dev_dbg(mpu_dev, "%s: cpu%d: no freq match for %d(ret=%d)\n",
+ __func__, policy->cpu, target_freq, ret);
+ return ret;
+ }
+ freqs.new = freq_table[i].frequency;
+ if (!freqs.new) {
+ dev_err(mpu_dev, "%s: cpu%d: no match for freq %d\n", __func__,
+ policy->cpu, target_freq);
+ return -EINVAL;
+ }
freqs.old = omap_getspeed(policy->cpu);
- freqs.new = clk_round_rate(mpu_clk, target_freq * 1000) / 1000;
freqs.cpu = policy->cpu;
if (freqs.old == freqs.new && policy->cur == freqs.new)
@@ -161,19 +161,18 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy)
return -EINVAL;
policy->cur = policy->min = policy->max = omap_getspeed(policy->cpu);
- opp_init_cpufreq_table(mpu_dev, &freq_table);
-
- if (freq_table) {
- result = cpufreq_frequency_table_cpuinfo(policy, freq_table);
- if (!result)
- cpufreq_frequency_table_get_attr(freq_table,
- policy->cpu);
- } else {
- policy->cpuinfo.min_freq = clk_round_rate(mpu_clk, 0) / 1000;
- policy->cpuinfo.max_freq = clk_round_rate(mpu_clk,
- VERY_HI_RATE) / 1000;
+ result = opp_init_cpufreq_table(mpu_dev, &freq_table);
+
+ if (result) {
+ dev_err(mpu_dev, "%s: cpu%d: failed creating freq table[%d]\n",
+ __func__, policy->cpu, result);
+ return result;
}
+ result = cpufreq_frequency_table_cpuinfo(policy, freq_table);
+ if (!result)
+ cpufreq_frequency_table_get_attr(freq_table, policy->cpu);
+
policy->min = policy->cpuinfo.min_freq;
policy->max = policy->cpuinfo.max_freq;
policy->cur = omap_getspeed(policy->cpu);
--
1.7.6
^ permalink raw reply related
* [PATCH 08/10] cpufreq: OMAP: only supports OPP library
From: Kevin Hilman @ 2011-09-22 21:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1316725648-26710-1-git-send-email-khilman@ti.com>
From: Nishanth Menon <nm@ti.com>
OMAP2 is the only family using clk_[init|exit]_cpufreq_table, however,
the cpufreq code does not currently use clk_init_cpufreq_table. As a
result, it is unusuable for OMAP2 and only usable only on platforms
using OPP library.
Remove the unbalanced clk_exit_cpufreq_table(). Any platforms where
OPPs are not availble will fail on init because a freq table will not
be properly initialized.
Signed-off-by: Nishanth Menon <nm@ti.com>
[khilman at ti.com: changelog edits, and graceful failure mode changes]
Signed-off-by: Kevin Hilman <khilman@ti.com>
---
drivers/cpufreq/omap-cpufreq.c | 3 +--
1 files changed, 1 insertions(+), 2 deletions(-)
diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c
index 635778d..ce82d50 100644
--- a/drivers/cpufreq/omap-cpufreq.c
+++ b/drivers/cpufreq/omap-cpufreq.c
@@ -1,5 +1,5 @@
/*
- * CPU frequency scaling for OMAP
+ * CPU frequency scaling for OMAP using OPP information
*
* Copyright (C) 2005 Nokia Corporation
* Written by Tony Lindgren <tony@atomide.com>
@@ -197,7 +197,6 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy)
static int omap_cpu_exit(struct cpufreq_policy *policy)
{
- clk_exit_cpufreq_table(&freq_table);
clk_put(mpu_clk);
return 0;
}
--
1.7.6
^ permalink raw reply related
* [PATCH 09/10] cpufreq: OMAP: put clk if cpu_init failed
From: Kevin Hilman @ 2011-09-22 21:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1316725648-26710-1-git-send-email-khilman@ti.com>
From: Nishanth Menon <nm@ti.com>
Release the mpu_clk in fail paths.
Reported-by: Todd Poynor <toddpoynor@google.com>
Signed-off-by: Nishanth Menon <nm@ti.com>
Signed-off-by: Kevin Hilman <khilman@ti.com>
---
drivers/cpufreq/omap-cpufreq.c | 14 +++++++++++---
1 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c
index ce82d50..a66c8b5 100644
--- a/drivers/cpufreq/omap-cpufreq.c
+++ b/drivers/cpufreq/omap-cpufreq.c
@@ -157,8 +157,10 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy)
if (IS_ERR(mpu_clk))
return PTR_ERR(mpu_clk);
- if (policy->cpu >= NR_CPUS)
- return -EINVAL;
+ if (policy->cpu >= NR_CPUS) {
+ result = -EINVAL;
+ goto fail_ck;
+ }
policy->cur = policy->min = policy->max = omap_getspeed(policy->cpu);
result = opp_init_cpufreq_table(mpu_dev, &freq_table);
@@ -166,12 +168,14 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy)
if (result) {
dev_err(mpu_dev, "%s: cpu%d: failed creating freq table[%d]\n",
__func__, policy->cpu, result);
- return result;
+ goto fail_ck;
}
result = cpufreq_frequency_table_cpuinfo(policy, freq_table);
if (!result)
cpufreq_frequency_table_get_attr(freq_table, policy->cpu);
+ else
+ goto fail_ck;
policy->min = policy->cpuinfo.min_freq;
policy->max = policy->cpuinfo.max_freq;
@@ -193,6 +197,10 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy)
policy->cpuinfo.transition_latency = 300 * 1000;
return 0;
+
+fail_ck:
+ clk_put(mpu_clk);
+ return result;
}
static int omap_cpu_exit(struct cpufreq_policy *policy)
--
1.7.6
^ permalink raw reply related
* [PATCH 10/10] cpufreq: OMAP: fix freq_table leak
From: Kevin Hilman @ 2011-09-22 21:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1316725648-26710-1-git-send-email-khilman@ti.com>
From: Nishanth Menon <nm@ti.com>
We use a single frequency table for multiple CPUs. But, with
OMAP4, since we have multiple CPUs, the cpu_init call for CPU1
causes freq_table previously allocated for CPU0 to be overwritten.
In addition, we dont free the table on exit path.
We solve this by maintaining an atomic type counter to ensure
just a single table exists at a given time.
Signed-off-by: Nishanth Menon <nm@ti.com>
Signed-off-by: Kevin Hilman <khilman@ti.com>
---
drivers/cpufreq/omap-cpufreq.c | 22 +++++++++++++++++-----
1 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c
index a66c8b5..90918e1 100644
--- a/drivers/cpufreq/omap-cpufreq.c
+++ b/drivers/cpufreq/omap-cpufreq.c
@@ -45,6 +45,7 @@ static struct lpj_info global_lpj_ref;
#endif
static struct cpufreq_frequency_table *freq_table;
+static atomic_t freq_table_users = ATOMIC_INIT(0);
static struct clk *mpu_clk;
static char *mpu_clk_name;
static struct device *mpu_dev;
@@ -149,6 +150,12 @@ static int omap_target(struct cpufreq_policy *policy,
return ret;
}
+static inline void freq_table_free(void)
+{
+ if (atomic_dec_and_test(&freq_table_users))
+ opp_free_cpufreq_table(mpu_dev, &freq_table);
+}
+
static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy)
{
int result = 0;
@@ -163,7 +170,9 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy)
}
policy->cur = policy->min = policy->max = omap_getspeed(policy->cpu);
- result = opp_init_cpufreq_table(mpu_dev, &freq_table);
+
+ if (atomic_inc_return(&freq_table_users) == 1)
+ result = opp_init_cpufreq_table(mpu_dev, &freq_table);
if (result) {
dev_err(mpu_dev, "%s: cpu%d: failed creating freq table[%d]\n",
@@ -172,10 +181,10 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy)
}
result = cpufreq_frequency_table_cpuinfo(policy, freq_table);
- if (!result)
- cpufreq_frequency_table_get_attr(freq_table, policy->cpu);
- else
- goto fail_ck;
+ if (result)
+ goto fail_table;
+
+ cpufreq_frequency_table_get_attr(freq_table, policy->cpu);
policy->min = policy->cpuinfo.min_freq;
policy->max = policy->cpuinfo.max_freq;
@@ -198,6 +207,8 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy)
return 0;
+fail_table:
+ freq_table_free();
fail_ck:
clk_put(mpu_clk);
return result;
@@ -205,6 +216,7 @@ fail_ck:
static int omap_cpu_exit(struct cpufreq_policy *policy)
{
+ freq_table_free();
clk_put(mpu_clk);
return 0;
}
--
1.7.6
^ permalink raw reply related
* [PATCH] S3C2443: Fix bit-reset in setrate of clk_armdiv
From: Heiko Stübner @ 2011-09-22 21:08 UTC (permalink / raw)
To: linux-arm-kernel
The changed statement should set the old armdiv bits to 0
and not everything else, before setting the new value.
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
arch/arm/mach-s3c2443/clock.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/arch/arm/mach-s3c2443/clock.c b/arch/arm/mach-s3c2443/clock.c
index 966bde5..27549c4 100644
--- a/arch/arm/mach-s3c2443/clock.c
+++ b/arch/arm/mach-s3c2443/clock.c
@@ -124,7 +124,7 @@ static int s3c2443_armclk_setrate(struct clk *clk, unsigned long rate)
unsigned long clkcon0;
clkcon0 = __raw_readl(S3C2443_CLKDIV0);
- clkcon0 &= S3C2443_CLKDIV0_ARMDIV_MASK;
+ clkcon0 &= ~S3C2443_CLKDIV0_ARMDIV_MASK;
clkcon0 |= val << S3C2443_CLKDIV0_ARMDIV_SHIFT;
__raw_writel(clkcon0, S3C2443_CLKDIV0);
}
--
1.7.2.3
^ permalink raw reply related
* [RFC PATCH v3] drivercore: Add driver probe deferral mechanism
From: Grant Likely @ 2011-09-22 21:19 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20110922212909.49966cf4@lxorguk.ukuu.org.uk>
On Thu, Sep 22, 2011 at 2:29 PM, Alan Cox <alan@lxorguk.ukuu.org.uk> wrote:
> Definitely what is needed for some of the x86 SoC stuff and would let us
> rip out some of the special case magic for the SCU discovery.
>
> First thing that strikes me is driver_bound kicks the processing queue
> again. That seems odd - surely this isn't needed because any driver that
> does initialise this time and may allow something else to get going will
> queue the kick itself. Thus this seems to just add overhead.
>
> It all looks a bit O(N?) if we don't expect the drivers that might
> trigger something else binding to just say 'hey I'm one of the
> troublemakers'
The way I read it, absolute worst case is when every device but one
depends on another device. In that case I believe it will be
O(Nlog(N)). (Every device gets probed on the first pass, but only the
last one gets probed. Then it goes through N-1 devices to the result
of only 1 more device getting probed, then N-2, etc.). Actual usage
won't be anywhere near that complexity because there tends to be a
small number of devices on these SoCs that a lot of other devices
depend on, so I expect that there will be somewhere on the order of
3-4 passes for the typical embedded SoCs that I've seen.
driver_bound is used to kick off the process because anytime a new
device becomes active (bound to a driver), there is a high likelyhood
that one of the devices in the pending list can now be probed.
However, the re-probing is performed in a workqueue to make sure that
it is somewhat serialized to prevent the *entire* pending list to be
processed for every driver_bound() call. Instead, by kicking a
workqueue, it guarantees that all the pending items are retried, but
kicking the workqueue 6 times doesn't mean that the list is going to
be processed 6 times.
In other words, driver_bound will kick off the process, but it doesn't
actually do the re-probing work.
g.
^ permalink raw reply
* [PATCH v4 2/3] omap_twl: Prevent SR to enable for am3517/am3505 devices
From: Kevin Hilman @ 2011-09-22 21:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1316697993-696-3-git-send-email-abhilash.kv@ti.com>
Abhilash K V <abhilash.kv@ti.com> writes:
> In case of AM3517 & AM3505, SmartReflex is not applicable so
> we must not enable it.
This part is fine, but...
> So omap3_twl_init() is now not called when the processor does not
> support SR.
...I don't think this is right. DVFS using the PMIC is still doable
without SR.
Are you assuming that no DVFS is done on these devices?
Kevin
> Signed-off-by: Vaibhav Hiremath <hvaibhav@ti.com>
> Signed-off-by: Abhilash K V <abhilash.kv@ti.com>
> ---
> arch/arm/mach-omap2/id.c | 2 +-
> arch/arm/mach-omap2/pm.c | 3 ++-
> arch/arm/plat-omap/include/plat/cpu.h | 2 ++
> 3 files changed, 5 insertions(+), 2 deletions(-)
>
> diff --git a/arch/arm/mach-omap2/id.c b/arch/arm/mach-omap2/id.c
> index d27daf9..b7e3082 100644
> --- a/arch/arm/mach-omap2/id.c
> +++ b/arch/arm/mach-omap2/id.c
> @@ -188,7 +188,7 @@ static void __init omap3_check_features(void)
> if (cpu_is_omap3630())
> omap_features |= OMAP3_HAS_192MHZ_CLK;
> if (!cpu_is_omap3505() && !cpu_is_omap3517())
> - omap_features |= OMAP3_HAS_IO_WAKEUP;
> + omap_features |= (OMAP3_HAS_IO_WAKEUP | OMAP3_HAS_SR);
>
> omap_features |= OMAP3_HAS_SDRC;
>
> diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c
> index d34fc52..da71abc 100644
> --- a/arch/arm/mach-omap2/pm.c
> +++ b/arch/arm/mach-omap2/pm.c
> @@ -252,7 +252,8 @@ postcore_initcall(omap2_common_pm_init);
> static int __init omap2_common_pm_late_init(void)
> {
> /* Init the OMAP TWL parameters */
> - omap3_twl_init();
> + if (omap3_has_sr())
> + omap3_twl_init();
> omap4_twl_init();
>
> /* Init the voltage layer */
> diff --git a/arch/arm/plat-omap/include/plat/cpu.h b/arch/arm/plat-omap/include/plat/cpu.h
> index 2f90269..cc6fcd3 100644
> --- a/arch/arm/plat-omap/include/plat/cpu.h
> +++ b/arch/arm/plat-omap/include/plat/cpu.h
> @@ -413,6 +413,7 @@ extern u32 omap_features;
> #define OMAP4_HAS_MPU_1GHZ BIT(8)
> #define OMAP4_HAS_MPU_1_2GHZ BIT(9)
> #define OMAP4_HAS_MPU_1_5GHZ BIT(10)
> +#define OMAP3_HAS_SR BIT(11)
>
>
> #define OMAP3_HAS_FEATURE(feat,flag) \
> @@ -429,6 +430,7 @@ OMAP3_HAS_FEATURE(isp, ISP)
> OMAP3_HAS_FEATURE(192mhz_clk, 192MHZ_CLK)
> OMAP3_HAS_FEATURE(io_wakeup, IO_WAKEUP)
> OMAP3_HAS_FEATURE(sdrc, SDRC)
> +OMAP3_HAS_FEATURE(sr, SR)
>
> /*
> * Runtime detection of OMAP4 features
^ permalink raw reply
* [PATCH v4 3/3] OMAP2+: voltage: add check for missing PMIC info in vp init
From: Kevin Hilman @ 2011-09-22 21:39 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1316697993-696-4-git-send-email-abhilash.kv@ti.com>
Abhilash K V <abhilash.kv@ti.com> writes:
> If PMIC info is not available in omap_vp_init(), abort.
>
> Signed-off-by: Abhilash K V <abhilash.kv@ti.com>
Looks good, some minor nitpicking below...
> ---
> arch/arm/mach-omap2/vp.c | 7 +++++++
> 1 files changed, 7 insertions(+), 0 deletions(-)
>
> diff --git a/arch/arm/mach-omap2/vp.c b/arch/arm/mach-omap2/vp.c
> index 66bd700..2c99837 100644
> --- a/arch/arm/mach-omap2/vp.c
> +++ b/arch/arm/mach-omap2/vp.c
> @@ -41,6 +41,13 @@ void __init omap_vp_init(struct voltagedomain *voltdm)
> u32 val, sys_clk_rate, timeout, waittime;
> u32 vddmin, vddmax, vstepmin, vstepmax;
>
> + if (!voltdm->pmic || !voltdm->pmic->uv_to_vsel) {
> + pr_err("%s: PMIC info requried to configure vp for"
nit 1: s/vp/VP/
nit 2: need a space at the end of this string, otherwise the "vdd_" will
be right after the "for" with no spaces.
> + "vdd_%s not populated.Hence cannot initialize vp\n",
nit 3: Add space after '.'
Kevin
> + __func__, voltdm->name);
> + return;
> + }
> +
> if (!voltdm->read || !voltdm->write) {
> pr_err("%s: No read/write API for accessing vdd_%s regs\n",
> __func__, voltdm->name);
^ permalink raw reply
* [PATCH 01/10] cpufreq: OMAP: cleanup for multi-SoC support, move into drivers/cpufreq
From: Tony Lindgren @ 2011-09-22 21:53 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1316725648-26710-2-git-send-email-khilman@ti.com>
* Kevin Hilman <khilman@ti.com> [110922 13:35]:
> From: Santosh Shilimkar <santosh.shilimkar@ti.com>
>
> Move OMAP cpufreq driver from arch/arm/mach-omap2 into
> drivers/cpufreq, along with a few cleanups:
>
> - generalize support for better handling of different SoCs in the OMAP
> - use OPP layer instead of OMAP clock internals for frequency table init
>
> Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com>
> [khilman at ti.com: move to drivers]
> Signed-off-by: Kevin Hilman <khilman@ti.com>
Nice to see this happening:
Acked-by: Tony Lindgren <tony@atomide.com>
^ permalink raw reply
* [PATCH v2] AM3517 : support for suspend/resume
From: Kevin Hilman @ 2011-09-22 21:55 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1316008126-7322-1-git-send-email-abhilash.kv@ti.com>
Abhilash K V <abhilash.kv@ti.com> writes:
> This patch-set adds support for suspension to RAM and
> resumption on the AM3517. This includes:
> 1. Patch to disable dynamic sleep (as it is not supported
> on AM35xx).
This should still be a separate patch, with justification. More on this
below.
> 2. Imported the unique suspend/resume sequence for AM3517,
> contained in the new file arch/arm/mach-omap2/sleep3517.S.
>
> Caveat: If "no_console_suspend" is enabled (via boot-args),the
> device doesnot resume but simply hangs.
> Kevin's fix below should fix this:
> http://marc.info/?l=linux-omap&m=131593828001388&w=2#1
should fix? I assumed you tested it along with this.
> Signed-off-by: Ranjith Lohithakshan <ranjithl@ti.com>
> Reviewed-by: Vaibhav Hiremath <hvaibhav@ti.com>
> Signed-off-by: Abhilash K V <abhilash.kv@ti.com>
> ---
> This patch is dependent on the following patch-sets:
> * [PATCH v3 0/2] AM3517: Booting up
> at http://marc.info/?l=linux-omap&m=131548349725176&w=2
> * [PATCH v2 0/3] AM35x: Adding PM init
> at http://marc.info/?l=linux-kernel&m=131548606728209&w=2
>
> The patches are tested on master of tmlind/linux-omap-2.6.git.
> Kernel version is 3.1-rc3 and last commit on top of which these patches
> were added is:
> b148d763841161894ed6629794064065a834aa2b: Linux-omap rebuilt: Updated to
> use omap_sdrc_init
>
> with the folowing commit reverted:
> f3637a5f2e2eb391ff5757bc83fb5de8f9726464: irq: Always set IRQF_ONESHOT
> if no primary handler is specified
>
> Changes in v2:
> * Synchronised with the cleaned-up suspend-resume code for OMAP3
> * Removed unused *_get_restore_pointer code
> * Added SECURE_SRAM feature to disallow saving and restoring
> secure ram context for AM35x
Adding a new feature flag should be a separate patch.
> * Compacted the number of patches by squashing three closely coupled
> ones and eliminating one that was no longer needed.
>
> arch/arm/mach-omap2/Makefile | 3 +-
> arch/arm/mach-omap2/id.c | 4 +-
> arch/arm/mach-omap2/pm.h | 3 +
> arch/arm/mach-omap2/pm34xx.c | 21 ++++-
> arch/arm/mach-omap2/sleep3517.S | 156 +++++++++++++++++++++++++++++++++
> arch/arm/plat-omap/include/plat/cpu.h | 2 +
> 6 files changed, 183 insertions(+), 6 deletions(-)
> create mode 100644 arch/arm/mach-omap2/sleep3517.S
>
> diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
> index 590e797..37f62ae 100644
> --- a/arch/arm/mach-omap2/Makefile
> +++ b/arch/arm/mach-omap2/Makefile
> @@ -61,7 +61,7 @@ endif
> ifeq ($(CONFIG_PM),y)
> obj-$(CONFIG_ARCH_OMAP2) += pm24xx.o
> obj-$(CONFIG_ARCH_OMAP2) += sleep24xx.o
> -obj-$(CONFIG_ARCH_OMAP3) += pm34xx.o sleep34xx.o \
> +obj-$(CONFIG_ARCH_OMAP3) += pm34xx.o sleep34xx.o sleep3517.o \
> cpuidle34xx.o
> obj-$(CONFIG_ARCH_OMAP4) += pm44xx.o
> obj-$(CONFIG_PM_DEBUG) += pm-debug.o
> @@ -70,6 +70,7 @@ obj-$(CONFIG_OMAP_SMARTREFLEX_CLASS3) += smartreflex-class3.o
>
> AFLAGS_sleep24xx.o :=-Wa,-march=armv6
> AFLAGS_sleep34xx.o :=-Wa,-march=armv7-a$(plus_sec)
> +AFLAGS_sleep3517.o :=-Wa,-march=armv7-a$(plus_sec)
>
> ifeq ($(CONFIG_PM_VERBOSE),y)
> CFLAGS_pm_bus.o += -DDEBUG
> diff --git a/arch/arm/mach-omap2/id.c b/arch/arm/mach-omap2/id.c
> index da71098..3e40c02 100644
> --- a/arch/arm/mach-omap2/id.c
> +++ b/arch/arm/mach-omap2/id.c
> @@ -202,7 +202,9 @@ static void __init omap3_check_features(void)
> if (cpu_is_omap3630())
> omap_features |= OMAP3_HAS_192MHZ_CLK;
> if (!cpu_is_omap3505() && !cpu_is_omap3517())
> - omap_features |= (OMAP3_HAS_IO_WAKEUP | OMAP3_HAS_SR);
> + omap_features |= (OMAP3_HAS_IO_WAKEUP
> + | OMAP3_HAS_SR
> + | OMAP3_HAS_SECURE_SRAM);
This is not related to suspend either, and should probably be part of
the bootup series.
The HAS_SECURE_SRAM part should be added to the separate patch that adds
this new feature flag.
> omap_features |= OMAP3_HAS_SDRC;
>
> diff --git a/arch/arm/mach-omap2/pm.h b/arch/arm/mach-omap2/pm.h
> index ce028f6..952eb2b 100644
> --- a/arch/arm/mach-omap2/pm.h
> +++ b/arch/arm/mach-omap2/pm.h
> @@ -82,10 +82,13 @@ extern unsigned int omap24xx_cpu_suspend_sz;
>
> /* 3xxx */
> extern void omap34xx_cpu_suspend(int save_state);
> +extern void omap3517_cpu_suspend(int save_state);
>
> /* omap3_do_wfi function pointer and size, for copy to SRAM */
> extern void omap3_do_wfi(void);
> +extern void omap3517_do_wfi(void);
> extern unsigned int omap3_do_wfi_sz;
> +extern unsigned int omap3517_do_wfi_sz;
> /* ... and its pointer from SRAM after copy */
> extern void (*omap3_do_wfi_sram)(void);
>
> diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c
> index 7255d9b..44f7bac 100644
> --- a/arch/arm/mach-omap2/pm34xx.c
> +++ b/arch/arm/mach-omap2/pm34xx.c
> @@ -80,6 +80,7 @@ static LIST_HEAD(pwrst_list);
>
> static int (*_omap_save_secure_sram)(u32 *addr);
> void (*omap3_do_wfi_sram)(void);
> +void (*omap3517_do_wfi_sram)(void);
>
> static struct powerdomain *mpu_pwrdm, *neon_pwrdm;
> static struct powerdomain *core_pwrdm, *per_pwrdm;
> @@ -323,7 +324,10 @@ static void omap34xx_save_context(u32 *save)
>
> static int omap34xx_do_sram_idle(unsigned long save_state)
> {
> - omap34xx_cpu_suspend(save_state);
> + if (cpu_is_omap3505() || cpu_is_omap3517())
> + omap3517_cpu_suspend(save_state);
> + else
> + omap34xx_cpu_suspend(save_state);
We don't want cpu_is_* checks at runtime. Make this a function pointer
that is initialized at init time based on SoC.
> return 0;
> }
>
> @@ -485,6 +489,8 @@ console_still_active:
>
> int omap3_can_sleep(void)
> {
> + if (cpu_is_omap3505() || cpu_is_omap3517())
> + return 0;
This needs to be a separate patch with a descriptive changelog and
justification as to why you can't do WFI in idle.
Adding something like this means the device will *never* attempt a WFI
during idle.
I suspect that avoiding WFI in idle is masking a bug that you don't see
in the suspend path.
> if (!omap_uart_can_sleep())
> return 0;
> return 1;
> @@ -843,11 +849,18 @@ static int __init clkdms_setup(struct clockdomain *clkdm, void *unused)
> */
> void omap_push_sram_idle(void)
> {
> - omap3_do_wfi_sram = omap_sram_push(omap3_do_wfi, omap3_do_wfi_sz);
> + if (cpu_is_omap3505() || cpu_is_omap3517())
> + omap3517_do_wfi_sram = omap_sram_push(omap3517_do_wfi,
> + omap3517_do_wfi_sz);
> + else
> + omap3_do_wfi_sram = omap_sram_push(omap3_do_wfi,
> + omap3_do_wfi_sz);
>
> if (omap_type() != OMAP2_DEVICE_TYPE_GP)
> - _omap_save_secure_sram = omap_sram_push(save_secure_ram_context,
> - save_secure_ram_context_sz);
> + if (omap3_has_secure_sram())
> + _omap_save_secure_sram = omap_sram_push(
> + save_secure_ram_context,
> + save_secure_ram_context_sz);
This should be part of the separate patch that add the secure SRAM
feature flag.
Kevin
^ permalink raw reply
* [PATCH v3 3/3] OMAP3: Remove auto-selection of PMICs
From: Kevin Hilman @ 2011-09-22 22:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1316698155-954-4-git-send-email-abhilash.kv@ti.com>
Abhilash K V <abhilash.kv@ti.com> writes:
> The current implementation almost assumes that only
almost? ;)
> TWL4030/TWL5030/TWl6030 are (or can be) used with the
> OMAP processors. This is, however, not true.
>
> This patch removes the automatic selection of the PMIC
> from Kconfig.
> All currently used PMICs are now added to omap2plus_defconfig;
I don't think adding them to omap2plus_defconfig is right either, as
those will get stripped out the next time that file is (re)generated.
> any new PMIC that gets supported in future could now be
> enabled here rather than by changing Kconfig for
> ARCH_OMAP2PLUS_TYPICAL
IMO, the right fix is to have the board Kconfig entries select the PMIC
being used on those boards. That is the only solution that will scale,
and also work when using 'make randconfig'.
Kevin
>
> Signed-off-by: Sanjeev Premi <premi@ti.com>
> Signed-off-by: Abhilash K V <abhilash.kv@ti.com>
> ---
> arch/arm/configs/omap2plus_defconfig | 3 +++
> arch/arm/mach-omap2/Kconfig | 3 ---
> drivers/mfd/Kconfig | 2 +-
> 3 files changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/arch/arm/configs/omap2plus_defconfig b/arch/arm/configs/omap2plus_defconfig
> index d5f00d7..076b131 100644
> --- a/arch/arm/configs/omap2plus_defconfig
> +++ b/arch/arm/configs/omap2plus_defconfig
> @@ -130,6 +130,9 @@ CONFIG_POWER_SUPPLY=y
> CONFIG_WATCHDOG=y
> CONFIG_OMAP_WATCHDOG=y
> CONFIG_TWL4030_WATCHDOG=y
> +CONFIG_MENELAUS=y
> +CONFIG_TWL4030_CORE=y
> +CONFIG_TWL4030_POWER=y
> CONFIG_REGULATOR_TWL4030=y
> CONFIG_REGULATOR_TPS65023=y
> CONFIG_REGULATOR_TPS6507X=y
> diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
> index 7edf802..d40f6d2 100644
> --- a/arch/arm/mach-omap2/Kconfig
> +++ b/arch/arm/mach-omap2/Kconfig
> @@ -15,9 +15,6 @@ config ARCH_OMAP2PLUS_TYPICAL
> select I2C
> select I2C_OMAP
> select MFD_SUPPORT
> - select MENELAUS if ARCH_OMAP2
> - select TWL4030_CORE if ARCH_OMAP3 || ARCH_OMAP4
> - select TWL4030_POWER if ARCH_OMAP3 || ARCH_OMAP4
> help
> Compile a kernel suitable for booting most boards
>
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index 21574bd..72e15c8 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -238,7 +238,7 @@ config TWL4030_MADC
>
> config TWL4030_POWER
> bool "Support power resources on TWL4030 family chips"
> - depends on TWL4030_CORE && ARM
> + depends on TWL4030_CORE
> help
> Say yes here if you want to use the power resources on the
> TWL4030 family chips. Most of these resources are regulators,
^ permalink raw reply
* [PATCH v3 3/3] OMAP3: Remove auto-selection of PMICs
From: Tony Lindgren @ 2011-09-22 22:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <877h50qlne.fsf@ti.com>
* Kevin Hilman <khilman@ti.com> [110922 14:30]:
> Abhilash K V <abhilash.kv@ti.com> writes:
>
> > The current implementation almost assumes that only
>
> almost? ;)
>
> > TWL4030/TWL5030/TWl6030 are (or can be) used with the
> > OMAP processors. This is, however, not true.
> >
> > This patch removes the automatic selection of the PMIC
> > from Kconfig.
> > All currently used PMICs are now added to omap2plus_defconfig;
>
> I don't think adding them to omap2plus_defconfig is right either, as
> those will get stripped out the next time that file is (re)generated.
>
> > any new PMIC that gets supported in future could now be
> > enabled here rather than by changing Kconfig for
> > ARCH_OMAP2PLUS_TYPICAL
>
> IMO, the right fix is to have the board Kconfig entries select the PMIC
> being used on those boards. That is the only solution that will scale,
> and also work when using 'make randconfig'.
Agreed.
Tony
^ permalink raw reply
* [PATCH v2 0/7] Add a generic struct clk
From: Mike Turquette @ 2011-09-22 22:26 UTC (permalink / raw)
To: linux-arm-kernel
Hi all,
The goal of this series is to provide a cross-platform clock framework
that platforms can use to model their clock trees and perform common
operations on them. Currently everyone re-invents their own clock tree
inside platform code which makes it impossible for drivers to use the
clock APIs safely across many platforms and for distro's to compile
multi-platform kernels which all redefine struct clk and its operations.
This is the second version of the common clock patches which were
originally posted by Jeremy Kerr and then re-posted with some additional
patches by Mark Brown. Mark's re-post didn't have any changes done to
the original four patches from Jeremy which is why this series is "v2".
The changes in this series are minimal: I've folded in some of Mark's
fixes and most of the comments posted to his series as well as rebasing
on top of v3.1-rc7. The design and functionality hasn't changed much
since Jeremy posted v1 of this series. Propagating the rate change up
to the parent has been removed from clk_set_rate since that needs some
more thought. I also dropped Mark's change to append a device's name to
a clk name since device tree might solve this neatly. Again more
discussion around that would be good.
v1 of the series can be found at,
http://article.gmane.org/gmane.linux.kernel/1143182
Mark's re-post (v1+) can be found at,
http://article.gmane.org/gmane.linux.ports.arm.kernel/129889
Todo in follow-up patches:
clk_set_parent
Implement a *safe* upstream propagation for parent rate changes
Track tree topology and export to userspace (sysfs or debugfs)
Manage root clocks globally
Device tree support
Per-tree locking instead of framework-wide locking
Device driver rate-change notifiers
Jeremy Kerr (4):
clk: Add a generic clock infrastructure
clk: Implement clk_set_rate
clk: Add fixed-rate clock
clk: Add simple gated clock
Mark Brown (3):
clk: Add Kconfig option to build all generic clk drivers
clk: Add initial WM831x clock driver
x86: Enable generic clk API on x86
MAINTAINERS | 1 +
arch/x86/Kconfig | 1 +
drivers/clk/Kconfig | 27 +++-
drivers/clk/Makefile | 4 +
drivers/clk/clk-fixed.c | 24 +++
drivers/clk/clk-gate.c | 78 ++++++++++
drivers/clk/clk-wm831x.c | 386 ++++++++++++++++++++++++++++++++++++++++++++++
drivers/clk/clk.c | 291 ++++++++++++++++++++++++++++++++++
drivers/clk/clkdev.c | 7 +
include/linux/clk.h | 179 ++++++++++++++++++++--
10 files changed, 985 insertions(+), 13 deletions(-)
create mode 100644 drivers/clk/clk-fixed.c
create mode 100644 drivers/clk/clk-gate.c
create mode 100644 drivers/clk/clk-wm831x.c
create mode 100644 drivers/clk/clk.c
--
1.7.4.1
^ permalink raw reply
* [PATCH v2 1/7] clk: Add a generic clock infrastructure
From: Mike Turquette @ 2011-09-22 22:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1316730422-20027-1-git-send-email-mturquette@ti.com>
From: Jeremy Kerr <jeremy.kerr@canonical.com>
We currently have ~21 definitions of struct clk in the ARM architecture,
each defined on a per-platform basis. This makes it difficult to define
platform- (or architecture-) independent clock sources without making
assumptions about struct clk, and impossible to compile two
platforms with different struct clks into a single image.
This change is an effort to unify struct clk where possible, by defining
a common struct clk, and a set of clock operations. Different clock
implementations can set their own operations, and have a standard
interface for generic code. The callback interface is exposed to the
kernel proper, while the clock implementations only need to be seen by
the platform internals.
The interface is split into two halves:
* struct clk, which is the generic-device-driver interface. This
provides a set of functions which drivers may use to request
enable/disable, query or manipulate in a hardware-independent manner.
* struct clk_hw and struct clk_hw_ops, which is the hardware-specific
interface. Clock drivers implement the ops, which allow the core
clock code to implement the generic 'struct clk' API.
This allows us to share clock code among platforms, and makes it
possible to dynamically create clock devices in platform-independent
code.
Platforms can enable the generic struct clock through
CONFIG_GENERIC_CLK. In this case, the clock infrastructure consists of a
common, opaque struct clk, and a set of clock operations (defined per
type of clock):
struct clk_hw_ops {
int (*prepare)(struct clk_hw *);
void (*unprepare)(struct clk_hw *);
int (*enable)(struct clk_hw *);
void (*disable)(struct clk_hw *);
unsigned long (*recalc_rate)(struct clk_hw *);
int (*set_rate)(struct clk_hw *,
unsigned long, unsigned long *);
long (*round_rate)(struct clk_hw *, unsigned long);
int (*set_parent)(struct clk_hw *, struct clk *);
struct clk * (*get_parent)(struct clk_hw *);
};
Platform clock code can register a clock through clk_register, passing a
set of operations, and a pointer to hardware-specific data:
struct clk_hw_foo {
struct clk_hw clk;
void __iomem *enable_reg;
};
#define to_clk_foo(c) offsetof(c, clk_hw_foo, clk)
static int clk_foo_enable(struct clk_hw *clk)
{
struct clk_foo *foo = to_clk_foo(clk);
raw_writeb(foo->enable_reg, 1);
return 0;
}
struct clk_hw_ops clk_foo_ops = {
.enable = clk_foo_enable,
};
And in the platform initialisation code:
struct clk_foo my_clk_foo;
void init_clocks(void)
{
my_clk_foo.enable_reg = ioremap(...);
clk_register(&clk_foo_ops, &my_clk_foo, NULL);
}
Changes from Thomas Gleixner <tglx@linutronix.de>.
The common clock definitions are based on a development patch from Ben
Herrenschmidt <benh@kernel.crashing.org>.
TODO:
* We don't keep any internal reference to the clock topology at present.
Signed-off-by: Jeremy Kerr <jeremy.kerr@canonical.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: Mike Turquette <mturquette@ti.com>
---
Changes since v1:
Create a dummy clk_unregister and prototype/document it and clk_register
Constify struct clk_hw_ops
Remove spinlock.h header, include kernel.h
Use EOPNOTSUPP instead of ENOTSUPP
Add might_sleep to clk_prepare/clk_unprepare stubs
Properly init children hlist and child_node
Whitespace and typo fixes
drivers/clk/Kconfig | 3 +
drivers/clk/Makefile | 1 +
drivers/clk/clk.c | 232 ++++++++++++++++++++++++++++++++++++++++++++++++++
drivers/clk/clkdev.c | 7 ++
include/linux/clk.h | 140 +++++++++++++++++++++++++++---
5 files changed, 371 insertions(+), 12 deletions(-)
create mode 100644 drivers/clk/clk.c
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 3530927..c53ed59 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -5,3 +5,6 @@ config CLKDEV_LOOKUP
config HAVE_MACH_CLKDEV
bool
+
+config GENERIC_CLK
+ bool
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 07613fa..570d5b9 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -1,2 +1,3 @@
obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o
+obj-$(CONFIG_GENERIC_CLK) += clk.o
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
new file mode 100644
index 0000000..1cd7315
--- /dev/null
+++ b/drivers/clk/clk.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
+ *
+ * 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.
+ *
+ * Standard functionality for the common clock API.
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+struct clk {
+ const char *name;
+ const struct clk_hw_ops *ops;
+ struct clk_hw *hw;
+ unsigned int enable_count;
+ unsigned int prepare_count;
+ struct clk *parent;
+ unsigned long rate;
+};
+
+static DEFINE_SPINLOCK(enable_lock);
+static DEFINE_MUTEX(prepare_lock);
+
+static void __clk_unprepare(struct clk *clk)
+{
+ if (!clk)
+ return;
+
+ if (WARN_ON(clk->prepare_count == 0))
+ return;
+
+ if (--clk->prepare_count > 0)
+ return;
+
+ WARN_ON(clk->enable_count > 0);
+
+ if (clk->ops->unprepare)
+ clk->ops->unprepare(clk->hw);
+
+ __clk_unprepare(clk->parent);
+}
+
+void clk_unprepare(struct clk *clk)
+{
+ mutex_lock(&prepare_lock);
+ __clk_unprepare(clk);
+ mutex_unlock(&prepare_lock);
+}
+EXPORT_SYMBOL_GPL(clk_unprepare);
+
+static int __clk_prepare(struct clk *clk)
+{
+ int ret = 0;
+
+ if (!clk)
+ return 0;
+
+ if (clk->prepare_count == 0) {
+ ret = __clk_prepare(clk->parent);
+ if (ret)
+ return ret;
+
+ if (clk->ops->prepare) {
+ ret = clk->ops->prepare(clk->hw);
+ if (ret) {
+ __clk_unprepare(clk->parent);
+ return ret;
+ }
+ }
+ }
+
+ clk->prepare_count++;
+
+ return 0;
+}
+
+int clk_prepare(struct clk *clk)
+{
+ int ret;
+
+ mutex_lock(&prepare_lock);
+ ret = __clk_prepare(clk);
+ mutex_unlock(&prepare_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_prepare);
+
+static void __clk_disable(struct clk *clk)
+{
+ if (!clk)
+ return;
+
+ if (WARN_ON(clk->enable_count == 0))
+ return;
+
+ if (--clk->enable_count > 0)
+ return;
+
+ if (clk->ops->disable)
+ clk->ops->disable(clk->hw);
+ __clk_disable(clk->parent);
+}
+
+void clk_disable(struct clk *clk)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&enable_lock, flags);
+ __clk_disable(clk);
+ spin_unlock_irqrestore(&enable_lock, flags);
+}
+EXPORT_SYMBOL_GPL(clk_disable);
+
+static int __clk_enable(struct clk *clk)
+{
+ int ret;
+
+ if (!clk)
+ return 0;
+
+ if (WARN_ON(clk->prepare_count == 0))
+ return -ESHUTDOWN;
+
+
+ if (clk->enable_count == 0) {
+ ret = __clk_enable(clk->parent);
+ if (ret)
+ return ret;
+
+ if (clk->ops->enable) {
+ ret = clk->ops->enable(clk->hw);
+ if (ret) {
+ __clk_disable(clk->parent);
+ return ret;
+ }
+ }
+ }
+
+ clk->enable_count++;
+ return 0;
+}
+
+int clk_enable(struct clk *clk)
+{
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&enable_lock, flags);
+ ret = __clk_enable(clk);
+ spin_unlock_irqrestore(&enable_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(clk_enable);
+
+unsigned long clk_get_rate(struct clk *clk)
+{
+ if (!clk)
+ return 0;
+ return clk->rate;
+}
+EXPORT_SYMBOL_GPL(clk_get_rate);
+
+long clk_round_rate(struct clk *clk, unsigned long rate)
+{
+ if (clk && clk->ops->round_rate)
+ return clk->ops->round_rate(clk->hw, rate);
+ return rate;
+}
+EXPORT_SYMBOL_GPL(clk_round_rate);
+
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ /* not yet implemented */
+ return -ENOSYS;
+}
+EXPORT_SYMBOL_GPL(clk_set_rate);
+
+struct clk *clk_get_parent(struct clk *clk)
+{
+ if (!clk)
+ return NULL;
+
+ return clk->parent;
+}
+EXPORT_SYMBOL_GPL(clk_get_parent);
+
+int clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ /* not yet implemented */
+ return -ENOSYS;
+}
+EXPORT_SYMBOL_GPL(clk_set_parent);
+
+struct clk *clk_register(const struct clk_hw_ops *ops, struct clk_hw *hw,
+ const char *name)
+{
+ struct clk *clk;
+
+ clk = kzalloc(sizeof(*clk), GFP_KERNEL);
+ if (!clk)
+ return NULL;
+
+ INIT_HLIST_HEAD(&clk->children);
+ INIT_HLIST_NODE(&clk->child_node);
+
+ clk->name = name;
+ clk->ops = ops;
+ clk->hw = hw;
+ hw->clk = clk;
+
+ /* Query the hardware for parent and initial rate */
+
+ if (clk->ops->get_parent)
+ /* We don't to lock against prepare/enable here, as
+ * the clock is not yet accessible from anywhere */
+ clk->parent = clk->ops->get_parent(clk->hw);
+
+ if (clk->ops->recalc_rate)
+ clk->rate = clk->ops->recalc_rate(clk->hw);
+
+
+ return clk;
+}
+EXPORT_SYMBOL_GPL(clk_register);
diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c
index 6db161f..e2a9719 100644
--- a/drivers/clk/clkdev.c
+++ b/drivers/clk/clkdev.c
@@ -23,6 +23,13 @@
static LIST_HEAD(clocks);
static DEFINE_MUTEX(clocks_mutex);
+/* For USE_COMMON_STRUCT_CLK, these are provided in clk.c, but not exported
+ * through other headers; we don't want them used anywhere but here. */
+#ifdef CONFIG_USE_COMMON_STRUCT_CLK
+extern int __clk_get(struct clk *clk);
+extern void __clk_put(struct clk *clk);
+#endif
+
/*
* Find the correct struct clk for the device and connection ID.
* We do slightly fuzzy matching here:
diff --git a/include/linux/clk.h b/include/linux/clk.h
index 1d37f42..d6ae10b 100644
--- a/include/linux/clk.h
+++ b/include/linux/clk.h
@@ -3,6 +3,7 @@
*
* Copyright (C) 2004 ARM Limited.
* Written by Deep Blue Solutions Limited.
+ * Copyright (c) 2010-2011 Jeremy Kerr <jeremy.kerr@canonical.com>
*
* 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
@@ -11,17 +12,137 @@
#ifndef __LINUX_CLK_H
#define __LINUX_CLK_H
+#include <linux/kernel.h>
+#include <linux/errno.h>
+
struct device;
-/*
- * The base API.
+struct clk;
+
+#ifdef CONFIG_GENERIC_CLK
+
+struct clk_hw {
+ struct clk *clk;
+};
+
+/**
+ * struct clk_hw_ops - Callback operations for hardware clocks; these are to
+ * be provided by the clock implementation, and will be called by drivers
+ * through the clk_* API.
+ *
+ * @prepare: Prepare the clock for enabling. This must not return until
+ * the clock is fully prepared, and it's safe to call clk_enable.
+ * This callback is intended to allow clock implementations to
+ * do any initialisation that may sleep. Called with
+ * prepare_lock held.
+ *
+ * @unprepare: Release the clock from its prepared state. This will typically
+ * undo any work done in the @prepare callback. Called with
+ * prepare_lock held.
+ *
+ * @enable: Enable the clock atomically. This must not return until the
+ * clock is generating a valid clock signal, usable by consumer
+ * devices. Called with enable_lock held. This function must not
+ * sleep.
+ *
+ * @disable: Disable the clock atomically. Called with enable_lock held.
+ * This function must not sleep.
+ *
+ * @recalc_rate Recalculate the rate of this clock, by quering hardware
+ * and/or the clock's parent. Called with the global clock mutex
+ * held. Optional, but recommended - if this op is not set,
+ * clk_get_rate will return 0.
+ *
+ * @get_parent Query the parent of this clock; for clocks with multiple
+ * possible parents, query the hardware for the current
+ * parent. Currently only called when the clock is first
+ * registered.
+ *
+ * The clk_enable/clk_disable and clk_prepare/clk_unprepare pairs allow
+ * implementations to split any work between atomic (enable) and sleepable
+ * (prepare) contexts. If a clock requires sleeping code to be turned on, this
+ * should be done in clk_prepare. Switching that will not sleep should be done
+ * in clk_enable.
+ *
+ * Typically, drivers will call clk_prepare when a clock may be needed later
+ * (eg. when a device is opened), and clk_enable when the clock is actually
+ * required (eg. from an interrupt). Note that clk_prepare *must* have been
+ * called before clk_enable.
*/
+struct clk_hw_ops {
+ int (*prepare)(struct clk_hw *);
+ void (*unprepare)(struct clk_hw *);
+ int (*enable)(struct clk_hw *);
+ void (*disable)(struct clk_hw *);
+ unsigned long (*recalc_rate)(struct clk_hw *);
+ long (*round_rate)(struct clk_hw *, unsigned long);
+ struct clk * (*get_parent)(struct clk_hw *);
+};
+/**
+ * clk_prepare - prepare clock for atomic enabling.
+ *
+ * @clk: The clock to prepare
+ *
+ * Do any possibly sleeping initialisation on @clk, allowing the clock to be
+ * later enabled atomically (via clk_enable). This function may sleep.
+ */
+int clk_prepare(struct clk *clk);
+
+/**
+ * clk_unprepare - release clock from prepared state
+ *
+ * @clk: The clock to release
+ *
+ * Do any (possibly sleeping) cleanup on clk. This function may sleep.
+ */
+void clk_unprepare(struct clk *clk);
+
+/**
+ * clk_register - register and initialize a new clock
+ *
+ * @ops: ops for the new clock
+ * @hw: struct clk_hw to be passed to the ops of the new clock
+ * @name: name to use for the new clock
+ *
+ * Register a new clock with the clk subsystem. Returns either a
+ * struct clk for the new clock or a NULL pointer.
+ */
+struct clk *clk_register(const struct clk_hw_ops *ops, struct clk_hw *hw,
+ const char *name);
+
+/**
+ * clk_unregister - remove a clock
+ *
+ * @clk: clock to unregister
+ *
+ * Remove a clock from the clk subsystem. This is currently not
+ * implemented but is provided to allow unregistration code to be
+ * written in drivers ready for use when an implementation is
+ * provided.
+ */
+static inline int clk_unregister(struct clk *clk)
+{
+ return -EOPNOTSUPP;
+}
+
+#else /* !CONFIG_GENERIC_CLK */
/*
- * struct clk - an machine class defined object / cookie.
+ * For !CONFIG_GENERIC_CLK, we don't enforce any atomicity
+ * requirements for clk_enable/clk_disable, so the prepare and unprepare
+ * functions are no-ops
*/
-struct clk;
+static inline int clk_prepare(struct clk *clk) {
+ might_sleep();
+ return 0;
+}
+
+static inline void clk_unprepare(struct clk *clk) {
+ might_sleep();
+}
+
+#endif /* !CONFIG_GENERIC_CLK */
/**
* clk_get - lookup and obtain a reference to a clock producer.
@@ -67,6 +188,7 @@ void clk_disable(struct clk *clk);
/**
* clk_get_rate - obtain the current clock rate (in Hz) for a clock source.
* This is only valid once the clock source has been enabled.
+ * Returns zero if the clock rate is unknown.
* @clk: clock source
*/
unsigned long clk_get_rate(struct clk *clk);
@@ -83,12 +205,6 @@ unsigned long clk_get_rate(struct clk *clk);
*/
void clk_put(struct clk *clk);
-
-/*
- * The remaining APIs are optional for machine class support.
- */
-
-
/**
* clk_round_rate - adjust a rate to the exact rate a clock can provide
* @clk: clock source
@@ -97,7 +213,7 @@ void clk_put(struct clk *clk);
* Returns rounded clock rate in Hz, or negative errno.
*/
long clk_round_rate(struct clk *clk, unsigned long rate);
-
+
/**
* clk_set_rate - set the clock rate for a clock source
* @clk: clock source
@@ -106,7 +222,7 @@ long clk_round_rate(struct clk *clk, unsigned long rate);
* Returns success (0) or negative errno.
*/
int clk_set_rate(struct clk *clk, unsigned long rate);
-
+
/**
* clk_set_parent - set the parent clock source for this clock
* @clk: clock source
--
1.7.4.1
^ permalink raw reply related
* [PATCH v2 2/7] clk: Implement clk_set_rate
From: Mike Turquette @ 2011-09-22 22:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1316730422-20027-1-git-send-email-mturquette@ti.com>
From: Jeremy Kerr <jeremy.kerr@canonical.com>
Implement clk_set_rate by adding a set_rate callback to clk_hw_ops.
Rates are propagated down the clock tree and recalculated. Also adds a
flag for signaling that parents must change rates to achieve the desired
frequency (upstream propagation).
TODO:
Upstream propagation is not yet implemented.
Device pre-change and post-change notifications are not implemented, but
are marked up as FIXME comments.
Signed-off-by: Jeremy Kerr <jeremy.kerr@canonical.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: Mike Turquette <mturquette@ti.com>
---
Changes since v1:
Remove upstream propagation (for now)
Rename CLK_SET_RATE_PROPAGATE to CLK_PARENT_RATE_CHANGE
drivers/clk/clk.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++----
include/linux/clk.h | 12 ++++++++
2 files changed, 77 insertions(+), 6 deletions(-)
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 1cd7315..86636c2 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -21,6 +21,8 @@ struct clk {
unsigned int enable_count;
unsigned int prepare_count;
struct clk *parent;
+ struct hlist_head children;
+ struct hlist_node child_node;
unsigned long rate;
};
@@ -176,10 +178,57 @@ long clk_round_rate(struct clk *clk, unsigned long rate)
}
EXPORT_SYMBOL_GPL(clk_round_rate);
+/*
+ * clk_recalc_rates - Given a clock (with a recently updated clk->rate),
+ * notify its children that the rate may need to be recalculated, using
+ * ops->recalc_rate().
+ */
+static void clk_recalc_rates(struct clk *clk)
+{
+ struct hlist_node *tmp;
+ struct clk *child;
+
+ if (clk->ops->recalc_rate)
+ clk->rate = clk->ops->recalc_rate(clk->hw);
+
+ /* FIXME add post-rate change notification here */
+
+ hlist_for_each_entry(child, tmp, &clk->children, child_node)
+ clk_recalc_rates(child);
+}
+
int clk_set_rate(struct clk *clk, unsigned long rate)
{
- /* not yet implemented */
- return -ENOSYS;
+ unsigned long parent_rate, new_rate;
+ int ret = 0;
+
+ if (!clk->ops->set_rate)
+ return -ENOSYS;
+
+ new_rate = rate;
+
+ /* prevent racing with updates to the clock topology */
+ mutex_lock(&prepare_lock);
+
+ /* FIXME add pre-rate change notification here */
+
+ ret = clk->ops->set_rate(clk->hw, new_rate, &parent_rate);
+
+ /* FIXME ignores CLK_PARENT_RATE_CHANGE */
+ if (ret < 0)
+ /* FIXME add rate change abort notification here */
+ goto out;
+
+ /*
+ * If successful recalculate the rates of the clock, including
+ * children.
+ */
+ clk_recalc_rates(clk);
+
+out:
+ mutex_unlock(&prepare_lock);
+
+ return ret;
}
EXPORT_SYMBOL_GPL(clk_set_rate);
@@ -216,16 +265,26 @@ struct clk *clk_register(const struct clk_hw_ops *ops, struct clk_hw *hw,
clk->hw = hw;
hw->clk = clk;
- /* Query the hardware for parent and initial rate */
+ /*
+ * Query the hardware for parent and initial rate. We may alter
+ * the clock topology, making this clock available from the parent's
+ * children list. So, we need to protect against concurrent
+ * accesses through set_rate
+ */
+ mutex_lock(&prepare_lock);
- if (clk->ops->get_parent)
- /* We don't to lock against prepare/enable here, as
- * the clock is not yet accessible from anywhere */
+ if (clk->ops->get_parent) {
clk->parent = clk->ops->get_parent(clk->hw);
+ if (clk->parent)
+ hlist_add_head(&clk->child_node,
+ &clk->parent->children);
+ }
if (clk->ops->recalc_rate)
clk->rate = clk->ops->recalc_rate(clk->hw);
+ mutex_unlock(&prepare_lock);
+
return clk;
}
diff --git a/include/linux/clk.h b/include/linux/clk.h
index d6ae10b..0d2cd5e 100644
--- a/include/linux/clk.h
+++ b/include/linux/clk.h
@@ -58,6 +58,12 @@ struct clk_hw {
* parent. Currently only called when the clock is first
* registered.
*
+ * @set_rate Change the rate of this clock. If this callback returns
+ * CLK_SET_RATE_PROPAGATE, the rate change will be propagated to
+ * the parent clock (which may propagate again). The requested
+ * rate of the parent is passed back from the callback in the
+ * second 'unsigned long *' argument.
+ *
* The clk_enable/clk_disable and clk_prepare/clk_unprepare pairs allow
* implementations to split any work between atomic (enable) and sleepable
* (prepare) contexts. If a clock requires sleeping code to be turned on, this
@@ -76,9 +82,15 @@ struct clk_hw_ops {
void (*disable)(struct clk_hw *);
unsigned long (*recalc_rate)(struct clk_hw *);
long (*round_rate)(struct clk_hw *, unsigned long);
+ int (*set_rate)(struct clk_hw *,
+ unsigned long, unsigned long *);
struct clk * (*get_parent)(struct clk_hw *);
};
+enum {
+ CLK_PARENT_RATE_CHANGE = 1,
+};
+
/**
* clk_prepare - prepare clock for atomic enabling.
*
--
1.7.4.1
^ permalink raw reply related
* [PATCH v2 3/7] clk: Add fixed-rate clock
From: Mike Turquette @ 2011-09-22 22:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1316730422-20027-1-git-send-email-mturquette@ti.com>
From: Jeremy Kerr <jeremy.kerr@canonical.com>
Signed-off-by: Jeremy Kerr <jeremy.kerr@canonical.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: Mike Turquette <mturquette@ti.com>
---
Changes since v1:
Add copyright header
drivers/clk/Kconfig | 4 ++++
drivers/clk/Makefile | 1 +
drivers/clk/clk-fixed.c | 24 ++++++++++++++++++++++++
include/linux/clk.h | 14 ++++++++++++++
4 files changed, 43 insertions(+), 0 deletions(-)
create mode 100644 drivers/clk/clk-fixed.c
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index c53ed59..d8313d7 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -8,3 +8,7 @@ config HAVE_MACH_CLKDEV
config GENERIC_CLK
bool
+
+config GENERIC_CLK_FIXED
+ bool
+ depends on GENERIC_CLK
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 570d5b9..9a3325a 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -1,3 +1,4 @@
obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o
obj-$(CONFIG_GENERIC_CLK) += clk.o
+obj-$(CONFIG_GENERIC_CLK_FIXED) += clk-fixed.o
diff --git a/drivers/clk/clk-fixed.c b/drivers/clk/clk-fixed.c
new file mode 100644
index 0000000..956fb9a
--- /dev/null
+++ b/drivers/clk/clk-fixed.c
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
+ *
+ * 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.
+ *
+ * Simple fixed-rate clock implementation
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+
+#define to_clk_fixed(c) container_of(c, struct clk_hw_fixed, hw)
+
+static unsigned long clk_fixed_recalc_rate(struct clk_hw *hw)
+{
+ return to_clk_fixed(hw)->rate;
+}
+
+struct clk_hw_ops clk_fixed_ops = {
+ .recalc_rate = clk_fixed_recalc_rate,
+};
+EXPORT_SYMBOL_GPL(clk_fixed_ops);
diff --git a/include/linux/clk.h b/include/linux/clk.h
index 0d2cd5e..1903dd8 100644
--- a/include/linux/clk.h
+++ b/include/linux/clk.h
@@ -110,6 +110,20 @@ int clk_prepare(struct clk *clk);
*/
void clk_unprepare(struct clk *clk);
+/* Base clock implementations. Platform clock implementations can use these
+ * directly, or 'subclass' as approprate */
+
+#ifdef CONFIG_GENERIC_CLK_FIXED
+
+struct clk_hw_fixed {
+ struct clk_hw hw;
+ unsigned long rate;
+};
+
+extern struct clk_hw_ops clk_fixed_ops;
+
+#endif /* CONFIG_GENERIC_CLK_FIXED */
+
/**
* clk_register - register and initialize a new clock
*
--
1.7.4.1
^ permalink raw reply related
* [PATCH v2 4/7] clk: Add simple gated clock
From: Mike Turquette @ 2011-09-22 22:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1316730422-20027-1-git-send-email-mturquette@ti.com>
From: Jeremy Kerr <jeremy.kerr@canonical.com>
Signed-off-by: Jeremy Kerr <jeremy.kerr@canonical.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: Jamie Iles <jamie@jamieiles.com>
Signed-off-by: Mike Turquette <mturquette@ti.com>
---
Changes since v1:
Add copyright header
Fold in Jamie's patch for set-to-disable clks
Use BIT macro instead of shift
drivers/clk/Kconfig | 4 ++
drivers/clk/Makefile | 1 +
drivers/clk/clk-gate.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/clk.h | 13 ++++++++
4 files changed, 96 insertions(+), 0 deletions(-)
create mode 100644 drivers/clk/clk-gate.c
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index d8313d7..a78967c 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -12,3 +12,7 @@ config GENERIC_CLK
config GENERIC_CLK_FIXED
bool
depends on GENERIC_CLK
+
+config GENERIC_CLK_GATE
+ bool
+ depends on GENERIC_CLK
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 9a3325a..d186446 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -2,3 +2,4 @@
obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o
obj-$(CONFIG_GENERIC_CLK) += clk.o
obj-$(CONFIG_GENERIC_CLK_FIXED) += clk-fixed.o
+obj-$(CONFIG_GENERIC_CLK_GATE) += clk-gate.o
diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c
new file mode 100644
index 0000000..a1d8e79
--- /dev/null
+++ b/drivers/clk/clk-gate.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
+ *
+ * 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.
+ *
+ * Simple clk gate implementation
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <asm/io.h>
+
+#define to_clk_gate(clk) container_of(clk, struct clk_gate, hw)
+
+static unsigned long clk_gate_get_rate(struct clk_hw *clk)
+{
+ return clk_get_rate(clk_get_parent(clk->clk));
+}
+
+static void clk_gate_set_bit(struct clk_hw *clk)
+{
+ struct clk_gate *gate = to_clk_gate(clk);
+ u32 reg;
+
+ reg = __raw_readl(gate->reg);
+ reg |= BIT(gate->bit_idx);
+ __raw_writel(reg, gate->reg);
+}
+
+static void clk_gate_clear_bit(struct clk_hw *clk)
+{
+ struct clk_gate *gate = to_clk_gate(clk);
+ u32 reg;
+
+ reg = __raw_readl(gate->reg);
+ reg &= ~BIT(gate->bit_idx);
+ __raw_writel(reg, gate->reg);
+}
+
+static int clk_gate_enable_set(struct clk_hw *clk)
+{
+ clk_gate_set_bit(clk);
+
+ return 0;
+}
+
+static void clk_gate_disable_clear(struct clk_hw *clk)
+{
+ clk_gate_clear_bit(clk);
+}
+
+struct clk_hw_ops clk_gate_set_enable_ops = {
+ .recalc_rate = clk_gate_get_rate,
+ .enable = clk_gate_enable_set,
+ .disable = clk_gate_disable_clear,
+};
+EXPORT_SYMBOL_GPL(clk_gate_set_enable_ops);
+
+static int clk_gate_enable_clear(struct clk_hw *clk)
+{
+ clk_gate_clear_bit(clk);
+
+ return 0;
+}
+
+static void clk_gate_disable_set(struct clk_hw *clk)
+{
+ clk_gate_set_bit(clk);
+}
+
+struct clk_hw_ops clk_gate_set_disable_ops = {
+ .recalc_rate = clk_gate_get_rate,
+ .enable = clk_gate_enable_clear,
+ .disable = clk_gate_disable_set,
+};
+EXPORT_SYMBOL_GPL(clk_gate_set_disable_ops);
diff --git a/include/linux/clk.h b/include/linux/clk.h
index 1903dd8..626fd0d 100644
--- a/include/linux/clk.h
+++ b/include/linux/clk.h
@@ -124,6 +124,19 @@ extern struct clk_hw_ops clk_fixed_ops;
#endif /* CONFIG_GENERIC_CLK_FIXED */
+#ifdef CONFIG_GENERIC_CLK_GATE
+
+struct clk_gate {
+ struct clk_hw hw;
+ void __iomem *reg;
+ u8 bit_idx;
+};
+
+extern struct clk_hw_ops clk_gate_set_enable_ops;
+extern struct clk_hw_ops clk_gate_set_disable_ops;
+
+#endif /* CONFIG_GENERIC_CLK_GATE */
+
/**
* clk_register - register and initialize a new clock
*
--
1.7.4.1
^ permalink raw reply related
* [PATCH v2 5/7] clk: Add Kconfig option to build all generic clk drivers
From: Mike Turquette @ 2011-09-22 22:27 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1316730422-20027-1-git-send-email-mturquette@ti.com>
From: Mark Brown <broonie@opensource.wolfsonmicro.com>
Currently drivers for the generic clk subsystem must be selected by
platforms using them in order to enable build. When doing development on
the API or generic build time testing it is useful to be able to build
unused drivers in order to improve coverage so supply a Kconfig option
which allows this.
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
---
No change since v1
drivers/clk/Kconfig | 11 ++++++++++-
1 files changed, 10 insertions(+), 1 deletions(-)
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index a78967c..95b42a3 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -1,4 +1,3 @@
-
config CLKDEV_LOOKUP
bool
select HAVE_CLK
@@ -9,6 +8,16 @@ config HAVE_MACH_CLKDEV
config GENERIC_CLK
bool
+config GENERIC_CLK_BUILD_TEST
+ bool "Build all generic clock drivers"
+ depends on EXPERIMENTAL && GENERIC_CLK
+ select GENERIC_CLK_FIXED
+ select GENERIC_CLK_GATE
+ help
+ Enable all possible generic clock drivers. This is only
+ useful for improving build coverage, it is not useful for
+ production kernel builds.
+
config GENERIC_CLK_FIXED
bool
depends on GENERIC_CLK
--
1.7.4.1
^ permalink raw reply related
* [PATCH v2 6/7] clk: Add initial WM831x clock driver
From: Mike Turquette @ 2011-09-22 22:27 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1316730422-20027-1-git-send-email-mturquette@ti.com>
From: Mark Brown <broonie@opensource.wolfsonmicro.com>
The WM831x and WM832x series of PMICs contain a flexible clocking
subsystem intended to provide always on and system core clocks. It
features:
- A 32.768kHz crystal oscillator which can optionally be used to pass
through an externally generated clock.
- A FLL which can be clocked from either the 32.768kHz oscillator or
the CLKIN pin.
- A CLKOUT pin which can bring out either the oscillator or the FLL
output.
- The 32.768kHz clock can also optionally be brought out on the GPIO
pins of the device.
This driver fully supports the 32.768kHz oscillator and CLKOUT. The FLL
is supported only in AUTO mode, the full flexibility of the FLL cannot
currently be used. The use of clock references other than the internal
oscillator is not currently supported, and since clk_set_parent() is not
implemented in the generic clock API the clock tree configuration cannot
be changed at runtime.
Due to a lack of access to systems where the core SoC has been converted
to use the generic clock API this driver has been compile tested only.
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: Mike Turquette <mturquette@ti.com>
---
Changes since v1:
Changed CLK_SET_RATE_PROPAGATE to CLK_PARENT_RATE_CHANGE
Converted to clk_register to the original style, without passing in
struct device *dev. This is due to Mark's clock name collision
patch being dropped.
MAINTAINERS | 1 +
drivers/clk/Kconfig | 5 +
drivers/clk/Makefile | 1 +
drivers/clk/clk-wm831x.c | 386 ++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 393 insertions(+), 0 deletions(-)
create mode 100644 drivers/clk/clk-wm831x.c
diff --git a/MAINTAINERS b/MAINTAINERS
index ae8820e..a100ea0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7208,6 +7208,7 @@ T: git git://opensource.wolfsonmicro.com/linux-2.6-audioplus
W: http://opensource.wolfsonmicro.com/content/linux-drivers-wolfson-devices
S: Supported
F: Documentation/hwmon/wm83??
+F: drivers/clk/clk-wm83*.c
F: drivers/leds/leds-wm83*.c
F: drivers/input/misc/wm831x-on.c
F: drivers/input/touchscreen/wm831x-ts.c
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 95b42a3..8aca5ab 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -13,6 +13,7 @@ config GENERIC_CLK_BUILD_TEST
depends on EXPERIMENTAL && GENERIC_CLK
select GENERIC_CLK_FIXED
select GENERIC_CLK_GATE
+ select GENERIC_CLK_WM831X if MFD_WM831X=y
help
Enable all possible generic clock drivers. This is only
useful for improving build coverage, it is not useful for
@@ -25,3 +26,7 @@ config GENERIC_CLK_FIXED
config GENERIC_CLK_GATE
bool
depends on GENERIC_CLK
+
+config GENERIC_CLK_WM831X
+ tristate
+ depends on GENERIC_CLK && MFD_WM831X
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index d186446..6628ad5 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -3,3 +3,4 @@ obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o
obj-$(CONFIG_GENERIC_CLK) += clk.o
obj-$(CONFIG_GENERIC_CLK_FIXED) += clk-fixed.o
obj-$(CONFIG_GENERIC_CLK_GATE) += clk-gate.o
+obj-$(CONFIG_GENERIC_CLK_WM831X) += clk-wm831x.o
diff --git a/drivers/clk/clk-wm831x.c b/drivers/clk/clk-wm831x.c
new file mode 100644
index 0000000..bfcbcb5
--- /dev/null
+++ b/drivers/clk/clk-wm831x.c
@@ -0,0 +1,386 @@
+/*
+ * WM831x clock control
+ *
+ * Copyright 2011 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/wm831x/core.h>
+
+struct wm831x_clk {
+ struct wm831x *wm831x;
+ struct clk_hw xtal_hw;
+ struct clk_hw fll_hw;
+ struct clk_hw clkout_hw;
+ bool xtal_ena;
+};
+
+static int wm831x_xtal_enable(struct clk_hw *hw)
+{
+ struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
+ xtal_hw);
+
+ if (clkdata->xtal_ena)
+ return 0;
+ else
+ return -EPERM;
+}
+
+static unsigned long wm831x_xtal_recalc_rate(struct clk_hw *hw)
+{
+ struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
+ xtal_hw);
+
+ if (clkdata->xtal_ena)
+ return 32768;
+ else
+ return 0;
+}
+
+static long wm831x_xtal_round_rate(struct clk_hw *hw, unsigned long rate)
+{
+ return wm831x_xtal_recalc_rate(hw);
+}
+
+static const struct clk_hw_ops wm831x_xtal_ops = {
+ .enable = wm831x_xtal_enable,
+ .recalc_rate = wm831x_xtal_recalc_rate,
+ .round_rate = wm831x_xtal_round_rate,
+};
+
+static const unsigned long wm831x_fll_auto_rates[] = {
+ 2048000,
+ 11289600,
+ 12000000,
+ 12288000,
+ 19200000,
+ 22579600,
+ 24000000,
+ 24576000,
+};
+
+static bool wm831x_fll_enabled(struct wm831x *wm831x)
+{
+ int ret;
+
+ ret = wm831x_reg_read(wm831x, WM831X_FLL_CONTROL_1);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "Unable to read FLL_CONTROL_1: %d\n",
+ ret);
+ return true;
+ }
+
+ return ret & WM831X_FLL_ENA;
+}
+
+static int wm831x_fll_prepare(struct clk_hw *hw)
+{
+ struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
+ fll_hw);
+ struct wm831x *wm831x = clkdata->wm831x;
+ int ret;
+
+ ret = wm831x_set_bits(wm831x, WM831X_FLL_CONTROL_2,
+ WM831X_FLL_ENA, WM831X_FLL_ENA);
+ if (ret != 0)
+ dev_crit(wm831x->dev, "Failed to enable FLL: %d\n", ret);
+
+ return ret;
+}
+
+static void wm831x_fll_unprepare(struct clk_hw *hw)
+{
+ struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
+ fll_hw);
+ struct wm831x *wm831x = clkdata->wm831x;
+ int ret;
+
+ ret = wm831x_set_bits(wm831x, WM831X_FLL_CONTROL_2, WM831X_FLL_ENA, 0);
+ if (ret != 0)
+ dev_crit(wm831x->dev, "Failed to disaable FLL: %d\n", ret);
+}
+
+static unsigned long wm831x_fll_recalc_rate(struct clk_hw *hw)
+{
+ struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
+ fll_hw);
+ struct wm831x *wm831x = clkdata->wm831x;
+ int ret;
+
+ ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_2: %d\n",
+ ret);
+ return 0;
+ }
+
+ if (ret & WM831X_FLL_AUTO)
+ return wm831x_fll_auto_rates[ret & WM831X_FLL_AUTO_FREQ_MASK];
+
+ dev_err(wm831x->dev, "FLL only supported in AUTO mode\n");
+ return 0;
+}
+
+static int wm831x_fll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
+ fll_hw);
+ struct wm831x *wm831x = clkdata->wm831x;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(wm831x_fll_auto_rates); i++)
+ if (wm831x_fll_auto_rates[i] == rate)
+ break;
+ if (i == ARRAY_SIZE(wm831x_fll_auto_rates))
+ return -EINVAL;
+
+ if (wm831x_fll_enabled(wm831x))
+ return -EPERM;
+
+ return wm831x_set_bits(wm831x, WM831X_CLOCK_CONTROL_2,
+ WM831X_FLL_AUTO_FREQ_MASK, i);
+}
+
+static struct clk *wm831x_fll_get_parent(struct clk_hw *hw)
+{
+ struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
+ fll_hw);
+ struct wm831x *wm831x = clkdata->wm831x;
+ int ret;
+
+ /* AUTO mode is always clocked from the crystal */
+ ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_2: %d\n",
+ ret);
+ return NULL;
+ }
+
+ if (ret & WM831X_FLL_AUTO)
+ return clkdata->xtal_hw.clk;
+
+ ret = wm831x_reg_read(wm831x, WM831X_FLL_CONTROL_5);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "Unable to read FLL_CONTROL_5: %d\n",
+ ret);
+ return NULL;
+ }
+
+ switch (ret & WM831X_FLL_CLK_SRC_MASK) {
+ case 0:
+ return clkdata->xtal_hw.clk;
+ case 1:
+ dev_warn(wm831x->dev,
+ "FLL clocked from CLKIN not yet supported\n");
+ return NULL;
+ default:
+ dev_err(wm831x->dev, "Unsupported FLL clock source %d\n",
+ ret & WM831X_FLL_CLK_SRC_MASK);
+ return NULL;
+ }
+}
+
+static const struct clk_hw_ops wm831x_fll_ops = {
+ .prepare = wm831x_fll_prepare,
+ .unprepare = wm831x_fll_unprepare,
+ .recalc_rate = wm831x_fll_recalc_rate,
+ .set_rate = wm831x_fll_set_rate,
+ .get_parent = wm831x_fll_get_parent,
+};
+
+static int wm831x_clkout_prepare(struct clk_hw *hw)
+{
+ struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
+ clkout_hw);
+ struct wm831x *wm831x = clkdata->wm831x;
+ int ret;
+
+ ret = wm831x_reg_unlock(wm831x);
+ if (ret != 0) {
+ dev_crit(wm831x->dev, "Failed to lock registers: %d\n", ret);
+ return ret;
+ }
+
+ ret = wm831x_set_bits(wm831x, WM831X_CLOCK_CONTROL_1,
+ WM831X_CLKOUT_ENA, WM831X_CLKOUT_ENA);
+ if (ret != 0)
+ dev_crit(wm831x->dev, "Failed to enable CLKOUT: %d\n", ret);
+
+ wm831x_reg_lock(wm831x);
+
+ return ret;
+}
+
+static void wm831x_clkout_unprepare(struct clk_hw *hw)
+{
+ struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
+ clkout_hw);
+ struct wm831x *wm831x = clkdata->wm831x;
+ int ret;
+
+ ret = wm831x_reg_unlock(wm831x);
+ if (ret != 0) {
+ dev_crit(wm831x->dev, "Failed to lock registers: %d\n", ret);
+ return;
+ }
+
+ ret = wm831x_set_bits(wm831x, WM831X_CLOCK_CONTROL_1,
+ WM831X_CLKOUT_ENA, 0);
+ if (ret != 0)
+ dev_crit(wm831x->dev, "Failed to disable CLKOUT: %d\n", ret);
+
+ wm831x_reg_lock(wm831x);
+}
+
+static unsigned long wm831x_clkout_recalc_rate(struct clk_hw *hw)
+{
+ return clk_get_rate(clk_get_parent(hw->clk));
+}
+
+static long wm831x_clkout_round_rate(struct clk_hw *hw, unsigned long rate)
+{
+ return clk_round_rate(clk_get_parent(hw->clk), rate);
+}
+
+static int wm831x_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ *parent_rate = rate;
+ return CLK_PARENT_RATE_CHANGE;
+}
+
+static struct clk *wm831x_clkout_get_parent(struct clk_hw *hw)
+{
+ struct wm831x_clk *clkdata = container_of(hw, struct wm831x_clk,
+ clkout_hw);
+ struct wm831x *wm831x = clkdata->wm831x;
+ int ret;
+
+ ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_1);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_1: %d\n",
+ ret);
+ return NULL;
+ }
+
+ if (ret & WM831X_CLKOUT_SRC)
+ return clkdata->xtal_hw.clk;
+ else
+ return clkdata->fll_hw.clk;
+}
+
+static const struct clk_hw_ops wm831x_clkout_ops = {
+ .prepare = wm831x_clkout_prepare,
+ .unprepare = wm831x_clkout_unprepare,
+ .recalc_rate = wm831x_clkout_recalc_rate,
+ .round_rate = wm831x_clkout_round_rate,
+ .set_rate = wm831x_clkout_set_rate,
+ .get_parent = wm831x_clkout_get_parent,
+};
+
+static __devinit int wm831x_clk_probe(struct platform_device *pdev)
+{
+ struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
+ struct wm831x_clk *clkdata;
+ int ret;
+
+ clkdata = kzalloc(sizeof(*clkdata), GFP_KERNEL);
+ if (!clkdata)
+ return -ENOMEM;
+
+ /* XTAL_ENA can only be set via OTP/InstantConfig so just read once */
+ ret = wm831x_reg_read(wm831x, WM831X_CLOCK_CONTROL_2);
+ if (ret < 0) {
+ dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_2: %d\n",
+ ret);
+ goto err_alloc;
+ }
+ clkdata->xtal_ena = ret & WM831X_XTAL_ENA;
+
+ if (!clk_register(&wm831x_xtal_ops, &clkdata->xtal_hw,
+ "xtal")) {
+ ret = -EINVAL;
+ goto err_alloc;
+ }
+
+ if (!clk_register(&wm831x_fll_ops, &clkdata->fll_hw,
+ "fll")) {
+ ret = -EINVAL;
+ goto err_xtal;
+ }
+
+ if (!clk_register(&wm831x_clkout_ops, &clkdata->clkout_hw,
+ "clkout")) {
+ ret = -EINVAL;
+ goto err_fll;
+ }
+
+ dev_set_drvdata(&pdev->dev, clkdata);
+
+ return 0;
+
+err_fll:
+ clk_unregister(clkdata->fll_hw.clk);
+err_xtal:
+ clk_unregister(clkdata->xtal_hw.clk);
+err_alloc:
+ kfree(clkdata);
+ return ret;
+}
+
+static __devexit int wm831x_clk_remove(struct platform_device *pdev)
+{
+ struct wm831x_clk *clkdata = dev_get_drvdata(&pdev->dev);
+
+ clk_unregister(clkdata->clkout_hw.clk);
+ clk_unregister(clkdata->fll_hw.clk);
+ clk_unregister(clkdata->xtal_hw.clk);
+ kfree(clkdata);
+
+ return 0;
+}
+
+static struct platform_driver wm831x_clk_driver = {
+ .probe = wm831x_clk_probe,
+ .remove = __devexit_p(wm831x_clk_remove),
+ .driver = {
+ .name = "wm831x-clk",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init wm831x_clk_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&wm831x_clk_driver);
+ if (ret != 0)
+ pr_err("Failed to register WM831x clock driver: %d\n", ret);
+
+ return ret;
+}
+module_init(wm831x_clk_init);
+
+static void __exit wm831x_clk_exit(void)
+{
+ platform_driver_unregister(&wm831x_clk_driver);
+}
+module_exit(wm831x_clk_exit);
+
+/* Module information */
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("WM831x clock driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm831x-clk");
--
1.7.4.1
^ permalink raw reply related
* [PATCH v2 7/7] x86: Enable generic clk API on x86
From: Mike Turquette @ 2011-09-22 22:27 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1316730422-20027-1-git-send-email-mturquette@ti.com>
From: Mark Brown <broonie@opensource.wolfsonmicro.com>
Enable the generic clk API on x86, enabling use of the API by drivers for
x86 modules and also improving build coverage for clock API using devices.
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
---
No change since v1
arch/x86/Kconfig | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 6a47bb2..571b2c5 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -73,6 +73,7 @@ config X86
select HAVE_BPF_JIT if (X86_64 && NET)
select CLKEVT_I8253
select ARCH_HAVE_NMI_SAFE_CMPXCHG
+ select GENERIC_CLK
config INSTRUCTION_DECODER
def_bool (KPROBES || PERF_EVENTS)
--
1.7.4.1
^ permalink raw reply related
* [PATCH v3 1/3] AM35x: voltage: Basic initialization
From: Kevin Hilman @ 2011-09-22 22:30 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1316698155-954-2-git-send-email-abhilash.kv@ti.com>
Hi Abhilash,
Abhilash K V <abhilash.kv@ti.com> writes:
> This patch adds the basic initialization of voltage layer
> for AM35x. Since AM35x doesn't support voltage scaling,
I must admit to still being confused by this series.
This patch says AM35x doesn't support voltage scaling, and the next
patch adds PMIC support, and registers it with the voltage layer.
However, with each voltdm->scalable flag set to false, none of the PMIC
values will ever be used (by the current voltage layer.) Do you have
more patches on top of this that extend the voltage layer to directly
use the PMIC instead of using VC or VP?
I'm assuming we have some more assumptions in our current voltage layer
about the presence of VC/VP that are wrong and need to be fixed. Now
that the big voltage layer cleanup is done, I am *very* interested in
getting rid of any more assumptions we have in that code about how
devices are hooked up with PMICs.
Can you summarize how these devices are using (or want to use) the
voltage layer?
Thanks,
Kevin
> Many functions have been defined to plug into existing
> voltage layer.
>
> Signed-off-by: Sanjeev Premi <premi@ti.com>
> Signed-off-by: Abhilash K V <abhilash.kv@ti.com>
> ---
> arch/arm/mach-omap2/omap_opp_data.h | 1 +
> arch/arm/mach-omap2/opp3xxx_data.c | 9 +++++++++
> arch/arm/mach-omap2/voltagedomains3xxx_data.c | 10 ++++++++--
> 3 files changed, 18 insertions(+), 2 deletions(-)
>
> diff --git a/arch/arm/mach-omap2/omap_opp_data.h b/arch/arm/mach-omap2/omap_opp_data.h
> index c784c12..c7cedf3 100644
> --- a/arch/arm/mach-omap2/omap_opp_data.h
> +++ b/arch/arm/mach-omap2/omap_opp_data.h
> @@ -88,6 +88,7 @@ extern struct omap_volt_data omap34xx_vddmpu_volt_data[];
> extern struct omap_volt_data omap34xx_vddcore_volt_data[];
> extern struct omap_volt_data omap36xx_vddmpu_volt_data[];
> extern struct omap_volt_data omap36xx_vddcore_volt_data[];
> +extern struct omap_volt_data am35xx_vdd_volt_data[];
>
> extern struct omap_volt_data omap44xx_vdd_mpu_volt_data[];
> extern struct omap_volt_data omap44xx_vdd_iva_volt_data[];
> diff --git a/arch/arm/mach-omap2/opp3xxx_data.c b/arch/arm/mach-omap2/opp3xxx_data.c
> index d95f3f9..e4a5ee6 100644
> --- a/arch/arm/mach-omap2/opp3xxx_data.c
> +++ b/arch/arm/mach-omap2/opp3xxx_data.c
> @@ -85,6 +85,15 @@ struct omap_volt_data omap36xx_vddcore_volt_data[] = {
> VOLT_DATA_DEFINE(0, 0, 0, 0),
> };
>
> +/* AM35x
> + *
> + * Fields related to SmartReflex and Voltage Processor are set to 0.
> + */
fix multi-line comment style (search for 'multi-line' in Documentation/CodingStyle)
> +struct omap_volt_data am35xx_vdd_volt_data[] = {
> + VOLT_DATA_DEFINE(OMAP3430_VDD_MPU_OPP3_UV, 0x0, 0x0, 0x0),
> + VOLT_DATA_DEFINE(0, 0, 0, 0),
> +};
> +
> /* OPP data */
>
> static struct omap_opp_def __initdata omap34xx_opp_def_list[] = {
> diff --git a/arch/arm/mach-omap2/voltagedomains3xxx_data.c b/arch/arm/mach-omap2/voltagedomains3xxx_data.c
> index 071101d..530082f 100644
> --- a/arch/arm/mach-omap2/voltagedomains3xxx_data.c
> +++ b/arch/arm/mach-omap2/voltagedomains3xxx_data.c
> @@ -85,7 +85,10 @@ void __init omap3xxx_voltagedomains_init(void)
> * XXX Will depend on the process, validation, and binning
> * for the currently-running IC
> */
> - if (cpu_is_omap3630()) {
> + if (cpu_is_omap3505() || cpu_is_omap3517()) {
> + omap3_voltdm_mpu.volt_data = am35xx_vdd_volt_data;
> + omap3_voltdm_core.volt_data = am35xx_vdd_volt_data;
> + } else if (cpu_is_omap3630()) {
> omap3_voltdm_mpu.volt_data = omap36xx_vddmpu_volt_data;
> omap3_voltdm_core.volt_data = omap36xx_vddcore_volt_data;
> } else {
> @@ -93,8 +96,11 @@ void __init omap3xxx_voltagedomains_init(void)
> omap3_voltdm_core.volt_data = omap34xx_vddcore_volt_data;
> }
>
> - for (i = 0; voltdm = voltagedomains_omap3[i], voltdm; i++)
> + for (i = 0; voltdm = voltagedomains_omap3[i], voltdm; i++) {
> + if (cpu_is_omap3505() || cpu_is_omap3517())
> + voltdm->scalable = false;
> voltdm->sys_clk.name = sys_clk_name;
> + }
>
> voltdm_init(voltagedomains_omap3);
> };
^ permalink raw reply
* [RFC PATCH v3] drivercore: Add driver probe deferral mechanism
From: Alan Cox @ 2011-09-22 22:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <4E7BA661.7070903@cavium.com>
> How would a given driver know that something else is waiting for it? Or
> would we add the explicit kick to each and every driver in the tree?
I think there are very very few drivers that have this property and don't
already implicitly cause a probe by creating a new bus or device.
Those drivers that set something up for another device really should
know what is going on because they are making a guarantee that they are
ready for the other device to call into them or whatever is going on at
some point, either explicitly in the kick or implicitly in returning from
their probe method.
I know which I think is clearer and easier for a 3rd party to see and not
miss completely when updating code.
Alan
^ permalink raw reply
* [PATCH v2] gpio: pl061: add DT binding support
From: Rob Herring @ 2011-09-22 23:08 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1313011906-24161-1-git-send-email-robherring2@gmail.com>
Grant,
On 08/10/2011 04:31 PM, Rob Herring wrote:
> From: Rob Herring <rob.herring@calxeda.com>
>
> This adds devicetree binding support to the ARM pl061 driver removing the
> platform_data dependency. When DT binding is used, the gpio numbering is
> assigned dynamically. For now, interrupts are not supported with DT until
> irqdomains learn dynamic irq assignment.
>
> Rather than add another case of -1, updating the driver to use NO_IRQ.
>
> Signed-off-by: Rob Herring <rob.herring@calxeda.com>
> Cc: Grant Likely <grant.likely@secretlab.ca>
> ---
Ping. Any comments on this?
Rob
>
> v2:
> - Add other -1 to NO_IRQ conversions
> - Drop DT irq support for now
>
> drivers/gpio/gpio-pl061.c | 31 +++++++++++++++++++++----------
> include/linux/amba/pl061.h | 3 +--
> 2 files changed, 22 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c
> index 2c5a18f..093c90b 100644
> --- a/drivers/gpio/gpio-pl061.c
> +++ b/drivers/gpio/gpio-pl061.c
> @@ -118,7 +118,7 @@ static int pl061_to_irq(struct gpio_chip *gc, unsigned offset)
> {
> struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc);
>
> - if (chip->irq_base == (unsigned) -1)
> + if (chip->irq_base == NO_IRQ)
> return -EINVAL;
>
> return chip->irq_base + offset;
> @@ -246,6 +246,18 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id)
> if (chip == NULL)
> return -ENOMEM;
>
> + pdata = dev->dev.platform_data;
> + if (pdata) {
> + chip->gc.base = pdata->gpio_base;
> + chip->irq_base = pdata->irq_base;
> + } else if (dev->dev.of_node) {
> + chip->gc.base = -1;
> + chip->irq_base = NO_IRQ;
> + } else {
> + ret = -ENODEV;
> + goto free_mem;
> + }
> +
> if (!request_mem_region(dev->res.start,
> resource_size(&dev->res), "pl061")) {
> ret = -EBUSY;
> @@ -267,14 +279,11 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id)
> chip->gc.get = pl061_get_value;
> chip->gc.set = pl061_set_value;
> chip->gc.to_irq = pl061_to_irq;
> - chip->gc.base = pdata->gpio_base;
> chip->gc.ngpio = PL061_GPIO_NR;
> chip->gc.label = dev_name(&dev->dev);
> chip->gc.dev = &dev->dev;
> chip->gc.owner = THIS_MODULE;
>
> - chip->irq_base = pdata->irq_base;
> -
> ret = gpiochip_add(&chip->gc);
> if (ret)
> goto iounmap;
> @@ -283,7 +292,7 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id)
> * irq_chip support
> */
>
> - if (chip->irq_base == (unsigned) -1)
> + if (chip->irq_base == NO_IRQ)
> return 0;
>
> writeb(0, chip->base + GPIOIE); /* disable irqs */
> @@ -307,11 +316,13 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id)
> list_add(&chip->list, chip_list);
>
> for (i = 0; i < PL061_GPIO_NR; i++) {
> - if (pdata->directions & (1 << i))
> - pl061_direction_output(&chip->gc, i,
> - pdata->values & (1 << i));
> - else
> - pl061_direction_input(&chip->gc, i);
> + if (pdata) {
> + if (pdata->directions & (1 << i))
> + pl061_direction_output(&chip->gc, i,
> + pdata->values & (1 << i));
> + else
> + pl061_direction_input(&chip->gc, i);
> + }
>
> irq_set_chip_and_handler(i + chip->irq_base, &pl061_irqchip,
> handle_simple_irq);
> diff --git a/include/linux/amba/pl061.h b/include/linux/amba/pl061.h
> index 5ddd9ad..2412af9 100644
> --- a/include/linux/amba/pl061.h
> +++ b/include/linux/amba/pl061.h
> @@ -7,8 +7,7 @@ struct pl061_platform_data {
> unsigned gpio_base;
>
> /* number of the first IRQ.
> - * If the IRQ functionality in not desired this must be set to
> - * (unsigned) -1.
> + * If the IRQ functionality in not desired this must be set to NO_IRQ.
> */
> unsigned irq_base;
>
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox