Sekhar Nori <[email protected]> writes: > Currently, the spinlock in DaVinci clock framework is being > used to: > > 1) Protect clock structure variables usecount and rate against > concurrent modification. > > 2) Protect against simultaneous PSC enables/disables ie. > serialize davinci_psc_config(). > > 3) Serialize clk_set_rate(): > i. Prevent simultaneous setting of clock rates > ii. Ensure clock list remains sane during rate > propagation (also in clk_set_parent). > > Remove the spinlock usage in clock framework by: > > 1) Making clock rate and usecount as atomic variables. > > 2) Making davinci_psc_config() protect itself instead of > relying on serialization by caller. > > 3) (i) Allowing the clk->set_rate to serialize itself. There > should be no need to serialize all clock rate settings. > Currently the only user of rate setting is cpufreq driver on > DA850. cpufreq naturally serializes the calls to set CPU rate. > Also, there appears no need to lock IRQs during CPU rate > transtitions. If required, IRQs can be locked in the actual > set_rate function. > > 3) (ii) Use the mutex already in place for serialzing clock list > manipulation for serializing clock rate propagation as well. > > Apart from the general benefit of reducing locking granurlarity, > the main motivation behind this change is to enable usage of > clock framework for off-chip clock synthesizers. One such > synthesizer, CDCE949, is present on DM6467 EVM. Access to the > synthesizer happens through I2C bus - accessing which can lead to > CPU sleep. Having IRQs locked in clk_set_rate prevents the > clk->set_rate function from sleeping. > > Signed-off-by: Sekhar Nori <[email protected]> > --- > arch/arm/mach-davinci/board-dm646x-evm.c | 4 +- > arch/arm/mach-davinci/clock.c | 74 ++++++++++++----------------- > arch/arm/mach-davinci/clock.h | 4 +- > arch/arm/mach-davinci/da830.c | 2 +- > arch/arm/mach-davinci/da850.c | 4 +- > arch/arm/mach-davinci/dm355.c | 4 +- > arch/arm/mach-davinci/dm365.c | 4 +- > arch/arm/mach-davinci/dm644x.c | 8 ++-- > arch/arm/mach-davinci/dm646x.c | 8 ++-- > arch/arm/mach-davinci/psc.c | 6 ++ > 10 files changed, 56 insertions(+), 62 deletions(-) > > diff --git a/arch/arm/mach-davinci/board-dm646x-evm.c > b/arch/arm/mach-davinci/board-dm646x-evm.c > index 542bfdb..6ff3411 100644 > --- a/arch/arm/mach-davinci/board-dm646x-evm.c > +++ b/arch/arm/mach-davinci/board-dm646x-evm.c > @@ -722,9 +722,9 @@ static __init void davinci_dm646x_evm_irq_init(void) > void __init dm646x_board_setup_refclk(struct clk *clk) > { > if (machine_is_davinci_dm6467tevm()) > - clk->rate = DM6467T_EVM_REF_FREQ; > + atomic_set(&clk->rate, DM6467T_EVM_REF_FREQ); > else > - clk->rate = DM646X_EVM_REF_FREQ; > + atomic_set(&clk->rate, DM646X_EVM_REF_FREQ); > } > > MACHINE_START(DAVINCI_DM6467_EVM, "DaVinci DM646x EVM") > diff --git a/arch/arm/mach-davinci/clock.c b/arch/arm/mach-davinci/clock.c > index baece65..df884c8 100644 > --- a/arch/arm/mach-davinci/clock.c > +++ b/arch/arm/mach-davinci/clock.c > @@ -28,7 +28,6 @@ > > static LIST_HEAD(clocks); > static DEFINE_MUTEX(clocks_mutex); > -static DEFINE_SPINLOCK(clockfw_lock); > > static unsigned psc_domain(struct clk *clk) > { > @@ -41,15 +40,16 @@ static void __clk_enable(struct clk *clk) > { > if (clk->parent) > __clk_enable(clk->parent); > - if (clk->usecount++ == 0 && (clk->flags & CLK_PSC)) > + if (atomic_read(&clk->usecount) == 0 && (clk->flags & CLK_PSC)) > davinci_psc_config(psc_domain(clk), clk->gpsc, clk->lpsc, 1); > + atomic_inc(&clk->usecount); > } > > static void __clk_disable(struct clk *clk) > { > - if (WARN_ON(clk->usecount == 0)) > + if (WARN_ON(atomic_read(&clk->usecount) == 0)) > return; > - if (--clk->usecount == 0 && !(clk->flags & CLK_PLL)) > + if (atomic_dec_and_test(&clk->usecount) && !(clk->flags & CLK_PLL)) > davinci_psc_config(psc_domain(clk), clk->gpsc, clk->lpsc, 0); > if (clk->parent) > __clk_disable(clk->parent); > @@ -57,14 +57,10 @@ static void __clk_disable(struct clk *clk) > > int clk_enable(struct clk *clk) > { > - unsigned long flags; > - > if (clk == NULL || IS_ERR(clk)) > return -EINVAL; > > - spin_lock_irqsave(&clockfw_lock, flags); > __clk_enable(clk); > - spin_unlock_irqrestore(&clockfw_lock, flags); > > return 0; > } > @@ -72,14 +68,10 @@ EXPORT_SYMBOL(clk_enable); > > void clk_disable(struct clk *clk) > { > - unsigned long flags; > - > if (clk == NULL || IS_ERR(clk)) > return; > > - spin_lock_irqsave(&clockfw_lock, flags); > __clk_disable(clk); > - spin_unlock_irqrestore(&clockfw_lock, flags); > } > EXPORT_SYMBOL(clk_disable); > > @@ -88,7 +80,7 @@ unsigned long clk_get_rate(struct clk *clk) > if (clk == NULL || IS_ERR(clk)) > return -EINVAL; > > - return clk->rate; > + return atomic_read(&clk->rate); > } > EXPORT_SYMBOL(clk_get_rate); > > @@ -100,7 +92,7 @@ long clk_round_rate(struct clk *clk, unsigned long rate) > if (clk->round_rate) > return clk->round_rate(clk, rate); > > - return clk->rate; > + return atomic_read(&clk->rate); > } > EXPORT_SYMBOL(clk_round_rate); > > @@ -111,28 +103,27 @@ static void propagate_rate(struct clk *root) > > list_for_each_entry(clk, &root->children, childnode) { > if (clk->recalc) > - clk->rate = clk->recalc(clk); > + atomic_set(&clk->rate, clk->recalc(clk)); > propagate_rate(clk); > } > } > > int clk_set_rate(struct clk *clk, unsigned long rate) > { > - unsigned long flags; > int ret = -EINVAL; > > if (clk == NULL || IS_ERR(clk)) > return ret; > > - spin_lock_irqsave(&clockfw_lock, flags); > if (clk->set_rate) > ret = clk->set_rate(clk, rate); > if (ret == 0) { > if (clk->recalc) > - clk->rate = clk->recalc(clk); > + atomic_set(&clk->rate, clk->recalc(clk)); > + mutex_lock(&clocks_mutex); > propagate_rate(clk); > + mutex_unlock(&clocks_mutex); > } > - spin_unlock_irqrestore(&clockfw_lock, flags); > > return ret; > } > @@ -140,26 +131,22 @@ EXPORT_SYMBOL(clk_set_rate); > > int clk_set_parent(struct clk *clk, struct clk *parent) > { > - unsigned long flags; > - > if (clk == NULL || IS_ERR(clk)) > return -EINVAL; > > /* Cannot change parent on enabled clock */ > - if (WARN_ON(clk->usecount)) > + if (WARN_ON(atomic_read(&clk->usecount))) > return -EINVAL; > > mutex_lock(&clocks_mutex); > clk->parent = parent; > list_del_init(&clk->childnode); > list_add(&clk->childnode, &clk->parent->children); > - mutex_unlock(&clocks_mutex); > > - spin_lock_irqsave(&clockfw_lock, flags); > if (clk->recalc) > - clk->rate = clk->recalc(clk); > + atomic_set(&clk->rate, clk->recalc(clk)); > propagate_rate(clk); > - spin_unlock_irqrestore(&clockfw_lock, flags); > + mutex_unlock(&clocks_mutex); > > return 0; > } > @@ -170,7 +157,7 @@ int clk_register(struct clk *clk) > if (clk == NULL || IS_ERR(clk)) > return -EINVAL; > > - if (WARN(clk->parent && !clk->parent->rate, > + if (WARN(clk->parent && !atomic_read(&clk->parent->rate), > "CLK: %s parent %s has no rate!\n", > clk->name, clk->parent->name)) > return -EINVAL; > @@ -184,16 +171,16 @@ int clk_register(struct clk *clk) > mutex_unlock(&clocks_mutex); > > /* If rate is already set, use it */ > - if (clk->rate) > + if (atomic_read(&clk->rate)) > return 0; > > /* Else, see if there is a way to calculate it */ > if (clk->recalc) > - clk->rate = clk->recalc(clk); > + atomic_set(&clk->rate, clk->recalc(clk)); > > /* Otherwise, default to parent rate */ > else if (clk->parent) > - clk->rate = clk->parent->rate; > + atomic_set(&clk->rate, atomic_read(&clk->parent->rate)); > > return 0; > } > @@ -219,9 +206,9 @@ static int __init clk_disable_unused(void) > { > struct clk *ck; > > - spin_lock_irq(&clockfw_lock); > + mutex_lock(&clocks_mutex); > list_for_each_entry(ck, &clocks, node) { > - if (ck->usecount > 0) > + if (atomic_read(&ck->usecount) > 0) > continue; > if (!(ck->flags & CLK_PSC)) > continue; > @@ -233,7 +220,7 @@ static int __init clk_disable_unused(void) > pr_info("Clocks: disable unused %s\n", ck->name); > davinci_psc_config(psc_domain(ck), ck->gpsc, ck->lpsc, 0); > } > - spin_unlock_irq(&clockfw_lock); > + mutex_unlock(&clocks_mutex); > > return 0; > } > @@ -244,7 +231,7 @@ static unsigned long clk_sysclk_recalc(struct clk *clk) > { > u32 v, plldiv; > struct pll_data *pll; > - unsigned long rate = clk->rate; > + unsigned long rate = atomic_read(&clk->rate); > > /* If this is the PLL base clock, no more calculations needed */ > if (clk->pll_data) > @@ -253,7 +240,7 @@ static unsigned long clk_sysclk_recalc(struct clk *clk) > if (WARN_ON(!clk->parent)) > return rate; > > - rate = clk->parent->rate; > + rate = atomic_read(&clk->parent->rate); > > /* Otherwise, the parent must be a PLL */ > if (WARN_ON(!clk->parent->pll_data)) > @@ -281,9 +268,9 @@ static unsigned long clk_sysclk_recalc(struct clk *clk) > static unsigned long clk_leafclk_recalc(struct clk *clk) > { > if (WARN_ON(!clk->parent)) > - return clk->rate; > + return atomic_read(&clk->rate); > > - return clk->parent->rate; > + return atomic_read(&clk->parent->rate); > } > > static unsigned long clk_pllclk_recalc(struct clk *clk) > @@ -291,11 +278,11 @@ static unsigned long clk_pllclk_recalc(struct clk *clk) > u32 ctrl, mult = 1, prediv = 1, postdiv = 1; > u8 bypass; > struct pll_data *pll = clk->pll_data; > - unsigned long rate = clk->rate; > + unsigned long rate = atomic_read(&clk->rate); > > pll->base = IO_ADDRESS(pll->phys_base); > ctrl = __raw_readl(pll->base + PLLCTL); > - rate = pll->input_rate = clk->parent->rate; > + rate = pll->input_rate = atomic_read(&clk->parent->rate); > > if (ctrl & PLLCTL_PLLEN) { > bypass = 0; > @@ -333,8 +320,8 @@ static unsigned long clk_pllclk_recalc(struct clk *clk) > rate /= postdiv; > } > > - pr_debug("PLL%d: input = %lu MHz [ ", > - pll->num, clk->parent->rate / 1000000); > + pr_debug("PLL%d: input = %u MHz [ ", > + pll->num, atomic_read(&clk->parent->rate) / 1000000); > if (bypass) > pr_debug("bypass "); > if (prediv > 1) > @@ -452,7 +439,7 @@ int __init davinci_clk_init(struct davinci_clk *clocks) > } > > if (clk->recalc) > - clk->rate = clk->recalc(clk); > + atomic_set(&clk->rate, clk->recalc(clk)); > > if (clk->lpsc) > clk->flags |= CLK_PSC; > @@ -514,7 +501,8 @@ dump_clock(struct seq_file *s, unsigned nest, struct clk > *parent) > min(i, (unsigned)(sizeof(buf) - 1 - nest))); > > seq_printf(s, "%s users=%2d %-3s %9ld Hz\n", > - buf, parent->usecount, state, clk_get_rate(parent)); > + buf, atomic_read(&parent->usecount), state, > + clk_get_rate(parent)); > /* REVISIT show device associations too */ > > /* cost is now small, but not linear... */ > diff --git a/arch/arm/mach-davinci/clock.h b/arch/arm/mach-davinci/clock.h > index c92d77a..796a568 100644 > --- a/arch/arm/mach-davinci/clock.h > +++ b/arch/arm/mach-davinci/clock.h > @@ -67,8 +67,8 @@ struct clk { > struct list_head node; > struct module *owner; > const char *name; > - unsigned long rate; > - u8 usecount; > + atomic_t rate; > + atomic_t usecount; > u8 lpsc; > u8 gpsc; > u32 flags; > diff --git a/arch/arm/mach-davinci/da830.c b/arch/arm/mach-davinci/da830.c > index b22b5cf..d1961d2 100644 > --- a/arch/arm/mach-davinci/da830.c > +++ b/arch/arm/mach-davinci/da830.c > @@ -43,7 +43,7 @@ static struct pll_data pll0_data = { > > static struct clk ref_clk = { > .name = "ref_clk", > - .rate = DA830_REF_FREQ, > + .rate = ATOMIC_INIT(DA830_REF_FREQ), > }; > > static struct clk pll0_clk = { > diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c > index 717806c..62f8c22 100644 > --- a/arch/arm/mach-davinci/da850.c > +++ b/arch/arm/mach-davinci/da850.c > @@ -54,7 +54,7 @@ static struct pll_data pll0_data = { > > static struct clk ref_clk = { > .name = "ref_clk", > - .rate = DA850_REF_FREQ, > + .rate = ATOMIC_INIT(DA850_REF_FREQ), > }; > > static struct clk pll0_clk = { > @@ -1024,7 +1024,7 @@ static int da850_set_pll0rate(struct clk *clk, unsigned > long armrate) > > static int da850_round_armrate(struct clk *clk, unsigned long rate) > { > - return clk->rate; > + return atomic_read(&clk->rate); > } > #endif > > diff --git a/arch/arm/mach-davinci/dm355.c b/arch/arm/mach-davinci/dm355.c > index dedf4d4..2244e8c 100644 > --- a/arch/arm/mach-davinci/dm355.c > +++ b/arch/arm/mach-davinci/dm355.c > @@ -55,7 +55,7 @@ static struct pll_data pll2_data = { > static struct clk ref_clk = { > .name = "ref_clk", > /* FIXME -- crystal rate is board-specific */ > - .rate = DM355_REF_FREQ, > + .rate = ATOMIC_INIT(DM355_REF_FREQ), > }; > > static struct clk pll1_clk = { > @@ -314,7 +314,7 @@ static struct clk timer2_clk = { > .name = "timer2", > .parent = &pll1_aux_clk, > .lpsc = DAVINCI_LPSC_TIMER2, > - .usecount = 1, /* REVISIT: why cant' this be disabled? */ > + .usecount = ATOMIC_INIT(1), /* REVISIT: why cant' this be disabled? */ > }; > > static struct clk timer3_clk = { > diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c > index 2ec619e..cc3bae4 100644 > --- a/arch/arm/mach-davinci/dm365.c > +++ b/arch/arm/mach-davinci/dm365.c > @@ -52,7 +52,7 @@ static struct pll_data pll2_data = { > > static struct clk ref_clk = { > .name = "ref_clk", > - .rate = DM365_REF_FREQ, > + .rate = ATOMIC_INIT(DM365_REF_FREQ), > }; > > static struct clk pll1_clk = { > @@ -358,7 +358,7 @@ static struct clk timer2_clk = { > .name = "timer2", > .parent = &pll1_aux_clk, > .lpsc = DAVINCI_LPSC_TIMER2, > - .usecount = 1, > + .usecount = ATOMIC_INIT(1), > }; > > static struct clk timer3_clk = { > diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c > index 2cd0081..e65e29e 100644 > --- a/arch/arm/mach-davinci/dm644x.c > +++ b/arch/arm/mach-davinci/dm644x.c > @@ -47,7 +47,7 @@ static struct pll_data pll2_data = { > > static struct clk ref_clk = { > .name = "ref_clk", > - .rate = DM644X_REF_FREQ, > + .rate = ATOMIC_INIT(DM644X_REF_FREQ), > }; > > static struct clk pll1_clk = { > @@ -131,7 +131,7 @@ static struct clk dsp_clk = { > .parent = &pll1_sysclk1, > .lpsc = DAVINCI_LPSC_GEM, > .flags = PSC_DSP, > - .usecount = 1, /* REVISIT how to disable? */ > + .usecount = ATOMIC_INIT(1), /* REVISIT how to disable? */ > }; > > static struct clk arm_clk = { > @@ -146,7 +146,7 @@ static struct clk vicp_clk = { > .parent = &pll1_sysclk2, > .lpsc = DAVINCI_LPSC_IMCOP, > .flags = PSC_DSP, > - .usecount = 1, /* REVISIT how to disable? */ > + .usecount = ATOMIC_INIT(1), /* REVISIT how to disable? */ > }; > > static struct clk vpss_master_clk = { > @@ -274,7 +274,7 @@ static struct clk timer2_clk = { > .name = "timer2", > .parent = &pll1_aux_clk, > .lpsc = DAVINCI_LPSC_TIMER2, > - .usecount = 1, /* REVISIT: why cant' this be disabled? */ > + .usecount = ATOMIC_INIT(1), /* REVISIT: why cant' this be disabled? */ > }; > > struct davinci_clk dm644x_clks[] = { > diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c > index 515d3ed..6f80616 100644 > --- a/arch/arm/mach-davinci/dm646x.c > +++ b/arch/arm/mach-davinci/dm646x.c > @@ -60,7 +60,7 @@ static struct clk ref_clk = { > > static struct clk aux_clkin = { > .name = "aux_clkin", > - .rate = DM646X_AUX_FREQ, > + .rate = ATOMIC_INIT(DM646X_AUX_FREQ), > }; > > static struct clk pll1_clk = { > @@ -158,7 +158,7 @@ static struct clk dsp_clk = { > .parent = &pll1_sysclk1, > .lpsc = DM646X_LPSC_C64X_CPU, > .flags = PSC_DSP, > - .usecount = 1, /* REVISIT how to disable? */ > + .usecount = ATOMIC_INIT(1), /* REVISIT how to disable? */ > }; > > static struct clk arm_clk = { > @@ -262,14 +262,14 @@ static struct clk pwm0_clk = { > .name = "pwm0", > .parent = &pll1_sysclk3, > .lpsc = DM646X_LPSC_PWM0, > - .usecount = 1, /* REVIST: disabling hangs system */ > + .usecount = ATOMIC_INIT(1), /* REVIST: disabling hangs system */ > }; > > static struct clk pwm1_clk = { > .name = "pwm1", > .parent = &pll1_sysclk3, > .lpsc = DM646X_LPSC_PWM1, > - .usecount = 1, /* REVIST: disabling hangs system */ > + .usecount = ATOMIC_INIT(1), /* REVIST: disabling hangs system */ > }; > > static struct clk timer0_clk = { > diff --git a/arch/arm/mach-davinci/psc.c b/arch/arm/mach-davinci/psc.c > index 04a3cb7..a9a4d08 100644 > --- a/arch/arm/mach-davinci/psc.c > +++ b/arch/arm/mach-davinci/psc.c > @@ -21,6 +21,7 @@ > #include <linux/kernel.h> > #include <linux/init.h> > #include <linux/io.h> > +#include <linux/spinlock.h> > > #include <mach/cputype.h> > #include <mach/psc.h> > @@ -64,6 +65,9 @@ void davinci_psc_config(unsigned int domain, unsigned int > ctlr, > void __iomem *psc_base; > struct davinci_soc_info *soc_info = &davinci_soc_info; > u32 next_state = enable ? 0x3 : 0x2; /* 0x3 enables, 0x2 disables */ > + /* Protect against simultaneous enable/disable of PSCs */ > + DEFINE_SPINLOCK(lock); > + unsigned long flags; > > if (!soc_info->psc_bases || (ctlr >= soc_info->psc_bases_num)) { > pr_warning("PSC: Bad psc data: 0x%x[%d]\n", > @@ -73,6 +77,7 @@ void davinci_psc_config(unsigned int domain, unsigned int > ctlr, > > psc_base = soc_info->psc_bases[ctlr]; > > + spin_lock_irqsave(&lock, flags); > mdctl = __raw_readl(psc_base + MDCTL + 4 * id); > mdctl &= ~MDSTAT_STATE_MASK; > mdctl |= next_state; > @@ -111,4 +116,5 @@ void davinci_psc_config(unsigned int domain, unsigned int > ctlr, > do { > mdstat = __raw_readl(psc_base + MDSTAT + 4 * id); > } while (!((mdstat & MDSTAT_STATE_MASK) == next_state)); > + spin_unlock_irqrestore(&lock, flags); > } > -- > 1.6.2.4 > > _______________________________________________ > Davinci-linux-open-source mailing list > [email protected] > http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source
Sekhar Nori <[email protected]> writes: > This patch modifies clock dump to take care of > clock tress rooted at multiple oscillators. > Current code assumes the entire tree is rooted > on a single oscillator. When using off-chip > clock synthesizers, some of the clocks can > be obtained from a different on-board oscillator. > > Signed-off-by: Sekhar Nori <[email protected]> > --- > arch/arm/mach-davinci/clock.c | 11 +++++++---- > 1 files changed, 7 insertions(+), 4 deletions(-) > > diff --git a/arch/arm/mach-davinci/clock.c b/arch/arm/mach-davinci/clock.c > index df884c8..ba0b68f 100644 > --- a/arch/arm/mach-davinci/clock.c > +++ b/arch/arm/mach-davinci/clock.c > @@ -513,12 +513,15 @@ dump_clock(struct seq_file *s, unsigned nest, struct > clk *parent) > > static int davinci_ck_show(struct seq_file *m, void *v) > { > - /* Show clock tree; we know the main oscillator is first. > - * We trust nonzero usecounts equate to PSC enables... > + struct clk *clk; > + > + /* > + * Show clock tree; We trust nonzero usecounts equate to PSC enables... > */ > mutex_lock(&clocks_mutex); > - if (!list_empty(&clocks)) > - dump_clock(m, 0, list_first_entry(&clocks, struct clk, node)); > + list_for_each_entry(clk, &clocks, node) > + if (!clk->parent) > + dump_clock(m, 0, clk); > mutex_unlock(&clocks_mutex); > > return 0; > -- > 1.6.2.4 > > _______________________________________________ > Davinci-linux-open-source mailing list > [email protected] > http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source Sekhar Nori <[email protected]> writes: > Move /proc/davinci_clocks to /sys/kernel/debug/davinci_clocks > (debugfs). > > debugfs is more suited for this since the clock dump is > debug information. > > Signed-off-by: Sekhar Nori <[email protected]> > --- > arch/arm/mach-davinci/clock.c | 42 ++++++++++------------------------------ > 1 files changed, 11 insertions(+), 31 deletions(-) > > diff --git a/arch/arm/mach-davinci/clock.c b/arch/arm/mach-davinci/clock.c > index ba0b68f..c9ea503 100644 > --- a/arch/arm/mach-davinci/clock.c > +++ b/arch/arm/mach-davinci/clock.c > @@ -455,24 +455,10 @@ int __init davinci_clk_init(struct davinci_clk *clocks) > return 0; > } > > -#ifdef CONFIG_PROC_FS > -#include <linux/proc_fs.h> > -#include <linux/seq_file.h> > - > -static void *davinci_ck_start(struct seq_file *m, loff_t *pos) > -{ > - return *pos < 1 ? (void *)1 : NULL; > -} > - > -static void *davinci_ck_next(struct seq_file *m, void *v, loff_t *pos) > -{ > - ++*pos; > - return NULL; > -} > +#ifdef CONFIG_DEBUG_FS > > -static void davinci_ck_stop(struct seq_file *m, void *v) > -{ > -} > +#include <linux/debugfs.h> > +#include <linux/seq_file.h> > > #define CLKNAME_MAX 10 /* longest clock name */ > #define NEST_DELTA 2 > @@ -527,30 +513,24 @@ static int davinci_ck_show(struct seq_file *m, void *v) > return 0; > } > > -static const struct seq_operations davinci_ck_op = { > - .start = davinci_ck_start, > - .next = davinci_ck_next, > - .stop = davinci_ck_stop, > - .show = davinci_ck_show > -}; > - > static int davinci_ck_open(struct inode *inode, struct file *file) > { > - return seq_open(file, &davinci_ck_op); > + return single_open(file, davinci_ck_show, NULL); > } > > -static const struct file_operations proc_davinci_ck_operations = { > +static const struct file_operations davinci_ck_operations = { > .open = davinci_ck_open, > .read = seq_read, > .llseek = seq_lseek, > - .release = seq_release, > + .release = single_release, > }; > > -static int __init davinci_ck_proc_init(void) > +static int __init davinci_clk_debugfs_init(void) > { > - proc_create("davinci_clocks", 0, NULL, &proc_davinci_ck_operations); > + debugfs_create_file("davinci_clocks", S_IFREG | S_IRUGO, NULL, NULL, > + &davinci_ck_operations); > return 0; > > } > -__initcall(davinci_ck_proc_init); > -#endif /* CONFIG_DEBUG_PROC_FS */ > +device_initcall(davinci_clk_debugfs_init); > +#endif /* CONFIG_DEBUG_FS */ > -- > 1.6.2.4 > > _______________________________________________ > Davinci-linux-open-source mailing list > [email protected] > http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source Sekhar Nori <[email protected]> writes: > From: Nageswari Srinivasan <[email protected]> > > This patch adds support for TI's CDCE949 - a clock > synthesizer with 4 PLLs and 9 outputs. > > It is used on DM6467 EVM. On the EVM, it generates > clocks required for VPIF, TSIF and Audio modules. > > This patch adds it as part of the DaVinci clock framework. > > Testing: > The various frequency outputs on Y1 have been tested using > a out-of-tree VPIF video driver supporting HD video. > The register values for Y5 frequency outputs have been > derived from TSIF driver sources in MontaVista LSP kernel, > but actual output has not been tested for lack of TSIF > driver which actually works on the latest kernel. > > Signed-off-by: Nageswari Srinivasan <[email protected]> > Signed-off-by: Sekhar Nori <[email protected]> > --- > arch/arm/mach-davinci/cdce949.c | 289 > ++++++++++++++++++++++++++ > arch/arm/mach-davinci/include/mach/cdce949.h | 19 ++ > 2 files changed, 308 insertions(+), 0 deletions(-) > create mode 100644 arch/arm/mach-davinci/cdce949.c > create mode 100644 arch/arm/mach-davinci/include/mach/cdce949.h > > diff --git a/arch/arm/mach-davinci/cdce949.c b/arch/arm/mach-davinci/cdce949.c > new file mode 100644 > index 0000000..5a08283 > --- /dev/null > +++ b/arch/arm/mach-davinci/cdce949.c > @@ -0,0 +1,289 @@ > +/* > + * TI CDCE949 clock synthesizer driver > + * > + * Note: This implementation assumes an input of 27MHz to the CDCE. > + * This is by no means constrained by CDCE hardware although the datasheet > + * does use this as an example for all illustrations and more importantly: > + * that is the crystal input on boards it is currently used on. > + * > + * Copyright (C) 2009 Texas Instruments Incorporated. http://www.ti.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. > + * > + */ > +#include <linux/kernel.h> > +#include <linux/clk.h> > +#include <linux/platform_device.h> > +#include <linux/i2c.h> > + > +#include <mach/clock.h> > + > +#include "clock.h" > + > +static struct i2c_client *cdce_i2c_client; > + > +/* CDCE register descriptor */ > +struct cdce_reg { > + u8 addr; > + u8 val; > +}; > + > +/* Per-Output (Y1, Y2 etc.) frequency descriptor */ > +struct cdce_freq { > + /* Frequency in KHz */ > + unsigned long frequency; > + /* > + * List of registers to program to obtain a particular frequency. > + * 0x0 in register address and value is the end of list marker. > + */ > + struct cdce_reg *reglist; > +}; > + > +#define CDCE_FREQ_TABLE_ENTRY(line, out) \ > +{ \ > + .reglist = cdce_y ##line## _ ##out, \ > + .frequency = out, \ > +} > + > +/* List of CDCE outputs */ > +struct cdce_output { > + /* List of frequencies on this output */ > + struct cdce_freq *freq_table; > + /* Number of possible frequencies */ > + int size; > +}; > + > +/* > + * Finding out the values to program into CDCE949 registers for a particular > + * frequency output is not a simple calculation. Have a look at the datasheet > + * for the details. There is desktop software available to help users with > + * the calculations. Here, we just depend on the output of that software > + * (or hand calculations) instead trying to runtime calculate the register > + * values and inflicting misery on ourselves. > + */ > +static struct cdce_reg cdce_y1_148500[] = { > + { 0x13, 0x00 }, > + /* program PLL1_0 multiplier */ > + { 0x18, 0xaf }, > + { 0x19, 0x50 }, > + { 0x1a, 0x02 }, > + { 0x1b, 0xc9 }, > + /* program PLL1_11 multiplier */ > + { 0x1c, 0x00 }, > + { 0x1d, 0x40 }, > + { 0x1e, 0x02 }, > + { 0x1f, 0xc9 }, > + /* output state selection */ > + { 0x15, 0x00 }, > + { 0x14, 0xef }, > + /* switch MUX to PLL1 output */ > + { 0x14, 0x6f }, > + { 0x16, 0x06 }, > + /* set P2DIV divider, P3DIV and input crystal */ > + { 0x17, 0x06 }, > + { 0x01, 0x00 }, > + { 0x05, 0x48 }, > + { 0x02, 0x80 }, > + /* enable and disable PLL */ > + { 0x02, 0xbc }, > + { 0x03, 0x01 }, > + { }, > +}; > + > +static struct cdce_reg cdce_y1_74250[] = { > + { 0x13, 0x00 }, > + { 0x18, 0xaf }, > + { 0x19, 0x50 }, > + { 0x1a, 0x02 }, > + { 0x1b, 0xc9 }, > + { 0x1c, 0x00 }, > + { 0x1d, 0x40 }, > + { 0x1e, 0x02 }, > + { 0x1f, 0xc9 }, > + /* output state selection */ > + { 0x15, 0x00 }, > + { 0x14, 0xef }, > + /* switch MUX to PLL1 output */ > + { 0x14, 0x6f }, > + { 0x16, 0x06 }, > + /* set P2DIV divider, P3DIV and input crystal */ > + { 0x17, 0x06 }, > + { 0x01, 0x00 }, > + { 0x05, 0x48 }, > + { 0x02, 0x80 }, > + /* enable and disable PLL */ > + { 0x02, 0xbc }, > + { 0x03, 0x02 }, > + { }, > +}; > + > +static struct cdce_reg cdce_y1_27000[] = { > + { 0x13, 0x00 }, > + { 0x18, 0x00 }, > + { 0x19, 0x40 }, > + { 0x1a, 0x02 }, > + { 0x1b, 0x08 }, > + { 0x1c, 0x00 }, > + { 0x1d, 0x40 }, > + { 0x1e, 0x02 }, > + { 0x1f, 0x08 }, > + { 0x15, 0x02 }, > + { 0x14, 0xed }, > + { 0x16, 0x01 }, > + { 0x17, 0x01 }, > + { 0x01, 0x00 }, > + { 0x05, 0x50 }, > + { 0x02, 0xb4 }, > + { 0x03, 0x01 }, > + { }, > +}; > + > +static struct cdce_freq cdce_y1_freqs[] = { > + CDCE_FREQ_TABLE_ENTRY(1, 148500), > + CDCE_FREQ_TABLE_ENTRY(1, 74250), > + CDCE_FREQ_TABLE_ENTRY(1, 27000), > +}; > + > +static struct cdce_reg cdce_y5_13500[] = { > + { 0x27, 0x08 }, > + { 0x28, 0x00 }, > + { 0x29, 0x40 }, > + { 0x2a, 0x02 }, > + { 0x2b, 0x08 }, > + { 0x24, 0x6f }, > + { }, > +}; > + > +static struct cdce_reg cdce_y5_16875[] = { > + { 0x27, 0x08 }, > + { 0x28, 0x9f }, > + { 0x29, 0xb0 }, > + { 0x2a, 0x02 }, > + { 0x2b, 0x89 }, > + { 0x24, 0x6f }, > + { }, > +}; > + > +static struct cdce_reg cdce_y5_27000[] = { > + { 0x27, 0x04 }, > + { 0x28, 0x00 }, > + { 0x29, 0x40 }, > + { 0x2a, 0x02 }, > + { 0x2b, 0x08 }, > + { 0x24, 0x6f }, > + { }, > +}; > +static struct cdce_reg cdce_y5_54000[] = { > + { 0x27, 0x04 }, > + { 0x28, 0xff }, > + { 0x29, 0x80 }, > + { 0x2a, 0x02 }, > + { 0x2b, 0x07 }, > + { 0x24, 0x6f }, > + { }, > +}; > + > +static struct cdce_reg cdce_y5_81000[] = { > + { 0x27, 0x02 }, > + { 0x28, 0xbf }, > + { 0x29, 0xa0 }, > + { 0x2a, 0x03 }, > + { 0x2b, 0x0a }, > + { 0x24, 0x6f }, > + { }, > +}; > + > +static struct cdce_freq cdce_y5_freqs[] = { > + CDCE_FREQ_TABLE_ENTRY(5, 13500), > + CDCE_FREQ_TABLE_ENTRY(5, 16875), > + CDCE_FREQ_TABLE_ENTRY(5, 27000), > + CDCE_FREQ_TABLE_ENTRY(5, 54000), > + CDCE_FREQ_TABLE_ENTRY(5, 81000), > +}; > + > + > +static struct cdce_output output_list[] = { > + [1] = { cdce_y1_freqs, ARRAY_SIZE(cdce_y1_freqs) }, > + [5] = { cdce_y5_freqs, ARRAY_SIZE(cdce_y5_freqs) }, > +}; > + > +int cdce_set_rate(struct clk *clk, unsigned long rate) > +{ > + int i, ret = 0; > + struct cdce_freq *freq_table = output_list[clk->lpsc].freq_table; > + struct cdce_reg *regs = NULL; > + > + if (!cdce_i2c_client) > + return -ENODEV; > + > + if (!freq_table) > + return -EINVAL; > + > + for (i = 0; i < output_list[clk->lpsc].size; i++) { > + if (freq_table[i].frequency == rate / 1000) { > + regs = freq_table[i].reglist; > + break; > + } > + } > + > + if (!regs) > + return -EINVAL; > + > + for (i = 0; regs[i].addr; i++) { > + ret = i2c_smbus_write_byte_data(cdce_i2c_client, > + regs[i].addr | 0x80, regs[i].val); > + if (ret) > + return ret; > + } > + > + atomic_set(&clk->rate, rate); > + > + return 0; > +} > + > +static int cdce_probe(struct i2c_client *client, > + const struct i2c_device_id *id) > +{ > + cdce_i2c_client = client; > + return 0; > +} > + > +static int __devexit cdce_remove(struct i2c_client *client) > +{ > + cdce_i2c_client = NULL; > + return 0; > +} > + > +static const struct i2c_device_id cdce_id[] = { > + {"cdce949", 0}, > + {}, > +}; > +MODULE_DEVICE_TABLE(i2c, cdce_id); > + > +static struct i2c_driver cdce_driver = { > + .driver = { > + .owner = THIS_MODULE, > + .name = "cdce949", > + }, > + .probe = cdce_probe, > + .remove = __devexit_p(cdce_remove), > + .id_table = cdce_id, > +}; > + > +static int __init cdce_init(void) > +{ > + return i2c_add_driver(&cdce_driver); > +} > +subsys_initcall(cdce_init); > + > +static void __exit cdce_exit(void) > +{ > + i2c_del_driver(&cdce_driver); > +} > +module_exit(cdce_exit); > + > +MODULE_AUTHOR("Texas Instruments"); > +MODULE_DESCRIPTION("CDCE949 clock synthesizer driver"); > +MODULE_LICENSE("GPL v2"); > diff --git a/arch/arm/mach-davinci/include/mach/cdce949.h > b/arch/arm/mach-davinci/include/mach/cdce949.h > new file mode 100644 > index 0000000..c73331f > --- /dev/null > +++ b/arch/arm/mach-davinci/include/mach/cdce949.h > @@ -0,0 +1,19 @@ > +/* > + * TI CDCE949 off-chip clock synthesizer support > + * > + * 2009 (C) Texas Instruments, Inc. http://www.ti.com/ > + * > + * This file is licensed under the terms of the GNU General Public License > + * version 2. This program is licensed "as is" without any warranty of any > + * kind, whether express or implied. > + */ > +#ifndef _MACH_DAVINCI_CDCE949_H > +#define _MACH_DAVINCI_CDCE949_H > + > +#include <linux/clk.h> > + > +#include <mach/clock.h> > + > +int cdce_set_rate(struct clk *clk, unsigned long rate); > + > +#endif > -- > 1.6.2.4 > > _______________________________________________ > Davinci-linux-open-source mailing list > [email protected] > http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source Sekhar Nori <[email protected]> writes: > From: Nageswari Srinivasan <[email protected]> > > This patch adds the CDCE949 reference oscillator to > the davinci clock list. > > On the DM6467T EVM, the CDCE949 is responsible for > generating the pixel clock for display. On the DM6467 > EVM, this pixel clock was being obtained from an > internal source. This is not possible on the DM6467T > EVM because of the presence of a 33MHz oscillator. > > The TSIF module also requires the CDCE949 to generate > the data clocks. > > The actual clock definitions will be added by patches > adding support for DM6467T VPIF and TSIF. This patch > mearly lays the foundation for that work. > > Signed-off-by: Nageswari Srinivasan <[email protected]> > Signed-off-by: Sekhar Nori <[email protected]> > --- > arch/arm/mach-davinci/Makefile | 2 +- > arch/arm/mach-davinci/board-dm646x-evm.c | 32 > ++++++++++++++++++++++++++++++ > 2 files changed, 33 insertions(+), 1 deletions(-) > > diff --git a/arch/arm/mach-davinci/Makefile b/arch/arm/mach-davinci/Makefile > index eeb9230..7e806b0 100644 > --- a/arch/arm/mach-davinci/Makefile > +++ b/arch/arm/mach-davinci/Makefile > @@ -26,7 +26,7 @@ obj-$(CONFIG_MACH_SFFSDR) += board-sffsdr.o > obj-$(CONFIG_MACH_NEUROS_OSD2) += board-neuros-osd2.o > obj-$(CONFIG_MACH_DAVINCI_DM355_EVM) += board-dm355-evm.o > obj-$(CONFIG_MACH_DM355_LEOPARD) += board-dm355-leopard.o > -obj-$(CONFIG_MACH_DAVINCI_DM6467_EVM) += board-dm646x-evm.o > +obj-$(CONFIG_MACH_DAVINCI_DM6467_EVM) += board-dm646x-evm.o cdce949.o > obj-$(CONFIG_MACH_DAVINCI_DM365_EVM) += board-dm365-evm.o > obj-$(CONFIG_MACH_DAVINCI_DA830_EVM) += board-da830-evm.o > obj-$(CONFIG_MACH_DAVINCI_DA850_EVM) += board-da850-evm.o > diff --git a/arch/arm/mach-davinci/board-dm646x-evm.c > b/arch/arm/mach-davinci/board-dm646x-evm.c > index 6ff3411..5d9283f 100644 > --- a/arch/arm/mach-davinci/board-dm646x-evm.c > +++ b/arch/arm/mach-davinci/board-dm646x-evm.c > @@ -40,6 +40,8 @@ > #include <mach/serial.h> > #include <mach/i2c.h> > #include <mach/nand.h> > +#include <mach/clock.h> > +#include <mach/cdce949.h> > > #include "clock.h" > > @@ -389,6 +391,9 @@ static struct i2c_board_info __initdata i2c_info[] = { > { > I2C_BOARD_INFO("cpld_video", 0x3b), > }, > + { > + I2C_BOARD_INFO("cdce949", 0x6c), > + }, > }; > > static struct davinci_i2c_platform_data i2c_pdata = { > @@ -681,9 +686,36 @@ static void __init evm_init_i2c(void) > evm_init_video(); > } > > +#define CDCE949_XIN_RATE 27000000 > + > +/* CDCE949 support - "lpsc" field is overridden to work as clock number */ > +static struct clk cdce_clk_in = { > + .name = "cdce_xin", > + .flags = ALWAYS_ENABLED, Why do you need ALWAYS_ENABLED here. I'd rather see clk_get()/clk_enable() used when this clock is required. To do this, we'll need to add enable/disable functionaltiy for non-PSC clocks. Probably the best way to do this is to add add an 'enable' hook to 'struct clk' and clk_enable() can use that. Kevin _______________________________________________ Davinci-linux-open-source mailing list [email protected] http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source
