Author: manu
Date: Mon Dec 12 16:43:31 2016
New Revision: 309894
URL: https://svnweb.freebsd.org/changeset/base/309894

Log:
  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

Modified:
  head/sys/dev/extres/clk/clk_div.c
  head/sys/dev/extres/clk/clk_div.h

Modified: head/sys/dev/extres/clk/clk_div.c
==============================================================================
--- head/sys/dev/extres/clk/clk_div.c   Mon Dec 12 15:37:11 2016        
(r309893)
+++ head/sys/dev/extres/clk/clk_div.c   Mon Dec 12 16:43:31 2016        
(r309894)
@@ -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_meth
 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, de
                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
                    (*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 *clkd
        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);

Modified: head/sys/dev/extres/clk/clk_div.h
==============================================================================
--- head/sys/dev/extres/clk/clk_div.h   Mon Dec 12 15:37:11 2016        
(r309893)
+++ head/sys/dev/extres/clk/clk_div.h   Mon Dec 12 16:43:31 2016        
(r309894)
@@ -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);
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to