[PATCH v2 06/16] clk: tz1090: add PLL clock driver

2014-12-01 Thread James Hogan
Add a clock driver for the main PLLs in the TZ1090 SoC, the system PLL
and the ADC PLL. The system PLL is used to derive the core Meta clock,
the DDR clock, and the system clock. The ADC PLL can be used for various
purposes, but is usually used for the pixel clock.

The PLL is a True Circuits PLL, but the arrangement of the fields in the
registers is specific to the TZ1090 SoC.

The driver supports recalc_rate, round_rate, and set_rate operations.

The tz1090_clk_register_plls() helper function can be used to register a
set of PLLs from static initialisation data. A PLL() macro is provided
in tz1090/clk.h to aid the creation of this data. For example:

static const struct tz1090_clk_pll plls[] __initconst = {
PLL(CLK_TOP_SYSPLL, "sys_sw", "sys_pll", TOP_SYSPLL_CTL0),
...
};
...
tz1090_clk_register_plls(p, plls, ARRAY_SIZE(plls));

Signed-off-by: James Hogan 
Cc: Mike Turquette 
Cc: linux-me...@vger.kernel.org
---
Changes since v1 (patch 4):
- Renamed function prefixes from clk_tz1090_ to tz1090_clk_ for
  consistency with the rest.
- Drop DT binding as it will be instantiated directly from a provider.
- Add tz1090_clk_register_plls() to conveniently register a set of PLLs
  in a clock provider from static initilisation data.
- Extend tz1090/clk.h interface for easy static initialisation with
  macros.
---
 drivers/clk/tz1090/Makefile |   1 +
 drivers/clk/tz1090/clk-tz1090-pll.c | 276 
 drivers/clk/tz1090/clk.h|  22 +++
 3 files changed, 299 insertions(+)
 create mode 100644 drivers/clk/tz1090/clk-tz1090-pll.c

diff --git a/drivers/clk/tz1090/Makefile b/drivers/clk/tz1090/Makefile
index a7127d9..a28a5bb 100644
--- a/drivers/clk/tz1090/Makefile
+++ b/drivers/clk/tz1090/Makefile
@@ -5,3 +5,4 @@ obj-y   += clk-tz1090-deleter.o
 obj-y  += clk-tz1090-divider.o
 obj-y  += clk-tz1090-gate-bank.o
 obj-y  += clk-tz1090-mux-bank.o
