Hi,
this patchset is the first implementation of the common clock framework for
the STA2X11 (aka connext) chip.
Actually 1/3 and 2/3 are not strictly relevant to the clock framework, but
they're needed to get the rest of the code to compile and have already
been submitted some months ago (see https://lkml.org/lkml/2012/9/20/363)
and acked by Alessandro Rubini (see https://lkml.org/lkml/2012/9/20/452).
3/3 has been submitted RFC on Jun 20th 2012
(see https://lkml.org/lkml/2012/6/20/779).
Mike Turquette reviewed it (see https://lkml.org/lkml/2012/8/23/624) and
his comments have been taken into account for this submission.
Please note that this patchset applies to next-20130307, should build cleanly
and do no harm at runtime, but it is not functional because it needs the
pci-to-amba bridge patch by Alessandro Rubini, which has already been
submitted several times but has not made it in the linux-next tree
yet (see thread https://lkml.org/lkml/2013/2/20/561 for more details).
Some clocks still have to be added and some others need to be implemented
with different types (see the TODOs in the code), but the current
implementation is actually enough to get a Connext based board up and running
(with its UARTs working at least), of course provided that the pci to
amba bridge is there. Multiple instances of the Connext chip should also be
supported, even if no test has been done up to now.
Thanks and regards
Davide
Davide Ciminaghi (3):
x86, pci sta2x11-fixup: add clk related field to struct
sta2x11_instance
x86, pci sta2x11-fixup: add function to access sta2x11 instance id
drivers/clk: sta2x11 common clock framework implementation
arch/x86/Kconfig | 1 +
arch/x86/include/asm/sta2x11.h | 16 +
arch/x86/pci/sta2x11-fixup.c | 20 +
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 ++++
9 files changed, 1140 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
--
1.7.10.4
Signed-off-by: Davide Ciminaghi <[email protected]>
---
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 <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * 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
The sta2x11 instance id will be included in clock names to make them
unique in case of multiple sta2x11's living on the same machine.
Signed-off-by: Davide Ciminaghi <[email protected]>
---
arch/x86/pci/sta2x11-fixup.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/arch/x86/pci/sta2x11-fixup.c b/arch/x86/pci/sta2x11-fixup.c
index 478c228..6450853 100644
--- a/arch/x86/pci/sta2x11-fixup.c
+++ b/arch/x86/pci/sta2x11-fixup.c
@@ -138,6 +138,12 @@ struct sta2x11_instance *sta2x11_get_instance(struct pci_dev *pdev)
}
EXPORT_SYMBOL(sta2x11_get_instance);
+int sta2x11_get_instance_id(struct sta2x11_instance *instance)
+{
+ return instance->bus0;
+}
+EXPORT_SYMBOL(sta2x11_get_instance_id);
+
/**
* p2a - Translate physical address to STA2x11 AMBA address,
--
1.7.10.4
Use struct sta2x11_instance for storing a pointer to an array
of struct clk pointers representing the set of clocks belonging
to a single connext chip.
Since sta2x11_instance is an opaque type, we also add functions to
access the new field.
Signed-off-by: Davide Ciminaghi <[email protected]>
---
arch/x86/include/asm/sta2x11.h | 16 ++++++++++++++++
arch/x86/pci/sta2x11-fixup.c | 14 ++++++++++++++
2 files changed, 30 insertions(+)
diff --git a/arch/x86/include/asm/sta2x11.h b/arch/x86/include/asm/sta2x11.h
index e9d32df..2b5d8bc 100644
--- a/arch/x86/include/asm/sta2x11.h
+++ b/arch/x86/include/asm/sta2x11.h
@@ -5,8 +5,24 @@
#define __ASM_STA2X11_H
#include <linux/pci.h>
+#include <linux/clk.h>
/* This needs to be called from the MFD to configure its sub-devices */
struct sta2x11_instance *sta2x11_get_instance(struct pci_dev *pdev);
+/*
+ * Return instance id
+ */
+int sta2x11_get_instance_id(struct sta2x11_instance *instance);
+
+/*
+ * Add clks array to instance. This is called by clock common API
+ */
+void sta2x11_instance_add_clks(struct sta2x11_instance *, struct clk **);
+
+/*
+ * Returns clks array of instance. This is called by clock common API
+ */
+struct clk **sta2x11_instance_get_clks(struct sta2x11_instance *);
+
#endif /* __ASM_STA2X11_H */
diff --git a/arch/x86/pci/sta2x11-fixup.c b/arch/x86/pci/sta2x11-fixup.c
index 9d8a509..478c228 100644
--- a/arch/x86/pci/sta2x11-fixup.c
+++ b/arch/x86/pci/sta2x11-fixup.c
@@ -27,6 +27,8 @@
#include <linux/export.h>
#include <linux/list.h>
+#include <asm/sta2x11.h>
+
#define STA2X11_SWIOTLB_SIZE (4*1024*1024)
extern int swiotlb_late_init_with_default_size(size_t default_size);
@@ -52,6 +54,7 @@ struct sta2x11_instance {
struct list_head list;
int bus0;
struct sta2x11_mapping map[STA2X11_NR_EP];
+ struct clk **clks;
};
static LIST_HEAD(sta2x11_instance_list);
@@ -78,6 +81,17 @@ static void sta2x11_new_instance(struct pci_dev *pdev)
}
DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_STMICRO, 0xcc17, sta2x11_new_instance);
+void sta2x11_instance_add_clks(struct sta2x11_instance *instance,
+ struct clk **clks)
+{
+ instance->clks = clks;
+}
+
+struct clk **sta2x11_instance_get_clks(struct sta2x11_instance *instance)
+{
+ return instance->clks;
+}
+
/*
* Utility functions used in this file from below
*/
--
1.7.10.4
On Friday 08 March 2013, Davide Ciminaghi wrote:
>
> The sta2x11 instance id will be included in clock names to make them
> unique in case of multiple sta2x11's living on the same machine.
>
> Signed-off-by: Davide Ciminaghi <[email protected]>
I might be missing something, but this seems counterintuitive. Shouldn't
the clock names really be constant and independent of the instance?
The instance should be identified already by the dev_name, right?
Arnd
On Fri, Mar 08, 2013 at 04:13:34PM +0000, Arnd Bergmann wrote:
> On Friday 08 March 2013, Davide Ciminaghi wrote:
> >
> > The sta2x11 instance id will be included in clock names to make them
> > unique in case of multiple sta2x11's living on the same machine.
> >
> > Signed-off-by: Davide Ciminaghi <[email protected]>
>
> I might be missing something, but this seems counterintuitive. Shouldn't
> the clock names really be constant and independent of the instance?
>
> The instance should be identified already by the dev_name, right?
>
Hi,
sorry for the delay.
Well, I actually wrote this some months ago, so I might also be missing
something (yes, I know I shouldn't :-).
If I understand well, I should call clk_register_* with dev pointing to
the struct device corresponding to the sta2x11 instance to which the clock
belongs and a constant name (while currently I set dev to NULL and use a
different name for each instance of the same clock, by postfixing the name
with an instance id).
You're probably right, even if looking at the code in drivers/clk/clk.c,
there's something I'm not sure about (and I cannot do any tests right now):
_clk_register() takes hw->init->name as clock name, where hw->init->name
should come from the second arg of clk_register_*, if I'm not wrong.
Then __clk_init does:
if (__clk_lookup(clk->name)) {
pr_debug("%s: clk %s already initialized\n", __func__, clk->name);
ret = -EEXIST;
goto out;
}
Is it actually possible registering multiple clocks with the same name ?
Sorry maybe I missed your point ...
Thanks and regards
Davide