Skip to content

Commit

Permalink
clk: ti: Implement FAPLL set_rate for the synthesizer
Browse files Browse the repository at this point in the history
We can pretty much get any rate out of the FAPLL because of the fractional
divider. Let's first try just adjusting the post divider, and if that is
not enough, then reprogram both the fractional divider and the post divider.

Let's also add a define for the fixed SYNTH_PHASE_K instead of using 8.

Cc: Brian Hutchinson <[email protected]>
Cc: Matthijs van Duin <[email protected]>
Cc: Tero Kristo <[email protected]>
Signed-off-by: Tony Lindgren <[email protected]>
Signed-off-by: Tero Kristo <[email protected]>
  • Loading branch information
tmlind authored and Tero Kristo committed Mar 24, 2015
1 parent 33ca29c commit cafeb00
Showing 1 changed file with 132 additions and 2 deletions.
134 changes: 132 additions & 2 deletions drivers/clk/ti/fapll.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/math64.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/clk/ti.h>
Expand Down Expand Up @@ -47,6 +48,8 @@
/* Synthesizer frequency register */
#define SYNTH_LDFREQ BIT(31)

#define SYNTH_PHASE_K 8
#define SYNTH_MAX_INT_DIV 0xf
#define SYNTH_MAX_DIV_M 0xff

struct fapll_data {
Expand Down Expand Up @@ -204,7 +207,7 @@ static unsigned long ti_fapll_synth_recalc_rate(struct clk_hw *hw,
/*
* Synth frequency integer and fractional divider.
* Note that the phase output K is 8, so the result needs
* to be multiplied by 8.
* to be multiplied by SYNTH_PHASE_K.
*/
if (synth->freq) {
u32 v, synth_int_div, synth_frac_div, synth_div_freq;
Expand All @@ -215,7 +218,7 @@ static unsigned long ti_fapll_synth_recalc_rate(struct clk_hw *hw,
synth_div_freq = (synth_int_div * 10000000) + synth_frac_div;
rate *= 10000000;
do_div(rate, synth_div_freq);
rate *= 8;
rate *= SYNTH_PHASE_K;
}

/* Synth post-divider M */
Expand All @@ -224,11 +227,138 @@ static unsigned long ti_fapll_synth_recalc_rate(struct clk_hw *hw,
return DIV_ROUND_UP_ULL(rate, synth_div_m);
}

static unsigned long ti_fapll_synth_get_frac_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct fapll_synth *synth = to_synth(hw);
unsigned long current_rate, frac_rate;
u32 post_div_m;

current_rate = ti_fapll_synth_recalc_rate(hw, parent_rate);
post_div_m = readl_relaxed(synth->div) & SYNTH_MAX_DIV_M;
frac_rate = current_rate * post_div_m;

return frac_rate;
}

static u32 ti_fapll_synth_set_frac_rate(struct fapll_synth *synth,
unsigned long rate,
unsigned long parent_rate)
{
u32 post_div_m, synth_int_div = 0, synth_frac_div = 0, v;

post_div_m = DIV_ROUND_UP_ULL((u64)parent_rate * SYNTH_PHASE_K, rate);
post_div_m = post_div_m / SYNTH_MAX_INT_DIV;
if (post_div_m > SYNTH_MAX_DIV_M)
return -EINVAL;
if (!post_div_m)
post_div_m = 1;

for (; post_div_m < SYNTH_MAX_DIV_M; post_div_m++) {
synth_int_div = DIV_ROUND_UP_ULL((u64)parent_rate *
SYNTH_PHASE_K *
10000000,
rate * post_div_m);
synth_frac_div = synth_int_div % 10000000;
synth_int_div /= 10000000;

if (synth_int_div <= SYNTH_MAX_INT_DIV)
break;
}

if (synth_int_div > SYNTH_MAX_INT_DIV)
return -EINVAL;

v = readl_relaxed(synth->freq);
v &= ~0x1fffffff;
v |= (synth_int_div & SYNTH_MAX_INT_DIV) << 24;
v |= (synth_frac_div & 0xffffff);
v |= SYNTH_LDFREQ;
writel_relaxed(v, synth->freq);

return post_div_m;
}

static long ti_fapll_synth_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
struct fapll_synth *synth = to_synth(hw);
struct fapll_data *fd = synth->fd;
unsigned long r;

if (ti_fapll_clock_is_bypass(fd) || !synth->div || !rate)
return -EINVAL;

/* Only post divider m available with no fractional divider? */
if (!synth->freq) {
unsigned long frac_rate;
u32 synth_post_div_m;

frac_rate = ti_fapll_synth_get_frac_rate(hw, *parent_rate);
synth_post_div_m = DIV_ROUND_UP(frac_rate, rate);
r = DIV_ROUND_UP(frac_rate, synth_post_div_m);
goto out;
}

r = *parent_rate * SYNTH_PHASE_K;
if (rate > r)
goto out;

r = DIV_ROUND_UP_ULL(r, SYNTH_MAX_INT_DIV * SYNTH_MAX_DIV_M);
if (rate < r)
goto out;

r = rate;
out:
return r;
}

static int ti_fapll_synth_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct fapll_synth *synth = to_synth(hw);
struct fapll_data *fd = synth->fd;
unsigned long frac_rate, post_rate = 0;
u32 post_div_m = 0, v;

if (ti_fapll_clock_is_bypass(fd) || !synth->div || !rate)
return -EINVAL;

/* Produce the rate with just post divider M? */
frac_rate = ti_fapll_synth_get_frac_rate(hw, parent_rate);
if (frac_rate < rate) {
if (!synth->freq)
return -EINVAL;
} else {
post_div_m = DIV_ROUND_UP(frac_rate, rate);
if (post_div_m && (post_div_m <= SYNTH_MAX_DIV_M))
post_rate = DIV_ROUND_UP(frac_rate, post_div_m);
if (!synth->freq && !post_rate)
return -EINVAL;
}

/* Need to recalculate the fractional divider? */
if ((post_rate != rate) && synth->freq)
post_div_m = ti_fapll_synth_set_frac_rate(synth,
rate,
parent_rate);

v = readl_relaxed(synth->div);
v &= ~SYNTH_MAX_DIV_M;
v |= post_div_m;
v |= SYNTH_LDMDIV1;
writel_relaxed(v, synth->div);

return 0;
}

static struct clk_ops ti_fapll_synt_ops = {
.enable = ti_fapll_synth_enable,
.disable = ti_fapll_synth_disable,
.is_enabled = ti_fapll_synth_is_enabled,
.recalc_rate = ti_fapll_synth_recalc_rate,
.round_rate = ti_fapll_synth_round_rate,
.set_rate = ti_fapll_synth_set_rate,
};

static struct clk * __init ti_fapll_synth_setup(struct fapll_data *fd,
Expand Down

0 comments on commit cafeb00

Please sign in to comment.