+obj-y  += clk-tz1090-pll.o
diff --git a/drivers/clk/tz1090/clk-tz1090-pll.c 
b/drivers/clk/tz1090/clk-tz1090-pll.c
new file mode 100644
index 000..ca8260b
--- /dev/null
+++ b/drivers/clk/tz1090/clk-tz1090-pll.c
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2011 Sascha Hauer, Pengutronix 
+ * Copyright (C) 2011 Richard Zhao, Linaro 
+ * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd 
+ * Copyright (C) 2013-2014 Imagination Technologies Ltd.
+ *
+ * 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.
+ *
+ * True Circuits PLL in TZ1090 SoC.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "clk.h"
+
+/* Register definitions */
+
+#define PLL_CTL0   0
+#define  PLL_CTL0_BWADJ_M  0xfff
+#define  PLL_CTL0_BWADJ_S  20
+#define  PLL_CTL0_CLKF_M   0x1fff
+#define  PLL_CTL0_CLKF_S   4
+#define  PLL_CTL0_CLKOD_M  0x7
+#define  PLL_CTL0_CLKOD_S  0
+#define PLL_CTL1   4
+#define  PLL_CTL1_RESET_B  BIT(28)
+#define  PLL_CTL1_FASTEN_B BIT(27)
+#define  PLL_CTL1_ENSAT_B  BIT(26)
+#define  PLL_CTL1_BYPASS_B BIT(25)
+#define  PLL_CTL1_PWRDN_B  BIT(24)
+#define  PLL_CTL1_CLKR_M   0x3f
+#define  PLL_CTL1_CLKR_S   0
+
+/**
+ * struct tz1090_clk_pll_priv - PLL in TZ1090
+ *
+ * @hw:handle between common and hardware-specific interfaces
+ * @reg:   first of two registers
+ *
+ * PLL in TZ1090.
+ */
+struct tz1090_clk_pll_priv {
+   struct clk_hw   hw;
+   void __iomem*reg;
+};
+
+#define to_tz1090_clk_pll(_hw) container_of(_hw, struct tz1090_clk_pll_priv, 
hw)
+
+static unsigned long tz1090_clk_pll_recalc_rate(struct clk_hw *hw,
+   unsigned long f_in)
+{
+   struct tz1090_clk_pll_priv *pll = to_tz1090_clk_pll(hw);
+   u32 ctl0, ctl1;
+   unsigned int clk_f; /* feedback divide */
+   unsigned int clk_od;/* output divide */
+   unsigned int clk_r; /* reference divide */
+   unsigned long f_out;
+
+   ctl0 = readl(pll->reg + PLL_CTL0);
+   ctl1 = readl(pll->reg + PLL_CTL1);
+
+   /* Bypass? */
+   if (ctl1 & PLL_CTL1_BYPASS_B)
+   return f_in;
+
+   /* Get divider values */
+   clk_f  = 1 + ((ctl0 >> PLL_CTL0_CLKF_S)  & PLL_CTL0_CLKF_M);
+   clk_od = 1 + ((ctl0 >> PLL_CTL0_CLKOD_S) & PLL_CTL0_CLKOD_M);
+   clk_r  = 1 + ((ctl1 >> PLL_CTL1_CLKR_S)  & PLL_CTL1_CLKR_M);
+
+   /*
+* formula:
+* f_out = (f_in / clk_r) * (clk_f / 2) / clk_od
+*   = (f_in * clk_f) / (2 * clk_r * clk_od)
+*/
+   f_out = div_u64((u64)f_in * clk_f,
+   2 * clk_r * clk_od);
+   return f_out;
+}
+
+/* finds best pll parameters and returns rate on success (or 0) */
+static int 

[PATCH v2 06/16] clk: tz1090: add PLL clock driver

2014-12-01 Thread James Hogan
Add a clock driver for the main PLLs in the TZ1090 SoC, the system PLL
and the ADC PLL. The system PLL is used to derive the core Meta clock,
the DDR clock, and the system clock. The ADC PLL can be used for various
purposes, but is usually used for the pixel clock.

The PLL is a True Circuits PLL, but the arrangement of the fields in the
registers is specific to the TZ1090 SoC.

The driver supports recalc_rate, round_rate, and set_rate operations.

The tz1090_clk_register_plls() helper function can be used to register a
set of PLLs from static initialisation data. A PLL() macro is provided
in tz1090/clk.h to aid the creation of this data. For example:

static const struct tz1090_clk_pll plls[] __initconst = {
PLL(CLK_TOP_SYSPLL, sys_sw, sys_pll, TOP_SYSPLL_CTL0),
...
};
...
tz1090_clk_register_plls(p, plls, ARRAY_SIZE(plls));

Signed-off-by: James Hogan james.ho...@imgtec.com
Cc: Mike Turquette mturque...@linaro.org
Cc: linux-me...@vger.kernel.org
---
Changes since v1 (patch 4):
- Renamed function prefixes from clk_tz1090_ to tz1090_clk_ for
  consistency with the rest.
- Drop DT binding as it will be instantiated directly from a provider.
- Add tz1090_clk_register_plls() to conveniently register a set of PLLs
  in a clock provider from static initilisation data.
- Extend tz1090/clk.h interface for easy static initialisation with
  macros.
---
 drivers/clk/tz1090/Makefile |   1 +
 drivers/clk/tz1090/clk-tz1090-pll.c | 276 
 drivers/clk/tz1090/clk.h|  22 +++
 3 files changed, 299 insertions(+)
 create mode 100644 drivers/clk/tz1090/clk-tz1090-pll.c

