* [PATCH 0/4] Add common clock support for Mediatek MT8135. @ 2014-11-28 11:34 ` James Liao 0 siblings, 0 replies; 15+ messages in thread From: James Liao @ 2014-11-28 11:34 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. This patchset also contains a basic clock support for Mediatek MT8135. This driver is based on 3.18-rc1 + MT8135 basic support. James Liao (4): clk: Add initial common clock support for Mediatek SoC MT8135. clk: Add INFRA and PERI clocks for Mediatek MT8135. clk: dts: document Mediatek MT8135 clock binding dts: Enable clock support for Mediatek MT8135. .../bindings/clock/mediatek,mt8135-clock.txt | 36 + arch/arm/boot/dts/mt8135.dtsi | 47 + drivers/clk/Makefile | 1 + drivers/clk/mediatek/Makefile | 2 + drivers/clk/mediatek/clk-gate.c | 151 +++ drivers/clk/mediatek/clk-gate.h | 48 + drivers/clk/mediatek/clk-mt8135-pll.c | 891 +++++++++++++++++ drivers/clk/mediatek/clk-mt8135-pll.h | 28 + drivers/clk/mediatek/clk-mt8135.c | 1012 ++++++++++++++++++++ drivers/clk/mediatek/clk-mtk.c | 93 ++ drivers/clk/mediatek/clk-mtk.h | 47 + drivers/clk/mediatek/clk-pll.c | 63 ++ drivers/clk/mediatek/clk-pll.h | 50 + include/dt-bindings/clock/mt8135-clk.h | 190 ++++ 14 files changed, 2659 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/mediatek,mt8135-clock.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-pll.c create mode 100644 drivers/clk/mediatek/clk-mt8135-pll.h create mode 100644 drivers/clk/mediatek/clk-mt8135.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/clk-pll.h create mode 100644 include/dt-bindings/clock/mt8135-clk.h -- 1.8.1.1.dirty ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 0/4] Add common clock support for Mediatek MT8135. @ 2014-11-28 11:34 ` James Liao 0 siblings, 0 replies; 15+ messages in thread From: James Liao @ 2014-11-28 11:34 UTC (permalink / raw) To: Rob Herring, Matthias Brugger, Mike Turquette Cc: Mark Rutland, Ashwin Chaugule, srv_jamesjj.liao, Russell King, srv_heupstream, Pawel Moll, Ian Campbell, Catalin Marinas, linux-kernel, devicetree, Vladimir Murzin, Sascha Hauer, Kumar Gala, Joe.C, eddie.huang, 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. This patchset also contains a basic clock support for Mediatek MT8135. This driver is based on 3.18-rc1 + MT8135 basic support. James Liao (4): clk: Add initial common clock support for Mediatek SoC MT8135. clk: Add INFRA and PERI clocks for Mediatek MT8135. clk: dts: document Mediatek MT8135 clock binding dts: Enable clock support for Mediatek MT8135. .../bindings/clock/mediatek,mt8135-clock.txt | 36 + arch/arm/boot/dts/mt8135.dtsi | 47 + drivers/clk/Makefile | 1 + drivers/clk/mediatek/Makefile | 2 + drivers/clk/mediatek/clk-gate.c | 151 +++ drivers/clk/mediatek/clk-gate.h | 48 + drivers/clk/mediatek/clk-mt8135-pll.c | 891 +++++++++++++++++ drivers/clk/mediatek/clk-mt8135-pll.h | 28 + drivers/clk/mediatek/clk-mt8135.c | 1012 ++++++++++++++++++++ drivers/clk/mediatek/clk-mtk.c | 93 ++ drivers/clk/mediatek/clk-mtk.h | 47 + drivers/clk/mediatek/clk-pll.c | 63 ++ drivers/clk/mediatek/clk-pll.h | 50 + include/dt-bindings/clock/mt8135-clk.h | 190 ++++ 14 files changed, 2659 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/mediatek,mt8135-clock.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-pll.c create mode 100644 drivers/clk/mediatek/clk-mt8135-pll.h create mode 100644 drivers/clk/mediatek/clk-mt8135.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/clk-pll.h create mode 100644 include/dt-bindings/clock/mt8135-clk.h -- 1.8.1.1.dirty ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 1/4] clk: Add initial common clock support for Mediatek SoC MT8135. 2014-11-28 11:34 ` James Liao @ 2014-11-28 11:34 ` James Liao -1 siblings, 0 replies; 15+ messages in thread From: James Liao @ 2014-11-28 11:34 UTC (permalink / raw) To: linux-arm-kernel This patch adds common clock support for Mediatek SoCs, and basic clocks such as PLLs and TOPCKGEN clocks for MT8135. Signed-off-by: James Liao <jamesjj.liao@mediatek.com> --- drivers/clk/Makefile | 1 + drivers/clk/mediatek/Makefile | 2 + drivers/clk/mediatek/clk-mt8135-pll.c | 891 +++++++++++++++++++++++++++++++++ drivers/clk/mediatek/clk-mt8135-pll.h | 28 ++ drivers/clk/mediatek/clk-mt8135.c | 752 ++++++++++++++++++++++++++++ drivers/clk/mediatek/clk-mtk.c | 93 ++++ drivers/clk/mediatek/clk-mtk.h | 47 ++ drivers/clk/mediatek/clk-pll.c | 63 +++ drivers/clk/mediatek/clk-pll.h | 50 ++ include/dt-bindings/clock/mt8135-clk.h | 128 +++++ 10 files changed, 2055 insertions(+) create mode 100644 drivers/clk/mediatek/Makefile create mode 100644 drivers/clk/mediatek/clk-mt8135-pll.c create mode 100644 drivers/clk/mediatek/clk-mt8135-pll.h create mode 100644 drivers/clk/mediatek/clk-mt8135.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/clk-pll.h create mode 100644 include/dt-bindings/clock/mt8135-clk.h diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index d5fba5b..ce6c250 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -47,6 +47,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..3c3432b --- /dev/null +++ b/drivers/clk/mediatek/Makefile @@ -0,0 +1,2 @@ +obj-y += clk-mtk.o clk-pll.o +obj-y += clk-mt8135.o clk-mt8135-pll.o diff --git a/drivers/clk/mediatek/clk-mt8135-pll.c b/drivers/clk/mediatek/clk-mt8135-pll.c new file mode 100644 index 0000000..999f640 --- /dev/null +++ b/drivers/clk/mediatek/clk-mt8135-pll.c @@ -0,0 +1,891 @@ +/* + * 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/io.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/clkdev.h> + +#include "clk-mtk.h" +#include "clk-pll.h" +#include "clk-mt8135-pll.h" + +/* + * clk_pll + */ + +#define PLL_BASE_EN BIT(0) +#define PLL_PWR_ON BIT(0) +#define PLL_ISO_EN BIT(1) +#define PLL_PCW_CHG BIT(31) +#define RST_BAR_MASK BIT(27) +#define AUDPLL_TUNER_EN BIT(31) + +#define PLL_PREDIV_H 5 +#define PLL_PREDIV_L 4 +#define PLL_PREDIV_MASK GENMASK(PLL_PREDIV_H, PLL_PREDIV_L) +#define PLL_VCODIV_L 19 +#define PLL_VCODIV_MASK BIT(19) + +static const u32 pll_vcodivsel_map[2] = { 1, 2 }; +static const u32 pll_prediv_map[4] = { 1, 2, 4, 4 }; +static const u32 pll_posdiv_map[8] = { 1, 2, 4, 8, 16, 16, 16, 16 }; +static const u32 pll_fbksel_map[4] = { 1, 2, 4, 4 }; + +static u32 calc_pll_vco_freq( + u32 fin, + u32 pcw, + u32 vcodivsel, + u32 prediv, + u32 pcwfbits) +{ + /* vco = (fin * pcw * vcodivsel / prediv) >> pcwfbits; */ + u64 vco = fin; + u8 c = 0; + + vco = vco * pcw * vcodivsel; + do_div(vco, prediv); + + if (vco & GENMASK(pcwfbits - 1, 0)) + c = 1; + vco >>= pcwfbits; + + if (c) + vco++; + + return (u32)vco; +} + +static u32 freq_limit(u32 freq) +{ + static const u32 freq_max = 2000 * 1000 * 1000; /* 2000 MHz */ + static const u32 freq_min = 1000 * 1000 * 1000 / 16; /* 1000 MHz */ + + if (freq <= freq_min) + freq = freq_min + 16; + else if (freq > freq_max) + freq = freq_max; + + return freq; +} + +static int calc_pll_freq_cfg( + u32 *pcw, + u32 *postdiv_idx, + u32 freq, + u32 fin, + int pcwfbits) +{ + static const u64 freq_max = 2000 * 1000 * 1000; /* 2000 MHz */ + static const u64 freq_min = 1000 * 1000 * 1000; /* 1000 MHz */ + static const u64 postdiv[] = { 1, 2, 4, 8, 16 }; + u64 n_info; + u32 idx; + + pr_debug("freq: %u\n", freq); + + /* search suitable postdiv */ + for (idx = 0; + idx < ARRAY_SIZE(postdiv) && postdiv[idx] * freq <= freq_min; + idx++) + ; + + if (idx >= ARRAY_SIZE(postdiv)) + return -EINVAL; /* freq is out of range (too low) */ + else if (postdiv[idx] * freq > freq_max) + return -EINVAL; /* freq is out of range (too high) */ + + /* n_info = freq * postdiv / 26MHz * 2^pcwfbits */ + n_info = (postdiv[idx] * freq) << pcwfbits; + do_div(n_info, fin); + + *postdiv_idx = idx; + *pcw = (u32)n_info; + + return 0; +} + +static int clk_pll_is_enabled(struct clk_hw *hw) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + + int r = (readl_relaxed(pll->base_addr) & PLL_BASE_EN) != 0; + + pr_debug("%d: %s\n", r, __clk_get_name(hw->clk)); + + return r; +} + +static int clk_pll_prepare(struct clk_hw *hw) +{ + unsigned long flags = 0; + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 r; + + pr_debug("%s\n", __clk_get_name(hw->clk)); + + mtk_clk_lock(flags); + + r = readl_relaxed(pll->pwr_addr) | PLL_PWR_ON; + writel_relaxed(r, pll->pwr_addr); + dsb(); + udelay(1); + + r = readl_relaxed(pll->pwr_addr) & ~PLL_ISO_EN; + writel_relaxed(r , pll->pwr_addr); + dsb(); + udelay(1); + + r = readl_relaxed(pll->base_addr) | pll->en_mask; + writel_relaxed(r, pll->base_addr); + dsb(); + udelay(20); + + if (pll->flags & HAVE_RST_BAR) { + r = readl_relaxed(pll->base_addr) | RST_BAR_MASK; + writel_relaxed(r, pll->base_addr); + } + + mtk_clk_unlock(flags); + + return 0; +} + +static void clk_pll_unprepare(struct clk_hw *hw) +{ + unsigned long flags = 0; + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 r; + + pr_debug("%s: PLL_AO: %d\n", + __clk_get_name(hw->clk), !!(pll->flags & PLL_AO)); + + if (pll->flags & PLL_AO) + return; + + mtk_clk_lock(flags); + + if (pll->flags & HAVE_RST_BAR) { + r = readl_relaxed(pll->base_addr) & ~RST_BAR_MASK; + writel_relaxed(r, pll->base_addr); + } + + r = readl_relaxed(pll->base_addr) & ~PLL_BASE_EN; + writel_relaxed(r, pll->base_addr); + + r = readl_relaxed(pll->pwr_addr) | PLL_ISO_EN; + writel_relaxed(r, pll->pwr_addr); + + r = readl_relaxed(pll->pwr_addr) & ~PLL_PWR_ON; + writel_relaxed(r, pll->pwr_addr); + + mtk_clk_unlock(flags); +} + +#define SDM_PLL_POSTDIV_H 8 +#define SDM_PLL_POSTDIV_L 6 +#define SDM_PLL_POSTDIV_MASK GENMASK(SDM_PLL_POSTDIV_H, SDM_PLL_POSTDIV_L) +#define SDM_PLL_PCW_H 20 +#define SDM_PLL_PCW_L 0 +#define SDM_PLL_PCW_MASK GENMASK(SDM_PLL_PCW_H, SDM_PLL_PCW_L) + +static unsigned long clk_pll_recalc_rate( + struct clk_hw *hw, + unsigned long parent_rate) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + + u32 con0 = readl_relaxed(pll->base_addr); + u32 con1 = readl_relaxed(pll->base_addr + 4); + + u32 vcodivsel = (con0 & PLL_VCODIV_MASK) >> PLL_VCODIV_L; + u32 prediv = (con0 & PLL_PREDIV_MASK) >> PLL_PREDIV_L; + u32 posdiv = (con0 & SDM_PLL_POSTDIV_MASK) >> SDM_PLL_POSTDIV_L; + u32 pcw = (con1 & SDM_PLL_PCW_MASK) >> SDM_PLL_PCW_L; + u32 pcwfbits = 14; + + u32 vco_freq; + unsigned long r; + + parent_rate = parent_rate ? parent_rate : 26000000; + vcodivsel = pll_vcodivsel_map[vcodivsel]; + prediv = pll_prediv_map[prediv]; + + vco_freq = calc_pll_vco_freq( + parent_rate, pcw, vcodivsel, prediv, pcwfbits); + r = vco_freq / pll_posdiv_map[posdiv]; + + pr_debug("%lu: %s\n", r, __clk_get_name(hw->clk)); + + return r; +} + +static long clk_pll_round_rate( + struct clk_hw *hw, + unsigned long rate, + unsigned long *prate) +{ + int r; + u32 pcwfbits = 14; + u32 pcw = 0; + u32 postdiv = 0; + + pr_debug("%s, rate: %lu\n", __clk_get_name(hw->clk), rate); + + *prate = *prate ? *prate : 26000000; + rate = freq_limit(rate); + calc_pll_freq_cfg(&pcw, &postdiv, rate, *prate, pcwfbits); + + r = calc_pll_vco_freq(*prate, pcw, 1, 1, pcwfbits); + r = r / pll_posdiv_map[postdiv]; + return r; +} + +static void clk_pll_set_rate_regs( + struct clk_hw *hw, + u32 pcw, + u32 postdiv_idx) +{ + unsigned long flags = 0; + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + void __iomem *con0_addr = pll->base_addr; + void __iomem *con1_addr = pll->base_addr + 4; + u32 con0; + u32 con1; + u32 pll_en; + + mtk_clk_lock(flags); + + con0 = readl_relaxed(con0_addr); + con1 = readl_relaxed(con1_addr); + + pll_en = con0 & PLL_BASE_EN; + + /* set postdiv */ + con0 &= ~SDM_PLL_POSTDIV_MASK; + con0 |= postdiv_idx << SDM_PLL_POSTDIV_L; + writel_relaxed(con0, con0_addr); + + /* set pcw */ + con1 &= ~SDM_PLL_PCW_MASK; + con1 |= pcw << SDM_PLL_PCW_L; + + if (pll_en) + con1 |= PLL_PCW_CHG; + + writel_relaxed(con1, con1_addr); + + if (pll_en) { + dsb(); + udelay(20); + } + + mtk_clk_unlock(flags); +} + +static int clk_pll_set_rate( + struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + u32 pcwfbits = 14; + u32 pcw = 0; + u32 postdiv_idx = 0; + int r; + + parent_rate = parent_rate ? parent_rate : 26000000; + r = calc_pll_freq_cfg(&pcw, &postdiv_idx, rate, parent_rate, pcwfbits); + + pr_debug("%s, rate: %lu, pcw: %u, postdiv_idx: %u\n", + __clk_get_name(hw->clk), rate, pcw, postdiv_idx); + + if (r == 0) + clk_pll_set_rate_regs(hw, pcw, postdiv_idx); + + return r; +} + +const struct clk_ops mtk_clk_pll_ops = { + .is_enabled = clk_pll_is_enabled, + .prepare = clk_pll_prepare, + .unprepare = clk_pll_unprepare, + .recalc_rate = clk_pll_recalc_rate, + .round_rate = clk_pll_round_rate, + .set_rate = clk_pll_set_rate, +}; + +#define ARM_PLL_POSTDIV_H 26 +#define ARM_PLL_POSTDIV_L 24 +#define ARM_PLL_POSTDIV_MASK GENMASK(ARM_PLL_POSTDIV_H, ARM_PLL_POSTDIV_L) +#define ARM_PLL_PCW_H 20 +#define ARM_PLL_PCW_L 0 +#define ARM_PLL_PCW_MASK GENMASK(ARM_PLL_PCW_H, ARM_PLL_PCW_L) + +static unsigned long clk_arm_pll_recalc_rate( + struct clk_hw *hw, + unsigned long parent_rate) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + + u32 con0 = readl_relaxed(pll->base_addr); + u32 con1 = readl_relaxed(pll->base_addr + 4); + + u32 vcodivsel = (con0 & PLL_VCODIV_MASK) >> PLL_VCODIV_L; + u32 prediv = (con0 & PLL_PREDIV_MASK) >> PLL_PREDIV_L; + u32 posdiv = (con1 & ARM_PLL_POSTDIV_MASK) >> ARM_PLL_POSTDIV_L; + u32 pcw = (con1 & ARM_PLL_PCW_MASK) >> ARM_PLL_PCW_L; + u32 pcwfbits = 14; + + u32 vco_freq; + unsigned long r; + + parent_rate = parent_rate ? parent_rate : 26000000; + vcodivsel = pll_vcodivsel_map[vcodivsel]; + prediv = pll_prediv_map[prediv]; + + vco_freq = calc_pll_vco_freq( + parent_rate, pcw, vcodivsel, prediv, pcwfbits); + r = vco_freq / pll_posdiv_map[posdiv]; + + pr_debug("%lu: %s\n", r, __clk_get_name(hw->clk)); + + return r; +} + +static void clk_arm_pll_set_rate_regs( + struct clk_hw *hw, + u32 pcw, + u32 postdiv_idx) + +{ + unsigned long flags = 0; + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + void __iomem *con0_addr = pll->base_addr; + void __iomem *con1_addr = pll->base_addr + 4; + u32 con0; + u32 con1; + u32 pll_en; + + mtk_clk_lock(flags); + + con0 = readl_relaxed(con0_addr); + con1 = readl_relaxed(con1_addr); + + pll_en = con0 & PLL_BASE_EN; + + /* postdiv */ + con1 &= ~ARM_PLL_POSTDIV_MASK; + con1 |= postdiv_idx << ARM_PLL_POSTDIV_L; + + /* pcw */ + con1 &= ~ARM_PLL_PCW_MASK; + con1 |= pcw << ARM_PLL_PCW_L; + + if (pll_en) + con1 |= PLL_PCW_CHG; + + writel_relaxed(con1, con1_addr); + + if (pll_en) { + dsb(); + udelay(20); + } + + mtk_clk_unlock(flags); +} + +static int clk_arm_pll_set_rate( + struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + u32 pcwfbits = 14; + u32 pcw = 0; + u32 postdiv_idx = 0; + int r; + + parent_rate = parent_rate ? parent_rate : 26000000; + r = calc_pll_freq_cfg(&pcw, &postdiv_idx, rate, parent_rate, pcwfbits); + + pr_debug("%s, rate: %lu, pcw: %u, postdiv_idx: %u\n", + __clk_get_name(hw->clk), rate, pcw, postdiv_idx); + + if (r == 0) + clk_arm_pll_set_rate_regs(hw, pcw, postdiv_idx); + + return r; +} + +const struct clk_ops mtk_clk_arm_pll_ops = { + .is_enabled = clk_pll_is_enabled, + .prepare = clk_pll_prepare, + .unprepare = clk_pll_unprepare, + .recalc_rate = clk_arm_pll_recalc_rate, + .round_rate = clk_pll_round_rate, + .set_rate = clk_arm_pll_set_rate, +}; + +static int clk_lc_pll_prepare(struct clk_hw *hw) +{ + unsigned long flags = 0; + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 r; + + pr_debug("%s\n", __clk_get_name(hw->clk)); + + mtk_clk_lock(flags); + + r = readl_relaxed(pll->base_addr) | pll->en_mask; + writel_relaxed(r, pll->base_addr); + dsb(); + udelay(20); + + if (pll->flags & HAVE_RST_BAR) { + r = readl_relaxed(pll->base_addr) | RST_BAR_MASK; + writel_relaxed(r, pll->base_addr); + } + + mtk_clk_unlock(flags); + + return 0; +} + +static void clk_lc_pll_unprepare(struct clk_hw *hw) +{ + unsigned long flags = 0; + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 r; + + pr_debug("%s\n", __clk_get_name(hw->clk)); + + mtk_clk_lock(flags); + + if (pll->flags & HAVE_RST_BAR) { + r = readl_relaxed(pll->base_addr) & ~RST_BAR_MASK; + writel_relaxed(r, pll->base_addr); + } + + r = readl_relaxed(pll->base_addr) & ~PLL_BASE_EN; + writel_relaxed(r, pll->base_addr); + + mtk_clk_unlock(flags); +} + +#define LC_PLL_FBKSEL_H 21 +#define LC_PLL_FBKSEL_L 20 +#define LC_PLL_FBKSEL_MASK GENMASK(LC_PLL_FBKSEL_H, LC_PLL_FBKSEL_L) +#define LC_PLL_POSTDIV_H 8 +#define LC_PLL_POSTDIV_L 6 +#define LC_PLL_POSTDIV_MASK GENMASK(LC_PLL_POSTDIV_H, LC_PLL_POSTDIV_L) +#define LC_PLL_FBKDIV_H 15 +#define LC_PLL_FBKDIV_L 9 +#define LC_PLL_FBKDIV_MASK GENMASK(LC_PLL_FBKDIV_H, LC_PLL_FBKDIV_L) + +static unsigned long clk_lc_pll_recalc_rate( + struct clk_hw *hw, + unsigned long parent_rate) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + + u32 con0 = readl_relaxed(pll->base_addr); + + u32 fbksel = (con0 & LC_PLL_FBKSEL_MASK) >> LC_PLL_FBKSEL_L; + u32 vcodivsel = (con0 & PLL_VCODIV_MASK) >> PLL_VCODIV_L; + u32 fbkdiv = (con0 & LC_PLL_FBKDIV_MASK) >> LC_PLL_FBKDIV_L; + u32 prediv = (con0 & PLL_PREDIV_MASK) >> PLL_PREDIV_L; + u32 posdiv = (con0 & LC_PLL_POSTDIV_MASK) >> LC_PLL_POSTDIV_L; + + u32 vco_freq; + unsigned long r; + + parent_rate = parent_rate ? parent_rate : 26000000; + vcodivsel = pll_vcodivsel_map[vcodivsel]; + fbksel = pll_fbksel_map[fbksel]; + prediv = pll_prediv_map[prediv]; + + vco_freq = parent_rate * fbkdiv * fbksel * vcodivsel / prediv; + r = vco_freq / pll_posdiv_map[posdiv]; + + pr_debug("%lu: %s\n", r, __clk_get_name(hw->clk)); + + return r; +} + +static long clk_lc_pll_round_rate( + struct clk_hw *hw, + unsigned long rate, + unsigned long *prate) +{ + int r; + u32 pcwfbits = 0; + u32 pcw = 0; + u32 postdiv = 0; + + pr_debug("%s, rate: %lu\n", __clk_get_name(hw->clk), rate); + + *prate = *prate ? *prate : 26000000; + rate = freq_limit(rate); + calc_pll_freq_cfg(&pcw, &postdiv, rate, *prate, pcwfbits); + + r = calc_pll_vco_freq(*prate, pcw, 1, 1, pcwfbits); + r = r / pll_posdiv_map[postdiv]; + return r; +} + +static void clk_lc_pll_set_rate_regs( + struct clk_hw *hw, + u32 pcw, + u32 postdiv_idx) +{ + unsigned long flags = 0; + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + void __iomem *con0_addr = pll->base_addr; + u32 con0; + u32 pll_en; + + mtk_clk_lock(flags); + + con0 = readl_relaxed(con0_addr); + + pll_en = con0 & PLL_BASE_EN; + + /* postdiv */ + con0 &= ~LC_PLL_POSTDIV_MASK; + con0 |= postdiv_idx << LC_PLL_POSTDIV_L; + + /* fkbdiv */ + con0 &= ~LC_PLL_FBKDIV_MASK; + con0 |= pcw << LC_PLL_FBKDIV_L; + + writel_relaxed(con0, con0_addr); + + if (pll_en) { + dsb(); + udelay(20); + } + mtk_clk_unlock(flags); +} + +static int clk_lc_pll_set_rate( + struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + u32 pcwfbits = 0; + u32 pcw = 0; + u32 postdiv_idx = 0; + int r; + + parent_rate = parent_rate ? parent_rate : 26000000; + r = calc_pll_freq_cfg(&pcw, &postdiv_idx, rate, parent_rate, pcwfbits); + + pr_debug("%s, rate: %lu, pcw: %u, postdiv_idx: %u\n", + __clk_get_name(hw->clk), rate, pcw, postdiv_idx); + + if (r == 0) + clk_lc_pll_set_rate_regs(hw, pcw, postdiv_idx); + + return r; +} + +const struct clk_ops mtk_clk_lc_pll_ops = { + .is_enabled = clk_pll_is_enabled, + .prepare = clk_lc_pll_prepare, + .unprepare = clk_lc_pll_unprepare, + .recalc_rate = clk_lc_pll_recalc_rate, + .round_rate = clk_lc_pll_round_rate, + .set_rate = clk_lc_pll_set_rate, +}; + +static int clk_aud_pll_prepare(struct clk_hw *hw) +{ + unsigned long flags = 0; + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 r; + + pr_debug("%s\n", __clk_get_name(hw->clk)); + + mtk_clk_lock(flags); + + r = readl_relaxed(pll->pwr_addr) | PLL_PWR_ON; + writel_relaxed(r, pll->pwr_addr); + dsb(); + udelay(1); + + r = readl_relaxed(pll->pwr_addr) & ~PLL_ISO_EN; + writel_relaxed(r, pll->pwr_addr); + dsb(); + udelay(1); + + r = readl_relaxed(pll->base_addr) | pll->en_mask; + writel_relaxed(r, pll->base_addr); + + r = readl_relaxed(pll->base_addr + 16) | AUDPLL_TUNER_EN; + writel_relaxed(r, pll->base_addr + 16); + dsb(); + udelay(20); + + if (pll->flags & HAVE_RST_BAR) { + r = readl_relaxed(pll->base_addr) | RST_BAR_MASK; + writel_relaxed(r, pll->base_addr); + } + + mtk_clk_unlock(flags); + + return 0; +} + +static void clk_aud_pll_unprepare(struct clk_hw *hw) +{ + unsigned long flags = 0; + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 r; + + pr_debug("%s\n", __clk_get_name(hw->clk)); + + mtk_clk_lock(flags); + + if (pll->flags & HAVE_RST_BAR) { + r = readl_relaxed(pll->base_addr) & ~RST_BAR_MASK; + writel_relaxed(r, pll->base_addr); + } + r = readl_relaxed(pll->base_addr + 16) & ~AUDPLL_TUNER_EN; + writel_relaxed(r, pll->base_addr + 16); + + r = readl_relaxed(pll->base_addr) & ~PLL_BASE_EN; + writel_relaxed(r, pll->base_addr); + + r = readl_relaxed(pll->pwr_addr) | PLL_ISO_EN; + writel_relaxed(r, pll->pwr_addr); + + r = readl_relaxed(pll->pwr_addr) & ~PLL_PWR_ON; + writel_relaxed(r, pll->pwr_addr); + + mtk_clk_unlock(flags); +} + +#define AUD_PLL_POSTDIV_H 8 +#define AUD_PLL_POSTDIV_L 6 +#define AUD_PLL_POSTDIV_MASK GENMASK(AUD_PLL_POSTDIV_H, AUD_PLL_POSTDIV_L) +#define AUD_PLL_PCW_H 30 +#define AUD_PLL_PCW_L 0 +#define AUD_PLL_PCW_MASK GENMASK(AUD_PLL_PCW_H, AUD_PLL_PCW_L) + +static unsigned long clk_aud_pll_recalc_rate( + struct clk_hw *hw, + unsigned long parent_rate) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + + u32 con0 = readl_relaxed(pll->base_addr); + u32 con1 = readl_relaxed(pll->base_addr + 4); + + u32 vcodivsel = (con0 & PLL_VCODIV_MASK) >> PLL_VCODIV_L; + u32 prediv = (con0 & PLL_PREDIV_MASK) >> PLL_PREDIV_L; + u32 posdiv = (con0 & AUD_PLL_POSTDIV_MASK) >> AUD_PLL_POSTDIV_L; + u32 pcw = (con1 & AUD_PLL_PCW_MASK) >> AUD_PLL_PCW_L; + u32 pcwfbits = 24; + + u32 vco_freq; + unsigned long r; + + parent_rate = parent_rate ? parent_rate : 26000000; + vcodivsel = pll_vcodivsel_map[vcodivsel]; + prediv = pll_prediv_map[prediv]; + + vco_freq = calc_pll_vco_freq( + parent_rate, pcw, vcodivsel, prediv, pcwfbits); + r = vco_freq / pll_posdiv_map[posdiv]; + + pr_debug("%lu: %s\n", r, __clk_get_name(hw->clk)); + + return r; +} + +static long clk_aud_pll_round_rate( + struct clk_hw *hw, + unsigned long rate, + unsigned long *prate) +{ + int r; + u32 pcwfbits = 24; + u32 pcw = 0; + u32 postdiv = 0; + + pr_debug("%s, rate: %lu\n", __clk_get_name(hw->clk), rate); + + *prate = *prate ? *prate : 26000000; + rate = freq_limit(rate); + calc_pll_freq_cfg(&pcw, &postdiv, rate, *prate, pcwfbits); + + r = calc_pll_vco_freq(*prate, pcw, 1, 1, pcwfbits); + r = r / pll_posdiv_map[postdiv]; + return r; +} + +static void clk_aud_pll_set_rate_regs( + struct clk_hw *hw, + u32 pcw, + u32 postdiv_idx) +{ + unsigned long flags = 0; + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + void __iomem *con0_addr = pll->base_addr; + void __iomem *con1_addr = pll->base_addr + 4; + void __iomem *con4_addr = pll->base_addr + 16; + u32 con0; + u32 con1; + u32 pll_en; + + mtk_clk_lock(flags); + + con0 = readl_relaxed(con0_addr); + con1 = readl_relaxed(con1_addr); + + pll_en = con0 & PLL_BASE_EN; + + /* set postdiv */ + con0 &= ~AUD_PLL_POSTDIV_MASK; + con0 |= postdiv_idx << AUD_PLL_POSTDIV_L; + writel_relaxed(con0, con0_addr); + + /* set pcw */ + con1 &= ~AUD_PLL_PCW_MASK; + con1 |= pcw << AUD_PLL_PCW_L; + + if (pll_en) + con1 |= PLL_PCW_CHG; + + writel_relaxed(con1, con1_addr); + writel_relaxed(con1 + 1, con4_addr); + /* AUDPLL_CON4[30:0] (AUDPLL_TUNER_N_INFO) = (pcw + 1) */ + + if (pll_en) { + dsb(); + udelay(20); + } + + mtk_clk_unlock(flags); +} + +static int clk_aud_pll_set_rate( + struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + u32 pcwfbits = 24; + u32 pcw = 0; + u32 postdiv_idx = 0; + int r; + + parent_rate = parent_rate ? parent_rate : 26000000; + r = calc_pll_freq_cfg(&pcw, &postdiv_idx, rate, parent_rate, pcwfbits); + + pr_debug("%s, rate: %lu, pcw: %u, postdiv_idx: %u\n", + __clk_get_name(hw->clk), rate, pcw, postdiv_idx); + + if (r == 0) + clk_aud_pll_set_rate_regs(hw, pcw, postdiv_idx); + + return r; +} + +const struct clk_ops mtk_clk_aud_pll_ops = { + .is_enabled = clk_pll_is_enabled, + .prepare = clk_aud_pll_prepare, + .unprepare = clk_aud_pll_unprepare, + .recalc_rate = clk_aud_pll_recalc_rate, + .round_rate = clk_aud_pll_round_rate, + .set_rate = clk_aud_pll_set_rate, +}; + +#define TVD_PLL_POSTDIV_H 8 +#define TVD_PLL_POSTDIV_L 6 +#define TVD_PLL_POSTDIV_MASK GENMASK(TVD_PLL_POSTDIV_H, TVD_PLL_POSTDIV_L) +#define TVD_PLL_PCW_H 30 +#define TVD_PLL_PCW_L 0 +#define TVD_PLL_PCW_MASK GENMASK(TVD_PLL_PCW_H, TVD_PLL_PCW_L) + +static void clk_tvd_pll_set_rate_regs( + struct clk_hw *hw, + u32 pcw, + u32 postdiv_idx) +{ + unsigned long flags = 0; + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + void __iomem *con0_addr = pll->base_addr; + void __iomem *con1_addr = pll->base_addr + 4; + u32 con0; + u32 con1; + u32 pll_en; + + mtk_clk_lock(flags); + + con0 = readl_relaxed(con0_addr); + con1 = readl_relaxed(con1_addr); + + pll_en = con0 & PLL_BASE_EN; + + /* set postdiv */ + con0 &= ~TVD_PLL_POSTDIV_MASK; + con0 |= postdiv_idx << TVD_PLL_POSTDIV_L; + writel_relaxed(con0, con0_addr); + + /* set pcw */ + con1 &= ~TVD_PLL_PCW_MASK; + con1 |= pcw << TVD_PLL_PCW_L; + + if (pll_en) + con1 |= PLL_PCW_CHG; + + writel_relaxed(con1, con1_addr); + + if (pll_en) { + dsb(); + udelay(20); + } + + mtk_clk_unlock(flags); +} + +static int clk_tvd_pll_set_rate( + struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + u32 pcwfbits = 24; + u32 pcw = 0; + u32 postdiv_idx = 0; + int r; + + parent_rate = parent_rate ? parent_rate : 26000000; + r = calc_pll_freq_cfg(&pcw, &postdiv_idx, rate, parent_rate, pcwfbits); + + pr_debug("%s, rate: %lu, pcw: %u, postdiv_idx: %u\n", + __clk_get_name(hw->clk), rate, pcw, postdiv_idx); + + if (r == 0) + clk_tvd_pll_set_rate_regs(hw, pcw, postdiv_idx); + + return r; +} + +const struct clk_ops mtk_clk_tvd_pll_ops = { + .is_enabled = clk_pll_is_enabled, + .prepare = clk_pll_prepare, + .unprepare = clk_pll_unprepare, + .recalc_rate = clk_aud_pll_recalc_rate, + .round_rate = clk_aud_pll_round_rate, + .set_rate = clk_tvd_pll_set_rate, +}; diff --git a/drivers/clk/mediatek/clk-mt8135-pll.h b/drivers/clk/mediatek/clk-mt8135-pll.h new file mode 100644 index 0000000..0628189 --- /dev/null +++ b/drivers/clk/mediatek/clk-mt8135-pll.h @@ -0,0 +1,28 @@ +/* + * 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_MT8135_PLL_H +#define __DRV_CLK_MT8135_PLL_H + +/* + * This is a private header file. DO NOT include it except clk-*.c. + */ + +extern const struct clk_ops mtk_clk_pll_ops; +extern const struct clk_ops mtk_clk_arm_pll_ops; +extern const struct clk_ops mtk_clk_lc_pll_ops; +extern const struct clk_ops mtk_clk_aud_pll_ops; +extern const struct clk_ops mtk_clk_tvd_pll_ops; + +#endif /* __DRV_CLK_MT8135_PLL_H */ diff --git a/drivers/clk/mediatek/clk-mt8135.c b/drivers/clk/mediatek/clk-mt8135.c new file mode 100644 index 0000000..fa8dd7d --- /dev/null +++ b/drivers/clk/mediatek/clk-mt8135.c @@ -0,0 +1,752 @@ +/* + * 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 "clk-mtk.h" +#include "clk-pll.h" +#include "clk-mt8135-pll.h" + +#include <dt-bindings/clock/mt8135-clk.h> + +/* + * platform clocks + */ + +/* ROOT */ +#define clk_null "clk_null" +#define clk26m "clk26m" +#define rtc32k "rtc32k" + +#define dsi0_lntc_dsiclk "dsi0_lntc_dsi" +#define hdmitx_clkdig_cts "hdmitx_dig_cts" +#define clkph_mck "clkph_mck" +#define cpum_tck_in "cpum_tck_in" + +/* PLL */ +#define armpll1 "armpll1" +#define armpll2 "armpll2" +#define mainpll "mainpll" +#define univpll "univpll" +#define mmpll "mmpll" +#define msdcpll "msdcpll" +#define tvdpll "tvdpll" +#define lvdspll "lvdspll" +#define audpll "audpll" +#define vdecpll "vdecpll" + +#define mainpll_806m "mainpll_806m" +#define mainpll_537p3m "mainpll_537p3m" +#define mainpll_322p4m "mainpll_322p4m" +#define mainpll_230p3m "mainpll_230p3m" + +#define univpll_624m "univpll_624m" +#define univpll_416m "univpll_416m" +#define univpll_249p6m "univpll_249p6m" +#define univpll_178p3m "univpll_178p3m" +#define univpll_48m "univpll_48m" + +/* DIV */ +#define mmpll_d2 "mmpll_d2" +#define mmpll_d3 "mmpll_d3" +#define mmpll_d5 "mmpll_d5" +#define mmpll_d7 "mmpll_d7" +#define mmpll_d4 "mmpll_d4" +#define mmpll_d6 "mmpll_d6" + +#define syspll_d2 "syspll_d2" +#define syspll_d4 "syspll_d4" +#define syspll_d6 "syspll_d6" +#define syspll_d8 "syspll_d8" +#define syspll_d10 "syspll_d10" +#define syspll_d12 "syspll_d12" +#define syspll_d16 "syspll_d16" +#define syspll_d24 "syspll_d24" +#define syspll_d3 "syspll_d3" +#define syspll_d2p5 "syspll_d2p5" +#define syspll_d5 "syspll_d5" +#define syspll_d3p5 "syspll_d3p5" + +#define univpll1_d2 "univpll1_d2" +#define univpll1_d4 "univpll1_d4" +#define univpll1_d6 "univpll1_d6" +#define univpll1_d8 "univpll1_d8" +#define univpll1_d10 "univpll1_d10" + +#define univpll2_d2 "univpll2_d2" +#define univpll2_d4 "univpll2_d4" +#define univpll2_d6 "univpll2_d6" +#define univpll2_d8 "univpll2_d8" + +#define univpll_d3 "univpll_d3" +#define univpll_d5 "univpll_d5" +#define univpll_d7 "univpll_d7" +#define univpll_d10 "univpll_d10" +#define univpll_d26 "univpll_d26" + +#define apll_ck "apll" +#define apll_d4 "apll_d4" +#define apll_d8 "apll_d8" +#define apll_d16 "apll_d16" +#define apll_d24 "apll_d24" + +#define lvdspll_d2 "lvdspll_d2" +#define lvdspll_d4 "lvdspll_d4" +#define lvdspll_d8 "lvdspll_d8" + +#define lvdstx_clkdig_cts "lvdstx_dig_cts" +#define vpll_dpix_ck "vpll_dpix_ck" +#define tvhdmi_h_ck "tvhdmi_h_ck" +#define hdmitx_clkdig_d2 "hdmitx_dig_d2" +#define hdmitx_clkdig_d3 "hdmitx_dig_d3" +#define tvhdmi_d2 "tvhdmi_d2" +#define tvhdmi_d4 "tvhdmi_d4" +#define mempll_mck_d4 "mempll_mck_d4" + +/* TOP */ +#define axi_sel "axi_sel" +#define smi_sel "smi_sel" +#define mfg_sel "mfg_sel" +#define irda_sel "irda_sel" +#define cam_sel "cam_sel" +#define aud_intbus_sel "aud_intbus_sel" +#define jpg_sel "jpg_sel" +#define disp_sel "disp_sel" +#define msdc30_1_sel "msdc30_1_sel" +#define msdc30_2_sel "msdc30_2_sel" +#define msdc30_3_sel "msdc30_3_sel" +#define msdc30_4_sel "msdc30_4_sel" +#define usb20_sel "usb20_sel" +#define venc_sel "venc_sel" +#define spi_sel "spi_sel" +#define uart_sel "uart_sel" +#define mem_sel "mem_sel" +#define camtg_sel "camtg_sel" +#define audio_sel "audio_sel" +#define fix_sel "fix_sel" +#define vdec_sel "vdec_sel" +#define ddrphycfg_sel "ddrphycfg_sel" +#define dpilvds_sel "dpilvds_sel" +#define pmicspi_sel "pmicspi_sel" +#define msdc30_0_sel "msdc30_0_sel" +#define smi_mfg_as_sel "smi_mfg_as_sel" +#define gcpu_sel "gcpu_sel" +#define dpi1_sel "dpi1_sel" +#define cci_sel "cci_sel" +#define apll_sel "apll_sel" +#define hdmipll_sel "hdmipll_sel" + +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, \ + } + +static void __init init_factors(struct mtk_fixed_factor *clks, int num, + struct clk_onecell_data *clk_data) +{ + int i; + struct clk *clk; + + for (i = 0; i < num; i++) { + struct mtk_fixed_factor *ff = &clks[i]; + + clk = clk_register_fixed_factor(NULL, ff->name, ff->parent_name, + 0, 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; + + pr_debug("factor %3d: %s\n", i, ff->name); + } +} + +static struct mtk_fixed_factor root_clk_alias[] __initdata = { + 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 void __init init_clk_root_alias(struct clk_onecell_data *clk_data) +{ + init_factors(root_clk_alias, ARRAY_SIZE(root_clk_alias), clk_data); +} + +static struct mtk_fixed_factor top_divs[] __initdata = { + 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 void __init init_clk_top_div(struct clk_onecell_data *clk_data) +{ + init_factors(top_divs, ARRAY_SIZE(top_divs), clk_data); +} + +static const char *axi_parents[] __initconst = { + clk26m, + syspll_d3, + syspll_d4, + syspll_d6, + univpll_d5, + univpll2_d2, + syspll_d3p5}; + +static const char *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 *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 *irda_parents[] __initconst = { + clk26m, + univpll2_d8, + univpll1_d6}; + +static const char *cam_parents[] __initconst = { + clk26m, + syspll_d3, + syspll_d3p5, + syspll_d4, + univpll_d5, + univpll2_d2, + univpll_d7, + univpll1_d4}; + +static const char *aud_intbus_parents[] __initconst = { + clk26m, + syspll_d6, + univpll_d10}; + +static const char *jpg_parents[] __initconst = { + clk26m, + syspll_d5, + syspll_d4, + syspll_d3, + univpll_d7, + univpll2_d2, + univpll_d5}; + +static const char *disp_parents[] __initconst = { + clk26m, + syspll_d3p5, + syspll_d3, + univpll2_d2, + univpll_d5, + univpll1_d2, + lvdspll, + vdecpll}; + +static const char *msdc30_parents[] __initconst = { + clk26m, + syspll_d6, + syspll_d5, + univpll1_d4, + univpll2_d4, + msdcpll}; + +static const char *usb20_parents[] __initconst = { + clk26m, + univpll2_d6, + univpll1_d10}; + +static const char *venc_parents[] __initconst = { + clk26m, + syspll_d3, + syspll_d8, + univpll_d5, + univpll1_d6, + mmpll_d4, + mmpll_d5, + mmpll_d6}; + +static const char *spi_parents[] __initconst = { + clk26m, + syspll_d6, + syspll_d8, + syspll_d10, + univpll1_d6, + univpll1_d8}; + +static const char *uart_parents[] __initconst = { + clk26m, + univpll2_d8}; + +static const char *mem_parents[] __initconst = { + clk26m, + clkph_mck}; + +static const char *camtg_parents[] __initconst = { + clk26m, + univpll_d26, + univpll1_d6, + syspll_d16, + syspll_d8}; + +static const char *audio_parents[] __initconst = { + clk26m, + syspll_d24}; + +static const char *fix_parents[] __initconst = { + rtc32k, + clk26m, + univpll_d5, + univpll_d7, + univpll1_d2, + univpll1_d4, + univpll1_d6, + univpll1_d8}; + +static const char *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 *ddrphycfg_parents[] __initconst = { + clk26m, + axi_sel, + syspll_d12}; + +static const char *dpilvds_parents[] __initconst = { + clk26m, + lvdspll, + lvdspll_d2, + lvdspll_d4, + lvdspll_d8}; + +static const char *pmicspi_parents[] __initconst = { + clk26m, + univpll2_d6, + syspll_d8, + syspll_d10, + univpll1_d10, + mempll_mck_d4, + univpll_d26, + syspll_d24}; + +static const char *smi_mfg_as_parents[] __initconst = { + clk26m, + smi_sel, + mfg_sel, + mem_sel}; + +static const char *gcpu_parents[] __initconst = { + clk26m, + syspll_d4, + univpll_d7, + syspll_d5, + syspll_d6}; + +static const char *dpi1_parents[] __initconst = { + clk26m, + tvhdmi_h_ck, + tvhdmi_d2, + tvhdmi_d4}; + +static const char *cci_parents[] __initconst = { + clk26m, + mainpll_537p3m, + univpll_d3, + syspll_d2p5, + syspll_d3, + syspll_d5}; + +static const char *apll_parents[] __initconst = { + clk26m, + apll_ck, + apll_d4, + apll_d8, + apll_d16, + apll_d24}; + +static const char *hdmipll_parents[] __initconst = { + clk26m, + hdmitx_clkdig_cts, + hdmitx_clkdig_d2, + hdmitx_clkdig_d3}; + +struct mtk_mux { + int id; + const char *name; + u32 reg; + int shift; + int width; + int gate; + const char **parent_names; + int num_parents; +}; + +#define MUX(_id, _name, _parents, _reg, _shift, _width, _gate) { \ + .id = _id, \ + .name = _name, \ + .reg = _reg, \ + .shift = _shift, \ + .width = _width, \ + .gate = _gate, \ + .parent_names = (const char **)_parents, \ + .num_parents = ARRAY_SIZE(_parents), \ + } + +static struct mtk_mux top_muxes[] __initdata = { + /* CLK_CFG_0 */ + MUX(TOP_AXI_SEL, axi_sel, axi_parents, + 0x0140, 0, 3, INVALID_MUX_GATE_BIT), + MUX(TOP_SMI_SEL, smi_sel, smi_parents, 0x0140, 8, 4, 15), + MUX(TOP_MFG_SEL, mfg_sel, mfg_parents, 0x0140, 16, 4, 23), + MUX(TOP_IRDA_SEL, irda_sel, irda_parents, 0x0140, 24, 2, 31), + /* CLK_CFG_1 */ + MUX(TOP_CAM_SEL, cam_sel, cam_parents, 0x0144, 0, 3, 7), + MUX(TOP_AUD_INTBUS_SEL, aud_intbus_sel, aud_intbus_parents, + 0x0144, 8, 2, 15), + MUX(TOP_JPG_SEL, jpg_sel, jpg_parents, 0x0144, 16, 3, 23), + MUX(TOP_DISP_SEL, disp_sel, disp_parents, 0x0144, 24, 3, 31), + /* CLK_CFG_2 */ + MUX(TOP_MSDC30_1_SEL, msdc30_1_sel, msdc30_parents, 0x0148, 0, 3, 7), + MUX(TOP_MSDC30_2_SEL, msdc30_2_sel, msdc30_parents, 0x0148, 8, 3, 15), + MUX(TOP_MSDC30_3_SEL, msdc30_3_sel, msdc30_parents, 0x0148, 16, 3, 23), + MUX(TOP_MSDC30_4_SEL, msdc30_4_sel, msdc30_parents, 0x0148, 24, 3, 31), + /* CLK_CFG_3 */ + MUX(TOP_USB20_SEL, usb20_sel, usb20_parents, 0x014c, 0, 2, 7), + /* CLK_CFG_4 */ + MUX(TOP_VENC_SEL, venc_sel, venc_parents, 0x0150, 8, 3, 15), + MUX(TOP_SPI_SEL, spi_sel, spi_parents, 0x0150, 16, 3, 23), + MUX(TOP_UART_SEL, uart_sel, uart_parents, 0x0150, 24, 2, 31), + /* CLK_CFG_6 */ + MUX(TOP_MEM_SEL, mem_sel, mem_parents, 0x0158, 0, 2, 7), + MUX(TOP_CAMTG_SEL, camtg_sel, camtg_parents, 0x0158, 8, 3, 15), + MUX(TOP_AUDIO_SEL, audio_sel, audio_parents, 0x0158, 24, 2, 31), + /* CLK_CFG_7 */ + MUX(TOP_FIX_SEL, fix_sel, fix_parents, 0x015c, 0, 3, 7), + MUX(TOP_VDEC_SEL, vdec_sel, vdec_parents, 0x015c, 8, 4, 15), + MUX(TOP_DDRPHYCFG_SEL, ddrphycfg_sel, ddrphycfg_parents, + 0x015c, 16, 2, 23), + MUX(TOP_DPILVDS_SEL, dpilvds_sel, dpilvds_parents, 0x015c, 24, 3, 31), + /* CLK_CFG_8 */ + MUX(TOP_PMICSPI_SEL, pmicspi_sel, pmicspi_parents, 0x0164, 0, 3, 7), + MUX(TOP_MSDC30_0_SEL, msdc30_0_sel, msdc30_parents, 0x0164, 8, 3, 15), + MUX(TOP_SMI_MFG_AS_SEL, smi_mfg_as_sel, smi_mfg_as_parents, + 0x0164, 16, 2, 23), + MUX(TOP_GCPU_SEL, gcpu_sel, gcpu_parents, 0x0164, 24, 3, 31), + /* CLK_CFG_9 */ + MUX(TOP_DPI1_SEL, dpi1_sel, dpi1_parents, 0x0168, 0, 2, 7), + MUX(TOP_CCI_SEL, cci_sel, cci_parents, 0x0168, 8, 3, 15), + MUX(TOP_APLL_SEL, apll_sel, apll_parents, 0x0168, 16, 3, 23), + MUX(TOP_HDMIPLL_SEL, hdmipll_sel, hdmipll_parents, 0x0168, 24, 2, 31), +}; + +static void __init 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++) { + struct mtk_mux *mux = &top_muxes[i]; + + clk = mtk_clk_register_mux(mux->name, + mux->parent_names, mux->num_parents, + top_base + mux->reg, mux->shift, mux->width, mux->gate); + + 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; + + pr_debug("mux %3d: %s\n", i, mux->name); + } +} + +struct mtk_pll { + int id; + const char *name; + const char *parent_name; + u32 reg; + u32 pwr_reg; + u32 en_mask; + unsigned int flags; + const struct clk_ops *ops; +}; + +#define PLL(_id, _name, _parent, _reg, _pwr_reg, _en_mask, _flags, _ops) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .reg = _reg, \ + .pwr_reg = _pwr_reg, \ + .en_mask = _en_mask, \ + .flags = _flags, \ + .ops = _ops, \ + } + +static struct mtk_pll plls[] __initdata = { + PLL(APMIXED_ARMPLL1, armpll1, clk26m, 0x0200, 0x0218, + 0x80000001, HAVE_PLL_HP, &mtk_clk_arm_pll_ops), + PLL(APMIXED_ARMPLL2, armpll2, clk26m, 0x02cc, 0x02e4, + 0x80000001, HAVE_PLL_HP, &mtk_clk_arm_pll_ops), + PLL(APMIXED_MAINPLL, mainpll, clk26m, 0x021c, 0x0234, + 0xf0000001, HAVE_PLL_HP | HAVE_RST_BAR | PLL_AO, + &mtk_clk_pll_ops), + PLL(APMIXED_UNIVPLL, univpll, clk26m, 0x0238, 0x0250, + 0xf3000001, HAVE_RST_BAR | HAVE_FIX_FRQ | PLL_AO, + &mtk_clk_lc_pll_ops), + PLL(APMIXED_MMPLL, mmpll, clk26m, 0x0254, 0x026c, + 0xf0000001, HAVE_PLL_HP | HAVE_RST_BAR, &mtk_clk_pll_ops), + PLL(APMIXED_MSDCPLL, msdcpll, clk26m, 0x0278, 0x0290, + 0x80000001, HAVE_PLL_HP, &mtk_clk_pll_ops), + PLL(APMIXED_TVDPLL, tvdpll, clk26m, 0x0294, 0x02ac, + 0x80000001, HAVE_PLL_HP, &mtk_clk_tvd_pll_ops), + PLL(APMIXED_LVDSPLL, lvdspll, clk26m, 0x02b0, 0x02c8, + 0x80000001, HAVE_PLL_HP, &mtk_clk_pll_ops), + PLL(APMIXED_AUDPLL, audpll, clk26m, 0x02e8, 0x0300, + 0x80000001, 0, &mtk_clk_aud_pll_ops), + PLL(APMIXED_VDECPLL, vdecpll, clk26m, 0x0304, 0x031c, + 0x80000001, HAVE_PLL_HP, &mtk_clk_pll_ops), +}; + +static void __init init_clk_apmixedsys(void __iomem *apmixed_base, + struct clk_onecell_data *clk_data) +{ + int i; + struct clk *clk; + + for (i = 0; i < ARRAY_SIZE(plls); i++) { + struct mtk_pll *pll = &plls[i]; + + clk = mtk_clk_register_pll(pll->name, pll->parent_name, + apmixed_base + pll->reg, + apmixed_base + pll->pwr_reg, + pll->en_mask, pll->flags, pll->ops); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + pll->name, PTR_ERR(clk)); + continue; + } + + if (clk_data) + clk_data->clks[pll->id] = clk; + + pr_debug("pll %3d: %s\n", i, pll->name); + } +} + +/* + * device tree support + */ + +static struct clk_onecell_data *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(struct clk *), GFP_KERNEL); + if (!clk_data->clks) { + kfree(clk_data); + return NULL; + } + + clk_data->clk_num = clk_num; + + for (i = 0; i < clk_num; ++i) + clk_data->clks[i] = ERR_PTR(-ENOENT); + + return clk_data; +} + +static void __init mtk_topckgen_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + void __iomem *base; + int r; + + pr_debug("%s: %s\n", __func__, node->name); + + base = of_iomap(node, 0); + if (!base) { + pr_err("ioremap topckgen failed\n"); + return; + } + + clk_data = alloc_clk_data(TOP_NR_CLK); + + init_clk_root_alias(clk_data); + init_clk_top_div(clk_data); + init_clk_topckgen(base, clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("could not register clock provide\n"); +} +CLK_OF_DECLARE(mtk_topckgen, "mediatek,mt8135-topckgen", mtk_topckgen_init); + +static void __init mtk_apmixedsys_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + void __iomem *base; + int r; + + pr_debug("%s: %s\n", __func__, node->name); + + base = of_iomap(node, 0); + if (!base) { + pr_err("ioremap apmixedsys failed\n"); + return; + } + + clk_data = alloc_clk_data(APMIXED_NR_CLK); + + init_clk_apmixedsys(base, clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("could not register clock provide\n"); +} +CLK_OF_DECLARE(mtk_apmixedsys, "mediatek,mt8135-apmixedsys", + mtk_apmixedsys_init); diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c new file mode 100644 index 0000000..41a12d3 --- /dev/null +++ b/drivers/clk/mediatek/clk-mtk.c @@ -0,0 +1,93 @@ +/* + * 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" + +static DEFINE_SPINLOCK(clk_ops_lock); + +spinlock_t *get_mtk_clk_lock(void) +{ + return &clk_ops_lock; +} + +/* + * clk_mux + */ + +struct clk *mtk_clk_register_mux( + const char *name, + const char **parent_names, + u8 num_parents, + void __iomem *base_addr, + u8 shift, + u8 width, + u8 gate_bit) +{ + struct clk *clk; + struct clk_mux *mux; + struct clk_gate *gate = NULL; + struct clk_hw *gate_hw = NULL; + const struct clk_ops *gate_ops = NULL; + u32 mask = BIT(width) - 1; + + pr_debug("name: %s, num_parents: %d, gate_bit: %d\n", + name, (int)num_parents, (int)gate_bit); + + mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL); + if (!mux) + return ERR_PTR(-ENOMEM); + + mux->reg = base_addr; + mux->mask = mask; + mux->shift = shift; + mux->flags = 0; + mux->lock = &clk_ops_lock; + + if (gate_bit <= MAX_MUX_GATE_BIT) { + gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL); + if (!gate) { + kfree(mux); + return ERR_PTR(-ENOMEM); + } + + gate->reg = base_addr; + gate->bit_idx = gate_bit; + gate->flags = CLK_GATE_SET_TO_DISABLE; + gate->lock = &clk_ops_lock; + + gate_hw = &gate->hw; + gate_ops = &clk_gate_ops; + } + + clk = clk_register_composite(NULL, name, parent_names, num_parents, + &mux->hw, &clk_mux_ops, + NULL, NULL, + gate_hw, gate_ops, + CLK_IGNORE_UNUSED); + + if (IS_ERR(clk)) { + kfree(gate); + kfree(mux); + } + + return clk; +} diff --git a/drivers/clk/mediatek/clk-mtk.h b/drivers/clk/mediatek/clk-mtk.h new file mode 100644 index 0000000..b69245d --- /dev/null +++ b/drivers/clk/mediatek/clk-mtk.h @@ -0,0 +1,47 @@ +/* + * 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 + +/* + * This is a private header file. DO NOT include it except clk-*.c. + */ + +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> + +#define CLK_DEBUG 0 +#define DUMMY_REG_TEST 0 + +extern spinlock_t *get_mtk_clk_lock(void); + +#define mtk_clk_lock(flags) spin_lock_irqsave(get_mtk_clk_lock(), flags) +#define mtk_clk_unlock(flags) \ + spin_unlock_irqrestore(get_mtk_clk_lock(), flags) + +#define MAX_MUX_GATE_BIT 31 +#define INVALID_MUX_GATE_BIT (MAX_MUX_GATE_BIT + 1) + +struct clk *mtk_clk_register_mux( + const char *name, + const char **parent_names, + u8 num_parents, + void __iomem *base_addr, + u8 shift, + u8 width, + u8 gate_bit); + +#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..864cc60 --- /dev/null +++ b/drivers/clk/mediatek/clk-pll.c @@ -0,0 +1,63 @@ +/* + * 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/io.h> +#include <linux/slab.h> +#include <linux/clkdev.h> + +#include "clk-mtk.h" +#include "clk-pll.h" + +/* + * clk_pll + */ + +struct clk *mtk_clk_register_pll( + const char *name, + const char *parent_name, + u32 *base_addr, + u32 *pwr_addr, + u32 en_mask, + u32 flags, + const struct clk_ops *ops) +{ + struct mtk_clk_pll *pll; + struct clk_init_data init; + struct clk *clk; + + pr_debug("name: %s\n", name); + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + pll->base_addr = base_addr; + pll->pwr_addr = pwr_addr; + pll->en_mask = en_mask; + pll->flags = flags; + pll->hw.init = &init; + + init.name = name; + init.ops = ops; + init.flags = CLK_IGNORE_UNUSED; + init.parent_names = &parent_name; + init.num_parents = 1; + + clk = clk_register(NULL, &pll->hw); + + if (IS_ERR(clk)) + kfree(pll); + + return clk; +} diff --git a/drivers/clk/mediatek/clk-pll.h b/drivers/clk/mediatek/clk-pll.h new file mode 100644 index 0000000..cb7f335 --- /dev/null +++ b/drivers/clk/mediatek/clk-pll.h @@ -0,0 +1,50 @@ +/* + * 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_PLL_H +#define __DRV_CLK_PLL_H + +/* + * This is a private header file. DO NOT include it except clk-*.c. + */ + +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> + +struct mtk_clk_pll { + struct clk_hw hw; + void __iomem *base_addr; + void __iomem *pwr_addr; + u32 en_mask; + u32 flags; +}; + +#define to_mtk_clk_pll(_hw) container_of(_hw, struct mtk_clk_pll, hw) + +#define HAVE_RST_BAR BIT(0) +#define HAVE_PLL_HP BIT(1) +#define HAVE_FIX_FRQ BIT(2) +#define PLL_AO BIT(3) + +struct clk *mtk_clk_register_pll( + const char *name, + const char *parent_name, + u32 *base_addr, + u32 *pwr_addr, + u32 en_mask, + u32 flags, + const struct clk_ops *ops); + +#endif /* __DRV_CLK_PLL_H */ diff --git a/include/dt-bindings/clock/mt8135-clk.h b/include/dt-bindings/clock/mt8135-clk.h new file mode 100644 index 0000000..674fd1a --- /dev/null +++ b/include/dt-bindings/clock/mt8135-clk.h @@ -0,0 +1,128 @@ +/* +* 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 + +#endif /* _DT_BINDINGS_CLK_MT8135_H */ -- 1.8.1.1.dirty ^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 1/4] clk: Add initial common clock support for Mediatek SoC MT8135. @ 2014-11-28 11:34 ` James Liao 0 siblings, 0 replies; 15+ messages in thread From: James Liao @ 2014-11-28 11:34 UTC (permalink / raw) To: Rob Herring, Matthias Brugger, Mike Turquette Cc: Mark Rutland, Ashwin Chaugule, srv_jamesjj.liao, Russell King, srv_heupstream, Pawel Moll, Ian Campbell, Catalin Marinas, linux-kernel, devicetree, Vladimir Murzin, Sascha Hauer, Kumar Gala This patch adds common clock support for Mediatek SoCs, and basic clocks such as PLLs and TOPCKGEN clocks for MT8135. Signed-off-by: James Liao <jamesjj.liao@mediatek.com> --- drivers/clk/Makefile | 1 + drivers/clk/mediatek/Makefile | 2 + drivers/clk/mediatek/clk-mt8135-pll.c | 891 +++++++++++++++++++++++++++++++++ drivers/clk/mediatek/clk-mt8135-pll.h | 28 ++ drivers/clk/mediatek/clk-mt8135.c | 752 ++++++++++++++++++++++++++++ drivers/clk/mediatek/clk-mtk.c | 93 ++++ drivers/clk/mediatek/clk-mtk.h | 47 ++ drivers/clk/mediatek/clk-pll.c | 63 +++ drivers/clk/mediatek/clk-pll.h | 50 ++ include/dt-bindings/clock/mt8135-clk.h | 128 +++++ 10 files changed, 2055 insertions(+) create mode 100644 drivers/clk/mediatek/Makefile create mode 100644 drivers/clk/mediatek/clk-mt8135-pll.c create mode 100644 drivers/clk/mediatek/clk-mt8135-pll.h create mode 100644 drivers/clk/mediatek/clk-mt8135.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/clk-pll.h create mode 100644 include/dt-bindings/clock/mt8135-clk.h diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index d5fba5b..ce6c250 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -47,6 +47,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..3c3432b --- /dev/null +++ b/drivers/clk/mediatek/Makefile @@ -0,0 +1,2 @@ +obj-y += clk-mtk.o clk-pll.o +obj-y += clk-mt8135.o clk-mt8135-pll.o diff --git a/drivers/clk/mediatek/clk-mt8135-pll.c b/drivers/clk/mediatek/clk-mt8135-pll.c new file mode 100644 index 0000000..999f640 --- /dev/null +++ b/drivers/clk/mediatek/clk-mt8135-pll.c @@ -0,0 +1,891 @@ +/* + * 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/io.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/clkdev.h> + +#include "clk-mtk.h" +#include "clk-pll.h" +#include "clk-mt8135-pll.h" + +/* + * clk_pll + */ + +#define PLL_BASE_EN BIT(0) +#define PLL_PWR_ON BIT(0) +#define PLL_ISO_EN BIT(1) +#define PLL_PCW_CHG BIT(31) +#define RST_BAR_MASK BIT(27) +#define AUDPLL_TUNER_EN BIT(31) + +#define PLL_PREDIV_H 5 +#define PLL_PREDIV_L 4 +#define PLL_PREDIV_MASK GENMASK(PLL_PREDIV_H, PLL_PREDIV_L) +#define PLL_VCODIV_L 19 +#define PLL_VCODIV_MASK BIT(19) + +static const u32 pll_vcodivsel_map[2] = { 1, 2 }; +static const u32 pll_prediv_map[4] = { 1, 2, 4, 4 }; +static const u32 pll_posdiv_map[8] = { 1, 2, 4, 8, 16, 16, 16, 16 }; +static const u32 pll_fbksel_map[4] = { 1, 2, 4, 4 }; + +static u32 calc_pll_vco_freq( + u32 fin, + u32 pcw, + u32 vcodivsel, + u32 prediv, + u32 pcwfbits) +{ + /* vco = (fin * pcw * vcodivsel / prediv) >> pcwfbits; */ + u64 vco = fin; + u8 c = 0; + + vco = vco * pcw * vcodivsel; + do_div(vco, prediv); + + if (vco & GENMASK(pcwfbits - 1, 0)) + c = 1; + vco >>= pcwfbits; + + if (c) + vco++; + + return (u32)vco; +} + +static u32 freq_limit(u32 freq) +{ + static const u32 freq_max = 2000 * 1000 * 1000; /* 2000 MHz */ + static const u32 freq_min = 1000 * 1000 * 1000 / 16; /* 1000 MHz */ + + if (freq <= freq_min) + freq = freq_min + 16; + else if (freq > freq_max) + freq = freq_max; + + return freq; +} + +static int calc_pll_freq_cfg( + u32 *pcw, + u32 *postdiv_idx, + u32 freq, + u32 fin, + int pcwfbits) +{ + static const u64 freq_max = 2000 * 1000 * 1000; /* 2000 MHz */ + static const u64 freq_min = 1000 * 1000 * 1000; /* 1000 MHz */ + static const u64 postdiv[] = { 1, 2, 4, 8, 16 }; + u64 n_info; + u32 idx; + + pr_debug("freq: %u\n", freq); + + /* search suitable postdiv */ + for (idx = 0; + idx < ARRAY_SIZE(postdiv) && postdiv[idx] * freq <= freq_min; + idx++) + ; + + if (idx >= ARRAY_SIZE(postdiv)) + return -EINVAL; /* freq is out of range (too low) */ + else if (postdiv[idx] * freq > freq_max) + return -EINVAL; /* freq is out of range (too high) */ + + /* n_info = freq * postdiv / 26MHz * 2^pcwfbits */ + n_info = (postdiv[idx] * freq) << pcwfbits; + do_div(n_info, fin); + + *postdiv_idx = idx; + *pcw = (u32)n_info; + + return 0; +} + +static int clk_pll_is_enabled(struct clk_hw *hw) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + + int r = (readl_relaxed(pll->base_addr) & PLL_BASE_EN) != 0; + + pr_debug("%d: %s\n", r, __clk_get_name(hw->clk)); + + return r; +} + +static int clk_pll_prepare(struct clk_hw *hw) +{ + unsigned long flags = 0; + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 r; + + pr_debug("%s\n", __clk_get_name(hw->clk)); + + mtk_clk_lock(flags); + + r = readl_relaxed(pll->pwr_addr) | PLL_PWR_ON; + writel_relaxed(r, pll->pwr_addr); + dsb(); + udelay(1); + + r = readl_relaxed(pll->pwr_addr) & ~PLL_ISO_EN; + writel_relaxed(r , pll->pwr_addr); + dsb(); + udelay(1); + + r = readl_relaxed(pll->base_addr) | pll->en_mask; + writel_relaxed(r, pll->base_addr); + dsb(); + udelay(20); + + if (pll->flags & HAVE_RST_BAR) { + r = readl_relaxed(pll->base_addr) | RST_BAR_MASK; + writel_relaxed(r, pll->base_addr); + } + + mtk_clk_unlock(flags); + + return 0; +} + +static void clk_pll_unprepare(struct clk_hw *hw) +{ + unsigned long flags = 0; + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 r; + + pr_debug("%s: PLL_AO: %d\n", + __clk_get_name(hw->clk), !!(pll->flags & PLL_AO)); + + if (pll->flags & PLL_AO) + return; + + mtk_clk_lock(flags); + + if (pll->flags & HAVE_RST_BAR) { + r = readl_relaxed(pll->base_addr) & ~RST_BAR_MASK; + writel_relaxed(r, pll->base_addr); + } + + r = readl_relaxed(pll->base_addr) & ~PLL_BASE_EN; + writel_relaxed(r, pll->base_addr); + + r = readl_relaxed(pll->pwr_addr) | PLL_ISO_EN; + writel_relaxed(r, pll->pwr_addr); + + r = readl_relaxed(pll->pwr_addr) & ~PLL_PWR_ON; + writel_relaxed(r, pll->pwr_addr); + + mtk_clk_unlock(flags); +} + +#define SDM_PLL_POSTDIV_H 8 +#define SDM_PLL_POSTDIV_L 6 +#define SDM_PLL_POSTDIV_MASK GENMASK(SDM_PLL_POSTDIV_H, SDM_PLL_POSTDIV_L) +#define SDM_PLL_PCW_H 20 +#define SDM_PLL_PCW_L 0 +#define SDM_PLL_PCW_MASK GENMASK(SDM_PLL_PCW_H, SDM_PLL_PCW_L) + +static unsigned long clk_pll_recalc_rate( + struct clk_hw *hw, + unsigned long parent_rate) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + + u32 con0 = readl_relaxed(pll->base_addr); + u32 con1 = readl_relaxed(pll->base_addr + 4); + + u32 vcodivsel = (con0 & PLL_VCODIV_MASK) >> PLL_VCODIV_L; + u32 prediv = (con0 & PLL_PREDIV_MASK) >> PLL_PREDIV_L; + u32 posdiv = (con0 & SDM_PLL_POSTDIV_MASK) >> SDM_PLL_POSTDIV_L; + u32 pcw = (con1 & SDM_PLL_PCW_MASK) >> SDM_PLL_PCW_L; + u32 pcwfbits = 14; + + u32 vco_freq; + unsigned long r; + + parent_rate = parent_rate ? parent_rate : 26000000; + vcodivsel = pll_vcodivsel_map[vcodivsel]; + prediv = pll_prediv_map[prediv]; + + vco_freq = calc_pll_vco_freq( + parent_rate, pcw, vcodivsel, prediv, pcwfbits); + r = vco_freq / pll_posdiv_map[posdiv]; + + pr_debug("%lu: %s\n", r, __clk_get_name(hw->clk)); + + return r; +} + +static long clk_pll_round_rate( + struct clk_hw *hw, + unsigned long rate, + unsigned long *prate) +{ + int r; + u32 pcwfbits = 14; + u32 pcw = 0; + u32 postdiv = 0; + + pr_debug("%s, rate: %lu\n", __clk_get_name(hw->clk), rate); + + *prate = *prate ? *prate : 26000000; + rate = freq_limit(rate); + calc_pll_freq_cfg(&pcw, &postdiv, rate, *prate, pcwfbits); + + r = calc_pll_vco_freq(*prate, pcw, 1, 1, pcwfbits); + r = r / pll_posdiv_map[postdiv]; + return r; +} + +static void clk_pll_set_rate_regs( + struct clk_hw *hw, + u32 pcw, + u32 postdiv_idx) +{ + unsigned long flags = 0; + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + void __iomem *con0_addr = pll->base_addr; + void __iomem *con1_addr = pll->base_addr + 4; + u32 con0; + u32 con1; + u32 pll_en; + + mtk_clk_lock(flags); + + con0 = readl_relaxed(con0_addr); + con1 = readl_relaxed(con1_addr); + + pll_en = con0 & PLL_BASE_EN; + + /* set postdiv */ + con0 &= ~SDM_PLL_POSTDIV_MASK; + con0 |= postdiv_idx << SDM_PLL_POSTDIV_L; + writel_relaxed(con0, con0_addr); + + /* set pcw */ + con1 &= ~SDM_PLL_PCW_MASK; + con1 |= pcw << SDM_PLL_PCW_L; + + if (pll_en) + con1 |= PLL_PCW_CHG; + + writel_relaxed(con1, con1_addr); + + if (pll_en) { + dsb(); + udelay(20); + } + + mtk_clk_unlock(flags); +} + +static int clk_pll_set_rate( + struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + u32 pcwfbits = 14; + u32 pcw = 0; + u32 postdiv_idx = 0; + int r; + + parent_rate = parent_rate ? parent_rate : 26000000; + r = calc_pll_freq_cfg(&pcw, &postdiv_idx, rate, parent_rate, pcwfbits); + + pr_debug("%s, rate: %lu, pcw: %u, postdiv_idx: %u\n", + __clk_get_name(hw->clk), rate, pcw, postdiv_idx); + + if (r == 0) + clk_pll_set_rate_regs(hw, pcw, postdiv_idx); + + return r; +} + +const struct clk_ops mtk_clk_pll_ops = { + .is_enabled = clk_pll_is_enabled, + .prepare = clk_pll_prepare, + .unprepare = clk_pll_unprepare, + .recalc_rate = clk_pll_recalc_rate, + .round_rate = clk_pll_round_rate, + .set_rate = clk_pll_set_rate, +}; + +#define ARM_PLL_POSTDIV_H 26 +#define ARM_PLL_POSTDIV_L 24 +#define ARM_PLL_POSTDIV_MASK GENMASK(ARM_PLL_POSTDIV_H, ARM_PLL_POSTDIV_L) +#define ARM_PLL_PCW_H 20 +#define ARM_PLL_PCW_L 0 +#define ARM_PLL_PCW_MASK GENMASK(ARM_PLL_PCW_H, ARM_PLL_PCW_L) + +static unsigned long clk_arm_pll_recalc_rate( + struct clk_hw *hw, + unsigned long parent_rate) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + + u32 con0 = readl_relaxed(pll->base_addr); + u32 con1 = readl_relaxed(pll->base_addr + 4); + + u32 vcodivsel = (con0 & PLL_VCODIV_MASK) >> PLL_VCODIV_L; + u32 prediv = (con0 & PLL_PREDIV_MASK) >> PLL_PREDIV_L; + u32 posdiv = (con1 & ARM_PLL_POSTDIV_MASK) >> ARM_PLL_POSTDIV_L; + u32 pcw = (con1 & ARM_PLL_PCW_MASK) >> ARM_PLL_PCW_L; + u32 pcwfbits = 14; + + u32 vco_freq; + unsigned long r; + + parent_rate = parent_rate ? parent_rate : 26000000; + vcodivsel = pll_vcodivsel_map[vcodivsel]; + prediv = pll_prediv_map[prediv]; + + vco_freq = calc_pll_vco_freq( + parent_rate, pcw, vcodivsel, prediv, pcwfbits); + r = vco_freq / pll_posdiv_map[posdiv]; + + pr_debug("%lu: %s\n", r, __clk_get_name(hw->clk)); + + return r; +} + +static void clk_arm_pll_set_rate_regs( + struct clk_hw *hw, + u32 pcw, + u32 postdiv_idx) + +{ + unsigned long flags = 0; + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + void __iomem *con0_addr = pll->base_addr; + void __iomem *con1_addr = pll->base_addr + 4; + u32 con0; + u32 con1; + u32 pll_en; + + mtk_clk_lock(flags); + + con0 = readl_relaxed(con0_addr); + con1 = readl_relaxed(con1_addr); + + pll_en = con0 & PLL_BASE_EN; + + /* postdiv */ + con1 &= ~ARM_PLL_POSTDIV_MASK; + con1 |= postdiv_idx << ARM_PLL_POSTDIV_L; + + /* pcw */ + con1 &= ~ARM_PLL_PCW_MASK; + con1 |= pcw << ARM_PLL_PCW_L; + + if (pll_en) + con1 |= PLL_PCW_CHG; + + writel_relaxed(con1, con1_addr); + + if (pll_en) { + dsb(); + udelay(20); + } + + mtk_clk_unlock(flags); +} + +static int clk_arm_pll_set_rate( + struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + u32 pcwfbits = 14; + u32 pcw = 0; + u32 postdiv_idx = 0; + int r; + + parent_rate = parent_rate ? parent_rate : 26000000; + r = calc_pll_freq_cfg(&pcw, &postdiv_idx, rate, parent_rate, pcwfbits); + + pr_debug("%s, rate: %lu, pcw: %u, postdiv_idx: %u\n", + __clk_get_name(hw->clk), rate, pcw, postdiv_idx); + + if (r == 0) + clk_arm_pll_set_rate_regs(hw, pcw, postdiv_idx); + + return r; +} + +const struct clk_ops mtk_clk_arm_pll_ops = { + .is_enabled = clk_pll_is_enabled, + .prepare = clk_pll_prepare, + .unprepare = clk_pll_unprepare, + .recalc_rate = clk_arm_pll_recalc_rate, + .round_rate = clk_pll_round_rate, + .set_rate = clk_arm_pll_set_rate, +}; + +static int clk_lc_pll_prepare(struct clk_hw *hw) +{ + unsigned long flags = 0; + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 r; + + pr_debug("%s\n", __clk_get_name(hw->clk)); + + mtk_clk_lock(flags); + + r = readl_relaxed(pll->base_addr) | pll->en_mask; + writel_relaxed(r, pll->base_addr); + dsb(); + udelay(20); + + if (pll->flags & HAVE_RST_BAR) { + r = readl_relaxed(pll->base_addr) | RST_BAR_MASK; + writel_relaxed(r, pll->base_addr); + } + + mtk_clk_unlock(flags); + + return 0; +} + +static void clk_lc_pll_unprepare(struct clk_hw *hw) +{ + unsigned long flags = 0; + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 r; + + pr_debug("%s\n", __clk_get_name(hw->clk)); + + mtk_clk_lock(flags); + + if (pll->flags & HAVE_RST_BAR) { + r = readl_relaxed(pll->base_addr) & ~RST_BAR_MASK; + writel_relaxed(r, pll->base_addr); + } + + r = readl_relaxed(pll->base_addr) & ~PLL_BASE_EN; + writel_relaxed(r, pll->base_addr); + + mtk_clk_unlock(flags); +} + +#define LC_PLL_FBKSEL_H 21 +#define LC_PLL_FBKSEL_L 20 +#define LC_PLL_FBKSEL_MASK GENMASK(LC_PLL_FBKSEL_H, LC_PLL_FBKSEL_L) +#define LC_PLL_POSTDIV_H 8 +#define LC_PLL_POSTDIV_L 6 +#define LC_PLL_POSTDIV_MASK GENMASK(LC_PLL_POSTDIV_H, LC_PLL_POSTDIV_L) +#define LC_PLL_FBKDIV_H 15 +#define LC_PLL_FBKDIV_L 9 +#define LC_PLL_FBKDIV_MASK GENMASK(LC_PLL_FBKDIV_H, LC_PLL_FBKDIV_L) + +static unsigned long clk_lc_pll_recalc_rate( + struct clk_hw *hw, + unsigned long parent_rate) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + + u32 con0 = readl_relaxed(pll->base_addr); + + u32 fbksel = (con0 & LC_PLL_FBKSEL_MASK) >> LC_PLL_FBKSEL_L; + u32 vcodivsel = (con0 & PLL_VCODIV_MASK) >> PLL_VCODIV_L; + u32 fbkdiv = (con0 & LC_PLL_FBKDIV_MASK) >> LC_PLL_FBKDIV_L; + u32 prediv = (con0 & PLL_PREDIV_MASK) >> PLL_PREDIV_L; + u32 posdiv = (con0 & LC_PLL_POSTDIV_MASK) >> LC_PLL_POSTDIV_L; + + u32 vco_freq; + unsigned long r; + + parent_rate = parent_rate ? parent_rate : 26000000; + vcodivsel = pll_vcodivsel_map[vcodivsel]; + fbksel = pll_fbksel_map[fbksel]; + prediv = pll_prediv_map[prediv]; + + vco_freq = parent_rate * fbkdiv * fbksel * vcodivsel / prediv; + r = vco_freq / pll_posdiv_map[posdiv]; + + pr_debug("%lu: %s\n", r, __clk_get_name(hw->clk)); + + return r; +} + +static long clk_lc_pll_round_rate( + struct clk_hw *hw, + unsigned long rate, + unsigned long *prate) +{ + int r; + u32 pcwfbits = 0; + u32 pcw = 0; + u32 postdiv = 0; + + pr_debug("%s, rate: %lu\n", __clk_get_name(hw->clk), rate); + + *prate = *prate ? *prate : 26000000; + rate = freq_limit(rate); + calc_pll_freq_cfg(&pcw, &postdiv, rate, *prate, pcwfbits); + + r = calc_pll_vco_freq(*prate, pcw, 1, 1, pcwfbits); + r = r / pll_posdiv_map[postdiv]; + return r; +} + +static void clk_lc_pll_set_rate_regs( + struct clk_hw *hw, + u32 pcw, + u32 postdiv_idx) +{ + unsigned long flags = 0; + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + void __iomem *con0_addr = pll->base_addr; + u32 con0; + u32 pll_en; + + mtk_clk_lock(flags); + + con0 = readl_relaxed(con0_addr); + + pll_en = con0 & PLL_BASE_EN; + + /* postdiv */ + con0 &= ~LC_PLL_POSTDIV_MASK; + con0 |= postdiv_idx << LC_PLL_POSTDIV_L; + + /* fkbdiv */ + con0 &= ~LC_PLL_FBKDIV_MASK; + con0 |= pcw << LC_PLL_FBKDIV_L; + + writel_relaxed(con0, con0_addr); + + if (pll_en) { + dsb(); + udelay(20); + } + mtk_clk_unlock(flags); +} + +static int clk_lc_pll_set_rate( + struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + u32 pcwfbits = 0; + u32 pcw = 0; + u32 postdiv_idx = 0; + int r; + + parent_rate = parent_rate ? parent_rate : 26000000; + r = calc_pll_freq_cfg(&pcw, &postdiv_idx, rate, parent_rate, pcwfbits); + + pr_debug("%s, rate: %lu, pcw: %u, postdiv_idx: %u\n", + __clk_get_name(hw->clk), rate, pcw, postdiv_idx); + + if (r == 0) + clk_lc_pll_set_rate_regs(hw, pcw, postdiv_idx); + + return r; +} + +const struct clk_ops mtk_clk_lc_pll_ops = { + .is_enabled = clk_pll_is_enabled, + .prepare = clk_lc_pll_prepare, + .unprepare = clk_lc_pll_unprepare, + .recalc_rate = clk_lc_pll_recalc_rate, + .round_rate = clk_lc_pll_round_rate, + .set_rate = clk_lc_pll_set_rate, +}; + +static int clk_aud_pll_prepare(struct clk_hw *hw) +{ + unsigned long flags = 0; + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 r; + + pr_debug("%s\n", __clk_get_name(hw->clk)); + + mtk_clk_lock(flags); + + r = readl_relaxed(pll->pwr_addr) | PLL_PWR_ON; + writel_relaxed(r, pll->pwr_addr); + dsb(); + udelay(1); + + r = readl_relaxed(pll->pwr_addr) & ~PLL_ISO_EN; + writel_relaxed(r, pll->pwr_addr); + dsb(); + udelay(1); + + r = readl_relaxed(pll->base_addr) | pll->en_mask; + writel_relaxed(r, pll->base_addr); + + r = readl_relaxed(pll->base_addr + 16) | AUDPLL_TUNER_EN; + writel_relaxed(r, pll->base_addr + 16); + dsb(); + udelay(20); + + if (pll->flags & HAVE_RST_BAR) { + r = readl_relaxed(pll->base_addr) | RST_BAR_MASK; + writel_relaxed(r, pll->base_addr); + } + + mtk_clk_unlock(flags); + + return 0; +} + +static void clk_aud_pll_unprepare(struct clk_hw *hw) +{ + unsigned long flags = 0; + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 r; + + pr_debug("%s\n", __clk_get_name(hw->clk)); + + mtk_clk_lock(flags); + + if (pll->flags & HAVE_RST_BAR) { + r = readl_relaxed(pll->base_addr) & ~RST_BAR_MASK; + writel_relaxed(r, pll->base_addr); + } + r = readl_relaxed(pll->base_addr + 16) & ~AUDPLL_TUNER_EN; + writel_relaxed(r, pll->base_addr + 16); + + r = readl_relaxed(pll->base_addr) & ~PLL_BASE_EN; + writel_relaxed(r, pll->base_addr); + + r = readl_relaxed(pll->pwr_addr) | PLL_ISO_EN; + writel_relaxed(r, pll->pwr_addr); + + r = readl_relaxed(pll->pwr_addr) & ~PLL_PWR_ON; + writel_relaxed(r, pll->pwr_addr); + + mtk_clk_unlock(flags); +} + +#define AUD_PLL_POSTDIV_H 8 +#define AUD_PLL_POSTDIV_L 6 +#define AUD_PLL_POSTDIV_MASK GENMASK(AUD_PLL_POSTDIV_H, AUD_PLL_POSTDIV_L) +#define AUD_PLL_PCW_H 30 +#define AUD_PLL_PCW_L 0 +#define AUD_PLL_PCW_MASK GENMASK(AUD_PLL_PCW_H, AUD_PLL_PCW_L) + +static unsigned long clk_aud_pll_recalc_rate( + struct clk_hw *hw, + unsigned long parent_rate) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + + u32 con0 = readl_relaxed(pll->base_addr); + u32 con1 = readl_relaxed(pll->base_addr + 4); + + u32 vcodivsel = (con0 & PLL_VCODIV_MASK) >> PLL_VCODIV_L; + u32 prediv = (con0 & PLL_PREDIV_MASK) >> PLL_PREDIV_L; + u32 posdiv = (con0 & AUD_PLL_POSTDIV_MASK) >> AUD_PLL_POSTDIV_L; + u32 pcw = (con1 & AUD_PLL_PCW_MASK) >> AUD_PLL_PCW_L; + u32 pcwfbits = 24; + + u32 vco_freq; + unsigned long r; + + parent_rate = parent_rate ? parent_rate : 26000000; + vcodivsel = pll_vcodivsel_map[vcodivsel]; + prediv = pll_prediv_map[prediv]; + + vco_freq = calc_pll_vco_freq( + parent_rate, pcw, vcodivsel, prediv, pcwfbits); + r = vco_freq / pll_posdiv_map[posdiv]; + + pr_debug("%lu: %s\n", r, __clk_get_name(hw->clk)); + + return r; +} + +static long clk_aud_pll_round_rate( + struct clk_hw *hw, + unsigned long rate, + unsigned long *prate) +{ + int r; + u32 pcwfbits = 24; + u32 pcw = 0; + u32 postdiv = 0; + + pr_debug("%s, rate: %lu\n", __clk_get_name(hw->clk), rate); + + *prate = *prate ? *prate : 26000000; + rate = freq_limit(rate); + calc_pll_freq_cfg(&pcw, &postdiv, rate, *prate, pcwfbits); + + r = calc_pll_vco_freq(*prate, pcw, 1, 1, pcwfbits); + r = r / pll_posdiv_map[postdiv]; + return r; +} + +static void clk_aud_pll_set_rate_regs( + struct clk_hw *hw, + u32 pcw, + u32 postdiv_idx) +{ + unsigned long flags = 0; + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + void __iomem *con0_addr = pll->base_addr; + void __iomem *con1_addr = pll->base_addr + 4; + void __iomem *con4_addr = pll->base_addr + 16; + u32 con0; + u32 con1; + u32 pll_en; + + mtk_clk_lock(flags); + + con0 = readl_relaxed(con0_addr); + con1 = readl_relaxed(con1_addr); + + pll_en = con0 & PLL_BASE_EN; + + /* set postdiv */ + con0 &= ~AUD_PLL_POSTDIV_MASK; + con0 |= postdiv_idx << AUD_PLL_POSTDIV_L; + writel_relaxed(con0, con0_addr); + + /* set pcw */ + con1 &= ~AUD_PLL_PCW_MASK; + con1 |= pcw << AUD_PLL_PCW_L; + + if (pll_en) + con1 |= PLL_PCW_CHG; + + writel_relaxed(con1, con1_addr); + writel_relaxed(con1 + 1, con4_addr); + /* AUDPLL_CON4[30:0] (AUDPLL_TUNER_N_INFO) = (pcw + 1) */ + + if (pll_en) { + dsb(); + udelay(20); + } + + mtk_clk_unlock(flags); +} + +static int clk_aud_pll_set_rate( + struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + u32 pcwfbits = 24; + u32 pcw = 0; + u32 postdiv_idx = 0; + int r; + + parent_rate = parent_rate ? parent_rate : 26000000; + r = calc_pll_freq_cfg(&pcw, &postdiv_idx, rate, parent_rate, pcwfbits); + + pr_debug("%s, rate: %lu, pcw: %u, postdiv_idx: %u\n", + __clk_get_name(hw->clk), rate, pcw, postdiv_idx); + + if (r == 0) + clk_aud_pll_set_rate_regs(hw, pcw, postdiv_idx); + + return r; +} + +const struct clk_ops mtk_clk_aud_pll_ops = { + .is_enabled = clk_pll_is_enabled, + .prepare = clk_aud_pll_prepare, + .unprepare = clk_aud_pll_unprepare, + .recalc_rate = clk_aud_pll_recalc_rate, + .round_rate = clk_aud_pll_round_rate, + .set_rate = clk_aud_pll_set_rate, +}; + +#define TVD_PLL_POSTDIV_H 8 +#define TVD_PLL_POSTDIV_L 6 +#define TVD_PLL_POSTDIV_MASK GENMASK(TVD_PLL_POSTDIV_H, TVD_PLL_POSTDIV_L) +#define TVD_PLL_PCW_H 30 +#define TVD_PLL_PCW_L 0 +#define TVD_PLL_PCW_MASK GENMASK(TVD_PLL_PCW_H, TVD_PLL_PCW_L) + +static void clk_tvd_pll_set_rate_regs( + struct clk_hw *hw, + u32 pcw, + u32 postdiv_idx) +{ + unsigned long flags = 0; + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + void __iomem *con0_addr = pll->base_addr; + void __iomem *con1_addr = pll->base_addr + 4; + u32 con0; + u32 con1; + u32 pll_en; + + mtk_clk_lock(flags); + + con0 = readl_relaxed(con0_addr); + con1 = readl_relaxed(con1_addr); + + pll_en = con0 & PLL_BASE_EN; + + /* set postdiv */ + con0 &= ~TVD_PLL_POSTDIV_MASK; + con0 |= postdiv_idx << TVD_PLL_POSTDIV_L; + writel_relaxed(con0, con0_addr); + + /* set pcw */ + con1 &= ~TVD_PLL_PCW_MASK; + con1 |= pcw << TVD_PLL_PCW_L; + + if (pll_en) + con1 |= PLL_PCW_CHG; + + writel_relaxed(con1, con1_addr); + + if (pll_en) { + dsb(); + udelay(20); + } + + mtk_clk_unlock(flags); +} + +static int clk_tvd_pll_set_rate( + struct clk_hw *hw, + unsigned long rate, + unsigned long parent_rate) +{ + u32 pcwfbits = 24; + u32 pcw = 0; + u32 postdiv_idx = 0; + int r; + + parent_rate = parent_rate ? parent_rate : 26000000; + r = calc_pll_freq_cfg(&pcw, &postdiv_idx, rate, parent_rate, pcwfbits); + + pr_debug("%s, rate: %lu, pcw: %u, postdiv_idx: %u\n", + __clk_get_name(hw->clk), rate, pcw, postdiv_idx); + + if (r == 0) + clk_tvd_pll_set_rate_regs(hw, pcw, postdiv_idx); + + return r; +} + +const struct clk_ops mtk_clk_tvd_pll_ops = { + .is_enabled = clk_pll_is_enabled, + .prepare = clk_pll_prepare, + .unprepare = clk_pll_unprepare, + .recalc_rate = clk_aud_pll_recalc_rate, + .round_rate = clk_aud_pll_round_rate, + .set_rate = clk_tvd_pll_set_rate, +}; diff --git a/drivers/clk/mediatek/clk-mt8135-pll.h b/drivers/clk/mediatek/clk-mt8135-pll.h new file mode 100644 index 0000000..0628189 --- /dev/null +++ b/drivers/clk/mediatek/clk-mt8135-pll.h @@ -0,0 +1,28 @@ +/* + * 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_MT8135_PLL_H +#define __DRV_CLK_MT8135_PLL_H + +/* + * This is a private header file. DO NOT include it except clk-*.c. + */ + +extern const struct clk_ops mtk_clk_pll_ops; +extern const struct clk_ops mtk_clk_arm_pll_ops; +extern const struct clk_ops mtk_clk_lc_pll_ops; +extern const struct clk_ops mtk_clk_aud_pll_ops; +extern const struct clk_ops mtk_clk_tvd_pll_ops; + +#endif /* __DRV_CLK_MT8135_PLL_H */ diff --git a/drivers/clk/mediatek/clk-mt8135.c b/drivers/clk/mediatek/clk-mt8135.c new file mode 100644 index 0000000..fa8dd7d --- /dev/null +++ b/drivers/clk/mediatek/clk-mt8135.c @@ -0,0 +1,752 @@ +/* + * 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 "clk-mtk.h" +#include "clk-pll.h" +#include "clk-mt8135-pll.h" + +#include <dt-bindings/clock/mt8135-clk.h> + +/* + * platform clocks + */ + +/* ROOT */ +#define clk_null "clk_null" +#define clk26m "clk26m" +#define rtc32k "rtc32k" + +#define dsi0_lntc_dsiclk "dsi0_lntc_dsi" +#define hdmitx_clkdig_cts "hdmitx_dig_cts" +#define clkph_mck "clkph_mck" +#define cpum_tck_in "cpum_tck_in" + +/* PLL */ +#define armpll1 "armpll1" +#define armpll2 "armpll2" +#define mainpll "mainpll" +#define univpll "univpll" +#define mmpll "mmpll" +#define msdcpll "msdcpll" +#define tvdpll "tvdpll" +#define lvdspll "lvdspll" +#define audpll "audpll" +#define vdecpll "vdecpll" + +#define mainpll_806m "mainpll_806m" +#define mainpll_537p3m "mainpll_537p3m" +#define mainpll_322p4m "mainpll_322p4m" +#define mainpll_230p3m "mainpll_230p3m" + +#define univpll_624m "univpll_624m" +#define univpll_416m "univpll_416m" +#define univpll_249p6m "univpll_249p6m" +#define univpll_178p3m "univpll_178p3m" +#define univpll_48m "univpll_48m" + +/* DIV */ +#define mmpll_d2 "mmpll_d2" +#define mmpll_d3 "mmpll_d3" +#define mmpll_d5 "mmpll_d5" +#define mmpll_d7 "mmpll_d7" +#define mmpll_d4 "mmpll_d4" +#define mmpll_d6 "mmpll_d6" + +#define syspll_d2 "syspll_d2" +#define syspll_d4 "syspll_d4" +#define syspll_d6 "syspll_d6" +#define syspll_d8 "syspll_d8" +#define syspll_d10 "syspll_d10" +#define syspll_d12 "syspll_d12" +#define syspll_d16 "syspll_d16" +#define syspll_d24 "syspll_d24" +#define syspll_d3 "syspll_d3" +#define syspll_d2p5 "syspll_d2p5" +#define syspll_d5 "syspll_d5" +#define syspll_d3p5 "syspll_d3p5" + +#define univpll1_d2 "univpll1_d2" +#define univpll1_d4 "univpll1_d4" +#define univpll1_d6 "univpll1_d6" +#define univpll1_d8 "univpll1_d8" +#define univpll1_d10 "univpll1_d10" + +#define univpll2_d2 "univpll2_d2" +#define univpll2_d4 "univpll2_d4" +#define univpll2_d6 "univpll2_d6" +#define univpll2_d8 "univpll2_d8" + +#define univpll_d3 "univpll_d3" +#define univpll_d5 "univpll_d5" +#define univpll_d7 "univpll_d7" +#define univpll_d10 "univpll_d10" +#define univpll_d26 "univpll_d26" + +#define apll_ck "apll" +#define apll_d4 "apll_d4" +#define apll_d8 "apll_d8" +#define apll_d16 "apll_d16" +#define apll_d24 "apll_d24" + +#define lvdspll_d2 "lvdspll_d2" +#define lvdspll_d4 "lvdspll_d4" +#define lvdspll_d8 "lvdspll_d8" + +#define lvdstx_clkdig_cts "lvdstx_dig_cts" +#define vpll_dpix_ck "vpll_dpix_ck" +#define tvhdmi_h_ck "tvhdmi_h_ck" +#define hdmitx_clkdig_d2 "hdmitx_dig_d2" +#define hdmitx_clkdig_d3 "hdmitx_dig_d3" +#define tvhdmi_d2 "tvhdmi_d2" +#define tvhdmi_d4 "tvhdmi_d4" +#define mempll_mck_d4 "mempll_mck_d4" + +/* TOP */ +#define axi_sel "axi_sel" +#define smi_sel "smi_sel" +#define mfg_sel "mfg_sel" +#define irda_sel "irda_sel" +#define cam_sel "cam_sel" +#define aud_intbus_sel "aud_intbus_sel" +#define jpg_sel "jpg_sel" +#define disp_sel "disp_sel" +#define msdc30_1_sel "msdc30_1_sel" +#define msdc30_2_sel "msdc30_2_sel" +#define msdc30_3_sel "msdc30_3_sel" +#define msdc30_4_sel "msdc30_4_sel" +#define usb20_sel "usb20_sel" +#define venc_sel "venc_sel" +#define spi_sel "spi_sel" +#define uart_sel "uart_sel" +#define mem_sel "mem_sel" +#define camtg_sel "camtg_sel" +#define audio_sel "audio_sel" +#define fix_sel "fix_sel" +#define vdec_sel "vdec_sel" +#define ddrphycfg_sel "ddrphycfg_sel" +#define dpilvds_sel "dpilvds_sel" +#define pmicspi_sel "pmicspi_sel" +#define msdc30_0_sel "msdc30_0_sel" +#define smi_mfg_as_sel "smi_mfg_as_sel" +#define gcpu_sel "gcpu_sel" +#define dpi1_sel "dpi1_sel" +#define cci_sel "cci_sel" +#define apll_sel "apll_sel" +#define hdmipll_sel "hdmipll_sel" + +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, \ + } + +static void __init init_factors(struct mtk_fixed_factor *clks, int num, + struct clk_onecell_data *clk_data) +{ + int i; + struct clk *clk; + + for (i = 0; i < num; i++) { + struct mtk_fixed_factor *ff = &clks[i]; + + clk = clk_register_fixed_factor(NULL, ff->name, ff->parent_name, + 0, 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; + + pr_debug("factor %3d: %s\n", i, ff->name); + } +} + +static struct mtk_fixed_factor root_clk_alias[] __initdata = { + 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 void __init init_clk_root_alias(struct clk_onecell_data *clk_data) +{ + init_factors(root_clk_alias, ARRAY_SIZE(root_clk_alias), clk_data); +} + +static struct mtk_fixed_factor top_divs[] __initdata = { + 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 void __init init_clk_top_div(struct clk_onecell_data *clk_data) +{ + init_factors(top_divs, ARRAY_SIZE(top_divs), clk_data); +} + +static const char *axi_parents[] __initconst = { + clk26m, + syspll_d3, + syspll_d4, + syspll_d6, + univpll_d5, + univpll2_d2, + syspll_d3p5}; + +static const char *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 *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 *irda_parents[] __initconst = { + clk26m, + univpll2_d8, + univpll1_d6}; + +static const char *cam_parents[] __initconst = { + clk26m, + syspll_d3, + syspll_d3p5, + syspll_d4, + univpll_d5, + univpll2_d2, + univpll_d7, + univpll1_d4}; + +static const char *aud_intbus_parents[] __initconst = { + clk26m, + syspll_d6, + univpll_d10}; + +static const char *jpg_parents[] __initconst = { + clk26m, + syspll_d5, + syspll_d4, + syspll_d3, + univpll_d7, + univpll2_d2, + univpll_d5}; + +static const char *disp_parents[] __initconst = { + clk26m, + syspll_d3p5, + syspll_d3, + univpll2_d2, + univpll_d5, + univpll1_d2, + lvdspll, + vdecpll}; + +static const char *msdc30_parents[] __initconst = { + clk26m, + syspll_d6, + syspll_d5, + univpll1_d4, + univpll2_d4, + msdcpll}; + +static const char *usb20_parents[] __initconst = { + clk26m, + univpll2_d6, + univpll1_d10}; + +static const char *venc_parents[] __initconst = { + clk26m, + syspll_d3, + syspll_d8, + univpll_d5, + univpll1_d6, + mmpll_d4, + mmpll_d5, + mmpll_d6}; + +static const char *spi_parents[] __initconst = { + clk26m, + syspll_d6, + syspll_d8, + syspll_d10, + univpll1_d6, + univpll1_d8}; + +static const char *uart_parents[] __initconst = { + clk26m, + univpll2_d8}; + +static const char *mem_parents[] __initconst = { + clk26m, + clkph_mck}; + +static const char *camtg_parents[] __initconst = { + clk26m, + univpll_d26, + univpll1_d6, + syspll_d16, + syspll_d8}; + +static const char *audio_parents[] __initconst = { + clk26m, + syspll_d24}; + +static const char *fix_parents[] __initconst = { + rtc32k, + clk26m, + univpll_d5, + univpll_d7, + univpll1_d2, + univpll1_d4, + univpll1_d6, + univpll1_d8}; + +static const char *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 *ddrphycfg_parents[] __initconst = { + clk26m, + axi_sel, + syspll_d12}; + +static const char *dpilvds_parents[] __initconst = { + clk26m, + lvdspll, + lvdspll_d2, + lvdspll_d4, + lvdspll_d8}; + +static const char *pmicspi_parents[] __initconst = { + clk26m, + univpll2_d6, + syspll_d8, + syspll_d10, + univpll1_d10, + mempll_mck_d4, + univpll_d26, + syspll_d24}; + +static const char *smi_mfg_as_parents[] __initconst = { + clk26m, + smi_sel, + mfg_sel, + mem_sel}; + +static const char *gcpu_parents[] __initconst = { + clk26m, + syspll_d4, + univpll_d7, + syspll_d5, + syspll_d6}; + +static const char *dpi1_parents[] __initconst = { + clk26m, + tvhdmi_h_ck, + tvhdmi_d2, + tvhdmi_d4}; + +static const char *cci_parents[] __initconst = { + clk26m, + mainpll_537p3m, + univpll_d3, + syspll_d2p5, + syspll_d3, + syspll_d5}; + +static const char *apll_parents[] __initconst = { + clk26m, + apll_ck, + apll_d4, + apll_d8, + apll_d16, + apll_d24}; + +static const char *hdmipll_parents[] __initconst = { + clk26m, + hdmitx_clkdig_cts, + hdmitx_clkdig_d2, + hdmitx_clkdig_d3}; + +struct mtk_mux { + int id; + const char *name; + u32 reg; + int shift; + int width; + int gate; + const char **parent_names; + int num_parents; +}; + +#define MUX(_id, _name, _parents, _reg, _shift, _width, _gate) { \ + .id = _id, \ + .name = _name, \ + .reg = _reg, \ + .shift = _shift, \ + .width = _width, \ + .gate = _gate, \ + .parent_names = (const char **)_parents, \ + .num_parents = ARRAY_SIZE(_parents), \ + } + +static struct mtk_mux top_muxes[] __initdata = { + /* CLK_CFG_0 */ + MUX(TOP_AXI_SEL, axi_sel, axi_parents, + 0x0140, 0, 3, INVALID_MUX_GATE_BIT), + MUX(TOP_SMI_SEL, smi_sel, smi_parents, 0x0140, 8, 4, 15), + MUX(TOP_MFG_SEL, mfg_sel, mfg_parents, 0x0140, 16, 4, 23), + MUX(TOP_IRDA_SEL, irda_sel, irda_parents, 0x0140, 24, 2, 31), + /* CLK_CFG_1 */ + MUX(TOP_CAM_SEL, cam_sel, cam_parents, 0x0144, 0, 3, 7), + MUX(TOP_AUD_INTBUS_SEL, aud_intbus_sel, aud_intbus_parents, + 0x0144, 8, 2, 15), + MUX(TOP_JPG_SEL, jpg_sel, jpg_parents, 0x0144, 16, 3, 23), + MUX(TOP_DISP_SEL, disp_sel, disp_parents, 0x0144, 24, 3, 31), + /* CLK_CFG_2 */ + MUX(TOP_MSDC30_1_SEL, msdc30_1_sel, msdc30_parents, 0x0148, 0, 3, 7), + MUX(TOP_MSDC30_2_SEL, msdc30_2_sel, msdc30_parents, 0x0148, 8, 3, 15), + MUX(TOP_MSDC30_3_SEL, msdc30_3_sel, msdc30_parents, 0x0148, 16, 3, 23), + MUX(TOP_MSDC30_4_SEL, msdc30_4_sel, msdc30_parents, 0x0148, 24, 3, 31), + /* CLK_CFG_3 */ + MUX(TOP_USB20_SEL, usb20_sel, usb20_parents, 0x014c, 0, 2, 7), + /* CLK_CFG_4 */ + MUX(TOP_VENC_SEL, venc_sel, venc_parents, 0x0150, 8, 3, 15), + MUX(TOP_SPI_SEL, spi_sel, spi_parents, 0x0150, 16, 3, 23), + MUX(TOP_UART_SEL, uart_sel, uart_parents, 0x0150, 24, 2, 31), + /* CLK_CFG_6 */ + MUX(TOP_MEM_SEL, mem_sel, mem_parents, 0x0158, 0, 2, 7), + MUX(TOP_CAMTG_SEL, camtg_sel, camtg_parents, 0x0158, 8, 3, 15), + MUX(TOP_AUDIO_SEL, audio_sel, audio_parents, 0x0158, 24, 2, 31), + /* CLK_CFG_7 */ + MUX(TOP_FIX_SEL, fix_sel, fix_parents, 0x015c, 0, 3, 7), + MUX(TOP_VDEC_SEL, vdec_sel, vdec_parents, 0x015c, 8, 4, 15), + MUX(TOP_DDRPHYCFG_SEL, ddrphycfg_sel, ddrphycfg_parents, + 0x015c, 16, 2, 23), + MUX(TOP_DPILVDS_SEL, dpilvds_sel, dpilvds_parents, 0x015c, 24, 3, 31), + /* CLK_CFG_8 */ + MUX(TOP_PMICSPI_SEL, pmicspi_sel, pmicspi_parents, 0x0164, 0, 3, 7), + MUX(TOP_MSDC30_0_SEL, msdc30_0_sel, msdc30_parents, 0x0164, 8, 3, 15), + MUX(TOP_SMI_MFG_AS_SEL, smi_mfg_as_sel, smi_mfg_as_parents, + 0x0164, 16, 2, 23), + MUX(TOP_GCPU_SEL, gcpu_sel, gcpu_parents, 0x0164, 24, 3, 31), + /* CLK_CFG_9 */ + MUX(TOP_DPI1_SEL, dpi1_sel, dpi1_parents, 0x0168, 0, 2, 7), + MUX(TOP_CCI_SEL, cci_sel, cci_parents, 0x0168, 8, 3, 15), + MUX(TOP_APLL_SEL, apll_sel, apll_parents, 0x0168, 16, 3, 23), + MUX(TOP_HDMIPLL_SEL, hdmipll_sel, hdmipll_parents, 0x0168, 24, 2, 31), +}; + +static void __init 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++) { + struct mtk_mux *mux = &top_muxes[i]; + + clk = mtk_clk_register_mux(mux->name, + mux->parent_names, mux->num_parents, + top_base + mux->reg, mux->shift, mux->width, mux->gate); + + 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; + + pr_debug("mux %3d: %s\n", i, mux->name); + } +} + +struct mtk_pll { + int id; + const char *name; + const char *parent_name; + u32 reg; + u32 pwr_reg; + u32 en_mask; + unsigned int flags; + const struct clk_ops *ops; +}; + +#define PLL(_id, _name, _parent, _reg, _pwr_reg, _en_mask, _flags, _ops) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .reg = _reg, \ + .pwr_reg = _pwr_reg, \ + .en_mask = _en_mask, \ + .flags = _flags, \ + .ops = _ops, \ + } + +static struct mtk_pll plls[] __initdata = { + PLL(APMIXED_ARMPLL1, armpll1, clk26m, 0x0200, 0x0218, + 0x80000001, HAVE_PLL_HP, &mtk_clk_arm_pll_ops), + PLL(APMIXED_ARMPLL2, armpll2, clk26m, 0x02cc, 0x02e4, + 0x80000001, HAVE_PLL_HP, &mtk_clk_arm_pll_ops), + PLL(APMIXED_MAINPLL, mainpll, clk26m, 0x021c, 0x0234, + 0xf0000001, HAVE_PLL_HP | HAVE_RST_BAR | PLL_AO, + &mtk_clk_pll_ops), + PLL(APMIXED_UNIVPLL, univpll, clk26m, 0x0238, 0x0250, + 0xf3000001, HAVE_RST_BAR | HAVE_FIX_FRQ | PLL_AO, + &mtk_clk_lc_pll_ops), + PLL(APMIXED_MMPLL, mmpll, clk26m, 0x0254, 0x026c, + 0xf0000001, HAVE_PLL_HP | HAVE_RST_BAR, &mtk_clk_pll_ops), + PLL(APMIXED_MSDCPLL, msdcpll, clk26m, 0x0278, 0x0290, + 0x80000001, HAVE_PLL_HP, &mtk_clk_pll_ops), + PLL(APMIXED_TVDPLL, tvdpll, clk26m, 0x0294, 0x02ac, + 0x80000001, HAVE_PLL_HP, &mtk_clk_tvd_pll_ops), + PLL(APMIXED_LVDSPLL, lvdspll, clk26m, 0x02b0, 0x02c8, + 0x80000001, HAVE_PLL_HP, &mtk_clk_pll_ops), + PLL(APMIXED_AUDPLL, audpll, clk26m, 0x02e8, 0x0300, + 0x80000001, 0, &mtk_clk_aud_pll_ops), + PLL(APMIXED_VDECPLL, vdecpll, clk26m, 0x0304, 0x031c, + 0x80000001, HAVE_PLL_HP, &mtk_clk_pll_ops), +}; + +static void __init init_clk_apmixedsys(void __iomem *apmixed_base, + struct clk_onecell_data *clk_data) +{ + int i; + struct clk *clk; + + for (i = 0; i < ARRAY_SIZE(plls); i++) { + struct mtk_pll *pll = &plls[i]; + + clk = mtk_clk_register_pll(pll->name, pll->parent_name, + apmixed_base + pll->reg, + apmixed_base + pll->pwr_reg, + pll->en_mask, pll->flags, pll->ops); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + pll->name, PTR_ERR(clk)); + continue; + } + + if (clk_data) + clk_data->clks[pll->id] = clk; + + pr_debug("pll %3d: %s\n", i, pll->name); + } +} + +/* + * device tree support + */ + +static struct clk_onecell_data *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(struct clk *), GFP_KERNEL); + if (!clk_data->clks) { + kfree(clk_data); + return NULL; + } + + clk_data->clk_num = clk_num; + + for (i = 0; i < clk_num; ++i) + clk_data->clks[i] = ERR_PTR(-ENOENT); + + return clk_data; +} + +static void __init mtk_topckgen_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + void __iomem *base; + int r; + + pr_debug("%s: %s\n", __func__, node->name); + + base = of_iomap(node, 0); + if (!base) { + pr_err("ioremap topckgen failed\n"); + return; + } + + clk_data = alloc_clk_data(TOP_NR_CLK); + + init_clk_root_alias(clk_data); + init_clk_top_div(clk_data); + init_clk_topckgen(base, clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("could not register clock provide\n"); +} +CLK_OF_DECLARE(mtk_topckgen, "mediatek,mt8135-topckgen", mtk_topckgen_init); + +static void __init mtk_apmixedsys_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + void __iomem *base; + int r; + + pr_debug("%s: %s\n", __func__, node->name); + + base = of_iomap(node, 0); + if (!base) { + pr_err("ioremap apmixedsys failed\n"); + return; + } + + clk_data = alloc_clk_data(APMIXED_NR_CLK); + + init_clk_apmixedsys(base, clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("could not register clock provide\n"); +} +CLK_OF_DECLARE(mtk_apmixedsys, "mediatek,mt8135-apmixedsys", + mtk_apmixedsys_init); diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c new file mode 100644 index 0000000..41a12d3 --- /dev/null +++ b/drivers/clk/mediatek/clk-mtk.c @@ -0,0 +1,93 @@ +/* + * 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" + +static DEFINE_SPINLOCK(clk_ops_lock); + +spinlock_t *get_mtk_clk_lock(void) +{ + return &clk_ops_lock; +} + +/* + * clk_mux + */ + +struct clk *mtk_clk_register_mux( + const char *name, + const char **parent_names, + u8 num_parents, + void __iomem *base_addr, + u8 shift, + u8 width, + u8 gate_bit) +{ + struct clk *clk; + struct clk_mux *mux; + struct clk_gate *gate = NULL; + struct clk_hw *gate_hw = NULL; + const struct clk_ops *gate_ops = NULL; + u32 mask = BIT(width) - 1; + + pr_debug("name: %s, num_parents: %d, gate_bit: %d\n", + name, (int)num_parents, (int)gate_bit); + + mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL); + if (!mux) + return ERR_PTR(-ENOMEM); + + mux->reg = base_addr; + mux->mask = mask; + mux->shift = shift; + mux->flags = 0; + mux->lock = &clk_ops_lock; + + if (gate_bit <= MAX_MUX_GATE_BIT) { + gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL); + if (!gate) { + kfree(mux); + return ERR_PTR(-ENOMEM); + } + + gate->reg = base_addr; + gate->bit_idx = gate_bit; + gate->flags = CLK_GATE_SET_TO_DISABLE; + gate->lock = &clk_ops_lock; + + gate_hw = &gate->hw; + gate_ops = &clk_gate_ops; + } + + clk = clk_register_composite(NULL, name, parent_names, num_parents, + &mux->hw, &clk_mux_ops, + NULL, NULL, + gate_hw, gate_ops, + CLK_IGNORE_UNUSED); + + if (IS_ERR(clk)) { + kfree(gate); + kfree(mux); + } + + return clk; +} diff --git a/drivers/clk/mediatek/clk-mtk.h b/drivers/clk/mediatek/clk-mtk.h new file mode 100644 index 0000000..b69245d --- /dev/null +++ b/drivers/clk/mediatek/clk-mtk.h @@ -0,0 +1,47 @@ +/* + * 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 + +/* + * This is a private header file. DO NOT include it except clk-*.c. + */ + +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> + +#define CLK_DEBUG 0 +#define DUMMY_REG_TEST 0 + +extern spinlock_t *get_mtk_clk_lock(void); + +#define mtk_clk_lock(flags) spin_lock_irqsave(get_mtk_clk_lock(), flags) +#define mtk_clk_unlock(flags) \ + spin_unlock_irqrestore(get_mtk_clk_lock(), flags) + +#define MAX_MUX_GATE_BIT 31 +#define INVALID_MUX_GATE_BIT (MAX_MUX_GATE_BIT + 1) + +struct clk *mtk_clk_register_mux( + const char *name, + const char **parent_names, + u8 num_parents, + void __iomem *base_addr, + u8 shift, + u8 width, + u8 gate_bit); + +#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..864cc60 --- /dev/null +++ b/drivers/clk/mediatek/clk-pll.c @@ -0,0 +1,63 @@ +/* + * 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/io.h> +#include <linux/slab.h> +#include <linux/clkdev.h> + +#include "clk-mtk.h" +#include "clk-pll.h" + +/* + * clk_pll + */ + +struct clk *mtk_clk_register_pll( + const char *name, + const char *parent_name, + u32 *base_addr, + u32 *pwr_addr, + u32 en_mask, + u32 flags, + const struct clk_ops *ops) +{ + struct mtk_clk_pll *pll; + struct clk_init_data init; + struct clk *clk; + + pr_debug("name: %s\n", name); + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + pll->base_addr = base_addr; + pll->pwr_addr = pwr_addr; + pll->en_mask = en_mask; + pll->flags = flags; + pll->hw.init = &init; + + init.name = name; + init.ops = ops; + init.flags = CLK_IGNORE_UNUSED; + init.parent_names = &parent_name; + init.num_parents = 1; + + clk = clk_register(NULL, &pll->hw); + + if (IS_ERR(clk)) + kfree(pll); + + return clk; +} diff --git a/drivers/clk/mediatek/clk-pll.h b/drivers/clk/mediatek/clk-pll.h new file mode 100644 index 0000000..cb7f335 --- /dev/null +++ b/drivers/clk/mediatek/clk-pll.h @@ -0,0 +1,50 @@ +/* + * 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_PLL_H +#define __DRV_CLK_PLL_H + +/* + * This is a private header file. DO NOT include it except clk-*.c. + */ + +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> + +struct mtk_clk_pll { + struct clk_hw hw; + void __iomem *base_addr; + void __iomem *pwr_addr; + u32 en_mask; + u32 flags; +}; + +#define to_mtk_clk_pll(_hw) container_of(_hw, struct mtk_clk_pll, hw) + +#define HAVE_RST_BAR BIT(0) +#define HAVE_PLL_HP BIT(1) +#define HAVE_FIX_FRQ BIT(2) +#define PLL_AO BIT(3) + +struct clk *mtk_clk_register_pll( + const char *name, + const char *parent_name, + u32 *base_addr, + u32 *pwr_addr, + u32 en_mask, + u32 flags, + const struct clk_ops *ops); + +#endif /* __DRV_CLK_PLL_H */ diff --git a/include/dt-bindings/clock/mt8135-clk.h b/include/dt-bindings/clock/mt8135-clk.h new file mode 100644 index 0000000..674fd1a --- /dev/null +++ b/include/dt-bindings/clock/mt8135-clk.h @@ -0,0 +1,128 @@ +/* +* 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 + +#endif /* _DT_BINDINGS_CLK_MT8135_H */ -- 1.8.1.1.dirty ^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 2/4] clk: Add INFRA and PERI clocks for Mediatek MT8135. 2014-11-28 11:34 ` James Liao @ 2014-11-28 11:34 ` James Liao -1 siblings, 0 replies; 15+ messages in thread From: James Liao @ 2014-11-28 11:34 UTC (permalink / raw) To: linux-arm-kernel This patch adds clock gates support for Mediatek SoCs, and adds clocks of INFRA sys and PERI sys for MT8135. Signed-off-by: James Liao <jamesjj.liao@mediatek.com> --- drivers/clk/mediatek/Makefile | 2 +- drivers/clk/mediatek/clk-gate.c | 151 +++++++++++++++++++ drivers/clk/mediatek/clk-gate.h | 48 ++++++ drivers/clk/mediatek/clk-mt8135.c | 260 +++++++++++++++++++++++++++++++++ include/dt-bindings/clock/mt8135-clk.h | 62 ++++++++ 5 files changed, 522 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/mediatek/clk-gate.c create mode 100644 drivers/clk/mediatek/clk-gate.h diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile index 3c3432b..96a7044 100644 --- a/drivers/clk/mediatek/Makefile +++ b/drivers/clk/mediatek/Makefile @@ -1,2 +1,2 @@ -obj-y += clk-mtk.o clk-pll.o +obj-y += clk-mtk.o clk-pll.o clk-gate.o obj-y += clk-mt8135.o clk-mt8135-pll.o diff --git a/drivers/clk/mediatek/clk-gate.c b/drivers/clk/mediatek/clk-gate.c new file mode 100644 index 0000000..8f9a9ea --- /dev/null +++ b/drivers/clk/mediatek/clk-gate.c @@ -0,0 +1,151 @@ +/* + * 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" + +/* + * clk_gate + */ + +static void cg_set_mask(struct mtk_clk_gate *cg, u32 mask) +{ + u32 r; + + if (cg->flags & CLK_GATE_NO_SETCLR_REG) { + r = readl_relaxed(cg->sta_addr) | mask; + writel_relaxed(r, cg->sta_addr); + } else + writel_relaxed(mask, cg->set_addr); +} + +static void cg_clr_mask(struct mtk_clk_gate *cg, u32 mask) +{ + u32 r; + + if (cg->flags & CLK_GATE_NO_SETCLR_REG) { + r = readl_relaxed(cg->sta_addr) & ~mask; + writel_relaxed(r, cg->sta_addr); + } else + writel_relaxed(mask, cg->clr_addr); +} + +static int cg_enable(struct clk_hw *hw) +{ + unsigned long flags = 0; + struct mtk_clk_gate *cg = to_clk_gate(hw); + u32 mask = BIT(cg->bit); + + pr_debug("%s, bit: %u\n", __clk_get_name(hw->clk), cg->bit); + + mtk_clk_lock(flags); + + if (cg->flags & CLK_GATE_INVERSE) + cg_set_mask(cg, mask); + else + cg_clr_mask(cg, mask); + + mtk_clk_unlock(flags); + + return 0; +} + +static void cg_disable(struct clk_hw *hw) +{ + unsigned long flags = 0; + struct mtk_clk_gate *cg = to_clk_gate(hw); + u32 mask = BIT(cg->bit); + + pr_debug("%s, bit: %u\n", __clk_get_name(hw->clk), cg->bit); + + mtk_clk_lock(flags); + + if (cg->flags & CLK_GATE_INVERSE) + cg_clr_mask(cg, mask); + else + cg_set_mask(cg, mask); + + mtk_clk_unlock(flags); +} + +static int cg_is_enabled(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_clk_gate(hw); + u32 mask; + u32 val; + int r; + + mask = BIT(cg->bit); + val = mask & readl(cg->sta_addr); + + r = (cg->flags & CLK_GATE_INVERSE) ? (val != 0) : (val == 0); + + pr_debug("%d, %s, bit[%d]\n", r, __clk_get_name(hw->clk), (int)cg->bit); + + return r; +} + +static const struct clk_ops mtk_clk_gate_ops = { + .is_enabled = cg_is_enabled, + .enable = cg_enable, + .disable = cg_disable, +}; + +struct clk *mtk_clk_register_gate( + const char *name, + const char *parent_name, + void __iomem *set_addr, + void __iomem *clr_addr, + void __iomem *sta_addr, + u8 bit, + u32 flags) +{ + struct mtk_clk_gate *cg; + struct clk *clk; + struct clk_init_data init; + + pr_debug("name: %s, bit: %d\n", name, (int)bit); + + cg = kzalloc(sizeof(*cg), GFP_KERNEL); + if (!cg) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.flags = CLK_IGNORE_UNUSED; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + init.ops = &mtk_clk_gate_ops; + + cg->set_addr = set_addr; + cg->clr_addr = clr_addr; + cg->sta_addr = sta_addr; + cg->bit = bit; + cg->flags = flags; + + 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..0d71aad --- /dev/null +++ b/drivers/clk/mediatek/clk-gate.h @@ -0,0 +1,48 @@ +/* + * 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 + +/* + * This is a private header file. DO NOT include it except clk-*.c. + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> + +struct mtk_clk_gate { + struct clk_hw hw; + void __iomem *set_addr; + void __iomem *clr_addr; + void __iomem *sta_addr; + u8 bit; + u32 flags; +}; + +#define to_clk_gate(_hw) container_of(_hw, struct mtk_clk_gate, hw) + +#define CLK_GATE_INVERSE BIT(0) +#define CLK_GATE_NO_SETCLR_REG BIT(1) + +struct clk *mtk_clk_register_gate( + const char *name, + const char *parent_name, + void __iomem *set_addr, + void __iomem *clr_addr, + void __iomem *sta_addr, + u8 bit, + u32 flags); + +#endif /* __DRV_CLK_GATE_H */ diff --git a/drivers/clk/mediatek/clk-mt8135.c b/drivers/clk/mediatek/clk-mt8135.c index fa8dd7d..2496e54 100644 --- a/drivers/clk/mediatek/clk-mt8135.c +++ b/drivers/clk/mediatek/clk-mt8135.c @@ -18,6 +18,7 @@ #include "clk-mtk.h" #include "clk-pll.h" +#include "clk-gate.h" #include "clk-mt8135-pll.h" #include <dt-bindings/clock/mt8135-clk.h> @@ -149,6 +150,66 @@ #define apll_sel "apll_sel" #define hdmipll_sel "hdmipll_sel" +/* PERI0 */ +#define i2c5_ck "i2c5_ck" +#define i2c4_ck "i2c4_ck" +#define i2c3_ck "i2c3_ck" +#define i2c2_ck "i2c2_ck" +#define i2c1_ck "i2c1_ck" +#define i2c0_ck "i2c0_ck" +#define uart3_ck "uart3_ck" +#define uart2_ck "uart2_ck" +#define uart1_ck "uart1_ck" +#define uart0_ck "uart0_ck" +#define irda_ck "irda_ck" +#define nli_ck "nli_ck" +#define md_hif_ck "md_hif_ck" +#define ap_hif_ck "ap_hif_ck" +#define msdc30_3_ck "msdc30_3_ck" +#define msdc30_2_ck "msdc30_2_ck" +#define msdc30_1_ck "msdc30_1_ck" +#define msdc20_2_ck "msdc20_2_ck" +#define msdc20_1_ck "msdc20_1_ck" +#define ap_dma_ck "ap_dma_ck" +#define usb1_ck "usb1_ck" +#define usb0_ck "usb0_ck" +#define pwm_ck "pwm_ck" +#define pwm7_ck "pwm7_ck" +#define pwm6_ck "pwm6_ck" +#define pwm5_ck "pwm5_ck" +#define pwm4_ck "pwm4_ck" +#define pwm3_ck "pwm3_ck" +#define pwm2_ck "pwm2_ck" +#define pwm1_ck "pwm1_ck" +#define therm_ck "therm_ck" +#define nfi_ck "nfi_ck" + +/* PERI1 */ +#define usbslv_ck "usbslv_ck" +#define usb1_mcu_ck "usb1_mcu_ck" +#define usb0_mcu_ck "usb0_mcu_ck" +#define gcpu_ck "gcpu_ck" +#define fhctl_ck "fhctl_ck" +#define spi1_ck "spi1_ck" +#define auxadc_ck "auxadc_ck" +#define peri_pwrap_ck "peri_pwrap_ck" +#define i2c6_ck "i2c6_ck" + +/* INFRA */ +#define pmic_wrap_ck "pmic_wrap_ck" +#define pmicspi_ck "pmicspi_ck" +#define ccif1_ap_ctrl "ccif1_ap_ctrl" +#define ccif0_ap_ctrl "ccif0_ap_ctrl" +#define kp_ck "kp_ck" +#define cpum_ck "cpum_ck" +#define m4u_ck "m4u_ck" +#define mfgaxi_ck "mfgaxi_ck" +#define devapc_ck "devapc_ck" +#define audio_ck "audio_ck" +#define mfg_bus_ck "mfg_bus_ck" +#define smi_ck "smi_ck" +#define dbgclk_ck "dbgclk_ck" + struct mtk_fixed_factor { int id; const char *name; @@ -673,6 +734,157 @@ static void __init init_clk_apmixedsys(void __iomem *apmixed_base, } } +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; + struct mtk_gate_regs *regs; + int shift; + u32 flags; +}; + +#define GATE(_id, _name, _parent, _regs, _shift, _flags) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &_regs, \ + .shift = _shift, \ + .flags = _flags, \ + } + +static void __init init_clk_gates( + void __iomem *reg_base, + struct mtk_gate *clks, int num, + struct clk_onecell_data *clk_data) +{ + int i; + struct clk *clk; + + for (i = 0; i < num; i++) { + struct mtk_gate *gate = &clks[i]; + + clk = mtk_clk_register_gate(gate->name, gate->parent_name, + reg_base + gate->regs->set_ofs, + reg_base + gate->regs->clr_ofs, + reg_base + gate->regs->sta_ofs, + gate->shift, gate->flags); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + gate->name, PTR_ERR(clk)); + continue; + } + + if (clk_data) + clk_data->clks[gate->id] = clk; + + pr_debug("gate %3d: %s\n", i, gate->name); + } +} + +static struct mtk_gate_regs infra_cg_regs = { + .set_ofs = 0x0040, + .clr_ofs = 0x0044, + .sta_ofs = 0x0048, +}; + +static struct mtk_gate infra_clks[] __initdata = { + GATE(INFRA_PMIC_WRAP_CK, pmic_wrap_ck, axi_sel, infra_cg_regs, 23, 0), + GATE(INFRA_PMICSPI_CK, pmicspi_ck, pmicspi_sel, infra_cg_regs, 22, 0), + GATE(INFRA_CCIF1_AP_CTRL, ccif1_ap_ctrl, axi_sel, infra_cg_regs, 21, 0), + GATE(INFRA_CCIF0_AP_CTRL, ccif0_ap_ctrl, axi_sel, infra_cg_regs, 20, 0), + GATE(INFRA_KP_CK, kp_ck, axi_sel, infra_cg_regs, 16, 0), + GATE(INFRA_CPUM_CK, cpum_ck, cpum_tck_in, infra_cg_regs, 15, 0), + GATE(INFRA_M4U_CK, m4u_ck, mem_sel, infra_cg_regs, 8, 0), + GATE(INFRA_MFGAXI_CK, mfgaxi_ck, axi_sel, infra_cg_regs, 7, 0), + GATE(INFRA_DEVAPC_CK, devapc_ck, axi_sel, infra_cg_regs, 6, + CLK_GATE_INVERSE), + GATE(INFRA_AUDIO_CK, audio_ck, aud_intbus_sel, infra_cg_regs, 5, 0), + GATE(INFRA_MFG_BUS_CK, mfg_bus_ck, axi_sel, infra_cg_regs, 2, 0), + GATE(INFRA_SMI_CK, smi_ck, smi_sel, infra_cg_regs, 1, 0), + GATE(INFRA_DBGCLK_CK, dbgclk_ck, axi_sel, infra_cg_regs, 0, 0), +}; + +static void __init init_clk_infrasys(void __iomem *infrasys_base, + struct clk_onecell_data *clk_data) +{ + pr_debug("init infrasys gates:\n"); + init_clk_gates(infrasys_base, infra_clks, ARRAY_SIZE(infra_clks), + clk_data); +} + +static struct mtk_gate_regs peri0_cg_regs = { + .set_ofs = 0x0008, + .clr_ofs = 0x0010, + .sta_ofs = 0x0018, +}; + +static struct mtk_gate_regs peri1_cg_regs = { + .set_ofs = 0x000c, + .clr_ofs = 0x0014, + .sta_ofs = 0x001c, +}; + +static struct mtk_gate peri_clks[] __initdata = { + /* PERI0 */ + GATE(PERI_I2C5_CK, i2c5_ck, axi_sel, peri0_cg_regs, 31, 0), + GATE(PERI_I2C4_CK, i2c4_ck, axi_sel, peri0_cg_regs, 30, 0), + GATE(PERI_I2C3_CK, i2c3_ck, axi_sel, peri0_cg_regs, 29, 0), + GATE(PERI_I2C2_CK, i2c2_ck, axi_sel, peri0_cg_regs, 28, 0), + GATE(PERI_I2C1_CK, i2c1_ck, axi_sel, peri0_cg_regs, 27, 0), + GATE(PERI_I2C0_CK, i2c0_ck, axi_sel, peri0_cg_regs, 26, 0), + GATE(PERI_UART3_CK, uart3_ck, axi_sel, peri0_cg_regs, 25, 0), + GATE(PERI_UART2_CK, uart2_ck, axi_sel, peri0_cg_regs, 24, 0), + GATE(PERI_UART1_CK, uart1_ck, axi_sel, peri0_cg_regs, 23, 0), + GATE(PERI_UART0_CK, uart0_ck, axi_sel, peri0_cg_regs, 22, 0), + GATE(PERI_IRDA_CK, irda_ck, irda_sel, peri0_cg_regs, 21, 0), + GATE(PERI_NLI_CK, nli_ck, axi_sel, peri0_cg_regs, 20, 0), + GATE(PERI_MD_HIF_CK, md_hif_ck, axi_sel, peri0_cg_regs, 19, 0), + GATE(PERI_AP_HIF_CK, ap_hif_ck, axi_sel, peri0_cg_regs, 18, 0), + GATE(PERI_MSDC30_3_CK, msdc30_3_ck, msdc30_4_sel, peri0_cg_regs, 17, 0), + GATE(PERI_MSDC30_2_CK, msdc30_2_ck, msdc30_3_sel, peri0_cg_regs, 16, 0), + GATE(PERI_MSDC30_1_CK, msdc30_1_ck, msdc30_2_sel, peri0_cg_regs, 15, 0), + GATE(PERI_MSDC20_2_CK, msdc20_2_ck, msdc30_1_sel, peri0_cg_regs, 14, 0), + GATE(PERI_MSDC20_1_CK, msdc20_1_ck, msdc30_0_sel, peri0_cg_regs, 13, 0), + GATE(PERI_AP_DMA_CK, ap_dma_ck, axi_sel, peri0_cg_regs, 12, 0), + GATE(PERI_USB1_CK, usb1_ck, usb20_sel, peri0_cg_regs, 11, 0), + GATE(PERI_USB0_CK, usb0_ck, usb20_sel, peri0_cg_regs, 10, 0), + GATE(PERI_PWM_CK, pwm_ck, axi_sel, peri0_cg_regs, 9, 0), + GATE(PERI_PWM7_CK, pwm7_ck, axi_sel, peri0_cg_regs, 8, 0), + GATE(PERI_PWM6_CK, pwm6_ck, axi_sel, peri0_cg_regs, 7, 0), + GATE(PERI_PWM5_CK, pwm5_ck, axi_sel, peri0_cg_regs, 6, 0), + GATE(PERI_PWM4_CK, pwm4_ck, axi_sel, peri0_cg_regs, 5, 0), + GATE(PERI_PWM3_CK, pwm3_ck, axi_sel, peri0_cg_regs, 4, 0), + GATE(PERI_PWM2_CK, pwm2_ck, axi_sel, peri0_cg_regs, 3, 0), + GATE(PERI_PWM1_CK, pwm1_ck, axi_sel, peri0_cg_regs, 2, 0), + GATE(PERI_THERM_CK, therm_ck, axi_sel, peri0_cg_regs, 1, 0), + GATE(PERI_NFI_CK, nfi_ck, axi_sel, peri0_cg_regs, 0, 0), + /* PERI1 */ + GATE(PERI_USBSLV_CK, usbslv_ck, axi_sel, peri1_cg_regs, 8, 0), + GATE(PERI_USB1_MCU_CK, usb1_mcu_ck, axi_sel, peri1_cg_regs, 7, 0), + GATE(PERI_USB0_MCU_CK, usb0_mcu_ck, axi_sel, peri1_cg_regs, 6, 0), + GATE(PERI_GCPU_CK, gcpu_ck, gcpu_sel, peri1_cg_regs, 5, 0), + GATE(PERI_FHCTL_CK, fhctl_ck, clk26m, peri1_cg_regs, 4, 0), + GATE(PERI_SPI1_CK, spi1_ck, spi_sel, peri1_cg_regs, 3, 0), + GATE(PERI_AUXADC_CK, auxadc_ck, clk26m, peri1_cg_regs, 2, 0), + GATE(PERI_PERI_PWRAP_CK, peri_pwrap_ck, axi_sel, peri1_cg_regs, 1, 0), + GATE(PERI_I2C6_CK, i2c6_ck, axi_sel, peri1_cg_regs, 0, 0), +}; + +static void __init init_clk_perisys(void __iomem *perisys_base, + struct clk_onecell_data *clk_data) +{ + pr_debug("init perisys gates:\n"); + init_clk_gates(perisys_base, peri_clks, ARRAY_SIZE(peri_clks), + clk_data); +} + /* * device tree support */ @@ -750,3 +962,51 @@ static void __init mtk_apmixedsys_init(struct device_node *node) } CLK_OF_DECLARE(mtk_apmixedsys, "mediatek,mt8135-apmixedsys", mtk_apmixedsys_init); + +static void __init mtk_infrasys_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + void __iomem *base; + int r; + + pr_debug("%s: %s\n", __func__, node->name); + + base = of_iomap(node, 0); + if (!base) { + pr_err("ioremap infrasys failed\n"); + return; + } + + clk_data = alloc_clk_data(INFRA_NR_CLK); + + init_clk_infrasys(base, clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("could not register clock provide\n"); +} +CLK_OF_DECLARE(mtk_infrasys, "mediatek,mt8135-infracfg", mtk_infrasys_init); + +static void __init mtk_perisys_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + void __iomem *base; + int r; + + pr_debug("%s: %s\n", __func__, node->name); + + base = of_iomap(node, 0); + if (!base) { + pr_err("ioremap infrasys failed\n"); + return; + } + + clk_data = alloc_clk_data(PERI_NR_CLK); + + init_clk_perisys(base, clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("could not register clock provide\n"); +} +CLK_OF_DECLARE(mtk_perisys, "mediatek,mt8135-pericfg", mtk_perisys_init); diff --git a/include/dt-bindings/clock/mt8135-clk.h b/include/dt-bindings/clock/mt8135-clk.h index 674fd1a..a477354 100644 --- a/include/dt-bindings/clock/mt8135-clk.h +++ b/include/dt-bindings/clock/mt8135-clk.h @@ -125,4 +125,66 @@ #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 */ -- 1.8.1.1.dirty ^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 2/4] clk: Add INFRA and PERI clocks for Mediatek MT8135. @ 2014-11-28 11:34 ` James Liao 0 siblings, 0 replies; 15+ messages in thread From: James Liao @ 2014-11-28 11:34 UTC (permalink / raw) To: Rob Herring, Matthias Brugger, Mike Turquette Cc: Mark Rutland, Ashwin Chaugule, srv_jamesjj.liao, Russell King, srv_heupstream, Pawel Moll, Ian Campbell, Catalin Marinas, linux-kernel, devicetree, Vladimir Murzin, Sascha Hauer, Kumar Gala This patch adds clock gates support for Mediatek SoCs, and adds clocks of INFRA sys and PERI sys for MT8135. Signed-off-by: James Liao <jamesjj.liao@mediatek.com> --- drivers/clk/mediatek/Makefile | 2 +- drivers/clk/mediatek/clk-gate.c | 151 +++++++++++++++++++ drivers/clk/mediatek/clk-gate.h | 48 ++++++ drivers/clk/mediatek/clk-mt8135.c | 260 +++++++++++++++++++++++++++++++++ include/dt-bindings/clock/mt8135-clk.h | 62 ++++++++ 5 files changed, 522 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/mediatek/clk-gate.c create mode 100644 drivers/clk/mediatek/clk-gate.h diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile index 3c3432b..96a7044 100644 --- a/drivers/clk/mediatek/Makefile +++ b/drivers/clk/mediatek/Makefile @@ -1,2 +1,2 @@ -obj-y += clk-mtk.o clk-pll.o +obj-y += clk-mtk.o clk-pll.o clk-gate.o obj-y += clk-mt8135.o clk-mt8135-pll.o diff --git a/drivers/clk/mediatek/clk-gate.c b/drivers/clk/mediatek/clk-gate.c new file mode 100644 index 0000000..8f9a9ea --- /dev/null +++ b/drivers/clk/mediatek/clk-gate.c @@ -0,0 +1,151 @@ +/* + * 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" + +/* + * clk_gate + */ + +static void cg_set_mask(struct mtk_clk_gate *cg, u32 mask) +{ + u32 r; + + if (cg->flags & CLK_GATE_NO_SETCLR_REG) { + r = readl_relaxed(cg->sta_addr) | mask; + writel_relaxed(r, cg->sta_addr); + } else + writel_relaxed(mask, cg->set_addr); +} + +static void cg_clr_mask(struct mtk_clk_gate *cg, u32 mask) +{ + u32 r; + + if (cg->flags & CLK_GATE_NO_SETCLR_REG) { + r = readl_relaxed(cg->sta_addr) & ~mask; + writel_relaxed(r, cg->sta_addr); + } else + writel_relaxed(mask, cg->clr_addr); +} + +static int cg_enable(struct clk_hw *hw) +{ + unsigned long flags = 0; + struct mtk_clk_gate *cg = to_clk_gate(hw); + u32 mask = BIT(cg->bit); + + pr_debug("%s, bit: %u\n", __clk_get_name(hw->clk), cg->bit); + + mtk_clk_lock(flags); + + if (cg->flags & CLK_GATE_INVERSE) + cg_set_mask(cg, mask); + else + cg_clr_mask(cg, mask); + + mtk_clk_unlock(flags); + + return 0; +} + +static void cg_disable(struct clk_hw *hw) +{ + unsigned long flags = 0; + struct mtk_clk_gate *cg = to_clk_gate(hw); + u32 mask = BIT(cg->bit); + + pr_debug("%s, bit: %u\n", __clk_get_name(hw->clk), cg->bit); + + mtk_clk_lock(flags); + + if (cg->flags & CLK_GATE_INVERSE) + cg_clr_mask(cg, mask); + else + cg_set_mask(cg, mask); + + mtk_clk_unlock(flags); +} + +static int cg_is_enabled(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_clk_gate(hw); + u32 mask; + u32 val; + int r; + + mask = BIT(cg->bit); + val = mask & readl(cg->sta_addr); + + r = (cg->flags & CLK_GATE_INVERSE) ? (val != 0) : (val == 0); + + pr_debug("%d, %s, bit[%d]\n", r, __clk_get_name(hw->clk), (int)cg->bit); + + return r; +} + +static const struct clk_ops mtk_clk_gate_ops = { + .is_enabled = cg_is_enabled, + .enable = cg_enable, + .disable = cg_disable, +}; + +struct clk *mtk_clk_register_gate( + const char *name, + const char *parent_name, + void __iomem *set_addr, + void __iomem *clr_addr, + void __iomem *sta_addr, + u8 bit, + u32 flags) +{ + struct mtk_clk_gate *cg; + struct clk *clk; + struct clk_init_data init; + + pr_debug("name: %s, bit: %d\n", name, (int)bit); + + cg = kzalloc(sizeof(*cg), GFP_KERNEL); + if (!cg) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.flags = CLK_IGNORE_UNUSED; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + init.ops = &mtk_clk_gate_ops; + + cg->set_addr = set_addr; + cg->clr_addr = clr_addr; + cg->sta_addr = sta_addr; + cg->bit = bit; + cg->flags = flags; + + 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..0d71aad --- /dev/null +++ b/drivers/clk/mediatek/clk-gate.h @@ -0,0 +1,48 @@ +/* + * 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 + +/* + * This is a private header file. DO NOT include it except clk-*.c. + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> + +struct mtk_clk_gate { + struct clk_hw hw; + void __iomem *set_addr; + void __iomem *clr_addr; + void __iomem *sta_addr; + u8 bit; + u32 flags; +}; + +#define to_clk_gate(_hw) container_of(_hw, struct mtk_clk_gate, hw) + +#define CLK_GATE_INVERSE BIT(0) +#define CLK_GATE_NO_SETCLR_REG BIT(1) + +struct clk *mtk_clk_register_gate( + const char *name, + const char *parent_name, + void __iomem *set_addr, + void __iomem *clr_addr, + void __iomem *sta_addr, + u8 bit, + u32 flags); + +#endif /* __DRV_CLK_GATE_H */ diff --git a/drivers/clk/mediatek/clk-mt8135.c b/drivers/clk/mediatek/clk-mt8135.c index fa8dd7d..2496e54 100644 --- a/drivers/clk/mediatek/clk-mt8135.c +++ b/drivers/clk/mediatek/clk-mt8135.c @@ -18,6 +18,7 @@ #include "clk-mtk.h" #include "clk-pll.h" +#include "clk-gate.h" #include "clk-mt8135-pll.h" #include <dt-bindings/clock/mt8135-clk.h> @@ -149,6 +150,66 @@ #define apll_sel "apll_sel" #define hdmipll_sel "hdmipll_sel" +/* PERI0 */ +#define i2c5_ck "i2c5_ck" +#define i2c4_ck "i2c4_ck" +#define i2c3_ck "i2c3_ck" +#define i2c2_ck "i2c2_ck" +#define i2c1_ck "i2c1_ck" +#define i2c0_ck "i2c0_ck" +#define uart3_ck "uart3_ck" +#define uart2_ck "uart2_ck" +#define uart1_ck "uart1_ck" +#define uart0_ck "uart0_ck" +#define irda_ck "irda_ck" +#define nli_ck "nli_ck" +#define md_hif_ck "md_hif_ck" +#define ap_hif_ck "ap_hif_ck" +#define msdc30_3_ck "msdc30_3_ck" +#define msdc30_2_ck "msdc30_2_ck" +#define msdc30_1_ck "msdc30_1_ck" +#define msdc20_2_ck "msdc20_2_ck" +#define msdc20_1_ck "msdc20_1_ck" +#define ap_dma_ck "ap_dma_ck" +#define usb1_ck "usb1_ck" +#define usb0_ck "usb0_ck" +#define pwm_ck "pwm_ck" +#define pwm7_ck "pwm7_ck" +#define pwm6_ck "pwm6_ck" +#define pwm5_ck "pwm5_ck" +#define pwm4_ck "pwm4_ck" +#define pwm3_ck "pwm3_ck" +#define pwm2_ck "pwm2_ck" +#define pwm1_ck "pwm1_ck" +#define therm_ck "therm_ck" +#define nfi_ck "nfi_ck" + +/* PERI1 */ +#define usbslv_ck "usbslv_ck" +#define usb1_mcu_ck "usb1_mcu_ck" +#define usb0_mcu_ck "usb0_mcu_ck" +#define gcpu_ck "gcpu_ck" +#define fhctl_ck "fhctl_ck" +#define spi1_ck "spi1_ck" +#define auxadc_ck "auxadc_ck" +#define peri_pwrap_ck "peri_pwrap_ck" +#define i2c6_ck "i2c6_ck" + +/* INFRA */ +#define pmic_wrap_ck "pmic_wrap_ck" +#define pmicspi_ck "pmicspi_ck" +#define ccif1_ap_ctrl "ccif1_ap_ctrl" +#define ccif0_ap_ctrl "ccif0_ap_ctrl" +#define kp_ck "kp_ck" +#define cpum_ck "cpum_ck" +#define m4u_ck "m4u_ck" +#define mfgaxi_ck "mfgaxi_ck" +#define devapc_ck "devapc_ck" +#define audio_ck "audio_ck" +#define mfg_bus_ck "mfg_bus_ck" +#define smi_ck "smi_ck" +#define dbgclk_ck "dbgclk_ck" + struct mtk_fixed_factor { int id; const char *name; @@ -673,6 +734,157 @@ static void __init init_clk_apmixedsys(void __iomem *apmixed_base, } } +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; + struct mtk_gate_regs *regs; + int shift; + u32 flags; +}; + +#define GATE(_id, _name, _parent, _regs, _shift, _flags) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &_regs, \ + .shift = _shift, \ + .flags = _flags, \ + } + +static void __init init_clk_gates( + void __iomem *reg_base, + struct mtk_gate *clks, int num, + struct clk_onecell_data *clk_data) +{ + int i; + struct clk *clk; + + for (i = 0; i < num; i++) { + struct mtk_gate *gate = &clks[i]; + + clk = mtk_clk_register_gate(gate->name, gate->parent_name, + reg_base + gate->regs->set_ofs, + reg_base + gate->regs->clr_ofs, + reg_base + gate->regs->sta_ofs, + gate->shift, gate->flags); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + gate->name, PTR_ERR(clk)); + continue; + } + + if (clk_data) + clk_data->clks[gate->id] = clk; + + pr_debug("gate %3d: %s\n", i, gate->name); + } +} + +static struct mtk_gate_regs infra_cg_regs = { + .set_ofs = 0x0040, + .clr_ofs = 0x0044, + .sta_ofs = 0x0048, +}; + +static struct mtk_gate infra_clks[] __initdata = { + GATE(INFRA_PMIC_WRAP_CK, pmic_wrap_ck, axi_sel, infra_cg_regs, 23, 0), + GATE(INFRA_PMICSPI_CK, pmicspi_ck, pmicspi_sel, infra_cg_regs, 22, 0), + GATE(INFRA_CCIF1_AP_CTRL, ccif1_ap_ctrl, axi_sel, infra_cg_regs, 21, 0), + GATE(INFRA_CCIF0_AP_CTRL, ccif0_ap_ctrl, axi_sel, infra_cg_regs, 20, 0), + GATE(INFRA_KP_CK, kp_ck, axi_sel, infra_cg_regs, 16, 0), + GATE(INFRA_CPUM_CK, cpum_ck, cpum_tck_in, infra_cg_regs, 15, 0), + GATE(INFRA_M4U_CK, m4u_ck, mem_sel, infra_cg_regs, 8, 0), + GATE(INFRA_MFGAXI_CK, mfgaxi_ck, axi_sel, infra_cg_regs, 7, 0), + GATE(INFRA_DEVAPC_CK, devapc_ck, axi_sel, infra_cg_regs, 6, + CLK_GATE_INVERSE), + GATE(INFRA_AUDIO_CK, audio_ck, aud_intbus_sel, infra_cg_regs, 5, 0), + GATE(INFRA_MFG_BUS_CK, mfg_bus_ck, axi_sel, infra_cg_regs, 2, 0), + GATE(INFRA_SMI_CK, smi_ck, smi_sel, infra_cg_regs, 1, 0), + GATE(INFRA_DBGCLK_CK, dbgclk_ck, axi_sel, infra_cg_regs, 0, 0), +}; + +static void __init init_clk_infrasys(void __iomem *infrasys_base, + struct clk_onecell_data *clk_data) +{ + pr_debug("init infrasys gates:\n"); + init_clk_gates(infrasys_base, infra_clks, ARRAY_SIZE(infra_clks), + clk_data); +} + +static struct mtk_gate_regs peri0_cg_regs = { + .set_ofs = 0x0008, + .clr_ofs = 0x0010, + .sta_ofs = 0x0018, +}; + +static struct mtk_gate_regs peri1_cg_regs = { + .set_ofs = 0x000c, + .clr_ofs = 0x0014, + .sta_ofs = 0x001c, +}; + +static struct mtk_gate peri_clks[] __initdata = { + /* PERI0 */ + GATE(PERI_I2C5_CK, i2c5_ck, axi_sel, peri0_cg_regs, 31, 0), + GATE(PERI_I2C4_CK, i2c4_ck, axi_sel, peri0_cg_regs, 30, 0), + GATE(PERI_I2C3_CK, i2c3_ck, axi_sel, peri0_cg_regs, 29, 0), + GATE(PERI_I2C2_CK, i2c2_ck, axi_sel, peri0_cg_regs, 28, 0), + GATE(PERI_I2C1_CK, i2c1_ck, axi_sel, peri0_cg_regs, 27, 0), + GATE(PERI_I2C0_CK, i2c0_ck, axi_sel, peri0_cg_regs, 26, 0), + GATE(PERI_UART3_CK, uart3_ck, axi_sel, peri0_cg_regs, 25, 0), + GATE(PERI_UART2_CK, uart2_ck, axi_sel, peri0_cg_regs, 24, 0), + GATE(PERI_UART1_CK, uart1_ck, axi_sel, peri0_cg_regs, 23, 0), + GATE(PERI_UART0_CK, uart0_ck, axi_sel, peri0_cg_regs, 22, 0), + GATE(PERI_IRDA_CK, irda_ck, irda_sel, peri0_cg_regs, 21, 0), + GATE(PERI_NLI_CK, nli_ck, axi_sel, peri0_cg_regs, 20, 0), + GATE(PERI_MD_HIF_CK, md_hif_ck, axi_sel, peri0_cg_regs, 19, 0), + GATE(PERI_AP_HIF_CK, ap_hif_ck, axi_sel, peri0_cg_regs, 18, 0), + GATE(PERI_MSDC30_3_CK, msdc30_3_ck, msdc30_4_sel, peri0_cg_regs, 17, 0), + GATE(PERI_MSDC30_2_CK, msdc30_2_ck, msdc30_3_sel, peri0_cg_regs, 16, 0), + GATE(PERI_MSDC30_1_CK, msdc30_1_ck, msdc30_2_sel, peri0_cg_regs, 15, 0), + GATE(PERI_MSDC20_2_CK, msdc20_2_ck, msdc30_1_sel, peri0_cg_regs, 14, 0), + GATE(PERI_MSDC20_1_CK, msdc20_1_ck, msdc30_0_sel, peri0_cg_regs, 13, 0), + GATE(PERI_AP_DMA_CK, ap_dma_ck, axi_sel, peri0_cg_regs, 12, 0), + GATE(PERI_USB1_CK, usb1_ck, usb20_sel, peri0_cg_regs, 11, 0), + GATE(PERI_USB0_CK, usb0_ck, usb20_sel, peri0_cg_regs, 10, 0), + GATE(PERI_PWM_CK, pwm_ck, axi_sel, peri0_cg_regs, 9, 0), + GATE(PERI_PWM7_CK, pwm7_ck, axi_sel, peri0_cg_regs, 8, 0), + GATE(PERI_PWM6_CK, pwm6_ck, axi_sel, peri0_cg_regs, 7, 0), + GATE(PERI_PWM5_CK, pwm5_ck, axi_sel, peri0_cg_regs, 6, 0), + GATE(PERI_PWM4_CK, pwm4_ck, axi_sel, peri0_cg_regs, 5, 0), + GATE(PERI_PWM3_CK, pwm3_ck, axi_sel, peri0_cg_regs, 4, 0), + GATE(PERI_PWM2_CK, pwm2_ck, axi_sel, peri0_cg_regs, 3, 0), + GATE(PERI_PWM1_CK, pwm1_ck, axi_sel, peri0_cg_regs, 2, 0), + GATE(PERI_THERM_CK, therm_ck, axi_sel, peri0_cg_regs, 1, 0), + GATE(PERI_NFI_CK, nfi_ck, axi_sel, peri0_cg_regs, 0, 0), + /* PERI1 */ + GATE(PERI_USBSLV_CK, usbslv_ck, axi_sel, peri1_cg_regs, 8, 0), + GATE(PERI_USB1_MCU_CK, usb1_mcu_ck, axi_sel, peri1_cg_regs, 7, 0), + GATE(PERI_USB0_MCU_CK, usb0_mcu_ck, axi_sel, peri1_cg_regs, 6, 0), + GATE(PERI_GCPU_CK, gcpu_ck, gcpu_sel, peri1_cg_regs, 5, 0), + GATE(PERI_FHCTL_CK, fhctl_ck, clk26m, peri1_cg_regs, 4, 0), + GATE(PERI_SPI1_CK, spi1_ck, spi_sel, peri1_cg_regs, 3, 0), + GATE(PERI_AUXADC_CK, auxadc_ck, clk26m, peri1_cg_regs, 2, 0), + GATE(PERI_PERI_PWRAP_CK, peri_pwrap_ck, axi_sel, peri1_cg_regs, 1, 0), + GATE(PERI_I2C6_CK, i2c6_ck, axi_sel, peri1_cg_regs, 0, 0), +}; + +static void __init init_clk_perisys(void __iomem *perisys_base, + struct clk_onecell_data *clk_data) +{ + pr_debug("init perisys gates:\n"); + init_clk_gates(perisys_base, peri_clks, ARRAY_SIZE(peri_clks), + clk_data); +} + /* * device tree support */ @@ -750,3 +962,51 @@ static void __init mtk_apmixedsys_init(struct device_node *node) } CLK_OF_DECLARE(mtk_apmixedsys, "mediatek,mt8135-apmixedsys", mtk_apmixedsys_init); + +static void __init mtk_infrasys_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + void __iomem *base; + int r; + + pr_debug("%s: %s\n", __func__, node->name); + + base = of_iomap(node, 0); + if (!base) { + pr_err("ioremap infrasys failed\n"); + return; + } + + clk_data = alloc_clk_data(INFRA_NR_CLK); + + init_clk_infrasys(base, clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("could not register clock provide\n"); +} +CLK_OF_DECLARE(mtk_infrasys, "mediatek,mt8135-infracfg", mtk_infrasys_init); + +static void __init mtk_perisys_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + void __iomem *base; + int r; + + pr_debug("%s: %s\n", __func__, node->name); + + base = of_iomap(node, 0); + if (!base) { + pr_err("ioremap infrasys failed\n"); + return; + } + + clk_data = alloc_clk_data(PERI_NR_CLK); + + init_clk_perisys(base, clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("could not register clock provide\n"); +} +CLK_OF_DECLARE(mtk_perisys, "mediatek,mt8135-pericfg", mtk_perisys_init); diff --git a/include/dt-bindings/clock/mt8135-clk.h b/include/dt-bindings/clock/mt8135-clk.h index 674fd1a..a477354 100644 --- a/include/dt-bindings/clock/mt8135-clk.h +++ b/include/dt-bindings/clock/mt8135-clk.h @@ -125,4 +125,66 @@ #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 */ -- 1.8.1.1.dirty ^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 2/4] clk: Add INFRA and PERI clocks for Mediatek MT8135. @ 2014-11-28 12:06 ` Mark Rutland 0 siblings, 0 replies; 15+ messages in thread From: Mark Rutland @ 2014-11-28 12:06 UTC (permalink / raw) To: linux-arm-kernel On Fri, Nov 28, 2014 at 11:34:48AM +0000, James Liao wrote: > This patch adds clock gates support for Mediatek SoCs, and adds clocks of > INFRA sys and PERI sys for MT8135. > > Signed-off-by: James Liao <jamesjj.liao@mediatek.com> > --- > drivers/clk/mediatek/Makefile | 2 +- > drivers/clk/mediatek/clk-gate.c | 151 +++++++++++++++++++ > drivers/clk/mediatek/clk-gate.h | 48 ++++++ > drivers/clk/mediatek/clk-mt8135.c | 260 +++++++++++++++++++++++++++++++++ > include/dt-bindings/clock/mt8135-clk.h | 62 ++++++++ Please fold the include/dt-bindings changes into the DT documentation patch, and put that earlier in the series. Mark. ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 2/4] clk: Add INFRA and PERI clocks for Mediatek MT8135. @ 2014-11-28 12:06 ` Mark Rutland 0 siblings, 0 replies; 15+ messages in thread From: Mark Rutland @ 2014-11-28 12:06 UTC (permalink / raw) To: James Liao Cc: Rob Herring, Matthias Brugger, Mike Turquette, srv_heupstream@mediatek.com, srv_jamesjj.liao@mediatek.com, Sascha Hauer, eddie.huang@mediatek.com, Pawel Moll, Ian Campbell, Kumar Gala, Russell King, Catalin Marinas, Vladimir Murzin, Ashwin Chaugule, Joe.C, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org On Fri, Nov 28, 2014 at 11:34:48AM +0000, James Liao wrote: > This patch adds clock gates support for Mediatek SoCs, and adds clocks of > INFRA sys and PERI sys for MT8135. > > Signed-off-by: James Liao <jamesjj.liao@mediatek.com> > --- > drivers/clk/mediatek/Makefile | 2 +- > drivers/clk/mediatek/clk-gate.c | 151 +++++++++++++++++++ > drivers/clk/mediatek/clk-gate.h | 48 ++++++ > drivers/clk/mediatek/clk-mt8135.c | 260 +++++++++++++++++++++++++++++++++ > include/dt-bindings/clock/mt8135-clk.h | 62 ++++++++ Please fold the include/dt-bindings changes into the DT documentation patch, and put that earlier in the series. Mark. ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 2/4] clk: Add INFRA and PERI clocks for Mediatek MT8135. @ 2014-11-28 12:06 ` Mark Rutland 0 siblings, 0 replies; 15+ messages in thread From: Mark Rutland @ 2014-11-28 12:06 UTC (permalink / raw) Cc: Rob Herring, Matthias Brugger, Mike Turquette, srv_heupstream-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org, srv_jamesjj.liao-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org, Sascha Hauer, eddie.huang-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org, Pawel Moll, Ian Campbell, Kumar Gala, Russell King, Catalin Marinas, Vladimir Murzin, Ashwin Chaugule, Joe.C, devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org On Fri, Nov 28, 2014 at 11:34:48AM +0000, James Liao wrote: > This patch adds clock gates support for Mediatek SoCs, and adds clocks of > INFRA sys and PERI sys for MT8135. > > Signed-off-by: James Liao <jamesjj.liao-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org> > --- > drivers/clk/mediatek/Makefile | 2 +- > drivers/clk/mediatek/clk-gate.c | 151 +++++++++++++++++++ > drivers/clk/mediatek/clk-gate.h | 48 ++++++ > drivers/clk/mediatek/clk-mt8135.c | 260 +++++++++++++++++++++++++++++++++ > include/dt-bindings/clock/mt8135-clk.h | 62 ++++++++ Please fold the include/dt-bindings changes into the DT documentation patch, and put that earlier in the series. Mark. -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 2/4] clk: Add INFRA and PERI clocks for Mediatek MT8135. 2014-11-28 12:06 ` Mark Rutland @ 2014-12-01 3:41 ` james liao -1 siblings, 0 replies; 15+ messages in thread From: james liao @ 2014-12-01 3:41 UTC (permalink / raw) To: linux-arm-kernel Hi Mark, On Fri, 2014-11-28 at 12:06 +0000, Mark Rutland wrote: > On Fri, Nov 28, 2014 at 11:34:48AM +0000, James Liao wrote: > > This patch adds clock gates support for Mediatek SoCs, and adds clocks of > > INFRA sys and PERI sys for MT8135. > > > > Please fold the include/dt-bindings changes into the DT documentation > patch, and put that earlier in the series. > OK, I'll send a new patchset later with the following sequence: 1. include/dt-bindings + DT documention. 2. Basic clocks (PLLs + TOPCKGEN). 3. Basic clocks (INFRA + PERI) 4. Update device tree to enable clocks. Best regards, James ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 2/4] clk: Add INFRA and PERI clocks for Mediatek MT8135. @ 2014-12-01 3:41 ` james liao 0 siblings, 0 replies; 15+ messages in thread From: james liao @ 2014-12-01 3:41 UTC (permalink / raw) To: Mark Rutland Cc: Ashwin Chaugule, Vladimir Murzin, Mike Turquette, Pawel Moll, srv_heupstream@mediatek.com, Ian Campbell, Catalin Marinas, linux-kernel@vger.kernel.org, HenryC.Chen, Joe.C, devicetree@vger.kernel.org, Rob Herring, Sascha Hauer, Kumar Gala, Matthias Brugger, Russell King, eddie.huang@mediatek.com, linux-arm-kernel@lists.infradead.org Hi Mark, On Fri, 2014-11-28 at 12:06 +0000, Mark Rutland wrote: > On Fri, Nov 28, 2014 at 11:34:48AM +0000, James Liao wrote: > > This patch adds clock gates support for Mediatek SoCs, and adds clocks of > > INFRA sys and PERI sys for MT8135. > > > > Please fold the include/dt-bindings changes into the DT documentation > patch, and put that earlier in the series. > OK, I'll send a new patchset later with the following sequence: 1. include/dt-bindings + DT documention. 2. Basic clocks (PLLs + TOPCKGEN). 3. Basic clocks (INFRA + PERI) 4. Update device tree to enable clocks. Best regards, James ^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 3/4] clk: dts: document Mediatek MT8135 clock binding 2014-11-28 11:34 ` James Liao @ 2014-11-28 11:34 ` James Liao -1 siblings, 0 replies; 15+ messages in thread From: James Liao @ 2014-11-28 11:34 UTC (permalink / raw) To: linux-arm-kernel Document the device-tree binding of Mediatek MT8135 SoC, including TOPCKGEN, PLLs, INFRA and PERI clock controller. Signed-off-by: James Liao <jamesjj.liao@mediatek.com> --- .../bindings/clock/mediatek,mt8135-clock.txt | 36 ++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/mediatek,mt8135-clock.txt diff --git a/Documentation/devicetree/bindings/clock/mediatek,mt8135-clock.txt b/Documentation/devicetree/bindings/clock/mediatek,mt8135-clock.txt new file mode 100644 index 0000000..e23df49 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/mediatek,mt8135-clock.txt @@ -0,0 +1,36 @@ +Mediatek MT8135 Clock Controller + +This binding uses the common clock binding: +Documentation/devicetree/bindings/clock/clock-bindings.txt + +The Mediatek MT8135 clock controller generates and supplies clock to various +controllers within Mediatek MT8135 SoC. + +Required Properties: + +- compatible: should be one of following: + - "mediatek,mt8135-topckgen" : for topckgen clock controller of MT8135. + - "mediatek,mt8135-apmixedsys" : for apmixed_sys (PLLs) of MT8135. + - "mediatek,mt8135-infracfg" : for infra_sys clock controller of MT8135. + - "mediatek,mt8135-pericfg" : for peri_sys clock controller of MT8135. + +- reg: physical base address of the controller and length of memory mapped + region. + +- #clock-cells: should be 1. + +All available clocks are defined as preprocessor macros in +dt-bindings/clock/mt8135-clk.h header and can be used in device tree sources. + +Example: I2C controller node that consumes the clock generated by the clock + controller (refer to the standard clock bindings for information about + "clocks" and "clock-names" properties): + + i2c0: i2c at 1100d000 { + compatible = "mediatek,mt8135-i2c", "mediatek,mt6589-i2c"; + reg = <0 0x1100d000 0 0x70>, <0 0x11000300 0 0x80>; + interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_LOW>; + clock-div = <16>; + clocks = <&pericfg PERI_I2C0_CK>, <&pericfg PERI_AP_DMA_CK>; + clock-names = "main", "dma"; + }; -- 1.8.1.1.dirty ^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 3/4] clk: dts: document Mediatek MT8135 clock binding @ 2014-11-28 11:34 ` James Liao 0 siblings, 0 replies; 15+ messages in thread From: James Liao @ 2014-11-28 11:34 UTC (permalink / raw) To: Rob Herring, Matthias Brugger, Mike Turquette Cc: Mark Rutland, Ashwin Chaugule, srv_jamesjj.liao, Russell King, srv_heupstream, Pawel Moll, Ian Campbell, Catalin Marinas, linux-kernel, devicetree, Vladimir Murzin, Sascha Hauer, Kumar Gala Document the device-tree binding of Mediatek MT8135 SoC, including TOPCKGEN, PLLs, INFRA and PERI clock controller. Signed-off-by: James Liao <jamesjj.liao@mediatek.com> --- .../bindings/clock/mediatek,mt8135-clock.txt | 36 ++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/mediatek,mt8135-clock.txt diff --git a/Documentation/devicetree/bindings/clock/mediatek,mt8135-clock.txt b/Documentation/devicetree/bindings/clock/mediatek,mt8135-clock.txt new file mode 100644 index 0000000..e23df49 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/mediatek,mt8135-clock.txt @@ -0,0 +1,36 @@ +Mediatek MT8135 Clock Controller + +This binding uses the common clock binding: +Documentation/devicetree/bindings/clock/clock-bindings.txt + +The Mediatek MT8135 clock controller generates and supplies clock to various +controllers within Mediatek MT8135 SoC. + +Required Properties: + +- compatible: should be one of following: + - "mediatek,mt8135-topckgen" : for topckgen clock controller of MT8135. + - "mediatek,mt8135-apmixedsys" : for apmixed_sys (PLLs) of MT8135. + - "mediatek,mt8135-infracfg" : for infra_sys clock controller of MT8135. + - "mediatek,mt8135-pericfg" : for peri_sys clock controller of MT8135. + +- reg: physical base address of the controller and length of memory mapped + region. + +- #clock-cells: should be 1. + +All available clocks are defined as preprocessor macros in +dt-bindings/clock/mt8135-clk.h header and can be used in device tree sources. + +Example: I2C controller node that consumes the clock generated by the clock + controller (refer to the standard clock bindings for information about + "clocks" and "clock-names" properties): + + i2c0: i2c@1100d000 { + compatible = "mediatek,mt8135-i2c", "mediatek,mt6589-i2c"; + reg = <0 0x1100d000 0 0x70>, <0 0x11000300 0 0x80>; + interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_LOW>; + clock-div = <16>; + clocks = <&pericfg PERI_I2C0_CK>, <&pericfg PERI_AP_DMA_CK>; + clock-names = "main", "dma"; + }; -- 1.8.1.1.dirty ^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 4/4] dts: Enable clock support for Mediatek MT8135. 2014-11-28 11:34 ` James Liao @ 2014-11-28 11:34 ` James Liao -1 siblings, 0 replies; 15+ messages in thread From: James Liao @ 2014-11-28 11:34 UTC (permalink / raw) To: linux-arm-kernel This patch adds MT8135 clock controllers into device tree. Signed-off-by: James Liao <jamesjj.liao@mediatek.com> --- arch/arm/boot/dts/mt8135.dtsi | 47 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/arch/arm/boot/dts/mt8135.dtsi b/arch/arm/boot/dts/mt8135.dtsi index 90a56ad..37d852b 100644 --- a/arch/arm/boot/dts/mt8135.dtsi +++ b/arch/arm/boot/dts/mt8135.dtsi @@ -12,6 +12,7 @@ * GNU General Public License for more details. */ +#include <dt-bindings/clock/mt8135-clk.h> #include <dt-bindings/interrupt-controller/irq.h> #include <dt-bindings/interrupt-controller/arm-gic.h> #include "skeleton64.dtsi" @@ -86,6 +87,24 @@ clock-frequency = <32000>; #clock-cells = <0>; }; + + clk_null: clk_null { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + }; + + clk26m: clk26m { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <26000000>; + }; + + rtc32k: rtc32k { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <32000>; + }; }; soc { @@ -94,6 +113,28 @@ compatible = "simple-bus"; ranges; + topckgen: topckgen at 10000000 { + compatible = "mediatek,mt8135-topckgen"; + reg = <0 0x10000000 0 0x1000>; + #clock-cells = <1>; + }; + + infracfg: syscon at 10001000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "mediatek,mt8135-infracfg", "syscon"; + reg = <0 0x10001000 0 0x1000>; + #clock-cells = <1>; + }; + + pericfg: syscon at 10003000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "mediatek,mt8135-pericfg", "syscon"; + reg = <0 0x10003000 0 0x1000>; + #clock-cells = <1>; + }; + timer: timer at 10008000 { compatible = "mediatek,mt8135-timer", "mediatek,mt6577-timer"; reg = <0 0x10008000 0 0x80>; @@ -102,6 +143,12 @@ clock-names = "system-clk", "rtc-clk"; }; + apmixedsys: apmixedsys at 10209000 { + compatible = "mediatek,mt8135-apmixedsys"; + reg = <0 0x10209000 0 0x1000>; + #clock-cells = <1>; + }; + gic: interrupt-controller at 10211000 { compatible = "arm,cortex-a15-gic"; interrupt-controller; -- 1.8.1.1.dirty ^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 4/4] dts: Enable clock support for Mediatek MT8135. @ 2014-11-28 11:34 ` James Liao 0 siblings, 0 replies; 15+ messages in thread From: James Liao @ 2014-11-28 11:34 UTC (permalink / raw) To: Rob Herring, Matthias Brugger, Mike Turquette Cc: Mark Rutland, Ashwin Chaugule, srv_jamesjj.liao, Russell King, srv_heupstream, Pawel Moll, Ian Campbell, Catalin Marinas, linux-kernel, devicetree, Vladimir Murzin, Sascha Hauer, Kumar Gala This patch adds MT8135 clock controllers into device tree. Signed-off-by: James Liao <jamesjj.liao@mediatek.com> --- arch/arm/boot/dts/mt8135.dtsi | 47 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/arch/arm/boot/dts/mt8135.dtsi b/arch/arm/boot/dts/mt8135.dtsi index 90a56ad..37d852b 100644 --- a/arch/arm/boot/dts/mt8135.dtsi +++ b/arch/arm/boot/dts/mt8135.dtsi @@ -12,6 +12,7 @@ * GNU General Public License for more details. */ +#include <dt-bindings/clock/mt8135-clk.h> #include <dt-bindings/interrupt-controller/irq.h> #include <dt-bindings/interrupt-controller/arm-gic.h> #include "skeleton64.dtsi" @@ -86,6 +87,24 @@ clock-frequency = <32000>; #clock-cells = <0>; }; + + clk_null: clk_null { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + }; + + clk26m: clk26m { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <26000000>; + }; + + rtc32k: rtc32k { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <32000>; + }; }; soc { @@ -94,6 +113,28 @@ compatible = "simple-bus"; ranges; + topckgen: topckgen@10000000 { + compatible = "mediatek,mt8135-topckgen"; + reg = <0 0x10000000 0 0x1000>; + #clock-cells = <1>; + }; + + infracfg: syscon@10001000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "mediatek,mt8135-infracfg", "syscon"; + reg = <0 0x10001000 0 0x1000>; + #clock-cells = <1>; + }; + + pericfg: syscon@10003000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "mediatek,mt8135-pericfg", "syscon"; + reg = <0 0x10003000 0 0x1000>; + #clock-cells = <1>; + }; + timer: timer@10008000 { compatible = "mediatek,mt8135-timer", "mediatek,mt6577-timer"; reg = <0 0x10008000 0 0x80>; @@ -102,6 +143,12 @@ clock-names = "system-clk", "rtc-clk"; }; + apmixedsys: apmixedsys@10209000 { + compatible = "mediatek,mt8135-apmixedsys"; + reg = <0 0x10209000 0 0x1000>; + #clock-cells = <1>; + }; + gic: interrupt-controller@10211000 { compatible = "arm,cortex-a15-gic"; interrupt-controller; -- 1.8.1.1.dirty ^ permalink raw reply related [flat|nested] 15+ messages in thread
end of thread, other threads:[~2014-12-01 3:41 UTC | newest] Thread overview: 15+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2014-11-28 11:34 [PATCH 0/4] Add common clock support for Mediatek MT8135 James Liao 2014-11-28 11:34 ` James Liao 2014-11-28 11:34 ` [PATCH 1/4] clk: Add initial common clock support for Mediatek SoC MT8135 James Liao 2014-11-28 11:34 ` James Liao 2014-11-28 11:34 ` [PATCH 2/4] clk: Add INFRA and PERI clocks for Mediatek MT8135 James Liao 2014-11-28 11:34 ` James Liao 2014-11-28 12:06 ` Mark Rutland 2014-11-28 12:06 ` Mark Rutland 2014-11-28 12:06 ` Mark Rutland 2014-12-01 3:41 ` james liao 2014-12-01 3:41 ` james liao 2014-11-28 11:34 ` [PATCH 3/4] clk: dts: document Mediatek MT8135 clock binding James Liao 2014-11-28 11:34 ` James Liao 2014-11-28 11:34 ` [PATCH 4/4] dts: Enable clock support for Mediatek MT8135 James Liao 2014-11-28 11:34 ` James Liao
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.