diff options
author | Olof Johansson <olof@lixom.net> | 2018-12-12 21:56:36 +0100 |
---|---|---|
committer | Olof Johansson <olof@lixom.net> | 2018-12-12 21:56:36 +0100 |
commit | fafda335f81a6e5af64a3452185299c86ec32b5e (patch) | |
tree | 6a94fd30425681dfd2d8c2a5ebc823e325bb6b60 /drivers | |
parent | Merge tag 'imx7d-pico-4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/... (diff) | |
parent | ARM: dts: imx: add imx7ulp evk support (diff) | |
download | linux-fafda335f81a6e5af64a3452185299c86ec32b5e.tar.xz linux-fafda335f81a6e5af64a3452185299c86ec32b5e.zip |
Merge tag 'imx7ulp-dt-4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux into next/dt
i.MX7ULP device tree for 4.21:
- It includes the initial device tree for i.MX7ULP SoC and EVK board
support.
* tag 'imx7ulp-dt-4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux:
ARM: dts: imx: add imx7ulp evk support
ARM: dts: imx: add common imx7ulp dtsi support
dt-bindings: fsl: add imx7ulp pm related components bindings
dt-bindings: fsl: add compatible for imx7ulp evk
clk: imx: add imx7ulp clk driver
clk: imx: implement new clk_hw based APIs
clk: imx: make mux parent strings const
dt-bindings: clock: add imx7ulp clock binding doc
clk: imx: add imx7ulp composite clk support
clk: imx: add pfdv2 support
clk: imx: add pllv4 support
clk: fractional-divider: add CLK_FRAC_DIVIDER_ZERO_BASED flag support
clk: imx: add gatable clock divider support
Signed-off-by: Olof Johansson <olof@lixom.net>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/clk/clk-fractional-divider.c | 10 | ||||
-rw-r--r-- | drivers/clk/imx/Makefile | 7 | ||||
-rw-r--r-- | drivers/clk/imx/clk-busy.c | 2 | ||||
-rw-r--r-- | drivers/clk/imx/clk-composite-7ulp.c | 87 | ||||
-rw-r--r-- | drivers/clk/imx/clk-divider-gate.c | 221 | ||||
-rw-r--r-- | drivers/clk/imx/clk-fixup-mux.c | 2 | ||||
-rw-r--r-- | drivers/clk/imx/clk-imx7ulp.c | 220 | ||||
-rw-r--r-- | drivers/clk/imx/clk-pfdv2.c | 203 | ||||
-rw-r--r-- | drivers/clk/imx/clk-pllv4.c | 184 | ||||
-rw-r--r-- | drivers/clk/imx/clk.c | 22 | ||||
-rw-r--r-- | drivers/clk/imx/clk.h | 96 |
11 files changed, 1044 insertions, 10 deletions
diff --git a/drivers/clk/clk-fractional-divider.c b/drivers/clk/clk-fractional-divider.c index fdf625fb10fa..7ccde6bd8dd5 100644 --- a/drivers/clk/clk-fractional-divider.c +++ b/drivers/clk/clk-fractional-divider.c @@ -40,6 +40,11 @@ static unsigned long clk_fd_recalc_rate(struct clk_hw *hw, m = (val & fd->mmask) >> fd->mshift; n = (val & fd->nmask) >> fd->nshift; + if (fd->flags & CLK_FRAC_DIVIDER_ZERO_BASED) { + m++; + n++; + } + if (!n || !m) return parent_rate; @@ -103,6 +108,11 @@ static int clk_fd_set_rate(struct clk_hw *hw, unsigned long rate, GENMASK(fd->mwidth - 1, 0), GENMASK(fd->nwidth - 1, 0), &m, &n); + if (fd->flags & CLK_FRAC_DIVIDER_ZERO_BASED) { + m--; + n--; + } + if (fd->lock) spin_lock_irqsave(fd->lock, flags); else diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile index 8c3baa7e6496..615b413c5681 100644 --- a/drivers/clk/imx/Makefile +++ b/drivers/clk/imx/Makefile @@ -4,6 +4,8 @@ obj-y += \ clk.o \ clk-busy.o \ clk-cpu.o \ + clk-composite-7ulp.o \ + clk-divider-gate.o \ clk-fixup-div.o \ clk-fixup-mux.o \ clk-gate-exclusive.o \ @@ -11,7 +13,9 @@ obj-y += \ clk-pllv1.o \ clk-pllv2.o \ clk-pllv3.o \ - clk-pfd.o + clk-pllv4.o \ + clk-pfd.o \ + clk-pfdv2.o obj-$(CONFIG_SOC_IMX1) += clk-imx1.o obj-$(CONFIG_SOC_IMX21) += clk-imx21.o @@ -26,4 +30,5 @@ obj-$(CONFIG_SOC_IMX6SLL) += clk-imx6sll.o obj-$(CONFIG_SOC_IMX6SX) += clk-imx6sx.o obj-$(CONFIG_SOC_IMX6UL) += clk-imx6ul.o obj-$(CONFIG_SOC_IMX7D) += clk-imx7d.o +obj-$(CONFIG_SOC_IMX7ULP) += clk-imx7ulp.o obj-$(CONFIG_SOC_VF610) += clk-vf610.o diff --git a/drivers/clk/imx/clk-busy.c b/drivers/clk/imx/clk-busy.c index 99036527eb0d..e695622c5aa5 100644 --- a/drivers/clk/imx/clk-busy.c +++ b/drivers/clk/imx/clk-busy.c @@ -154,7 +154,7 @@ static const struct clk_ops clk_busy_mux_ops = { struct clk *imx_clk_busy_mux(const char *name, void __iomem *reg, u8 shift, u8 width, void __iomem *busy_reg, u8 busy_shift, - const char **parent_names, int num_parents) + const char * const *parent_names, int num_parents) { struct clk_busy_mux *busy; struct clk *clk; diff --git a/drivers/clk/imx/clk-composite-7ulp.c b/drivers/clk/imx/clk-composite-7ulp.c new file mode 100644 index 000000000000..060f8600ea0d --- /dev/null +++ b/drivers/clk/imx/clk-composite-7ulp.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Freescale Semiconductor, Inc. + * Copyright 2017~2018 NXP + * + */ + +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/slab.h> + +#include "clk.h" + +#define PCG_PCS_SHIFT 24 +#define PCG_PCS_MASK 0x7 +#define PCG_CGC_SHIFT 30 +#define PCG_FRAC_SHIFT 3 +#define PCG_FRAC_WIDTH 1 +#define PCG_FRAC_MASK BIT(3) +#define PCG_PCD_SHIFT 0 +#define PCG_PCD_WIDTH 3 +#define PCG_PCD_MASK 0x7 + +struct clk_hw *imx7ulp_clk_composite(const char *name, + const char * const *parent_names, + int num_parents, bool mux_present, + bool rate_present, bool gate_present, + void __iomem *reg) +{ + struct clk_hw *mux_hw = NULL, *fd_hw = NULL, *gate_hw = NULL; + struct clk_fractional_divider *fd = NULL; + struct clk_gate *gate = NULL; + struct clk_mux *mux = NULL; + struct clk_hw *hw; + + if (mux_present) { + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) + return ERR_PTR(-ENOMEM); + mux_hw = &mux->hw; + mux->reg = reg; + mux->shift = PCG_PCS_SHIFT; + mux->mask = PCG_PCS_MASK; + } + + if (rate_present) { + fd = kzalloc(sizeof(*fd), GFP_KERNEL); + if (!fd) { + kfree(mux); + return ERR_PTR(-ENOMEM); + } + fd_hw = &fd->hw; + fd->reg = reg; + fd->mshift = PCG_FRAC_SHIFT; + fd->mwidth = PCG_FRAC_WIDTH; + fd->mmask = PCG_FRAC_MASK; + fd->nshift = PCG_PCD_SHIFT; + fd->nwidth = PCG_PCD_WIDTH; + fd->nmask = PCG_PCD_MASK; + fd->flags = CLK_FRAC_DIVIDER_ZERO_BASED; + } + + if (gate_present) { + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) { + kfree(mux); + kfree(fd); + return ERR_PTR(-ENOMEM); + } + gate_hw = &gate->hw; + gate->reg = reg; + gate->bit_idx = PCG_CGC_SHIFT; + } + + hw = clk_hw_register_composite(NULL, name, parent_names, num_parents, + mux_hw, &clk_mux_ops, fd_hw, + &clk_fractional_divider_ops, gate_hw, + &clk_gate_ops, CLK_SET_RATE_GATE | + CLK_SET_PARENT_GATE); + if (IS_ERR(hw)) { + kfree(mux); + kfree(fd); + kfree(gate); + } + + return hw; +} diff --git a/drivers/clk/imx/clk-divider-gate.c b/drivers/clk/imx/clk-divider-gate.c new file mode 100644 index 000000000000..df1f8429fe16 --- /dev/null +++ b/drivers/clk/imx/clk-divider-gate.c @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 NXP. + * Dong Aisheng <aisheng.dong@nxp.com> + */ + +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/slab.h> + +#include "clk.h" + +struct clk_divider_gate { + struct clk_divider divider; + u32 cached_val; +}; + +static inline struct clk_divider_gate *to_clk_divider_gate(struct clk_hw *hw) +{ + struct clk_divider *div = to_clk_divider(hw); + + return container_of(div, struct clk_divider_gate, divider); +} + +static unsigned long clk_divider_gate_recalc_rate_ro(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_divider *div = to_clk_divider(hw); + unsigned int val; + + val = clk_readl(div->reg) >> div->shift; + val &= clk_div_mask(div->width); + if (!val) + return 0; + + return divider_recalc_rate(hw, parent_rate, val, div->table, + div->flags, div->width); +} + +static unsigned long clk_divider_gate_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_divider_gate *div_gate = to_clk_divider_gate(hw); + struct clk_divider *div = to_clk_divider(hw); + unsigned long flags = 0; + unsigned int val; + + spin_lock_irqsave(div->lock, flags); + + if (!clk_hw_is_enabled(hw)) { + val = div_gate->cached_val; + } else { + val = clk_readl(div->reg) >> div->shift; + val &= clk_div_mask(div->width); + } + + spin_unlock_irqrestore(div->lock, flags); + + if (!val) + return 0; + + return divider_recalc_rate(hw, parent_rate, val, div->table, + div->flags, div->width); +} + +static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + return clk_divider_ops.round_rate(hw, rate, prate); +} + +static int clk_divider_gate_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_divider_gate *div_gate = to_clk_divider_gate(hw); + struct clk_divider *div = to_clk_divider(hw); + unsigned long flags = 0; + int value; + u32 val; + + value = divider_get_val(rate, parent_rate, div->table, + div->width, div->flags); + if (value < 0) + return value; + + spin_lock_irqsave(div->lock, flags); + + if (clk_hw_is_enabled(hw)) { + val = clk_readl(div->reg); + val &= ~(clk_div_mask(div->width) << div->shift); + val |= (u32)value << div->shift; + clk_writel(val, div->reg); + } else { + div_gate->cached_val = value; + } + + spin_unlock_irqrestore(div->lock, flags); + + return 0; +} + +static int clk_divider_enable(struct clk_hw *hw) +{ + struct clk_divider_gate *div_gate = to_clk_divider_gate(hw); + struct clk_divider *div = to_clk_divider(hw); + unsigned long flags = 0; + u32 val; + + if (!div_gate->cached_val) { + pr_err("%s: no valid preset rate\n", clk_hw_get_name(hw)); + return -EINVAL; + } + + spin_lock_irqsave(div->lock, flags); + /* restore div val */ + val = clk_readl(div->reg); + val |= div_gate->cached_val << div->shift; + clk_writel(val, div->reg); + + spin_unlock_irqrestore(div->lock, flags); + + return 0; +} + +static void clk_divider_disable(struct clk_hw *hw) +{ + struct clk_divider_gate *div_gate = to_clk_divider_gate(hw); + struct clk_divider *div = to_clk_divider(hw); + unsigned long flags = 0; + u32 val; + + spin_lock_irqsave(div->lock, flags); + + /* store the current div val */ + val = clk_readl(div->reg) >> div->shift; + val &= clk_div_mask(div->width); + div_gate->cached_val = val; + clk_writel(0, div->reg); + + spin_unlock_irqrestore(div->lock, flags); +} + +static int clk_divider_is_enabled(struct clk_hw *hw) +{ + struct clk_divider *div = to_clk_divider(hw); + u32 val; + + val = clk_readl(div->reg) >> div->shift; + val &= clk_div_mask(div->width); + + return val ? 1 : 0; +} + +static const struct clk_ops clk_divider_gate_ro_ops = { + .recalc_rate = clk_divider_gate_recalc_rate_ro, + .round_rate = clk_divider_round_rate, +}; + +static const struct clk_ops clk_divider_gate_ops = { + .recalc_rate = clk_divider_gate_recalc_rate, + .round_rate = clk_divider_round_rate, + .set_rate = clk_divider_gate_set_rate, + .enable = clk_divider_enable, + .disable = clk_divider_disable, + .is_enabled = clk_divider_is_enabled, +}; + +/* + * NOTE: In order to resue the most code from the common divider, + * we also design our divider following the way that provids an extra + * clk_divider_flags, however it's fixed to CLK_DIVIDER_ONE_BASED by + * default as our HW is. Besides that it supports only CLK_DIVIDER_READ_ONLY + * flag which can be specified by user flexibly. + */ +struct clk_hw *imx_clk_divider_gate(const char *name, const char *parent_name, + unsigned long flags, void __iomem *reg, + u8 shift, u8 width, u8 clk_divider_flags, + const struct clk_div_table *table, + spinlock_t *lock) +{ + struct clk_init_data init; + struct clk_divider_gate *div_gate; + struct clk_hw *hw; + u32 val; + int ret; + + div_gate = kzalloc(sizeof(*div_gate), GFP_KERNEL); + if (!div_gate) + return ERR_PTR(-ENOMEM); + + init.name = name; + if (clk_divider_flags & CLK_DIVIDER_READ_ONLY) + init.ops = &clk_divider_gate_ro_ops; + else + init.ops = &clk_divider_gate_ops; + init.flags = flags; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + + div_gate->divider.reg = reg; + div_gate->divider.shift = shift; + div_gate->divider.width = width; + div_gate->divider.lock = lock; + div_gate->divider.table = table; + div_gate->divider.hw.init = &init; + div_gate->divider.flags = CLK_DIVIDER_ONE_BASED | clk_divider_flags; + /* cache gate status */ + val = clk_readl(reg) >> shift; + val &= clk_div_mask(width); + div_gate->cached_val = val; + + hw = &div_gate->divider.hw; + ret = clk_hw_register(NULL, hw); + if (ret) { + kfree(div_gate); + hw = ERR_PTR(ret); + } + + return hw; +} diff --git a/drivers/clk/imx/clk-fixup-mux.c b/drivers/clk/imx/clk-fixup-mux.c index c9b327e0a8dd..44817c1b0b88 100644 --- a/drivers/clk/imx/clk-fixup-mux.c +++ b/drivers/clk/imx/clk-fixup-mux.c @@ -70,7 +70,7 @@ static const struct clk_ops clk_fixup_mux_ops = { }; struct clk *imx_clk_fixup_mux(const char *name, void __iomem *reg, - u8 shift, u8 width, const char **parents, + u8 shift, u8 width, const char * const *parents, int num_parents, void (*fixup)(u32 *val)) { struct clk_fixup_mux *fixup_mux; diff --git a/drivers/clk/imx/clk-imx7ulp.c b/drivers/clk/imx/clk-imx7ulp.c new file mode 100644 index 000000000000..3b7507ff7869 --- /dev/null +++ b/drivers/clk/imx/clk-imx7ulp.c @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Freescale Semiconductor, Inc. + * Copyright 2017~2018 NXP + * + * Author: Dong Aisheng <aisheng.dong@nxp.com> + * + */ + +#include <dt-bindings/clock/imx7ulp-clock.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "clk.h" + +static const char * const pll_pre_sels[] = { "sosc", "firc", }; +static const char * const spll_pfd_sels[] = { "spll_pfd0", "spll_pfd1", "spll_pfd2", "spll_pfd3", }; +static const char * const spll_sels[] = { "spll", "spll_pfd_sel", }; +static const char * const apll_pfd_sels[] = { "apll_pfd0", "apll_pfd1", "apll_pfd2", "apll_pfd3", }; +static const char * const apll_sels[] = { "apll", "apll_pfd_sel", }; +static const char * const scs_sels[] = { "dummy", "sosc", "sirc", "firc", "dummy", "apll_sel", "spll_sel", "upll", }; +static const char * const ddr_sels[] = { "apll_pfd_sel", "upll", }; +static const char * const nic_sels[] = { "firc", "ddr_clk", }; +static const char * const periph_plat_sels[] = { "dummy", "nic1_bus_clk", "nic1_clk", "ddr_clk", "apll_pfd2", "apll_pfd1", "apll_pfd0", "upll", }; +static const char * const periph_bus_sels[] = { "dummy", "sosc_bus_clk", "mpll", "firc_bus_clk", "rosc", "nic1_bus_clk", "nic1_clk", "spll_bus_clk", }; + +/* used by sosc/sirc/firc/ddr/spll/apll dividers */ +static const struct clk_div_table ulp_div_table[] = { + { .val = 1, .div = 1, }, + { .val = 2, .div = 2, }, + { .val = 3, .div = 4, }, + { .val = 4, .div = 8, }, + { .val = 5, .div = 16, }, + { .val = 6, .div = 32, }, + { .val = 7, .div = 64, }, +}; + +static void __init imx7ulp_clk_scg1_init(struct device_node *np) +{ + struct clk_hw_onecell_data *clk_data; + struct clk_hw **clks; + void __iomem *base; + + clk_data = kzalloc(sizeof(*clk_data) + sizeof(*clk_data->hws) * + IMX7ULP_CLK_SCG1_END, GFP_KERNEL); + if (!clk_data) + return; + + clk_data->num = IMX7ULP_CLK_SCG1_END; + clks = clk_data->hws; + + clks[IMX7ULP_CLK_DUMMY] = imx_clk_hw_fixed("dummy", 0); + + clks[IMX7ULP_CLK_ROSC] = imx_obtain_fixed_clk_hw(np, "rosc"); + clks[IMX7ULP_CLK_SOSC] = imx_obtain_fixed_clk_hw(np, "sosc"); + clks[IMX7ULP_CLK_SIRC] = imx_obtain_fixed_clk_hw(np, "sirc"); + clks[IMX7ULP_CLK_FIRC] = imx_obtain_fixed_clk_hw(np, "firc"); + clks[IMX7ULP_CLK_MIPI_PLL] = imx_obtain_fixed_clk_hw(np, "mpll"); + clks[IMX7ULP_CLK_UPLL] = imx_obtain_fixed_clk_hw(np, "upll"); + + /* SCG1 */ + base = of_iomap(np, 0); + WARN_ON(!base); + + /* NOTE: xPLL config can't be changed when xPLL is enabled */ + clks[IMX7ULP_CLK_APLL_PRE_SEL] = imx_clk_hw_mux_flags("apll_pre_sel", base + 0x508, 0, 1, pll_pre_sels, ARRAY_SIZE(pll_pre_sels), CLK_SET_PARENT_GATE); + clks[IMX7ULP_CLK_SPLL_PRE_SEL] = imx_clk_hw_mux_flags("spll_pre_sel", base + 0x608, 0, 1, pll_pre_sels, ARRAY_SIZE(pll_pre_sels), CLK_SET_PARENT_GATE); + + /* name parent_name reg shift width flags */ + clks[IMX7ULP_CLK_APLL_PRE_DIV] = imx_clk_hw_divider_flags("apll_pre_div", "apll_pre_sel", base + 0x508, 8, 3, CLK_SET_RATE_GATE); + clks[IMX7ULP_CLK_SPLL_PRE_DIV] = imx_clk_hw_divider_flags("spll_pre_div", "spll_pre_sel", base + 0x608, 8, 3, CLK_SET_RATE_GATE); + + /* name parent_name base */ + clks[IMX7ULP_CLK_APLL] = imx_clk_pllv4("apll", "apll_pre_div", base + 0x500); + clks[IMX7ULP_CLK_SPLL] = imx_clk_pllv4("spll", "spll_pre_div", base + 0x600); + + /* APLL PFDs */ + clks[IMX7ULP_CLK_APLL_PFD0] = imx_clk_pfdv2("apll_pfd0", "apll", base + 0x50c, 0); + clks[IMX7ULP_CLK_APLL_PFD1] = imx_clk_pfdv2("apll_pfd1", "apll", base + 0x50c, 1); + clks[IMX7ULP_CLK_APLL_PFD2] = imx_clk_pfdv2("apll_pfd2", "apll", base + 0x50c, 2); + clks[IMX7ULP_CLK_APLL_PFD3] = imx_clk_pfdv2("apll_pfd3", "apll", base + 0x50c, 3); + + /* SPLL PFDs */ + clks[IMX7ULP_CLK_SPLL_PFD0] = imx_clk_pfdv2("spll_pfd0", "spll", base + 0x60C, 0); + clks[IMX7ULP_CLK_SPLL_PFD1] = imx_clk_pfdv2("spll_pfd1", "spll", base + 0x60C, 1); + clks[IMX7ULP_CLK_SPLL_PFD2] = imx_clk_pfdv2("spll_pfd2", "spll", base + 0x60C, 2); + clks[IMX7ULP_CLK_SPLL_PFD3] = imx_clk_pfdv2("spll_pfd3", "spll", base + 0x60C, 3); + + /* PLL Mux */ + clks[IMX7ULP_CLK_APLL_PFD_SEL] = imx_clk_hw_mux_flags("apll_pfd_sel", base + 0x508, 14, 2, apll_pfd_sels, ARRAY_SIZE(apll_pfd_sels), CLK_SET_RATE_PARENT | CLK_SET_PARENT_GATE); + clks[IMX7ULP_CLK_SPLL_PFD_SEL] = imx_clk_hw_mux_flags("spll_pfd_sel", base + 0x608, 14, 2, spll_pfd_sels, ARRAY_SIZE(spll_pfd_sels), CLK_SET_RATE_PARENT | CLK_SET_PARENT_GATE); + clks[IMX7ULP_CLK_APLL_SEL] = imx_clk_hw_mux_flags("apll_sel", base + 0x508, 1, 1, apll_sels, ARRAY_SIZE(apll_sels), CLK_SET_RATE_PARENT | CLK_SET_PARENT_GATE); + clks[IMX7ULP_CLK_SPLL_SEL] = imx_clk_hw_mux_flags("spll_sel", base + 0x608, 1, 1, spll_sels, ARRAY_SIZE(spll_sels), CLK_SET_RATE_PARENT | CLK_SET_PARENT_GATE); + + clks[IMX7ULP_CLK_SPLL_BUS_CLK] = imx_clk_divider_gate("spll_bus_clk", "spll_sel", CLK_SET_RATE_GATE, base + 0x604, 8, 3, 0, ulp_div_table, &imx_ccm_lock); + + /* scs/ddr/nic select different clock source requires that clock to be enabled first */ + clks[IMX7ULP_CLK_SYS_SEL] = imx_clk_hw_mux2("scs_sel", base + 0x14, 24, 4, scs_sels, ARRAY_SIZE(scs_sels)); + clks[IMX7ULP_CLK_NIC_SEL] = imx_clk_hw_mux2("nic_sel", base + 0x40, 28, 1, nic_sels, ARRAY_SIZE(nic_sels)); + clks[IMX7ULP_CLK_DDR_SEL] = imx_clk_hw_mux_flags("ddr_sel", base + 0x30, 24, 1, ddr_sels, ARRAY_SIZE(ddr_sels), CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE); + + clks[IMX7ULP_CLK_CORE_DIV] = imx_clk_hw_divider_flags("divcore", "scs_sel", base + 0x14, 16, 4, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); + + clks[IMX7ULP_CLK_DDR_DIV] = imx_clk_divider_gate("ddr_clk", "ddr_sel", CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, base + 0x30, 0, 3, + 0, ulp_div_table, &imx_ccm_lock); + + clks[IMX7ULP_CLK_NIC0_DIV] = imx_clk_hw_divider_flags("nic0_clk", "nic_sel", base + 0x40, 24, 4, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); + clks[IMX7ULP_CLK_NIC1_DIV] = imx_clk_hw_divider_flags("nic1_clk", "nic0_clk", base + 0x40, 16, 4, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); + clks[IMX7ULP_CLK_NIC1_BUS_DIV] = imx_clk_hw_divider_flags("nic1_bus_clk", "nic1_clk", base + 0x40, 4, 4, CLK_SET_RATE_PARENT | CLK_IS_CRITICAL); + + clks[IMX7ULP_CLK_GPU_DIV] = imx_clk_hw_divider("gpu_clk", "nic0_clk", base + 0x40, 20, 4); + + clks[IMX7ULP_CLK_SOSC_BUS_CLK] = imx_clk_divider_gate("sosc_bus_clk", "sosc", 0, base + 0x104, 8, 3, + CLK_DIVIDER_READ_ONLY, ulp_div_table, &imx_ccm_lock); + clks[IMX7ULP_CLK_FIRC_BUS_CLK] = imx_clk_divider_gate("firc_bus_clk", "firc", 0, base + 0x304, 8, 3, + CLK_DIVIDER_READ_ONLY, ulp_div_table, &imx_ccm_lock); + + imx_check_clk_hws(clks, clk_data->num); + + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data); +} +CLK_OF_DECLARE(imx7ulp_clk_scg1, "fsl,imx7ulp-scg1", imx7ulp_clk_scg1_init); + +static void __init imx7ulp_clk_pcc2_init(struct device_node *np) +{ + struct clk_hw_onecell_data *clk_data; + struct clk_hw **clks; + void __iomem *base; + + clk_data = kzalloc(sizeof(*clk_data) + sizeof(*clk_data->hws) * + IMX7ULP_CLK_PCC2_END, GFP_KERNEL); + if (!clk_data) + return; + + clk_data->num = IMX7ULP_CLK_PCC2_END; + clks = clk_data->hws; + + /* PCC2 */ + base = of_iomap(np, 0); + WARN_ON(!base); + + clks[IMX7ULP_CLK_DMA1] = imx_clk_hw_gate("dma1", "nic1_clk", base + 0x20, 30); + clks[IMX7ULP_CLK_RGPIO2P1] = imx_clk_hw_gate("rgpio2p1", "nic1_bus_clk", base + 0x3c, 30); + clks[IMX7ULP_CLK_DMA_MUX1] = imx_clk_hw_gate("dma_mux1", "nic1_bus_clk", base + 0x84, 30); + clks[IMX7ULP_CLK_SNVS] = imx_clk_hw_gate("snvs", "nic1_bus_clk", base + 0x8c, 30); + clks[IMX7ULP_CLK_CAAM] = imx_clk_hw_gate("caam", "nic1_clk", base + 0x90, 30); + clks[IMX7ULP_CLK_LPTPM4] = imx7ulp_clk_composite("lptpm4", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x94); + clks[IMX7ULP_CLK_LPTPM5] = imx7ulp_clk_composite("lptpm5", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x98); + clks[IMX7ULP_CLK_LPIT1] = imx7ulp_clk_composite("lpit1", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x9c); + clks[IMX7ULP_CLK_LPSPI2] = imx7ulp_clk_composite("lpspi2", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xa4); + clks[IMX7ULP_CLK_LPSPI3] = imx7ulp_clk_composite("lpspi3", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xa8); + clks[IMX7ULP_CLK_LPI2C4] = imx7ulp_clk_composite("lpi2c4", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xac); + clks[IMX7ULP_CLK_LPI2C5] = imx7ulp_clk_composite("lpi2c5", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xb0); + clks[IMX7ULP_CLK_LPUART4] = imx7ulp_clk_composite("lpuart4", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xb4); + clks[IMX7ULP_CLK_LPUART5] = imx7ulp_clk_composite("lpuart5", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xb8); + clks[IMX7ULP_CLK_FLEXIO1] = imx7ulp_clk_composite("flexio1", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0xc4); + clks[IMX7ULP_CLK_USB0] = imx7ulp_clk_composite("usb0", periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, true, true, base + 0xcc); + clks[IMX7ULP_CLK_USB1] = imx7ulp_clk_composite("usb1", periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, true, true, base + 0xd0); + clks[IMX7ULP_CLK_USB_PHY] = imx_clk_hw_gate("usb_phy", "nic1_bus_clk", base + 0xd4, 30); + clks[IMX7ULP_CLK_USDHC0] = imx7ulp_clk_composite("usdhc0", periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, true, true, base + 0xdc); + clks[IMX7ULP_CLK_USDHC1] = imx7ulp_clk_composite("usdhc1", periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, true, true, base + 0xe0); + clks[IMX7ULP_CLK_WDG1] = imx7ulp_clk_composite("wdg1", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, true, true, base + 0xf4); + clks[IMX7ULP_CLK_WDG2] = imx7ulp_clk_composite("sdg2", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, true, true, base + 0x10c); + + imx_check_clk_hws(clks, clk_data->num); + + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data); +} +CLK_OF_DECLARE(imx7ulp_clk_pcc2, "fsl,imx7ulp-pcc2", imx7ulp_clk_pcc2_init); + +static void __init imx7ulp_clk_pcc3_init(struct device_node *np) +{ + struct clk_hw_onecell_data *clk_data; + struct clk_hw **clks; + void __iomem *base; + + clk_data = kzalloc(sizeof(*clk_data) + sizeof(*clk_data->hws) * + IMX7ULP_CLK_PCC3_END, GFP_KERNEL); + if (!clk_data) + return; + + clk_data->num = IMX7ULP_CLK_PCC3_END; + clks = clk_data->hws; + + /* PCC3 */ + base = of_iomap(np, 0); + WARN_ON(!base); + + clks[IMX7ULP_CLK_LPTPM6] = imx7ulp_clk_composite("lptpm6", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x84); + clks[IMX7ULP_CLK_LPTPM7] = imx7ulp_clk_composite("lptpm7", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x88); + + clks[IMX7ULP_CLK_MMDC] = clk_hw_register_gate(NULL, "mmdc", "nic1_clk", CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, + base + 0xac, 30, 0, &imx_ccm_lock); + clks[IMX7ULP_CLK_LPI2C6] = imx7ulp_clk_composite("lpi2c6", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x90); + clks[IMX7ULP_CLK_LPI2C7] = imx7ulp_clk_composite("lpi2c7", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x94); + clks[IMX7ULP_CLK_LPUART6] = imx7ulp_clk_composite("lpuart6", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x98); + clks[IMX7ULP_CLK_LPUART7] = imx7ulp_clk_composite("lpuart7", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, false, true, base + 0x9c); + clks[IMX7ULP_CLK_DSI] = imx7ulp_clk_composite("dsi", periph_bus_sels, ARRAY_SIZE(periph_bus_sels), true, true, true, base + 0xa4); + clks[IMX7ULP_CLK_LCDIF] = imx7ulp_clk_composite("lcdif", periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, true, true, base + 0xa8); + + clks[IMX7ULP_CLK_VIU] = imx_clk_hw_gate("viu", "nic1_clk", base + 0xa0, 30); + clks[IMX7ULP_CLK_PCTLC] = imx_clk_hw_gate("pctlc", "nic1_bus_clk", base + 0xb8, 30); + clks[IMX7ULP_CLK_PCTLD] = imx_clk_hw_gate("pctld", "nic1_bus_clk", base + 0xbc, 30); + clks[IMX7ULP_CLK_PCTLE] = imx_clk_hw_gate("pctle", "nic1_bus_clk", base + 0xc0, 30); + clks[IMX7ULP_CLK_PCTLF] = imx_clk_hw_gate("pctlf", "nic1_bus_clk", base + 0xc4, 30); + + clks[IMX7ULP_CLK_GPU3D] = imx7ulp_clk_composite("gpu3d", periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, false, true, base + 0x140); + clks[IMX7ULP_CLK_GPU2D] = imx7ulp_clk_composite("gpu2d", periph_plat_sels, ARRAY_SIZE(periph_plat_sels), true, false, true, base + 0x144); + + imx_check_clk_hws(clks, clk_data->num); + + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data); +} +CLK_OF_DECLARE(imx7ulp_clk_pcc3, "fsl,imx7ulp-pcc3", imx7ulp_clk_pcc3_init); diff --git a/drivers/clk/imx/clk-pfdv2.c b/drivers/clk/imx/clk-pfdv2.c new file mode 100644 index 000000000000..7e9134b205ab --- /dev/null +++ b/drivers/clk/imx/clk-pfdv2.c @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Freescale Semiconductor, Inc. + * Copyright 2017~2018 NXP + * + * Author: Dong Aisheng <aisheng.dong@nxp.com> + * + */ + +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/iopoll.h> +#include <linux/slab.h> + +#include "clk.h" + +/** + * struct clk_pfdv2 - IMX PFD clock + * @clk_hw: clock source + * @reg: PFD register address + * @gate_bit: Gate bit offset + * @vld_bit: Valid bit offset + * @frac_off: PLL Fractional Divider offset + */ + +struct clk_pfdv2 { + struct clk_hw hw; + void __iomem *reg; + u8 gate_bit; + u8 vld_bit; + u8 frac_off; +}; + +#define to_clk_pfdv2(_hw) container_of(_hw, struct clk_pfdv2, hw) + +#define CLK_PFDV2_FRAC_MASK 0x3f + +#define LOCK_TIMEOUT_US USEC_PER_MSEC + +static DEFINE_SPINLOCK(pfd_lock); + +static int clk_pfdv2_wait(struct clk_pfdv2 *pfd) +{ + u32 val; + + return readl_poll_timeout(pfd->reg, val, val & pfd->vld_bit, + 0, LOCK_TIMEOUT_US); +} + +static int clk_pfdv2_enable(struct clk_hw *hw) +{ + struct clk_pfdv2 *pfd = to_clk_pfdv2(hw); + unsigned long flags; + u32 val; + + spin_lock_irqsave(&pfd_lock, flags); + val = readl_relaxed(pfd->reg); + val &= ~pfd->gate_bit; + writel_relaxed(val, pfd->reg); + spin_unlock_irqrestore(&pfd_lock, flags); + + return clk_pfdv2_wait(pfd); +} + +static void clk_pfdv2_disable(struct clk_hw *hw) +{ + struct clk_pfdv2 *pfd = to_clk_pfdv2(hw); + unsigned long flags; + u32 val; + + spin_lock_irqsave(&pfd_lock, flags); + val = readl_relaxed(pfd->reg); + val |= pfd->gate_bit; + writel_relaxed(val, pfd->reg); + spin_unlock_irqrestore(&pfd_lock, flags); +} + +static unsigned long clk_pfdv2_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_pfdv2 *pfd = to_clk_pfdv2(hw); + u64 tmp = parent_rate; + u8 frac; + + frac = (readl_relaxed(pfd->reg) >> pfd->frac_off) + & CLK_PFDV2_FRAC_MASK; + + if (!frac) { + pr_debug("clk_pfdv2: %s invalid pfd frac value 0\n", + clk_hw_get_name(hw)); + return 0; + } + + tmp *= 18; + do_div(tmp, frac); + + return tmp; +} + +static long clk_pfdv2_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + u64 tmp = *prate; + u8 frac; + + tmp = tmp * 18 + rate / 2; + do_div(tmp, rate); + frac = tmp; + + if (frac < 12) + frac = 12; + else if (frac > 35) + frac = 35; + + tmp = *prate; + tmp *= 18; + do_div(tmp, frac); + + return tmp; +} + +static int clk_pfdv2_is_enabled(struct clk_hw *hw) +{ + struct clk_pfdv2 *pfd = to_clk_pfdv2(hw); + + if (readl_relaxed(pfd->reg) & pfd->gate_bit) + return 0; + + return 1; +} + +static int clk_pfdv2_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_pfdv2 *pfd = to_clk_pfdv2(hw); + unsigned long flags; + u64 tmp = parent_rate; + u32 val; + u8 frac; + + tmp = tmp * 18 + rate / 2; + do_div(tmp, rate); + frac = tmp; + if (frac < 12) + frac = 12; + else if (frac > 35) + frac = 35; + + spin_lock_irqsave(&pfd_lock, flags); + val = readl_relaxed(pfd->reg); + val &= ~(CLK_PFDV2_FRAC_MASK << pfd->frac_off); + val |= frac << pfd->frac_off; + writel_relaxed(val, pfd->reg); + spin_unlock_irqrestore(&pfd_lock, flags); + + return 0; +} + +static const struct clk_ops clk_pfdv2_ops = { + .enable = clk_pfdv2_enable, + .disable = clk_pfdv2_disable, + .recalc_rate = clk_pfdv2_recalc_rate, + .round_rate = clk_pfdv2_round_rate, + .set_rate = clk_pfdv2_set_rate, + .is_enabled = clk_pfdv2_is_enabled, +}; + +struct clk_hw *imx_clk_pfdv2(const char *name, const char *parent_name, + void __iomem *reg, u8 idx) +{ + struct clk_init_data init; + struct clk_pfdv2 *pfd; + struct clk_hw *hw; + int ret; + + WARN_ON(idx > 3); + + pfd = kzalloc(sizeof(*pfd), GFP_KERNEL); + if (!pfd) + return ERR_PTR(-ENOMEM); + + pfd->reg = reg; + pfd->gate_bit = 1 << ((idx + 1) * 8 - 1); + pfd->vld_bit = pfd->gate_bit - 1; + pfd->frac_off = idx * 8; + + init.name = name; + init.ops = &clk_pfdv2_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + init.flags = CLK_SET_RATE_GATE; + + pfd->hw.init = &init; + + hw = &pfd->hw; + ret = clk_hw_register(NULL, hw); + if (ret) { + kfree(pfd); + hw = ERR_PTR(ret); + } + + return hw; +} diff --git a/drivers/clk/imx/clk-pllv4.c b/drivers/clk/imx/clk-pllv4.c new file mode 100644 index 000000000000..d38bc9f87c1d --- /dev/null +++ b/drivers/clk/imx/clk-pllv4.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Freescale Semiconductor, Inc. + * Copyright 2017~2018 NXP + * + * Author: Dong Aisheng <aisheng.dong@nxp.com> + * + */ + +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/iopoll.h> +#include <linux/slab.h> + +#include "clk.h" + +/* PLL Control Status Register (xPLLCSR) */ +#define PLL_CSR_OFFSET 0x0 +#define PLL_VLD BIT(24) +#define PLL_EN BIT(0) + +/* PLL Configuration Register (xPLLCFG) */ +#define PLL_CFG_OFFSET 0x08 +#define BP_PLL_MULT 16 +#define BM_PLL_MULT (0x7f << 16) + +/* PLL Numerator Register (xPLLNUM) */ +#define PLL_NUM_OFFSET 0x10 + +/* PLL Denominator Register (xPLLDENOM) */ +#define PLL_DENOM_OFFSET 0x14 + +struct clk_pllv4 { + struct clk_hw hw; + void __iomem *base; +}; + +/* Valid PLL MULT Table */ +static const int pllv4_mult_table[] = {33, 27, 22, 20, 17, 16}; + +#define to_clk_pllv4(__hw) container_of(__hw, struct clk_pllv4, hw) + +#define LOCK_TIMEOUT_US USEC_PER_MSEC + +static inline int clk_pllv4_wait_lock(struct clk_pllv4 *pll) +{ + u32 csr; + + return readl_poll_timeout(pll->base + PLL_CSR_OFFSET, + csr, csr & PLL_VLD, 0, LOCK_TIMEOUT_US); +} + +static int clk_pllv4_is_enabled(struct clk_hw *hw) +{ + struct clk_pllv4 *pll = to_clk_pllv4(hw); + + if (readl_relaxed(pll->base) & PLL_EN) + return 1; + + return 0; +} + +static unsigned long clk_pllv4_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_pllv4 *pll = to_clk_pllv4(hw); + u32 div; + + div = readl_relaxed(pll->base + PLL_CFG_OFFSET); + div &= BM_PLL_MULT; + div >>= BP_PLL_MULT; + + return parent_rate * div; +} + +static long clk_pllv4_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + unsigned long parent_rate = *prate; + unsigned long round_rate, i; + + for (i = 0; i < ARRAY_SIZE(pllv4_mult_table); i++) { + round_rate = parent_rate * pllv4_mult_table[i]; + if (rate >= round_rate) + return round_rate; + } + + return round_rate; +} + +static bool clk_pllv4_is_valid_mult(unsigned int mult) +{ + int i; + + /* check if mult is in valid MULT table */ + for (i = 0; i < ARRAY_SIZE(pllv4_mult_table); i++) { + if (pllv4_mult_table[i] == mult) + return true; + } + + return false; +} + +static int clk_pllv4_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_pllv4 *pll = to_clk_pllv4(hw); + u32 val, mult; + + mult = rate / parent_rate; + + if (!clk_pllv4_is_valid_mult(mult)) + return -EINVAL; + + val = readl_relaxed(pll->base + PLL_CFG_OFFSET); + val &= ~BM_PLL_MULT; + val |= mult << BP_PLL_MULT; + writel_relaxed(val, pll->base + PLL_CFG_OFFSET); + + return 0; +} + +static int clk_pllv4_enable(struct clk_hw *hw) +{ + u32 val; + struct clk_pllv4 *pll = to_clk_pllv4(hw); + + val = readl_relaxed(pll->base); + val |= PLL_EN; + writel_relaxed(val, pll->base); + + return clk_pllv4_wait_lock(pll); +} + +static void clk_pllv4_disable(struct clk_hw *hw) +{ + u32 val; + struct clk_pllv4 *pll = to_clk_pllv4(hw); + + val = readl_relaxed(pll->base); + val &= ~PLL_EN; + writel_relaxed(val, pll->base); +} + +static const struct clk_ops clk_pllv4_ops = { + .recalc_rate = clk_pllv4_recalc_rate, + .round_rate = clk_pllv4_round_rate, + .set_rate = clk_pllv4_set_rate, + .enable = clk_pllv4_enable, + .disable = clk_pllv4_disable, + .is_enabled = clk_pllv4_is_enabled, +}; + +struct clk_hw *imx_clk_pllv4(const char *name, const char *parent_name, + void __iomem *base) +{ + struct clk_pllv4 *pll; + struct clk_hw *hw; + struct clk_init_data init; + int ret; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + pll->base = base; + + init.name = name; + init.ops = &clk_pllv4_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + init.flags = CLK_SET_RATE_GATE; + + pll->hw.init = &init; + + hw = &pll->hw; + ret = clk_hw_register(NULL, hw); + if (ret) { + kfree(pll); + hw = ERR_PTR(ret); + } + + return hw; +} diff --git a/drivers/clk/imx/clk.c b/drivers/clk/imx/clk.c index 9074e6974b6d..1efed86217f7 100644 --- a/drivers/clk/imx/clk.c +++ b/drivers/clk/imx/clk.c @@ -18,6 +18,16 @@ void __init imx_check_clocks(struct clk *clks[], unsigned int count) i, PTR_ERR(clks[i])); } +void imx_check_clk_hws(struct clk_hw *clks[], unsigned int count) +{ + unsigned int i; + + for (i = 0; i < count; i++) + if (IS_ERR(clks[i])) + pr_err("i.MX clk %u: register failed with %ld\n", + i, PTR_ERR(clks[i])); +} + static struct clk * __init imx_obtain_fixed_clock_from_dt(const char *name) { struct of_phandle_args phandle; @@ -49,6 +59,18 @@ struct clk * __init imx_obtain_fixed_clock( return clk; } +struct clk_hw * __init imx_obtain_fixed_clk_hw(struct device_node *np, + const char *name) +{ + struct clk *clk; + + clk = of_clk_get_by_name(np, name); + if (IS_ERR(clk)) + return ERR_PTR(-ENOENT); + + return __clk_get_hw(clk); +} + /* * This fixups the register CCM_CSCMR1 write value. * The write/read/divider values of the aclk_podf field diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index 5895e2237b6c..a34b93e48e0b 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -8,6 +8,7 @@ extern spinlock_t imx_ccm_lock; void imx_check_clocks(struct clk *clks[], unsigned int count); +void imx_check_clk_hws(struct clk_hw *clks[], unsigned int count); void imx_register_uart_clocks(struct clk ** const clks[]); extern void imx_cscmr1_fixup(u32 *val); @@ -42,6 +43,9 @@ enum imx_pllv3_type { struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name, const char *parent_name, void __iomem *base, u32 div_mask); +struct clk_hw *imx_clk_pllv4(const char *name, const char *parent_name, + void __iomem *base); + struct clk *clk_register_gate2(struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 bit_idx, u8 cgr_val, @@ -51,26 +55,38 @@ struct clk *clk_register_gate2(struct device *dev, const char *name, struct clk * imx_obtain_fixed_clock( const char *name, unsigned long rate); +struct clk_hw *imx_obtain_fixed_clk_hw(struct device_node *np, + const char *name); + struct clk *imx_clk_gate_exclusive(const char *name, const char *parent, void __iomem *reg, u8 shift, u32 exclusive_mask); struct clk *imx_clk_pfd(const char *name, const char *parent_name, void __iomem *reg, u8 idx); +struct clk_hw *imx_clk_pfdv2(const char *name, const char *parent_name, + void __iomem *reg, u8 idx); + struct clk *imx_clk_busy_divider(const char *name, const char *parent_name, void __iomem *reg, u8 shift, u8 width, void __iomem *busy_reg, u8 busy_shift); struct clk *imx_clk_busy_mux(const char *name, void __iomem *reg, u8 shift, u8 width, void __iomem *busy_reg, u8 busy_shift, - const char **parent_names, int num_parents); + const char * const *parent_names, int num_parents); + +struct clk_hw *imx7ulp_clk_composite(const char *name, + const char * const *parent_names, + int num_parents, bool mux_present, + bool rate_present, bool gate_present, + void __iomem *reg); struct clk *imx_clk_fixup_divider(const char *name, const char *parent, void __iomem *reg, u8 shift, u8 width, void (*fixup)(u32 *val)); struct clk *imx_clk_fixup_mux(const char *name, void __iomem *reg, - u8 shift, u8 width, const char **parents, + u8 shift, u8 width, const char * const *parents, int num_parents, void (*fixup)(u32 *val)); static inline struct clk *imx_clk_fixed(const char *name, int rate) @@ -78,8 +94,19 @@ static inline struct clk *imx_clk_fixed(const char *name, int rate) return clk_register_fixed_rate(NULL, name, NULL, 0, rate); } +static inline struct clk_hw *imx_clk_hw_fixed(const char *name, int rate) +{ + return clk_hw_register_fixed_rate(NULL, name, NULL, 0, rate); +} + +static inline struct clk_hw *imx_get_clk_hw_fixed(const char *name, int rate) +{ + return clk_hw_register_fixed_rate(NULL, name, NULL, 0, rate); +} + static inline struct clk *imx_clk_mux_ldb(const char *name, void __iomem *reg, - u8 shift, u8 width, const char **parents, int num_parents) + u8 shift, u8 width, const char * const *parents, + int num_parents) { return clk_register_mux(NULL, name, parents, num_parents, CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT, reg, @@ -100,6 +127,15 @@ static inline struct clk *imx_clk_divider(const char *name, const char *parent, reg, shift, width, 0, &imx_ccm_lock); } +static inline struct clk_hw *imx_clk_hw_divider(const char *name, + const char *parent, + void __iomem *reg, u8 shift, + u8 width) +{ + return clk_hw_register_divider(NULL, name, parent, CLK_SET_RATE_PARENT, + reg, shift, width, 0, &imx_ccm_lock); +} + static inline struct clk *imx_clk_divider_flags(const char *name, const char *parent, void __iomem *reg, u8 shift, u8 width, unsigned long flags) @@ -108,6 +144,15 @@ static inline struct clk *imx_clk_divider_flags(const char *name, reg, shift, width, 0, &imx_ccm_lock); } +static inline struct clk_hw *imx_clk_hw_divider_flags(const char *name, + const char *parent, + void __iomem *reg, u8 shift, + u8 width, unsigned long flags) +{ + return clk_hw_register_divider(NULL, name, parent, flags, + reg, shift, width, 0, &imx_ccm_lock); +} + static inline struct clk *imx_clk_divider2(const char *name, const char *parent, void __iomem *reg, u8 shift, u8 width) { @@ -130,6 +175,13 @@ static inline struct clk *imx_clk_gate_flags(const char *name, const char *paren shift, 0, &imx_ccm_lock); } +static inline struct clk_hw *imx_clk_hw_gate(const char *name, const char *parent, + void __iomem *reg, u8 shift) +{ + return clk_hw_register_gate(NULL, name, parent, CLK_SET_RATE_PARENT, reg, + shift, 0, &imx_ccm_lock); +} + static inline struct clk *imx_clk_gate_dis(const char *name, const char *parent, void __iomem *reg, u8 shift) { @@ -199,7 +251,8 @@ static inline struct clk *imx_clk_gate4(const char *name, const char *parent, } static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg, - u8 shift, u8 width, const char **parents, int num_parents) + u8 shift, u8 width, const char * const *parents, + int num_parents) { return clk_register_mux(NULL, name, parents, num_parents, CLK_SET_RATE_NO_REPARENT, reg, shift, @@ -207,24 +260,53 @@ static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg, } static inline struct clk *imx_clk_mux2(const char *name, void __iomem *reg, - u8 shift, u8 width, const char **parents, int num_parents) + u8 shift, u8 width, const char * const *parents, + int num_parents) { return clk_register_mux(NULL, name, parents, num_parents, CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE, reg, shift, width, 0, &imx_ccm_lock); } +static inline struct clk_hw *imx_clk_hw_mux2(const char *name, void __iomem *reg, + u8 shift, u8 width, + const char * const *parents, + int num_parents) +{ + return clk_hw_register_mux(NULL, name, parents, num_parents, + CLK_SET_RATE_NO_REPARENT | + CLK_OPS_PARENT_ENABLE, + reg, shift, width, 0, &imx_ccm_lock); +} + static inline struct clk *imx_clk_mux_flags(const char *name, - void __iomem *reg, u8 shift, u8 width, const char **parents, - int num_parents, unsigned long flags) + void __iomem *reg, u8 shift, u8 width, + const char * const *parents, int num_parents, + unsigned long flags) { return clk_register_mux(NULL, name, parents, num_parents, flags | CLK_SET_RATE_NO_REPARENT, reg, shift, width, 0, &imx_ccm_lock); } +static inline struct clk_hw *imx_clk_hw_mux_flags(const char *name, + void __iomem *reg, u8 shift, + u8 width, + const char * const *parents, + int num_parents, + unsigned long flags) +{ + return clk_hw_register_mux(NULL, name, parents, num_parents, + flags | CLK_SET_RATE_NO_REPARENT, + reg, shift, width, 0, &imx_ccm_lock); +} + struct clk *imx_clk_cpu(const char *name, const char *parent_name, struct clk *div, struct clk *mux, struct clk *pll, struct clk *step); +struct clk_hw *imx_clk_divider_gate(const char *name, const char *parent_name, + unsigned long flags, void __iomem *reg, u8 shift, u8 width, + u8 clk_divider_flags, const struct clk_div_table *table, + spinlock_t *lock); #endif |