clk: at91: better support for the PCKs
Subsystems (2)
v3 cover clk: at91: better support for the PCKs
From: Boris BREZILLON <[email protected]>
This serie implements a better support for the Programmable Clocks. The first two patch are related to changing the rate of the PCKs. The 3rd patch is a fix to handle properly the PCKRDY interrupt. The last patch is a small optimzation/simplification of the determine_rate algorithm for the PCK.
Changes since v2:
- remove unnecessary CLK_SET_RATE_GATE flag from system clk setting
Boris BREZILLON (2):
clk: at91: replace prog clk round_rate with determine_rate
clk: at91: propagate rate change on system clks
Jean-Jacques Hiblot (2):
clk: at91: fix programmable clk irq handling
clk: at91: optimization of the set_rate callback
drivers/clk/at91/clk-programmable.c | 202 +++++++++++------------------------ drivers/clk/at91/clk-system.c | 76 +++++++++++-- 2 files changed, 126 insertions(+), 152 deletions(-)
Signature · 1 line
1.7.9.5
PATCH v3 1/4 clk: at91: replace prog clk round_rate with determine_rate
From: Boris BREZILLON <[email protected]>
Implement the determine_rate callback to choose the best parent clk that fulfills the requested rate.
--- drivers/clk/at91/clk-programmable.c | 56 +++++++++++++++++------------------ 1 file changed, 28 insertions(+), 28 deletions(-)
drivers/clk/at91/clk-programmable.c +28 -28
@@ -102,40 +102,40 @@ static unsigned long clk_programmable_recalc_rate(struct clk_hw *hw,return parent_rate >> prog->pres; } -static long clk_programmable_round_rate (struct clk_hw *hw,unsigned long rate, -unsigned long *parent_rate) +static long clk_programmable_determine_rate (struct clk_hw *hw, +unsigned long rate, +unsigned long *best_parent_rate, +struct clk **best_parent_clk) { -unsigned long best_rate = *parent_rate; -unsigned long best_diff; -unsigned long new_diff; -unsigned long cur_rate; -int shift = shift; - -if (rate > *parent_rate) -return *parent_rate; -else - best_diff = *parent_rate - rate; - -if (!best_diff) -return best_rate; +struct clk *parent =NULL ; +long best_rate = -EINVAL ; +unsigned long parent_rate; +unsigned long tmp_rate; +int shift; +int i; -for (shift =1 ; shift <PROG_PRES_MASK ; shift++) { - cur_rate = *parent_rate >> shift; +for (i =0 ; i <__clk_get_num_parents (hw->clk); i++) { + parent =clk_get_parent_by_index (hw->clk, i); +if (!parent) +continue ; -if (cur_rate > rate) - new_diff = cur_rate - rate; -else - new_diff = rate - cur_rate; + parent_rate =__clk_get_rate (parent); +for (shift =0 ; shift <PROG_PRES_MASK ; shift++) { + tmp_rate = parent_rate >> shift; +if (tmp_rate <= rate) +break ; + } -if (!new_diff) -return cur_rate; +if (tmp_rate > rate) +continue ; -if (new_diff < best_diff) { - best_diff = new_diff; - best_rate = cur_rate; +if (best_rate <0 || (rate - tmp_rate) < (rate - best_rate)) { + best_rate = tmp_rate; + *best_parent_rate = parent_rate; + *best_parent_clk = parent; } -if (rate > cur_rate) +if (!best_rate)break ; }
@@ -228,7 +228,7 @@ static const struct clk_ops programmable_ops = { .prepare = clk_programmable_prepare, .is_prepared = clk_programmable_is_ready, .recalc_rate = clk_programmable_recalc_rate, - .round_rate = clk_programmable_round_rate, + .determine_rate = clk_programmable_determine_rate, .get_parent = clk_programmable_get_parent, .set_parent = clk_programmable_set_parent, .set_rate = clk_programmable_set_rate,
Signature · 1 line
1.7.9.5
PATCH v3 2/4 clk: at91: propagate rate change on system clks
In reply to Boris BREZILLONFrom: Boris BREZILLON <[email protected]>
System clks are just gates, and thus do not provide any rate operations. Authorize clk rate change to be propagated to system clk parents.
--- drivers/clk/at91/clk-system.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/clk/at91/clk-system.c +1 -1
@@ -84,7 +84,7 @@ at91_clk_register_system(struct at91_pmc *pmc, const char *name,* (see drivers/memory) which would request and enable the ddrck clock. * When this is done we will be able to remove CLK_IGNORE_UNUSED flag. */ - init.flags =CLK_IGNORE_UNUSED ; + init.flags =CLK_SET_RATE_PARENT |CLK_IGNORE_UNUSED ; sys->id = id; sys->hw.init = &init;
Signature · 1 line
1.7.9.5
PATCH v3 3/4 clk: at91: fix programmable clk irq handling
In reply to Boris BREZILLONFrom: Jean-Jacques Hiblot <[email protected]>
The PCKRDY bit is not set until the system clock is enabled. This patch moves the management of the ready status in the system clock driver.
--- drivers/clk/at91/clk-programmable.c | 108 ++++++++--------------------------- drivers/clk/at91/clk-system.c | 74 ++++++++++++++++++++---- 2 files changed, 89 insertions(+), 93 deletions(-)
drivers/clk/at91/clk-programmable.c +25 -83
@@ -13,12 +13,9 @@#include <linux/clk/at91_pmc.h> #include <linux/of.h> #include <linux/of_address.h> -#include <linux/of_irq.h> #include <linux/io.h> #include <linux/wait.h> #include <linux/sched.h> -#include <linux/interrupt.h> -#include <linux/irq.h> #include "pmc.h"
@@ -38,68 +35,23 @@ struct clk_programmable_layout {struct clk_programmable {struct clk_hw hw;struct at91_pmc *pmc; -unsigned int irq; -wait_queue_head_t wait;u8 id; -u8 css; -u8 pres; -u8 slckmck;const struct clk_programmable_layout *layout; };#define to_clk_programmable (hw) container_of(hw, struct clk_programmable, hw) - -static irqreturn_t clk_programmable_irq_handler (int irq,void *dev_id) -{ -struct clk_programmable *prog = (struct clk_programmable *)dev_id; - -wake_up (&prog->wait); - -return IRQ_HANDLED ; -} - -static int clk_programmable_prepare (struct clk_hw *hw) -{ -u32 tmp; -struct clk_programmable *prog =to_clk_programmable (hw); -struct at91_pmc *pmc = prog->pmc; -const struct clk_programmable_layout *layout = prog->layout; -u8 id = prog->id; -u32 mask =PROG_STATUS_MASK (id); - - tmp = prog->css | (prog->pres << layout->pres_shift); -if (layout->have_slck_mck && prog->slckmck) - tmp |=AT91_PMC_CSSMCK_MCK ; - -pmc_write (pmc,AT91_PMC_PCKR (id), tmp); - -while (!(pmc_read (pmc,AT91_PMC_SR ) & mask)) -wait_event (prog->wait,pmc_read (pmc,AT91_PMC_SR ) & mask); - -return 0 ; -} - -static int clk_programmable_is_ready (struct clk_hw *hw) -{ -struct clk_programmable *prog =to_clk_programmable (hw); -struct at91_pmc *pmc = prog->pmc; - -return !!(pmc_read (pmc,AT91_PMC_SR ) &AT91_PMC_PCKR (prog->id)); -} -static unsigned long clk_programmable_recalc_rate (struct clk_hw *hw,unsigned long parent_rate) { -u32 tmp; +u32 pres;struct clk_programmable *prog =to_clk_programmable (hw);struct at91_pmc *pmc = prog->pmc;const struct clk_programmable_layout *layout = prog->layout; - tmp =pmc_read (pmc,AT91_PMC_PCKR (prog->id)); - prog->pres = (tmp >> layout->pres_shift) &PROG_PRES_MASK ; - -return parent_rate >> prog->pres; + pres = (pmc_read (pmc,AT91_PMC_PCKR (prog->id)) >> layout->pres_shift) & +PROG_PRES_MASK ; +return parent_rate >> pres; } static long clk_programmable_determine_rate(struct clk_hw *hw,
@@ -146,17 +98,22 @@ static int clk_programmable_set_parent(struct clk_hw *hw, u8 index) {struct clk_programmable *prog =to_clk_programmable (hw);const struct clk_programmable_layout *layout = prog->layout; +struct at91_pmc *pmc = prog->pmc; +u32 tmp =pmc_read (pmc,AT91_PMC_PCKR (prog->id)) & ~layout->css_mask; + +if (layout->have_slck_mck) + tmp &=AT91_PMC_CSSMCK_MCK ; +if (index > layout->css_mask) {if (index >PROG_MAX_RM9200_CSS && layout->have_slck_mck) { - prog->css =0 ; - prog->slckmck =1 ; + tmp |=AT91_PMC_CSSMCK_MCK ;return 0 ; }else {return -EINVAL ; } } - prog->css = index; +pmc_write (pmc,AT91_PMC_PCKR (prog->id), tmp | index);return 0 ; }
@@ -169,13 +126,9 @@ static u8 clk_programmable_get_parent(struct clk_hw *hw)const struct clk_programmable_layout *layout = prog->layout; tmp =pmc_read (pmc,AT91_PMC_PCKR (prog->id)); - prog->css = tmp & layout->css_mask; - ret = prog->css; -if (layout->have_slck_mck) { - prog->slckmck = !!(tmp &AT91_PMC_CSSMCK_MCK ); -if (prog->slckmck && !ret) - ret =PROG_MAX_RM9200_CSS +1 ; - } + ret = tmp & layout->css_mask; +if (layout->have_slck_mck && (tmp &AT91_PMC_CSSMCK_MCK ) && !ret) + ret =PROG_MAX_RM9200_CSS +1 ;return ret; }
@@ -184,11 +137,15 @@ static int clk_programmable_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) {struct clk_programmable *prog =to_clk_programmable (hw); +struct at91_pmc *pmc = prog->pmc; +const struct clk_programmable_layout *layout = prog->layout;unsigned long best_rate = parent_rate;unsigned long best_diff;unsigned long new_diff;unsigned long cur_rate;int shift =0 ; +u32 tmp =pmc_read (pmc,AT91_PMC_PCKR (prog->id)) & + ~(PROG_PRES_MASK << layout->pres_shift);if (rate > parent_rate)return parent_rate;
@@ -196,7 +153,7 @@ static int clk_programmable_set_rate(struct clk_hw *hw, unsigned long rate, best_diff = parent_rate - rate;if (!best_diff) { - prog->pres = shift; +pmc_write (pmc,AT91_PMC_PCKR (prog->id), tmp | shift);return 0 ; }
@@ -220,13 +177,13 @@ static int clk_programmable_set_rate(struct clk_hw *hw, unsigned long rate,break ; } - prog->pres = shift; +pmc_write (pmc,AT91_PMC_PCKR (prog->id), + tmp | (shift << layout->pres_shift)); +return 0 ; }static const struct clk_ops programmable_ops = { - .prepare = clk_programmable_prepare, - .is_prepared = clk_programmable_is_ready, .recalc_rate = clk_programmable_recalc_rate, .determine_rate = clk_programmable_determine_rate, .get_parent = clk_programmable_get_parent,
@@ -235,16 +192,14 @@ static const struct clk_ops programmable_ops = { };static struct clk *__init -at91_clk_register_programmable (struct at91_pmc *pmc,unsigned int irq, +at91_clk_register_programmable (struct at91_pmc *pmc,const char *name,const char **parent_names,u8 num_parents,u8 id,const struct clk_programmable_layout *layout) { -int ret;struct clk_programmable *prog;struct clk *clk =NULL ;struct clk_init_data init; -char irq_name[11 ];if (id >PROG_ID_MAX )return ERR_PTR (-EINVAL );
@@ -263,14 +218,6 @@ at91_clk_register_programmable(struct at91_pmc *pmc, unsigned int irq, prog->layout = layout; prog->hw.init = &init; prog->pmc = pmc; - prog->irq = irq; -init_waitqueue_head (&prog->wait); -irq_set_status_flags (prog->irq,IRQ_NOAUTOEN ); -snprintf (irq_name,sizeof (irq_name),"clk-prog%d" , id); - ret =request_irq (prog->irq, clk_programmable_irq_handler, -IRQF_TRIGGER_HIGH , irq_name, prog); -if (ret) -return ERR_PTR (ret); clk =clk_register (NULL , &prog->hw);if (IS_ERR (clk))
@@ -304,7 +251,6 @@ of_at91_clk_prog_setup(struct device_node *np, struct at91_pmc *pmc,int num;u32 id;int i; -unsigned int irq;struct clk *clk;int num_parents;const char *parent_names[PROG_SOURCE_MAX ];
@@ -332,11 +278,7 @@ of_at91_clk_prog_setup(struct device_node *np, struct at91_pmc *pmc,if (of_property_read_string (np,"clock-output-names" , &name)) name = progclknp->name; - irq =irq_of_parse_and_map (progclknp,0 ); -if (!irq) -continue ; - - clk =at91_clk_register_programmable (pmc, irq, name, + clk =at91_clk_register_programmable (pmc, name, parent_names, num_parents, id, layout);if (IS_ERR (clk))
drivers/clk/at91/clk-system.c +64 -10
137 lines not on this page · raw message
Signature · 1 line
1.7.9.5
PATCH v3 4/4 clk: at91: optimization of the set_rate callback
In reply to Boris BREZILLONFrom: Jean-Jacques Hiblot <[email protected]>
--- drivers/clk/at91/clk-programmable.c | 38 ++++++++--------------------------- 1 file changed, 8 insertions(+), 30 deletions(-)
drivers/clk/at91/clk-programmable.c +8 -30
@@ -139,43 +139,21 @@ static int clk_programmable_set_rate(struct clk_hw *hw, unsigned long rate,struct clk_programmable *prog =to_clk_programmable (hw);struct at91_pmc *pmc = prog->pmc;const struct clk_programmable_layout *layout = prog->layout; -unsigned long best_rate = parent_rate; -unsigned long best_diff; -unsigned long new_diff; -unsigned long cur_rate; +unsigned long div = parent_rate / rate;int shift =0 ;u32 tmp =pmc_read (pmc,AT91_PMC_PCKR (prog->id)) & ~(PROG_PRES_MASK << layout->pres_shift); -if (rate > parent_rate) -return parent_rate; -else - best_diff = parent_rate - rate; +if (!div) +return -EINVAL ; -if (!best_diff) { -pmc_write (pmc,AT91_PMC_PCKR (prog->id), tmp | shift); -return 0 ; - } - -for (shift =1 ; shift <PROG_PRES_MASK ; shift++) { - cur_rate = parent_rate >> shift; - -if (cur_rate > rate) - new_diff = cur_rate - rate; -else - new_diff = rate - cur_rate; - -if (!new_diff) -break ; + shift =fls (div) -1 ; -if (new_diff < best_diff) { - best_diff = new_diff; - best_rate = cur_rate; - } +if (div != (1 <<shift)) +return -EINVAL ; -if (rate > cur_rate) -break ; - } +if (shift >=PROG_PRES_MASK ) +return -EINVAL ;pmc_write (pmc,AT91_PMC_PCKR (prog->id), tmp | (shift << layout->pres_shift));
Signature · 1 line
1.7.9.5
v3 cover clk: at91: better support for the PCKs
In reply to Boris BREZILLONQuoting Boris BREZILLON (2014-03-11 02:00:31)
Quoted 7 lines
From: Boris BREZILLON <[email protected]>
This serie implements a better support for the Programmable Clocks. The first two patch are related to changing the rate of the PCKs. The 3rd patch is a fix to handle properly the PCKRDY interrupt. The last patch is a small optimzation/simplification of the
determine_ratealgorithm for the PCK.
Taken into clk-next.
Regards,
Mike
Quoted 19 lines
Changes since v2:
- remove unnecessaryCLK_SET_RATE_GATEflag from system clk settingBoris BREZILLON (2):
clk: at91: replace prog clkround_ratewithdetermine_rate
clk: at91: propagate rate change on system clksJean-Jacques Hiblot (2):
clk: at91: fix programmable clk irq handling
clk: at91: optimization of theset_ratecallbackdrivers/clk/at91/clk-programmable.c | 202 +++++++++++------------------------ drivers/clk/at91/clk-system.c | 76 +++++++++++-- 2 files changed, 126 insertions(+), 152 deletions(-)Signature · 1 line
1.7.9.5