* [PATCH v8]: clk: Add common clock support for Mediatek MT8135 and MT8173 @ 2015-03-19 8:42 Sascha Hauer 2015-03-19 8:42 ` [PATCH 1/6] clk: make strings in parent name arrays const Sascha Hauer ` (6 more replies) 0 siblings, 7 replies; 22+ messages in thread From: Sascha Hauer @ 2015-03-19 8:42 UTC (permalink / raw) To: linux-arm-kernel The following changes since commit 9eccca0843205f87c00404b663188b88eb248051: Linux 4.0-rc3 (2015-03-08 16:09:09 -0700) are available in the git repository at: git://git.pengutronix.de/git/imx/linux-2.6.git tags/v4.0-clk-mediatek-v8 for you to fetch changes up to 54449c74078c9d93619757c7a91b820eaa28c0f4: dt-bindings: ARM: Mediatek: Document devicetree bindings for clock/reset controllers (2015-03-19 09:15:33 +0100) ---------------------------------------------------------------- This patchset contains the initial common clock support for Mediatek SoCs. Mediatek SoC's clock architecture comprises of various PLLs, dividers, muxes and clock gates. Changes in v8: - add patch to allow to put parent_name arrays in __initconst - put parent_name arrays into __initconst Changes in v7: - fix duplicate definition/declaration of mtk_register_reset_controller - fix pd_reg offset of tvdpll - make clk initialization arrays const Changes in v6: - rework PLL support, only a fraction of original size now - Move binding docs to Documentation/devicetree/bindings/arm/mediatek since the units are not really clock specific (they contain reset controllers) Changes in v5: - Add reset controller support for pericfg/infracfg - Use regmap for the gates - remove now unnecessary spinlock for the gates - Add PMIC wrapper support as of v3 Changes in v4: - Support MT8173 platform. - Re-ordered patchset. driver/clk/Makefile in 2nd patch. - Extract the common part definition(mtk_gate/mtk_pll/mtk_mux) from clk-mt8135.c/clk-mt8173.c to clk-mtk.c. - Refine code. Rmove unnessacary debug information and unsed defines, add prefix "mtk_" for static functions. - Remove flag CLK_IGNORE_UNUSED and set flag CLK_SET_RATE_PARENT on gate/mux/fixed-factor. - Use spin_lock_irqsave(&clk_ops_lock, flags) instead of mtk_clk_lock. - Example above include a node for the clock controller itself, followed by the i2c controller example above. Changes in v3: - Rebase to 3.19-rc1. - Refine code. Remove unneed functions, debug logs and comments, and fine tune error logs. Changes in v2: - Re-ordered patchset. Fold include/dt-bindings and DT document in 1st patch. ---------------------------------------------------------------- James Liao (3): clk: mediatek: Add initial common clock support for Mediatek SoCs. clk: mediatek: Add basic clocks for Mediatek MT8135. clk: mediatek: Add basic clocks for Mediatek MT8173. Sascha Hauer (3): clk: make strings in parent name arrays const clk: mediatek: Add reset controller support dt-bindings: ARM: Mediatek: Document devicetree bindings for clock/reset controllers .../bindings/arm/mediatek/mediatek,apmixedsys.txt | 23 + .../bindings/arm/mediatek/mediatek,infracfg.txt | 30 + .../bindings/arm/mediatek/mediatek,pericfg.txt | 30 + .../bindings/arm/mediatek/mediatek,topckgen.txt | 23 + drivers/clk/Makefile | 1 + drivers/clk/clk-composite.c | 2 +- drivers/clk/clk-mux.c | 4 +- drivers/clk/mediatek/Makefile | 4 + drivers/clk/mediatek/clk-gate.c | 137 ++++ drivers/clk/mediatek/clk-gate.h | 49 ++ drivers/clk/mediatek/clk-mt8135.c | 640 ++++++++++++++++ drivers/clk/mediatek/clk-mt8173.c | 826 +++++++++++++++++++++ drivers/clk/mediatek/clk-mtk.c | 197 +++++ drivers/clk/mediatek/clk-mtk.h | 165 ++++ drivers/clk/mediatek/clk-pll.c | 325 ++++++++ drivers/clk/mediatek/reset.c | 99 +++ include/dt-bindings/clock/mt8135-clk.h | 190 +++++ include/dt-bindings/clock/mt8173-clk.h | 231 ++++++ .../dt-bindings/reset-controller/mt8135-resets.h | 64 ++ .../dt-bindings/reset-controller/mt8173-resets.h | 63 ++ include/linux/clk-provider.h | 8 +- 21 files changed, 3104 insertions(+), 7 deletions(-) create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt create mode 100644 drivers/clk/mediatek/Makefile create mode 100644 drivers/clk/mediatek/clk-gate.c create mode 100644 drivers/clk/mediatek/clk-gate.h create mode 100644 drivers/clk/mediatek/clk-mt8135.c create mode 100644 drivers/clk/mediatek/clk-mt8173.c create mode 100644 drivers/clk/mediatek/clk-mtk.c create mode 100644 drivers/clk/mediatek/clk-mtk.h create mode 100644 drivers/clk/mediatek/clk-pll.c create mode 100644 drivers/clk/mediatek/reset.c create mode 100644 include/dt-bindings/clock/mt8135-clk.h create mode 100644 include/dt-bindings/clock/mt8173-clk.h create mode 100644 include/dt-bindings/reset-controller/mt8135-resets.h create mode 100644 include/dt-bindings/reset-controller/mt8173-resets.h ^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 1/6] clk: make strings in parent name arrays const 2015-03-19 8:42 [PATCH v8]: clk: Add common clock support for Mediatek MT8135 and MT8173 Sascha Hauer @ 2015-03-19 8:42 ` Sascha Hauer 2015-03-19 8:48 ` Uwe Kleine-König 2015-03-19 8:42 ` [PATCH 2/6] clk: mediatek: Add initial common clock support for Mediatek SoCs Sascha Hauer ` (5 subsequent siblings) 6 siblings, 1 reply; 22+ messages in thread From: Sascha Hauer @ 2015-03-19 8:42 UTC (permalink / raw) To: linux-arm-kernel The clk functions and structs declare the parent_name arrays as 'const char **parent_names' which means the parent name strings are const, but the array itself is not. Use 'const char * const * parent_names' instead which also makes the array const. This allows us to put the parent_name arrays into the __initconst section. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- drivers/clk/clk-composite.c | 2 +- drivers/clk/clk-mux.c | 4 ++-- include/linux/clk-provider.h | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c index 956b7e5..077f4c714 100644 --- a/drivers/clk/clk-composite.c +++ b/drivers/clk/clk-composite.c @@ -188,7 +188,7 @@ static void clk_composite_disable(struct clk_hw *hw) } struct clk *clk_register_composite(struct device *dev, const char *name, - const char **parent_names, int num_parents, + const char * const *parent_names, int num_parents, struct clk_hw *mux_hw, const struct clk_ops *mux_ops, struct clk_hw *rate_hw, const struct clk_ops *rate_ops, struct clk_hw *gate_hw, const struct clk_ops *gate_ops, diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index 69a094c..1fa2a8d 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -114,7 +114,7 @@ const struct clk_ops clk_mux_ro_ops = { EXPORT_SYMBOL_GPL(clk_mux_ro_ops); struct clk *clk_register_mux_table(struct device *dev, const char *name, - const char **parent_names, u8 num_parents, unsigned long flags, + const char * const *parent_names, u8 num_parents, unsigned long flags, void __iomem *reg, u8 shift, u32 mask, u8 clk_mux_flags, u32 *table, spinlock_t *lock) { @@ -166,7 +166,7 @@ struct clk *clk_register_mux_table(struct device *dev, const char *name, EXPORT_SYMBOL_GPL(clk_register_mux_table); struct clk *clk_register_mux(struct device *dev, const char *name, - const char **parent_names, u8 num_parents, unsigned long flags, + const char * const *parent_names, u8 num_parents, unsigned long flags, void __iomem *reg, u8 shift, u8 width, u8 clk_mux_flags, spinlock_t *lock) { diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 5591ea7..410684d 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -209,7 +209,7 @@ struct clk_ops { struct clk_init_data { const char *name; const struct clk_ops *ops; - const char **parent_names; + const char * const *parent_names; u8 num_parents; unsigned long flags; }; @@ -426,12 +426,12 @@ extern const struct clk_ops clk_mux_ops; extern const struct clk_ops clk_mux_ro_ops; struct clk *clk_register_mux(struct device *dev, const char *name, - const char **parent_names, u8 num_parents, unsigned long flags, + const char * const *parent_names, u8 num_parents, unsigned long flags, void __iomem *reg, u8 shift, u8 width, u8 clk_mux_flags, spinlock_t *lock); struct clk *clk_register_mux_table(struct device *dev, const char *name, - const char **parent_names, u8 num_parents, unsigned long flags, + const char * const *parent_names, u8 num_parents, unsigned long flags, void __iomem *reg, u8 shift, u32 mask, u8 clk_mux_flags, u32 *table, spinlock_t *lock); @@ -518,7 +518,7 @@ struct clk_composite { }; struct clk *clk_register_composite(struct device *dev, const char *name, - const char **parent_names, int num_parents, + const char * const *parent_names, int num_parents, struct clk_hw *mux_hw, const struct clk_ops *mux_ops, struct clk_hw *rate_hw, const struct clk_ops *rate_ops, struct clk_hw *gate_hw, const struct clk_ops *gate_ops, -- 2.1.4 ^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 1/6] clk: make strings in parent name arrays const 2015-03-19 8:42 ` [PATCH 1/6] clk: make strings in parent name arrays const Sascha Hauer @ 2015-03-19 8:48 ` Uwe Kleine-König 0 siblings, 0 replies; 22+ messages in thread From: Uwe Kleine-König @ 2015-03-19 8:48 UTC (permalink / raw) To: linux-arm-kernel Hello, On Thu, Mar 19, 2015 at 09:42:05AM +0100, Sascha Hauer wrote: > The clk functions and structs declare the parent_name arrays as > 'const char **parent_names' which means the parent name strings > are const, but the array itself is not. Use > 'const char * const * parent_names' instead which also makes > the array const. This allows us to put the parent_name arrays into > the __initconst section. > > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> looks sensible: Acked-by: Uwe Kleine-K?nig <u.kleine-koenig@pengutronix.de> It would be nice to have this for more clock types (which of course shouldn't delay this patch). Best regards Uwe -- Pengutronix e.K. | Uwe Kleine-K?nig | Industrial Linux Solutions | http://www.pengutronix.de/ | ^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 2/6] clk: mediatek: Add initial common clock support for Mediatek SoCs. 2015-03-19 8:42 [PATCH v8]: clk: Add common clock support for Mediatek MT8135 and MT8173 Sascha Hauer 2015-03-19 8:42 ` [PATCH 1/6] clk: make strings in parent name arrays const Sascha Hauer @ 2015-03-19 8:42 ` Sascha Hauer 2015-03-19 8:42 ` [PATCH 3/6] clk: mediatek: Add reset controller support Sascha Hauer ` (4 subsequent siblings) 6 siblings, 0 replies; 22+ messages in thread From: Sascha Hauer @ 2015-03-19 8:42 UTC (permalink / raw) To: linux-arm-kernel From: James Liao <jamesjj.liao@mediatek.com> This patch adds common clock support for Mediatek SoCs, including plls, muxes and clock gates. Signed-off-by: James Liao <jamesjj.liao@mediatek.com> Signed-off-by: Henry Chen <henryc.chen@mediatek.com> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- drivers/clk/Makefile | 1 + drivers/clk/mediatek/Makefile | 1 + drivers/clk/mediatek/clk-gate.c | 137 +++++++++++++++++ drivers/clk/mediatek/clk-gate.h | 49 ++++++ drivers/clk/mediatek/clk-mtk.c | 197 ++++++++++++++++++++++++ drivers/clk/mediatek/clk-mtk.h | 155 +++++++++++++++++++ drivers/clk/mediatek/clk-pll.c | 325 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 865 insertions(+) create mode 100644 drivers/clk/mediatek/Makefile create mode 100644 drivers/clk/mediatek/clk-gate.c create mode 100644 drivers/clk/mediatek/clk-gate.h create mode 100644 drivers/clk/mediatek/clk-mtk.c create mode 100644 drivers/clk/mediatek/clk-mtk.h create mode 100644 drivers/clk/mediatek/clk-pll.c diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index d478ceb..6d97203 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -49,6 +49,7 @@ obj-$(CONFIG_ARCH_HI3xxx) += hisilicon/ obj-$(CONFIG_ARCH_HIP04) += hisilicon/ obj-$(CONFIG_ARCH_HIX5HD2) += hisilicon/ obj-$(CONFIG_COMMON_CLK_KEYSTONE) += keystone/ +obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/ ifeq ($(CONFIG_COMMON_CLK), y) obj-$(CONFIG_ARCH_MMP) += mmp/ endif diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile new file mode 100644 index 0000000..c384e97 --- /dev/null +++ b/drivers/clk/mediatek/Makefile @@ -0,0 +1 @@ +obj-y += clk-mtk.o clk-pll.o clk-gate.o diff --git a/drivers/clk/mediatek/clk-gate.c b/drivers/clk/mediatek/clk-gate.c new file mode 100644 index 0000000..9d77ee3 --- /dev/null +++ b/drivers/clk/mediatek/clk-gate.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao <jamesjj.liao@mediatek.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. + */ + +#include <linux/of.h> +#include <linux/of_address.h> + +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/clkdev.h> + +#include "clk-mtk.h" +#include "clk-gate.h" + +static int mtk_cg_bit_is_cleared(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_clk_gate(hw); + u32 val; + + regmap_read(cg->regmap, cg->sta_ofs, &val); + + val &= BIT(cg->bit); + + return val == 0; +} + +static int mtk_cg_bit_is_set(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_clk_gate(hw); + u32 val; + + regmap_read(cg->regmap, cg->sta_ofs, &val); + + val &= BIT(cg->bit); + + return val != 0; +} + +static void mtk_cg_set_bit(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_clk_gate(hw); + + regmap_write(cg->regmap, cg->set_ofs, BIT(cg->bit)); +} + +static void mtk_cg_clr_bit(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_clk_gate(hw); + + regmap_write(cg->regmap, cg->clr_ofs, BIT(cg->bit)); +} + +static int mtk_cg_enable(struct clk_hw *hw) +{ + mtk_cg_clr_bit(hw); + + return 0; +} + +static void mtk_cg_disable(struct clk_hw *hw) +{ + mtk_cg_set_bit(hw); +} + +static int mtk_cg_enable_inv(struct clk_hw *hw) +{ + mtk_cg_set_bit(hw); + + return 0; +} + +static void mtk_cg_disable_inv(struct clk_hw *hw) +{ + mtk_cg_clr_bit(hw); +} + +const struct clk_ops mtk_clk_gate_ops_setclr = { + .is_enabled = mtk_cg_bit_is_cleared, + .enable = mtk_cg_enable, + .disable = mtk_cg_disable, +}; + +const struct clk_ops mtk_clk_gate_ops_setclr_inv = { + .is_enabled = mtk_cg_bit_is_set, + .enable = mtk_cg_enable_inv, + .disable = mtk_cg_disable_inv, +}; + +struct clk *mtk_clk_register_gate( + const char *name, + const char *parent_name, + struct regmap *regmap, + int set_ofs, + int clr_ofs, + int sta_ofs, + u8 bit, + const struct clk_ops *ops) +{ + struct mtk_clk_gate *cg; + struct clk *clk; + struct clk_init_data init; + + cg = kzalloc(sizeof(*cg), GFP_KERNEL); + if (!cg) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + init.ops = ops; + + cg->regmap = regmap; + cg->set_ofs = set_ofs; + cg->clr_ofs = clr_ofs; + cg->sta_ofs = sta_ofs; + cg->bit = bit; + + cg->hw.init = &init; + + clk = clk_register(NULL, &cg->hw); + if (IS_ERR(clk)) + kfree(cg); + + return clk; +} diff --git a/drivers/clk/mediatek/clk-gate.h b/drivers/clk/mediatek/clk-gate.h new file mode 100644 index 0000000..6b6780b --- /dev/null +++ b/drivers/clk/mediatek/clk-gate.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao <jamesjj.liao@mediatek.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. + */ + +#ifndef __DRV_CLK_GATE_H +#define __DRV_CLK_GATE_H + +#include <linux/regmap.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> + +struct mtk_clk_gate { + struct clk_hw hw; + struct regmap *regmap; + int set_ofs; + int clr_ofs; + int sta_ofs; + u8 bit; +}; + +static inline struct mtk_clk_gate *to_clk_gate(struct clk_hw *hw) +{ + return container_of(hw, struct mtk_clk_gate, hw); +} + +extern const struct clk_ops mtk_clk_gate_ops_setclr; +extern const struct clk_ops mtk_clk_gate_ops_setclr_inv; + +struct clk *mtk_clk_register_gate( + const char *name, + const char *parent_name, + struct regmap *regmap, + int set_ofs, + int clr_ofs, + int sta_ofs, + u8 bit, + const struct clk_ops *ops); + +#endif /* __DRV_CLK_GATE_H */ diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c new file mode 100644 index 0000000..a0c9f43 --- /dev/null +++ b/drivers/clk/mediatek/clk-mtk.c @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao <jamesjj.liao@mediatek.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. + */ + +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/clkdev.h> +#include <linux/mfd/syscon.h> + +#include "clk-mtk.h" +#include "clk-gate.h" + +struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num) +{ + int i; + struct clk_onecell_data *clk_data; + + clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); + if (!clk_data) + return NULL; + + clk_data->clks = kcalloc(clk_num, sizeof(*clk_data->clks), GFP_KERNEL); + if (!clk_data->clks) + goto err_out; + + clk_data->clk_num = clk_num; + + for (i = 0; i < clk_num; ++i) + clk_data->clks[i] = ERR_PTR(-ENOENT); + + return clk_data; +err_out: + kfree(clk_data); + + return NULL; +} + +void mtk_clk_register_factors(const struct mtk_fixed_factor *clks, int num, + struct clk_onecell_data *clk_data) +{ + int i; + struct clk *clk; + + for (i = 0; i < num; i++) { + const struct mtk_fixed_factor *ff = &clks[i]; + + clk = clk_register_fixed_factor(NULL, ff->name, ff->parent_name, + CLK_SET_RATE_PARENT, ff->mult, ff->div); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + ff->name, PTR_ERR(clk)); + continue; + } + + if (clk_data) + clk_data->clks[ff->id] = clk; + } +} + +int mtk_clk_register_gates(struct device_node *node, const struct mtk_gate *clks, + int num, struct clk_onecell_data *clk_data) +{ + int i; + struct clk *clk; + struct regmap *regmap; + + if (!clk_data) + return -ENOMEM; + + regmap = syscon_node_to_regmap(node); + if (IS_ERR(regmap)) { + pr_err("Cannot find regmap for %s: %ld\n", node->full_name, + PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + for (i = 0; i < num; i++) { + const struct mtk_gate *gate = &clks[i]; + + clk = mtk_clk_register_gate(gate->name, gate->parent_name, + regmap, + gate->regs->set_ofs, + gate->regs->clr_ofs, + gate->regs->sta_ofs, + gate->shift, gate->ops); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + gate->name, PTR_ERR(clk)); + continue; + } + + clk_data->clks[gate->id] = clk; + } + + return 0; +} + +struct clk *mtk_clk_register_composite(const struct mtk_composite *mc, + void __iomem *base, spinlock_t *lock) +{ + struct clk *clk; + struct clk_mux *mux = NULL; + struct clk_gate *gate = NULL; + struct clk_divider *div = NULL; + struct clk_hw *mux_hw = NULL, *gate_hw = NULL, *div_hw = NULL; + const struct clk_ops *mux_ops = NULL, *gate_ops = NULL, *div_ops = NULL; + const char * const *parent_names; + const char *parent; + int num_parents; + int ret; + + if (mc->mux_shift >= 0) { + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) + return ERR_PTR(-ENOMEM); + + mux->reg = base + mc->mux_reg; + mux->mask = BIT(mc->mux_width) - 1; + mux->shift = mc->mux_shift; + mux->lock = lock; + + mux_hw = &mux->hw; + mux_ops = &clk_mux_ops; + + parent_names = mc->parent_names; + num_parents = mc->num_parents; + } else { + parent = mc->parent; + parent_names = &parent; + num_parents = 1; + } + + if (mc->gate_shift >= 0) { + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) { + ret = -ENOMEM; + goto err_out; + } + + gate->reg = base + mc->gate_reg; + gate->bit_idx = mc->gate_shift; + gate->flags = CLK_GATE_SET_TO_DISABLE; + gate->lock = lock; + + gate_hw = &gate->hw; + gate_ops = &clk_gate_ops; + } + + if (mc->divider_shift >= 0) { + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) { + ret = -ENOMEM; + goto err_out; + } + + div->reg = base + mc->divider_reg; + div->shift = mc->divider_shift; + div->width = mc->divider_width; + div->lock = lock; + + div_hw = &div->hw; + div_ops = &clk_divider_ops; + } + + clk = clk_register_composite(NULL, mc->name, parent_names, num_parents, + mux_hw, mux_ops, + div_hw, div_ops, + gate_hw, gate_ops, + mc->flags); + + if (IS_ERR(clk)) { + kfree(gate); + kfree(mux); + } + + return clk; +err_out: + kfree(mux); + + return ERR_PTR(ret); +} diff --git a/drivers/clk/mediatek/clk-mtk.h b/drivers/clk/mediatek/clk-mtk.h new file mode 100644 index 0000000..5aaba81 --- /dev/null +++ b/drivers/clk/mediatek/clk-mtk.h @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao <jamesjj.liao@mediatek.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. + */ + +#ifndef __DRV_CLK_MTK_H +#define __DRV_CLK_MTK_H + +#include <linux/regmap.h> +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> + +#define MAX_MUX_GATE_BIT 31 +#define INVALID_MUX_GATE_BIT (MAX_MUX_GATE_BIT + 1) + +#define MHZ (1000 * 1000) + +struct mtk_fixed_factor { + int id; + const char *name; + const char *parent_name; + int mult; + int div; +}; + +#define FACTOR(_id, _name, _parent, _mult, _div) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .mult = _mult, \ + .div = _div, \ + } + +extern void mtk_clk_register_factors(const struct mtk_fixed_factor *clks, + int num, struct clk_onecell_data *clk_data); + +struct mtk_composite { + int id; + const char *name; + const char * const * parent_names; + const char *parent; + unsigned flags; + + uint32_t mux_reg; + uint32_t divider_reg; + uint32_t gate_reg; + + signed char mux_shift; + signed char mux_width; + signed char gate_shift; + + signed char divider_shift; + signed char divider_width; + + signed char num_parents; +}; + +#define MUX_GATE(_id, _name, _parents, _reg, _shift, _width, _gate) { \ + .id = _id, \ + .name = _name, \ + .mux_reg = _reg, \ + .mux_shift = _shift, \ + .mux_width = _width, \ + .gate_reg = _reg, \ + .gate_shift = _gate, \ + .divider_shift = -1, \ + .parent_names = _parents, \ + .num_parents = ARRAY_SIZE(_parents), \ + .flags = CLK_SET_RATE_PARENT, \ + } + +#define MUX(_id, _name, _parents, _reg, _shift, _width) { \ + .id = _id, \ + .name = _name, \ + .mux_reg = _reg, \ + .mux_shift = _shift, \ + .mux_width = _width, \ + .gate_shift = -1, \ + .divider_shift = -1, \ + .parent_names = _parents, \ + .num_parents = ARRAY_SIZE(_parents), \ + .flags = CLK_SET_RATE_PARENT, \ + } + +#define DIV_GATE(_id, _name, _parent, _gate_reg, _gate_shift, _div_reg, _div_width, _div_shift) { \ + .id = _id, \ + .parent = _parent, \ + .name = _name, \ + .divider_reg = _div_reg, \ + .divider_shift = _div_shift, \ + .divider_width = _div_width, \ + .gate_reg = _gate_reg, \ + .gate_shift = _gate_shift, \ + .mux_shift = -1, \ + .flags = 0, \ + } + +struct clk *mtk_clk_register_composite(const struct mtk_composite *mc, + void __iomem *base, spinlock_t *lock); + +struct mtk_gate_regs { + u32 sta_ofs; + u32 clr_ofs; + u32 set_ofs; +}; + +struct mtk_gate { + int id; + const char *name; + const char *parent_name; + const struct mtk_gate_regs *regs; + int shift; + const struct clk_ops *ops; +}; + +int mtk_clk_register_gates(struct device_node *node, const struct mtk_gate *clks, + int num, struct clk_onecell_data *clk_data); + +struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num); + +#define HAVE_RST_BAR BIT(0) + +struct mtk_pll_data { + int id; + const char *name; + uint32_t reg; + uint32_t pwr_reg; + uint32_t en_mask; + uint32_t pd_reg; + uint32_t tuner_reg; + int pd_shift; + unsigned int flags; + const struct clk_ops *ops; + u32 rst_bar_mask; + unsigned long fmax; + int pcwbits; + uint32_t pcw_reg; + int pcw_shift; +}; + +void __init mtk_clk_register_plls(struct device_node *node, + const struct mtk_pll_data *plls, int num_plls, + struct clk_onecell_data *clk_data); + +#endif /* __DRV_CLK_MTK_H */ diff --git a/drivers/clk/mediatek/clk-pll.c b/drivers/clk/mediatek/clk-pll.c new file mode 100644 index 0000000..5589e45 --- /dev/null +++ b/drivers/clk/mediatek/clk-pll.c @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao <jamesjj.liao@mediatek.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. + */ + +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/clkdev.h> +#include <linux/delay.h> + +#include "clk-mtk.h" + +#define CON0_BASE_EN BIT(0) +#define CON0_PWR_ON BIT(0) +#define CON0_ISO_EN BIT(1) +#define CON0_PCW_CHG BIT(31) + +#define AUDPLL_TUNER_EN BIT(31) + +/* + * MediaTek PLLs are configured through their pcw value. The pcw value describes + * a divider in the PLL feedback loop which consists of 7 bits for the integer + * part and the remaining bits (if present) for the fractional part. Also they + * have a 3 bit power-of-two post divider. + */ + +struct mtk_clk_pll { + struct clk_hw hw; + void __iomem *base_addr; + void __iomem *pd_addr; + void __iomem *pwr_addr; + void __iomem *tuner_addr; + void __iomem *pcw_addr; + const struct mtk_pll_data *data; +}; + +static inline struct mtk_clk_pll *to_mtk_clk_pll(struct clk_hw *hw) +{ + return container_of(hw, struct mtk_clk_pll, hw); +} + +static int mtk_pll_is_prepared(struct clk_hw *hw) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + + return (readl(pll->base_addr) & CON0_BASE_EN) != 0; +} + +static unsigned long __mtk_pll_recalc_rate(struct mtk_clk_pll *pll, u32 fin, + u32 pcw, int postdiv) +{ + int pcwbits = pll->data->pcwbits; + int pcwfbits; + u64 vco; + u8 c = 0; + + /* The fractional part of the PLL divider. */ + pcwfbits = pcwbits > 7 ? pcwbits - 7 : 0; + + vco = (u64)fin * pcw; + + if (pcwfbits && (vco & GENMASK(pcwfbits - 1, 0))) + c = 1; + + vco >>= pcwfbits; + + if (c) + ++vco; + + return ((unsigned long)vco + postdiv - 1) / postdiv; +} + +static void mtk_pll_set_rate_regs(struct clk_hw *hw, u32 pcw, + int postdiv) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 con1, pd, val; + int pll_en; + + /* set postdiv */ + pd = readl(pll->pd_addr); + pd &= ~(0x7 << pll->data->pd_shift); + pd |= (ffs(postdiv) - 1) << pll->data->pd_shift; + writel(pd, pll->pd_addr); + + pll_en = readl(pll->base_addr) & CON0_BASE_EN; + + /* set pcw */ + val = readl(pll->pcw_addr); + + val &= ~GENMASK(pll->data->pcw_shift + pll->data->pcwbits - 1, + pll->data->pcw_shift); + val |= pcw << pll->data->pcw_shift; + writel(val, pll->pcw_addr); + + con1 = readl(pll->base_addr + 4); + + if (pll_en) + con1 |= CON0_PCW_CHG; + + writel(con1, pll->base_addr + 4); + if (pll->tuner_addr) + writel(con1 + 1, pll->tuner_addr); + + if (pll_en) + usleep_range(20, 1000); +} + +/* + * mtk_pll_calc_values - calculate good values for a given input frequency. + * @pll: The pll + * @pcw: The pcw value (output) + * @postdiv: The post divider (output) + * @freq: The desired target frequency + * @fin: The input frequency + * + */ +static void mtk_pll_calc_values(struct mtk_clk_pll *pll, u32 *pcw, u32 *postdiv, + u32 freq, u32 fin) +{ + unsigned long fmin = 1000 * MHZ; + u64 _pcw; + u32 val; + + if (freq > pll->data->fmax) + freq = pll->data->fmax; + + for (val = 0; val < 4; val++) { + *postdiv = 1 << val; + if (freq * *postdiv >= fmin) + break; + } + + /* _pcw = freq * postdiv / fin * 2^pcwfbits */ + _pcw = ((u64)freq << val) << (pll->data->pcwbits - 7); + do_div(_pcw, fin); + + *pcw = (u32)_pcw; +} + +static int mtk_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 pcw = 0; + u32 postdiv; + + mtk_pll_calc_values(pll, &pcw, &postdiv, rate, parent_rate); + mtk_pll_set_rate_regs(hw, pcw, postdiv); + + return 0; +} + +static unsigned long mtk_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 postdiv; + u32 pcw; + + postdiv = (readl(pll->pd_addr) >> pll->data->pd_shift) & 0x7; + postdiv = 1 << postdiv; + + pcw = readl(pll->pcw_addr) >> pll->data->pcw_shift; + pcw &= GENMASK(pll->data->pcwbits - 1, 0); + + return __mtk_pll_recalc_rate(pll, parent_rate, pcw, postdiv); +} + +static long mtk_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 pcw = 0; + int postdiv; + + mtk_pll_calc_values(pll, &pcw, &postdiv, rate, *prate); + + return __mtk_pll_recalc_rate(pll, *prate, pcw, postdiv); +} + +static int mtk_pll_prepare(struct clk_hw *hw) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 r; + + r = readl(pll->pwr_addr) | CON0_PWR_ON; + writel(r, pll->pwr_addr); + usleep_range(1, 100); + + r = readl(pll->pwr_addr) & ~CON0_ISO_EN; + writel(r, pll->pwr_addr); + usleep_range(1, 100); + + r = readl(pll->base_addr) | pll->data->en_mask; + writel(r, pll->base_addr); + + if (pll->tuner_addr) { + r = readl(pll->tuner_addr) | AUDPLL_TUNER_EN; + writel(r, pll->tuner_addr); + } + + usleep_range(20, 1000); + + if (pll->data->flags & HAVE_RST_BAR) { + r = readl(pll->base_addr) | pll->data->rst_bar_mask; + writel(r, pll->base_addr); + } + + return 0; +} + +static void mtk_pll_unprepare(struct clk_hw *hw) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 r; + + if (pll->data->flags & HAVE_RST_BAR) { + r = readl(pll->base_addr) & ~pll->data->rst_bar_mask; + writel(r, pll->base_addr); + } + + if (pll->tuner_addr) { + r = readl(pll->tuner_addr) & ~AUDPLL_TUNER_EN; + writel(r, pll->tuner_addr); + } + + r = readl(pll->base_addr) & ~CON0_BASE_EN; + writel(r, pll->base_addr); + + r = readl(pll->pwr_addr) | CON0_ISO_EN; + writel(r, pll->pwr_addr); + + r = readl(pll->pwr_addr) & ~CON0_PWR_ON; + writel(r, pll->pwr_addr); +} + +static const struct clk_ops mtk_pll_ops = { + .is_prepared = mtk_pll_is_prepared, + .prepare = mtk_pll_prepare, + .unprepare = mtk_pll_unprepare, + .recalc_rate = mtk_pll_recalc_rate, + .round_rate = mtk_pll_round_rate, + .set_rate = mtk_pll_set_rate, +}; + +static struct clk *mtk_clk_register_pll(const struct mtk_pll_data *data, + void __iomem *base) +{ + struct mtk_clk_pll *pll; + struct clk_init_data init; + struct clk *clk; + const char *parent_name = "clk26m"; + + pr_debug("%s(): name: %s\n", __func__, data->name); + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + pll->base_addr = base + data->reg; + pll->pwr_addr = base + data->pwr_reg; + pll->pd_addr = base + data->pd_reg; + pll->pcw_addr = base + data->pcw_reg; + if (data->tuner_reg) + pll->tuner_addr = base + data->tuner_reg; + pll->hw.init = &init; + pll->data = data; + + init.name = data->name; + init.ops = &mtk_pll_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + + clk = clk_register(NULL, &pll->hw); + + if (IS_ERR(clk)) + kfree(pll); + + return clk; +} + +void __init mtk_clk_register_plls(struct device_node *node, + const struct mtk_pll_data *plls, int num_plls, struct clk_onecell_data *clk_data) +{ + void __iomem *base; + int r, i; + struct clk *clk; + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s(): ioremap failed\n", __func__); + return; + } + + for (i = 0; i < num_plls; i++) { + const struct mtk_pll_data *pll = &plls[i]; + + clk = mtk_clk_register_pll(pll, base); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + pll->name, PTR_ERR(clk)); + continue; + } + + clk_data->clks[pll->id] = clk; + } + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} -- 2.1.4 ^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 3/6] clk: mediatek: Add reset controller support 2015-03-19 8:42 [PATCH v8]: clk: Add common clock support for Mediatek MT8135 and MT8173 Sascha Hauer 2015-03-19 8:42 ` [PATCH 1/6] clk: make strings in parent name arrays const Sascha Hauer 2015-03-19 8:42 ` [PATCH 2/6] clk: mediatek: Add initial common clock support for Mediatek SoCs Sascha Hauer @ 2015-03-19 8:42 ` Sascha Hauer 2015-03-27 13:32 ` Matthias Brugger 2015-03-19 8:42 ` [PATCH 4/6] clk: mediatek: Add basic clocks for Mediatek MT8135 Sascha Hauer ` (3 subsequent siblings) 6 siblings, 1 reply; 22+ messages in thread From: Sascha Hauer @ 2015-03-19 8:42 UTC (permalink / raw) To: linux-arm-kernel The pericfg and infracfg units also provide reset lines to several other SoC internal units. Add support for the reset controller. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- drivers/clk/mediatek/Makefile | 1 + drivers/clk/mediatek/clk-mtk.h | 10 +++++ drivers/clk/mediatek/reset.c | 99 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+) create mode 100644 drivers/clk/mediatek/reset.c diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile index c384e97..0b6f1c3 100644 --- a/drivers/clk/mediatek/Makefile +++ b/drivers/clk/mediatek/Makefile @@ -1 +1,2 @@ obj-y += clk-mtk.o clk-pll.o clk-gate.o +obj-$(CONFIG_RESET_CONTROLLER) += reset.o diff --git a/drivers/clk/mediatek/clk-mtk.h b/drivers/clk/mediatek/clk-mtk.h index 5aaba81..5a6b5dd 100644 --- a/drivers/clk/mediatek/clk-mtk.h +++ b/drivers/clk/mediatek/clk-mtk.h @@ -152,4 +152,14 @@ void __init mtk_clk_register_plls(struct device_node *node, const struct mtk_pll_data *plls, int num_plls, struct clk_onecell_data *clk_data); +#ifdef CONFIG_RESET_CONTROLLER +void mtk_register_reset_controller(struct device_node *np, + unsigned int num_regs, int regofs); +#else +static inline void mtk_register_reset_controller(struct device_node *np, + unsigned int num_regs, int regofs) +{ +} +#endif + #endif /* __DRV_CLK_MTK_H */ diff --git a/drivers/clk/mediatek/reset.c b/drivers/clk/mediatek/reset.c new file mode 100644 index 0000000..3a85a53 --- /dev/null +++ b/drivers/clk/mediatek/reset.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * + * 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/reset-controller.h> +#include <linux/slab.h> + +#include "clk-mtk.h" + +struct mtk_reset { + struct regmap *regmap; + int regofs; + struct reset_controller_dev rcdev; +}; + +static int mtk_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct mtk_reset *data = container_of(rcdev, struct mtk_reset, rcdev); + + return regmap_update_bits(data->regmap, data->regofs + ((id / 32) << 2), + BIT(id % 32), ~0); +} + +static int mtk_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct mtk_reset *data = container_of(rcdev, struct mtk_reset, rcdev); + + return regmap_update_bits(data->regmap, data->regofs + ((id / 32) << 2), + BIT(id % 32), 0); +} + +static int mtk_reset(struct reset_controller_dev *rcdev, + unsigned long id) +{ + int ret; + + ret = mtk_reset_assert(rcdev, id); + if (ret) + return ret; + + return mtk_reset_deassert(rcdev, id); +} + +static struct reset_control_ops mtk_reset_ops = { + .assert = mtk_reset_assert, + .deassert = mtk_reset_deassert, + .reset = mtk_reset, +}; + +void mtk_register_reset_controller(struct device_node *np, + unsigned int num_regs, int regofs) +{ + struct mtk_reset *data; + int ret; + struct regmap *regmap; + + regmap = syscon_node_to_regmap(np); + if (IS_ERR(regmap)) { + pr_err("Cannot find regmap for %s: %ld\n", np->full_name, + PTR_ERR(regmap)); + return; + } + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return; + + data->regmap = regmap; + data->regofs = regofs; + data->rcdev.owner = THIS_MODULE; + data->rcdev.nr_resets = num_regs * 32; + data->rcdev.ops = &mtk_reset_ops; + data->rcdev.of_node = np; + + ret = reset_controller_register(&data->rcdev); + if (ret) { + pr_err("could not register reset controller: %d\n", ret); + kfree(data); + return; + } +} -- 2.1.4 ^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 3/6] clk: mediatek: Add reset controller support 2015-03-19 8:42 ` [PATCH 3/6] clk: mediatek: Add reset controller support Sascha Hauer @ 2015-03-27 13:32 ` Matthias Brugger 0 siblings, 0 replies; 22+ messages in thread From: Matthias Brugger @ 2015-03-27 13:32 UTC (permalink / raw) To: linux-arm-kernel On 19/03/15 09:42, Sascha Hauer wrote: > The pericfg and infracfg units also provide reset lines to several > other SoC internal units. Add support for the reset controller. This messages is a bit confusing, could you explain better what the patch does and how this interacts with pericfg and infracfg? > > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> > --- > drivers/clk/mediatek/Makefile | 1 + > drivers/clk/mediatek/clk-mtk.h | 10 +++++ > drivers/clk/mediatek/reset.c | 99 ++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 110 insertions(+) > create mode 100644 drivers/clk/mediatek/reset.c > > diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile > index c384e97..0b6f1c3 100644 > --- a/drivers/clk/mediatek/Makefile > +++ b/drivers/clk/mediatek/Makefile > @@ -1 +1,2 @@ > obj-y += clk-mtk.o clk-pll.o clk-gate.o > +obj-$(CONFIG_RESET_CONTROLLER) += reset.o > diff --git a/drivers/clk/mediatek/clk-mtk.h b/drivers/clk/mediatek/clk-mtk.h > index 5aaba81..5a6b5dd 100644 > --- a/drivers/clk/mediatek/clk-mtk.h > +++ b/drivers/clk/mediatek/clk-mtk.h > @@ -152,4 +152,14 @@ void __init mtk_clk_register_plls(struct device_node *node, > const struct mtk_pll_data *plls, int num_plls, > struct clk_onecell_data *clk_data); > > +#ifdef CONFIG_RESET_CONTROLLER > +void mtk_register_reset_controller(struct device_node *np, > + unsigned int num_regs, int regofs); > +#else > +static inline void mtk_register_reset_controller(struct device_node *np, > + unsigned int num_regs, int regofs) > +{ > +} > +#endif > + > #endif /* __DRV_CLK_MTK_H */ > diff --git a/drivers/clk/mediatek/reset.c b/drivers/clk/mediatek/reset.c > new file mode 100644 > index 0000000..3a85a53 > --- /dev/null > +++ b/drivers/clk/mediatek/reset.c > @@ -0,0 +1,99 @@ > +/* > + * Copyright (c) 2014 MediaTek Inc. > + * > + * 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. > + */ > + > +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt Do we need this? Cheers, Matthias ^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 4/6] clk: mediatek: Add basic clocks for Mediatek MT8135. 2015-03-19 8:42 [PATCH v8]: clk: Add common clock support for Mediatek MT8135 and MT8173 Sascha Hauer ` (2 preceding siblings ...) 2015-03-19 8:42 ` [PATCH 3/6] clk: mediatek: Add reset controller support Sascha Hauer @ 2015-03-19 8:42 ` Sascha Hauer 2015-03-27 7:39 ` Stephen Boyd 2015-03-19 8:42 ` [PATCH 5/6] clk: mediatek: Add basic clocks for Mediatek MT8173 Sascha Hauer ` (2 subsequent siblings) 6 siblings, 1 reply; 22+ messages in thread From: Sascha Hauer @ 2015-03-19 8:42 UTC (permalink / raw) To: linux-arm-kernel From: James Liao <jamesjj.liao@mediatek.com> This patch adds basic clocks for MT8135, including TOPCKGEN, PLLs, INFRA and PERI clocks. Signed-off-by: James Liao <jamesjj.liao@mediatek.com> Signed-off-by: Henry Chen <henryc.chen@mediatek.com> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- drivers/clk/mediatek/Makefile | 1 + drivers/clk/mediatek/clk-mt8135.c | 640 +++++++++++++++++++++ include/dt-bindings/clock/mt8135-clk.h | 190 ++++++ .../dt-bindings/reset-controller/mt8135-resets.h | 64 +++ 4 files changed, 895 insertions(+) create mode 100644 drivers/clk/mediatek/clk-mt8135.c create mode 100644 include/dt-bindings/clock/mt8135-clk.h create mode 100644 include/dt-bindings/reset-controller/mt8135-resets.h diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile index 0b6f1c3..12ce576 100644 --- a/drivers/clk/mediatek/Makefile +++ b/drivers/clk/mediatek/Makefile @@ -1,2 +1,3 @@ obj-y += clk-mtk.o clk-pll.o clk-gate.o obj-$(CONFIG_RESET_CONTROLLER) += reset.o +obj-y += clk-mt8135.o diff --git a/drivers/clk/mediatek/clk-mt8135.c b/drivers/clk/mediatek/clk-mt8135.c new file mode 100644 index 0000000..a22f6fa --- /dev/null +++ b/drivers/clk/mediatek/clk-mt8135.c @@ -0,0 +1,640 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao <jamesjj.liao@mediatek.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. + */ + +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/slab.h> +#include <linux/mfd/syscon.h> +#include <dt-bindings/clock/mt8135-clk.h> + +#include "clk-mtk.h" +#include "clk-gate.h" + +static DEFINE_SPINLOCK(lock); + +static const struct mtk_fixed_factor root_clk_alias[] __initconst = { + FACTOR(TOP_DSI0_LNTC_DSICLK, "dsi0_lntc_dsiclk", "clk_null", 1, 1), + FACTOR(TOP_HDMITX_CLKDIG_CTS, "hdmitx_clkdig_cts", "clk_null", 1, 1), + FACTOR(TOP_CLKPH_MCK, "clkph_mck", "clk_null", 1, 1), + FACTOR(TOP_CPUM_TCK_IN, "cpum_tck_in", "clk_null", 1, 1), +}; + +static const struct mtk_fixed_factor top_divs[] __initconst = { + FACTOR(TOP_MAINPLL_806M, "mainpll_806m", "mainpll", 1, 2), + FACTOR(TOP_MAINPLL_537P3M, "mainpll_537p3m", "mainpll", 1, 3), + FACTOR(TOP_MAINPLL_322P4M, "mainpll_322p4m", "mainpll", 1, 5), + FACTOR(TOP_MAINPLL_230P3M, "mainpll_230p3m", "mainpll", 1, 7), + + FACTOR(TOP_UNIVPLL_624M, "univpll_624m", "univpll", 1, 2), + FACTOR(TOP_UNIVPLL_416M, "univpll_416m", "univpll", 1, 3), + FACTOR(TOP_UNIVPLL_249P6M, "univpll_249p6m", "univpll", 1, 5), + FACTOR(TOP_UNIVPLL_178P3M, "univpll_178p3m", "univpll", 1, 7), + FACTOR(TOP_UNIVPLL_48M, "univpll_48m", "univpll", 1, 26), + + FACTOR(TOP_MMPLL_D2, "mmpll_d2", "mmpll", 1, 2), + FACTOR(TOP_MMPLL_D3, "mmpll_d3", "mmpll", 1, 3), + FACTOR(TOP_MMPLL_D5, "mmpll_d5", "mmpll", 1, 5), + FACTOR(TOP_MMPLL_D7, "mmpll_d7", "mmpll", 1, 7), + FACTOR(TOP_MMPLL_D4, "mmpll_d4", "mmpll_d2", 1, 2), + FACTOR(TOP_MMPLL_D6, "mmpll_d6", "mmpll_d3", 1, 2), + + FACTOR(TOP_SYSPLL_D2, "syspll_d2", "mainpll_806m", 1, 1), + FACTOR(TOP_SYSPLL_D4, "syspll_d4", "mainpll_806m", 1, 2), + FACTOR(TOP_SYSPLL_D6, "syspll_d6", "mainpll_806m", 1, 3), + FACTOR(TOP_SYSPLL_D8, "syspll_d8", "mainpll_806m", 1, 4), + FACTOR(TOP_SYSPLL_D10, "syspll_d10", "mainpll_806m", 1, 5), + FACTOR(TOP_SYSPLL_D12, "syspll_d12", "mainpll_806m", 1, 6), + FACTOR(TOP_SYSPLL_D16, "syspll_d16", "mainpll_806m", 1, 8), + FACTOR(TOP_SYSPLL_D24, "syspll_d24", "mainpll_806m", 1, 12), + + FACTOR(TOP_SYSPLL_D3, "syspll_d3", "mainpll_537p3m", 1, 1), + + FACTOR(TOP_SYSPLL_D2P5, "syspll_d2p5", "mainpll_322p4m", 2, 1), + FACTOR(TOP_SYSPLL_D5, "syspll_d5", "mainpll_322p4m", 1, 1), + + FACTOR(TOP_SYSPLL_D3P5, "syspll_d3p5", "mainpll_230p3m", 2, 1), + + FACTOR(TOP_UNIVPLL1_D2, "univpll1_d2", "univpll_624m", 1, 2), + FACTOR(TOP_UNIVPLL1_D4, "univpll1_d4", "univpll_624m", 1, 4), + FACTOR(TOP_UNIVPLL1_D6, "univpll1_d6", "univpll_624m", 1, 6), + FACTOR(TOP_UNIVPLL1_D8, "univpll1_d8", "univpll_624m", 1, 8), + FACTOR(TOP_UNIVPLL1_D10, "univpll1_d10", "univpll_624m", 1, 10), + + FACTOR(TOP_UNIVPLL2_D2, "univpll2_d2", "univpll_416m", 1, 2), + FACTOR(TOP_UNIVPLL2_D4, "univpll2_d4", "univpll_416m", 1, 4), + FACTOR(TOP_UNIVPLL2_D6, "univpll2_d6", "univpll_416m", 1, 6), + FACTOR(TOP_UNIVPLL2_D8, "univpll2_d8", "univpll_416m", 1, 8), + + FACTOR(TOP_UNIVPLL_D3, "univpll_d3", "univpll_416m", 1, 1), + FACTOR(TOP_UNIVPLL_D5, "univpll_d5", "univpll_249p6m", 1, 1), + FACTOR(TOP_UNIVPLL_D7, "univpll_d7", "univpll_178p3m", 1, 1), + FACTOR(TOP_UNIVPLL_D10, "univpll_d10", "univpll_249p6m", 1, 5), + FACTOR(TOP_UNIVPLL_D26, "univpll_d26", "univpll_48m", 1, 1), + + FACTOR(TOP_APLL_CK, "apll_ck", "audpll", 1, 1), + FACTOR(TOP_APLL_D4, "apll_d4", "audpll", 1, 4), + FACTOR(TOP_APLL_D8, "apll_d8", "audpll", 1, 8), + FACTOR(TOP_APLL_D16, "apll_d16", "audpll", 1, 16), + FACTOR(TOP_APLL_D24, "apll_d24", "audpll", 1, 24), + + FACTOR(TOP_LVDSPLL_D2, "lvdspll_d2", "lvdspll", 1, 2), + FACTOR(TOP_LVDSPLL_D4, "lvdspll_d4", "lvdspll", 1, 4), + FACTOR(TOP_LVDSPLL_D8, "lvdspll_d8", "lvdspll", 1, 8), + + FACTOR(TOP_LVDSTX_CLKDIG_CT, "lvdstx_clkdig_cts", "lvdspll", 1, 1), + FACTOR(TOP_VPLL_DPIX_CK, "vpll_dpix_ck", "lvdspll", 1, 1), + + FACTOR(TOP_TVHDMI_H_CK, "tvhdmi_h_ck", "tvdpll", 1, 1), + + FACTOR(TOP_HDMITX_CLKDIG_D2, "hdmitx_clkdig_d2", "hdmitx_clkdig_cts", 1, 2), + FACTOR(TOP_HDMITX_CLKDIG_D3, "hdmitx_clkdig_d3", "hdmitx_clkdig_cts", 1, 3), + + FACTOR(TOP_TVHDMI_D2, "tvhdmi_d2", "tvhdmi_h_ck", 1, 2), + FACTOR(TOP_TVHDMI_D4, "tvhdmi_d4", "tvhdmi_h_ck", 1, 4), + + FACTOR(TOP_MEMPLL_MCK_D4, "mempll_mck_d4", "clkph_mck", 1, 4), +}; + +static const char * const axi_parents[] __initconst = { + "clk26m", + "syspll_d3", + "syspll_d4", + "syspll_d6", + "univpll_d5", + "univpll2_d2", + "syspll_d3p5" +}; + +static const char * const smi_parents[] __initconst = { + "clk26m", + "clkph_mck", + "syspll_d2p5", + "syspll_d3", + "syspll_d8", + "univpll_d5", + "univpll1_d2", + "univpll1_d6", + "mmpll_d3", + "mmpll_d4", + "mmpll_d5", + "mmpll_d6", + "mmpll_d7", + "vdecpll", + "lvdspll" +}; + +static const char * const mfg_parents[] __initconst = { + "clk26m", + "univpll1_d4", + "syspll_d2", + "syspll_d2p5", + "syspll_d3", + "univpll_d5", + "univpll1_d2", + "mmpll_d2", + "mmpll_d3", + "mmpll_d4", + "mmpll_d5", + "mmpll_d6", + "mmpll_d7" +}; + +static const char * const irda_parents[] __initconst = { + "clk26m", + "univpll2_d8", + "univpll1_d6" +}; + +static const char * const cam_parents[] __initconst = { + "clk26m", + "syspll_d3", + "syspll_d3p5", + "syspll_d4", + "univpll_d5", + "univpll2_d2", + "univpll_d7", + "univpll1_d4" +}; + +static const char * const aud_intbus_parents[] __initconst = { + "clk26m", + "syspll_d6", + "univpll_d10" +}; + +static const char * const jpg_parents[] __initconst = { + "clk26m", + "syspll_d5", + "syspll_d4", + "syspll_d3", + "univpll_d7", + "univpll2_d2", + "univpll_d5" +}; + +static const char * const disp_parents[] __initconst = { + "clk26m", + "syspll_d3p5", + "syspll_d3", + "univpll2_d2", + "univpll_d5", + "univpll1_d2", + "lvdspll", + "vdecpll" +}; + +static const char * const msdc30_parents[] __initconst = { + "clk26m", + "syspll_d6", + "syspll_d5", + "univpll1_d4", + "univpll2_d4", + "msdcpll" +}; + +static const char * const usb20_parents[] __initconst = { + "clk26m", + "univpll2_d6", + "univpll1_d10" +}; + +static const char * const venc_parents[] __initconst = { + "clk26m", + "syspll_d3", + "syspll_d8", + "univpll_d5", + "univpll1_d6", + "mmpll_d4", + "mmpll_d5", + "mmpll_d6" +}; + +static const char * const spi_parents[] __initconst = { + "clk26m", + "syspll_d6", + "syspll_d8", + "syspll_d10", + "univpll1_d6", + "univpll1_d8" +}; + +static const char * const uart_parents[] __initconst = { + "clk26m", + "univpll2_d8" +}; + +static const char * const mem_parents[] __initconst = { + "clk26m", + "clkph_mck" +}; + +static const char * const camtg_parents[] __initconst = { + "clk26m", + "univpll_d26", + "univpll1_d6", + "syspll_d16", + "syspll_d8" +}; + +static const char * const audio_parents[] __initconst = { + "clk26m", + "syspll_d24" +}; + +static const char * const fix_parents[] __initconst = { + "rtc32k", + "clk26m", + "univpll_d5", + "univpll_d7", + "univpll1_d2", + "univpll1_d4", + "univpll1_d6", + "univpll1_d8" +}; + +static const char * const vdec_parents[] __initconst = { + "clk26m", + "vdecpll", + "clkph_mck", + "syspll_d2p5", + "syspll_d3", + "syspll_d3p5", + "syspll_d4", + "syspll_d5", + "syspll_d6", + "syspll_d8", + "univpll1_d2", + "univpll2_d2", + "univpll_d7", + "univpll_d10", + "univpll2_d4", + "lvdspll" +}; + +static const char * const ddrphycfg_parents[] __initconst = { + "clk26m", + "axi_sel", + "syspll_d12" +}; + +static const char * const dpilvds_parents[] __initconst = { + "clk26m", + "lvdspll", + "lvdspll_d2", + "lvdspll_d4", + "lvdspll_d8" +}; + +static const char * const pmicspi_parents[] __initconst = { + "clk26m", + "univpll2_d6", + "syspll_d8", + "syspll_d10", + "univpll1_d10", + "mempll_mck_d4", + "univpll_d26", + "syspll_d24" +}; + +static const char * const smi_mfg_as_parents[] __initconst = { + "clk26m", + "smi_sel", + "mfg_sel", + "mem_sel" +}; + +static const char * const gcpu_parents[] __initconst = { + "clk26m", + "syspll_d4", + "univpll_d7", + "syspll_d5", + "syspll_d6" +}; + +static const char * const dpi1_parents[] __initconst = { + "clk26m", + "tvhdmi_h_ck", + "tvhdmi_d2", + "tvhdmi_d4" +}; + +static const char * const cci_parents[] __initconst = { + "clk26m", + "mainpll_537p3m", + "univpll_d3", + "syspll_d2p5", + "syspll_d3", + "syspll_d5" +}; + +static const char * const apll_parents[] __initconst = { + "clk26m", + "apll_ck", + "apll_d4", + "apll_d8", + "apll_d16", + "apll_d24" +}; + +static const char * const hdmipll_parents[] __initconst = { + "clk26m", + "hdmitx_clkdig_cts", + "hdmitx_clkdig_d2", + "hdmitx_clkdig_d3" +}; + +static const struct mtk_composite top_muxes[] __initconst = { + /* CLK_CFG_0 */ + MUX_GATE(TOP_AXI_SEL, "axi_sel", axi_parents, + 0x0140, 0, 3, INVALID_MUX_GATE_BIT), + MUX_GATE(TOP_SMI_SEL, "smi_sel", smi_parents, 0x0140, 8, 4, 15), + MUX_GATE(TOP_MFG_SEL, "mfg_sel", mfg_parents, 0x0140, 16, 4, 23), + MUX_GATE(TOP_IRDA_SEL, "irda_sel", irda_parents, 0x0140, 24, 2, 31), + /* CLK_CFG_1 */ + MUX_GATE(TOP_CAM_SEL, "cam_sel", cam_parents, 0x0144, 0, 3, 7), + MUX_GATE(TOP_AUD_INTBUS_SEL, "aud_intbus_sel", aud_intbus_parents, + 0x0144, 8, 2, 15), + MUX_GATE(TOP_JPG_SEL, "jpg_sel", jpg_parents, 0x0144, 16, 3, 23), + MUX_GATE(TOP_DISP_SEL, "disp_sel", disp_parents, 0x0144, 24, 3, 31), + /* CLK_CFG_2 */ + MUX_GATE(TOP_MSDC30_1_SEL, "msdc30_1_sel", msdc30_parents, 0x0148, 0, 3, 7), + MUX_GATE(TOP_MSDC30_2_SEL, "msdc30_2_sel", msdc30_parents, 0x0148, 8, 3, 15), + MUX_GATE(TOP_MSDC30_3_SEL, "msdc30_3_sel", msdc30_parents, 0x0148, 16, 3, 23), + MUX_GATE(TOP_MSDC30_4_SEL, "msdc30_4_sel", msdc30_parents, 0x0148, 24, 3, 31), + /* CLK_CFG_3 */ + MUX_GATE(TOP_USB20_SEL, "usb20_sel", usb20_parents, 0x014c, 0, 2, 7), + /* CLK_CFG_4 */ + MUX_GATE(TOP_VENC_SEL, "venc_sel", venc_parents, 0x0150, 8, 3, 15), + MUX_GATE(TOP_SPI_SEL, "spi_sel", spi_parents, 0x0150, 16, 3, 23), + MUX_GATE(TOP_UART_SEL, "uart_sel", uart_parents, 0x0150, 24, 2, 31), + /* CLK_CFG_6 */ + MUX_GATE(TOP_MEM_SEL, "mem_sel", mem_parents, 0x0158, 0, 2, 7), + MUX_GATE(TOP_CAMTG_SEL, "camtg_sel", camtg_parents, 0x0158, 8, 3, 15), + MUX_GATE(TOP_AUDIO_SEL, "audio_sel", audio_parents, 0x0158, 24, 2, 31), + /* CLK_CFG_7 */ + MUX_GATE(TOP_FIX_SEL, "fix_sel", fix_parents, 0x015c, 0, 3, 7), + MUX_GATE(TOP_VDEC_SEL, "vdec_sel", vdec_parents, 0x015c, 8, 4, 15), + MUX_GATE(TOP_DDRPHYCFG_SEL, "ddrphycfg_sel", ddrphycfg_parents, + 0x015c, 16, 2, 23), + MUX_GATE(TOP_DPILVDS_SEL, "dpilvds_sel", dpilvds_parents, 0x015c, 24, 3, 31), + /* CLK_CFG_8 */ + MUX_GATE(TOP_PMICSPI_SEL, "pmicspi_sel", pmicspi_parents, 0x0164, 0, 3, 7), + MUX_GATE(TOP_MSDC30_0_SEL, "msdc30_0_sel", msdc30_parents, 0x0164, 8, 3, 15), + MUX_GATE(TOP_SMI_MFG_AS_SEL, "smi_mfg_as_sel", smi_mfg_as_parents, + 0x0164, 16, 2, 23), + MUX_GATE(TOP_GCPU_SEL, "gcpu_sel", gcpu_parents, 0x0164, 24, 3, 31), + /* CLK_CFG_9 */ + MUX_GATE(TOP_DPI1_SEL, "dpi1_sel", dpi1_parents, 0x0168, 0, 2, 7), + MUX_GATE(TOP_CCI_SEL, "cci_sel", cci_parents, 0x0168, 8, 3, 15), + MUX_GATE(TOP_APLL_SEL, "apll_sel", apll_parents, 0x0168, 16, 3, 23), + MUX_GATE(TOP_HDMIPLL_SEL, "hdmipll_sel", hdmipll_parents, 0x0168, 24, 2, 31), +}; + +static void __init mtk_init_clk_topckgen(void __iomem *top_base, + struct clk_onecell_data *clk_data) +{ + int i; + struct clk *clk; + + for (i = 0; i < ARRAY_SIZE(top_muxes); i++) { + const struct mtk_composite *mux = &top_muxes[i]; + + clk = mtk_clk_register_composite(mux, top_base, &lock); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + mux->name, PTR_ERR(clk)); + continue; + } + + if (clk_data) + clk_data->clks[mux->id] = clk; + } +} + +static const struct mtk_gate_regs infra_cg_regs = { + .set_ofs = 0x0040, + .clr_ofs = 0x0044, + .sta_ofs = 0x0048, +}; + +#define GATE_ICG(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &infra_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +static const struct mtk_gate infra_clks[] __initconst = { + GATE_ICG(INFRA_PMIC_WRAP_CK, "pmic_wrap_ck", "axi_sel", 23), + GATE_ICG(INFRA_PMICSPI_CK, "pmicspi_ck", "pmicspi_sel", 22), + GATE_ICG(INFRA_CCIF1_AP_CTRL, "ccif1_ap_ctrl", "axi_sel", 21), + GATE_ICG(INFRA_CCIF0_AP_CTRL, "ccif0_ap_ctrl", "axi_sel", 20), + GATE_ICG(INFRA_KP_CK, "kp_ck", "axi_sel", 16), + GATE_ICG(INFRA_CPUM_CK, "cpum_ck", "cpum_tck_in", 15), + GATE_ICG(INFRA_M4U_CK, "m4u_ck", "mem_sel", 8), + GATE_ICG(INFRA_MFGAXI_CK, "mfgaxi_ck", "axi_sel", 7), + GATE_ICG(INFRA_DEVAPC_CK, "devapc_ck", "axi_sel", 6), + GATE_ICG(INFRA_AUDIO_CK, "audio_ck", "aud_intbus_sel", 5), + GATE_ICG(INFRA_MFG_BUS_CK, "mfg_bus_ck", "axi_sel", 2), + GATE_ICG(INFRA_SMI_CK, "smi_ck", "smi_sel", 1), + GATE_ICG(INFRA_DBGCLK_CK, "dbgclk_ck", "axi_sel", 0), +}; + +static const struct mtk_gate_regs peri0_cg_regs = { + .set_ofs = 0x0008, + .clr_ofs = 0x0010, + .sta_ofs = 0x0018, +}; + +static const struct mtk_gate_regs peri1_cg_regs = { + .set_ofs = 0x000c, + .clr_ofs = 0x0014, + .sta_ofs = 0x001c, +}; + +#define GATE_PERI0(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &peri0_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +#define GATE_PERI1(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &peri1_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +static const struct mtk_gate peri_clks[] __initconst = { + /* PERI0 */ + GATE_PERI0(PERI_I2C5_CK, "i2c5_ck", "axi_sel", 31), + GATE_PERI0(PERI_I2C4_CK, "i2c4_ck", "axi_sel", 30), + GATE_PERI0(PERI_I2C3_CK, "i2c3_ck", "axi_sel", 29), + GATE_PERI0(PERI_I2C2_CK, "i2c2_ck", "axi_sel", 28), + GATE_PERI0(PERI_I2C1_CK, "i2c1_ck", "axi_sel", 27), + GATE_PERI0(PERI_I2C0_CK, "i2c0_ck", "axi_sel", 26), + GATE_PERI0(PERI_UART3_CK, "uart3_ck", "axi_sel", 25), + GATE_PERI0(PERI_UART2_CK, "uart2_ck", "axi_sel", 24), + GATE_PERI0(PERI_UART1_CK, "uart1_ck", "axi_sel", 23), + GATE_PERI0(PERI_UART0_CK, "uart0_ck", "axi_sel", 22), + GATE_PERI0(PERI_IRDA_CK, "irda_ck", "irda_sel", 21), + GATE_PERI0(PERI_NLI_CK, "nli_ck", "axi_sel", 20), + GATE_PERI0(PERI_MD_HIF_CK, "md_hif_ck", "axi_sel", 19), + GATE_PERI0(PERI_AP_HIF_CK, "ap_hif_ck", "axi_sel", 18), + GATE_PERI0(PERI_MSDC30_3_CK, "msdc30_3_ck", "msdc30_4_sel", 17), + GATE_PERI0(PERI_MSDC30_2_CK, "msdc30_2_ck", "msdc30_3_sel", 16), + GATE_PERI0(PERI_MSDC30_1_CK, "msdc30_1_ck", "msdc30_2_sel", 15), + GATE_PERI0(PERI_MSDC20_2_CK, "msdc20_2_ck", "msdc30_1_sel", 14), + GATE_PERI0(PERI_MSDC20_1_CK, "msdc20_1_ck", "msdc30_0_sel", 13), + GATE_PERI0(PERI_AP_DMA_CK, "ap_dma_ck", "axi_sel", 12), + GATE_PERI0(PERI_USB1_CK, "usb1_ck", "usb20_sel", 11), + GATE_PERI0(PERI_USB0_CK, "usb0_ck", "usb20_sel", 10), + GATE_PERI0(PERI_PWM_CK, "pwm_ck", "axi_sel", 9), + GATE_PERI0(PERI_PWM7_CK, "pwm7_ck", "axi_sel", 8), + GATE_PERI0(PERI_PWM6_CK, "pwm6_ck", "axi_sel", 7), + GATE_PERI0(PERI_PWM5_CK, "pwm5_ck", "axi_sel", 6), + GATE_PERI0(PERI_PWM4_CK, "pwm4_ck", "axi_sel", 5), + GATE_PERI0(PERI_PWM3_CK, "pwm3_ck", "axi_sel", 4), + GATE_PERI0(PERI_PWM2_CK, "pwm2_ck", "axi_sel", 3), + GATE_PERI0(PERI_PWM1_CK, "pwm1_ck", "axi_sel", 2), + GATE_PERI0(PERI_THERM_CK, "therm_ck", "axi_sel", 1), + GATE_PERI0(PERI_NFI_CK, "nfi_ck", "axi_sel", 0), + /* PERI1 */ + GATE_PERI1(PERI_USBSLV_CK, "usbslv_ck", "axi_sel", 8), + GATE_PERI1(PERI_USB1_MCU_CK, "usb1_mcu_ck", "axi_sel", 7), + GATE_PERI1(PERI_USB0_MCU_CK, "usb0_mcu_ck", "axi_sel", 6), + GATE_PERI1(PERI_GCPU_CK, "gcpu_ck", "gcpu_sel", 5), + GATE_PERI1(PERI_FHCTL_CK, "fhctl_ck", "clk26m", 4), + GATE_PERI1(PERI_SPI1_CK, "spi1_ck", "spi_sel", 3), + GATE_PERI1(PERI_AUXADC_CK, "auxadc_ck", "clk26m", 2), + GATE_PERI1(PERI_PERI_PWRAP_CK, "peri_pwrap_ck", "axi_sel", 1), + GATE_PERI1(PERI_I2C6_CK, "i2c6_ck", "axi_sel", 0), +}; + +static void __init mtk_topckgen_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + void __iomem *base; + int r; + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s(): ioremap failed\n", __func__); + return; + } + + clk_data = mtk_alloc_clk_data(TOP_NR_CLK); + + mtk_clk_register_factors(root_clk_alias, ARRAY_SIZE(root_clk_alias), clk_data); + mtk_clk_register_factors(top_divs, ARRAY_SIZE(top_divs), clk_data); + mtk_init_clk_topckgen(base, clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} +CLK_OF_DECLARE(mtk_topckgen, "mediatek,mt8135-topckgen", mtk_topckgen_init); + +static void __init mtk_infrasys_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + + clk_data = mtk_alloc_clk_data(INFRA_NR_CLK); + + mtk_clk_register_gates(node, infra_clks, ARRAY_SIZE(infra_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); + + mtk_register_reset_controller(node, 2, 0x30); +} +CLK_OF_DECLARE(mtk_infrasys, "mediatek,mt8135-infracfg", mtk_infrasys_init); + +static void __init mtk_pericfg_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + + clk_data = mtk_alloc_clk_data(PERI_NR_CLK); + + mtk_clk_register_gates(node, peri_clks, ARRAY_SIZE(peri_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); + + mtk_register_reset_controller(node, 2, 0); +} +CLK_OF_DECLARE(mtk_pericfg, "mediatek,mt8135-pericfg", mtk_pericfg_init); + +#define MT8135_PLL_FMAX (2000 * MHZ) +#define CON0_MT8135_RST_BAR BIT(27) + +#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, _pcw_shift) { \ + .id = _id, \ + .name = _name, \ + .reg = _reg, \ + .pwr_reg = _pwr_reg, \ + .en_mask = _en_mask, \ + .flags = _flags, \ + .rst_bar_mask = CON0_MT8135_RST_BAR, \ + .fmax = MT8135_PLL_FMAX, \ + .pcwbits = _pcwbits, \ + .pd_reg = _pd_reg, \ + .pd_shift = _pd_shift, \ + .tuner_reg = _tuner_reg, \ + .pcw_reg = _pcw_reg, \ + .pcw_shift = _pcw_shift, \ + } + +static const struct mtk_pll_data plls[] = { + PLL(APMIXED_ARMPLL1, "armpll1", 0x200, 0x218, 0x80000001, 0, 21, 0x204, 24, 0x0, 0x204, 0), + PLL(APMIXED_ARMPLL2, "armpll2", 0x2cc, 0x2e4, 0x80000001, 0, 21, 0x2d0, 24, 0x0, 0x2d0, 0), + PLL(APMIXED_MAINPLL, "mainpll", 0x21c, 0x234, 0xf0000001, HAVE_RST_BAR, 21, 0x21c, 6, 0x0, 0x220, 0), + PLL(APMIXED_UNIVPLL, "univpll", 0x238, 0x250, 0xf3000001, HAVE_RST_BAR, 7, 0x238, 6, 0x0, 0x238, 9), + PLL(APMIXED_MMPLL, "mmpll", 0x254, 0x26c, 0xf0000001, HAVE_RST_BAR, 21, 0x254, 6, 0x0, 0x258, 0), + PLL(APMIXED_MSDCPLL, "msdcpll", 0x278, 0x290, 0x80000001, 0, 21, 0x278, 6, 0x0, 0x27c, 0), + PLL(APMIXED_TVDPLL, "tvdpll", 0x294, 0x2ac, 0x80000001, 0, 31, 0x294, 6, 0x0, 0x298, 0), + PLL(APMIXED_LVDSPLL, "lvdspll", 0x2b0, 0x2c8, 0x80000001, 0, 21, 0x2b0, 6, 0x0, 0x2b4, 0), + PLL(APMIXED_AUDPLL, "audpll", 0x2e8, 0x300, 0x80000001, 0, 31, 0x2e8, 6, 0x2f8, 0x2ec, 0), + PLL(APMIXED_VDECPLL, "vdecpll", 0x304, 0x31c, 0x80000001, 0, 21, 0x2b0, 6, 0x0, 0x308, 0), +}; + +static void __init mtk_apmixedsys_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + + clk_data = mtk_alloc_clk_data(ARRAY_SIZE(plls)); + if (!clk_data) + return; + + mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data); +} +CLK_OF_DECLARE(mtk_apmixedsys, "mediatek,mt8135-apmixedsys", + mtk_apmixedsys_init); diff --git a/include/dt-bindings/clock/mt8135-clk.h b/include/dt-bindings/clock/mt8135-clk.h new file mode 100644 index 0000000..8aea762 --- /dev/null +++ b/include/dt-bindings/clock/mt8135-clk.h @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao <jamesjj.liao@mediatek.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. + */ + +#ifndef _DT_BINDINGS_CLK_MT8135_H +#define _DT_BINDINGS_CLK_MT8135_H + +/* TOPCKGEN */ + +#define TOP_DSI0_LNTC_DSICLK 1 +#define TOP_HDMITX_CLKDIG_CTS 2 +#define TOP_CLKPH_MCK 3 +#define TOP_CPUM_TCK_IN 4 +#define TOP_MAINPLL_806M 5 +#define TOP_MAINPLL_537P3M 6 +#define TOP_MAINPLL_322P4M 7 +#define TOP_MAINPLL_230P3M 8 +#define TOP_UNIVPLL_624M 9 +#define TOP_UNIVPLL_416M 10 +#define TOP_UNIVPLL_249P6M 11 +#define TOP_UNIVPLL_178P3M 12 +#define TOP_UNIVPLL_48M 13 +#define TOP_MMPLL_D2 14 +#define TOP_MMPLL_D3 15 +#define TOP_MMPLL_D5 16 +#define TOP_MMPLL_D7 17 +#define TOP_MMPLL_D4 18 +#define TOP_MMPLL_D6 19 +#define TOP_SYSPLL_D2 20 +#define TOP_SYSPLL_D4 21 +#define TOP_SYSPLL_D6 22 +#define TOP_SYSPLL_D8 23 +#define TOP_SYSPLL_D10 24 +#define TOP_SYSPLL_D12 25 +#define TOP_SYSPLL_D16 26 +#define TOP_SYSPLL_D24 27 +#define TOP_SYSPLL_D3 28 +#define TOP_SYSPLL_D2P5 29 +#define TOP_SYSPLL_D5 30 +#define TOP_SYSPLL_D3P5 31 +#define TOP_UNIVPLL1_D2 32 +#define TOP_UNIVPLL1_D4 33 +#define TOP_UNIVPLL1_D6 34 +#define TOP_UNIVPLL1_D8 35 +#define TOP_UNIVPLL1_D10 36 +#define TOP_UNIVPLL2_D2 37 +#define TOP_UNIVPLL2_D4 38 +#define TOP_UNIVPLL2_D6 39 +#define TOP_UNIVPLL2_D8 40 +#define TOP_UNIVPLL_D3 41 +#define TOP_UNIVPLL_D5 42 +#define TOP_UNIVPLL_D7 43 +#define TOP_UNIVPLL_D10 44 +#define TOP_UNIVPLL_D26 45 +#define TOP_APLL_CK 46 +#define TOP_APLL_D4 47 +#define TOP_APLL_D8 48 +#define TOP_APLL_D16 49 +#define TOP_APLL_D24 50 +#define TOP_LVDSPLL_D2 51 +#define TOP_LVDSPLL_D4 52 +#define TOP_LVDSPLL_D8 53 +#define TOP_LVDSTX_CLKDIG_CT 54 +#define TOP_VPLL_DPIX_CK 55 +#define TOP_TVHDMI_H_CK 56 +#define TOP_HDMITX_CLKDIG_D2 57 +#define TOP_HDMITX_CLKDIG_D3 58 +#define TOP_TVHDMI_D2 59 +#define TOP_TVHDMI_D4 60 +#define TOP_MEMPLL_MCK_D4 61 +#define TOP_AXI_SEL 62 +#define TOP_SMI_SEL 63 +#define TOP_MFG_SEL 64 +#define TOP_IRDA_SEL 65 +#define TOP_CAM_SEL 66 +#define TOP_AUD_INTBUS_SEL 67 +#define TOP_JPG_SEL 68 +#define TOP_DISP_SEL 69 +#define TOP_MSDC30_1_SEL 70 +#define TOP_MSDC30_2_SEL 71 +#define TOP_MSDC30_3_SEL 72 +#define TOP_MSDC30_4_SEL 73 +#define TOP_USB20_SEL 74 +#define TOP_VENC_SEL 75 +#define TOP_SPI_SEL 76 +#define TOP_UART_SEL 77 +#define TOP_MEM_SEL 78 +#define TOP_CAMTG_SEL 79 +#define TOP_AUDIO_SEL 80 +#define TOP_FIX_SEL 81 +#define TOP_VDEC_SEL 82 +#define TOP_DDRPHYCFG_SEL 83 +#define TOP_DPILVDS_SEL 84 +#define TOP_PMICSPI_SEL 85 +#define TOP_MSDC30_0_SEL 86 +#define TOP_SMI_MFG_AS_SEL 87 +#define TOP_GCPU_SEL 88 +#define TOP_DPI1_SEL 89 +#define TOP_CCI_SEL 90 +#define TOP_APLL_SEL 91 +#define TOP_HDMIPLL_SEL 92 +#define TOP_NR_CLK 93 + +/* APMIXED_SYS */ + +#define APMIXED_ARMPLL1 1 +#define APMIXED_ARMPLL2 2 +#define APMIXED_MAINPLL 3 +#define APMIXED_UNIVPLL 4 +#define APMIXED_MMPLL 5 +#define APMIXED_MSDCPLL 6 +#define APMIXED_TVDPLL 7 +#define APMIXED_LVDSPLL 8 +#define APMIXED_AUDPLL 9 +#define APMIXED_VDECPLL 10 +#define APMIXED_NR_CLK 11 + +/* INFRA_SYS */ + +#define INFRA_PMIC_WRAP_CK 1 +#define INFRA_PMICSPI_CK 2 +#define INFRA_CCIF1_AP_CTRL 3 +#define INFRA_CCIF0_AP_CTRL 4 +#define INFRA_KP_CK 5 +#define INFRA_CPUM_CK 6 +#define INFRA_M4U_CK 7 +#define INFRA_MFGAXI_CK 8 +#define INFRA_DEVAPC_CK 9 +#define INFRA_AUDIO_CK 10 +#define INFRA_MFG_BUS_CK 11 +#define INFRA_SMI_CK 12 +#define INFRA_DBGCLK_CK 13 +#define INFRA_NR_CLK 14 + +/* PERI_SYS */ + +#define PERI_I2C5_CK 1 +#define PERI_I2C4_CK 2 +#define PERI_I2C3_CK 3 +#define PERI_I2C2_CK 4 +#define PERI_I2C1_CK 5 +#define PERI_I2C0_CK 6 +#define PERI_UART3_CK 7 +#define PERI_UART2_CK 8 +#define PERI_UART1_CK 9 +#define PERI_UART0_CK 10 +#define PERI_IRDA_CK 11 +#define PERI_NLI_CK 12 +#define PERI_MD_HIF_CK 13 +#define PERI_AP_HIF_CK 14 +#define PERI_MSDC30_3_CK 15 +#define PERI_MSDC30_2_CK 16 +#define PERI_MSDC30_1_CK 17 +#define PERI_MSDC20_2_CK 18 +#define PERI_MSDC20_1_CK 19 +#define PERI_AP_DMA_CK 20 +#define PERI_USB1_CK 21 +#define PERI_USB0_CK 22 +#define PERI_PWM_CK 23 +#define PERI_PWM7_CK 24 +#define PERI_PWM6_CK 25 +#define PERI_PWM5_CK 26 +#define PERI_PWM4_CK 27 +#define PERI_PWM3_CK 28 +#define PERI_PWM2_CK 29 +#define PERI_PWM1_CK 30 +#define PERI_THERM_CK 31 +#define PERI_NFI_CK 32 +#define PERI_USBSLV_CK 33 +#define PERI_USB1_MCU_CK 34 +#define PERI_USB0_MCU_CK 35 +#define PERI_GCPU_CK 36 +#define PERI_FHCTL_CK 37 +#define PERI_SPI1_CK 38 +#define PERI_AUXADC_CK 39 +#define PERI_PERI_PWRAP_CK 40 +#define PERI_I2C6_CK 41 +#define PERI_NR_CLK 42 + +#endif /* _DT_BINDINGS_CLK_MT8135_H */ diff --git a/include/dt-bindings/reset-controller/mt8135-resets.h b/include/dt-bindings/reset-controller/mt8135-resets.h new file mode 100644 index 0000000..1fb6295 --- /dev/null +++ b/include/dt-bindings/reset-controller/mt8135-resets.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Flora Fu, MediaTek + * + * 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. + */ + +#ifndef _DT_BINDINGS_RESET_CONTROLLER_MT8135 +#define _DT_BINDINGS_RESET_CONTROLLER_MT8135 + +/* INFRACFG resets */ +#define MT8135_INFRA_EMI_REG_RST 0 +#define MT8135_INFRA_DRAMC0_A0_RST 1 +#define MT8135_INFRA_CCIF0_RST 2 +#define MT8135_INFRA_APCIRQ_EINT_RST 3 +#define MT8135_INFRA_APXGPT_RST 4 +#define MT8135_INFRA_SCPSYS_RST 5 +#define MT8135_INFRA_CCIF1_RST 6 +#define MT8135_INFRA_PMIC_WRAP_RST 7 +#define MT8135_INFRA_KP_RST 8 +#define MT8135_INFRA_EMI_RST 32 +#define MT8135_INFRA_DRAMC0_RST 34 +#define MT8135_INFRA_SMI_RST 35 +#define MT8135_INFRA_M4U_RST 36 + +/* PERICFG resets */ +#define MT8135_PERI_UART0_SW_RST 0 +#define MT8135_PERI_UART1_SW_RST 1 +#define MT8135_PERI_UART2_SW_RST 2 +#define MT8135_PERI_UART3_SW_RST 3 +#define MT8135_PERI_IRDA_SW_RST 4 +#define MT8135_PERI_PTP_SW_RST 5 +#define MT8135_PERI_AP_HIF_SW_RST 6 +#define MT8135_PERI_GPCU_SW_RST 7 +#define MT8135_PERI_MD_HIF_SW_RST 8 +#define MT8135_PERI_NLI_SW_RST 9 +#define MT8135_PERI_AUXADC_SW_RST 10 +#define MT8135_PERI_DMA_SW_RST 11 +#define MT8135_PERI_NFI_SW_RST 14 +#define MT8135_PERI_PWM_SW_RST 15 +#define MT8135_PERI_THERM_SW_RST 16 +#define MT8135_PERI_MSDC0_SW_RST 17 +#define MT8135_PERI_MSDC1_SW_RST 18 +#define MT8135_PERI_MSDC2_SW_RST 19 +#define MT8135_PERI_MSDC3_SW_RST 20 +#define MT8135_PERI_I2C0_SW_RST 22 +#define MT8135_PERI_I2C1_SW_RST 23 +#define MT8135_PERI_I2C2_SW_RST 24 +#define MT8135_PERI_I2C3_SW_RST 25 +#define MT8135_PERI_I2C4_SW_RST 26 +#define MT8135_PERI_I2C5_SW_RST 27 +#define MT8135_PERI_I2C6_SW_RST 28 +#define MT8135_PERI_USB_SW_RST 29 +#define MT8135_PERI_SPI1_SW_RST 33 +#define MT8135_PERI_PWRAP_BRIDGE_SW_RST 34 + +#endif /* _DT_BINDINGS_RESET_CONTROLLER_MT8135 */ -- 2.1.4 ^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 4/6] clk: mediatek: Add basic clocks for Mediatek MT8135. 2015-03-19 8:42 ` [PATCH 4/6] clk: mediatek: Add basic clocks for Mediatek MT8135 Sascha Hauer @ 2015-03-27 7:39 ` Stephen Boyd 2015-03-27 9:21 ` Sascha Hauer 0 siblings, 1 reply; 22+ messages in thread From: Stephen Boyd @ 2015-03-27 7:39 UTC (permalink / raw) To: linux-arm-kernel On 03/19, Sascha Hauer wrote: > diff --git a/drivers/clk/mediatek/clk-mt8135.c b/drivers/clk/mediatek/clk-mt8135.c > new file mode 100644 > index 0000000..a22f6fa > --- /dev/null > +++ b/drivers/clk/mediatek/clk-mt8135.c > @@ -0,0 +1,640 @@ > +/* > + * Copyright (c) 2014 MediaTek Inc. > + * Author: James Liao <jamesjj.liao@mediatek.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. > + */ > + > +#include <linux/of.h> > +#include <linux/of_address.h> > +#include <linux/slab.h> > +#include <linux/mfd/syscon.h> > +#include <dt-bindings/clock/mt8135-clk.h> > + > +#include "clk-mtk.h" > +#include "clk-gate.h" > + > +static DEFINE_SPINLOCK(lock); Nitpick: 'lock' is very generic. Lockdep will use the variable name in its output, so it's wise to make locks have better names if possible. How about mt8135_lock? -- Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project ^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 4/6] clk: mediatek: Add basic clocks for Mediatek MT8135. 2015-03-27 7:39 ` Stephen Boyd @ 2015-03-27 9:21 ` Sascha Hauer 0 siblings, 0 replies; 22+ messages in thread From: Sascha Hauer @ 2015-03-27 9:21 UTC (permalink / raw) To: linux-arm-kernel On Fri, Mar 27, 2015 at 12:39:50AM -0700, Stephen Boyd wrote: > On 03/19, Sascha Hauer wrote: > > diff --git a/drivers/clk/mediatek/clk-mt8135.c b/drivers/clk/mediatek/clk-mt8135.c > > new file mode 100644 > > index 0000000..a22f6fa > > --- /dev/null > > +++ b/drivers/clk/mediatek/clk-mt8135.c > > @@ -0,0 +1,640 @@ > > +/* > > + * Copyright (c) 2014 MediaTek Inc. > > + * Author: James Liao <jamesjj.liao@mediatek.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. > > + */ > > + > > +#include <linux/of.h> > > +#include <linux/of_address.h> > > +#include <linux/slab.h> > > +#include <linux/mfd/syscon.h> > > +#include <dt-bindings/clock/mt8135-clk.h> > > + > > +#include "clk-mtk.h" > > +#include "clk-gate.h" > > + > > +static DEFINE_SPINLOCK(lock); > > Nitpick: 'lock' is very generic. Lockdep will use the variable > name in its output, so it's wise to make locks have better names > if possible. How about mt8135_lock? I just renamed this to mt8135_clk_lock/mt8173_clk_lock and resent the series. Sascha -- Pengutronix e.K. | | Industrial Linux Solutions | http://www.pengutronix.de/ | Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 | ^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 5/6] clk: mediatek: Add basic clocks for Mediatek MT8173. 2015-03-19 8:42 [PATCH v8]: clk: Add common clock support for Mediatek MT8135 and MT8173 Sascha Hauer ` (3 preceding siblings ...) 2015-03-19 8:42 ` [PATCH 4/6] clk: mediatek: Add basic clocks for Mediatek MT8135 Sascha Hauer @ 2015-03-19 8:42 ` Sascha Hauer 2015-03-19 8:42 ` [PATCH 6/6] dt-bindings: ARM: Mediatek: Document devicetree bindings for clock/reset controllers Sascha Hauer 2015-03-27 6:05 ` [PATCH v8]: clk: Add common clock support for Mediatek MT8135 and MT8173 Sascha Hauer 6 siblings, 0 replies; 22+ messages in thread From: Sascha Hauer @ 2015-03-19 8:42 UTC (permalink / raw) To: linux-arm-kernel From: James Liao <jamesjj.liao@mediatek.com> This patch adds basic clocks for MT8173, including TOPCKGEN, PLLs, INFRA and PERI clocks. Signed-off-by: James Liao <jamesjj.liao@mediatek.com> Signed-off-by: Henry Chen <henryc.chen@mediatek.com> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- drivers/clk/mediatek/Makefile | 1 + drivers/clk/mediatek/clk-mt8173.c | 826 +++++++++++++++++++++ include/dt-bindings/clock/mt8173-clk.h | 231 ++++++ .../dt-bindings/reset-controller/mt8173-resets.h | 63 ++ 4 files changed, 1121 insertions(+) create mode 100644 drivers/clk/mediatek/clk-mt8173.c create mode 100644 include/dt-bindings/clock/mt8173-clk.h create mode 100644 include/dt-bindings/reset-controller/mt8173-resets.h diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile index 12ce576..8e4b2a4 100644 --- a/drivers/clk/mediatek/Makefile +++ b/drivers/clk/mediatek/Makefile @@ -1,3 +1,4 @@ obj-y += clk-mtk.o clk-pll.o clk-gate.o obj-$(CONFIG_RESET_CONTROLLER) += reset.o obj-y += clk-mt8135.o +obj-y += clk-mt8173.o diff --git a/drivers/clk/mediatek/clk-mt8173.c b/drivers/clk/mediatek/clk-mt8173.c new file mode 100644 index 0000000..23fe863 --- /dev/null +++ b/drivers/clk/mediatek/clk-mt8173.c @@ -0,0 +1,826 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao <jamesjj.liao@mediatek.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. + */ + +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/slab.h> +#include <linux/mfd/syscon.h> + +#include "clk-mtk.h" +#include "clk-gate.h" + +#include <dt-bindings/clock/mt8173-clk.h> + +static DEFINE_SPINLOCK(lock); + +static const struct mtk_fixed_factor root_clk_alias[] __initconst = { + FACTOR(TOP_CLKPH_MCK_O, "clkph_mck_o", "clk_null", 1, 1), + FACTOR(TOP_DPI_CK, "dpi_ck", "clk_null", 1, 1), + FACTOR(TOP_USB_SYSPLL_125M, "usb_syspll_125m", "clk_null", 1, 1), + FACTOR(TOP_HDMITX_DIG_CTS, "hdmitx_dig_cts", "clk_null", 1, 1), +}; + +static const struct mtk_fixed_factor top_divs[] __initconst = { + FACTOR(TOP_ARMCA7PLL_754M, "armca7pll_754m", "armca7pll", 1, 2), + FACTOR(TOP_ARMCA7PLL_502M, "armca7pll_502m", "armca7pll", 1, 3), + + FACTOR(TOP_MAIN_H546M, "main_h546m", "mainpll", 1, 2), + FACTOR(TOP_MAIN_H364M, "main_h364m", "mainpll", 1, 3), + FACTOR(TOP_MAIN_H218P4M, "main_h218p4m", "mainpll", 1, 5), + FACTOR(TOP_MAIN_H156M, "main_h156m", "mainpll", 1, 7), + + FACTOR(TOP_TVDPLL_445P5M, "tvdpll_445p5m", "tvdpll", 1, 4), + FACTOR(TOP_TVDPLL_594M, "tvdpll_594m", "tvdpll", 1, 3), + + FACTOR(TOP_UNIV_624M, "univ_624m", "univpll", 1, 2), + FACTOR(TOP_UNIV_416M, "univ_416m", "univpll", 1, 3), + FACTOR(TOP_UNIV_249P6M, "univ_249p6m", "univpll", 1, 5), + FACTOR(TOP_UNIV_178P3M, "univ_178p3m", "univpll", 1, 7), + FACTOR(TOP_UNIV_48M, "univ_48m", "univpll", 1, 26), + + FACTOR(TOP_CLKRTC_EXT, "clkrtc_ext", "clk32k", 1, 1), + FACTOR(TOP_CLKRTC_INT, "clkrtc_int", "clk26m", 1, 793), + FACTOR(TOP_FPC_CK, "fpc_ck", "clk26m", 1, 1), + + FACTOR(TOP_HDMITXPLL_D2, "hdmitxpll_d2", "hdmitx_dig_cts", 1, 2), + FACTOR(TOP_HDMITXPLL_D3, "hdmitxpll_d3", "hdmitx_dig_cts", 1, 3), + + FACTOR(TOP_ARMCA7PLL_D2, "armca7pll_d2", "armca7pll_754m", 1, 1), + FACTOR(TOP_ARMCA7PLL_D3, "armca7pll_d3", "armca7pll_502m", 1, 1), + + FACTOR(TOP_APLL1_CK, "apll1_ck", "apll1", 1, 1), + FACTOR(TOP_APLL2_CK, "apll2_ck", "apll2", 1, 1), + + FACTOR(TOP_DMPLL_CK, "dmpll_ck", "clkph_mck_o", 1, 1), + FACTOR(TOP_DMPLL_D2, "dmpll_d2", "clkph_mck_o", 1, 2), + FACTOR(TOP_DMPLL_D4, "dmpll_d4", "clkph_mck_o", 1, 4), + FACTOR(TOP_DMPLL_D8, "dmpll_d8", "clkph_mck_o", 1, 8), + FACTOR(TOP_DMPLL_D16, "dmpll_d16", "clkph_mck_o", 1, 16), + + FACTOR(TOP_LVDSPLL_D2, "lvdspll_d2", "lvdspll", 1, 2), + FACTOR(TOP_LVDSPLL_D4, "lvdspll_d4", "lvdspll", 1, 4), + FACTOR(TOP_LVDSPLL_D8, "lvdspll_d8", "lvdspll", 1, 8), + + FACTOR(TOP_MMPLL_CK, "mmpll_ck", "mmpll", 1, 1), + FACTOR(TOP_MMPLL_D2, "mmpll_d2", "mmpll", 1, 2), + + FACTOR(TOP_MSDCPLL_CK, "msdcpll_ck", "msdcpll", 1, 1), + FACTOR(TOP_MSDCPLL_D2, "msdcpll_d2", "msdcpll", 1, 2), + FACTOR(TOP_MSDCPLL_D4, "msdcpll_d4", "msdcpll", 1, 4), + FACTOR(TOP_MSDCPLL2_CK, "msdcpll2_ck", "msdcpll2", 1, 1), + FACTOR(TOP_MSDCPLL2_D2, "msdcpll2_d2", "msdcpll2", 1, 2), + FACTOR(TOP_MSDCPLL2_D4, "msdcpll2_d4", "msdcpll2", 1, 4), + + FACTOR(TOP_SYSPLL_D2, "syspll_d2", "main_h546m", 1, 1), + FACTOR(TOP_SYSPLL1_D2, "syspll1_d2", "main_h546m", 1, 2), + FACTOR(TOP_SYSPLL1_D4, "syspll1_d4", "main_h546m", 1, 4), + FACTOR(TOP_SYSPLL1_D8, "syspll1_d8", "main_h546m", 1, 8), + FACTOR(TOP_SYSPLL1_D16, "syspll1_d16", "main_h546m", 1, 16), + FACTOR(TOP_SYSPLL_D3, "syspll_d3", "main_h364m", 1, 1), + FACTOR(TOP_SYSPLL2_D2, "syspll2_d2", "main_h364m", 1, 2), + FACTOR(TOP_SYSPLL2_D4, "syspll2_d4", "main_h364m", 1, 4), + FACTOR(TOP_SYSPLL_D5, "syspll_d5", "main_h218p4m", 1, 1), + FACTOR(TOP_SYSPLL3_D2, "syspll3_d2", "main_h218p4m", 1, 2), + FACTOR(TOP_SYSPLL3_D4, "syspll3_d4", "main_h218p4m", 1, 4), + FACTOR(TOP_SYSPLL_D7, "syspll_d7", "main_h156m", 1, 1), + FACTOR(TOP_SYSPLL4_D2, "syspll4_d2", "main_h156m", 1, 2), + FACTOR(TOP_SYSPLL4_D4, "syspll4_d4", "main_h156m", 1, 4), + + FACTOR(TOP_TVDPLL_CK, "tvdpll_ck", "tvdpll_594m", 1, 1), + FACTOR(TOP_TVDPLL_D2, "tvdpll_d2", "tvdpll_594m", 1, 2), + FACTOR(TOP_TVDPLL_D4, "tvdpll_d4", "tvdpll_594m", 1, 4), + FACTOR(TOP_TVDPLL_D8, "tvdpll_d8", "tvdpll_594m", 1, 8), + FACTOR(TOP_TVDPLL_D16, "tvdpll_d16", "tvdpll_594m", 1, 16), + + FACTOR(TOP_UNIVPLL_D2, "univpll_d2", "univ_624m", 1, 1), + FACTOR(TOP_UNIVPLL1_D2, "univpll1_d2", "univ_624m", 1, 2), + FACTOR(TOP_UNIVPLL1_D4, "univpll1_d4", "univ_624m", 1, 4), + FACTOR(TOP_UNIVPLL1_D8, "univpll1_d8", "univ_624m", 1, 8), + FACTOR(TOP_UNIVPLL_D3, "univpll_d3", "univ_416m", 1, 1), + FACTOR(TOP_UNIVPLL2_D2, "univpll2_d2", "univ_416m", 1, 2), + FACTOR(TOP_UNIVPLL2_D4, "univpll2_d4", "univ_416m", 1, 4), + FACTOR(TOP_UNIVPLL2_D8, "univpll2_d8", "univ_416m", 1, 8), + FACTOR(TOP_UNIVPLL_D5, "univpll_d5", "univ_249p6m", 1, 1), + FACTOR(TOP_UNIVPLL3_D2, "univpll3_d2", "univ_249p6m", 1, 2), + FACTOR(TOP_UNIVPLL3_D4, "univpll3_d4", "univ_249p6m", 1, 4), + FACTOR(TOP_UNIVPLL3_D8, "univpll3_d8", "univ_249p6m", 1, 8), + FACTOR(TOP_UNIVPLL_D7, "univpll_d7", "univ_178p3m", 1, 1), + FACTOR(TOP_UNIVPLL_D26, "univpll_d26", "univ_48m", 1, 1), + FACTOR(TOP_UNIVPLL_D52, "univpll_d52", "univ_48m", 1, 2), + + FACTOR(TOP_VCODECPLL_CK, "vcodecpll_ck", "vcodecpll", 1, 3), + FACTOR(TOP_VCODECPLL_370P5, "vcodecpll_370p5", "vcodecpll", 1, 4), + + FACTOR(TOP_VENCPLL_CK, "vencpll_ck", "vencpll", 1, 1), + FACTOR(TOP_VENCPLL_D2, "vencpll_d2", "vencpll", 1, 2), + FACTOR(TOP_VENCPLL_D4, "vencpll_d4", "vencpll", 1, 4), +}; + +static const char * const axi_parents[] __initconst = { + "clk26m", + "syspll1_d2", + "syspll_d5", + "syspll1_d4", + "univpll_d5", + "univpll2_d2", + "dmpll_d2", + "dmpll_d4" +}; + +static const char * const mem_parents[] __initconst = { + "clk26m", + "dmpll_ck" +}; + +static const char * const ddrphycfg_parents[] __initconst = { + "clk26m", + "syspll1_d8" +}; + +static const char * const mm_parents[] __initconst = { + "clk26m", + "vencpll_d2", + "main_h364m", + "syspll1_d2", + "syspll_d5", + "syspll1_d4", + "univpll1_d2", + "univpll2_d2", + "dmpll_d2" +}; + +static const char * const pwm_parents[] __initconst = { + "clk26m", + "univpll2_d4", + "univpll3_d2", + "univpll1_d4" +}; + +static const char * const vdec_parents[] __initconst = { + "clk26m", + "vcodecpll_ck", + "tvdpll_445p5m", + "univpll_d3", + "vencpll_d2", + "syspll_d3", + "univpll1_d2", + "mmpll_d2", + "dmpll_d2", + "dmpll_d4" +}; + +static const char * const venc_parents[] __initconst = { + "clk26m", + "vcodecpll_ck", + "tvdpll_445p5m", + "univpll_d3", + "vencpll_d2", + "syspll_d3", + "univpll1_d2", + "univpll2_d2", + "dmpll_d2", + "dmpll_d4" +}; + +static const char * const mfg_parents[] __initconst = { + "clk26m", + "mmpll_ck", + "dmpll_ck", + "clk26m", + "clk26m", + "clk26m", + "clk26m", + "clk26m", + "clk26m", + "syspll_d3", + "syspll1_d2", + "syspll_d5", + "univpll_d3", + "univpll1_d2", + "univpll_d5", + "univpll2_d2" +}; + +static const char * const camtg_parents[] __initconst = { + "clk26m", + "univpll_d26", + "univpll2_d2", + "syspll3_d2", + "syspll3_d4", + "univpll1_d4" +}; + +static const char * const uart_parents[] __initconst = { + "clk26m", + "univpll2_d8" +}; + +static const char * const spi_parents[] __initconst = { + "clk26m", + "syspll3_d2", + "syspll1_d4", + "syspll4_d2", + "univpll3_d2", + "univpll2_d4", + "univpll1_d8" +}; + +static const char * const usb20_parents[] __initconst = { + "clk26m", + "univpll1_d8", + "univpll3_d4" +}; + +static const char * const usb30_parents[] __initconst = { + "clk26m", + "univpll3_d2", + "usb_syspll_125m", + "univpll2_d4" +}; + +static const char * const msdc50_0_h_parents[] __initconst = { + "clk26m", + "syspll1_d2", + "syspll2_d2", + "syspll4_d2", + "univpll_d5", + "univpll1_d4" +}; + +static const char * const msdc50_0_parents[] __initconst = { + "clk26m", + "msdcpll_ck", + "msdcpll_d2", + "univpll1_d4", + "syspll2_d2", + "syspll_d7", + "msdcpll_d4", + "vencpll_d4", + "tvdpll_ck", + "univpll_d2", + "univpll1_d2", + "mmpll_ck", + "msdcpll2_ck", + "msdcpll2_d2", + "msdcpll2_d4" +}; + +static const char * const msdc30_1_parents[] __initconst = { + "clk26m", + "univpll2_d2", + "msdcpll_d4", + "univpll1_d4", + "syspll2_d2", + "syspll_d7", + "univpll_d7", + "vencpll_d4" +}; + +static const char * const msdc30_2_parents[] __initconst = { + "clk26m", + "univpll2_d2", + "msdcpll_d4", + "univpll1_d4", + "syspll2_d2", + "syspll_d7", + "univpll_d7", + "vencpll_d2" +}; + +static const char * const msdc30_3_parents[] __initconst = { + "clk26m", + "msdcpll2_ck", + "msdcpll2_d2", + "univpll2_d2", + "msdcpll2_d4", + "msdcpll_d4", + "univpll1_d4", + "syspll2_d2", + "syspll_d7", + "univpll_d7", + "vencpll_d4", + "msdcpll_ck", + "msdcpll_d2", + "msdcpll_d4" +}; + +static const char * const audio_parents[] __initconst = { + "clk26m", + "syspll3_d4", + "syspll4_d4", + "syspll1_d16" +}; + +static const char * const aud_intbus_parents[] __initconst = { + "clk26m", + "syspll1_d4", + "syspll4_d2", + "univpll3_d2", + "univpll2_d8", + "dmpll_d4", + "dmpll_d8" +}; + +static const char * const pmicspi_parents[] __initconst = { + "clk26m", + "syspll1_d8", + "syspll3_d4", + "syspll1_d16", + "univpll3_d4", + "univpll_d26", + "dmpll_d8", + "dmpll_d16" +}; + +static const char * const scp_parents[] __initconst = { + "clk26m", + "syspll1_d2", + "univpll_d5", + "syspll_d5", + "dmpll_d2", + "dmpll_d4" +}; + +static const char * const atb_parents[] __initconst = { + "clk26m", + "syspll1_d2", + "univpll_d5", + "dmpll_d2" +}; + +static const char * const venc_lt_parents[] __initconst = { + "clk26m", + "univpll_d3", + "vcodecpll_ck", + "tvdpll_445p5m", + "vencpll_d2", + "syspll_d3", + "univpll1_d2", + "univpll2_d2", + "syspll1_d2", + "univpll_d5", + "vcodecpll_370p5", + "dmpll_ck" +}; + +static const char * const dpi0_parents[] __initconst = { + "clk26m", + "tvdpll_d2", + "tvdpll_d4", + "clk26m", + "clk26m", + "tvdpll_d8", + "tvdpll_d16" +}; + +static const char * const irda_parents[] __initconst = { + "clk26m", + "univpll2_d4", + "syspll2_d4" +}; + +static const char * const cci400_parents[] __initconst = { + "clk26m", + "vencpll_ck", + "armca7pll_754m", + "armca7pll_502m", + "univpll_d2", + "syspll_d2", + "msdcpll_ck", + "dmpll_ck" +}; + +static const char * const aud_1_parents[] __initconst = { + "clk26m", + "apll1_ck", + "univpll2_d4", + "univpll2_d8" +}; + +static const char * const aud_2_parents[] __initconst = { + "clk26m", + "apll2_ck", + "univpll2_d4", + "univpll2_d8" +}; + +static const char * const mem_mfg_in_parents[] __initconst = { + "clk26m", + "mmpll_ck", + "dmpll_ck", + "clk26m" +}; + +static const char * const axi_mfg_in_parents[] __initconst = { + "clk26m", + "axi_sel", + "dmpll_d2" +}; + +static const char * const scam_parents[] __initconst = { + "clk26m", + "syspll3_d2", + "univpll2_d4", + "dmpll_d4" +}; + +static const char * const spinfi_ifr_parents[] __initconst = { + "clk26m", + "univpll2_d8", + "univpll3_d4", + "syspll4_d2", + "univpll2_d4", + "univpll3_d2", + "syspll1_d4", + "univpll1_d4" +}; + +static const char * const hdmi_parents[] __initconst = { + "clk26m", + "hdmitx_dig_cts", + "hdmitxpll_d2", + "hdmitxpll_d3" +}; + +static const char * const dpilvds_parents[] __initconst = { + "clk26m", + "lvdspll", + "lvdspll_d2", + "lvdspll_d4", + "lvdspll_d8", + "fpc_ck" +}; + +static const char * const msdc50_2_h_parents[] __initconst = { + "clk26m", + "syspll1_d2", + "syspll2_d2", + "syspll4_d2", + "univpll_d5", + "univpll1_d4" +}; + +static const char * const hdcp_parents[] __initconst = { + "clk26m", + "syspll4_d2", + "syspll3_d4", + "univpll2_d4" +}; + +static const char * const hdcp_24m_parents[] __initconst = { + "clk26m", + "univpll_d26", + "univpll_d52", + "univpll2_d8" +}; + +static const char * const rtc_parents[] __initconst = { + "clkrtc_int", + "clkrtc_ext", + "clk26m", + "univpll3_d8" +}; + +static const char * const i2s0_m_ck_parents[] __initconst = { + "apll1_div1", + "apll2_div1" +}; + +static const char * const i2s1_m_ck_parents[] __initconst = { + "apll1_div2", + "apll2_div2" +}; + +static const char * const i2s2_m_ck_parents[] __initconst = { + "apll1_div3", + "apll2_div3" +}; + +static const char * const i2s3_m_ck_parents[] __initconst = { + "apll1_div4", + "apll2_div4" +}; + +static const char * const i2s3_b_ck_parents[] __initconst = { + "apll1_div5", + "apll2_div5" +}; + +static const struct mtk_composite top_muxes[] __initconst = { + /* CLK_CFG_0 */ + MUX(TOP_AXI_SEL, "axi_sel", axi_parents, 0x0040, 0, 3), + MUX(TOP_MEM_SEL, "mem_sel", mem_parents, 0x0040, 8, 1), + MUX_GATE(TOP_DDRPHYCFG_SEL, "ddrphycfg_sel", ddrphycfg_parents, 0x0040, 16, 1, 23), + MUX_GATE(TOP_MM_SEL, "mm_sel", mm_parents, 0x0040, 24, 4, 31), + /* CLK_CFG_1 */ + MUX_GATE(TOP_PWM_SEL, "pwm_sel", pwm_parents, 0x0050, 0, 2, 7), + MUX_GATE(TOP_VDEC_SEL, "vdec_sel", vdec_parents, 0x0050, 8, 4, 15), + MUX_GATE(TOP_VENC_SEL, "venc_sel", venc_parents, 0x0050, 16, 4, 23), + MUX_GATE(TOP_MFG_SEL, "mfg_sel", mfg_parents, 0x0050, 24, 4, 31), + /* CLK_CFG_2 */ + MUX_GATE(TOP_CAMTG_SEL, "camtg_sel", camtg_parents, 0x0060, 0, 3, 7), + MUX_GATE(TOP_UART_SEL, "uart_sel", uart_parents, 0x0060, 8, 1, 15), + MUX_GATE(TOP_SPI_SEL, "spi_sel", spi_parents, 0x0060, 16, 3, 23), + MUX_GATE(TOP_USB20_SEL, "usb20_sel", usb20_parents, 0x0060, 24, 2, 31), + /* CLK_CFG_3 */ + MUX_GATE(TOP_USB30_SEL, "usb30_sel", usb30_parents, 0x0070, 0, 2, 7), + MUX_GATE(TOP_MSDC50_0_H_SEL, "msdc50_0_h_sel", msdc50_0_h_parents, 0x0070, 8, 3, 15), + MUX_GATE(TOP_MSDC50_0_SEL, "msdc50_0_sel", msdc50_0_parents, 0x0070, 16, 4, 23), + MUX_GATE(TOP_MSDC30_1_SEL, "msdc30_1_sel", msdc30_1_parents, 0x0070, 24, 3, 31), + /* CLK_CFG_4 */ + MUX_GATE(TOP_MSDC30_2_SEL, "msdc30_2_sel", msdc30_2_parents, 0x0080, 0, 3, 7), + MUX_GATE(TOP_MSDC30_3_SEL, "msdc30_3_sel", msdc30_3_parents, 0x0080, 8, 4, 15), + MUX_GATE(TOP_AUDIO_SEL, "audio_sel", audio_parents, 0x0080, 16, 2, 23), + MUX_GATE(TOP_AUD_INTBUS_SEL, "aud_intbus_sel", aud_intbus_parents, 0x0080, 24, 3, 31), + /* CLK_CFG_5 */ + MUX_GATE(TOP_PMICSPI_SEL, "pmicspi_sel", pmicspi_parents, 0x0090, 0, 3, 7 /* 7:5 */), + MUX_GATE(TOP_SCP_SEL, "scp_sel", scp_parents, 0x0090, 8, 3, 15), + MUX_GATE(TOP_ATB_SEL, "atb_sel", atb_parents, 0x0090, 16, 2, 23), + MUX_GATE(TOP_VENC_LT_SEL, "venclt_sel", venc_lt_parents, 0x0090, 24, 4, 31), + /* CLK_CFG_6 */ + MUX_GATE(TOP_DPI0_SEL, "dpi0_sel", dpi0_parents, 0x00a0, 0, 3, 7), + MUX_GATE(TOP_IRDA_SEL, "irda_sel", irda_parents, 0x00a0, 8, 2, 15), + MUX_GATE(TOP_CCI400_SEL, "cci400_sel", cci400_parents, 0x00a0, 16, 3, 23), + MUX_GATE(TOP_AUD_1_SEL, "aud_1_sel", aud_1_parents, 0x00a0, 24, 2, 31), + /* CLK_CFG_7 */ + MUX_GATE(TOP_AUD_2_SEL, "aud_2_sel", aud_2_parents, 0x00b0, 0, 2, 7), + MUX_GATE(TOP_MEM_MFG_IN_SEL, "mem_mfg_in_sel", mem_mfg_in_parents, 0x00b0, 8, 2, 15), + MUX_GATE(TOP_AXI_MFG_IN_SEL, "axi_mfg_in_sel", axi_mfg_in_parents, 0x00b0, 16, 2, 23), + MUX_GATE(TOP_SCAM_SEL, "scam_sel", scam_parents, 0x00b0, 24, 2, 31), + /* CLK_CFG_12 */ + MUX_GATE(TOP_SPINFI_IFR_SEL, "spinfi_ifr_sel", spinfi_ifr_parents, 0x00c0, 0, 3, 7), + MUX_GATE(TOP_HDMI_SEL, "hdmi_sel", hdmi_parents, 0x00c0, 8, 2, 15), + MUX_GATE(TOP_DPILVDS_SEL, "dpilvds_sel", dpilvds_parents, 0x00c0, 24, 3, 31), + /* CLK_CFG_13 */ + MUX_GATE(TOP_MSDC50_2_H_SEL, "msdc50_2_h_sel", msdc50_2_h_parents, 0x00d0, 0, 3, 7), + MUX_GATE(TOP_HDCP_SEL, "hdcp_sel", hdcp_parents, 0x00d0, 8, 2, 15), + MUX_GATE(TOP_HDCP_24M_SEL, "hdcp_24m_sel", hdcp_24m_parents, 0x00d0, 16, 2, 23), + MUX(TOP_RTC_SEL, "rtc_sel", rtc_parents, 0x00d0, 24, 2), + + DIV_GATE(TOP_APLL1_DIV0, "apll1_div0", "aud_1_sel", 0x12c, 8, 0x120, 4, 24), + DIV_GATE(TOP_APLL1_DIV1, "apll1_div1", "aud_1_sel", 0x12c, 9, 0x124, 8, 0), + DIV_GATE(TOP_APLL1_DIV2, "apll1_div2", "aud_1_sel", 0x12c, 10, 0x124, 8, 8), + DIV_GATE(TOP_APLL1_DIV3, "apll1_div3", "aud_1_sel", 0x12c, 11, 0x124, 8, 16), + DIV_GATE(TOP_APLL1_DIV4, "apll1_div4", "aud_1_sel", 0x12c, 12, 0x124, 8, 24), + DIV_GATE(TOP_APLL1_DIV5, "apll1_div5", "aud_1_sel", 0x12c, 13, 0x12c, 4, 0), + + DIV_GATE(TOP_APLL2_DIV0, "apll2_div0", "aud_2_sel", 0x12c, 16, 0x120, 4, 28), + DIV_GATE(TOP_APLL2_DIV1, "apll2_div1", "aud_2_sel", 0x12c, 17, 0x128, 8, 0), + DIV_GATE(TOP_APLL2_DIV2, "apll2_div2", "aud_2_sel", 0x12c, 18, 0x128, 8, 8), + DIV_GATE(TOP_APLL2_DIV3, "apll2_div3", "aud_2_sel", 0x12c, 19, 0x128, 8, 16), + DIV_GATE(TOP_APLL2_DIV4, "apll2_div4", "aud_2_sel", 0x12c, 20, 0x128, 8, 24), + DIV_GATE(TOP_APLL2_DIV5, "apll2_div5", "aud_2_sel", 0x12c, 21, 0x12c, 4, 4), + + MUX(TOP_I2S0_M_CK_SEL, "i2s0_m_ck_sel", i2s0_m_ck_parents, 0x120, 4, 1), + MUX(TOP_I2S1_M_CK_SEL, "i2s1_m_ck_sel", i2s1_m_ck_parents, 0x120, 5, 1), + MUX(TOP_I2S2_M_CK_SEL, "i2s2_m_ck_sel", i2s2_m_ck_parents, 0x120, 6, 1), + MUX(TOP_I2S3_M_CK_SEL, "i2s3_m_ck_sel", i2s3_m_ck_parents, 0x120, 7, 1), + MUX(TOP_I2S3_B_CK_SEL, "i2s3_b_ck_sel", i2s3_b_ck_parents, 0x120, 8, 1), +}; + +static void __init mtk_init_clk_topckgen(void __iomem *top_base, + struct clk_onecell_data *clk_data) +{ + int i; + struct clk *clk; + + for (i = 0; i < ARRAY_SIZE(top_muxes); i++) { + const struct mtk_composite *mux = &top_muxes[i]; + + clk = mtk_clk_register_composite(mux, top_base, &lock); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + mux->name, PTR_ERR(clk)); + continue; + } + + if (clk_data) + clk_data->clks[mux->id] = clk; + } +} + +static const struct mtk_gate_regs infra_cg_regs = { + .set_ofs = 0x0040, + .clr_ofs = 0x0044, + .sta_ofs = 0x0048, +}; + +#define GATE_ICG(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &infra_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +static const struct mtk_gate infra_clks[] __initconst = { + GATE_ICG(INFRA_DBGCLK, "infra_dbgclk", "axi_sel", 0), + GATE_ICG(INFRA_SMI, "infra_smi", "mm_sel", 1), + GATE_ICG(INFRA_AUDIO, "infra_audio", "aud_intbus_sel", 5), + GATE_ICG(INFRA_GCE, "infra_gce", "axi_sel", 6), + GATE_ICG(INFRA_L2C_SRAM, "infra_l2c_sram", "axi_sel", 7), + GATE_ICG(INFRA_M4U, "infra_m4u", "mem_sel", 8), + GATE_ICG(INFRA_CPUM, "infra_cpum", "clk_null", 15), + GATE_ICG(INFRA_KP, "infra_kp", "axi_sel", 16), + GATE_ICG(INFRA_CEC, "infra_cec", "clk26m", 18), + GATE_ICG(INFRA_PMICSPI, "infra_pmicspi", "pmicspi_sel", 22), + GATE_ICG(INFRA_PMICWRAP, "infra_pmicwrap", "axi_sel", 23), +}; + +static const struct mtk_gate_regs peri0_cg_regs = { + .set_ofs = 0x0008, + .clr_ofs = 0x0010, + .sta_ofs = 0x0018, +}; + +static const struct mtk_gate_regs peri1_cg_regs = { + .set_ofs = 0x000c, + .clr_ofs = 0x0014, + .sta_ofs = 0x001c, +}; + +#define GATE_PERI0(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &peri0_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +#define GATE_PERI1(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &peri1_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +static const struct mtk_gate peri_clks[] __initconst = { + /* PERI0 */ + GATE_PERI0(PERI_NFI, "peri_nfi", "axi_sel", 0), + GATE_PERI0(PERI_THERM, "peri_therm", "axi_sel", 1), + GATE_PERI0(PERI_PWM1, "peri_pwm1", "axi_sel", 2), + GATE_PERI0(PERI_PWM2, "peri_pwm2", "axi_sel", 3), + GATE_PERI0(PERI_PWM3, "peri_pwm3", "axi_sel", 4), + GATE_PERI0(PERI_PWM4, "peri_pwm4", "axi_sel", 5), + GATE_PERI0(PERI_PWM5, "peri_pwm5", "axi_sel", 6), + GATE_PERI0(PERI_PWM6, "peri_pwm6", "axi_sel", 7), + GATE_PERI0(PERI_PWM7, "peri_pwm7", "axi_sel", 8), + GATE_PERI0(PERI_PWM, "peri_pwm", "axi_sel", 9), + GATE_PERI0(PERI_USB0, "peri_usb0", "usb20_sel", 10), + GATE_PERI0(PERI_USB1, "peri_usb1", "usb20_sel", 11), + GATE_PERI0(PERI_AP_DMA, "peri_ap_dma", "axi_sel", 12), + GATE_PERI0(PERI_MSDC30_0, "peri_msdc30_0", "msdc50_0_sel", 13), + GATE_PERI0(PERI_MSDC30_1, "peri_msdc30_1", "msdc30_1_sel", 14), + GATE_PERI0(PERI_MSDC30_2, "peri_msdc30_2", "msdc30_2_sel", 15), + GATE_PERI0(PERI_MSDC30_3, "peri_msdc30_3", "msdc30_3_sel", 16), + GATE_PERI0(PERI_NLI_ARB, "peri_nli_arb", "axi_sel", 17), + GATE_PERI0(PERI_IRDA, "peri_irda", "irda_sel", 18), + GATE_PERI0(PERI_UART0, "peri_uart0", "uart_sel", 19), + GATE_PERI0(PERI_UART1, "peri_uart1", "uart_sel", 20), + GATE_PERI0(PERI_UART2, "peri_uart2", "uart_sel", 21), + GATE_PERI0(PERI_UART3, "peri_uart3", "uart_sel", 22), + GATE_PERI0(PERI_I2C0, "peri_i2c0", "axi_sel", 23), + GATE_PERI0(PERI_I2C1, "peri_i2c1", "axi_sel", 24), + GATE_PERI0(PERI_I2C2, "peri_i2c2", "axi_sel", 25), + GATE_PERI0(PERI_I2C3, "peri_i2c3", "axi_sel", 26), + GATE_PERI0(PERI_I2C4, "peri_i2c4", "axi_sel", 27), + GATE_PERI0(PERI_AUXADC, "peri_auxadc", "clk26m", 28), + GATE_PERI0(PERI_SPI0, "peri_spi0", "spi_sel", 29), + GATE_PERI0(PERI_I2C5, "peri_i2c5", "axi_sel", 30), + GATE_PERI0(PERI_NFIECC, "peri_nfiecc", "axi_sel", 31), + /* PERI1 */ + GATE_PERI1(PERI_SPI, "peri_spi", "spi_sel", 0), + GATE_PERI1(PERI_IRRX, "peri_irrx", "spi_sel", 1), + GATE_PERI1(PERI_I2C6, "peri_i2c6", "axi_sel", 2), +}; + +static void __init mtk_topckgen_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + void __iomem *base; + int r; + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s(): ioremap failed\n", __func__); + return; + } + + clk_data = mtk_alloc_clk_data(TOP_NR_CLK); + + mtk_clk_register_factors(root_clk_alias, ARRAY_SIZE(root_clk_alias), clk_data); + mtk_clk_register_factors(top_divs, ARRAY_SIZE(top_divs), clk_data); + mtk_init_clk_topckgen(base, clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} +CLK_OF_DECLARE(mtk_topckgen, "mediatek,mt8173-topckgen", mtk_topckgen_init); + +static void __init mtk_infrasys_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + + clk_data = mtk_alloc_clk_data(INFRA_NR_CLK); + + mtk_clk_register_gates(node, infra_clks, ARRAY_SIZE(infra_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); + + mtk_register_reset_controller(node, 2, 0x30); +} +CLK_OF_DECLARE(mtk_infrasys, "mediatek,mt8173-infracfg", mtk_infrasys_init); + +static void __init mtk_pericfg_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + + clk_data = mtk_alloc_clk_data(PERI_NR_CLK); + + mtk_clk_register_gates(node, peri_clks, ARRAY_SIZE(peri_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); + + mtk_register_reset_controller(node, 2, 0); +} +CLK_OF_DECLARE(mtk_pericfg, "mediatek,mt8173-pericfg", mtk_pericfg_init); + +#define MT8173_PLL_FMAX (3000UL * MHZ) + +#define CON0_MT8173_RST_BAR BIT(24) + +#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, _pd_reg, _pd_shift, \ + _tuner_reg, _pcw_reg, _pcw_shift) { \ + .id = _id, \ + .name = _name, \ + .reg = _reg, \ + .pwr_reg = _pwr_reg, \ + .en_mask = _en_mask, \ + .flags = _flags, \ + .rst_bar_mask = CON0_MT8173_RST_BAR, \ + .fmax = MT8173_PLL_FMAX, \ + .pcwbits = _pcwbits, \ + .pd_reg = _pd_reg, \ + .pd_shift = _pd_shift, \ + .tuner_reg = _tuner_reg, \ + .pcw_reg = _pcw_reg, \ + .pcw_shift = _pcw_shift, \ + } + +static const struct mtk_pll_data plls[] = { + PLL(APMIXED_ARMCA15PLL, "armca15pll", 0x200, 0x20c, 0x00000001, 0, 21, 0x204, 24, 0x0, 0x204, 0), + PLL(APMIXED_ARMCA7PLL, "armca7pll", 0x210, 0x21c, 0x00000001, 0, 21, 0x214, 24, 0x0, 0x214, 0), + PLL(APMIXED_MAINPLL, "mainpll", 0x220, 0x22c, 0xf0000101, HAVE_RST_BAR, 21, 0x220, 4, 0x0, 0x224, 0), + PLL(APMIXED_UNIVPLL, "univpll", 0x230, 0x23c, 0xfe000001, HAVE_RST_BAR, 7, 0x230, 4, 0x0, 0x234, 14), + PLL(APMIXED_MMPLL, "mmpll", 0x240, 0x24c, 0x00000001, 0, 21, 0x244, 24, 0x0, 0x244, 0), + PLL(APMIXED_MSDCPLL, "msdcpll", 0x250, 0x25c, 0x00000001, 0, 21, 0x250, 4, 0x0, 0x254, 0), + PLL(APMIXED_VENCPLL, "vencpll", 0x260, 0x26c, 0x00000001, 0, 21, 0x260, 4, 0x0, 0x264, 0), + PLL(APMIXED_TVDPLL, "tvdpll", 0x270, 0x27c, 0x00000001, 0, 21, 0x270, 4, 0x0, 0x274, 0), + PLL(APMIXED_MPLL, "mpll", 0x280, 0x28c, 0x00000001, 0, 21, 0x280, 4, 0x0, 0x284, 0), + PLL(APMIXED_VCODECPLL, "vcodecpll", 0x290, 0x29c, 0x00000001, 0, 21, 0x290, 4, 0x0, 0x294, 0), + PLL(APMIXED_APLL1, "apll1", 0x2a0, 0x2b0, 0x00000001, 0, 31, 0x2a0, 4, 0x2a4, 0x2a4, 0), + PLL(APMIXED_APLL2, "apll2", 0x2b4, 0x2c4, 0x00000001, 0, 31, 0x2b4, 4, 0x2b8, 0x2b8, 0), + PLL(APMIXED_LVDSPLL, "lvdspll", 0x2d0, 0x2dc, 0x00000001, 0, 21, 0x2d0, 4, 0x0, 0x2d4, 0), + PLL(APMIXED_MSDCPLL2, "msdcpll2", 0x2f0, 0x2fc, 0x00000001, 0, 21, 0x2f0, 4, 0x0, 0x2f4, 0), +}; + +static void __init mtk_apmixedsys_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + + clk_data = mtk_alloc_clk_data(ARRAY_SIZE(plls)); + if (!clk_data) + return; + + mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data); +} +CLK_OF_DECLARE(mtk_apmixedsys, "mediatek,mt8173-apmixedsys", + mtk_apmixedsys_init); diff --git a/include/dt-bindings/clock/mt8173-clk.h b/include/dt-bindings/clock/mt8173-clk.h new file mode 100644 index 0000000..e648b28 --- /dev/null +++ b/include/dt-bindings/clock/mt8173-clk.h @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao <jamesjj.liao@mediatek.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. + */ + +#ifndef _DT_BINDINGS_CLK_MT8173_H +#define _DT_BINDINGS_CLK_MT8173_H + +/* TOPCKGEN */ + +#define TOP_CLKPH_MCK_O 1 +#define TOP_DPI_CK 2 +#define TOP_USB_SYSPLL_125M 3 +#define TOP_HDMITX_DIG_CTS 4 +#define TOP_ARMCA7PLL_754M 5 +#define TOP_ARMCA7PLL_502M 6 +#define TOP_MAIN_H546M 7 +#define TOP_MAIN_H364M 8 +#define TOP_MAIN_H218P4M 9 +#define TOP_MAIN_H156M 10 +#define TOP_TVDPLL_445P5M 11 +#define TOP_TVDPLL_594M 12 +#define TOP_UNIV_624M 13 +#define TOP_UNIV_416M 14 +#define TOP_UNIV_249P6M 15 +#define TOP_UNIV_178P3M 16 +#define TOP_UNIV_48M 17 +#define TOP_CLKRTC_EXT 18 +#define TOP_CLKRTC_INT 19 +#define TOP_FPC_CK 20 +#define TOP_HDMITXPLL_D2 21 +#define TOP_HDMITXPLL_D3 22 +#define TOP_ARMCA7PLL_D2 23 +#define TOP_ARMCA7PLL_D3 24 +#define TOP_APLL1_CK 25 +#define TOP_APLL2_CK 26 +#define TOP_DMPLL_CK 27 +#define TOP_DMPLL_D2 28 +#define TOP_DMPLL_D4 29 +#define TOP_DMPLL_D8 30 +#define TOP_DMPLL_D16 31 +#define TOP_LVDSPLL_D2 32 +#define TOP_LVDSPLL_D4 33 +#define TOP_LVDSPLL_D8 34 +#define TOP_MMPLL_CK 35 +#define TOP_MMPLL_D2 36 +#define TOP_MSDCPLL_CK 37 +#define TOP_MSDCPLL_D2 38 +#define TOP_MSDCPLL_D4 39 +#define TOP_MSDCPLL2_CK 40 +#define TOP_MSDCPLL2_D2 41 +#define TOP_MSDCPLL2_D4 42 +#define TOP_SYSPLL_D2 43 +#define TOP_SYSPLL1_D2 44 +#define TOP_SYSPLL1_D4 45 +#define TOP_SYSPLL1_D8 46 +#define TOP_SYSPLL1_D16 47 +#define TOP_SYSPLL_D3 48 +#define TOP_SYSPLL2_D2 49 +#define TOP_SYSPLL2_D4 50 +#define TOP_SYSPLL_D5 51 +#define TOP_SYSPLL3_D2 52 +#define TOP_SYSPLL3_D4 53 +#define TOP_SYSPLL_D7 54 +#define TOP_SYSPLL4_D2 55 +#define TOP_SYSPLL4_D4 56 +#define TOP_TVDPLL_CK 57 +#define TOP_TVDPLL_D2 58 +#define TOP_TVDPLL_D4 59 +#define TOP_TVDPLL_D8 60 +#define TOP_TVDPLL_D16 61 +#define TOP_UNIVPLL_D2 62 +#define TOP_UNIVPLL1_D2 63 +#define TOP_UNIVPLL1_D4 64 +#define TOP_UNIVPLL1_D8 65 +#define TOP_UNIVPLL_D3 66 +#define TOP_UNIVPLL2_D2 67 +#define TOP_UNIVPLL2_D4 68 +#define TOP_UNIVPLL2_D8 69 +#define TOP_UNIVPLL_D5 70 +#define TOP_UNIVPLL3_D2 71 +#define TOP_UNIVPLL3_D4 72 +#define TOP_UNIVPLL3_D8 73 +#define TOP_UNIVPLL_D7 74 +#define TOP_UNIVPLL_D26 75 +#define TOP_UNIVPLL_D52 76 +#define TOP_VCODECPLL_CK 77 +#define TOP_VCODECPLL_370P5 78 +#define TOP_VENCPLL_CK 79 +#define TOP_VENCPLL_D2 80 +#define TOP_VENCPLL_D4 81 +#define TOP_AXI_SEL 82 +#define TOP_MEM_SEL 83 +#define TOP_DDRPHYCFG_SEL 84 +#define TOP_MM_SEL 85 +#define TOP_PWM_SEL 86 +#define TOP_VDEC_SEL 87 +#define TOP_VENC_SEL 88 +#define TOP_MFG_SEL 89 +#define TOP_CAMTG_SEL 90 +#define TOP_UART_SEL 91 +#define TOP_SPI_SEL 92 +#define TOP_USB20_SEL 93 +#define TOP_USB30_SEL 94 +#define TOP_MSDC50_0_H_SEL 95 +#define TOP_MSDC50_0_SEL 96 +#define TOP_MSDC30_1_SEL 97 +#define TOP_MSDC30_2_SEL 98 +#define TOP_MSDC30_3_SEL 99 +#define TOP_AUDIO_SEL 100 +#define TOP_AUD_INTBUS_SEL 101 +#define TOP_PMICSPI_SEL 102 +#define TOP_SCP_SEL 103 +#define TOP_ATB_SEL 104 +#define TOP_VENC_LT_SEL 105 +#define TOP_DPI0_SEL 106 +#define TOP_IRDA_SEL 107 +#define TOP_CCI400_SEL 108 +#define TOP_AUD_1_SEL 109 +#define TOP_AUD_2_SEL 110 +#define TOP_MEM_MFG_IN_SEL 111 +#define TOP_AXI_MFG_IN_SEL 112 +#define TOP_SCAM_SEL 113 +#define TOP_SPINFI_IFR_SEL 114 +#define TOP_HDMI_SEL 115 +#define TOP_DPILVDS_SEL 116 +#define TOP_MSDC50_2_H_SEL 117 +#define TOP_HDCP_SEL 118 +#define TOP_HDCP_24M_SEL 119 +#define TOP_RTC_SEL 120 +#define TOP_APLL1_DIV0 121 +#define TOP_APLL1_DIV1 122 +#define TOP_APLL1_DIV2 123 +#define TOP_APLL1_DIV3 124 +#define TOP_APLL1_DIV4 125 +#define TOP_APLL1_DIV5 126 +#define TOP_APLL2_DIV0 127 +#define TOP_APLL2_DIV1 128 +#define TOP_APLL2_DIV2 129 +#define TOP_APLL2_DIV3 130 +#define TOP_APLL2_DIV4 131 +#define TOP_APLL2_DIV5 132 +#define TOP_I2S0_M_CK_SEL 133 +#define TOP_I2S1_M_CK_SEL 134 +#define TOP_I2S2_M_CK_SEL 135 +#define TOP_I2S3_M_CK_SEL 136 +#define TOP_I2S3_B_CK_SEL 137 +#define TOP_NR_CLK 138 + +/* APMIXED_SYS */ + +#define APMIXED_ARMCA15PLL 1 +#define APMIXED_ARMCA7PLL 2 +#define APMIXED_MAINPLL 3 +#define APMIXED_UNIVPLL 4 +#define APMIXED_MMPLL 5 +#define APMIXED_MSDCPLL 6 +#define APMIXED_VENCPLL 7 +#define APMIXED_TVDPLL 8 +#define APMIXED_MPLL 9 +#define APMIXED_VCODECPLL 10 +#define APMIXED_APLL1 11 +#define APMIXED_APLL2 12 +#define APMIXED_LVDSPLL 13 +#define APMIXED_MSDCPLL2 14 +#define APMIXED_NR_CLK 15 + +/* INFRA_SYS */ + +#define INFRA_DBGCLK 1 +#define INFRA_SMI 2 +#define INFRA_AUDIO 3 +#define INFRA_GCE 4 +#define INFRA_L2C_SRAM 5 +#define INFRA_M4U 6 +#define INFRA_CPUM 7 +#define INFRA_KP 8 +#define INFRA_CEC 9 +#define INFRA_PMICSPI 10 +#define INFRA_PMICWRAP 11 +#define INFRA_NR_CLK 12 + +/* PERI_SYS */ + +#define PERI_NFI 1 +#define PERI_THERM 2 +#define PERI_PWM1 3 +#define PERI_PWM2 4 +#define PERI_PWM3 5 +#define PERI_PWM4 6 +#define PERI_PWM5 7 +#define PERI_PWM6 8 +#define PERI_PWM7 9 +#define PERI_PWM 10 +#define PERI_USB0 11 +#define PERI_USB1 12 +#define PERI_AP_DMA 13 +#define PERI_MSDC30_0 14 +#define PERI_MSDC30_1 15 +#define PERI_MSDC30_2 16 +#define PERI_MSDC30_3 17 +#define PERI_NLI_ARB 18 +#define PERI_IRDA 19 +#define PERI_UART0 20 +#define PERI_UART1 21 +#define PERI_UART2 22 +#define PERI_UART3 23 +#define PERI_I2C0 24 +#define PERI_I2C1 25 +#define PERI_I2C2 26 +#define PERI_I2C3 27 +#define PERI_I2C4 28 +#define PERI_AUXADC 29 +#define PERI_SPI0 30 +#define PERI_I2C5 31 +#define PERI_NFIECC 32 +#define PERI_SPI 33 +#define PERI_IRRX 34 +#define PERI_I2C6 35 +#define PERI_NR_CLK 36 + +#endif /* _DT_BINDINGS_CLK_MT8173_H */ diff --git a/include/dt-bindings/reset-controller/mt8173-resets.h b/include/dt-bindings/reset-controller/mt8173-resets.h new file mode 100644 index 0000000..9464b37 --- /dev/null +++ b/include/dt-bindings/reset-controller/mt8173-resets.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Flora Fu, MediaTek + * + * 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. + */ + +#ifndef _DT_BINDINGS_RESET_CONTROLLER_MT8173 +#define _DT_BINDINGS_RESET_CONTROLLER_MT8173 + +/* INFRACFG resets */ +#define MT8173_INFRA_EMI_REG_RST 0 +#define MT8173_INFRA_DRAMC0_A0_RST 1 +#define MT8173_INFRA_APCIRQ_EINT_RST 3 +#define MT8173_INFRA_APXGPT_RST 4 +#define MT8173_INFRA_SCPSYS_RST 5 +#define MT8173_INFRA_KP_RST 6 +#define MT8173_INFRA_PMIC_WRAP_RST 7 +#define MT8173_INFRA_MPIP_RST 8 +#define MT8173_INFRA_CEC_RST 9 +#define MT8173_INFRA_EMI_RST 32 +#define MT8173_INFRA_DRAMC0_RST 34 +#define MT8173_INFRA_APMIXEDSYS_RST 35 +#define MT8173_INFRA_MIPI_DSI_RST 36 +#define MT8173_INFRA_TRNG_RST 37 +#define MT8173_INFRA_SYSIRQ_RST 38 +#define MT8173_INFRA_MIPI_CSI_RST 39 +#define MT8173_INFRA_GCE_FAXI_RST 40 +#define MT8173_INFRA_MMIOMMURST 47 + + +/* PERICFG resets */ +#define MT8173_PERI_UART0_SW_RST 0 +#define MT8173_PERI_UART1_SW_RST 1 +#define MT8173_PERI_UART2_SW_RST 2 +#define MT8173_PERI_UART3_SW_RST 3 +#define MT8173_PERI_IRRX_SW_RST 4 +#define MT8173_PERI_PWM_SW_RST 8 +#define MT8173_PERI_AUXADC_SW_RST 10 +#define MT8173_PERI_DMA_SW_RST 11 +#define MT8173_PERI_I2C6_SW_RST 13 +#define MT8173_PERI_NFI_SW_RST 14 +#define MT8173_PERI_THERM_SW_RST 16 +#define MT8173_PERI_MSDC2_SW_RST 17 +#define MT8173_PERI_MSDC3_SW_RST 18 +#define MT8173_PERI_MSDC0_SW_RST 19 +#define MT8173_PERI_MSDC1_SW_RST 20 +#define MT8173_PERI_I2C0_SW_RST 22 +#define MT8173_PERI_I2C1_SW_RST 23 +#define MT8173_PERI_I2C2_SW_RST 24 +#define MT8173_PERI_I2C3_SW_RST 25 +#define MT8173_PERI_I2C4_SW_RST 26 +#define MT8173_PERI_HDMI_SW_RST 29 +#define MT8173_PERI_SPI0_SW_RST 33 + +#endif /* _DT_BINDINGS_RESET_CONTROLLER_MT8173 */ -- 2.1.4 ^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 6/6] dt-bindings: ARM: Mediatek: Document devicetree bindings for clock/reset controllers 2015-03-19 8:42 [PATCH v8]: clk: Add common clock support for Mediatek MT8135 and MT8173 Sascha Hauer ` (4 preceding siblings ...) 2015-03-19 8:42 ` [PATCH 5/6] clk: mediatek: Add basic clocks for Mediatek MT8173 Sascha Hauer @ 2015-03-19 8:42 ` Sascha Hauer 2015-03-27 13:32 ` Matthias Brugger 2015-03-27 6:05 ` [PATCH v8]: clk: Add common clock support for Mediatek MT8135 and MT8173 Sascha Hauer 6 siblings, 1 reply; 22+ messages in thread From: Sascha Hauer @ 2015-03-19 8:42 UTC (permalink / raw) To: linux-arm-kernel Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- .../bindings/arm/mediatek/mediatek,apmixedsys.txt | 23 +++++++++++++++++ .../bindings/arm/mediatek/mediatek,infracfg.txt | 30 ++++++++++++++++++++++ .../bindings/arm/mediatek/mediatek,pericfg.txt | 30 ++++++++++++++++++++++ .../bindings/arm/mediatek/mediatek,topckgen.txt | 23 +++++++++++++++++ 4 files changed, 106 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt new file mode 100644 index 0000000..5af6d73 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt @@ -0,0 +1,23 @@ +Mediatek apmixedsys controller +============================== + +The Mediatek apmixedsys controller provides the PLLs to the system. + +Required Properties: + +- compatible: Should be: + - "mediatek,mt8135-apmixedsys" + - "mediatek,mt8173-apmixedsys" +- #clock-cells: Must be 1 + +The apmixedsys controller uses the common clk binding from +Documentation/devicetree/bindings/clock/clock-bindings.txt +The available clocks are defined in dt-bindings/clock/mt*-clk.h. + +Example: + +apmixedsys: apmixedsys at 10209000 { + compatible = "mediatek,mt8173-apmixedsys"; + reg = <0 0x10209000 0 0x1000>; + #clock-cells = <1>; +}; diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt new file mode 100644 index 0000000..684da473 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt @@ -0,0 +1,30 @@ +Mediatek infracfg controller +============================ + +The Mediatek infracfg controller provides various clocks and reset +outputs to the system. + +Required Properties: + +- compatible: Should be: + - "mediatek,mt8135-infracfg", "syscon" + - "mediatek,mt8173-infracfg", "syscon" +- #clock-cells: Must be 1 +- #reset-cells: Must be 1 + +The infracfg controller uses the common clk binding from +Documentation/devicetree/bindings/clock/clock-bindings.txt +The available clocks are defined in dt-bindings/clock/mt*-clk.h. +Also it uses the common reset controller binding from +Documentation/devicetree/bindings/reset/reset.txt. +The available reset outputs are defined in +dt-bindings/reset-controller/mt*-resets.h + +Example: + +infracfg: infracfg at 10001000 { + compatible = "mediatek,mt8173-infracfg", "syscon"; + reg = <0 0x10001000 0 0x1000>; + #clock-cells = <1>; + #reset-cells = <1>; +}; diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt new file mode 100644 index 0000000..fdb45c6 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt @@ -0,0 +1,30 @@ +Mediatek pericfg controller +=========================== + +The Mediatek pericfg controller provides various clocks and reset +outputs to the system. + +Required Properties: + +- compatible: Should be: + - "mediatek,mt8135-pericfg", "syscon" + - "mediatek,mt8173-pericfg", "syscon" +- #clock-cells: Must be 1 +- #reset-cells: Must be 1 + +The pericfg controller uses the common clk binding from +Documentation/devicetree/bindings/clock/clock-bindings.txt +The available clocks are defined in dt-bindings/clock/mt*-clk.h. +Also it uses the common reset controller binding from +Documentation/devicetree/bindings/reset/reset.txt. +The available reset outputs are defined in +dt-bindings/reset-controller/mt*-resets.h + +Example: + +pericfg: pericfg at 10003000 { + compatible = "mediatek,mt8173-pericfg", "syscon"; + reg = <0 0x10003000 0 0x1000>; + #clock-cells = <1>; + #reset-cells = <1>; +}; diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt new file mode 100644 index 0000000..a425248 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt @@ -0,0 +1,23 @@ +Mediatek topckgen controller +============================ + +The Mediatek topckgen controller provides various clocks to the system. + +Required Properties: + +- compatible: Should be: + - "mediatek,mt8135-topckgen" + - "mediatek,mt8173-topckgen" +- #clock-cells: Must be 1 + +The topckgen controller uses the common clk binding from +Documentation/devicetree/bindings/clock/clock-bindings.txt +The available clocks are defined in dt-bindings/clock/mt*-clk.h. + +Example: + +topckgen: topckgen at 10000000 { + compatible = "mediatek,mt8173-topckgen"; + reg = <0 0x10000000 0 0x1000>; + #clock-cells = <1>; +}; -- 2.1.4 ^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 6/6] dt-bindings: ARM: Mediatek: Document devicetree bindings for clock/reset controllers 2015-03-19 8:42 ` [PATCH 6/6] dt-bindings: ARM: Mediatek: Document devicetree bindings for clock/reset controllers Sascha Hauer @ 2015-03-27 13:32 ` Matthias Brugger 0 siblings, 0 replies; 22+ messages in thread From: Matthias Brugger @ 2015-03-27 13:32 UTC (permalink / raw) To: linux-arm-kernel On 19/03/15 09:42, Sascha Hauer wrote: > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> The patch should have a text as description. Thanks, Matthias > --- > .../bindings/arm/mediatek/mediatek,apmixedsys.txt | 23 +++++++++++++++++ > .../bindings/arm/mediatek/mediatek,infracfg.txt | 30 ++++++++++++++++++++++ > .../bindings/arm/mediatek/mediatek,pericfg.txt | 30 ++++++++++++++++++++++ > .../bindings/arm/mediatek/mediatek,topckgen.txt | 23 +++++++++++++++++ > 4 files changed, 106 insertions(+) > create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt > create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt > create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt > create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt > > diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt > new file mode 100644 > index 0000000..5af6d73 > --- /dev/null > +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt > @@ -0,0 +1,23 @@ > +Mediatek apmixedsys controller > +============================== > + > +The Mediatek apmixedsys controller provides the PLLs to the system. > + > +Required Properties: > + > +- compatible: Should be: > + - "mediatek,mt8135-apmixedsys" > + - "mediatek,mt8173-apmixedsys" > +- #clock-cells: Must be 1 > + > +The apmixedsys controller uses the common clk binding from > +Documentation/devicetree/bindings/clock/clock-bindings.txt > +The available clocks are defined in dt-bindings/clock/mt*-clk.h. > + > +Example: > + > +apmixedsys: apmixedsys at 10209000 { > + compatible = "mediatek,mt8173-apmixedsys"; > + reg = <0 0x10209000 0 0x1000>; > + #clock-cells = <1>; > +}; > diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt > new file mode 100644 > index 0000000..684da473 > --- /dev/null > +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt > @@ -0,0 +1,30 @@ > +Mediatek infracfg controller > +============================ > + > +The Mediatek infracfg controller provides various clocks and reset > +outputs to the system. > + > +Required Properties: > + > +- compatible: Should be: > + - "mediatek,mt8135-infracfg", "syscon" > + - "mediatek,mt8173-infracfg", "syscon" > +- #clock-cells: Must be 1 > +- #reset-cells: Must be 1 > + > +The infracfg controller uses the common clk binding from > +Documentation/devicetree/bindings/clock/clock-bindings.txt > +The available clocks are defined in dt-bindings/clock/mt*-clk.h. > +Also it uses the common reset controller binding from > +Documentation/devicetree/bindings/reset/reset.txt. > +The available reset outputs are defined in > +dt-bindings/reset-controller/mt*-resets.h > + > +Example: > + > +infracfg: infracfg at 10001000 { > + compatible = "mediatek,mt8173-infracfg", "syscon"; > + reg = <0 0x10001000 0 0x1000>; > + #clock-cells = <1>; > + #reset-cells = <1>; > +}; > diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt > new file mode 100644 > index 0000000..fdb45c6 > --- /dev/null > +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt > @@ -0,0 +1,30 @@ > +Mediatek pericfg controller > +=========================== > + > +The Mediatek pericfg controller provides various clocks and reset > +outputs to the system. > + > +Required Properties: > + > +- compatible: Should be: > + - "mediatek,mt8135-pericfg", "syscon" > + - "mediatek,mt8173-pericfg", "syscon" > +- #clock-cells: Must be 1 > +- #reset-cells: Must be 1 > + > +The pericfg controller uses the common clk binding from > +Documentation/devicetree/bindings/clock/clock-bindings.txt > +The available clocks are defined in dt-bindings/clock/mt*-clk.h. > +Also it uses the common reset controller binding from > +Documentation/devicetree/bindings/reset/reset.txt. > +The available reset outputs are defined in > +dt-bindings/reset-controller/mt*-resets.h > + > +Example: > + > +pericfg: pericfg at 10003000 { > + compatible = "mediatek,mt8173-pericfg", "syscon"; > + reg = <0 0x10003000 0 0x1000>; > + #clock-cells = <1>; > + #reset-cells = <1>; > +}; > diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt > new file mode 100644 > index 0000000..a425248 > --- /dev/null > +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt > @@ -0,0 +1,23 @@ > +Mediatek topckgen controller > +============================ > + > +The Mediatek topckgen controller provides various clocks to the system. > + > +Required Properties: > + > +- compatible: Should be: > + - "mediatek,mt8135-topckgen" > + - "mediatek,mt8173-topckgen" > +- #clock-cells: Must be 1 > + > +The topckgen controller uses the common clk binding from > +Documentation/devicetree/bindings/clock/clock-bindings.txt > +The available clocks are defined in dt-bindings/clock/mt*-clk.h. > + > +Example: > + > +topckgen: topckgen at 10000000 { > + compatible = "mediatek,mt8173-topckgen"; > + reg = <0 0x10000000 0 0x1000>; > + #clock-cells = <1>; > +}; > ^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v8]: clk: Add common clock support for Mediatek MT8135 and MT8173 2015-03-19 8:42 [PATCH v8]: clk: Add common clock support for Mediatek MT8135 and MT8173 Sascha Hauer ` (5 preceding siblings ...) 2015-03-19 8:42 ` [PATCH 6/6] dt-bindings: ARM: Mediatek: Document devicetree bindings for clock/reset controllers Sascha Hauer @ 2015-03-27 6:05 ` Sascha Hauer 2015-03-27 8:02 ` Stephen Boyd 6 siblings, 1 reply; 22+ messages in thread From: Sascha Hauer @ 2015-03-27 6:05 UTC (permalink / raw) To: linux-arm-kernel Mike, Stephen, Any chance to get this forward please? Sascha On Thu, Mar 19, 2015 at 09:42:04AM +0100, Sascha Hauer wrote: > > The following changes since commit 9eccca0843205f87c00404b663188b88eb248051: > > Linux 4.0-rc3 (2015-03-08 16:09:09 -0700) > > are available in the git repository at: > > git://git.pengutronix.de/git/imx/linux-2.6.git tags/v4.0-clk-mediatek-v8 > > for you to fetch changes up to 54449c74078c9d93619757c7a91b820eaa28c0f4: > > dt-bindings: ARM: Mediatek: Document devicetree bindings for clock/reset controllers (2015-03-19 09:15:33 +0100) > > ---------------------------------------------------------------- > This patchset contains the initial common clock support for Mediatek SoCs. > Mediatek SoC's clock architecture comprises of various PLLs, dividers, muxes > and clock gates. > > Changes in v8: > - add patch to allow to put parent_name arrays in __initconst > - put parent_name arrays into __initconst > > Changes in v7: > - fix duplicate definition/declaration of mtk_register_reset_controller > - fix pd_reg offset of tvdpll > - make clk initialization arrays const > > Changes in v6: > - rework PLL support, only a fraction of original size now > - Move binding docs to Documentation/devicetree/bindings/arm/mediatek since > the units are not really clock specific (they contain reset controllers) > > Changes in v5: > - Add reset controller support for pericfg/infracfg > - Use regmap for the gates > - remove now unnecessary spinlock for the gates > - Add PMIC wrapper support as of v3 > > Changes in v4: > - Support MT8173 platform. > - Re-ordered patchset. driver/clk/Makefile in 2nd patch. > - Extract the common part definition(mtk_gate/mtk_pll/mtk_mux) from > clk-mt8135.c/clk-mt8173.c to clk-mtk.c. > - Refine code. Rmove unnessacary debug information and unsed defines, > add prefix "mtk_" for static functions. > - Remove flag CLK_IGNORE_UNUSED and set flag CLK_SET_RATE_PARENT on > gate/mux/fixed-factor. > - Use spin_lock_irqsave(&clk_ops_lock, flags) instead of mtk_clk_lock. > - Example above include a node for the clock controller itself, followed > by the i2c controller example above. > > Changes in v3: > - Rebase to 3.19-rc1. > - Refine code. Remove unneed functions, debug logs and comments, and fine tune > error logs. > > Changes in v2: > - Re-ordered patchset. Fold include/dt-bindings and DT document in 1st patch. > > ---------------------------------------------------------------- > James Liao (3): > clk: mediatek: Add initial common clock support for Mediatek SoCs. > clk: mediatek: Add basic clocks for Mediatek MT8135. > clk: mediatek: Add basic clocks for Mediatek MT8173. > > Sascha Hauer (3): > clk: make strings in parent name arrays const > clk: mediatek: Add reset controller support > dt-bindings: ARM: Mediatek: Document devicetree bindings for clock/reset controllers > > .../bindings/arm/mediatek/mediatek,apmixedsys.txt | 23 + > .../bindings/arm/mediatek/mediatek,infracfg.txt | 30 + > .../bindings/arm/mediatek/mediatek,pericfg.txt | 30 + > .../bindings/arm/mediatek/mediatek,topckgen.txt | 23 + > drivers/clk/Makefile | 1 + > drivers/clk/clk-composite.c | 2 +- > drivers/clk/clk-mux.c | 4 +- > drivers/clk/mediatek/Makefile | 4 + > drivers/clk/mediatek/clk-gate.c | 137 ++++ > drivers/clk/mediatek/clk-gate.h | 49 ++ > drivers/clk/mediatek/clk-mt8135.c | 640 ++++++++++++++++ > drivers/clk/mediatek/clk-mt8173.c | 826 +++++++++++++++++++++ > drivers/clk/mediatek/clk-mtk.c | 197 +++++ > drivers/clk/mediatek/clk-mtk.h | 165 ++++ > drivers/clk/mediatek/clk-pll.c | 325 ++++++++ > drivers/clk/mediatek/reset.c | 99 +++ > include/dt-bindings/clock/mt8135-clk.h | 190 +++++ > include/dt-bindings/clock/mt8173-clk.h | 231 ++++++ > .../dt-bindings/reset-controller/mt8135-resets.h | 64 ++ > .../dt-bindings/reset-controller/mt8173-resets.h | 63 ++ > include/linux/clk-provider.h | 8 +- > 21 files changed, 3104 insertions(+), 7 deletions(-) > create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt > create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt > create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt > create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt > create mode 100644 drivers/clk/mediatek/Makefile > create mode 100644 drivers/clk/mediatek/clk-gate.c > create mode 100644 drivers/clk/mediatek/clk-gate.h > create mode 100644 drivers/clk/mediatek/clk-mt8135.c > create mode 100644 drivers/clk/mediatek/clk-mt8173.c > create mode 100644 drivers/clk/mediatek/clk-mtk.c > create mode 100644 drivers/clk/mediatek/clk-mtk.h > create mode 100644 drivers/clk/mediatek/clk-pll.c > create mode 100644 drivers/clk/mediatek/reset.c > create mode 100644 include/dt-bindings/clock/mt8135-clk.h > create mode 100644 include/dt-bindings/clock/mt8173-clk.h > create mode 100644 include/dt-bindings/reset-controller/mt8135-resets.h > create mode 100644 include/dt-bindings/reset-controller/mt8173-resets.h > > -- Pengutronix e.K. | | Industrial Linux Solutions | http://www.pengutronix.de/ | Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 | ^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v8]: clk: Add common clock support for Mediatek MT8135 and MT8173 2015-03-27 6:05 ` [PATCH v8]: clk: Add common clock support for Mediatek MT8135 and MT8173 Sascha Hauer @ 2015-03-27 8:02 ` Stephen Boyd 0 siblings, 0 replies; 22+ messages in thread From: Stephen Boyd @ 2015-03-27 8:02 UTC (permalink / raw) To: linux-arm-kernel On 03/27, Sascha Hauer wrote: > Mike, Stephen, > > Any chance to get this forward please? > Looks ok to me except for the minor nitpick on lock names. -- Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project ^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v9]: clk: Add common clock support for Mediatek MT8135 and MT8173 @ 2015-03-27 9:18 Sascha Hauer 2015-03-27 9:18 ` [PATCH 2/6] clk: mediatek: Add initial common clock support for Mediatek SoCs Sascha Hauer 0 siblings, 1 reply; 22+ messages in thread From: Sascha Hauer @ 2015-03-27 9:18 UTC (permalink / raw) To: linux-arm-kernel This patchset contains the initial common clock support for Mediatek SoCs. Mediatek SoC's clock architecture comprises of various PLLs, dividers, muxes and clock gates. Changes in v9: - rename 'lock' to 'mt81xx_clk_lock' to get better lockdep output Changes in v8: - add patch to allow to put parent_name arrays in __initconst - put parent_name arrays into __initconst Changes in v7: - fix duplicate definition/declaration of mtk_register_reset_controller - fix pd_reg offset of tvdpll - make clk initialization arrays const Changes in v6: - rework PLL support, only a fraction of original size now - Move binding docs to Documentation/devicetree/bindings/arm/mediatek since the units are not really clock specific (they contain reset controllers) Changes in v5: - Add reset controller support for pericfg/infracfg - Use regmap for the gates - remove now unnecessary spinlock for the gates - Add PMIC wrapper support as of v3 Changes in v4: - Support MT8173 platform. - Re-ordered patchset. driver/clk/Makefile in 2nd patch. - Extract the common part definition(mtk_gate/mtk_pll/mtk_mux) from clk-mt8135.c/clk-mt8173.c to clk-mtk.c. - Refine code. Rmove unnessacary debug information and unsed defines, add prefix "mtk_" for static functions. - Remove flag CLK_IGNORE_UNUSED and set flag CLK_SET_RATE_PARENT on gate/mux/fixed-factor. - Use spin_lock_irqsave(&clk_ops_lock, flags) instead of mtk_clk_lock. - Example above include a node for the clock controller itself, followed by the i2c controller example above. Changes in v3: - Rebase to 3.19-rc1. - Refine code. Remove unneed functions, debug logs and comments, and fine tune error logs. Changes in v2: - Re-ordered patchset. Fold include/dt-bindings and DT document in 1st patch. Sascha ^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 2/6] clk: mediatek: Add initial common clock support for Mediatek SoCs. 2015-03-27 9:18 [PATCH v9]: " Sascha Hauer @ 2015-03-27 9:18 ` Sascha Hauer 0 siblings, 0 replies; 22+ messages in thread From: Sascha Hauer @ 2015-03-27 9:18 UTC (permalink / raw) To: linux-arm-kernel From: James Liao <jamesjj.liao@mediatek.com> This patch adds common clock support for Mediatek SoCs, including plls, muxes and clock gates. Signed-off-by: James Liao <jamesjj.liao@mediatek.com> Signed-off-by: Henry Chen <henryc.chen@mediatek.com> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- drivers/clk/Makefile | 1 + drivers/clk/mediatek/Makefile | 1 + drivers/clk/mediatek/clk-gate.c | 137 +++++++++++++++++ drivers/clk/mediatek/clk-gate.h | 49 ++++++ drivers/clk/mediatek/clk-mtk.c | 197 ++++++++++++++++++++++++ drivers/clk/mediatek/clk-mtk.h | 155 +++++++++++++++++++ drivers/clk/mediatek/clk-pll.c | 325 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 865 insertions(+) create mode 100644 drivers/clk/mediatek/Makefile create mode 100644 drivers/clk/mediatek/clk-gate.c create mode 100644 drivers/clk/mediatek/clk-gate.h create mode 100644 drivers/clk/mediatek/clk-mtk.c create mode 100644 drivers/clk/mediatek/clk-mtk.h create mode 100644 drivers/clk/mediatek/clk-pll.c diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index d478ceb..6d97203 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -49,6 +49,7 @@ obj-$(CONFIG_ARCH_HI3xxx) += hisilicon/ obj-$(CONFIG_ARCH_HIP04) += hisilicon/ obj-$(CONFIG_ARCH_HIX5HD2) += hisilicon/ obj-$(CONFIG_COMMON_CLK_KEYSTONE) += keystone/ +obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/ ifeq ($(CONFIG_COMMON_CLK), y) obj-$(CONFIG_ARCH_MMP) += mmp/ endif diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile new file mode 100644 index 0000000..c384e97 --- /dev/null +++ b/drivers/clk/mediatek/Makefile @@ -0,0 +1 @@ +obj-y += clk-mtk.o clk-pll.o clk-gate.o diff --git a/drivers/clk/mediatek/clk-gate.c b/drivers/clk/mediatek/clk-gate.c new file mode 100644 index 0000000..9d77ee3 --- /dev/null +++ b/drivers/clk/mediatek/clk-gate.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao <jamesjj.liao@mediatek.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. + */ + +#include <linux/of.h> +#include <linux/of_address.h> + +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/clkdev.h> + +#include "clk-mtk.h" +#include "clk-gate.h" + +static int mtk_cg_bit_is_cleared(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_clk_gate(hw); + u32 val; + + regmap_read(cg->regmap, cg->sta_ofs, &val); + + val &= BIT(cg->bit); + + return val == 0; +} + +static int mtk_cg_bit_is_set(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_clk_gate(hw); + u32 val; + + regmap_read(cg->regmap, cg->sta_ofs, &val); + + val &= BIT(cg->bit); + + return val != 0; +} + +static void mtk_cg_set_bit(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_clk_gate(hw); + + regmap_write(cg->regmap, cg->set_ofs, BIT(cg->bit)); +} + +static void mtk_cg_clr_bit(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_clk_gate(hw); + + regmap_write(cg->regmap, cg->clr_ofs, BIT(cg->bit)); +} + +static int mtk_cg_enable(struct clk_hw *hw) +{ + mtk_cg_clr_bit(hw); + + return 0; +} + +static void mtk_cg_disable(struct clk_hw *hw) +{ + mtk_cg_set_bit(hw); +} + +static int mtk_cg_enable_inv(struct clk_hw *hw) +{ + mtk_cg_set_bit(hw); + + return 0; +} + +static void mtk_cg_disable_inv(struct clk_hw *hw) +{ + mtk_cg_clr_bit(hw); +} + +const struct clk_ops mtk_clk_gate_ops_setclr = { + .is_enabled = mtk_cg_bit_is_cleared, + .enable = mtk_cg_enable, + .disable = mtk_cg_disable, +}; + +const struct clk_ops mtk_clk_gate_ops_setclr_inv = { + .is_enabled = mtk_cg_bit_is_set, + .enable = mtk_cg_enable_inv, + .disable = mtk_cg_disable_inv, +}; + +struct clk *mtk_clk_register_gate( + const char *name, + const char *parent_name, + struct regmap *regmap, + int set_ofs, + int clr_ofs, + int sta_ofs, + u8 bit, + const struct clk_ops *ops) +{ + struct mtk_clk_gate *cg; + struct clk *clk; + struct clk_init_data init; + + cg = kzalloc(sizeof(*cg), GFP_KERNEL); + if (!cg) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + init.ops = ops; + + cg->regmap = regmap; + cg->set_ofs = set_ofs; + cg->clr_ofs = clr_ofs; + cg->sta_ofs = sta_ofs; + cg->bit = bit; + + cg->hw.init = &init; + + clk = clk_register(NULL, &cg->hw); + if (IS_ERR(clk)) + kfree(cg); + + return clk; +} diff --git a/drivers/clk/mediatek/clk-gate.h b/drivers/clk/mediatek/clk-gate.h new file mode 100644 index 0000000..6b6780b --- /dev/null +++ b/drivers/clk/mediatek/clk-gate.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao <jamesjj.liao@mediatek.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. + */ + +#ifndef __DRV_CLK_GATE_H +#define __DRV_CLK_GATE_H + +#include <linux/regmap.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> + +struct mtk_clk_gate { + struct clk_hw hw; + struct regmap *regmap; + int set_ofs; + int clr_ofs; + int sta_ofs; + u8 bit; +}; + +static inline struct mtk_clk_gate *to_clk_gate(struct clk_hw *hw) +{ + return container_of(hw, struct mtk_clk_gate, hw); +} + +extern const struct clk_ops mtk_clk_gate_ops_setclr; +extern const struct clk_ops mtk_clk_gate_ops_setclr_inv; + +struct clk *mtk_clk_register_gate( + const char *name, + const char *parent_name, + struct regmap *regmap, + int set_ofs, + int clr_ofs, + int sta_ofs, + u8 bit, + const struct clk_ops *ops); + +#endif /* __DRV_CLK_GATE_H */ diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c new file mode 100644 index 0000000..a0c9f43 --- /dev/null +++ b/drivers/clk/mediatek/clk-mtk.c @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao <jamesjj.liao@mediatek.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. + */ + +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/clkdev.h> +#include <linux/mfd/syscon.h> + +#include "clk-mtk.h" +#include "clk-gate.h" + +struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num) +{ + int i; + struct clk_onecell_data *clk_data; + + clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); + if (!clk_data) + return NULL; + + clk_data->clks = kcalloc(clk_num, sizeof(*clk_data->clks), GFP_KERNEL); + if (!clk_data->clks) + goto err_out; + + clk_data->clk_num = clk_num; + + for (i = 0; i < clk_num; ++i) + clk_data->clks[i] = ERR_PTR(-ENOENT); + + return clk_data; +err_out: + kfree(clk_data); + + return NULL; +} + +void mtk_clk_register_factors(const struct mtk_fixed_factor *clks, int num, + struct clk_onecell_data *clk_data) +{ + int i; + struct clk *clk; + + for (i = 0; i < num; i++) { + const struct mtk_fixed_factor *ff = &clks[i]; + + clk = clk_register_fixed_factor(NULL, ff->name, ff->parent_name, + CLK_SET_RATE_PARENT, ff->mult, ff->div); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + ff->name, PTR_ERR(clk)); + continue; + } + + if (clk_data) + clk_data->clks[ff->id] = clk; + } +} + +int mtk_clk_register_gates(struct device_node *node, const struct mtk_gate *clks, + int num, struct clk_onecell_data *clk_data) +{ + int i; + struct clk *clk; + struct regmap *regmap; + + if (!clk_data) + return -ENOMEM; + + regmap = syscon_node_to_regmap(node); + if (IS_ERR(regmap)) { + pr_err("Cannot find regmap for %s: %ld\n", node->full_name, + PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + for (i = 0; i < num; i++) { + const struct mtk_gate *gate = &clks[i]; + + clk = mtk_clk_register_gate(gate->name, gate->parent_name, + regmap, + gate->regs->set_ofs, + gate->regs->clr_ofs, + gate->regs->sta_ofs, + gate->shift, gate->ops); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + gate->name, PTR_ERR(clk)); + continue; + } + + clk_data->clks[gate->id] = clk; + } + + return 0; +} + +struct clk *mtk_clk_register_composite(const struct mtk_composite *mc, + void __iomem *base, spinlock_t *lock) +{ + struct clk *clk; + struct clk_mux *mux = NULL; + struct clk_gate *gate = NULL; + struct clk_divider *div = NULL; + struct clk_hw *mux_hw = NULL, *gate_hw = NULL, *div_hw = NULL; + const struct clk_ops *mux_ops = NULL, *gate_ops = NULL, *div_ops = NULL; + const char * const *parent_names; + const char *parent; + int num_parents; + int ret; + + if (mc->mux_shift >= 0) { + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) + return ERR_PTR(-ENOMEM); + + mux->reg = base + mc->mux_reg; + mux->mask = BIT(mc->mux_width) - 1; + mux->shift = mc->mux_shift; + mux->lock = lock; + + mux_hw = &mux->hw; + mux_ops = &clk_mux_ops; + + parent_names = mc->parent_names; + num_parents = mc->num_parents; + } else { + parent = mc->parent; + parent_names = &parent; + num_parents = 1; + } + + if (mc->gate_shift >= 0) { + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) { + ret = -ENOMEM; + goto err_out; + } + + gate->reg = base + mc->gate_reg; + gate->bit_idx = mc->gate_shift; + gate->flags = CLK_GATE_SET_TO_DISABLE; + gate->lock = lock; + + gate_hw = &gate->hw; + gate_ops = &clk_gate_ops; + } + + if (mc->divider_shift >= 0) { + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) { + ret = -ENOMEM; + goto err_out; + } + + div->reg = base + mc->divider_reg; + div->shift = mc->divider_shift; + div->width = mc->divider_width; + div->lock = lock; + + div_hw = &div->hw; + div_ops = &clk_divider_ops; + } + + clk = clk_register_composite(NULL, mc->name, parent_names, num_parents, + mux_hw, mux_ops, + div_hw, div_ops, + gate_hw, gate_ops, + mc->flags); + + if (IS_ERR(clk)) { + kfree(gate); + kfree(mux); + } + + return clk; +err_out: + kfree(mux); + + return ERR_PTR(ret); +} diff --git a/drivers/clk/mediatek/clk-mtk.h b/drivers/clk/mediatek/clk-mtk.h new file mode 100644 index 0000000..5aaba81 --- /dev/null +++ b/drivers/clk/mediatek/clk-mtk.h @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao <jamesjj.liao@mediatek.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. + */ + +#ifndef __DRV_CLK_MTK_H +#define __DRV_CLK_MTK_H + +#include <linux/regmap.h> +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> + +#define MAX_MUX_GATE_BIT 31 +#define INVALID_MUX_GATE_BIT (MAX_MUX_GATE_BIT + 1) + +#define MHZ (1000 * 1000) + +struct mtk_fixed_factor { + int id; + const char *name; + const char *parent_name; + int mult; + int div; +}; + +#define FACTOR(_id, _name, _parent, _mult, _div) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .mult = _mult, \ + .div = _div, \ + } + +extern void mtk_clk_register_factors(const struct mtk_fixed_factor *clks, + int num, struct clk_onecell_data *clk_data); + +struct mtk_composite { + int id; + const char *name; + const char * const * parent_names; + const char *parent; + unsigned flags; + + uint32_t mux_reg; + uint32_t divider_reg; + uint32_t gate_reg; + + signed char mux_shift; + signed char mux_width; + signed char gate_shift; + + signed char divider_shift; + signed char divider_width; + + signed char num_parents; +}; + +#define MUX_GATE(_id, _name, _parents, _reg, _shift, _width, _gate) { \ + .id = _id, \ + .name = _name, \ + .mux_reg = _reg, \ + .mux_shift = _shift, \ + .mux_width = _width, \ + .gate_reg = _reg, \ + .gate_shift = _gate, \ + .divider_shift = -1, \ + .parent_names = _parents, \ + .num_parents = ARRAY_SIZE(_parents), \ + .flags = CLK_SET_RATE_PARENT, \ + } + +#define MUX(_id, _name, _parents, _reg, _shift, _width) { \ + .id = _id, \ + .name = _name, \ + .mux_reg = _reg, \ + .mux_shift = _shift, \ + .mux_width = _width, \ + .gate_shift = -1, \ + .divider_shift = -1, \ + .parent_names = _parents, \ + .num_parents = ARRAY_SIZE(_parents), \ + .flags = CLK_SET_RATE_PARENT, \ + } + +#define DIV_GATE(_id, _name, _parent, _gate_reg, _gate_shift, _div_reg, _div_width, _div_shift) { \ + .id = _id, \ + .parent = _parent, \ + .name = _name, \ + .divider_reg = _div_reg, \ + .divider_shift = _div_shift, \ + .divider_width = _div_width, \ + .gate_reg = _gate_reg, \ + .gate_shift = _gate_shift, \ + .mux_shift = -1, \ + .flags = 0, \ + } + +struct clk *mtk_clk_register_composite(const struct mtk_composite *mc, + void __iomem *base, spinlock_t *lock); + +struct mtk_gate_regs { + u32 sta_ofs; + u32 clr_ofs; + u32 set_ofs; +}; + +struct mtk_gate { + int id; + const char *name; + const char *parent_name; + const struct mtk_gate_regs *regs; + int shift; + const struct clk_ops *ops; +}; + +int mtk_clk_register_gates(struct device_node *node, const struct mtk_gate *clks, + int num, struct clk_onecell_data *clk_data); + +struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num); + +#define HAVE_RST_BAR BIT(0) + +struct mtk_pll_data { + int id; + const char *name; + uint32_t reg; + uint32_t pwr_reg; + uint32_t en_mask; + uint32_t pd_reg; + uint32_t tuner_reg; + int pd_shift; + unsigned int flags; + const struct clk_ops *ops; + u32 rst_bar_mask; + unsigned long fmax; + int pcwbits; + uint32_t pcw_reg; + int pcw_shift; +}; + +void __init mtk_clk_register_plls(struct device_node *node, + const struct mtk_pll_data *plls, int num_plls, + struct clk_onecell_data *clk_data); + +#endif /* __DRV_CLK_MTK_H */ diff --git a/drivers/clk/mediatek/clk-pll.c b/drivers/clk/mediatek/clk-pll.c new file mode 100644 index 0000000..5589e45 --- /dev/null +++ b/drivers/clk/mediatek/clk-pll.c @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao <jamesjj.liao@mediatek.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. + */ + +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/clkdev.h> +#include <linux/delay.h> + +#include "clk-mtk.h" + +#define CON0_BASE_EN BIT(0) +#define CON0_PWR_ON BIT(0) +#define CON0_ISO_EN BIT(1) +#define CON0_PCW_CHG BIT(31) + +#define AUDPLL_TUNER_EN BIT(31) + +/* + * MediaTek PLLs are configured through their pcw value. The pcw value describes + * a divider in the PLL feedback loop which consists of 7 bits for the integer + * part and the remaining bits (if present) for the fractional part. Also they + * have a 3 bit power-of-two post divider. + */ + +struct mtk_clk_pll { + struct clk_hw hw; + void __iomem *base_addr; + void __iomem *pd_addr; + void __iomem *pwr_addr; + void __iomem *tuner_addr; + void __iomem *pcw_addr; + const struct mtk_pll_data *data; +}; + +static inline struct mtk_clk_pll *to_mtk_clk_pll(struct clk_hw *hw) +{ + return container_of(hw, struct mtk_clk_pll, hw); +} + +static int mtk_pll_is_prepared(struct clk_hw *hw) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + + return (readl(pll->base_addr) & CON0_BASE_EN) != 0; +} + +static unsigned long __mtk_pll_recalc_rate(struct mtk_clk_pll *pll, u32 fin, + u32 pcw, int postdiv) +{ + int pcwbits = pll->data->pcwbits; + int pcwfbits; + u64 vco; + u8 c = 0; + + /* The fractional part of the PLL divider. */ + pcwfbits = pcwbits > 7 ? pcwbits - 7 : 0; + + vco = (u64)fin * pcw; + + if (pcwfbits && (vco & GENMASK(pcwfbits - 1, 0))) + c = 1; + + vco >>= pcwfbits; + + if (c) + ++vco; + + return ((unsigned long)vco + postdiv - 1) / postdiv; +} + +static void mtk_pll_set_rate_regs(struct clk_hw *hw, u32 pcw, + int postdiv) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 con1, pd, val; + int pll_en; + + /* set postdiv */ + pd = readl(pll->pd_addr); + pd &= ~(0x7 << pll->data->pd_shift); + pd |= (ffs(postdiv) - 1) << pll->data->pd_shift; + writel(pd, pll->pd_addr); + + pll_en = readl(pll->base_addr) & CON0_BASE_EN; + + /* set pcw */ + val = readl(pll->pcw_addr); + + val &= ~GENMASK(pll->data->pcw_shift + pll->data->pcwbits - 1, + pll->data->pcw_shift); + val |= pcw << pll->data->pcw_shift; + writel(val, pll->pcw_addr); + + con1 = readl(pll->base_addr + 4); + + if (pll_en) + con1 |= CON0_PCW_CHG; + + writel(con1, pll->base_addr + 4); + if (pll->tuner_addr) + writel(con1 + 1, pll->tuner_addr); + + if (pll_en) + usleep_range(20, 1000); +} + +/* + * mtk_pll_calc_values - calculate good values for a given input frequency. + * @pll: The pll + * @pcw: The pcw value (output) + * @postdiv: The post divider (output) + * @freq: The desired target frequency + * @fin: The input frequency + * + */ +static void mtk_pll_calc_values(struct mtk_clk_pll *pll, u32 *pcw, u32 *postdiv, + u32 freq, u32 fin) +{ + unsigned long fmin = 1000 * MHZ; + u64 _pcw; + u32 val; + + if (freq > pll->data->fmax) + freq = pll->data->fmax; + + for (val = 0; val < 4; val++) { + *postdiv = 1 << val; + if (freq * *postdiv >= fmin) + break; + } + + /* _pcw = freq * postdiv / fin * 2^pcwfbits */ + _pcw = ((u64)freq << val) << (pll->data->pcwbits - 7); + do_div(_pcw, fin); + + *pcw = (u32)_pcw; +} + +static int mtk_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 pcw = 0; + u32 postdiv; + + mtk_pll_calc_values(pll, &pcw, &postdiv, rate, parent_rate); + mtk_pll_set_rate_regs(hw, pcw, postdiv); + + return 0; +} + +static unsigned long mtk_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 postdiv; + u32 pcw; + + postdiv = (readl(pll->pd_addr) >> pll->data->pd_shift) & 0x7; + postdiv = 1 << postdiv; + + pcw = readl(pll->pcw_addr) >> pll->data->pcw_shift; + pcw &= GENMASK(pll->data->pcwbits - 1, 0); + + return __mtk_pll_recalc_rate(pll, parent_rate, pcw, postdiv); +} + +static long mtk_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 pcw = 0; + int postdiv; + + mtk_pll_calc_values(pll, &pcw, &postdiv, rate, *prate); + + return __mtk_pll_recalc_rate(pll, *prate, pcw, postdiv); +} + +static int mtk_pll_prepare(struct clk_hw *hw) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 r; + + r = readl(pll->pwr_addr) | CON0_PWR_ON; + writel(r, pll->pwr_addr); + usleep_range(1, 100); + + r = readl(pll->pwr_addr) & ~CON0_ISO_EN; + writel(r, pll->pwr_addr); + usleep_range(1, 100); + + r = readl(pll->base_addr) | pll->data->en_mask; + writel(r, pll->base_addr); + + if (pll->tuner_addr) { + r = readl(pll->tuner_addr) | AUDPLL_TUNER_EN; + writel(r, pll->tuner_addr); + } + + usleep_range(20, 1000); + + if (pll->data->flags & HAVE_RST_BAR) { + r = readl(pll->base_addr) | pll->data->rst_bar_mask; + writel(r, pll->base_addr); + } + + return 0; +} + +static void mtk_pll_unprepare(struct clk_hw *hw) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 r; + + if (pll->data->flags & HAVE_RST_BAR) { + r = readl(pll->base_addr) & ~pll->data->rst_bar_mask; + writel(r, pll->base_addr); + } + + if (pll->tuner_addr) { + r = readl(pll->tuner_addr) & ~AUDPLL_TUNER_EN; + writel(r, pll->tuner_addr); + } + + r = readl(pll->base_addr) & ~CON0_BASE_EN; + writel(r, pll->base_addr); + + r = readl(pll->pwr_addr) | CON0_ISO_EN; + writel(r, pll->pwr_addr); + + r = readl(pll->pwr_addr) & ~CON0_PWR_ON; + writel(r, pll->pwr_addr); +} + +static const struct clk_ops mtk_pll_ops = { + .is_prepared = mtk_pll_is_prepared, + .prepare = mtk_pll_prepare, + .unprepare = mtk_pll_unprepare, + .recalc_rate = mtk_pll_recalc_rate, + .round_rate = mtk_pll_round_rate, + .set_rate = mtk_pll_set_rate, +}; + +static struct clk *mtk_clk_register_pll(const struct mtk_pll_data *data, + void __iomem *base) +{ + struct mtk_clk_pll *pll; + struct clk_init_data init; + struct clk *clk; + const char *parent_name = "clk26m"; + + pr_debug("%s(): name: %s\n", __func__, data->name); + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + pll->base_addr = base + data->reg; + pll->pwr_addr = base + data->pwr_reg; + pll->pd_addr = base + data->pd_reg; + pll->pcw_addr = base + data->pcw_reg; + if (data->tuner_reg) + pll->tuner_addr = base + data->tuner_reg; + pll->hw.init = &init; + pll->data = data; + + init.name = data->name; + init.ops = &mtk_pll_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + + clk = clk_register(NULL, &pll->hw); + + if (IS_ERR(clk)) + kfree(pll); + + return clk; +} + +void __init mtk_clk_register_plls(struct device_node *node, + const struct mtk_pll_data *plls, int num_plls, struct clk_onecell_data *clk_data) +{ + void __iomem *base; + int r, i; + struct clk *clk; + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s(): ioremap failed\n", __func__); + return; + } + + for (i = 0; i < num_plls; i++) { + const struct mtk_pll_data *pll = &plls[i]; + + clk = mtk_clk_register_pll(pll, base); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + pll->name, PTR_ERR(clk)); + continue; + } + + clk_data->clks[pll->id] = clk; + } + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} -- 2.1.4 ^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v10]: clk: Add common clock support for Mediatek MT8135 and MT8173 @ 2015-03-30 17:40 Sascha Hauer 2015-03-30 17:40 ` [PATCH 2/6] clk: mediatek: Add initial common clock support for Mediatek SoCs Sascha Hauer 0 siblings, 1 reply; 22+ messages in thread From: Sascha Hauer @ 2015-03-30 17:40 UTC (permalink / raw) To: linux-arm-kernel The following changes since commit 9eccca0843205f87c00404b663188b88eb248051: Linux 4.0-rc3 (2015-03-08 16:09:09 -0700) are available in the git repository at: git://git.pengutronix.de/git/imx/linux-2.6.git tags/v4.0-clk-mediatek-v10 for you to fetch changes up to 6c36b94751bb2830388c0cc11440f3056db65b6f: dt-bindings: ARM: Mediatek: Document devicetree bindings for clock/reset controllers (2015-03-30 19:37:46 +0200) ---------------------------------------------------------------- This patchset contains the initial common clock support for Mediatek SoCs. Mediatek SoC's clock architecture comprises of various PLLs, dividers, muxes and clock gates. Changes in v10: - polish some commit messages Changes in v9: - rename 'lock' to 'mt81xx_clk_lock' to get better lockdep output Changes in v8: - add patch to allow to put parent_name arrays in __initconst - put parent_name arrays into __initconst Changes in v7: - fix duplicate definition/declaration of mtk_register_reset_controller - fix pd_reg offset of tvdpll - make clk initialization arrays const Changes in v6: - rework PLL support, only a fraction of original size now - Move binding docs to Documentation/devicetree/bindings/arm/mediatek since the units are not really clock specific (they contain reset controllers) Changes in v5: - Add reset controller support for pericfg/infracfg - Use regmap for the gates - remove now unnecessary spinlock for the gates - Add PMIC wrapper support as of v3 Changes in v4: - Support MT8173 platform. - Re-ordered patchset. driver/clk/Makefile in 2nd patch. - Extract the common part definition(mtk_gate/mtk_pll/mtk_mux) from clk-mt8135.c/clk-mt8173.c to clk-mtk.c. - Refine code. Rmove unnessacary debug information and unsed defines, add prefix "mtk_" for static functions. - Remove flag CLK_IGNORE_UNUSED and set flag CLK_SET_RATE_PARENT on gate/mux/fixed-factor. - Use spin_lock_irqsave(&clk_ops_lock, flags) instead of mtk_clk_lock. - Example above include a node for the clock controller itself, followed by the i2c controller example above. Changes in v3: - Rebase to 3.19-rc1. - Refine code. Remove unneed functions, debug logs and comments, and fine tune error logs. Changes in v2: - Re-ordered patchset. Fold include/dt-bindings and DT document in 1st patch. ---------------------------------------------------------------- James Liao (3): clk: mediatek: Add initial common clock support for Mediatek SoCs. clk: mediatek: Add basic clocks for Mediatek MT8135. clk: mediatek: Add basic clocks for Mediatek MT8173. Sascha Hauer (3): clk: make strings in parent name arrays const clk: mediatek: Add reset controller support dt-bindings: ARM: Mediatek: Document devicetree bindings for clock/reset controllers .../bindings/arm/mediatek/mediatek,apmixedsys.txt | 23 + .../bindings/arm/mediatek/mediatek,infracfg.txt | 30 + .../bindings/arm/mediatek/mediatek,pericfg.txt | 30 + .../bindings/arm/mediatek/mediatek,topckgen.txt | 23 + drivers/clk/Makefile | 1 + drivers/clk/clk-composite.c | 2 +- drivers/clk/clk-mux.c | 4 +- drivers/clk/mediatek/Makefile | 4 + drivers/clk/mediatek/clk-gate.c | 137 ++++ drivers/clk/mediatek/clk-gate.h | 49 ++ drivers/clk/mediatek/clk-mt8135.c | 640 ++++++++++++++++ drivers/clk/mediatek/clk-mt8173.c | 826 +++++++++++++++++++++ drivers/clk/mediatek/clk-mtk.c | 197 +++++ drivers/clk/mediatek/clk-mtk.h | 165 ++++ drivers/clk/mediatek/clk-pll.c | 325 ++++++++ drivers/clk/mediatek/reset.c | 99 +++ include/dt-bindings/clock/mt8135-clk.h | 190 +++++ include/dt-bindings/clock/mt8173-clk.h | 231 ++++++ .../dt-bindings/reset-controller/mt8135-resets.h | 64 ++ .../dt-bindings/reset-controller/mt8173-resets.h | 63 ++ include/linux/clk-provider.h | 8 +- 21 files changed, 3104 insertions(+), 7 deletions(-) create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt create mode 100644 drivers/clk/mediatek/Makefile create mode 100644 drivers/clk/mediatek/clk-gate.c create mode 100644 drivers/clk/mediatek/clk-gate.h create mode 100644 drivers/clk/mediatek/clk-mt8135.c create mode 100644 drivers/clk/mediatek/clk-mt8173.c create mode 100644 drivers/clk/mediatek/clk-mtk.c create mode 100644 drivers/clk/mediatek/clk-mtk.h create mode 100644 drivers/clk/mediatek/clk-pll.c create mode 100644 drivers/clk/mediatek/reset.c create mode 100644 include/dt-bindings/clock/mt8135-clk.h create mode 100644 include/dt-bindings/clock/mt8173-clk.h create mode 100644 include/dt-bindings/reset-controller/mt8135-resets.h create mode 100644 include/dt-bindings/reset-controller/mt8173-resets.h ^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 2/6] clk: mediatek: Add initial common clock support for Mediatek SoCs. 2015-03-30 17:40 [PATCH v10]: clk: Add common clock support for Mediatek MT8135 and MT8173 Sascha Hauer @ 2015-03-30 17:40 ` Sascha Hauer 2015-03-30 17:55 ` Joe Perches 2015-03-31 1:21 ` Michael Turquette 0 siblings, 2 replies; 22+ messages in thread From: Sascha Hauer @ 2015-03-30 17:40 UTC (permalink / raw) To: linux-arm-kernel From: James Liao <jamesjj.liao@mediatek.com> This patch adds common clock support for Mediatek SoCs, including plls, muxes and clock gates. Signed-off-by: James Liao <jamesjj.liao@mediatek.com> Signed-off-by: Henry Chen <henryc.chen@mediatek.com> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- drivers/clk/Makefile | 1 + drivers/clk/mediatek/Makefile | 1 + drivers/clk/mediatek/clk-gate.c | 137 +++++++++++++++++ drivers/clk/mediatek/clk-gate.h | 49 ++++++ drivers/clk/mediatek/clk-mtk.c | 197 ++++++++++++++++++++++++ drivers/clk/mediatek/clk-mtk.h | 155 +++++++++++++++++++ drivers/clk/mediatek/clk-pll.c | 325 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 865 insertions(+) create mode 100644 drivers/clk/mediatek/Makefile create mode 100644 drivers/clk/mediatek/clk-gate.c create mode 100644 drivers/clk/mediatek/clk-gate.h create mode 100644 drivers/clk/mediatek/clk-mtk.c create mode 100644 drivers/clk/mediatek/clk-mtk.h create mode 100644 drivers/clk/mediatek/clk-pll.c diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index d478ceb..6d97203 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -49,6 +49,7 @@ obj-$(CONFIG_ARCH_HI3xxx) += hisilicon/ obj-$(CONFIG_ARCH_HIP04) += hisilicon/ obj-$(CONFIG_ARCH_HIX5HD2) += hisilicon/ obj-$(CONFIG_COMMON_CLK_KEYSTONE) += keystone/ +obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/ ifeq ($(CONFIG_COMMON_CLK), y) obj-$(CONFIG_ARCH_MMP) += mmp/ endif diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile new file mode 100644 index 0000000..c384e97 --- /dev/null +++ b/drivers/clk/mediatek/Makefile @@ -0,0 +1 @@ +obj-y += clk-mtk.o clk-pll.o clk-gate.o diff --git a/drivers/clk/mediatek/clk-gate.c b/drivers/clk/mediatek/clk-gate.c new file mode 100644 index 0000000..9d77ee3 --- /dev/null +++ b/drivers/clk/mediatek/clk-gate.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao <jamesjj.liao@mediatek.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. + */ + +#include <linux/of.h> +#include <linux/of_address.h> + +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/clkdev.h> + +#include "clk-mtk.h" +#include "clk-gate.h" + +static int mtk_cg_bit_is_cleared(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_clk_gate(hw); + u32 val; + + regmap_read(cg->regmap, cg->sta_ofs, &val); + + val &= BIT(cg->bit); + + return val == 0; +} + +static int mtk_cg_bit_is_set(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_clk_gate(hw); + u32 val; + + regmap_read(cg->regmap, cg->sta_ofs, &val); + + val &= BIT(cg->bit); + + return val != 0; +} + +static void mtk_cg_set_bit(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_clk_gate(hw); + + regmap_write(cg->regmap, cg->set_ofs, BIT(cg->bit)); +} + +static void mtk_cg_clr_bit(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_clk_gate(hw); + + regmap_write(cg->regmap, cg->clr_ofs, BIT(cg->bit)); +} + +static int mtk_cg_enable(struct clk_hw *hw) +{ + mtk_cg_clr_bit(hw); + + return 0; +} + +static void mtk_cg_disable(struct clk_hw *hw) +{ + mtk_cg_set_bit(hw); +} + +static int mtk_cg_enable_inv(struct clk_hw *hw) +{ + mtk_cg_set_bit(hw); + + return 0; +} + +static void mtk_cg_disable_inv(struct clk_hw *hw) +{ + mtk_cg_clr_bit(hw); +} + +const struct clk_ops mtk_clk_gate_ops_setclr = { + .is_enabled = mtk_cg_bit_is_cleared, + .enable = mtk_cg_enable, + .disable = mtk_cg_disable, +}; + +const struct clk_ops mtk_clk_gate_ops_setclr_inv = { + .is_enabled = mtk_cg_bit_is_set, + .enable = mtk_cg_enable_inv, + .disable = mtk_cg_disable_inv, +}; + +struct clk *mtk_clk_register_gate( + const char *name, + const char *parent_name, + struct regmap *regmap, + int set_ofs, + int clr_ofs, + int sta_ofs, + u8 bit, + const struct clk_ops *ops) +{ + struct mtk_clk_gate *cg; + struct clk *clk; + struct clk_init_data init; + + cg = kzalloc(sizeof(*cg), GFP_KERNEL); + if (!cg) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + init.ops = ops; + + cg->regmap = regmap; + cg->set_ofs = set_ofs; + cg->clr_ofs = clr_ofs; + cg->sta_ofs = sta_ofs; + cg->bit = bit; + + cg->hw.init = &init; + + clk = clk_register(NULL, &cg->hw); + if (IS_ERR(clk)) + kfree(cg); + + return clk; +} diff --git a/drivers/clk/mediatek/clk-gate.h b/drivers/clk/mediatek/clk-gate.h new file mode 100644 index 0000000..6b6780b --- /dev/null +++ b/drivers/clk/mediatek/clk-gate.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao <jamesjj.liao@mediatek.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. + */ + +#ifndef __DRV_CLK_GATE_H +#define __DRV_CLK_GATE_H + +#include <linux/regmap.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> + +struct mtk_clk_gate { + struct clk_hw hw; + struct regmap *regmap; + int set_ofs; + int clr_ofs; + int sta_ofs; + u8 bit; +}; + +static inline struct mtk_clk_gate *to_clk_gate(struct clk_hw *hw) +{ + return container_of(hw, struct mtk_clk_gate, hw); +} + +extern const struct clk_ops mtk_clk_gate_ops_setclr; +extern const struct clk_ops mtk_clk_gate_ops_setclr_inv; + +struct clk *mtk_clk_register_gate( + const char *name, + const char *parent_name, + struct regmap *regmap, + int set_ofs, + int clr_ofs, + int sta_ofs, + u8 bit, + const struct clk_ops *ops); + +#endif /* __DRV_CLK_GATE_H */ diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c new file mode 100644 index 0000000..a0c9f43 --- /dev/null +++ b/drivers/clk/mediatek/clk-mtk.c @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao <jamesjj.liao@mediatek.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. + */ + +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/clkdev.h> +#include <linux/mfd/syscon.h> + +#include "clk-mtk.h" +#include "clk-gate.h" + +struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num) +{ + int i; + struct clk_onecell_data *clk_data; + + clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); + if (!clk_data) + return NULL; + + clk_data->clks = kcalloc(clk_num, sizeof(*clk_data->clks), GFP_KERNEL); + if (!clk_data->clks) + goto err_out; + + clk_data->clk_num = clk_num; + + for (i = 0; i < clk_num; ++i) + clk_data->clks[i] = ERR_PTR(-ENOENT); + + return clk_data; +err_out: + kfree(clk_data); + + return NULL; +} + +void mtk_clk_register_factors(const struct mtk_fixed_factor *clks, int num, + struct clk_onecell_data *clk_data) +{ + int i; + struct clk *clk; + + for (i = 0; i < num; i++) { + const struct mtk_fixed_factor *ff = &clks[i]; + + clk = clk_register_fixed_factor(NULL, ff->name, ff->parent_name, + CLK_SET_RATE_PARENT, ff->mult, ff->div); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + ff->name, PTR_ERR(clk)); + continue; + } + + if (clk_data) + clk_data->clks[ff->id] = clk; + } +} + +int mtk_clk_register_gates(struct device_node *node, const struct mtk_gate *clks, + int num, struct clk_onecell_data *clk_data) +{ + int i; + struct clk *clk; + struct regmap *regmap; + + if (!clk_data) + return -ENOMEM; + + regmap = syscon_node_to_regmap(node); + if (IS_ERR(regmap)) { + pr_err("Cannot find regmap for %s: %ld\n", node->full_name, + PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + for (i = 0; i < num; i++) { + const struct mtk_gate *gate = &clks[i]; + + clk = mtk_clk_register_gate(gate->name, gate->parent_name, + regmap, + gate->regs->set_ofs, + gate->regs->clr_ofs, + gate->regs->sta_ofs, + gate->shift, gate->ops); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + gate->name, PTR_ERR(clk)); + continue; + } + + clk_data->clks[gate->id] = clk; + } + + return 0; +} + +struct clk *mtk_clk_register_composite(const struct mtk_composite *mc, + void __iomem *base, spinlock_t *lock) +{ + struct clk *clk; + struct clk_mux *mux = NULL; + struct clk_gate *gate = NULL; + struct clk_divider *div = NULL; + struct clk_hw *mux_hw = NULL, *gate_hw = NULL, *div_hw = NULL; + const struct clk_ops *mux_ops = NULL, *gate_ops = NULL, *div_ops = NULL; + const char * const *parent_names; + const char *parent; + int num_parents; + int ret; + + if (mc->mux_shift >= 0) { + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) + return ERR_PTR(-ENOMEM); + + mux->reg = base + mc->mux_reg; + mux->mask = BIT(mc->mux_width) - 1; + mux->shift = mc->mux_shift; + mux->lock = lock; + + mux_hw = &mux->hw; + mux_ops = &clk_mux_ops; + + parent_names = mc->parent_names; + num_parents = mc->num_parents; + } else { + parent = mc->parent; + parent_names = &parent; + num_parents = 1; + } + + if (mc->gate_shift >= 0) { + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) { + ret = -ENOMEM; + goto err_out; + } + + gate->reg = base + mc->gate_reg; + gate->bit_idx = mc->gate_shift; + gate->flags = CLK_GATE_SET_TO_DISABLE; + gate->lock = lock; + + gate_hw = &gate->hw; + gate_ops = &clk_gate_ops; + } + + if (mc->divider_shift >= 0) { + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) { + ret = -ENOMEM; + goto err_out; + } + + div->reg = base + mc->divider_reg; + div->shift = mc->divider_shift; + div->width = mc->divider_width; + div->lock = lock; + + div_hw = &div->hw; + div_ops = &clk_divider_ops; + } + + clk = clk_register_composite(NULL, mc->name, parent_names, num_parents, + mux_hw, mux_ops, + div_hw, div_ops, + gate_hw, gate_ops, + mc->flags); + + if (IS_ERR(clk)) { + kfree(gate); + kfree(mux); + } + + return clk; +err_out: + kfree(mux); + + return ERR_PTR(ret); +} diff --git a/drivers/clk/mediatek/clk-mtk.h b/drivers/clk/mediatek/clk-mtk.h new file mode 100644 index 0000000..5aaba81 --- /dev/null +++ b/drivers/clk/mediatek/clk-mtk.h @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao <jamesjj.liao@mediatek.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. + */ + +#ifndef __DRV_CLK_MTK_H +#define __DRV_CLK_MTK_H + +#include <linux/regmap.h> +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> + +#define MAX_MUX_GATE_BIT 31 +#define INVALID_MUX_GATE_BIT (MAX_MUX_GATE_BIT + 1) + +#define MHZ (1000 * 1000) + +struct mtk_fixed_factor { + int id; + const char *name; + const char *parent_name; + int mult; + int div; +}; + +#define FACTOR(_id, _name, _parent, _mult, _div) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .mult = _mult, \ + .div = _div, \ + } + +extern void mtk_clk_register_factors(const struct mtk_fixed_factor *clks, + int num, struct clk_onecell_data *clk_data); + +struct mtk_composite { + int id; + const char *name; + const char * const * parent_names; + const char *parent; + unsigned flags; + + uint32_t mux_reg; + uint32_t divider_reg; + uint32_t gate_reg; + + signed char mux_shift; + signed char mux_width; + signed char gate_shift; + + signed char divider_shift; + signed char divider_width; + + signed char num_parents; +}; + +#define MUX_GATE(_id, _name, _parents, _reg, _shift, _width, _gate) { \ + .id = _id, \ + .name = _name, \ + .mux_reg = _reg, \ + .mux_shift = _shift, \ + .mux_width = _width, \ + .gate_reg = _reg, \ + .gate_shift = _gate, \ + .divider_shift = -1, \ + .parent_names = _parents, \ + .num_parents = ARRAY_SIZE(_parents), \ + .flags = CLK_SET_RATE_PARENT, \ + } + +#define MUX(_id, _name, _parents, _reg, _shift, _width) { \ + .id = _id, \ + .name = _name, \ + .mux_reg = _reg, \ + .mux_shift = _shift, \ + .mux_width = _width, \ + .gate_shift = -1, \ + .divider_shift = -1, \ + .parent_names = _parents, \ + .num_parents = ARRAY_SIZE(_parents), \ + .flags = CLK_SET_RATE_PARENT, \ + } + +#define DIV_GATE(_id, _name, _parent, _gate_reg, _gate_shift, _div_reg, _div_width, _div_shift) { \ + .id = _id, \ + .parent = _parent, \ + .name = _name, \ + .divider_reg = _div_reg, \ + .divider_shift = _div_shift, \ + .divider_width = _div_width, \ + .gate_reg = _gate_reg, \ + .gate_shift = _gate_shift, \ + .mux_shift = -1, \ + .flags = 0, \ + } + +struct clk *mtk_clk_register_composite(const struct mtk_composite *mc, + void __iomem *base, spinlock_t *lock); + +struct mtk_gate_regs { + u32 sta_ofs; + u32 clr_ofs; + u32 set_ofs; +}; + +struct mtk_gate { + int id; + const char *name; + const char *parent_name; + const struct mtk_gate_regs *regs; + int shift; + const struct clk_ops *ops; +}; + +int mtk_clk_register_gates(struct device_node *node, const struct mtk_gate *clks, + int num, struct clk_onecell_data *clk_data); + +struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num); + +#define HAVE_RST_BAR BIT(0) + +struct mtk_pll_data { + int id; + const char *name; + uint32_t reg; + uint32_t pwr_reg; + uint32_t en_mask; + uint32_t pd_reg; + uint32_t tuner_reg; + int pd_shift; + unsigned int flags; + const struct clk_ops *ops; + u32 rst_bar_mask; + unsigned long fmax; + int pcwbits; + uint32_t pcw_reg; + int pcw_shift; +}; + +void __init mtk_clk_register_plls(struct device_node *node, + const struct mtk_pll_data *plls, int num_plls, + struct clk_onecell_data *clk_data); + +#endif /* __DRV_CLK_MTK_H */ diff --git a/drivers/clk/mediatek/clk-pll.c b/drivers/clk/mediatek/clk-pll.c new file mode 100644 index 0000000..5589e45 --- /dev/null +++ b/drivers/clk/mediatek/clk-pll.c @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao <jamesjj.liao@mediatek.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. + */ + +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/clkdev.h> +#include <linux/delay.h> + +#include "clk-mtk.h" + +#define CON0_BASE_EN BIT(0) +#define CON0_PWR_ON BIT(0) +#define CON0_ISO_EN BIT(1) +#define CON0_PCW_CHG BIT(31) + +#define AUDPLL_TUNER_EN BIT(31) + +/* + * MediaTek PLLs are configured through their pcw value. The pcw value describes + * a divider in the PLL feedback loop which consists of 7 bits for the integer + * part and the remaining bits (if present) for the fractional part. Also they + * have a 3 bit power-of-two post divider. + */ + +struct mtk_clk_pll { + struct clk_hw hw; + void __iomem *base_addr; + void __iomem *pd_addr; + void __iomem *pwr_addr; + void __iomem *tuner_addr; + void __iomem *pcw_addr; + const struct mtk_pll_data *data; +}; + +static inline struct mtk_clk_pll *to_mtk_clk_pll(struct clk_hw *hw) +{ + return container_of(hw, struct mtk_clk_pll, hw); +} + +static int mtk_pll_is_prepared(struct clk_hw *hw) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + + return (readl(pll->base_addr) & CON0_BASE_EN) != 0; +} + +static unsigned long __mtk_pll_recalc_rate(struct mtk_clk_pll *pll, u32 fin, + u32 pcw, int postdiv) +{ + int pcwbits = pll->data->pcwbits; + int pcwfbits; + u64 vco; + u8 c = 0; + + /* The fractional part of the PLL divider. */ + pcwfbits = pcwbits > 7 ? pcwbits - 7 : 0; + + vco = (u64)fin * pcw; + + if (pcwfbits && (vco & GENMASK(pcwfbits - 1, 0))) + c = 1; + + vco >>= pcwfbits; + + if (c) + ++vco; + + return ((unsigned long)vco + postdiv - 1) / postdiv; +} + +static void mtk_pll_set_rate_regs(struct clk_hw *hw, u32 pcw, + int postdiv) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 con1, pd, val; + int pll_en; + + /* set postdiv */ + pd = readl(pll->pd_addr); + pd &= ~(0x7 << pll->data->pd_shift); + pd |= (ffs(postdiv) - 1) << pll->data->pd_shift; + writel(pd, pll->pd_addr); + + pll_en = readl(pll->base_addr) & CON0_BASE_EN; + + /* set pcw */ + val = readl(pll->pcw_addr); + + val &= ~GENMASK(pll->data->pcw_shift + pll->data->pcwbits - 1, + pll->data->pcw_shift); + val |= pcw << pll->data->pcw_shift; + writel(val, pll->pcw_addr); + + con1 = readl(pll->base_addr + 4); + + if (pll_en) + con1 |= CON0_PCW_CHG; + + writel(con1, pll->base_addr + 4); + if (pll->tuner_addr) + writel(con1 + 1, pll->tuner_addr); + + if (pll_en) + usleep_range(20, 1000); +} + +/* + * mtk_pll_calc_values - calculate good values for a given input frequency. + * @pll: The pll + * @pcw: The pcw value (output) + * @postdiv: The post divider (output) + * @freq: The desired target frequency + * @fin: The input frequency + * + */ +static void mtk_pll_calc_values(struct mtk_clk_pll *pll, u32 *pcw, u32 *postdiv, + u32 freq, u32 fin) +{ + unsigned long fmin = 1000 * MHZ; + u64 _pcw; + u32 val; + + if (freq > pll->data->fmax) + freq = pll->data->fmax; + + for (val = 0; val < 4; val++) { + *postdiv = 1 << val; + if (freq * *postdiv >= fmin) + break; + } + + /* _pcw = freq * postdiv / fin * 2^pcwfbits */ + _pcw = ((u64)freq << val) << (pll->data->pcwbits - 7); + do_div(_pcw, fin); + + *pcw = (u32)_pcw; +} + +static int mtk_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 pcw = 0; + u32 postdiv; + + mtk_pll_calc_values(pll, &pcw, &postdiv, rate, parent_rate); + mtk_pll_set_rate_regs(hw, pcw, postdiv); + + return 0; +} + +static unsigned long mtk_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 postdiv; + u32 pcw; + + postdiv = (readl(pll->pd_addr) >> pll->data->pd_shift) & 0x7; + postdiv = 1 << postdiv; + + pcw = readl(pll->pcw_addr) >> pll->data->pcw_shift; + pcw &= GENMASK(pll->data->pcwbits - 1, 0); + + return __mtk_pll_recalc_rate(pll, parent_rate, pcw, postdiv); +} + +static long mtk_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 pcw = 0; + int postdiv; + + mtk_pll_calc_values(pll, &pcw, &postdiv, rate, *prate); + + return __mtk_pll_recalc_rate(pll, *prate, pcw, postdiv); +} + +static int mtk_pll_prepare(struct clk_hw *hw) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 r; + + r = readl(pll->pwr_addr) | CON0_PWR_ON; + writel(r, pll->pwr_addr); + usleep_range(1, 100); + + r = readl(pll->pwr_addr) & ~CON0_ISO_EN; + writel(r, pll->pwr_addr); + usleep_range(1, 100); + + r = readl(pll->base_addr) | pll->data->en_mask; + writel(r, pll->base_addr); + + if (pll->tuner_addr) { + r = readl(pll->tuner_addr) | AUDPLL_TUNER_EN; + writel(r, pll->tuner_addr); + } + + usleep_range(20, 1000); + + if (pll->data->flags & HAVE_RST_BAR) { + r = readl(pll->base_addr) | pll->data->rst_bar_mask; + writel(r, pll->base_addr); + } + + return 0; +} + +static void mtk_pll_unprepare(struct clk_hw *hw) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 r; + + if (pll->data->flags & HAVE_RST_BAR) { + r = readl(pll->base_addr) & ~pll->data->rst_bar_mask; + writel(r, pll->base_addr); + } + + if (pll->tuner_addr) { + r = readl(pll->tuner_addr) & ~AUDPLL_TUNER_EN; + writel(r, pll->tuner_addr); + } + + r = readl(pll->base_addr) & ~CON0_BASE_EN; + writel(r, pll->base_addr); + + r = readl(pll->pwr_addr) | CON0_ISO_EN; + writel(r, pll->pwr_addr); + + r = readl(pll->pwr_addr) & ~CON0_PWR_ON; + writel(r, pll->pwr_addr); +} + +static const struct clk_ops mtk_pll_ops = { + .is_prepared = mtk_pll_is_prepared, + .prepare = mtk_pll_prepare, + .unprepare = mtk_pll_unprepare, + .recalc_rate = mtk_pll_recalc_rate, + .round_rate = mtk_pll_round_rate, + .set_rate = mtk_pll_set_rate, +}; + +static struct clk *mtk_clk_register_pll(const struct mtk_pll_data *data, + void __iomem *base) +{ + struct mtk_clk_pll *pll; + struct clk_init_data init; + struct clk *clk; + const char *parent_name = "clk26m"; + + pr_debug("%s(): name: %s\n", __func__, data->name); + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + pll->base_addr = base + data->reg; + pll->pwr_addr = base + data->pwr_reg; + pll->pd_addr = base + data->pd_reg; + pll->pcw_addr = base + data->pcw_reg; + if (data->tuner_reg) + pll->tuner_addr = base + data->tuner_reg; + pll->hw.init = &init; + pll->data = data; + + init.name = data->name; + init.ops = &mtk_pll_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + + clk = clk_register(NULL, &pll->hw); + + if (IS_ERR(clk)) + kfree(pll); + + return clk; +} + +void __init mtk_clk_register_plls(struct device_node *node, + const struct mtk_pll_data *plls, int num_plls, struct clk_onecell_data *clk_data) +{ + void __iomem *base; + int r, i; + struct clk *clk; + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s(): ioremap failed\n", __func__); + return; + } + + for (i = 0; i < num_plls; i++) { + const struct mtk_pll_data *pll = &plls[i]; + + clk = mtk_clk_register_pll(pll, base); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + pll->name, PTR_ERR(clk)); + continue; + } + + clk_data->clks[pll->id] = clk; + } + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} -- 2.1.4 ^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 2/6] clk: mediatek: Add initial common clock support for Mediatek SoCs. 2015-03-30 17:40 ` [PATCH 2/6] clk: mediatek: Add initial common clock support for Mediatek SoCs Sascha Hauer @ 2015-03-30 17:55 ` Joe Perches 2015-03-30 18:31 ` Sascha Hauer 2015-03-31 1:21 ` Michael Turquette 1 sibling, 1 reply; 22+ messages in thread From: Joe Perches @ 2015-03-30 17:55 UTC (permalink / raw) To: linux-arm-kernel On Mon, 2015-03-30 at 19:40 +0200, Sascha Hauer wrote: > This patch adds common clock support for Mediatek SoCs, including plls, > muxes and clock gates. trivia: > diff --git a/drivers/clk/mediatek/clk-gate.c b/drivers/clk/mediatek/clk-gate.c > +static int mtk_cg_bit_is_cleared(struct clk_hw *hw) > +{ [] > + return val == 0; > +} > + > +static int mtk_cg_bit_is_set(struct clk_hw *hw) > +{ [] > + return val != 0; > +} These functions may be better returning a bool > diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c [] > +struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num) > +{ [] > + for (i = 0; i < clk_num; ++i) [] > +void mtk_clk_register_factors(const struct mtk_fixed_factor *clks, int num, > + struct clk_onecell_data *clk_data) > +{ > + for (i = 0; i < num; i++) { Please use consistent postfix ++ style ^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 2/6] clk: mediatek: Add initial common clock support for Mediatek SoCs. 2015-03-30 17:55 ` Joe Perches @ 2015-03-30 18:31 ` Sascha Hauer 0 siblings, 0 replies; 22+ messages in thread From: Sascha Hauer @ 2015-03-30 18:31 UTC (permalink / raw) To: linux-arm-kernel On Mon, Mar 30, 2015 at 10:55:46AM -0700, Joe Perches wrote: > On Mon, 2015-03-30 at 19:40 +0200, Sascha Hauer wrote: > > This patch adds common clock support for Mediatek SoCs, including plls, > > muxes and clock gates. > > trivia: > > > diff --git a/drivers/clk/mediatek/clk-gate.c b/drivers/clk/mediatek/clk-gate.c > > > +static int mtk_cg_bit_is_cleared(struct clk_hw *hw) > > +{ > [] > > + return val == 0; > > +} > > + > > +static int mtk_cg_bit_is_set(struct clk_hw *hw) > > +{ > [] > > + return val != 0; > > +} > > These functions may be better returning a bool The return type of these functions is forced by function prototype in struct clk_ops. Sascha -- Pengutronix e.K. | | Industrial Linux Solutions | http://www.pengutronix.de/ | Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 | ^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 2/6] clk: mediatek: Add initial common clock support for Mediatek SoCs. 2015-03-30 17:40 ` [PATCH 2/6] clk: mediatek: Add initial common clock support for Mediatek SoCs Sascha Hauer 2015-03-30 17:55 ` Joe Perches @ 2015-03-31 1:21 ` Michael Turquette 1 sibling, 0 replies; 22+ messages in thread From: Michael Turquette @ 2015-03-31 1:21 UTC (permalink / raw) To: linux-arm-kernel Quoting Sascha Hauer (2015-03-30 10:40:41) > +static void mtk_pll_set_rate_regs(struct clk_hw *hw, u32 pcw, > + int postdiv) > +{ > + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); > + u32 con1, pd, val; > + int pll_en; > + > + /* set postdiv */ > + pd = readl(pll->pd_addr); > + pd &= ~(0x7 << pll->data->pd_shift); Hi Sascha, I found a couple of magic numbers in here. Can we replace these with some descriptive constant? Regards, Mike ^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v11]: clk: Add common clock support for Mediatek MT8135 and MT8173 @ 2015-03-31 18:16 Sascha Hauer 2015-03-31 18:16 ` [PATCH 2/6] clk: mediatek: Add initial common clock support for Mediatek SoCs Sascha Hauer 0 siblings, 1 reply; 22+ messages in thread From: Sascha Hauer @ 2015-03-31 18:16 UTC (permalink / raw) To: linux-arm-kernel The following changes since commit 9eccca0843205f87c00404b663188b88eb248051: Linux 4.0-rc3 (2015-03-08 16:09:09 -0700) are available in the git repository at: git://git.pengutronix.de/git/imx/linux-2.6.git tags/v4.0-clk-mediatek-v11 for you to fetch changes up to ae9129219143cfdefe8b3a463deb8c5cb8955525: dt-bindings: ARM: Mediatek: Document devicetree bindings for clock/reset controllers (2015-03-31 20:08:46 +0200) ---------------------------------------------------------------- This patchset contains the initial common clock support for Mediatek SoCs. Mediatek SoC's clock architecture comprises of various PLLs, dividers, muxes and clock gates. Changes in v11: - Use more defines in PLL code - drop unused pr_fmt - use i++ instead of ++i in two places Changes in v10: - polish some commit messages Changes in v9: - rename 'lock' to 'mt81xx_clk_lock' to get better lockdep output Changes in v8: - add patch to allow to put parent_name arrays in __initconst - put parent_name arrays into __initconst Changes in v7: - fix duplicate definition/declaration of mtk_register_reset_controller - fix pd_reg offset of tvdpll - make clk initialization arrays const Changes in v6: - rework PLL support, only a fraction of original size now - Move binding docs to Documentation/devicetree/bindings/arm/mediatek since the units are not really clock specific (they contain reset controllers) Changes in v5: - Add reset controller support for pericfg/infracfg - Use regmap for the gates - remove now unnecessary spinlock for the gates - Add PMIC wrapper support as of v3 Changes in v4: - Support MT8173 platform. - Re-ordered patchset. driver/clk/Makefile in 2nd patch. - Extract the common part definition(mtk_gate/mtk_pll/mtk_mux) from clk-mt8135.c/clk-mt8173.c to clk-mtk.c. - Refine code. Rmove unnessacary debug information and unsed defines, add prefix "mtk_" for static functions. - Remove flag CLK_IGNORE_UNUSED and set flag CLK_SET_RATE_PARENT on gate/mux/fixed-factor. - Use spin_lock_irqsave(&clk_ops_lock, flags) instead of mtk_clk_lock. - Example above include a node for the clock controller itself, followed by the i2c controller example above. Changes in v3: - Rebase to 3.19-rc1. - Refine code. Remove unneed functions, debug logs and comments, and fine tune error logs. Changes in v2: - Re-ordered patchset. Fold include/dt-bindings and DT document in 1st patch. ---------------------------------------------------------------- James Liao (3): clk: mediatek: Add initial common clock support for Mediatek SoCs. clk: mediatek: Add basic clocks for Mediatek MT8135. clk: mediatek: Add basic clocks for Mediatek MT8173. Sascha Hauer (3): clk: make strings in parent name arrays const clk: mediatek: Add reset controller support dt-bindings: ARM: Mediatek: Document devicetree bindings for clock/reset controllers .../bindings/arm/mediatek/mediatek,apmixedsys.txt | 23 + .../bindings/arm/mediatek/mediatek,infracfg.txt | 30 + .../bindings/arm/mediatek/mediatek,pericfg.txt | 30 + .../bindings/arm/mediatek/mediatek,topckgen.txt | 23 + drivers/clk/Makefile | 1 + drivers/clk/clk-composite.c | 2 +- drivers/clk/clk-mux.c | 4 +- drivers/clk/mediatek/Makefile | 4 + drivers/clk/mediatek/clk-gate.c | 137 ++++ drivers/clk/mediatek/clk-gate.h | 49 ++ drivers/clk/mediatek/clk-mt8135.c | 640 ++++++++++++++++ drivers/clk/mediatek/clk-mt8173.c | 826 +++++++++++++++++++++ drivers/clk/mediatek/clk-mtk.c | 197 +++++ drivers/clk/mediatek/clk-mtk.h | 165 ++++ drivers/clk/mediatek/clk-pll.c | 335 +++++++++ drivers/clk/mediatek/reset.c | 97 +++ include/dt-bindings/clock/mt8135-clk.h | 190 +++++ include/dt-bindings/clock/mt8173-clk.h | 231 ++++++ .../dt-bindings/reset-controller/mt8135-resets.h | 64 ++ .../dt-bindings/reset-controller/mt8173-resets.h | 63 ++ include/linux/clk-provider.h | 8 +- 21 files changed, 3112 insertions(+), 7 deletions(-) create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt create mode 100644 drivers/clk/mediatek/Makefile create mode 100644 drivers/clk/mediatek/clk-gate.c create mode 100644 drivers/clk/mediatek/clk-gate.h create mode 100644 drivers/clk/mediatek/clk-mt8135.c create mode 100644 drivers/clk/mediatek/clk-mt8173.c create mode 100644 drivers/clk/mediatek/clk-mtk.c create mode 100644 drivers/clk/mediatek/clk-mtk.h create mode 100644 drivers/clk/mediatek/clk-pll.c create mode 100644 drivers/clk/mediatek/reset.c create mode 100644 include/dt-bindings/clock/mt8135-clk.h create mode 100644 include/dt-bindings/clock/mt8173-clk.h create mode 100644 include/dt-bindings/reset-controller/mt8135-resets.h create mode 100644 include/dt-bindings/reset-controller/mt8173-resets.h ^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 2/6] clk: mediatek: Add initial common clock support for Mediatek SoCs. 2015-03-31 18:16 [PATCH v11]: clk: Add common clock support for Mediatek MT8135 and MT8173 Sascha Hauer @ 2015-03-31 18:16 ` Sascha Hauer 2015-04-09 17:03 ` Matthias Brugger 0 siblings, 1 reply; 22+ messages in thread From: Sascha Hauer @ 2015-03-31 18:16 UTC (permalink / raw) To: linux-arm-kernel From: James Liao <jamesjj.liao@mediatek.com> This patch adds common clock support for Mediatek SoCs, including plls, muxes and clock gates. Signed-off-by: James Liao <jamesjj.liao@mediatek.com> Signed-off-by: Henry Chen <henryc.chen@mediatek.com> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- drivers/clk/Makefile | 1 + drivers/clk/mediatek/Makefile | 1 + drivers/clk/mediatek/clk-gate.c | 137 ++++++++++++++++ drivers/clk/mediatek/clk-gate.h | 49 ++++++ drivers/clk/mediatek/clk-mtk.c | 197 +++++++++++++++++++++++ drivers/clk/mediatek/clk-mtk.h | 155 +++++++++++++++++++ drivers/clk/mediatek/clk-pll.c | 335 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 875 insertions(+) create mode 100644 drivers/clk/mediatek/Makefile create mode 100644 drivers/clk/mediatek/clk-gate.c create mode 100644 drivers/clk/mediatek/clk-gate.h create mode 100644 drivers/clk/mediatek/clk-mtk.c create mode 100644 drivers/clk/mediatek/clk-mtk.h create mode 100644 drivers/clk/mediatek/clk-pll.c diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index d478ceb..6d97203 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -49,6 +49,7 @@ obj-$(CONFIG_ARCH_HI3xxx) += hisilicon/ obj-$(CONFIG_ARCH_HIP04) += hisilicon/ obj-$(CONFIG_ARCH_HIX5HD2) += hisilicon/ obj-$(CONFIG_COMMON_CLK_KEYSTONE) += keystone/ +obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/ ifeq ($(CONFIG_COMMON_CLK), y) obj-$(CONFIG_ARCH_MMP) += mmp/ endif diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile new file mode 100644 index 0000000..c384e97 --- /dev/null +++ b/drivers/clk/mediatek/Makefile @@ -0,0 +1 @@ +obj-y += clk-mtk.o clk-pll.o clk-gate.o diff --git a/drivers/clk/mediatek/clk-gate.c b/drivers/clk/mediatek/clk-gate.c new file mode 100644 index 0000000..9d77ee3 --- /dev/null +++ b/drivers/clk/mediatek/clk-gate.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao <jamesjj.liao@mediatek.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. + */ + +#include <linux/of.h> +#include <linux/of_address.h> + +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/clkdev.h> + +#include "clk-mtk.h" +#include "clk-gate.h" + +static int mtk_cg_bit_is_cleared(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_clk_gate(hw); + u32 val; + + regmap_read(cg->regmap, cg->sta_ofs, &val); + + val &= BIT(cg->bit); + + return val == 0; +} + +static int mtk_cg_bit_is_set(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_clk_gate(hw); + u32 val; + + regmap_read(cg->regmap, cg->sta_ofs, &val); + + val &= BIT(cg->bit); + + return val != 0; +} + +static void mtk_cg_set_bit(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_clk_gate(hw); + + regmap_write(cg->regmap, cg->set_ofs, BIT(cg->bit)); +} + +static void mtk_cg_clr_bit(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_clk_gate(hw); + + regmap_write(cg->regmap, cg->clr_ofs, BIT(cg->bit)); +} + +static int mtk_cg_enable(struct clk_hw *hw) +{ + mtk_cg_clr_bit(hw); + + return 0; +} + +static void mtk_cg_disable(struct clk_hw *hw) +{ + mtk_cg_set_bit(hw); +} + +static int mtk_cg_enable_inv(struct clk_hw *hw) +{ + mtk_cg_set_bit(hw); + + return 0; +} + +static void mtk_cg_disable_inv(struct clk_hw *hw) +{ + mtk_cg_clr_bit(hw); +} + +const struct clk_ops mtk_clk_gate_ops_setclr = { + .is_enabled = mtk_cg_bit_is_cleared, + .enable = mtk_cg_enable, + .disable = mtk_cg_disable, +}; + +const struct clk_ops mtk_clk_gate_ops_setclr_inv = { + .is_enabled = mtk_cg_bit_is_set, + .enable = mtk_cg_enable_inv, + .disable = mtk_cg_disable_inv, +}; + +struct clk *mtk_clk_register_gate( + const char *name, + const char *parent_name, + struct regmap *regmap, + int set_ofs, + int clr_ofs, + int sta_ofs, + u8 bit, + const struct clk_ops *ops) +{ + struct mtk_clk_gate *cg; + struct clk *clk; + struct clk_init_data init; + + cg = kzalloc(sizeof(*cg), GFP_KERNEL); + if (!cg) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + init.ops = ops; + + cg->regmap = regmap; + cg->set_ofs = set_ofs; + cg->clr_ofs = clr_ofs; + cg->sta_ofs = sta_ofs; + cg->bit = bit; + + cg->hw.init = &init; + + clk = clk_register(NULL, &cg->hw); + if (IS_ERR(clk)) + kfree(cg); + + return clk; +} diff --git a/drivers/clk/mediatek/clk-gate.h b/drivers/clk/mediatek/clk-gate.h new file mode 100644 index 0000000..6b6780b --- /dev/null +++ b/drivers/clk/mediatek/clk-gate.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao <jamesjj.liao@mediatek.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. + */ + +#ifndef __DRV_CLK_GATE_H +#define __DRV_CLK_GATE_H + +#include <linux/regmap.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> + +struct mtk_clk_gate { + struct clk_hw hw; + struct regmap *regmap; + int set_ofs; + int clr_ofs; + int sta_ofs; + u8 bit; +}; + +static inline struct mtk_clk_gate *to_clk_gate(struct clk_hw *hw) +{ + return container_of(hw, struct mtk_clk_gate, hw); +} + +extern const struct clk_ops mtk_clk_gate_ops_setclr; +extern const struct clk_ops mtk_clk_gate_ops_setclr_inv; + +struct clk *mtk_clk_register_gate( + const char *name, + const char *parent_name, + struct regmap *regmap, + int set_ofs, + int clr_ofs, + int sta_ofs, + u8 bit, + const struct clk_ops *ops); + +#endif /* __DRV_CLK_GATE_H */ diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c new file mode 100644 index 0000000..746bb34 --- /dev/null +++ b/drivers/clk/mediatek/clk-mtk.c @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao <jamesjj.liao@mediatek.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. + */ + +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/clkdev.h> +#include <linux/mfd/syscon.h> + +#include "clk-mtk.h" +#include "clk-gate.h" + +struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num) +{ + int i; + struct clk_onecell_data *clk_data; + + clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); + if (!clk_data) + return NULL; + + clk_data->clks = kcalloc(clk_num, sizeof(*clk_data->clks), GFP_KERNEL); + if (!clk_data->clks) + goto err_out; + + clk_data->clk_num = clk_num; + + for (i = 0; i < clk_num; i++) + clk_data->clks[i] = ERR_PTR(-ENOENT); + + return clk_data; +err_out: + kfree(clk_data); + + return NULL; +} + +void mtk_clk_register_factors(const struct mtk_fixed_factor *clks, int num, + struct clk_onecell_data *clk_data) +{ + int i; + struct clk *clk; + + for (i = 0; i < num; i++) { + const struct mtk_fixed_factor *ff = &clks[i]; + + clk = clk_register_fixed_factor(NULL, ff->name, ff->parent_name, + CLK_SET_RATE_PARENT, ff->mult, ff->div); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + ff->name, PTR_ERR(clk)); + continue; + } + + if (clk_data) + clk_data->clks[ff->id] = clk; + } +} + +int mtk_clk_register_gates(struct device_node *node, const struct mtk_gate *clks, + int num, struct clk_onecell_data *clk_data) +{ + int i; + struct clk *clk; + struct regmap *regmap; + + if (!clk_data) + return -ENOMEM; + + regmap = syscon_node_to_regmap(node); + if (IS_ERR(regmap)) { + pr_err("Cannot find regmap for %s: %ld\n", node->full_name, + PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + for (i = 0; i < num; i++) { + const struct mtk_gate *gate = &clks[i]; + + clk = mtk_clk_register_gate(gate->name, gate->parent_name, + regmap, + gate->regs->set_ofs, + gate->regs->clr_ofs, + gate->regs->sta_ofs, + gate->shift, gate->ops); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + gate->name, PTR_ERR(clk)); + continue; + } + + clk_data->clks[gate->id] = clk; + } + + return 0; +} + +struct clk *mtk_clk_register_composite(const struct mtk_composite *mc, + void __iomem *base, spinlock_t *lock) +{ + struct clk *clk; + struct clk_mux *mux = NULL; + struct clk_gate *gate = NULL; + struct clk_divider *div = NULL; + struct clk_hw *mux_hw = NULL, *gate_hw = NULL, *div_hw = NULL; + const struct clk_ops *mux_ops = NULL, *gate_ops = NULL, *div_ops = NULL; + const char * const *parent_names; + const char *parent; + int num_parents; + int ret; + + if (mc->mux_shift >= 0) { + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) + return ERR_PTR(-ENOMEM); + + mux->reg = base + mc->mux_reg; + mux->mask = BIT(mc->mux_width) - 1; + mux->shift = mc->mux_shift; + mux->lock = lock; + + mux_hw = &mux->hw; + mux_ops = &clk_mux_ops; + + parent_names = mc->parent_names; + num_parents = mc->num_parents; + } else { + parent = mc->parent; + parent_names = &parent; + num_parents = 1; + } + + if (mc->gate_shift >= 0) { + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) { + ret = -ENOMEM; + goto err_out; + } + + gate->reg = base + mc->gate_reg; + gate->bit_idx = mc->gate_shift; + gate->flags = CLK_GATE_SET_TO_DISABLE; + gate->lock = lock; + + gate_hw = &gate->hw; + gate_ops = &clk_gate_ops; + } + + if (mc->divider_shift >= 0) { + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) { + ret = -ENOMEM; + goto err_out; + } + + div->reg = base + mc->divider_reg; + div->shift = mc->divider_shift; + div->width = mc->divider_width; + div->lock = lock; + + div_hw = &div->hw; + div_ops = &clk_divider_ops; + } + + clk = clk_register_composite(NULL, mc->name, parent_names, num_parents, + mux_hw, mux_ops, + div_hw, div_ops, + gate_hw, gate_ops, + mc->flags); + + if (IS_ERR(clk)) { + kfree(gate); + kfree(mux); + } + + return clk; +err_out: + kfree(mux); + + return ERR_PTR(ret); +} diff --git a/drivers/clk/mediatek/clk-mtk.h b/drivers/clk/mediatek/clk-mtk.h new file mode 100644 index 0000000..5aaba81 --- /dev/null +++ b/drivers/clk/mediatek/clk-mtk.h @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao <jamesjj.liao@mediatek.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. + */ + +#ifndef __DRV_CLK_MTK_H +#define __DRV_CLK_MTK_H + +#include <linux/regmap.h> +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> + +#define MAX_MUX_GATE_BIT 31 +#define INVALID_MUX_GATE_BIT (MAX_MUX_GATE_BIT + 1) + +#define MHZ (1000 * 1000) + +struct mtk_fixed_factor { + int id; + const char *name; + const char *parent_name; + int mult; + int div; +}; + +#define FACTOR(_id, _name, _parent, _mult, _div) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .mult = _mult, \ + .div = _div, \ + } + +extern void mtk_clk_register_factors(const struct mtk_fixed_factor *clks, + int num, struct clk_onecell_data *clk_data); + +struct mtk_composite { + int id; + const char *name; + const char * const * parent_names; + const char *parent; + unsigned flags; + + uint32_t mux_reg; + uint32_t divider_reg; + uint32_t gate_reg; + + signed char mux_shift; + signed char mux_width; + signed char gate_shift; + + signed char divider_shift; + signed char divider_width; + + signed char num_parents; +}; + +#define MUX_GATE(_id, _name, _parents, _reg, _shift, _width, _gate) { \ + .id = _id, \ + .name = _name, \ + .mux_reg = _reg, \ + .mux_shift = _shift, \ + .mux_width = _width, \ + .gate_reg = _reg, \ + .gate_shift = _gate, \ + .divider_shift = -1, \ + .parent_names = _parents, \ + .num_parents = ARRAY_SIZE(_parents), \ + .flags = CLK_SET_RATE_PARENT, \ + } + +#define MUX(_id, _name, _parents, _reg, _shift, _width) { \ + .id = _id, \ + .name = _name, \ + .mux_reg = _reg, \ + .mux_shift = _shift, \ + .mux_width = _width, \ + .gate_shift = -1, \ + .divider_shift = -1, \ + .parent_names = _parents, \ + .num_parents = ARRAY_SIZE(_parents), \ + .flags = CLK_SET_RATE_PARENT, \ + } + +#define DIV_GATE(_id, _name, _parent, _gate_reg, _gate_shift, _div_reg, _div_width, _div_shift) { \ + .id = _id, \ + .parent = _parent, \ + .name = _name, \ + .divider_reg = _div_reg, \ + .divider_shift = _div_shift, \ + .divider_width = _div_width, \ + .gate_reg = _gate_reg, \ + .gate_shift = _gate_shift, \ + .mux_shift = -1, \ + .flags = 0, \ + } + +struct clk *mtk_clk_register_composite(const struct mtk_composite *mc, + void __iomem *base, spinlock_t *lock); + +struct mtk_gate_regs { + u32 sta_ofs; + u32 clr_ofs; + u32 set_ofs; +}; + +struct mtk_gate { + int id; + const char *name; + const char *parent_name; + const struct mtk_gate_regs *regs; + int shift; + const struct clk_ops *ops; +}; + +int mtk_clk_register_gates(struct device_node *node, const struct mtk_gate *clks, + int num, struct clk_onecell_data *clk_data); + +struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num); + +#define HAVE_RST_BAR BIT(0) + +struct mtk_pll_data { + int id; + const char *name; + uint32_t reg; + uint32_t pwr_reg; + uint32_t en_mask; + uint32_t pd_reg; + uint32_t tuner_reg; + int pd_shift; + unsigned int flags; + const struct clk_ops *ops; + u32 rst_bar_mask; + unsigned long fmax; + int pcwbits; + uint32_t pcw_reg; + int pcw_shift; +}; + +void __init mtk_clk_register_plls(struct device_node *node, + const struct mtk_pll_data *plls, int num_plls, + struct clk_onecell_data *clk_data); + +#endif /* __DRV_CLK_MTK_H */ diff --git a/drivers/clk/mediatek/clk-pll.c b/drivers/clk/mediatek/clk-pll.c new file mode 100644 index 0000000..3ef6b21 --- /dev/null +++ b/drivers/clk/mediatek/clk-pll.c @@ -0,0 +1,335 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao <jamesjj.liao@mediatek.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. + */ + +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/clkdev.h> +#include <linux/delay.h> + +#include "clk-mtk.h" + +#define REG_CON0 0 +#define REG_CON1 4 + +#define CON0_BASE_EN BIT(0) +#define CON0_PWR_ON BIT(0) +#define CON0_ISO_EN BIT(1) +#define CON0_PCW_CHG BIT(31) + +#define AUDPLL_TUNER_EN BIT(31) + +#define POSTDIV_MASK 0x7 +#define INTEGER_BITS 7 + +/* + * MediaTek PLLs are configured through their pcw value. The pcw value describes + * a divider in the PLL feedback loop which consists of 7 bits for the integer + * part and the remaining bits (if present) for the fractional part. Also they + * have a 3 bit power-of-two post divider. + */ + +struct mtk_clk_pll { + struct clk_hw hw; + void __iomem *base_addr; + void __iomem *pd_addr; + void __iomem *pwr_addr; + void __iomem *tuner_addr; + void __iomem *pcw_addr; + const struct mtk_pll_data *data; +}; + +static inline struct mtk_clk_pll *to_mtk_clk_pll(struct clk_hw *hw) +{ + return container_of(hw, struct mtk_clk_pll, hw); +} + +static int mtk_pll_is_prepared(struct clk_hw *hw) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + + return (readl(pll->base_addr + REG_CON0) & CON0_BASE_EN) != 0; +} + +static unsigned long __mtk_pll_recalc_rate(struct mtk_clk_pll *pll, u32 fin, + u32 pcw, int postdiv) +{ + int pcwbits = pll->data->pcwbits; + int pcwfbits; + u64 vco; + u8 c = 0; + + /* The fractional part of the PLL divider. */ + pcwfbits = pcwbits > INTEGER_BITS ? pcwbits - INTEGER_BITS : 0; + + vco = (u64)fin * pcw; + + if (pcwfbits && (vco & GENMASK(pcwfbits - 1, 0))) + c = 1; + + vco >>= pcwfbits; + + if (c) + vco++; + + return ((unsigned long)vco + postdiv - 1) / postdiv; +} + +static void mtk_pll_set_rate_regs(struct clk_hw *hw, u32 pcw, + int postdiv) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 con1, pd, val; + int pll_en; + + /* set postdiv */ + pd = readl(pll->pd_addr); + pd &= ~(POSTDIV_MASK << pll->data->pd_shift); + pd |= (ffs(postdiv) - 1) << pll->data->pd_shift; + writel(pd, pll->pd_addr); + + pll_en = readl(pll->base_addr + REG_CON0) & CON0_BASE_EN; + + /* set pcw */ + val = readl(pll->pcw_addr); + + val &= ~GENMASK(pll->data->pcw_shift + pll->data->pcwbits - 1, + pll->data->pcw_shift); + val |= pcw << pll->data->pcw_shift; + writel(val, pll->pcw_addr); + + con1 = readl(pll->base_addr + REG_CON1); + + if (pll_en) + con1 |= CON0_PCW_CHG; + + writel(con1, pll->base_addr + REG_CON1); + if (pll->tuner_addr) + writel(con1 + 1, pll->tuner_addr); + + if (pll_en) + usleep_range(20, 1000); +} + +/* + * mtk_pll_calc_values - calculate good values for a given input frequency. + * @pll: The pll + * @pcw: The pcw value (output) + * @postdiv: The post divider (output) + * @freq: The desired target frequency + * @fin: The input frequency + * + */ +static void mtk_pll_calc_values(struct mtk_clk_pll *pll, u32 *pcw, u32 *postdiv, + u32 freq, u32 fin) +{ + unsigned long fmin = 1000 * MHZ; + u64 _pcw; + u32 val; + + if (freq > pll->data->fmax) + freq = pll->data->fmax; + + for (val = 0; val < 4; val++) { + *postdiv = 1 << val; + if (freq * *postdiv >= fmin) + break; + } + + /* _pcw = freq * postdiv / fin * 2^pcwfbits */ + _pcw = ((u64)freq << val) << (pll->data->pcwbits - INTEGER_BITS); + do_div(_pcw, fin); + + *pcw = (u32)_pcw; +} + +static int mtk_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 pcw = 0; + u32 postdiv; + + mtk_pll_calc_values(pll, &pcw, &postdiv, rate, parent_rate); + mtk_pll_set_rate_regs(hw, pcw, postdiv); + + return 0; +} + +static unsigned long mtk_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 postdiv; + u32 pcw; + + postdiv = (readl(pll->pd_addr) >> pll->data->pd_shift) & POSTDIV_MASK; + postdiv = 1 << postdiv; + + pcw = readl(pll->pcw_addr) >> pll->data->pcw_shift; + pcw &= GENMASK(pll->data->pcwbits - 1, 0); + + return __mtk_pll_recalc_rate(pll, parent_rate, pcw, postdiv); +} + +static long mtk_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 pcw = 0; + int postdiv; + + mtk_pll_calc_values(pll, &pcw, &postdiv, rate, *prate); + + return __mtk_pll_recalc_rate(pll, *prate, pcw, postdiv); +} + +static int mtk_pll_prepare(struct clk_hw *hw) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 r; + + r = readl(pll->pwr_addr) | CON0_PWR_ON; + writel(r, pll->pwr_addr); + usleep_range(1, 100); + + r = readl(pll->pwr_addr) & ~CON0_ISO_EN; + writel(r, pll->pwr_addr); + usleep_range(1, 100); + + r = readl(pll->base_addr + REG_CON0); + r |= pll->data->en_mask; + writel(r, pll->base_addr + REG_CON0); + + if (pll->tuner_addr) { + r = readl(pll->tuner_addr) | AUDPLL_TUNER_EN; + writel(r, pll->tuner_addr); + } + + usleep_range(20, 1000); + + if (pll->data->flags & HAVE_RST_BAR) { + r = readl(pll->base_addr + REG_CON0); + r |= pll->data->rst_bar_mask; + writel(r, pll->base_addr + REG_CON0); + } + + return 0; +} + +static void mtk_pll_unprepare(struct clk_hw *hw) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 r; + + if (pll->data->flags & HAVE_RST_BAR) { + r = readl(pll->base_addr + REG_CON0); + r &= ~pll->data->rst_bar_mask; + writel(r, pll->base_addr + REG_CON0); + } + + if (pll->tuner_addr) { + r = readl(pll->tuner_addr) & ~AUDPLL_TUNER_EN; + writel(r, pll->tuner_addr); + } + + r = readl(pll->base_addr + REG_CON0); + r &= ~CON0_BASE_EN; + writel(r, pll->base_addr + REG_CON0); + + r = readl(pll->pwr_addr) | CON0_ISO_EN; + writel(r, pll->pwr_addr); + + r = readl(pll->pwr_addr) & ~CON0_PWR_ON; + writel(r, pll->pwr_addr); +} + +static const struct clk_ops mtk_pll_ops = { + .is_prepared = mtk_pll_is_prepared, + .prepare = mtk_pll_prepare, + .unprepare = mtk_pll_unprepare, + .recalc_rate = mtk_pll_recalc_rate, + .round_rate = mtk_pll_round_rate, + .set_rate = mtk_pll_set_rate, +}; + +static struct clk *mtk_clk_register_pll(const struct mtk_pll_data *data, + void __iomem *base) +{ + struct mtk_clk_pll *pll; + struct clk_init_data init; + struct clk *clk; + const char *parent_name = "clk26m"; + + pr_debug("%s(): name: %s\n", __func__, data->name); + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + pll->base_addr = base + data->reg; + pll->pwr_addr = base + data->pwr_reg; + pll->pd_addr = base + data->pd_reg; + pll->pcw_addr = base + data->pcw_reg; + if (data->tuner_reg) + pll->tuner_addr = base + data->tuner_reg; + pll->hw.init = &init; + pll->data = data; + + init.name = data->name; + init.ops = &mtk_pll_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + + clk = clk_register(NULL, &pll->hw); + + if (IS_ERR(clk)) + kfree(pll); + + return clk; +} + +void __init mtk_clk_register_plls(struct device_node *node, + const struct mtk_pll_data *plls, int num_plls, struct clk_onecell_data *clk_data) +{ + void __iomem *base; + int r, i; + struct clk *clk; + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s(): ioremap failed\n", __func__); + return; + } + + for (i = 0; i < num_plls; i++) { + const struct mtk_pll_data *pll = &plls[i]; + + clk = mtk_clk_register_pll(pll, base); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + pll->name, PTR_ERR(clk)); + continue; + } + + clk_data->clks[pll->id] = clk; + } + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} -- 2.1.4 ^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 2/6] clk: mediatek: Add initial common clock support for Mediatek SoCs. 2015-03-31 18:16 ` [PATCH 2/6] clk: mediatek: Add initial common clock support for Mediatek SoCs Sascha Hauer @ 2015-04-09 17:03 ` Matthias Brugger 0 siblings, 0 replies; 22+ messages in thread From: Matthias Brugger @ 2015-04-09 17:03 UTC (permalink / raw) To: linux-arm-kernel 2015-03-31 20:16 GMT+02:00 Sascha Hauer <s.hauer@pengutronix.de>: > From: James Liao <jamesjj.liao@mediatek.com> > > This patch adds common clock support for Mediatek SoCs, including plls, > muxes and clock gates. > > Signed-off-by: James Liao <jamesjj.liao@mediatek.com> > Signed-off-by: Henry Chen <henryc.chen@mediatek.com> > Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> > --- > drivers/clk/Makefile | 1 + > drivers/clk/mediatek/Makefile | 1 + > drivers/clk/mediatek/clk-gate.c | 137 ++++++++++++++++ > drivers/clk/mediatek/clk-gate.h | 49 ++++++ > drivers/clk/mediatek/clk-mtk.c | 197 +++++++++++++++++++++++ > drivers/clk/mediatek/clk-mtk.h | 155 +++++++++++++++++++ > drivers/clk/mediatek/clk-pll.c | 335 ++++++++++++++++++++++++++++++++++++++++ > 7 files changed, 875 insertions(+) > create mode 100644 drivers/clk/mediatek/Makefile > create mode 100644 drivers/clk/mediatek/clk-gate.c > create mode 100644 drivers/clk/mediatek/clk-gate.h > create mode 100644 drivers/clk/mediatek/clk-mtk.c > create mode 100644 drivers/clk/mediatek/clk-mtk.h > create mode 100644 drivers/clk/mediatek/clk-pll.c > > diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile > index d478ceb..6d97203 100644 > --- a/drivers/clk/Makefile > +++ b/drivers/clk/Makefile > @@ -49,6 +49,7 @@ obj-$(CONFIG_ARCH_HI3xxx) += hisilicon/ > obj-$(CONFIG_ARCH_HIP04) += hisilicon/ > obj-$(CONFIG_ARCH_HIX5HD2) += hisilicon/ > obj-$(CONFIG_COMMON_CLK_KEYSTONE) += keystone/ > +obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/ > ifeq ($(CONFIG_COMMON_CLK), y) > obj-$(CONFIG_ARCH_MMP) += mmp/ > endif > diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile > new file mode 100644 > index 0000000..c384e97 > --- /dev/null > +++ b/drivers/clk/mediatek/Makefile > @@ -0,0 +1 @@ > +obj-y += clk-mtk.o clk-pll.o clk-gate.o > diff --git a/drivers/clk/mediatek/clk-gate.c b/drivers/clk/mediatek/clk-gate.c > new file mode 100644 > index 0000000..9d77ee3 > --- /dev/null > +++ b/drivers/clk/mediatek/clk-gate.c > @@ -0,0 +1,137 @@ > +/* > + * Copyright (c) 2014 MediaTek Inc. > + * Author: James Liao <jamesjj.liao@mediatek.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. > + */ > + > +#include <linux/of.h> > +#include <linux/of_address.h> > + > +#include <linux/io.h> > +#include <linux/slab.h> > +#include <linux/delay.h> > +#include <linux/clkdev.h> > + > +#include "clk-mtk.h" > +#include "clk-gate.h" > + > +static int mtk_cg_bit_is_cleared(struct clk_hw *hw) > +{ > + struct mtk_clk_gate *cg = to_clk_gate(hw); > + u32 val; > + > + regmap_read(cg->regmap, cg->sta_ofs, &val); > + > + val &= BIT(cg->bit); > + > + return val == 0; > +} > + > +static int mtk_cg_bit_is_set(struct clk_hw *hw) > +{ > + struct mtk_clk_gate *cg = to_clk_gate(hw); > + u32 val; > + > + regmap_read(cg->regmap, cg->sta_ofs, &val); > + > + val &= BIT(cg->bit); > + > + return val != 0; > +} > + > +static void mtk_cg_set_bit(struct clk_hw *hw) > +{ > + struct mtk_clk_gate *cg = to_clk_gate(hw); > + > + regmap_write(cg->regmap, cg->set_ofs, BIT(cg->bit)); > +} > + > +static void mtk_cg_clr_bit(struct clk_hw *hw) > +{ > + struct mtk_clk_gate *cg = to_clk_gate(hw); > + > + regmap_write(cg->regmap, cg->clr_ofs, BIT(cg->bit)); > +} > + > +static int mtk_cg_enable(struct clk_hw *hw) > +{ > + mtk_cg_clr_bit(hw); > + > + return 0; > +} > + > +static void mtk_cg_disable(struct clk_hw *hw) > +{ > + mtk_cg_set_bit(hw); > +} > + > +static int mtk_cg_enable_inv(struct clk_hw *hw) > +{ > + mtk_cg_set_bit(hw); > + > + return 0; > +} > + > +static void mtk_cg_disable_inv(struct clk_hw *hw) > +{ > + mtk_cg_clr_bit(hw); > +} > + > +const struct clk_ops mtk_clk_gate_ops_setclr = { > + .is_enabled = mtk_cg_bit_is_cleared, > + .enable = mtk_cg_enable, > + .disable = mtk_cg_disable, > +}; > + > +const struct clk_ops mtk_clk_gate_ops_setclr_inv = { > + .is_enabled = mtk_cg_bit_is_set, > + .enable = mtk_cg_enable_inv, > + .disable = mtk_cg_disable_inv, > +}; > + > +struct clk *mtk_clk_register_gate( > + const char *name, > + const char *parent_name, > + struct regmap *regmap, > + int set_ofs, > + int clr_ofs, > + int sta_ofs, > + u8 bit, > + const struct clk_ops *ops) > +{ > + struct mtk_clk_gate *cg; > + struct clk *clk; > + struct clk_init_data init; > + > + cg = kzalloc(sizeof(*cg), GFP_KERNEL); > + if (!cg) > + return ERR_PTR(-ENOMEM); > + > + init.name = name; > + init.flags = CLK_SET_RATE_PARENT; > + init.parent_names = parent_name ? &parent_name : NULL; > + init.num_parents = parent_name ? 1 : 0; > + init.ops = ops; > + > + cg->regmap = regmap; > + cg->set_ofs = set_ofs; > + cg->clr_ofs = clr_ofs; > + cg->sta_ofs = sta_ofs; > + cg->bit = bit; > + > + cg->hw.init = &init; > + > + clk = clk_register(NULL, &cg->hw); > + if (IS_ERR(clk)) > + kfree(cg); > + > + return clk; > +} > diff --git a/drivers/clk/mediatek/clk-gate.h b/drivers/clk/mediatek/clk-gate.h > new file mode 100644 > index 0000000..6b6780b > --- /dev/null > +++ b/drivers/clk/mediatek/clk-gate.h > @@ -0,0 +1,49 @@ > +/* > + * Copyright (c) 2014 MediaTek Inc. > + * Author: James Liao <jamesjj.liao@mediatek.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. > + */ > + > +#ifndef __DRV_CLK_GATE_H > +#define __DRV_CLK_GATE_H > + > +#include <linux/regmap.h> > +#include <linux/clk.h> > +#include <linux/clk-provider.h> > + > +struct mtk_clk_gate { > + struct clk_hw hw; > + struct regmap *regmap; > + int set_ofs; > + int clr_ofs; > + int sta_ofs; > + u8 bit; > +}; > + > +static inline struct mtk_clk_gate *to_clk_gate(struct clk_hw *hw) > +{ > + return container_of(hw, struct mtk_clk_gate, hw); > +} > + > +extern const struct clk_ops mtk_clk_gate_ops_setclr; > +extern const struct clk_ops mtk_clk_gate_ops_setclr_inv; > + > +struct clk *mtk_clk_register_gate( > + const char *name, > + const char *parent_name, > + struct regmap *regmap, > + int set_ofs, > + int clr_ofs, > + int sta_ofs, > + u8 bit, > + const struct clk_ops *ops); > + > +#endif /* __DRV_CLK_GATE_H */ > diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c > new file mode 100644 > index 0000000..746bb34 > --- /dev/null > +++ b/drivers/clk/mediatek/clk-mtk.c > @@ -0,0 +1,197 @@ > +/* > + * Copyright (c) 2014 MediaTek Inc. > + * Author: James Liao <jamesjj.liao@mediatek.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. > + */ > + > +#include <linux/of.h> > +#include <linux/of_address.h> > +#include <linux/err.h> > +#include <linux/io.h> > +#include <linux/slab.h> > +#include <linux/delay.h> > +#include <linux/clkdev.h> > +#include <linux/mfd/syscon.h> > + > +#include "clk-mtk.h" > +#include "clk-gate.h" > + > +struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num) > +{ > + int i; > + struct clk_onecell_data *clk_data; > + > + clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); > + if (!clk_data) > + return NULL; > + > + clk_data->clks = kcalloc(clk_num, sizeof(*clk_data->clks), GFP_KERNEL); > + if (!clk_data->clks) > + goto err_out; > + > + clk_data->clk_num = clk_num; > + > + for (i = 0; i < clk_num; i++) > + clk_data->clks[i] = ERR_PTR(-ENOENT); > + > + return clk_data; > +err_out: > + kfree(clk_data); > + > + return NULL; > +} > + > +void mtk_clk_register_factors(const struct mtk_fixed_factor *clks, int num, > + struct clk_onecell_data *clk_data) > +{ > + int i; > + struct clk *clk; > + > + for (i = 0; i < num; i++) { > + const struct mtk_fixed_factor *ff = &clks[i]; > + > + clk = clk_register_fixed_factor(NULL, ff->name, ff->parent_name, > + CLK_SET_RATE_PARENT, ff->mult, ff->div); > + > + if (IS_ERR(clk)) { > + pr_err("Failed to register clk %s: %ld\n", > + ff->name, PTR_ERR(clk)); > + continue; > + } > + > + if (clk_data) > + clk_data->clks[ff->id] = clk; > + } > +} > + > +int mtk_clk_register_gates(struct device_node *node, const struct mtk_gate *clks, > + int num, struct clk_onecell_data *clk_data) > +{ > + int i; > + struct clk *clk; > + struct regmap *regmap; > + > + if (!clk_data) > + return -ENOMEM; > + > + regmap = syscon_node_to_regmap(node); > + if (IS_ERR(regmap)) { > + pr_err("Cannot find regmap for %s: %ld\n", node->full_name, > + PTR_ERR(regmap)); > + return PTR_ERR(regmap); > + } > + > + for (i = 0; i < num; i++) { > + const struct mtk_gate *gate = &clks[i]; > + > + clk = mtk_clk_register_gate(gate->name, gate->parent_name, > + regmap, > + gate->regs->set_ofs, > + gate->regs->clr_ofs, > + gate->regs->sta_ofs, > + gate->shift, gate->ops); > + > + if (IS_ERR(clk)) { > + pr_err("Failed to register clk %s: %ld\n", > + gate->name, PTR_ERR(clk)); > + continue; > + } > + > + clk_data->clks[gate->id] = clk; > + } > + > + return 0; > +} > + > +struct clk *mtk_clk_register_composite(const struct mtk_composite *mc, > + void __iomem *base, spinlock_t *lock) > +{ > + struct clk *clk; > + struct clk_mux *mux = NULL; > + struct clk_gate *gate = NULL; > + struct clk_divider *div = NULL; > + struct clk_hw *mux_hw = NULL, *gate_hw = NULL, *div_hw = NULL; > + const struct clk_ops *mux_ops = NULL, *gate_ops = NULL, *div_ops = NULL; > + const char * const *parent_names; > + const char *parent; > + int num_parents; > + int ret; > + > + if (mc->mux_shift >= 0) { > + mux = kzalloc(sizeof(*mux), GFP_KERNEL); > + if (!mux) > + return ERR_PTR(-ENOMEM); > + > + mux->reg = base + mc->mux_reg; > + mux->mask = BIT(mc->mux_width) - 1; > + mux->shift = mc->mux_shift; > + mux->lock = lock; > + > + mux_hw = &mux->hw; > + mux_ops = &clk_mux_ops; > + > + parent_names = mc->parent_names; > + num_parents = mc->num_parents; > + } else { > + parent = mc->parent; > + parent_names = &parent; > + num_parents = 1; > + } > + > + if (mc->gate_shift >= 0) { > + gate = kzalloc(sizeof(*gate), GFP_KERNEL); > + if (!gate) { > + ret = -ENOMEM; > + goto err_out; > + } > + > + gate->reg = base + mc->gate_reg; > + gate->bit_idx = mc->gate_shift; > + gate->flags = CLK_GATE_SET_TO_DISABLE; > + gate->lock = lock; > + > + gate_hw = &gate->hw; > + gate_ops = &clk_gate_ops; > + } > + > + if (mc->divider_shift >= 0) { > + div = kzalloc(sizeof(*div), GFP_KERNEL); > + if (!div) { > + ret = -ENOMEM; > + goto err_out; > + } > + > + div->reg = base + mc->divider_reg; > + div->shift = mc->divider_shift; > + div->width = mc->divider_width; > + div->lock = lock; > + > + div_hw = &div->hw; > + div_ops = &clk_divider_ops; > + } > + > + clk = clk_register_composite(NULL, mc->name, parent_names, num_parents, > + mux_hw, mux_ops, > + div_hw, div_ops, > + gate_hw, gate_ops, > + mc->flags); > + > + if (IS_ERR(clk)) { > + kfree(gate); > + kfree(mux); > + } > + > + return clk; > +err_out: > + kfree(mux); > + > + return ERR_PTR(ret); > +} > diff --git a/drivers/clk/mediatek/clk-mtk.h b/drivers/clk/mediatek/clk-mtk.h > new file mode 100644 > index 0000000..5aaba81 > --- /dev/null > +++ b/drivers/clk/mediatek/clk-mtk.h > @@ -0,0 +1,155 @@ > +/* > + * Copyright (c) 2014 MediaTek Inc. > + * Author: James Liao <jamesjj.liao@mediatek.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. > + */ > + > +#ifndef __DRV_CLK_MTK_H > +#define __DRV_CLK_MTK_H > + > +#include <linux/regmap.h> > +#include <linux/bitops.h> > +#include <linux/clk.h> > +#include <linux/clk-provider.h> > + > +#define MAX_MUX_GATE_BIT 31 > +#define INVALID_MUX_GATE_BIT (MAX_MUX_GATE_BIT + 1) > + > +#define MHZ (1000 * 1000) > + > +struct mtk_fixed_factor { > + int id; > + const char *name; > + const char *parent_name; > + int mult; > + int div; > +}; > + > +#define FACTOR(_id, _name, _parent, _mult, _div) { \ > + .id = _id, \ > + .name = _name, \ > + .parent_name = _parent, \ > + .mult = _mult, \ > + .div = _div, \ > + } > + > +extern void mtk_clk_register_factors(const struct mtk_fixed_factor *clks, > + int num, struct clk_onecell_data *clk_data); > + > +struct mtk_composite { > + int id; > + const char *name; > + const char * const * parent_names; > + const char *parent; > + unsigned flags; > + > + uint32_t mux_reg; > + uint32_t divider_reg; > + uint32_t gate_reg; > + > + signed char mux_shift; > + signed char mux_width; > + signed char gate_shift; > + > + signed char divider_shift; > + signed char divider_width; > + > + signed char num_parents; > +}; > + > +#define MUX_GATE(_id, _name, _parents, _reg, _shift, _width, _gate) { \ > + .id = _id, \ > + .name = _name, \ > + .mux_reg = _reg, \ > + .mux_shift = _shift, \ > + .mux_width = _width, \ > + .gate_reg = _reg, \ > + .gate_shift = _gate, \ > + .divider_shift = -1, \ > + .parent_names = _parents, \ > + .num_parents = ARRAY_SIZE(_parents), \ > + .flags = CLK_SET_RATE_PARENT, \ > + } > + > +#define MUX(_id, _name, _parents, _reg, _shift, _width) { \ > + .id = _id, \ > + .name = _name, \ > + .mux_reg = _reg, \ > + .mux_shift = _shift, \ > + .mux_width = _width, \ > + .gate_shift = -1, \ > + .divider_shift = -1, \ > + .parent_names = _parents, \ > + .num_parents = ARRAY_SIZE(_parents), \ > + .flags = CLK_SET_RATE_PARENT, \ > + } > + > +#define DIV_GATE(_id, _name, _parent, _gate_reg, _gate_shift, _div_reg, _div_width, _div_shift) { \ > + .id = _id, \ > + .parent = _parent, \ > + .name = _name, \ > + .divider_reg = _div_reg, \ > + .divider_shift = _div_shift, \ > + .divider_width = _div_width, \ > + .gate_reg = _gate_reg, \ > + .gate_shift = _gate_shift, \ > + .mux_shift = -1, \ > + .flags = 0, \ > + } > + > +struct clk *mtk_clk_register_composite(const struct mtk_composite *mc, > + void __iomem *base, spinlock_t *lock); > + > +struct mtk_gate_regs { > + u32 sta_ofs; > + u32 clr_ofs; > + u32 set_ofs; > +}; > + > +struct mtk_gate { > + int id; > + const char *name; > + const char *parent_name; > + const struct mtk_gate_regs *regs; > + int shift; > + const struct clk_ops *ops; > +}; > + > +int mtk_clk_register_gates(struct device_node *node, const struct mtk_gate *clks, > + int num, struct clk_onecell_data *clk_data); > + > +struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num); > + > +#define HAVE_RST_BAR BIT(0) > + > +struct mtk_pll_data { > + int id; > + const char *name; > + uint32_t reg; > + uint32_t pwr_reg; > + uint32_t en_mask; > + uint32_t pd_reg; > + uint32_t tuner_reg; > + int pd_shift; > + unsigned int flags; > + const struct clk_ops *ops; > + u32 rst_bar_mask; > + unsigned long fmax; > + int pcwbits; > + uint32_t pcw_reg; > + int pcw_shift; > +}; > + > +void __init mtk_clk_register_plls(struct device_node *node, > + const struct mtk_pll_data *plls, int num_plls, > + struct clk_onecell_data *clk_data); > + > +#endif /* __DRV_CLK_MTK_H */ > diff --git a/drivers/clk/mediatek/clk-pll.c b/drivers/clk/mediatek/clk-pll.c > new file mode 100644 > index 0000000..3ef6b21 > --- /dev/null > +++ b/drivers/clk/mediatek/clk-pll.c > @@ -0,0 +1,335 @@ > +/* > + * Copyright (c) 2014 MediaTek Inc. > + * Author: James Liao <jamesjj.liao@mediatek.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. > + */ > + > +#include <linux/of.h> > +#include <linux/of_address.h> > +#include <linux/io.h> > +#include <linux/slab.h> > +#include <linux/clkdev.h> > +#include <linux/delay.h> > + > +#include "clk-mtk.h" > + > +#define REG_CON0 0 > +#define REG_CON1 4 > + > +#define CON0_BASE_EN BIT(0) > +#define CON0_PWR_ON BIT(0) > +#define CON0_ISO_EN BIT(1) > +#define CON0_PCW_CHG BIT(31) > + > +#define AUDPLL_TUNER_EN BIT(31) > + > +#define POSTDIV_MASK 0x7 > +#define INTEGER_BITS 7 > + > +/* > + * MediaTek PLLs are configured through their pcw value. The pcw value describes > + * a divider in the PLL feedback loop which consists of 7 bits for the integer > + * part and the remaining bits (if present) for the fractional part. Also they > + * have a 3 bit power-of-two post divider. > + */ > + > +struct mtk_clk_pll { > + struct clk_hw hw; > + void __iomem *base_addr; > + void __iomem *pd_addr; > + void __iomem *pwr_addr; > + void __iomem *tuner_addr; > + void __iomem *pcw_addr; > + const struct mtk_pll_data *data; > +}; > + > +static inline struct mtk_clk_pll *to_mtk_clk_pll(struct clk_hw *hw) > +{ > + return container_of(hw, struct mtk_clk_pll, hw); > +} > + > +static int mtk_pll_is_prepared(struct clk_hw *hw) > +{ > + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); > + > + return (readl(pll->base_addr + REG_CON0) & CON0_BASE_EN) != 0; > +} > + > +static unsigned long __mtk_pll_recalc_rate(struct mtk_clk_pll *pll, u32 fin, > + u32 pcw, int postdiv) > +{ > + int pcwbits = pll->data->pcwbits; > + int pcwfbits; > + u64 vco; > + u8 c = 0; > + > + /* The fractional part of the PLL divider. */ > + pcwfbits = pcwbits > INTEGER_BITS ? pcwbits - INTEGER_BITS : 0; > + > + vco = (u64)fin * pcw; > + > + if (pcwfbits && (vco & GENMASK(pcwfbits - 1, 0))) > + c = 1; > + > + vco >>= pcwfbits; > + > + if (c) > + vco++; > + > + return ((unsigned long)vco + postdiv - 1) / postdiv; > +} > + > +static void mtk_pll_set_rate_regs(struct clk_hw *hw, u32 pcw, > + int postdiv) > +{ > + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); >From mtk_pll_set_rate we can pass a pointer to mtk_clk_pll instead of clk_hw. > + u32 con1, pd, val; > + int pll_en; > + > + /* set postdiv */ > + pd = readl(pll->pd_addr); > + pd &= ~(POSTDIV_MASK << pll->data->pd_shift); > + pd |= (ffs(postdiv) - 1) << pll->data->pd_shift; > + writel(pd, pll->pd_addr); > + > + pll_en = readl(pll->base_addr + REG_CON0) & CON0_BASE_EN; > + > + /* set pcw */ > + val = readl(pll->pcw_addr); > + > + val &= ~GENMASK(pll->data->pcw_shift + pll->data->pcwbits - 1, > + pll->data->pcw_shift); > + val |= pcw << pll->data->pcw_shift; > + writel(val, pll->pcw_addr); > + > + con1 = readl(pll->base_addr + REG_CON1); > + > + if (pll_en) > + con1 |= CON0_PCW_CHG; > + > + writel(con1, pll->base_addr + REG_CON1); > + if (pll->tuner_addr) > + writel(con1 + 1, pll->tuner_addr); > + > + if (pll_en) > + usleep_range(20, 1000); > +} > + > +/* > + * mtk_pll_calc_values - calculate good values for a given input frequency. > + * @pll: The pll > + * @pcw: The pcw value (output) > + * @postdiv: The post divider (output) > + * @freq: The desired target frequency > + * @fin: The input frequency > + * > + */ > +static void mtk_pll_calc_values(struct mtk_clk_pll *pll, u32 *pcw, u32 *postdiv, > + u32 freq, u32 fin) > +{ > + unsigned long fmin = 1000 * MHZ; > + u64 _pcw; > + u32 val; > + > + if (freq > pll->data->fmax) > + freq = pll->data->fmax; > + > + for (val = 0; val < 4; val++) { > + *postdiv = 1 << val; > + if (freq * *postdiv >= fmin) > + break; > + } > + > + /* _pcw = freq * postdiv / fin * 2^pcwfbits */ > + _pcw = ((u64)freq << val) << (pll->data->pcwbits - INTEGER_BITS); > + do_div(_pcw, fin); > + > + *pcw = (u32)_pcw; > +} > + > +static int mtk_pll_set_rate(struct clk_hw *hw, unsigned long rate, > + unsigned long parent_rate) > +{ > + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); > + u32 pcw = 0; > + u32 postdiv; > + > + mtk_pll_calc_values(pll, &pcw, &postdiv, rate, parent_rate); > + mtk_pll_set_rate_regs(hw, pcw, postdiv); > + > + return 0; > +} > + > +static unsigned long mtk_pll_recalc_rate(struct clk_hw *hw, > + unsigned long parent_rate) > +{ > + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); > + u32 postdiv; > + u32 pcw; > + > + postdiv = (readl(pll->pd_addr) >> pll->data->pd_shift) & POSTDIV_MASK; > + postdiv = 1 << postdiv; > + > + pcw = readl(pll->pcw_addr) >> pll->data->pcw_shift; > + pcw &= GENMASK(pll->data->pcwbits - 1, 0); > + > + return __mtk_pll_recalc_rate(pll, parent_rate, pcw, postdiv); > +} > + > +static long mtk_pll_round_rate(struct clk_hw *hw, unsigned long rate, > + unsigned long *prate) > +{ > + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); > + u32 pcw = 0; > + int postdiv; > + > + mtk_pll_calc_values(pll, &pcw, &postdiv, rate, *prate); > + > + return __mtk_pll_recalc_rate(pll, *prate, pcw, postdiv); > +} > + > +static int mtk_pll_prepare(struct clk_hw *hw) > +{ > + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); > + u32 r; > + > + r = readl(pll->pwr_addr) | CON0_PWR_ON; > + writel(r, pll->pwr_addr); > + usleep_range(1, 100); > + > + r = readl(pll->pwr_addr) & ~CON0_ISO_EN; > + writel(r, pll->pwr_addr); > + usleep_range(1, 100); > + > + r = readl(pll->base_addr + REG_CON0); > + r |= pll->data->en_mask; > + writel(r, pll->base_addr + REG_CON0); > + > + if (pll->tuner_addr) { > + r = readl(pll->tuner_addr) | AUDPLL_TUNER_EN; > + writel(r, pll->tuner_addr); > + } > + > + usleep_range(20, 1000); > + > + if (pll->data->flags & HAVE_RST_BAR) { > + r = readl(pll->base_addr + REG_CON0); > + r |= pll->data->rst_bar_mask; > + writel(r, pll->base_addr + REG_CON0); > + } > + > + return 0; > +} > + > +static void mtk_pll_unprepare(struct clk_hw *hw) > +{ > + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); > + u32 r; > + > + if (pll->data->flags & HAVE_RST_BAR) { > + r = readl(pll->base_addr + REG_CON0); > + r &= ~pll->data->rst_bar_mask; > + writel(r, pll->base_addr + REG_CON0); > + } > + > + if (pll->tuner_addr) { > + r = readl(pll->tuner_addr) & ~AUDPLL_TUNER_EN; > + writel(r, pll->tuner_addr); > + } > + > + r = readl(pll->base_addr + REG_CON0); > + r &= ~CON0_BASE_EN; > + writel(r, pll->base_addr + REG_CON0); > + > + r = readl(pll->pwr_addr) | CON0_ISO_EN; > + writel(r, pll->pwr_addr); > + > + r = readl(pll->pwr_addr) & ~CON0_PWR_ON; > + writel(r, pll->pwr_addr); > +} > + > +static const struct clk_ops mtk_pll_ops = { > + .is_prepared = mtk_pll_is_prepared, > + .prepare = mtk_pll_prepare, > + .unprepare = mtk_pll_unprepare, > + .recalc_rate = mtk_pll_recalc_rate, > + .round_rate = mtk_pll_round_rate, > + .set_rate = mtk_pll_set_rate, > +}; > + > +static struct clk *mtk_clk_register_pll(const struct mtk_pll_data *data, > + void __iomem *base) > +{ > + struct mtk_clk_pll *pll; > + struct clk_init_data init; > + struct clk *clk; > + const char *parent_name = "clk26m"; > + > + pr_debug("%s(): name: %s\n", __func__, data->name); Please please make this more descriptive or delete the debug message. > + > + pll = kzalloc(sizeof(*pll), GFP_KERNEL); > + if (!pll) > + return ERR_PTR(-ENOMEM); > + > + pll->base_addr = base + data->reg; > + pll->pwr_addr = base + data->pwr_reg; > + pll->pd_addr = base + data->pd_reg; > + pll->pcw_addr = base + data->pcw_reg; > + if (data->tuner_reg) > + pll->tuner_addr = base + data->tuner_reg; > + pll->hw.init = &init; > + pll->data = data; > + > + init.name = data->name; > + init.ops = &mtk_pll_ops; > + init.parent_names = &parent_name; > + init.num_parents = 1; > + > + clk = clk_register(NULL, &pll->hw); > + > + if (IS_ERR(clk)) > + kfree(pll); > + > + return clk; > +} > + > +void __init mtk_clk_register_plls(struct device_node *node, > + const struct mtk_pll_data *plls, int num_plls, struct clk_onecell_data *clk_data) > +{ > + void __iomem *base; > + int r, i; > + struct clk *clk; > + > + base = of_iomap(node, 0); > + if (!base) { > + pr_err("%s(): ioremap failed\n", __func__); > + return; > + } > + > + for (i = 0; i < num_plls; i++) { > + const struct mtk_pll_data *pll = &plls[i]; > + > + clk = mtk_clk_register_pll(pll, base); > + > + if (IS_ERR(clk)) { > + pr_err("Failed to register clk %s: %ld\n", > + pll->name, PTR_ERR(clk)); > + continue; > + } > + > + clk_data->clks[pll->id] = clk; > + } > + > + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); > + if (r) > + pr_err("%s(): could not register clock provider: %d\n", > + __func__, r); > +} > -- > 2.1.4 > -- motzblog.wordpress.com ^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v12] clk: Add common clock support for Mediatek MT8135 and MT8173 @ 2015-04-23 8:35 Sascha Hauer 2015-04-23 8:35 ` [PATCH 2/6] clk: mediatek: Add initial common clock support for Mediatek SoCs Sascha Hauer 0 siblings, 1 reply; 22+ messages in thread From: Sascha Hauer @ 2015-04-23 8:35 UTC (permalink / raw) To: linux-arm-kernel The following changes since commit 39a8804455fb23f09157341d3ba7db6d7ae6ee76: Linux 4.0 (2015-04-12 15:12:50 -0700) are available in the git repository at: git://git.pengutronix.de/git/sha/linux-2.6.git tags/v4.0-clk-mediatek-v12 for you to fetch changes up to e0ebeaa8a3f4a762cb9c2780170445aad15915d1: dt-bindings: ARM: Mediatek: Document devicetree bindings for clock/reset controllers (2015-04-23 10:22:34 +0200) ---------------------------------------------------------------- This patchset contains the initial common clock support for Mediatek SoCs. Mediatek SoC's clock architecture comprises of various PLLs, dividers, muxes and clock gates. Changes in v12: - Fix UART clocks: Add clocks providing the baudrate - enable necessary but unused clocks for surviving the disabling of unused clocks - cleanup clock define names: remove _CK suffix and consistently add a CLK_ prefix - add and use mtk_clk_register_composites() for registering multiple composite clocks Changes in v11: - Use more defines in PLL code - drop unused pr_fmt - use i++ instead of ++i in two places Changes in v10: - polish some commit messages Changes in v9: - rename 'lock' to 'mt81xx_clk_lock' to get better lockdep output Changes in v8: - add patch to allow to put parent_name arrays in __initconst - put parent_name arrays into __initconst Changes in v7: - fix duplicate definition/declaration of mtk_register_reset_controller - fix pd_reg offset of tvdpll - make clk initialization arrays const Changes in v6: - rework PLL support, only a fraction of original size now - Move binding docs to Documentation/devicetree/bindings/arm/mediatek since the units are not really clock specific (they contain reset controllers) Changes in v5: - Add reset controller support for pericfg/infracfg - Use regmap for the gates - remove now unnecessary spinlock for the gates - Add PMIC wrapper support as of v3 Changes in v4: - Support MT8173 platform. - Re-ordered patchset. driver/clk/Makefile in 2nd patch. - Extract the common part definition(mtk_gate/mtk_pll/mtk_mux) from clk-mt8135.c/clk-mt8173.c to clk-mtk.c. - Refine code. Rmove unnessacary debug information and unsed defines, add prefix "mtk_" for static functions. - Remove flag CLK_IGNORE_UNUSED and set flag CLK_SET_RATE_PARENT on gate/mux/fixed-factor. - Use spin_lock_irqsave(&clk_ops_lock, flags) instead of mtk_clk_lock. - Example above include a node for the clock controller itself, followed by the i2c controller example above. Changes in v3: - Rebase to 3.19-rc1. - Refine code. Remove unneed functions, debug logs and comments, and fine tune error logs. Changes in v2: - Re-ordered patchset. Fold include/dt-bindings and DT document in 1st patch. ---------------------------------------------------------------- James Liao (3): clk: mediatek: Add initial common clock support for Mediatek SoCs. clk: mediatek: Add basic clocks for Mediatek MT8135. clk: mediatek: Add basic clocks for Mediatek MT8173. Sascha Hauer (3): clk: make strings in parent name arrays const clk: mediatek: Add reset controller support dt-bindings: ARM: Mediatek: Document devicetree bindings for clock/reset controllers .../bindings/arm/mediatek/mediatek,apmixedsys.txt | 23 + .../bindings/arm/mediatek/mediatek,infracfg.txt | 30 + .../bindings/arm/mediatek/mediatek,pericfg.txt | 30 + .../bindings/arm/mediatek/mediatek,topckgen.txt | 23 + drivers/clk/Makefile | 1 + drivers/clk/clk-composite.c | 2 +- drivers/clk/clk-mux.c | 4 +- drivers/clk/mediatek/Makefile | 4 + drivers/clk/mediatek/clk-gate.c | 137 ++++ drivers/clk/mediatek/clk-gate.h | 49 ++ drivers/clk/mediatek/clk-mt8135.c | 644 ++++++++++++++++ drivers/clk/mediatek/clk-mt8173.c | 830 +++++++++++++++++++++ drivers/clk/mediatek/clk-mtk.c | 220 ++++++ drivers/clk/mediatek/clk-mtk.h | 169 +++++ drivers/clk/mediatek/clk-pll.c | 332 +++++++++ drivers/clk/mediatek/reset.c | 97 +++ include/dt-bindings/clock/mt8135-clk.h | 194 +++++ include/dt-bindings/clock/mt8173-clk.h | 235 ++++++ .../dt-bindings/reset-controller/mt8135-resets.h | 64 ++ .../dt-bindings/reset-controller/mt8173-resets.h | 63 ++ include/linux/clk-provider.h | 8 +- 21 files changed, 3152 insertions(+), 7 deletions(-) create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt create mode 100644 drivers/clk/mediatek/Makefile create mode 100644 drivers/clk/mediatek/clk-gate.c create mode 100644 drivers/clk/mediatek/clk-gate.h create mode 100644 drivers/clk/mediatek/clk-mt8135.c create mode 100644 drivers/clk/mediatek/clk-mt8173.c create mode 100644 drivers/clk/mediatek/clk-mtk.c create mode 100644 drivers/clk/mediatek/clk-mtk.h create mode 100644 drivers/clk/mediatek/clk-pll.c create mode 100644 drivers/clk/mediatek/reset.c create mode 100644 include/dt-bindings/clock/mt8135-clk.h create mode 100644 include/dt-bindings/clock/mt8173-clk.h create mode 100644 include/dt-bindings/reset-controller/mt8135-resets.h create mode 100644 include/dt-bindings/reset-controller/mt8173-resets.h ^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 2/6] clk: mediatek: Add initial common clock support for Mediatek SoCs. 2015-04-23 8:35 [PATCH v12] clk: Add common clock support for Mediatek MT8135 and MT8173 Sascha Hauer @ 2015-04-23 8:35 ` Sascha Hauer 0 siblings, 0 replies; 22+ messages in thread From: Sascha Hauer @ 2015-04-23 8:35 UTC (permalink / raw) To: linux-arm-kernel From: James Liao <jamesjj.liao@mediatek.com> This patch adds common clock support for Mediatek SoCs, including plls, muxes and clock gates. Signed-off-by: James Liao <jamesjj.liao@mediatek.com> Signed-off-by: Henry Chen <henryc.chen@mediatek.com> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> --- drivers/clk/Makefile | 1 + drivers/clk/mediatek/Makefile | 1 + drivers/clk/mediatek/clk-gate.c | 137 +++++++++++++++++ drivers/clk/mediatek/clk-gate.h | 49 ++++++ drivers/clk/mediatek/clk-mtk.c | 220 ++++++++++++++++++++++++++ drivers/clk/mediatek/clk-mtk.h | 159 +++++++++++++++++++ drivers/clk/mediatek/clk-pll.c | 332 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 899 insertions(+) create mode 100644 drivers/clk/mediatek/Makefile create mode 100644 drivers/clk/mediatek/clk-gate.c create mode 100644 drivers/clk/mediatek/clk-gate.h create mode 100644 drivers/clk/mediatek/clk-mtk.c create mode 100644 drivers/clk/mediatek/clk-mtk.h create mode 100644 drivers/clk/mediatek/clk-pll.c diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index d478ceb..6d97203 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -49,6 +49,7 @@ obj-$(CONFIG_ARCH_HI3xxx) += hisilicon/ obj-$(CONFIG_ARCH_HIP04) += hisilicon/ obj-$(CONFIG_ARCH_HIX5HD2) += hisilicon/ obj-$(CONFIG_COMMON_CLK_KEYSTONE) += keystone/ +obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/ ifeq ($(CONFIG_COMMON_CLK), y) obj-$(CONFIG_ARCH_MMP) += mmp/ endif diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile new file mode 100644 index 0000000..c384e97 --- /dev/null +++ b/drivers/clk/mediatek/Makefile @@ -0,0 +1 @@ +obj-y += clk-mtk.o clk-pll.o clk-gate.o diff --git a/drivers/clk/mediatek/clk-gate.c b/drivers/clk/mediatek/clk-gate.c new file mode 100644 index 0000000..9d77ee3 --- /dev/null +++ b/drivers/clk/mediatek/clk-gate.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao <jamesjj.liao@mediatek.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. + */ + +#include <linux/of.h> +#include <linux/of_address.h> + +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/clkdev.h> + +#include "clk-mtk.h" +#include "clk-gate.h" + +static int mtk_cg_bit_is_cleared(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_clk_gate(hw); + u32 val; + + regmap_read(cg->regmap, cg->sta_ofs, &val); + + val &= BIT(cg->bit); + + return val == 0; +} + +static int mtk_cg_bit_is_set(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_clk_gate(hw); + u32 val; + + regmap_read(cg->regmap, cg->sta_ofs, &val); + + val &= BIT(cg->bit); + + return val != 0; +} + +static void mtk_cg_set_bit(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_clk_gate(hw); + + regmap_write(cg->regmap, cg->set_ofs, BIT(cg->bit)); +} + +static void mtk_cg_clr_bit(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_clk_gate(hw); + + regmap_write(cg->regmap, cg->clr_ofs, BIT(cg->bit)); +} + +static int mtk_cg_enable(struct clk_hw *hw) +{ + mtk_cg_clr_bit(hw); + + return 0; +} + +static void mtk_cg_disable(struct clk_hw *hw) +{ + mtk_cg_set_bit(hw); +} + +static int mtk_cg_enable_inv(struct clk_hw *hw) +{ + mtk_cg_set_bit(hw); + + return 0; +} + +static void mtk_cg_disable_inv(struct clk_hw *hw) +{ + mtk_cg_clr_bit(hw); +} + +const struct clk_ops mtk_clk_gate_ops_setclr = { + .is_enabled = mtk_cg_bit_is_cleared, + .enable = mtk_cg_enable, + .disable = mtk_cg_disable, +}; + +const struct clk_ops mtk_clk_gate_ops_setclr_inv = { + .is_enabled = mtk_cg_bit_is_set, + .enable = mtk_cg_enable_inv, + .disable = mtk_cg_disable_inv, +}; + +struct clk *mtk_clk_register_gate( + const char *name, + const char *parent_name, + struct regmap *regmap, + int set_ofs, + int clr_ofs, + int sta_ofs, + u8 bit, + const struct clk_ops *ops) +{ + struct mtk_clk_gate *cg; + struct clk *clk; + struct clk_init_data init; + + cg = kzalloc(sizeof(*cg), GFP_KERNEL); + if (!cg) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + init.ops = ops; + + cg->regmap = regmap; + cg->set_ofs = set_ofs; + cg->clr_ofs = clr_ofs; + cg->sta_ofs = sta_ofs; + cg->bit = bit; + + cg->hw.init = &init; + + clk = clk_register(NULL, &cg->hw); + if (IS_ERR(clk)) + kfree(cg); + + return clk; +} diff --git a/drivers/clk/mediatek/clk-gate.h b/drivers/clk/mediatek/clk-gate.h new file mode 100644 index 0000000..6b6780b --- /dev/null +++ b/drivers/clk/mediatek/clk-gate.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao <jamesjj.liao@mediatek.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. + */ + +#ifndef __DRV_CLK_GATE_H +#define __DRV_CLK_GATE_H + +#include <linux/regmap.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> + +struct mtk_clk_gate { + struct clk_hw hw; + struct regmap *regmap; + int set_ofs; + int clr_ofs; + int sta_ofs; + u8 bit; +}; + +static inline struct mtk_clk_gate *to_clk_gate(struct clk_hw *hw) +{ + return container_of(hw, struct mtk_clk_gate, hw); +} + +extern const struct clk_ops mtk_clk_gate_ops_setclr; +extern const struct clk_ops mtk_clk_gate_ops_setclr_inv; + +struct clk *mtk_clk_register_gate( + const char *name, + const char *parent_name, + struct regmap *regmap, + int set_ofs, + int clr_ofs, + int sta_ofs, + u8 bit, + const struct clk_ops *ops); + +#endif /* __DRV_CLK_GATE_H */ diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c new file mode 100644 index 0000000..18444ae --- /dev/null +++ b/drivers/clk/mediatek/clk-mtk.c @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao <jamesjj.liao@mediatek.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. + */ + +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/clkdev.h> +#include <linux/mfd/syscon.h> + +#include "clk-mtk.h" +#include "clk-gate.h" + +struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num) +{ + int i; + struct clk_onecell_data *clk_data; + + clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); + if (!clk_data) + return NULL; + + clk_data->clks = kcalloc(clk_num, sizeof(*clk_data->clks), GFP_KERNEL); + if (!clk_data->clks) + goto err_out; + + clk_data->clk_num = clk_num; + + for (i = 0; i < clk_num; i++) + clk_data->clks[i] = ERR_PTR(-ENOENT); + + return clk_data; +err_out: + kfree(clk_data); + + return NULL; +} + +void mtk_clk_register_factors(const struct mtk_fixed_factor *clks, int num, + struct clk_onecell_data *clk_data) +{ + int i; + struct clk *clk; + + for (i = 0; i < num; i++) { + const struct mtk_fixed_factor *ff = &clks[i]; + + clk = clk_register_fixed_factor(NULL, ff->name, ff->parent_name, + CLK_SET_RATE_PARENT, ff->mult, ff->div); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + ff->name, PTR_ERR(clk)); + continue; + } + + if (clk_data) + clk_data->clks[ff->id] = clk; + } +} + +int mtk_clk_register_gates(struct device_node *node, const struct mtk_gate *clks, + int num, struct clk_onecell_data *clk_data) +{ + int i; + struct clk *clk; + struct regmap *regmap; + + if (!clk_data) + return -ENOMEM; + + regmap = syscon_node_to_regmap(node); + if (IS_ERR(regmap)) { + pr_err("Cannot find regmap for %s: %ld\n", node->full_name, + PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + for (i = 0; i < num; i++) { + const struct mtk_gate *gate = &clks[i]; + + clk = mtk_clk_register_gate(gate->name, gate->parent_name, + regmap, + gate->regs->set_ofs, + gate->regs->clr_ofs, + gate->regs->sta_ofs, + gate->shift, gate->ops); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + gate->name, PTR_ERR(clk)); + continue; + } + + clk_data->clks[gate->id] = clk; + } + + return 0; +} + +struct clk *mtk_clk_register_composite(const struct mtk_composite *mc, + void __iomem *base, spinlock_t *lock) +{ + struct clk *clk; + struct clk_mux *mux = NULL; + struct clk_gate *gate = NULL; + struct clk_divider *div = NULL; + struct clk_hw *mux_hw = NULL, *gate_hw = NULL, *div_hw = NULL; + const struct clk_ops *mux_ops = NULL, *gate_ops = NULL, *div_ops = NULL; + const char * const *parent_names; + const char *parent; + int num_parents; + int ret; + + if (mc->mux_shift >= 0) { + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) + return ERR_PTR(-ENOMEM); + + mux->reg = base + mc->mux_reg; + mux->mask = BIT(mc->mux_width) - 1; + mux->shift = mc->mux_shift; + mux->lock = lock; + + mux_hw = &mux->hw; + mux_ops = &clk_mux_ops; + + parent_names = mc->parent_names; + num_parents = mc->num_parents; + } else { + parent = mc->parent; + parent_names = &parent; + num_parents = 1; + } + + if (mc->gate_shift >= 0) { + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) { + ret = -ENOMEM; + goto err_out; + } + + gate->reg = base + mc->gate_reg; + gate->bit_idx = mc->gate_shift; + gate->flags = CLK_GATE_SET_TO_DISABLE; + gate->lock = lock; + + gate_hw = &gate->hw; + gate_ops = &clk_gate_ops; + } + + if (mc->divider_shift >= 0) { + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) { + ret = -ENOMEM; + goto err_out; + } + + div->reg = base + mc->divider_reg; + div->shift = mc->divider_shift; + div->width = mc->divider_width; + div->lock = lock; + + div_hw = &div->hw; + div_ops = &clk_divider_ops; + } + + clk = clk_register_composite(NULL, mc->name, parent_names, num_parents, + mux_hw, mux_ops, + div_hw, div_ops, + gate_hw, gate_ops, + mc->flags); + + if (IS_ERR(clk)) { + kfree(gate); + kfree(mux); + } + + return clk; +err_out: + kfree(mux); + + return ERR_PTR(ret); +} + +void mtk_clk_register_composites(const struct mtk_composite *mcs, + int num, void __iomem *base, spinlock_t *lock, + struct clk_onecell_data *clk_data) +{ + struct clk *clk; + int i; + + for (i = 0; i < num; i++) { + const struct mtk_composite *mc = &mcs[i]; + + clk = mtk_clk_register_composite(mc, base, lock); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + mc->name, PTR_ERR(clk)); + continue; + } + + if (clk_data) + clk_data->clks[mc->id] = clk; + } +} diff --git a/drivers/clk/mediatek/clk-mtk.h b/drivers/clk/mediatek/clk-mtk.h new file mode 100644 index 0000000..694fc39 --- /dev/null +++ b/drivers/clk/mediatek/clk-mtk.h @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao <jamesjj.liao@mediatek.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. + */ + +#ifndef __DRV_CLK_MTK_H +#define __DRV_CLK_MTK_H + +#include <linux/regmap.h> +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> + +#define MAX_MUX_GATE_BIT 31 +#define INVALID_MUX_GATE_BIT (MAX_MUX_GATE_BIT + 1) + +#define MHZ (1000 * 1000) + +struct mtk_fixed_factor { + int id; + const char *name; + const char *parent_name; + int mult; + int div; +}; + +#define FACTOR(_id, _name, _parent, _mult, _div) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .mult = _mult, \ + .div = _div, \ + } + +extern void mtk_clk_register_factors(const struct mtk_fixed_factor *clks, + int num, struct clk_onecell_data *clk_data); + +struct mtk_composite { + int id; + const char *name; + const char * const * parent_names; + const char *parent; + unsigned flags; + + uint32_t mux_reg; + uint32_t divider_reg; + uint32_t gate_reg; + + signed char mux_shift; + signed char mux_width; + signed char gate_shift; + + signed char divider_shift; + signed char divider_width; + + signed char num_parents; +}; + +#define MUX_GATE(_id, _name, _parents, _reg, _shift, _width, _gate) { \ + .id = _id, \ + .name = _name, \ + .mux_reg = _reg, \ + .mux_shift = _shift, \ + .mux_width = _width, \ + .gate_reg = _reg, \ + .gate_shift = _gate, \ + .divider_shift = -1, \ + .parent_names = _parents, \ + .num_parents = ARRAY_SIZE(_parents), \ + .flags = CLK_SET_RATE_PARENT, \ + } + +#define MUX(_id, _name, _parents, _reg, _shift, _width) { \ + .id = _id, \ + .name = _name, \ + .mux_reg = _reg, \ + .mux_shift = _shift, \ + .mux_width = _width, \ + .gate_shift = -1, \ + .divider_shift = -1, \ + .parent_names = _parents, \ + .num_parents = ARRAY_SIZE(_parents), \ + .flags = CLK_SET_RATE_PARENT, \ + } + +#define DIV_GATE(_id, _name, _parent, _gate_reg, _gate_shift, _div_reg, _div_width, _div_shift) { \ + .id = _id, \ + .parent = _parent, \ + .name = _name, \ + .divider_reg = _div_reg, \ + .divider_shift = _div_shift, \ + .divider_width = _div_width, \ + .gate_reg = _gate_reg, \ + .gate_shift = _gate_shift, \ + .mux_shift = -1, \ + .flags = 0, \ + } + +struct clk *mtk_clk_register_composite(const struct mtk_composite *mc, + void __iomem *base, spinlock_t *lock); + +void mtk_clk_register_composites(const struct mtk_composite *mcs, + int num, void __iomem *base, spinlock_t *lock, + struct clk_onecell_data *clk_data); + +struct mtk_gate_regs { + u32 sta_ofs; + u32 clr_ofs; + u32 set_ofs; +}; + +struct mtk_gate { + int id; + const char *name; + const char *parent_name; + const struct mtk_gate_regs *regs; + int shift; + const struct clk_ops *ops; +}; + +int mtk_clk_register_gates(struct device_node *node, const struct mtk_gate *clks, + int num, struct clk_onecell_data *clk_data); + +struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num); + +#define HAVE_RST_BAR BIT(0) + +struct mtk_pll_data { + int id; + const char *name; + uint32_t reg; + uint32_t pwr_reg; + uint32_t en_mask; + uint32_t pd_reg; + uint32_t tuner_reg; + int pd_shift; + unsigned int flags; + const struct clk_ops *ops; + u32 rst_bar_mask; + unsigned long fmax; + int pcwbits; + uint32_t pcw_reg; + int pcw_shift; +}; + +void __init mtk_clk_register_plls(struct device_node *node, + const struct mtk_pll_data *plls, int num_plls, + struct clk_onecell_data *clk_data); + +#endif /* __DRV_CLK_MTK_H */ diff --git a/drivers/clk/mediatek/clk-pll.c b/drivers/clk/mediatek/clk-pll.c new file mode 100644 index 0000000..66154ca --- /dev/null +++ b/drivers/clk/mediatek/clk-pll.c @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao <jamesjj.liao@mediatek.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. + */ + +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/clkdev.h> +#include <linux/delay.h> + +#include "clk-mtk.h" + +#define REG_CON0 0 +#define REG_CON1 4 + +#define CON0_BASE_EN BIT(0) +#define CON0_PWR_ON BIT(0) +#define CON0_ISO_EN BIT(1) +#define CON0_PCW_CHG BIT(31) + +#define AUDPLL_TUNER_EN BIT(31) + +#define POSTDIV_MASK 0x7 +#define INTEGER_BITS 7 + +/* + * MediaTek PLLs are configured through their pcw value. The pcw value describes + * a divider in the PLL feedback loop which consists of 7 bits for the integer + * part and the remaining bits (if present) for the fractional part. Also they + * have a 3 bit power-of-two post divider. + */ + +struct mtk_clk_pll { + struct clk_hw hw; + void __iomem *base_addr; + void __iomem *pd_addr; + void __iomem *pwr_addr; + void __iomem *tuner_addr; + void __iomem *pcw_addr; + const struct mtk_pll_data *data; +}; + +static inline struct mtk_clk_pll *to_mtk_clk_pll(struct clk_hw *hw) +{ + return container_of(hw, struct mtk_clk_pll, hw); +} + +static int mtk_pll_is_prepared(struct clk_hw *hw) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + + return (readl(pll->base_addr + REG_CON0) & CON0_BASE_EN) != 0; +} + +static unsigned long __mtk_pll_recalc_rate(struct mtk_clk_pll *pll, u32 fin, + u32 pcw, int postdiv) +{ + int pcwbits = pll->data->pcwbits; + int pcwfbits; + u64 vco; + u8 c = 0; + + /* The fractional part of the PLL divider. */ + pcwfbits = pcwbits > INTEGER_BITS ? pcwbits - INTEGER_BITS : 0; + + vco = (u64)fin * pcw; + + if (pcwfbits && (vco & GENMASK(pcwfbits - 1, 0))) + c = 1; + + vco >>= pcwfbits; + + if (c) + vco++; + + return ((unsigned long)vco + postdiv - 1) / postdiv; +} + +static void mtk_pll_set_rate_regs(struct mtk_clk_pll *pll, u32 pcw, + int postdiv) +{ + u32 con1, pd, val; + int pll_en; + + /* set postdiv */ + pd = readl(pll->pd_addr); + pd &= ~(POSTDIV_MASK << pll->data->pd_shift); + pd |= (ffs(postdiv) - 1) << pll->data->pd_shift; + writel(pd, pll->pd_addr); + + pll_en = readl(pll->base_addr + REG_CON0) & CON0_BASE_EN; + + /* set pcw */ + val = readl(pll->pcw_addr); + + val &= ~GENMASK(pll->data->pcw_shift + pll->data->pcwbits - 1, + pll->data->pcw_shift); + val |= pcw << pll->data->pcw_shift; + writel(val, pll->pcw_addr); + + con1 = readl(pll->base_addr + REG_CON1); + + if (pll_en) + con1 |= CON0_PCW_CHG; + + writel(con1, pll->base_addr + REG_CON1); + if (pll->tuner_addr) + writel(con1 + 1, pll->tuner_addr); + + if (pll_en) + udelay(20); +} + +/* + * mtk_pll_calc_values - calculate good values for a given input frequency. + * @pll: The pll + * @pcw: The pcw value (output) + * @postdiv: The post divider (output) + * @freq: The desired target frequency + * @fin: The input frequency + * + */ +static void mtk_pll_calc_values(struct mtk_clk_pll *pll, u32 *pcw, u32 *postdiv, + u32 freq, u32 fin) +{ + unsigned long fmin = 1000 * MHZ; + u64 _pcw; + u32 val; + + if (freq > pll->data->fmax) + freq = pll->data->fmax; + + for (val = 0; val < 4; val++) { + *postdiv = 1 << val; + if (freq * *postdiv >= fmin) + break; + } + + /* _pcw = freq * postdiv / fin * 2^pcwfbits */ + _pcw = ((u64)freq << val) << (pll->data->pcwbits - INTEGER_BITS); + do_div(_pcw, fin); + + *pcw = (u32)_pcw; +} + +static int mtk_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 pcw = 0; + u32 postdiv; + + mtk_pll_calc_values(pll, &pcw, &postdiv, rate, parent_rate); + mtk_pll_set_rate_regs(pll, pcw, postdiv); + + return 0; +} + +static unsigned long mtk_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 postdiv; + u32 pcw; + + postdiv = (readl(pll->pd_addr) >> pll->data->pd_shift) & POSTDIV_MASK; + postdiv = 1 << postdiv; + + pcw = readl(pll->pcw_addr) >> pll->data->pcw_shift; + pcw &= GENMASK(pll->data->pcwbits - 1, 0); + + return __mtk_pll_recalc_rate(pll, parent_rate, pcw, postdiv); +} + +static long mtk_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 pcw = 0; + int postdiv; + + mtk_pll_calc_values(pll, &pcw, &postdiv, rate, *prate); + + return __mtk_pll_recalc_rate(pll, *prate, pcw, postdiv); +} + +static int mtk_pll_prepare(struct clk_hw *hw) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 r; + + r = readl(pll->pwr_addr) | CON0_PWR_ON; + writel(r, pll->pwr_addr); + udelay(1); + + r = readl(pll->pwr_addr) & ~CON0_ISO_EN; + writel(r, pll->pwr_addr); + udelay(1); + + r = readl(pll->base_addr + REG_CON0); + r |= pll->data->en_mask; + writel(r, pll->base_addr + REG_CON0); + + if (pll->tuner_addr) { + r = readl(pll->tuner_addr) | AUDPLL_TUNER_EN; + writel(r, pll->tuner_addr); + } + + udelay(20); + + if (pll->data->flags & HAVE_RST_BAR) { + r = readl(pll->base_addr + REG_CON0); + r |= pll->data->rst_bar_mask; + writel(r, pll->base_addr + REG_CON0); + } + + return 0; +} + +static void mtk_pll_unprepare(struct clk_hw *hw) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 r; + + if (pll->data->flags & HAVE_RST_BAR) { + r = readl(pll->base_addr + REG_CON0); + r &= ~pll->data->rst_bar_mask; + writel(r, pll->base_addr + REG_CON0); + } + + if (pll->tuner_addr) { + r = readl(pll->tuner_addr) & ~AUDPLL_TUNER_EN; + writel(r, pll->tuner_addr); + } + + r = readl(pll->base_addr + REG_CON0); + r &= ~CON0_BASE_EN; + writel(r, pll->base_addr + REG_CON0); + + r = readl(pll->pwr_addr) | CON0_ISO_EN; + writel(r, pll->pwr_addr); + + r = readl(pll->pwr_addr) & ~CON0_PWR_ON; + writel(r, pll->pwr_addr); +} + +static const struct clk_ops mtk_pll_ops = { + .is_prepared = mtk_pll_is_prepared, + .prepare = mtk_pll_prepare, + .unprepare = mtk_pll_unprepare, + .recalc_rate = mtk_pll_recalc_rate, + .round_rate = mtk_pll_round_rate, + .set_rate = mtk_pll_set_rate, +}; + +static struct clk *mtk_clk_register_pll(const struct mtk_pll_data *data, + void __iomem *base) +{ + struct mtk_clk_pll *pll; + struct clk_init_data init; + struct clk *clk; + const char *parent_name = "clk26m"; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + pll->base_addr = base + data->reg; + pll->pwr_addr = base + data->pwr_reg; + pll->pd_addr = base + data->pd_reg; + pll->pcw_addr = base + data->pcw_reg; + if (data->tuner_reg) + pll->tuner_addr = base + data->tuner_reg; + pll->hw.init = &init; + pll->data = data; + + init.name = data->name; + init.ops = &mtk_pll_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + + clk = clk_register(NULL, &pll->hw); + + if (IS_ERR(clk)) + kfree(pll); + + return clk; +} + +void __init mtk_clk_register_plls(struct device_node *node, + const struct mtk_pll_data *plls, int num_plls, struct clk_onecell_data *clk_data) +{ + void __iomem *base; + int r, i; + struct clk *clk; + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s(): ioremap failed\n", __func__); + return; + } + + for (i = 0; i < num_plls; i++) { + const struct mtk_pll_data *pll = &plls[i]; + + clk = mtk_clk_register_pll(pll, base); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + pll->name, PTR_ERR(clk)); + continue; + } + + clk_data->clks[pll->id] = clk; + } + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} -- 2.1.4 ^ permalink raw reply related [flat|nested] 22+ messages in thread
end of thread, other threads:[~2015-04-23 8:35 UTC | newest] Thread overview: 22+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2015-03-19 8:42 [PATCH v8]: clk: Add common clock support for Mediatek MT8135 and MT8173 Sascha Hauer 2015-03-19 8:42 ` [PATCH 1/6] clk: make strings in parent name arrays const Sascha Hauer 2015-03-19 8:48 ` Uwe Kleine-König 2015-03-19 8:42 ` [PATCH 2/6] clk: mediatek: Add initial common clock support for Mediatek SoCs Sascha Hauer 2015-03-19 8:42 ` [PATCH 3/6] clk: mediatek: Add reset controller support Sascha Hauer 2015-03-27 13:32 ` Matthias Brugger 2015-03-19 8:42 ` [PATCH 4/6] clk: mediatek: Add basic clocks for Mediatek MT8135 Sascha Hauer 2015-03-27 7:39 ` Stephen Boyd 2015-03-27 9:21 ` Sascha Hauer 2015-03-19 8:42 ` [PATCH 5/6] clk: mediatek: Add basic clocks for Mediatek MT8173 Sascha Hauer 2015-03-19 8:42 ` [PATCH 6/6] dt-bindings: ARM: Mediatek: Document devicetree bindings for clock/reset controllers Sascha Hauer 2015-03-27 13:32 ` Matthias Brugger 2015-03-27 6:05 ` [PATCH v8]: clk: Add common clock support for Mediatek MT8135 and MT8173 Sascha Hauer 2015-03-27 8:02 ` Stephen Boyd -- strict thread matches above, loose matches on Subject: below -- 2015-03-27 9:18 [PATCH v9]: " Sascha Hauer 2015-03-27 9:18 ` [PATCH 2/6] clk: mediatek: Add initial common clock support for Mediatek SoCs Sascha Hauer 2015-03-30 17:40 [PATCH v10]: clk: Add common clock support for Mediatek MT8135 and MT8173 Sascha Hauer 2015-03-30 17:40 ` [PATCH 2/6] clk: mediatek: Add initial common clock support for Mediatek SoCs Sascha Hauer 2015-03-30 17:55 ` Joe Perches 2015-03-30 18:31 ` Sascha Hauer 2015-03-31 1:21 ` Michael Turquette 2015-03-31 18:16 [PATCH v11]: clk: Add common clock support for Mediatek MT8135 and MT8173 Sascha Hauer 2015-03-31 18:16 ` [PATCH 2/6] clk: mediatek: Add initial common clock support for Mediatek SoCs Sascha Hauer 2015-04-09 17:03 ` Matthias Brugger 2015-04-23 8:35 [PATCH v12] clk: Add common clock support for Mediatek MT8135 and MT8173 Sascha Hauer 2015-04-23 8:35 ` [PATCH 2/6] clk: mediatek: Add initial common clock support for Mediatek SoCs Sascha Hauer
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).