[PATCH 3/3] drivers/clk: sta2x11 common clock framework implementation
From: Davide Ciminaghi
Date: Fri Mar 08 2013 - 10:41:05 EST
Signed-off-by: Davide Ciminaghi <ciminaghi@xxxxxxxxx>
---
arch/x86/Kconfig | 1 +
drivers/clk/Makefile | 1 +
drivers/clk/sta2x11/Makefile | 1 +
drivers/clk/sta2x11/clk-audio-pll.c | 150 +++++++
drivers/clk/sta2x11/clk-soc-pll.c | 96 +++++
drivers/clk/sta2x11/clk.c | 782 +++++++++++++++++++++++++++++++++++
drivers/clk/sta2x11/clk.h | 73 ++++
7 files changed, 1104 insertions(+)
create mode 100644 drivers/clk/sta2x11/Makefile
create mode 100644 drivers/clk/sta2x11/clk-audio-pll.c
create mode 100644 drivers/clk/sta2x11/clk-soc-pll.c
create mode 100644 drivers/clk/sta2x11/clk.c
create mode 100644 drivers/clk/sta2x11/clk.h
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 058d4f49..2e73ff4 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -547,6 +547,7 @@ config STA2X11
depends on X86_32_NON_STANDARD && PCI
select X86_DEV_DMA_OPS
select X86_DMA_REMAP
+ select COMMON_CLK
select SWIOTLB
select MFD_STA2X11
select ARCH_REQUIRE_GPIOLIB
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 300d477..8558f55 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_ARCH_ZYNQ) += clk-zynq.o
obj-$(CONFIG_ARCH_TEGRA) += tegra/
obj-$(CONFIG_X86) += x86/
+obj-$(CONFIG_STA2X11) += sta2x11/
# Chip specific
obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
diff --git a/drivers/clk/sta2x11/Makefile b/drivers/clk/sta2x11/Makefile
new file mode 100644
index 0000000..60c319a
--- /dev/null
+++ b/drivers/clk/sta2x11/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_STA2X11) += clk.o clk-soc-pll.o clk-audio-pll.o
diff --git a/drivers/clk/sta2x11/clk-audio-pll.c b/drivers/clk/sta2x11/clk-audio-pll.c
new file mode 100644
index 0000000..1950954
--- /dev/null
+++ b/drivers/clk/sta2x11/clk-audio-pll.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright ST Microelectronics 2012
+ * Author: Davide Ciminaghi <ciminaghi@xxxxxxxxx>
+ *
+ * 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 in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Common clock api implementation for sta2x11
+ * audio-pll clock type implementation
+ */
+/* #define DEBUG */
+
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/sta2x11-mfd.h>
+#include <linux/clk-provider.h>
+#include <asm/div64.h>
+#include <asm/sta2x11.h>
+
+#include "clk.h"
+
+/**
+ * struct clk_audio_pll - sta2x11 audio pll clock
+ * @hw: clk_hw for the pll
+ *
+ * Soc pll
+ */
+struct clk_audio_pll {
+ struct clk_hw hw;
+ void __iomem *base;
+ spinlock_t *lock;
+};
+
+#define to_clk_audio_pll(_hw) container_of(_hw, struct clk_audio_pll, hw)
+
+static unsigned long clk_audio_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_audio_pll *audio_pll = to_clk_audio_pll(hw);
+ u32 scpllctl = readl(audio_pll->base + SCTL_SCPLLCTL);
+ u32 scpllfctrl = readl(audio_pll->base + SCTL_SCPLLFCTRL);
+ u16 scresfract = readl(audio_pll->base + SCTL_SCRESFRACT);
+ u64 fvco, inff, ndiv, fract, idf, phi, odf;
+ int frac_control, dither_disable;
+
+ inff = parent_rate;
+ frac_control = scpllctl & SCTL_SCPLLCTL_FRAC_CONTROL;
+ ndiv = (scpllfctrl >> SCTL_SCPLLFCTRL_AUDIO_PLL_NDIV_SHIFT) &
+ SCTL_SCPLLFCTRL_AUDIO_PLL_NDIV_MASK;
+ fract = scresfract & SCTL_SCRESFRACT_MASK ;
+ idf = (scpllfctrl >> SCTL_SCPLLFCTRL_AUDIO_PLL_IDF_SHIFT) &
+ SCTL_SCPLLFCTRL_AUDIO_PLL_IDF_MASK;
+ idf = idf ? idf : 1;
+ odf = (scpllfctrl >> SCTL_SCPLLFCTRL_AUDIO_PLL_ODF_SHIFT) &
+ SCTL_SCPLLFCTRL_AUDIO_PLL_ODF_MASK;
+ odf = odf > 5 ? 32 : (1<<odf);
+ dither_disable = ((scpllfctrl >> SCTL_SCPLLFCTRL_DITHER_DISABLE_SHIFT) &
+ SCTL_SCPLLFCTRL_DITHER_DISABLE_MASK) ? 0 : 1;
+
+ pr_debug("%s : refclk = %llu, scpllctl = 0x%08x\n", __func__,
+ inff, scpllctl);
+ pr_debug("%s : scpllfctrl = 0x%08x, scresfract = 0x%08x\n",
+ __func__, scpllfctrl, scresfract);
+ pr_debug("%s : ndiv = %llu, frac_control = %d, dither_disable = %d\n",
+ __func__, ndiv, frac_control, dither_disable);
+ pr_debug("%s: fract = %llu, idf = %llu, odf = %llu\n",
+ __func__, fract, idf, odf);
+
+ fvco = frac_control ?
+ div_u64((inff*2*((ndiv<<17)+(fract<<1)+dither_disable ? 0 : 1)),
+ (idf<<17)) :
+ div_u64((inff * 2 * ndiv), idf);
+ phi = div_u64(fvco, (odf * 2));
+
+ pr_debug("%s: fvco = %llu Hz, phi = %llu Hz\n", __func__, fvco, phi);
+
+ return phi;
+}
+
+static int clk_audio_pll_enable(struct clk_hw *hw)
+{
+ struct clk_audio_pll *audio_pll = to_clk_audio_pll(hw);
+ u32 scpllctl;
+ unsigned long flags;
+ spin_lock_irqsave(audio_pll->lock, flags);
+ scpllctl = readl(audio_pll->base + SCTL_SCPLLCTL);
+ scpllctl &= ~SCTL_SCPLLCTL_AUDIO_PLL_PD;
+ writel(scpllctl, audio_pll->base + SCTL_SCPLLCTL);
+ spin_unlock_irqrestore(audio_pll->lock, flags);
+ return 0;
+}
+
+static void clk_audio_pll_disable(struct clk_hw *hw)
+{
+ struct clk_audio_pll *audio_pll = to_clk_audio_pll(hw);
+ u32 scpllctl;
+ unsigned long flags;
+ spin_lock_irqsave(audio_pll->lock, flags);
+ scpllctl = readl(audio_pll->base + SCTL_SCPLLCTL);
+ scpllctl |= SCTL_SCPLLCTL_AUDIO_PLL_PD;
+ writel(scpllctl, audio_pll->base + SCTL_SCPLLCTL);
+ spin_unlock_irqrestore(audio_pll->lock, flags);
+}
+
+static const struct clk_ops clk_soc_pll_ops = {
+ .enable = clk_audio_pll_enable,
+ .disable = clk_audio_pll_disable,
+ .recalc_rate = clk_audio_pll_recalc_rate,
+};
+
+
+struct clk *register_sta2x11_clk_audio_pll(const char *name,
+ const char *parent_name,
+ void __iomem *base, spinlock_t *lock)
+{
+ struct clk_audio_pll *audio_pll;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ audio_pll = kzalloc(sizeof(*audio_pll), GFP_KERNEL);
+ if (!audio_pll)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &clk_soc_pll_ops;
+ init.flags = 0;
+ init.parent_names = (parent_name ? &parent_name : NULL);
+ init.num_parents = (parent_name ? 1 : 0);
+
+ audio_pll->base = base;
+ audio_pll->lock = lock;
+ audio_pll->hw.init = &init;
+
+ clk = clk_register(NULL, &audio_pll->hw);
+ if (IS_ERR(clk))
+ kfree(audio_pll);
+
+ return clk;
+}
diff --git a/drivers/clk/sta2x11/clk-soc-pll.c b/drivers/clk/sta2x11/clk-soc-pll.c
new file mode 100644
index 0000000..0033a1c
--- /dev/null
+++ b/drivers/clk/sta2x11/clk-soc-pll.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright ST Microelectronics 2012
+ * Author: Davide Ciminaghi <ciminaghi@xxxxxxxxx>
+ *
+ * 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 in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Common clock api implementation for sta2x11
+ * soc-pll clock type implementation
+ */
+/* #define DEBUG */
+
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/sta2x11-mfd.h>
+#include <asm/sta2x11.h>
+
+#include "clk.h"
+
+/**
+ * struct clk_soc_pll - sta2x11 soc pll clock
+ * @hw: clk_hw for the pll
+ *
+ * Soc pll
+ */
+struct clk_soc_pll {
+ struct clk_hw hw;
+ void __iomem *base;
+ spinlock_t *lock;
+};
+
+#define to_clk_soc_pll(_hw) container_of(_hw, struct clk_soc_pll, hw)
+
+#define PLL1NMUL_MASK 0x7f
+#define PLL1NMUL_SHIFT 3
+
+static unsigned long clk_soc_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_soc_pll *soc_pll = to_clk_soc_pll(hw);
+ unsigned long out;
+ u32 scpllfctrl = readl(soc_pll->base + SCTL_SCPLLFCTRL);
+ u32 nmul = (scpllfctrl >> PLL1NMUL_SHIFT) & PLL1NMUL_MASK;
+ out = parent_rate * nmul;
+ pr_debug("%s : soc_pll->base = %p\n", __func__, soc_pll->base);
+ pr_debug("%s : scpllfctrl = 0x%08x\n", __func__, scpllfctrl);
+ pr_debug("%s : nmul = %d\n", __func__, nmul);
+ pr_debug("%s : calculated rate = %lu\n", __func__, out);
+ pr_debug("%s : parent_rate = %lu\n", __func__, parent_rate);
+ return out;
+}
+
+static const struct clk_ops clk_soc_pll_ops = {
+ .recalc_rate = clk_soc_pll_recalc_rate,
+};
+
+
+struct clk *register_sta2x11_clk_soc_pll(const char *name,
+ const char *parent_name,
+ void __iomem *base, spinlock_t *lock)
+{
+ struct clk_soc_pll *soc_pll;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ soc_pll = kzalloc(sizeof(*soc_pll), GFP_KERNEL);
+ if (!soc_pll)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &clk_soc_pll_ops;
+ init.flags = 0;
+ init.parent_names = (parent_name ? &parent_name : NULL);
+ init.num_parents = (parent_name ? 1 : 0);
+
+ soc_pll->base = base;
+ soc_pll->lock = lock;
+ soc_pll->hw.init = &init;
+
+ clk = clk_register(NULL, &soc_pll->hw);
+ if (IS_ERR(clk))
+ kfree(soc_pll);
+
+ return clk;
+}
diff --git a/drivers/clk/sta2x11/clk.c b/drivers/clk/sta2x11/clk.c
new file mode 100644
index 0000000..4738449
--- /dev/null
+++ b/drivers/clk/sta2x11/clk.c
@@ -0,0 +1,782 @@
+/*
+ * Copyright ST Microelectronics 2012
+ * Author: Davide Ciminaghi <ciminaghi@xxxxxxxxx>
+ *
+ * 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 in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Common clock api implementation for sta2x11
+ */
+/* #define DEBUG */
+
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/sta2x11-mfd.h>
+#include <linux/clkdev.h>
+#include <asm/sta2x11.h>
+
+#include "clk.h"
+
+#define STA2X11_MAX_CLK_NAME_LEN 60
+#define STA2X11_MAX_CLK_NPARENTS 8
+
+/*
+ * struct sta2x11_clk_reg_data
+ * This structure contains data about the registers needed to control
+ * a connext instance's clocks. Since such registers are actually owned
+ * by sta2x11-mfd, we get this data via sta2x11_mfd_get_regs_data()
+ *
+ * @sctl_base : virtual address of sctl registers
+ * @sctl_lock : spinlock needed to access the sctl registers
+ * @apb_soc_base : virtual address of apb-soc registers
+ * @apb_soc_lock : spinlock needed to access the apb-soc registers
+ */
+struct sta2x11_clk_reg_data {
+ void __iomem *sctl_base;
+ spinlock_t *sctl_lock;
+ void __iomem *apb_soc_base;
+ spinlock_t *apb_soc_lock;
+};
+
+/*
+ * struct sta2x11_clk_data
+ * This structure is used to build the table listing all the
+ * clocks for a connext chip.
+ *
+ * @basename : basename of the clock (a .%d suffix will be added to
+ * deal with multiple connext instances).
+ * @type : sta2x11 clock type (see clk.h, enum sta2x11_clk_type)
+ * @reg_offset : the controlling register's offset for this clock
+ * @init : pointer to init function. When this pointer is not NULL, the
+ * pointed function is invoked before clock registration. This is used to
+ * fill the clock struct fields which are unknown at compile time (typically
+ * virtual addresses of controlling registers).
+ * @args : arguments needed for registration
+ */
+struct sta2x11_clk_data {
+ const char *basename;
+ int type;
+ unsigned int reg_offset;
+ void (*init)(struct sta2x11_clk_data *, struct sta2x11_clk_reg_data *);
+ unsigned long flags;
+ union {
+ struct {
+ const char *parent_name;
+ unsigned long rate;
+ } fixed_rate_root;
+ struct {
+ const char *parent_name;
+ unsigned int mult;
+ unsigned int div;
+ } fixed_factor;
+ struct {
+ const char **parent_names;
+ u8 num_parents;
+ void __iomem *reg;
+ u8 shift;
+ u8 width;
+ u8 clk_mux_flags;
+ spinlock_t *lock;
+ } mux;
+ struct {
+ const char *parent_name;
+ void __iomem *base;
+ spinlock_t *lock;
+ } soc_pll;
+ struct {
+ const char *parent_name;
+ void __iomem *base;
+ spinlock_t *lock;
+ } audio_pll;
+ struct {
+ const char *parent_name;
+ void __iomem *reg;
+ u8 shift;
+ u8 width;
+ spinlock_t *lock;
+ struct clk_div_table *div_tab;
+ u8 tab_divider_flags;
+ } tab_divider;
+ } args;
+};
+
+/* Various helper macros used to setup the clock table */
+
+/*
+ * Use this macro to declare a fixed clock with no parents
+ *
+ * @name : clock name
+ * @f : clock frequency
+ */
+#define DECLARE_FIXED_RATE_ROOT_CLK(n, f) \
+ [n] = { \
+ .basename = #n, \
+ .type = fixed_rate_root, \
+ .flags = CLK_IS_ROOT, \
+ .args = { \
+ .fixed_rate_root = { \
+ .parent_name = NULL, \
+ .rate = f, \
+ } \
+ } \
+ }
+
+/*
+ * Use this macro to declare a fixed factor clock
+ *
+ * @n : clock name
+ * @parent_name = name of parent clock
+ * @flags = clock flags
+ * @mult = const mult factor
+ * @div = const div factor
+ */
+#define DECLARE_FIXED_FACTOR_CLK(n, pn, f, m, d) \
+ [n] = { \
+ .basename = #n, \
+ .type = fixed_factor, \
+ .reg_offset = 0, \
+ .flags = f, \
+ .args = { \
+ .fixed_factor = { \
+ .parent_name = #pn, \
+ .mult = m, \
+ .div = d, \
+ } \
+ } \
+ }
+
+/*
+ * Use this macro to declare a mux clock
+ *
+ * @name : clock name
+ * @reg_offset : offset of controlling register
+ * @parent_names : names of parents
+ * @init : pointer to init function
+ * @flags : clock flags
+ * @shift : bitmask shift
+ * @width : bitmask width
+ * @clk_mux_flags : flags of clock mux
+ */
+#define DECLARE_MUX_CLK(n, ro, pn, in, f, s, w, cmf) \
+ [n] = { \
+ .basename = #n, \
+ .type = mux, \
+ .flags = f, \
+ .reg_offset = ro, \
+ .init = in, \
+ .args = { \
+ .mux = { \
+ .parent_names = pn, \
+ .num_parents = ARRAY_SIZE(pn), \
+ .shift = s, \
+ .width = w, \
+ .clk_mux_flags = cmf, \
+ }, \
+ } \
+ }
+
+/*
+ * Use this macro to declare a pll clock
+ *
+ * @n : clock name
+ * @parent_name : name of parent
+ * @type : clock type
+ */
+#ifndef xcat
+#define xcat(a, b) a##b
+#endif
+
+#define DECLARE_PLL_CLK(n, pn, t) \
+ [n] = { \
+ .basename = #n, \
+ .type = t, \
+ .init = xcat(t, _init), \
+ .args = { \
+ .t = { \
+ .parent_name = #pn, \
+ }, \
+ } \
+ }
+
+/*
+ * Use this macro to declare a soc-pll clock
+ *
+ * @n : clock name
+ * @parent_name : name of parent
+ */
+#define DECLARE_SOC_PLL_CLK(n, pn) DECLARE_PLL_CLK(n, pn, soc_pll)
+
+/*
+ * Use this macro to declare an audio-pll clock
+ *
+ * @n : clock name
+ * @parent_name : name of parent
+ */
+#define DECLARE_AUDIO_PLL_CLK(n, pn) DECLARE_PLL_CLK(n, pn, audio_pll)
+
+/*
+ * Use this macro to declare a tab-divider clock
+ *
+ * @n : clock name
+ * @ro : register offset
+ * @pn : parent name
+ * @in : init function
+ * @f : clock flags
+ * @s : controlling bitmask shift
+ * @w : controlling bitmask width
+ * @tdf : tab divider clock specific flags
+ */
+#define DECLARE_TAB_DIVIDER_CLK(n, ro, pn, in, f, s, w, tab, tdf) \
+ [n] = { \
+ .basename = #n, \
+ .type = tab_divider, \
+ .flags = f, \
+ .reg_offset = ro, \
+ .init = in, \
+ .args = { \
+ .tab_divider = { \
+ .parent_name = #pn, \
+ .shift = s, \
+ .width = w, \
+ .div_tab = tab, \
+ .tab_divider_flags = tdf, \
+ }, \
+ } \
+ }
+
+/* Arrays with parents */
+
+static __initdata const char *soc_phi_parents[] = {
+ "soc_phia.%d",
+ "soc_phib.%d",
+};
+
+static __initdata const char *audio_pll_sdmmc_parents[] = {
+ "audio_pll_phi.%d",
+ "eaudio_pll_phi_div2.%d",
+};
+
+static __initdata const char *audio_pll_msp_parents[] = {
+ "audio_pll_phi_div4.%d",
+ "audio_pll_phi_div10.%d",
+};
+
+static __initdata const char *audio_pll_sarac_parents[] = {
+ "audio_pll_phi_div4.%d",
+ "audio_pll_phi.%d",
+};
+
+static __initdata const char *hclk_pre_parents[] = {
+ "soc_phi_byp_div3.%d",
+ "soc_phi_byp_div4.%d",
+ "soc_phi_byp_div6.%d",
+ "soc_phi_byp_div3.%d",
+};
+
+static __initdata const char *hclk_parents[] = {
+ "soc_phi_byp.%d",
+ "hclk_pre.%d",
+};
+
+/*
+ * Dividers' tables
+ */
+
+/* phia odf */
+static __initdata struct clk_div_table phia_odf_tab[] = {
+ {
+ .val = 0,
+ .div = 1,
+ },
+ {
+ .val = 1,
+ .div = 2,
+ },
+ {
+ .val = 2,
+ .div = 4,
+ },
+ {
+ .val = 3,
+ .div = 6,
+ },
+ {
+ .val = 4,
+ .div = 8,
+ },
+ {
+ .val = 5,
+ .div = 10,
+ },
+ {
+ .val = 6,
+ .div = 12,
+ },
+ {
+ .val = 7,
+ .div = 14,
+ },
+ /* Terminator */
+ {
+ .div = 0,
+ },
+};
+
+/* phib dividers */
+static __initdata struct clk_div_table phib_div_tab[] = {
+ {
+ .val = 0,
+ .div = 3,
+ },
+ {
+ .val = 1,
+ .div = 5,
+ },
+ /* Terminator */
+ {
+ .div = 0,
+ },
+};
+
+/*
+ * Init functions
+ */
+/*
+ * Init function for soc-pll clock
+ */
+static void __init soc_pll_init(struct sta2x11_clk_data *cptr,
+ struct sta2x11_clk_reg_data *clk_reg_data)
+{
+ cptr->args.soc_pll.base = clk_reg_data->sctl_base;
+ cptr->args.soc_pll.lock = clk_reg_data->sctl_lock;
+}
+
+/*
+ * Init function for audio-pll clock
+ */
+static void __init audio_pll_init(struct sta2x11_clk_data *cptr,
+ struct sta2x11_clk_reg_data *clk_reg_data)
+{
+ cptr->args.audio_pll.base = clk_reg_data->sctl_base;
+ cptr->args.audio_pll.lock = clk_reg_data->sctl_lock;
+}
+
+/*
+ * Init functions for mux clocks
+ */
+static void __init
+sctl_mux_clock_init(struct sta2x11_clk_data *cptr,
+ struct sta2x11_clk_reg_data *clk_reg_data)
+{
+ cptr->args.mux.reg = clk_reg_data->sctl_base + cptr->reg_offset;
+ cptr->args.mux.lock = clk_reg_data->sctl_lock;
+}
+
+/*
+ * Init function for tab divider clock
+ */
+static void __init
+tab_divider_clock_init(struct sta2x11_clk_data *cptr,
+ struct sta2x11_clk_reg_data *clk_reg_data)
+{
+ cptr->args.tab_divider.reg = clk_reg_data->sctl_base +
+ cptr->reg_offset;
+ cptr->args.tab_divider.lock = clk_reg_data->sctl_lock;
+}
+
+/*
+ * This table contains everything is needed to register all the clocks
+ * on a single connext instance
+ *
+ * TODO: this table shall be patched at startup to deal with the (very few
+ * at present) differences between STA2X11 based boards. This will be done
+ * as soon as I know how to deal with platform data.
+ *
+ * TODO: complete this table with all the remaining clocks (mmc, msp, spi, ...)
+ */
+static __initdata struct sta2x11_clk_data clk_data[] = {
+ /* 24MHz refclk */
+ DECLARE_FIXED_RATE_ROOT_CLK(xtal, 24000000),
+ /* Sata clk */
+ DECLARE_FIXED_RATE_ROOT_CLK(sata, 100000000),
+ /* Eth clk */
+ DECLARE_FIXED_RATE_ROOT_CLK(eth, 50000000),
+ /* Soc pll vco */
+ DECLARE_SOC_PLL_CLK(soc_vco, xtal),
+ /* Soc pll vco dividers */
+ DECLARE_TAB_DIVIDER_CLK(soc_phia, SCTL_SCPLLFCTRL,
+ soc_vco, tab_divider_clock_init,
+ 0, 0, 3, phia_odf_tab, 0),
+ DECLARE_TAB_DIVIDER_CLK(soc_phib, SCTL_SCCTL, soc_vco,
+ tab_divider_clock_init,
+ 0, 10, 1, phib_div_tab, 0),
+ DECLARE_MUX_CLK(soc_phi, SCTL_SCCTL, soc_phi_parents,
+ sctl_mux_clock_init, 0, 2, 1, 0),
+ /*
+ * TODO : IMPLEMENT THIS ONE AS A DIFFERENT TYPE OF CLOCK
+ *
+ * We need a mux clock controlled by a custom function:
+ *
+ * soc_phi_byp = soc_phi if pll is locked && !powered down && !bypassed
+ * soc_phi_byp = xtal otherwise
+ *
+ * For now we assume that the soc pll is never bypassed, so we just
+ * use a fixed factor clock to keep the correct names
+ */
+ DECLARE_FIXED_FACTOR_CLK(soc_phi_byp, soc_phi, 0, 1, 1),
+ DECLARE_FIXED_FACTOR_CLK(clk_48M, soc_phi_byp, 0, 1, 13),
+ DECLARE_FIXED_FACTOR_CLK(soc_phi_byp_div3, soc_phi_byp, 0, 1, 3),
+ DECLARE_FIXED_FACTOR_CLK(soc_phi_byp_div4, soc_phi_byp, 0, 1, 4),
+ DECLARE_FIXED_FACTOR_CLK(soc_phi_byp_div6, soc_phi_byp, 0, 1, 6),
+ DECLARE_FIXED_FACTOR_CLK(clk_52M, soc_phi_byp_div6, 0, 1, 2),
+ DECLARE_MUX_CLK(hclk_pre, SCTL_SCCTL, hclk_pre_parents,
+ sctl_mux_clock_init, 0, 0, 2, 0),
+ DECLARE_MUX_CLK(hclk, SCTL_SCCTL, hclk_parents,
+ sctl_mux_clock_init, 0, 9, 1, 0),
+ /* Audio pll derived clocks */
+ DECLARE_AUDIO_PLL_CLK(audio_pll_phi, xtal),
+ DECLARE_FIXED_FACTOR_CLK(audio_pll_phi_div2, audio_pll_phi, 0, 1, 2),
+ DECLARE_FIXED_FACTOR_CLK(audio_pll_phi_div10, audio_pll_phi, 0, 1, 10),
+ DECLARE_FIXED_FACTOR_CLK(audio_pll_phi_div4, audio_pll_phi_div2,
+ 0, 1, 2),
+ DECLARE_MUX_CLK(audio_pll_sdmmc, SCTL_SCCTL, audio_pll_sdmmc_parents,
+ sctl_mux_clock_init, 0, 8, 1, 0),
+ DECLARE_MUX_CLK(audio_pll_msp, SCTL_SCCTL, audio_pll_msp_parents,
+ sctl_mux_clock_init, 0, 8, 1, 0),
+ DECLARE_MUX_CLK(audio_pll_sarac, SCTL_SCCTL, audio_pll_sarac_parents,
+ sctl_mux_clock_init, 0, 8, 1, 0),
+ /* Peripheral clocks for uarts. TODO: implement these as gated clocks */
+ DECLARE_FIXED_FACTOR_CLK(hclk_uart0, hclk, 0, 1, 1),
+ DECLARE_FIXED_FACTOR_CLK(hclk_uart1, hclk, 0, 1, 1),
+ DECLARE_FIXED_FACTOR_CLK(hclk_uart2, hclk, 0, 1, 1),
+ DECLARE_FIXED_FACTOR_CLK(hclk_uart3, hclk, 0, 1, 1),
+ /* Baud rate clocks for uarts. TODO: implement these as gated clocks */
+ DECLARE_FIXED_FACTOR_CLK(bclk_uart0, clk_48M, 0, 1, 1),
+ DECLARE_FIXED_FACTOR_CLK(bclk_uart1, clk_48M, 0, 1, 1),
+ DECLARE_FIXED_FACTOR_CLK(bclk_uart2, clk_48M, 0, 1, 1),
+ DECLARE_FIXED_FACTOR_CLK(bclk_uart3, clk_48M, 0, 1, 1),
+ /* Stmmac clock. TODO: implement this as a mux with sata */
+ DECLARE_FIXED_FACTOR_CLK(stmmac_rmii, eth, 0, 1, 1),
+ DECLARE_FIXED_FACTOR_CLK(stmmac_csr, stmmac_rmii, 0, 1, 2),
+};
+
+
+/*
+ * The following code registers various clock types for the connext
+ */
+
+typedef struct clk *(regfunc)(struct sta2x11_clk_data *, const char *, int);
+
+static __init struct clk *
+do_register_fixed_rate_root(struct sta2x11_clk_data *cptr,
+ const char *name,
+ int instance_id)
+{
+ pr_debug("Registering fixed rate root clock %s, rate = %lu\n",
+ name, cptr->args.fixed_rate_root.rate);
+ return clk_register_fixed_rate(NULL,
+ name,
+ NULL,
+ cptr->flags,
+ cptr->args.fixed_rate_root.rate);
+}
+
+static __init struct clk *
+do_register_fixed_factor(struct sta2x11_clk_data *cptr,
+ const char *name, int instance_id)
+{
+ char parent_name[STA2X11_MAX_CLK_NAME_LEN];
+ snprintf(parent_name, sizeof(parent_name), "%s.%d",
+ cptr->args.fixed_factor.parent_name, instance_id);
+ return clk_register_fixed_factor(NULL, name,
+ parent_name,
+ cptr->flags,
+ cptr->args.fixed_factor.mult,
+ cptr->args.fixed_factor.div);
+}
+
+static __init struct clk *
+do_register_mux(struct sta2x11_clk_data *cptr,
+ const char *name, int instance_id)
+{
+ int i, nparents = cptr->args.mux.num_parents;
+ char *parents[STA2X11_MAX_CLK_NPARENTS], *ptr;
+ char parent_names[STA2X11_MAX_CLK_NPARENTS*STA2X11_MAX_CLK_NAME_LEN];
+ if (nparents > STA2X11_MAX_CLK_NPARENTS)
+ return ERR_PTR(-ENOMEM);
+ for (i = 0, ptr = parent_names; i < nparents; i++,
+ ptr += STA2X11_MAX_CLK_NAME_LEN) {
+ snprintf(ptr, STA2X11_MAX_CLK_NAME_LEN,
+ cptr->args.mux.parent_names[i], instance_id);
+ parents[i] = ptr;
+ }
+ return clk_register_mux(NULL, name, (const char **)parents,
+ cptr->args.mux.num_parents, cptr->flags,
+ cptr->args.mux.reg, cptr->args.mux.shift,
+ cptr->args.mux.width,
+ cptr->args.mux.clk_mux_flags,
+ cptr->args.mux.lock);
+}
+
+static __init struct clk *
+do_register_tab_divider(struct sta2x11_clk_data *cptr,
+ const char *name, int instance_id)
+{
+ char parent_name[STA2X11_MAX_CLK_NAME_LEN];
+ snprintf(parent_name, sizeof(parent_name), "%s.%d",
+ cptr->args.tab_divider.parent_name, instance_id);
+ pr_debug("%s: registering tab_divider clock %s\n", __func__, name);
+ pr_debug("%s: parent = %s\n", __func__, parent_name);
+ return clk_register_divider_table(NULL, name, parent_name,
+ 0, cptr->args.tab_divider.reg,
+ cptr->args.tab_divider.shift,
+ cptr->args.tab_divider.width,
+ 0, cptr->args.tab_divider.div_tab,
+ cptr->args.tab_divider.lock);
+}
+
+static __init struct clk *
+do_register_soc_pll(struct sta2x11_clk_data *cptr,
+ const char *name, int instance_id)
+{
+ char parent_name[STA2X11_MAX_CLK_NAME_LEN];
+ snprintf(parent_name, sizeof(parent_name), "%s.%d",
+ cptr->args.soc_pll.parent_name, instance_id);
+ pr_debug("%s: registering soc_pll clock %s\n", __func__, name);
+ pr_debug("%s: parent = %s\n", __func__, parent_name);
+ return register_sta2x11_clk_soc_pll(name, parent_name,
+ cptr->args.soc_pll.base,
+ cptr->args.soc_pll.lock);
+}
+
+static __init struct clk *
+do_register_audio_pll(struct sta2x11_clk_data *cptr,
+ const char *name, int instance_id)
+{
+ char parent_name[STA2X11_MAX_CLK_NAME_LEN];
+ snprintf(parent_name, sizeof(parent_name), "%s.%d",
+ cptr->args.audio_pll.parent_name, instance_id);
+ pr_debug("%s: registering audio_pll clock %s\n", __func__, name);
+ pr_debug("%s: parent = %s\n", __func__, parent_name);
+ return register_sta2x11_clk_audio_pll(name, parent_name,
+ cptr->args.audio_pll.base,
+ cptr->args.audio_pll.lock);
+}
+
+/*
+ * This function registers all the clocks listed in the clk_data table
+ * Such table is static and can be modified on a per-board basis at startup.
+ */
+static __initdata regfunc * regfuncs[] = {
+ [fixed_rate_root] = do_register_fixed_rate_root,
+ [fixed_factor] = do_register_fixed_factor,
+ [mux] = do_register_mux,
+ [tab_divider] = do_register_tab_divider,
+ [soc_pll] = do_register_soc_pll,
+ [audio_pll] = do_register_audio_pll,
+};
+
+static int __init register_clocks(struct sta2x11_instance *instance,
+ struct sta2x11_clk_reg_data *clk_reg_data)
+{
+ int i, instance_id;
+ struct sta2x11_clk_data *cptr;
+ struct clk *clk, **clks;
+
+ /*
+ * struct sta2x11_instance is opaque at present.
+ */
+ instance_id = sta2x11_get_instance_id(instance);
+
+ /*
+ * When this function is called, kmalloc already works, so we should
+ * have no problem using it
+ */
+ clks = kzalloc(sta2x11_n_clks * sizeof(struct clk *), GFP_KERNEL);
+ if (!clks)
+ return -ENOMEM;
+
+ for (i = 0, cptr = clk_data; i < ARRAY_SIZE(clk_data); i++, cptr++) {
+ /*
+ * name can be on stack, since the clock framework does
+ * kstrdup on register
+ */
+ char name[STA2X11_MAX_CLK_NAME_LEN];
+ if (cptr->type < 0 || cptr->type > sta2x11_clk_ntypes) {
+ pr_err("%s: invalid type %d for clk %s, skipping\n",
+ __func__, cptr->type,
+ cptr->basename ? cptr->basename : "UNKNOWN");
+ continue;
+ }
+ if (cptr->type == none)
+ /* Clock not implemented on this board */
+ continue;
+ if (!regfuncs[cptr->type]) {
+ pr_err("%s : no regfunc for clk %s, skipping\n",
+ __func__, cptr->basename ? cptr->basename :
+ "UNKNOWN");
+ continue;
+ }
+ /*
+ * Set up a clock name by adding an instance id to its
+ * basename
+ */
+ snprintf(name, sizeof(name), "%s.%d", cptr->basename,
+ instance_id);
+ /*
+ * This should add runtime data to the clock. In particular,
+ * it should add the controlling register's virtual address
+ * (which is unknown at compile time)
+ */
+ if (cptr->init)
+ cptr->init(cptr, clk_reg_data);
+ /* Ok, now just register the clock */
+ clk = regfuncs[cptr->type](cptr, name, instance_id);
+ if (IS_ERR(clk)) {
+ pr_err("%s error registering clock %s\n",
+ __func__, name);
+ } else {
+ pr_info("%s: registered clock %s\n", __func__, name);
+ clks[i] = clk;
+ /*
+ A lookup is also added for each of the registered
+ clocks
+ */
+ clk_register_clkdev(clks[i], name, NULL);
+ }
+ }
+ /* Finally assign registered clocks to the instance they belong to */
+ sta2x11_instance_add_clks(instance, clks);
+ return 0;
+}
+
+/*
+ * Notifier routine for platform bus
+ * We basically look for sta2x11-sctl and sta2x11-apb-soc-regs to be
+ * bound to their platform drivers and then ask sta2x11-mfd for a base address
+ * and a spin lock address for each of the two devices. If everyhing is
+ * ok, we register the sta2x11 clocks.
+ */
+static int sta2x11_clk_notifier_fun(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct device *dev = (struct device *)data;
+ struct platform_device *platdev = to_platform_device(dev);
+ const struct mfd_cell *cell = mfd_get_cell(platdev);
+ struct pci_dev *pdev;
+ struct sta2x11_instance *instance;
+ struct sta2x11_clk_reg_data clk_reg_data;
+
+ dev_dbg(dev, "%s entered", __func__);
+
+ /* We are only interested in mfd devices' addition */
+ if (action != BUS_NOTIFY_BOUND_DRIVER) {
+ pr_debug("%s: ignoring action %lu\n", __func__, action);
+ return 0;
+ }
+ if (!cell) {
+ dev_dbg(dev, "%s: ignoring device", __func__);
+ return 0;
+ }
+ if (strcmp(cell->name, STA2X11_MFD_SCTL_NAME) &&
+ strcmp(cell->name, STA2X11_MFD_APB_SOC_REGS_NAME)) {
+ dev_dbg(dev, "%s: ignoring cell %s\n", __func__, cell->name);
+ return 0;
+ }
+ pdev = *(struct pci_dev **)(dev->platform_data);
+ instance = sta2x11_get_instance(pdev);
+ if (sta2x11_mfd_get_regs_data(platdev, sta2x11_sctl,
+ &clk_reg_data.sctl_base,
+ &clk_reg_data.sctl_lock)) {
+ pr_debug("%s : cannot get data on sctl regs\n", __func__);
+ return 0;
+ }
+ if (sta2x11_mfd_get_regs_data(platdev, sta2x11_apb_soc_regs,
+ &clk_reg_data.apb_soc_base,
+ &clk_reg_data.apb_soc_lock)) {
+ pr_debug("%s : cannot get data on apb_soc_regs\n", __func__);
+ return 0;
+ }
+ /* Ok, all mfd devices are up and running, we can now register clocks */
+ register_clocks(instance, &clk_reg_data);
+ return 0;
+}
+
+static struct notifier_block sta2x11_clk_notifier = {
+ .notifier_call = sta2x11_clk_notifier_fun
+};
+
+
+/*
+ * This function just registers a notifier for the platform bus to know
+ * when the sta2x11-sctl and sta2x11-apb-soc-regs devices have been bound
+ * to the relevant drivers
+ */
+static int __init sta2x11_init_clocks(void)
+{
+ pr_debug("%s : registering notifier\n", __func__);
+ if (bus_register_notifier(&platform_bus_type, &sta2x11_clk_notifier))
+ pr_err("%s: error registering platform bus notifier\n",
+ __func__);
+ return 0;
+}
+subsys_initcall(sta2x11_init_clocks);
+
+/*
+ * This is invoked on pci_enable() for every connext pci device
+ *
+ * It registers a lookup for the device's clock (if needed), so that
+ * the driver can find it.
+ */
+static void clk_new_pdev(struct pci_dev *pdev)
+{
+ struct sta2x11_instance *instance = sta2x11_get_instance(pdev);
+ int instance_id;
+ /* Initialize with an invalid index */
+ enum sta2x11_clk clk_index = sta2x11_n_clks;
+ struct clk **clks;
+ char *name;
+ if (!instance)
+ /*
+ * Just ignore devices not belonging to the connext chip
+ */
+ return;
+ name = devm_kzalloc(&pdev->dev, strlen(dev_name(&pdev->dev)) + 6,
+ GFP_KERNEL);
+ instance_id = sta2x11_get_instance_id(instance);
+
+ clks = sta2x11_instance_get_clks(instance);
+ switch (pdev->device) {
+ case PCI_DEVICE_ID_STMICRO_UART_HWFC:
+ clk_index = pdev->devfn == 5 ? bclk_uart2 : bclk_uart3;
+ /* FALL THROUGH */
+ case PCI_DEVICE_ID_STMICRO_UART_NO_HWFC:
+ if (clk_index == sta2x11_n_clks)
+ clk_index = pdev->devfn == 5 ? bclk_uart0 : bclk_uart1;
+ sprintf(name, "amba-%s", dev_name(&pdev->dev));
+ clk_register_clkdev(clks[clk_index], NULL, name,
+ pdev->bus->number, pdev->devfn);
+ break;
+ case PCI_DEVICE_ID_STMICRO_MAC:
+ clk_register_clkdev(clks[stmmac_csr], NULL,
+ dev_name(&pdev->dev));
+ break;
+ /* TODO : ADD MORE ID's HERE */
+ default:
+ devm_kfree(&pdev->dev, name);
+ dev_dbg(&pdev->dev, "clk: ignoring device\n");
+ break;
+ }
+}
+DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_STMICRO, PCI_ANY_ID, clk_new_pdev);
diff --git a/drivers/clk/sta2x11/clk.h b/drivers/clk/sta2x11/clk.h
new file mode 100644
index 0000000..87a422c
--- /dev/null
+++ b/drivers/clk/sta2x11/clk.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright ST Microelectronics 2012
+ * Author: Davide Ciminaghi <ciminaghi@xxxxxxxxx>
+ *
+ * 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 in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Common clock api implementation for sta2x11, main header file
+ */
+#ifndef __STA2X11_CLK_H__
+#define __STA2X11_CLK_H__
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/spinlock.h>
+
+/*
+ * Indices for connext clocks
+ */
+enum sta2x11_clk {
+ xtal, sata, pcie, sdvo, eth, usb,
+ soc_vco, soc_phia, soc_phib, audio_pll_phi,
+ soc_phi, soc_phi_byp,
+ soc_phi_byp_div3, soc_phi_byp_div4, soc_phi_byp_div6,
+ audio_pll_phi_div2, audio_pll_phi_div4, audio_pll_phi_div10,
+ audio_pll_msp, audio_pll_sarac, audio_pll_sdmmc,
+ hclk_pre, hclk, clk_48M, clk_52M, sdmmc,
+ /* Uarts' peripheral clocks */
+ hclk_uart0, hclk_uart1, hclk_uart2, hclk_uart3,
+ /* Uarts' baud rate clocks */
+ bclk_uart0, bclk_uart1, bclk_uart2, bclk_uart3,
+ stmmac_csr, stmmac_rmii,
+ sta2x11_n_clks,
+};
+
+/*
+ * Clock types used on the connext
+ *
+ * By convention, a clock listed in clk_data[] with type == none, is not
+ * registered (usually because the clock itself is not active on the
+ * board the kernel is being run on).
+ */
+enum sta2x11_clk_type {
+ /* Not present on this board */
+ none = 0,
+ fixed_rate_root = 1,
+ fixed_factor = 2,
+ mux = 3,
+ tab_divider = 4,
+ soc_pll = 5,
+ audio_pll = 6,
+ sta2x11_clk_ntypes,
+};
+
+struct clk *register_sta2x11_clk_soc_pll(const char *name,
+ const char *parent_name,
+ void __iomem *reg,
+ spinlock_t *lock);
+struct clk *register_sta2x11_clk_audio_pll(const char *name,
+ const char *parent_name,
+ void __iomem *base,
+ spinlock_t *lock);
+#endif /* __STA2X11_CLK_H__ */
--
1.7.10.4
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/