[PATCH v2 06/16] clk: tz1090: add PLL clock driver
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
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 *