clk: xgene: Add SoC and PMD PLL clocks with v2 hardware
authorLoc Ho <lho@apm.com>
Wed, 20 Jan 2016 02:27:42 +0000 (19:27 -0700)
committerStephen Boyd <sboyd@codeaurora.org>
Fri, 29 Jan 2016 20:54:35 +0000 (12:54 -0800)
Add X-Gene SoC and PMD PLL clocks support for v2 hardware.
X-Gene SoC v2 and above use an slightly different SoC
and PMD PLL hardware logic.

Signed-off-by: Loc Ho <lho@apm.com>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
drivers/clk/clk-xgene.c

index 10224b0..266d573 100644 (file)
@@ -29,7 +29,9 @@
 #include <linux/of_address.h>
 
 /* Register SCU_PCPPLL bit fields */
-#define N_DIV_RD(src)                  (((src) & 0x000001ff))
+#define N_DIV_RD(src)                  ((src) & 0x000001ff)
+#define SC_N_DIV_RD(src)               ((src) & 0x0000007f)
+#define SC_OUTDIV2(src)                        (((src) & 0x00000100) >> 8)
 
 /* Register SCU_SOCPLL bit fields */
 #define CLKR_RD(src)                   (((src) & 0x07000000)>>24)
@@ -63,6 +65,7 @@ struct xgene_clk_pll {
        spinlock_t      *lock;
        u32             pll_offset;
        enum xgene_pll_type     type;
+       int             version;
 };
 
 #define to_xgene_clk_pll(_hw) container_of(_hw, struct xgene_clk_pll, hw)
