diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-08-16 07:41:21 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-08-16 07:41:21 +0300 |
commit | db06f826ec12bf0701ea7fc0a3c0aa00b84417c8 (patch) | |
tree | 0f5cd2bb7af57574ae8a20bfc0e916512c5f2255 /drivers/clk/tegra | |
parent | 6de4c691eab8f421e34c5250f63bf3f477d30eec (diff) | |
parent | ac7da1b787d9ea43680c487613269742c48d8747 (diff) | |
download | linux-db06f826ec12bf0701ea7fc0a3c0aa00b84417c8.tar.xz |
Merge tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux
Pull clk updates from Stephen Boyd:
"The new and exciting feature this time around is in the clk core.
We've added duty cycle support to the clk API so that clk signal duty
cycle ratios can be adjusted while taking into account things like clk
dividers and clk tree hierarchy. So far only one SoC has implemented
support for this, but I expect there will be more to come in the
future.
Outside of the core, we have the usual pile of clk driver updates and
additions. The Amlogic meson driver got the most lines in the diffstat
this time around because it added support for a whole bunch of
hardware and duty cycle configuration. After that the Rockchip PX30,
Qualcomm SDM845, and Renesas SoC drivers fill in a majority of the
diff. We're left with the collection of non-critical fixes after that.
Overall it looks pretty quiet this time.
Core:
- Clk duty cycle support
- Proper CLK_SET_RATE_GATE support throughout the tree
New Drivers:
- Actions Semi Owl series S700 SoC clk driver
- Qualcomm SDM845 display clock controller
- i.MX6SX ocram_s clk support
- Uniphier NAND, USB3 PHY, and SPI clk support
- Qualcomm RPMh clk driver
- i.MX7D mailbox clk support
- Maxim 9485 Programmable Clock Generator
- expose 32 kHz PLL on PXA SoCs
- imx6sll GPIO clk gate support
- Atmel at91 I2S audio clk support
- SI544/SI514 clk on/off support
- i.MX6UL GPIO clock gates in CCM CCGR
- Renesas Crypto Engine clocks on R-Car H3
- Renesas clk support for the new RZ/N1D SoC
- Allwinner A64 display engine clock support
- support for Rockchip's PX30 SoC
- Amlogic Meson axg PCIe and audio clocks
- Amlogic Meson GEN CLK on gxbb, gxl and axg
Updates:
- remove an unused variable from Exynos4412 ISP driver
- fix a thinko bug in SCMI clk division logic
- add missing of_node_put()s in some i.MX clk drivers
- Tegra SDMMC clk jitter improvements with high speed signaling modes
- SPDX tagging for qcom and cs2000-cp drivers
- stop leaking con ids in __clk_put()
- fix a corner case in fixed factor clk probing where node is in DT
but parent clk is registered much later
- Marvell Armada 3700 clk_pm_cpu_get_parent() had an invalid return
value
- i.MX clk init arrays removed in place of CLK_IS_CRITICAL
- convert to CLK_IS_CRITICAL for i.MX51/53 driver
- fix Tegra BPMP driver oops when xlating a NULL clk
- proper default configuration for vic03 and vde clks on Tegra124
- mark Tegra memory controller clks as critical
- fix array bounds clamp in Tegra's emc determine_rate() op
- Ingenic i2s bit update and allow UDC clk to gate
- fix name of aspeed SDC clk define to have only one 'CLK'
- fix i.MX6QDL video clk parent
- critical clk markings for qcom SDM845
- fix Stratix10 mpu_free_clk and sdmmc_free_clk parents
- mark Rockchip's pclk_rkpwm_pmu as critical clock, due to it
supplying the pwm used to drive the logic supply of the rk3399
core"
* tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux: (85 commits)
clk: rockchip: Add pclk_rkpwm_pmu to PMU critical clocks in rk3399
clk: cs2000-cp: convert to SPDX identifiers
clk: scmi: Fix the rounding of clock rate
clk: qcom: Add display clock controller driver for SDM845
clk: mvebu: armada-37xx-periph: Remove unused var num_parents
clk: samsung: Remove unused mout_user_aclk400_mcuisp_p4x12 variable
clk: actions: Add S700 SoC clock support
dt-bindings: clock: Add S700 support for Actions Semi Soc's
clk: actions: Add missing REGMAP_MMIO dependency
clk: uniphier: add clock frequency support for SPI
clk: uniphier: add more USB3 PHY clocks
clk: uniphier: add NAND 200MHz clock
clk: tegra: make sdmmc2 and sdmmc4 as sdmmc clocks
clk: tegra: Add sdmmc mux divider clock
clk: tegra: Refactor fractional divider calculation
clk: tegra: Fix includes required by fence_udelay()
clk: imx6sll: fix missing of_node_put()
clk: imx6ul: fix missing of_node_put()
clk: imx: add ocram_s clock for i.mx6sx
clk: mvebu: armada-37xx-periph: Fix wrong return value in get_parent
...
Diffstat (limited to 'drivers/clk/tegra')
-rw-r--r-- | drivers/clk/tegra/Makefile | 2 | ||||
-rw-r--r-- | drivers/clk/tegra/clk-bpmp.c | 12 | ||||
-rw-r--r-- | drivers/clk/tegra/clk-divider.c | 35 | ||||
-rw-r--r-- | drivers/clk/tegra/clk-emc.c | 2 | ||||
-rw-r--r-- | drivers/clk/tegra/clk-id.h | 2 | ||||
-rw-r--r-- | drivers/clk/tegra/clk-sdmmc-mux.c | 251 | ||||
-rw-r--r-- | drivers/clk/tegra/clk-tegra-periph.c | 11 | ||||
-rw-r--r-- | drivers/clk/tegra/clk-tegra124.c | 3 | ||||
-rw-r--r-- | drivers/clk/tegra/clk-tegra210.c | 14 | ||||
-rw-r--r-- | drivers/clk/tegra/clk-utils.c | 43 | ||||
-rw-r--r-- | drivers/clk/tegra/clk.h | 30 |
11 files changed, 358 insertions, 47 deletions
diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile index b71692391bd6..6507acc843c7 100644 --- a/drivers/clk/tegra/Makefile +++ b/drivers/clk/tegra/Makefile @@ -8,6 +8,7 @@ obj-y += clk-periph-fixed.o obj-y += clk-periph-gate.o obj-y += clk-pll.o obj-y += clk-pll-out.o +obj-y += clk-sdmmc-mux.o obj-y += clk-super.o obj-y += clk-tegra-audio.o obj-y += clk-tegra-periph.o @@ -24,3 +25,4 @@ obj-$(CONFIG_ARCH_TEGRA_132_SOC) += clk-tegra124.o obj-y += cvb.o obj-$(CONFIG_ARCH_TEGRA_210_SOC) += clk-tegra210.o obj-$(CONFIG_CLK_TEGRA_BPMP) += clk-bpmp.o +obj-y += clk-utils.o diff --git a/drivers/clk/tegra/clk-bpmp.c b/drivers/clk/tegra/clk-bpmp.c index a896692b74ec..01dada561c10 100644 --- a/drivers/clk/tegra/clk-bpmp.c +++ b/drivers/clk/tegra/clk-bpmp.c @@ -586,9 +586,15 @@ static struct clk_hw *tegra_bpmp_clk_of_xlate(struct of_phandle_args *clkspec, unsigned int id = clkspec->args[0], i; struct tegra_bpmp *bpmp = data; - for (i = 0; i < bpmp->num_clocks; i++) - if (bpmp->clocks[i]->id == id) - return &bpmp->clocks[i]->hw; + for (i = 0; i < bpmp->num_clocks; i++) { + struct tegra_bpmp_clk *clk = bpmp->clocks[i]; + + if (!clk) + continue; + + if (clk->id == id) + return &clk->hw; + } return NULL; } diff --git a/drivers/clk/tegra/clk-divider.c b/drivers/clk/tegra/clk-divider.c index 16e0aee14773..205fe8ff63f0 100644 --- a/drivers/clk/tegra/clk-divider.c +++ b/drivers/clk/tegra/clk-divider.c @@ -32,35 +32,15 @@ static int get_div(struct tegra_clk_frac_div *divider, unsigned long rate, unsigned long parent_rate) { - u64 divider_ux1 = parent_rate; - u8 flags = divider->flags; - int mul; - - if (!rate) - return 0; - - mul = get_mul(divider); - - if (!(flags & TEGRA_DIVIDER_INT)) - divider_ux1 *= mul; - - if (flags & TEGRA_DIVIDER_ROUND_UP) - divider_ux1 += rate - 1; - - do_div(divider_ux1, rate); - - if (flags & TEGRA_DIVIDER_INT) - divider_ux1 *= mul; + int div; - divider_ux1 -= mul; + div = div_frac_get(rate, parent_rate, divider->width, + divider->frac_width, divider->flags); - if ((s64)divider_ux1 < 0) + if (div < 0) return 0; - if (divider_ux1 > get_max_div(divider)) - return get_max_div(divider); - - return divider_ux1; + return div; } static unsigned long clk_frac_div_recalc_rate(struct clk_hw *hw, @@ -194,6 +174,7 @@ static const struct clk_div_table mc_div_table[] = { struct clk *tegra_clk_register_mc(const char *name, const char *parent_name, void __iomem *reg, spinlock_t *lock) { - return clk_register_divider_table(NULL, name, parent_name, 0, reg, - 16, 1, 0, mc_div_table, lock); + return clk_register_divider_table(NULL, name, parent_name, + CLK_IS_CRITICAL, reg, 16, 1, 0, + mc_div_table, lock); } diff --git a/drivers/clk/tegra/clk-emc.c b/drivers/clk/tegra/clk-emc.c index 5234acd30e89..0621a3a82ea6 100644 --- a/drivers/clk/tegra/clk-emc.c +++ b/drivers/clk/tegra/clk-emc.c @@ -132,7 +132,7 @@ static int emc_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) timing = tegra->timings + i; if (timing->rate > req->max_rate) { - i = min(i, 1); + i = max(i, 1); req->rate = tegra->timings[i - 1].rate; return 0; } diff --git a/drivers/clk/tegra/clk-id.h b/drivers/clk/tegra/clk-id.h index b616e33c5255..de466b4446da 100644 --- a/drivers/clk/tegra/clk-id.h +++ b/drivers/clk/tegra/clk-id.h @@ -227,13 +227,11 @@ enum clk_id { tegra_clk_sdmmc1_9, tegra_clk_sdmmc2, tegra_clk_sdmmc2_8, - tegra_clk_sdmmc2_9, tegra_clk_sdmmc3, tegra_clk_sdmmc3_8, tegra_clk_sdmmc3_9, tegra_clk_sdmmc4, tegra_clk_sdmmc4_8, - tegra_clk_sdmmc4_9, tegra_clk_se, tegra_clk_soc_therm, tegra_clk_soc_therm_8, diff --git a/drivers/clk/tegra/clk-sdmmc-mux.c b/drivers/clk/tegra/clk-sdmmc-mux.c new file mode 100644 index 000000000000..473d418533cb --- /dev/null +++ b/drivers/clk/tegra/clk-sdmmc-mux.c @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018 NVIDIA CORPORATION. All rights reserved. + * + * based on clk-mux.c + * + * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de> + * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@linaro.org> + * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org> + * + */ + +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/types.h> + +#include "clk.h" + +#define DIV_MASK GENMASK(7, 0) +#define MUX_SHIFT 29 +#define MUX_MASK GENMASK(MUX_SHIFT + 2, MUX_SHIFT) +#define SDMMC_MUL 2 + +#define get_max_div(d) DIV_MASK +#define get_div_field(val) ((val) & DIV_MASK) +#define get_mux_field(val) (((val) & MUX_MASK) >> MUX_SHIFT) + +static const char * const mux_sdmmc_parents[] = { + "pll_p", "pll_c4_out2", "pll_c4_out0", "pll_c4_out1", "clk_m" +}; + +static const u8 mux_lj_idx[] = { + [0] = 0, [1] = 1, [2] = 2, [3] = 5, [4] = 6 +}; + +static const u8 mux_non_lj_idx[] = { + [0] = 0, [1] = 3, [2] = 7, [3] = 4, [4] = 6 +}; + +static u8 clk_sdmmc_mux_get_parent(struct clk_hw *hw) +{ + struct tegra_sdmmc_mux *sdmmc_mux = to_clk_sdmmc_mux(hw); + int num_parents, i; + u32 src, val; + const u8 *mux_idx; + + num_parents = clk_hw_get_num_parents(hw); + + val = readl_relaxed(sdmmc_mux->reg); + src = get_mux_field(val); + if (get_div_field(val)) + mux_idx = mux_non_lj_idx; + else + mux_idx = mux_lj_idx; + + for (i = 0; i < num_parents; i++) { + if (mux_idx[i] == src) + return i; + } + + WARN(1, "Unknown parent selector %d\n", src); + + return 0; +} + +static int clk_sdmmc_mux_set_parent(struct clk_hw *hw, u8 index) +{ + struct tegra_sdmmc_mux *sdmmc_mux = to_clk_sdmmc_mux(hw); + u32 val; + + + val = readl_relaxed(sdmmc_mux->reg); + if (get_div_field(val)) + index = mux_non_lj_idx[index]; + else + index = mux_lj_idx[index]; + + val &= ~MUX_MASK; + val |= index << MUX_SHIFT; + + writel(val, sdmmc_mux->reg); + + return 0; +} + +static unsigned long clk_sdmmc_mux_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct tegra_sdmmc_mux *sdmmc_mux = to_clk_sdmmc_mux(hw); + u32 val; + int div; + u64 rate = parent_rate; + + val = readl_relaxed(sdmmc_mux->reg); + div = get_div_field(val); + + div += SDMMC_MUL; + + rate *= SDMMC_MUL; + rate += div - 1; + do_div(rate, div); + + return rate; +} + +static int clk_sdmmc_mux_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct tegra_sdmmc_mux *sdmmc_mux = to_clk_sdmmc_mux(hw); + int div; + unsigned long output_rate = req->best_parent_rate; + + req->rate = max(req->rate, req->min_rate); + req->rate = min(req->rate, req->max_rate); + + if (!req->rate) + return output_rate; + + div = div_frac_get(req->rate, output_rate, 8, 1, sdmmc_mux->div_flags); + if (div < 0) + div = 0; + + if (sdmmc_mux->div_flags & TEGRA_DIVIDER_ROUND_UP) + req->rate = DIV_ROUND_UP(output_rate * SDMMC_MUL, + div + SDMMC_MUL); + else + req->rate = output_rate * SDMMC_MUL / (div + SDMMC_MUL); + + return 0; +} + +static int clk_sdmmc_mux_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct tegra_sdmmc_mux *sdmmc_mux = to_clk_sdmmc_mux(hw); + int div; + unsigned long flags = 0; + u32 val; + u8 src; + + div = div_frac_get(rate, parent_rate, 8, 1, sdmmc_mux->div_flags); + if (div < 0) + return div; + + if (sdmmc_mux->lock) + spin_lock_irqsave(sdmmc_mux->lock, flags); + + src = clk_sdmmc_mux_get_parent(hw); + if (div) + src = mux_non_lj_idx[src]; + else + src = mux_lj_idx[src]; + + val = src << MUX_SHIFT; + val |= div; + writel(val, sdmmc_mux->reg); + fence_udelay(2, sdmmc_mux->reg); + + if (sdmmc_mux->lock) + spin_unlock_irqrestore(sdmmc_mux->lock, flags); + + return 0; +} + +static int clk_sdmmc_mux_is_enabled(struct clk_hw *hw) +{ + struct tegra_sdmmc_mux *sdmmc_mux = to_clk_sdmmc_mux(hw); + const struct clk_ops *gate_ops = sdmmc_mux->gate_ops; + struct clk_hw *gate_hw = &sdmmc_mux->gate.hw; + + __clk_hw_set_clk(gate_hw, hw); + + return gate_ops->is_enabled(gate_hw); +} + +static int clk_sdmmc_mux_enable(struct clk_hw *hw) +{ + struct tegra_sdmmc_mux *sdmmc_mux = to_clk_sdmmc_mux(hw); + const struct clk_ops *gate_ops = sdmmc_mux->gate_ops; + struct clk_hw *gate_hw = &sdmmc_mux->gate.hw; + + __clk_hw_set_clk(gate_hw, hw); + + return gate_ops->enable(gate_hw); +} + +static void clk_sdmmc_mux_disable(struct clk_hw *hw) +{ + struct tegra_sdmmc_mux *sdmmc_mux = to_clk_sdmmc_mux(hw); + const struct clk_ops *gate_ops = sdmmc_mux->gate_ops; + struct clk_hw *gate_hw = &sdmmc_mux->gate.hw; + + gate_ops->disable(gate_hw); +} + +static const struct clk_ops tegra_clk_sdmmc_mux_ops = { + .get_parent = clk_sdmmc_mux_get_parent, + .set_parent = clk_sdmmc_mux_set_parent, + .determine_rate = clk_sdmmc_mux_determine_rate, + .recalc_rate = clk_sdmmc_mux_recalc_rate, + .set_rate = clk_sdmmc_mux_set_rate, + .is_enabled = clk_sdmmc_mux_is_enabled, + .enable = clk_sdmmc_mux_enable, + .disable = clk_sdmmc_mux_disable, +}; + +struct clk *tegra_clk_register_sdmmc_mux_div(const char *name, + void __iomem *clk_base, u32 offset, u32 clk_num, u8 div_flags, + unsigned long flags, void *lock) +{ + struct clk *clk; + struct clk_init_data init; + const struct tegra_clk_periph_regs *bank; + struct tegra_sdmmc_mux *sdmmc_mux; + + init.ops = &tegra_clk_sdmmc_mux_ops; + init.name = name; + init.flags = flags; + init.parent_names = mux_sdmmc_parents; + init.num_parents = ARRAY_SIZE(mux_sdmmc_parents); + + bank = get_reg_bank(clk_num); + if (!bank) + return ERR_PTR(-EINVAL); + + sdmmc_mux = kzalloc(sizeof(*sdmmc_mux), GFP_KERNEL); + if (!sdmmc_mux) + return ERR_PTR(-ENOMEM); + + /* Data in .init is copied by clk_register(), so stack variable OK */ + sdmmc_mux->hw.init = &init; + sdmmc_mux->reg = clk_base + offset; + sdmmc_mux->lock = lock; + sdmmc_mux->gate.clk_base = clk_base; + sdmmc_mux->gate.regs = bank; + sdmmc_mux->gate.enable_refcnt = periph_clk_enb_refcnt; + sdmmc_mux->gate.clk_num = clk_num; + sdmmc_mux->gate.flags = TEGRA_PERIPH_ON_APB; + sdmmc_mux->div_flags = div_flags; + sdmmc_mux->gate_ops = &tegra_clk_periph_gate_ops; + + clk = clk_register(NULL, &sdmmc_mux->hw); + if (IS_ERR(clk)) { + kfree(sdmmc_mux); + return clk; + } + + sdmmc_mux->gate.hw.clk = clk; + + return clk; +} diff --git a/drivers/clk/tegra/clk-tegra-periph.c b/drivers/clk/tegra/clk-tegra-periph.c index 2acba2986bc6..38c4eb28c8bf 100644 --- a/drivers/clk/tegra/clk-tegra-periph.c +++ b/drivers/clk/tegra/clk-tegra-periph.c @@ -451,15 +451,6 @@ static u32 mux_pllp_pllc4_out2_pllc4_out1_clkm_pllc4_out0_idx[] = { [0] = 0, [1] = 3, [2] = 4, [3] = 6, [4] = 7, }; -static const char *mux_pllp_clkm_pllc4_out2_out1_out0_lj[] = { - "pll_p", - "pll_c4_out2", "pll_c4_out0", /* LJ input */ - "pll_c4_out2", "pll_c4_out1", - "pll_c4_out1", /* LJ input */ - "clk_m", "pll_c4_out0" -}; -#define mux_pllp_clkm_pllc4_out2_out1_out0_lj_idx NULL - static const char *mux_pllp_pllc2_c_c3_clkm[] = { "pll_p", "pll_c2", "pll_c", "pll_c3", "clk_m" }; @@ -686,9 +677,7 @@ static struct tegra_periph_init_data periph_clks[] = { MUX("sdmmc3", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SDMMC3, 69, TEGRA_PERIPH_ON_APB, tegra_clk_sdmmc3), MUX("sdmmc4", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_SDMMC4, 15, TEGRA_PERIPH_ON_APB, tegra_clk_sdmmc4), MUX8("sdmmc1", mux_pllp_pllc4_out2_pllc4_out1_clkm_pllc4_out0, CLK_SOURCE_SDMMC1, 14, TEGRA_PERIPH_ON_APB, tegra_clk_sdmmc1_9), - MUX8("sdmmc2", mux_pllp_clkm_pllc4_out2_out1_out0_lj, CLK_SOURCE_SDMMC2, 9, TEGRA_PERIPH_ON_APB, tegra_clk_sdmmc2_9), MUX8("sdmmc3", mux_pllp_pllc4_out2_pllc4_out1_clkm_pllc4_out0, CLK_SOURCE_SDMMC3, 69, TEGRA_PERIPH_ON_APB, tegra_clk_sdmmc3_9), - MUX8("sdmmc4", mux_pllp_clkm_pllc4_out2_out1_out0_lj, CLK_SOURCE_SDMMC4, 15, TEGRA_PERIPH_ON_APB, tegra_clk_sdmmc4_9), MUX("la", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_LA, 76, TEGRA_PERIPH_ON_APB, tegra_clk_la), MUX("trace", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_TRACE, 77, TEGRA_PERIPH_ON_APB, tegra_clk_trace), MUX("owr", mux_pllp_pllc_pllm_clkm, CLK_SOURCE_OWR, 71, TEGRA_PERIPH_ON_APB, tegra_clk_owr), diff --git a/drivers/clk/tegra/clk-tegra124.c b/drivers/clk/tegra/clk-tegra124.c index 0c69c7970950..b6cf28ca2ed2 100644 --- a/drivers/clk/tegra/clk-tegra124.c +++ b/drivers/clk/tegra/clk-tegra124.c @@ -1267,7 +1267,7 @@ static struct tegra_clk_init_table common_init_table[] __initdata = { { TEGRA124_CLK_I2S2, TEGRA124_CLK_PLL_A_OUT0, 11289600, 0 }, { TEGRA124_CLK_I2S3, TEGRA124_CLK_PLL_A_OUT0, 11289600, 0 }, { TEGRA124_CLK_I2S4, TEGRA124_CLK_PLL_A_OUT0, 11289600, 0 }, - { TEGRA124_CLK_VDE, TEGRA124_CLK_CLK_MAX, 600000000, 0 }, + { TEGRA124_CLK_VDE, TEGRA124_CLK_PLL_C3, 600000000, 0 }, { TEGRA124_CLK_HOST1X, TEGRA124_CLK_PLL_P, 136000000, 1 }, { TEGRA124_CLK_DSIALP, TEGRA124_CLK_PLL_P, 68000000, 0 }, { TEGRA124_CLK_DSIBLP, TEGRA124_CLK_PLL_P, 68000000, 0 }, @@ -1290,6 +1290,7 @@ static struct tegra_clk_init_table common_init_table[] __initdata = { { TEGRA124_CLK_MSELECT, TEGRA124_CLK_CLK_MAX, 0, 1 }, { TEGRA124_CLK_CSITE, TEGRA124_CLK_CLK_MAX, 0, 1 }, { TEGRA124_CLK_TSENSOR, TEGRA124_CLK_CLK_M, 400000, 0 }, + { TEGRA124_CLK_VIC03, TEGRA124_CLK_PLL_C3, 0, 0 }, /* must be the last entry */ { TEGRA124_CLK_CLK_MAX, TEGRA124_CLK_CLK_MAX, 0, 0 }, }; diff --git a/drivers/clk/tegra/clk-tegra210.c b/drivers/clk/tegra/clk-tegra210.c index 5435d01c636a..9eb1cb14fce1 100644 --- a/drivers/clk/tegra/clk-tegra210.c +++ b/drivers/clk/tegra/clk-tegra210.c @@ -44,6 +44,8 @@ #define CLK_SOURCE_EMC 0x19c #define CLK_SOURCE_SOR1 0x410 #define CLK_SOURCE_LA 0x1f8 +#define CLK_SOURCE_SDMMC2 0x154 +#define CLK_SOURCE_SDMMC4 0x164 #define PLLC_BASE 0x80 #define PLLC_OUT 0x84 @@ -2286,11 +2288,9 @@ static struct tegra_clk tegra210_clks[tegra_clk_max] __initdata = { [tegra_clk_rtc] = { .dt_id = TEGRA210_CLK_RTC, .present = true }, [tegra_clk_timer] = { .dt_id = TEGRA210_CLK_TIMER, .present = true }, [tegra_clk_uarta_8] = { .dt_id = TEGRA210_CLK_UARTA, .present = true }, - [tegra_clk_sdmmc2_9] = { .dt_id = TEGRA210_CLK_SDMMC2, .present = true }, [tegra_clk_i2s1] = { .dt_id = TEGRA210_CLK_I2S1, .present = true }, [tegra_clk_i2c1] = { .dt_id = TEGRA210_CLK_I2C1, .present = true }, [tegra_clk_sdmmc1_9] = { .dt_id = TEGRA210_CLK_SDMMC1, .present = true }, - [tegra_clk_sdmmc4_9] = { .dt_id = TEGRA210_CLK_SDMMC4, .present = true }, [tegra_clk_pwm] = { .dt_id = TEGRA210_CLK_PWM, .present = true }, [tegra_clk_i2s2] = { .dt_id = TEGRA210_CLK_I2S2, .present = true }, [tegra_clk_usbd] = { .dt_id = TEGRA210_CLK_USBD, .present = true }, @@ -3030,6 +3030,16 @@ static __init void tegra210_periph_clk_init(void __iomem *clk_base, 0, NULL); clks[TEGRA210_CLK_ACLK] = clk; + clk = tegra_clk_register_sdmmc_mux_div("sdmmc2", clk_base, + CLK_SOURCE_SDMMC2, 9, + TEGRA_DIVIDER_ROUND_UP, 0, NULL); + clks[TEGRA210_CLK_SDMMC2] = clk; + + clk = tegra_clk_register_sdmmc_mux_div("sdmmc4", clk_base, + CLK_SOURCE_SDMMC4, 15, + TEGRA_DIVIDER_ROUND_UP, 0, NULL); + clks[TEGRA210_CLK_SDMMC4] = clk; + for (i = 0; i < ARRAY_SIZE(tegra210_periph); i++) { struct tegra_periph_init_data *init = &tegra210_periph[i]; struct clk **clkp; diff --git a/drivers/clk/tegra/clk-utils.c b/drivers/clk/tegra/clk-utils.c new file mode 100644 index 000000000000..1a5daae4e501 --- /dev/null +++ b/drivers/clk/tegra/clk-utils.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. + */ + +#include <asm/div64.h> + +#include "clk.h" + +#define div_mask(w) ((1 << (w)) - 1) + +int div_frac_get(unsigned long rate, unsigned parent_rate, u8 width, + u8 frac_width, u8 flags) +{ + u64 divider_ux1 = parent_rate; + int mul; + + if (!rate) + return 0; + + mul = 1 << frac_width; + + if (!(flags & TEGRA_DIVIDER_INT)) + divider_ux1 *= mul; + + if (flags & TEGRA_DIVIDER_ROUND_UP) + divider_ux1 += rate - 1; + + do_div(divider_ux1, rate); + + if (flags & TEGRA_DIVIDER_INT) + divider_ux1 *= mul; + + if (divider_ux1 < mul) + return 0; + + divider_ux1 -= mul; + + if (divider_ux1 > div_mask(width)) + return div_mask(width); + + return divider_ux1; +} diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index e1f88463b600..d2c3a010f8e9 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -19,6 +19,7 @@ #include <linux/clk-provider.h> #include <linux/clkdev.h> +#include <linux/delay.h> /** * struct tegra_clk_sync_source - external clock source from codec @@ -705,6 +706,32 @@ struct clk *tegra_clk_register_super_clk(const char *name, const char * const *parent_names, u8 num_parents, unsigned long flags, void __iomem *reg, u8 clk_super_flags, spinlock_t *lock); + +/** + * struct tegra_sdmmc_mux - switch divider with Low Jitter inputs for SDMMC + * + * @hw: handle between common and hardware-specific interfaces + * @reg: register controlling mux and divider + * @flags: hardware-specific flags + * @lock: optional register lock + * @gate: gate clock + * @gate_ops: gate clock ops + */ +struct tegra_sdmmc_mux { + struct clk_hw hw; + void __iomem *reg; + spinlock_t *lock; + const struct clk_ops *gate_ops; + struct tegra_clk_periph_gate gate; + u8 div_flags; +}; + +#define to_clk_sdmmc_mux(_hw) container_of(_hw, struct tegra_sdmmc_mux, hw) + +struct clk *tegra_clk_register_sdmmc_mux_div(const char *name, + void __iomem *clk_base, u32 offset, u32 clk_num, u8 div_flags, + unsigned long flags, void *lock); + /** * struct clk_init_table - clock initialization table * @clk_id: clock id as mentioned in device tree bindings @@ -811,6 +838,9 @@ extern tegra_clk_apply_init_table_func tegra_clk_apply_init_table; int tegra_pll_wait_for_lock(struct tegra_clk_pll *pll); u16 tegra_pll_get_fixed_mdiv(struct clk_hw *hw, unsigned long input_rate); int tegra_pll_p_div_to_hw(struct tegra_clk_pll *pll, u8 p_div); +int div_frac_get(unsigned long rate, unsigned parent_rate, u8 width, + u8 frac_width, u8 flags); + /* Combined read fence with delay */ #define fence_udelay(delay, reg) \ |