clk_div: Add a div lookup table

Some clocks on SoC have a diff between the value written in the register
and the real divider.
Add a table that where we can lookup the real value of the divider.

Reviewed by:	mmel (earlier revision)
Differential Revision:	https://reviews.freebsd.org/D8728
This commit is contained in:
Emmanuel Vadot 2016-12-12 16:43:31 +00:00
parent 5afb134c32
commit bbd72acb3b
2 changed files with 49 additions and 0 deletions

View File

@ -68,6 +68,8 @@ struct clknode_div_sc {
uint32_t f_width;
int div_flags;
uint32_t divider; /* in natural form */
struct clk_div_table *div_table;
};
static clknode_method_t clknode_div_methods[] = {
@ -80,6 +82,36 @@ static clknode_method_t clknode_div_methods[] = {
DEFINE_CLASS_1(clknode_div, clknode_div_class, clknode_div_methods,
sizeof(struct clknode_div_sc), clknode_class);
static uint32_t
clknode_div_table_get_divider(struct clknode_div_sc *sc, uint32_t divider)
{
struct clk_div_table *table;
if (!(sc->div_flags & CLK_DIV_WITH_TABLE))
return (divider);
for (table = sc->div_table; table->divider != 0; table++)
if (table->value == sc->divider)
return (table->divider);
return (0);
}
static uint32_t
clknode_div_table_get_value(struct clknode_div_sc *sc, uint32_t divider)
{
struct clk_div_table *table;
if (!(sc->div_flags & CLK_DIV_WITH_TABLE))
return (divider);
for (table = sc->div_table; table->divider != 0; table++)
if (table->divider == sc->divider)
return (table->value);
return (0);
}
static int
clknode_div_init(struct clknode *clk, device_t dev)
{
@ -101,6 +133,11 @@ clknode_div_init(struct clknode *clk, device_t dev)
i_div++;
f_div = (reg >> sc->f_shift) & sc->f_mask;
sc->divider = i_div << sc->f_width | f_div;
sc->divider = clknode_div_table_get_divider(sc, sc->divider);
if (sc->divider == 0)
panic("%s: divider is zero!\n", clknode_get_name(clk));
clknode_init_parent_idx(clk, 0);
return(0);
}
@ -177,6 +214,10 @@ clknode_div_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
(*fout != (_fin / divider)))
return (ERANGE);
divider = clknode_div_table_get_value(sc, divider);
if (divider == 0)
return (ERANGE);
DEVICE_LOCK(clk);
rv = MD4(clk, sc->offset,
(sc->i_mask << sc->i_shift) | (sc->f_mask << sc->f_shift),
@ -214,6 +255,7 @@ clknode_div_register(struct clkdom *clkdom, struct clk_div_def *clkdef)
sc->f_width = clkdef->f_width;
sc->f_mask = (1 << clkdef->f_width) - 1;
sc->div_flags = clkdef->div_flags;
sc->div_table = clkdef->div_table;
clknode_register(clkdom, clk);
return (0);

View File

@ -32,6 +32,12 @@
#include <dev/extres/clk/clk.h>
#define CLK_DIV_ZERO_BASED 0x0001 /* Zero based divider. */
#define CLK_DIV_WITH_TABLE 0x0002 /* Table to lookup the real value */
struct clk_div_table {
uint32_t value;
uint32_t divider;
};
struct clk_div_def {
struct clknode_init_def clkdef;
@ -41,6 +47,7 @@ struct clk_div_def {
uint32_t f_shift; /* Fractional divide bits, */
uint32_t f_width; /* set to 0 for int divider */
int div_flags; /* Divider-specific flags */
struct clk_div_table *div_table; /* Divider table */
};
int clknode_div_register(struct clkdom *clkdom, struct clk_div_def *clkdef);