@@ -92,27 +95,37 @@ static unsigned long xgene_clk_pll_recalc_rate(struct clk_hw *hw,
 
        pll = xgene_clk_read(pllclk->reg + pllclk->pll_offset);
 
-       if (pllclk->type == PLL_TYPE_PCP) {
-               /*
-                * PLL VCO = Reference clock * NF
-                * PCP PLL = PLL_VCO / 2
-                */
-               nout = 2;
-               fvco = parent_rate * (N_DIV_RD(pll) + 4);
+       if (pllclk->version <= 1) {
+               if (pllclk->type == PLL_TYPE_PCP) {
+                       /*
+                       * PLL VCO = Reference clock * NF
+                       * PCP PLL = PLL_VCO / 2
+                       */
+                       nout = 2;
+                       fvco = parent_rate * (N_DIV_RD(pll) + 4);
+               } else {
+                       /*
+                       * Fref = Reference Clock / NREF;
+                       * Fvco = Fref * NFB;
+                       * Fout = Fvco / NOUT;
+                       */
+                       nref = CLKR_RD(pll) + 1;
+                       nout = CLKOD_RD(pll) + 1;
+                       nfb = CLKF_RD(pll);
+                       fref = parent_rate / nref;
+                       fvco = fref * nfb;
+               }
        } else {
                /*
-                * Fref = Reference Clock / NREF;
-                * Fvco = Fref * NFB;
-                * Fout = Fvco / NOUT;
+                * fvco = Reference clock * FBDIVC
+                * PLL freq = fvco / NOUT
                 */
-               nref = CLKR_RD(pll) + 1;
-               nout = CLKOD_RD(pll) + 1;
-               nfb = CLKF_RD(pll);
-               fref = parent_rate / nref;
-               fvco = fref * nfb;
+               nout = SC_OUTDIV2(pll) ? 2 : 3;
+               fvco = parent_rate * SC_N_DIV_RD(pll);
        }
-       pr_debug("%s pll recalc rate %ld parent %ld\n", clk_hw_get_name(hw),
-               fvco / nout, parent_rate);
+       pr_debug("%s pll recalc rate %ld parent %ld version %d\n",
+                clk_hw_get_name(hw), fvco / nout, parent_rate,
+                pllclk->version);
 
        return fvco / nout;
 }
@@ -125,7 +138,7 @@ static const struct clk_ops xgene_clk_pll_ops = {
 static struct clk *xgene_register_clk_pll(struct device *dev,
        const char *name, const char *parent_name,
        unsigned long flags, void __iomem *reg, u32 pll_offset,
-       u32 type, spinlock_t *lock)
+       u32 type, spinlock_t *lock, int version)
 {
        struct xgene_clk_pll *apmclk;
        struct clk *clk;
@@ -144,6 +157,7 @@ static struct clk *xgene_register_clk_pll(struct device *dev,
        init.parent_names = parent_name ? &parent_name : NULL;
        init.num_parents = parent_name ? 1 : 0;
 
+       apmclk->version = version;
        apmclk->reg = reg;
        apmclk->lock = lock;
        apmclk->pll_offset = pll_offset;
@@ -160,26 +174,37 @@ static struct clk *xgene_register_clk_pll(struct device *dev,
        return clk;
 }
 
+static int xgene_pllclk_version(struct device_node *np)
+{
+       if (of_device_is_compatible(np, "apm,xgene-socpll-clock"))
+               return 1;
+       if (of_device_is_compatible(np, "apm,xgene-pcppll-clock"))
+               return 1;
+       return 2;
+}
+
 static void xgene_pllclk_init(struct device_node *np, enum xgene_pll_type pll_type)
 {
-        const char *clk_name = np->full_name;
-        struct clk *clk;
-        void __iomem *reg;
+       const char *clk_name = np->full_name;
+       struct clk *clk;
+       void __iomem *reg;
+       int version = xgene_pllclk_version(np);
 
-        reg = of_iomap(np, 0);
-        if (reg == NULL) {
-                pr_err("Unable to map CSR register for %s\n", np->full_name);
-                return;
-        }
-        of_property_read_string(np, "clock-output-names", &clk_name);
-        clk = xgene_register_clk_pll(NULL,
-                        clk_name, of_clk_get_parent_name(np, 0),
-                        CLK_IS_ROOT, reg, 0, pll_type, &clk_lock);
-        if (!IS_ERR(clk)) {
-                of_clk_add_provider(np, of_clk_src_simple_get, clk);
-                clk_register_clkdev(clk, clk_name, NULL);
-                pr_debug("Add %s clock PLL\n", clk_name);
-        }
+       reg = of_iomap(np, 0);
+       if (reg == NULL) {
+               pr_err("Unable to map CSR register for %s\n", np->full_name);
+               return;
+       }
+       of_property_read_string(np, "clock-output-names", &clk_name);
+       clk = xgene_register_clk_pll(NULL,
+                       clk_name, of_clk_get_parent_name(np, 0),
+                       CLK_IS_ROOT, reg, 0, pll_type, &clk_lock,
+                       version);
+       if (!IS_ERR(clk)) {
+               of_clk_add_provider(np, of_clk_src_simple_get, clk);
+               clk_register_clkdev(clk, clk_name, NULL);
+               pr_debug("Add %s clock PLL\n", clk_name);
+       }
 }
 
 static void xgene_socpllclk_init(struct device_node *np)
@@ -460,7 +485,7 @@ static void __init xgene_devclk_init(struct device_node *np)
                rc = of_address_to_resource(np, i, &res);
                if (rc != 0) {
                        if (i == 0) {
-                               pr_err("no DTS register for %s\n", 
+                               pr_err("no DTS register for %s\n",
                                        np->full_name);
                                return;
                        }
@@ -518,4 +543,8 @@ err:
 
 CLK_OF_DECLARE(xgene_socpll_clock, "apm,xgene-socpll-clock", xgene_socpllclk_init);
 CLK_OF_DECLARE(xgene_pcppll_clock, "apm,xgene-pcppll-clock", xgene_pcppllclk_init);
+CLK_OF_DECLARE(xgene_socpll_v2_clock, "apm,xgene-socpll-v2-clock",
+              xgene_socpllclk_init);
+CLK_OF_DECLARE(xgene_pcppll_v2_clock, "apm,xgene-pcppll-v2-clock",
+              xgene_pcppllclk_init);
 CLK_OF_DECLARE(xgene_dev_clock, "apm,xgene-device-clock", xgene_devclk_init);