lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Fri,  8 Mar 2013 16:31:31 +0100
From:	Davide Ciminaghi <ciminaghi@...dd.com>
To:	tglx@...utronix.de, mingo@...hat.com, hpa@...or.com,
	x86@...nel.org, rubini@...dd.com, sameo@...ux.intel.com,
	giancarlo.asnaghi@...com, mturquette@...aro.org, arnd@...db.de,
	linus.walleij@...aro.org, rob.herring@...xeda.com,
	broonie@...nsource.wolfsonmicro.com
Cc:	linux-kernel@...r.kernel.org
Subject: [PATCH 3/3] drivers/clk: sta2x11 common clock framework implementation

Signed-off-by: Davide Ciminaghi <ciminaghi@...dd.com>
---
 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@...dd.com>
+ *
+ * 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@...dd.com>
+ *
+ * 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@...dd.com>
+ *
+ * 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@...dd.com>
+ *
+ * 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@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