From: Davide Bonfanti <davide.bonfa...@bticino.it>

    Clockout2 is added as a child of pll1_sysclk9.
    A new dm365_clkout2_set_rate is used to set clockout2 frequency.

Signed-off-by: Davide Bonfanti <davide.bonfa...@bticino.it>
Signed-off-by: Raffaele Recalcati <raffaele.recalc...@bticino.it>
---
This patch has been developed against 
http://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux-davinci.git 
and has been tested for dm365 cpu on BMX board.
This patch depends on the following one submitted to the mailing list:
https://patchwork.kernel.org/patch/112994

TODO LIST
-The clkout2 management needs a recalc function, but I by now I don't know how 
to
point to it from davinci_clk_init.
-The unmapping of system_module_base has to be checked.
-Solving a possible race when writing PERI_CLKCTL, see Nori Sekhar explanation:
"Looks like the same register supports setting clocks for voice codec
and keyscan module as well. This can cause a race if those clocks are
modified in a different context. Can you extend this function to cover
those clocks as well? May be clk->lpsc can be used to determine if div1,
div2 or div3 need to be changed. This way you can serialize the setting
up of these clocks."

A fine tuning of clkout2 is possible using the function below (it is 
possible to do something similar with Nori Sekhar work about DIV_ROUND_CLOSEST()
in the davinci_set_sysclk_rate() function (max_rate has to be passed).

static int clkout2_fine_setting(unsigned long rate)
{
        struct clk *pll1_sysclk9;
        struct clk *clkout2;
        int i, err, min_err, i_min_err;
        int ret = -EINVAL;

        pll1_sysclk9 = clk_get(NULL, "pll1_sysclk9");
        if (IS_ERR(pll1_sysclk9)) {
                printk(KERN_ERR "Could not get pll1_sysclk9\n");
                return -ENODEV;
        }
        clk_enable(pll1_sysclk9);

        /* check all possibilities to get best fitting for the required freq */
        i_min_err = min_err = INT_MAX;
        for (i = 0x0F; i > 0; i--) {
                ret = clk_set_rate(pll1_sysclk9, rate * i);
                err = clk_get_rate(pll1_sysclk9) - rate * i;
                if (min_err > abs(err)) {
                        min_err = abs(err);
                        i_min_err = i;
                }
        }

        /* setting the best one to pll1_sysclk9 */
        ret = clk_set_rate(pll1_sysclk9, rate * i_min_err);
        if (ret)
                return ret;

        /* setting the best one to clkout2 */
        clkout2 = clk_get(NULL, "clkout2");
        if (IS_ERR(clkout2)) {
                printk(KERN_ERR "Could not get clkout2\n");
                return -ENODEV;
        }
        clk_enable(clkout2);
        clk_set_rate(clkout2,rate);
        return 0;
}



 arch/arm/mach-davinci/dm365.c |   42 +++++++++++++++++++++++++++++++++++++++++
 1 files changed, 42 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c
index 42fd4a4..0101a6c 100644
--- a/arch/arm/mach-davinci/dm365.c
+++ b/arch/arm/mach-davinci/dm365.c
@@ -40,6 +40,40 @@
 #include "mux.h"
 
 #define DM365_REF_FREQ         24000000        /* 24 MHz on the DM365 EVM */
+#define DIV1_MASK              0x78            /* DIV1 mask in PERI_CLKCTL */
+#define PERI_CLKCTL            0x48
+#define CLOCKOUT2EN            2
+
+int dm365_clkout2_set_rate(struct clk *clk, unsigned long rate)
+{
+       int i;
+       unsigned long input;
+       unsigned ratio;
+       unsigned regval;
+       void __iomem *system_module_base;
+
+       /* There must be a parent... */
+       if (WARN_ON(!clk->parent))
+               return -EINVAL;
+
+       input = clk->parent->rate;
+
+       if (input > rate)
+               ratio = DIV_ROUND_UP(input, rate) - 1;
+
+       system_module_base = ioremap(DAVINCI_SYSTEM_MODULE_BASE, SZ_4K);
+       regval = __raw_readl(system_module_base + PERI_CLKCTL);
+       regval &= ~DIV1_MASK;
+       regval |= ratio << 3;
+
+       /* to make changes work stop CLOCKOUT & start it again */
+       regval |= BIT(CLOCKOUT2EN);
+       __raw_writel(regval, system_module_base + PERI_CLKCTL);
+       regval &= ~(1 << CLOCKOUT2EN);
+       __raw_writel(regval, system_module_base + PERI_CLKCTL);
+       iounmap(system_module_base);
+       return 0;
+}
 
 static struct pll_data pll1_data = {
        .num            = 1,
@@ -145,6 +179,13 @@ static struct clk pll1_sysclk9 = {
        .parent         = &pll1_clk,
        .flags          = CLK_PLL,
        .div_reg        = PLLDIV9,
+       .set_rate       = davinci_set_sysclk_rate,
+};
+
+static struct clk clkout2_clk = {
+       .name           = "clkout2",
+       .parent         = &pll1_sysclk9,
+       .set_rate       = dm365_clkout2_set_rate,
 };
 
 static struct clk pll2_clk = {
@@ -421,6 +462,7 @@ static struct clk_lookup dm365_clks[] = {
        CLK(NULL, "pll1_sysclk7", &pll1_sysclk7),
        CLK(NULL, "pll1_sysclk8", &pll1_sysclk8),
        CLK(NULL, "pll1_sysclk9", &pll1_sysclk9),
+       CLK(NULL, "clkout2", &clkout2_clk),
        CLK(NULL, "pll2", &pll2_clk),
        CLK(NULL, "pll2_aux", &pll2_aux_clk),
        CLK(NULL, "clkout1", &clkout1_clk),
-- 
1.7.0.4

_______________________________________________
Davinci-linux-open-source mailing list
Davinci-linux-open-source@linux.davincidsp.com
http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source

Reply via email to