diff --git a/drivers/clk/tz1090/Makefile b/drivers/clk/tz1090/Makefile
index a7127d9..a28a5bb 100644
--- a/drivers/clk/tz1090/Makefile
+++ b/drivers/clk/tz1090/Makefile
@@ -5,3 +5,4 @@ obj-y   += clk-tz1090-deleter.o
 obj-y  += clk-tz1090-divider.o
 obj-y  += clk-tz1090-gate-bank.o
 obj-y  += clk-tz1090-mux-bank.o
+obj-y  += clk-tz1090-pll.o
diff --git a/drivers/clk/tz1090/clk-tz1090-pll.c 
b/drivers/clk/tz1090/clk-tz1090-pll.c
new file mode 100644
index 000..ca8260b
--- /dev/null
+++ b/drivers/clk/tz1090/clk-tz1090-pll.c
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2011 Sascha Hauer, Pengutronix s.ha...@pengutronix.de
+ * Copyright (C) 2011 Richard Zhao, Linaro richard.z...@linaro.org
+ * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd mturque...@linaro.org
+ * Copyright (C) 2013-2014 Imagination Technologies Ltd.
+ *
+ * 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.
+ *
+ * True Circuits PLL in TZ1090 SoC.
+ */
+
+#include linux/clk-provider.h
+#include linux/delay.h
+#include linux/err.h
+#include linux/io.h
+#include linux/slab.h
+
+#include clk.h
+
+/* Register definitions */
+
+#define PLL_CTL0   0
+#define  PLL_CTL0_BWADJ_M  0xfff
+#define  PLL_CTL0_BWADJ_S  20
+#define  PLL_CTL0_CLKF_M   0x1fff
+#define  PLL_CTL0_CLKF_S   4
+#define  PLL_CTL0_CLKOD_M  0x7
+#define  PLL_CTL0_CLKOD_S  0
+#define PLL_CTL1   4
+#define  PLL_CTL1_RESET_B  BIT(28)
+#define  PLL_CTL1_FASTEN_B BIT(27)
+#define  PLL_CTL1_ENSAT_B  BIT(26)
+#define  PLL_CTL1_BYPASS_B BIT(25)
+#define  PLL_CTL1_PWRDN_B  BIT(24)
+#define  PLL_CTL1_CLKR_M   0x3f
+#define  PLL_CTL1_CLKR_S   0
+
+/**
+ * struct tz1090_clk_pll_priv - PLL in TZ1090
+ *
+ * @hw:handle between common and hardware-specific interfaces
+ * @reg:   first of two registers
+ *
+ * PLL in TZ1090.
+ */
+struct tz1090_clk_pll_priv {
+   struct clk_hw   hw;
+   void __iomem*reg;
+};
+
+#define to_tz1090_clk_pll(_hw) container_of(_hw, struct tz1090_clk_pll_priv, 
hw)
+
+static unsigned long tz1090_clk_pll_recalc_rate(struct clk_hw *hw,
+   unsigned long f_in)
+{
+   struct tz1090_clk_pll_priv *pll = to_tz1090_clk_pll(hw);
+   u32 ctl0, ctl1;
+   unsigned int clk_f; /* feedback divide */
+   unsigned int clk_od;/* output divide */
+   unsigned int clk_r; /* reference divide */
+   unsigned long f_out;
+
+   ctl0 = readl(pll-reg + PLL_CTL0);
+   ctl1 = readl(pll-reg + PLL_CTL1);
+
+   /* Bypass? */
+   if (ctl1  PLL_CTL1_BYPASS_B)
+   return f_in;
+
+   /* Get divider values */
+   clk_f  = 1 + ((ctl0  PLL_CTL0_CLKF_S)   PLL_CTL0_CLKF_M);
+   clk_od = 1 + ((ctl0  PLL_CTL0_CLKOD_S)  PLL_CTL0_CLKOD_M);
+   clk_r  = 1 + ((ctl1  PLL_CTL1_CLKR_S)   PLL_CTL1_CLKR_M);
+
+   /*
+* formula:
+* f_out = (f_in / clk_r) * (clk_f / 2) / clk_od
+*   = (f_in * clk_f) / (2 * clk_r * clk_od)
+*/
+   f_out = div_u64((u64)f_in *