This is the driver for the main PLL clock hardware found on DM SoCs.
This driver borrowed code from arch/arm/mach-davinci/clock.c and
implemented the driver as per common clock provider API. The main PLL
hardware typically has a multiplier, a pre-divider and a post-divider.
Some of the SoCs has the divider fixed meaning they can not be
configured through a register. HAS_PREDIV and HAS_POSTDIV flags are used
to tell the driver if a hardware has these dividers present or not.
Driver is configured through the structure clk_davinci_pll_data
that has the platform data for the driver.

Signed-off-by: Murali Karicheri <m-kariche...@ti.com>

diff --git a/drivers/clk/davinci/clk-davinci-pll.c 
b/drivers/clk/davinci/clk-davinci-pll.c
new file mode 100644
index 0000000..13e1690
--- /dev/null
+++ b/drivers/clk/davinci/clk-davinci-pll.c
@@ -0,0 +1,128 @@
+/*
+ * PLL clk driver DaVinci devices
+ *
+ * Copyright (C) 2006-2012 Texas Instruments.
+ * Copyright (C) 2008-2009 Deep Root Systems, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * TODO - Add set_parent_rate()
+ */
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/platform_data/clk-davinci-pll.h>
+
+#include <mach/cputype.h>
+
+#define PLLM           0x110
+#define PLLM_PLLM_MASK  0xff
+#define PREDIV          0x114
+#define POSTDIV         0x128
+#define PLLDIV_EN       BIT(15)
+
+/**
+ * struct clk_davinci_pll - DaVinci Main pll clock
+ * @hw: clk_hw for the pll
+ * @pll_data: PLL driver specific data
+ */
+struct clk_davinci_pll {
+       struct clk_hw hw;
+       struct clk_davinci_pll_data *pll_data;
+};
+
+#define to_clk_pll(_hw) container_of(_hw, struct clk_davinci_pll, hw)
+
+static unsigned long clk_pllclk_recalc(struct clk_hw *hw,
+                                       unsigned long parent_rate)
+{
+       struct clk_davinci_pll *pll = to_clk_pll(hw);
+       struct clk_davinci_pll_data *pll_data = pll->pll_data;
+       u32 mult = 1, prediv = 1, postdiv = 1;
+       unsigned long rate = parent_rate;
+
+       /* If there is a device specific recalc defined invoke it. Otherwise
+        * fallback to default one
+        */
+       mult = __raw_readl(pll_data->pllm);
+       if (pll_data->pllm_multiplier)
+               mult =  pll_data->pllm_multiplier *
+                               (mult & pll_data->pllm_mask);
+       else
+               mult = (mult & pll_data->pllm_mask) + 1;
+
+       if (pll_data->flags & CLK_DAVINCI_PLL_HAS_PREDIV) {
+               /* pre-divider is fixed, take prediv value from pll_data  */
+               if (pll_data->fixed_prediv)
+                       prediv = pll_data->fixed_prediv;
+               else {
+                       prediv = __raw_readl(pll_data->prediv);
+                       if (prediv & PLLDIV_EN)
+                               prediv = (prediv & pll_data->prediv_mask) + 1;
+                       else
+                               prediv = 1;
+               }
+       }
+
+       if (pll_data->flags & CLK_DAVINCI_PLL_HAS_POSTDIV) {
+               postdiv = __raw_readl(pll_data->postdiv);
+               if (postdiv & PLLDIV_EN)
+                       postdiv = (postdiv & pll_data->postdiv_mask) + 1;
+               else
+                       postdiv = 1;
+       }
+
+       rate /= prediv;
+       rate *= mult;
+       rate /= postdiv;
+
+       pr_debug("PLL%d: input = %lu MHz [ ",
+                pll_data->num, parent_rate / 1000000);
+       if (prediv > 1)
+               pr_debug("/ %d ", prediv);
+       if (mult > 1)
+               pr_debug("* %d ", mult);
+       if (postdiv > 1)
+               pr_debug("/ %d ", postdiv);
+       pr_debug("] --> %lu MHz output.\n", rate / 1000000);
+       return rate;
+}
+
+static const struct clk_ops clk_pll_ops = {
+       .recalc_rate = clk_pllclk_recalc,
+};
+
+struct clk *clk_register_davinci_pll(struct device *dev, const char *name,
+                       const char *parent_name,
+                       struct clk_davinci_pll_data *pll_data)
+{
+       struct clk_init_data init;
+       struct clk_davinci_pll *pll;
+       struct clk *clk;
+
+       if (!pll_data)
+               return ERR_PTR(-ENODEV);
+
+       pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+       if (!pll)
+               return ERR_PTR(-ENOMEM);
+       init.name = name;
+       init.ops = &clk_pll_ops;
+       init.flags = pll_data->flags;
+       init.parent_names = (parent_name ? &parent_name : NULL);
+       init.num_parents = (parent_name ? 1 : 0);
+
+       pll->pll_data   = pll_data;
+       pll->hw.init = &init;
+
+       clk = clk_register(NULL, &pll->hw);
+       if (IS_ERR(clk))
+               kfree(pll);
+
+       return clk;
+}
diff --git a/include/linux/platform_data/clk-davinci-pll.h 
b/include/linux/platform_data/clk-davinci-pll.h
new file mode 100644
index 0000000..4c2920b
--- /dev/null
+++ b/include/linux/platform_data/clk-davinci-pll.h
@@ -0,0 +1,54 @@
+/*
+ * TI DaVinci clk-pll driver platform data definitions
+ *
+ * Copyright (C) 2006-2012 Texas Instruments.
+ * Copyright (C) 2008-2009 Deep Root Systems, LLC
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __CLK_DAVINCI_PLL_H
+#define __CLK_DAVINCI_PLL_H
+
+/* PLL flags */
+#define CLK_DAVINCI_PLL_HAS_PREDIV                     BIT(0)
+#define CLK_DAVINCI_PLL_HAS_POSTDIV                    BIT(1)
+
+struct clk_davinci_pll_data {
+       /* physical addresses set by platform code */
+       u32 phy_pllm;
+       /* if PLL has a prediv register this should be non zero */
+       u32 phy_prediv;
+       /* if PLL has a postdiv register this should be non zero */
+       u32 phy_postdiv;
+       /* mapped addresses. should be initialized by  */
+       void __iomem *pllm;
+       void __iomem *prediv;
+       void __iomem *postdiv;
+       u32 pllm_mask;
+       u32 prediv_mask;
+       u32 postdiv_mask;
+       u32 num;
+       /* framework flags */
+       u32 flags;
+       /* pll flags */
+       u32 pll_flags;
+       /* use this value for prediv */
+       u32 fixed_prediv;
+       /* multiply PLLM by this factor. By default most SOC set this to zero
+        * that translates to a multiplier of 1 and incrementer of 1.
+        * To override default, set this factor
+        */
+       u32 pllm_multiplier;
+};
+
+extern struct clk *clk_register_davinci_pll(struct device *dev,
+                       const char *name, const char *parent_name,
+                       struct clk_davinci_pll_data *pll_data);
+#endif /* CLK_DAVINCI_PLL_H */
-- 
1.7.9.5

_______________________________________________
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