Message ID | 1583226206-19758-5-git-send-email-abel.vesa@nxp.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Add generic MFD i.MX mix and audiomix support | expand |
> Subject: [RFC 04/11] clk: imx: Add gate shared for i.MX8MP audiomix > > The newer i.MX platform use some gates that have a shared control bit > between them. Could the existing clk_hw_register_gate2 handle your case? Thanks, Peng. > > Signed-off-by: Abel Vesa <abel.vesa@nxp.com> > --- > drivers/clk/imx/Makefile | 2 +- > drivers/clk/imx/clk-gate-shared.c | 111 > ++++++++++++++++++++++++++++++++++++++ > drivers/clk/imx/clk.h | 4 ++ > 3 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 > drivers/clk/imx/clk-gate-shared.c > > diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile index > 928f874c..799a8ef 100644 > --- a/drivers/clk/imx/Makefile > +++ b/drivers/clk/imx/Makefile > @@ -27,7 +27,7 @@ obj-$(CONFIG_MXC_CLK_SCU) += \ > > obj-$(CONFIG_CLK_IMX8MM) += clk-imx8mm.o > obj-$(CONFIG_CLK_IMX8MN) += clk-imx8mn.o > -obj-$(CONFIG_CLK_IMX8MP) += clk-imx8mp.o > +obj-$(CONFIG_CLK_IMX8MP) += clk-imx8mp.o clk-gate-shared.o > obj-$(CONFIG_CLK_IMX8MQ) += clk-imx8mq.o > obj-$(CONFIG_CLK_IMX8QXP) += clk-imx8qxp.o clk-imx8qxp-lpcg.o > > diff --git a/drivers/clk/imx/clk-gate-shared.c > b/drivers/clk/imx/clk-gate-shared.c > new file mode 100644 > index 00000000..961a0e3 > --- /dev/null > +++ b/drivers/clk/imx/clk-gate-shared.c > @@ -0,0 +1,111 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright 2019 NXP. > + */ > + > +#include <linux/clk-provider.h> > +#include <linux/err.h> > +#include <linux/io.h> > +#include <linux/slab.h> > +#include "clk.h" > + > +/** > + * struct clk_gate_shared - i.MX specific gate clock having the gate > +flag > + * shared with other gate clocks > + */ > +struct clk_gate_shared { > + struct clk_gate gate; > + spinlock_t *lock; > + unsigned int *share_count; > +}; > + > +static int clk_gate_shared_enable(struct clk_hw *hw) { > + struct clk_gate *gate = to_clk_gate(hw); > + struct clk_gate_shared *shgate = container_of(gate, > + struct clk_gate_shared, gate); > + unsigned long flags = 0; > + int ret = 0; > + > + spin_lock_irqsave(shgate->lock, flags); > + > + if (shgate->share_count && (*shgate->share_count)++ > 0) > + goto out; > + > + ret = clk_gate_ops.enable(hw); > +out: > + spin_unlock_irqrestore(shgate->lock, flags); > + > + return ret; > +} > + > +static void clk_gate_shared_disable(struct clk_hw *hw) { > + struct clk_gate *gate = to_clk_gate(hw); > + struct clk_gate_shared *shgate = container_of(gate, > + struct clk_gate_shared, gate); > + unsigned long flags = 0; > + > + spin_lock_irqsave(shgate->lock, flags); > + > + if (shgate->share_count) { > + if (WARN_ON(*shgate->share_count == 0)) > + goto out; > + else if (--(*shgate->share_count) > 0) > + goto out; > + } > + > + clk_gate_ops.disable(hw); > +out: > + spin_unlock_irqrestore(shgate->lock, flags); } > + > +static int clk_gate_shared_is_enabled(struct clk_hw *hw) { > + return clk_gate_ops.is_enabled(hw); > +} > + > +static const struct clk_ops clk_gate_shared_ops = { > + .enable = clk_gate_shared_enable, > + .disable = clk_gate_shared_disable, > + .is_enabled = clk_gate_shared_is_enabled, }; > + > +struct clk_hw *imx_dev_clk_hw_gate_shared(struct device *dev, const char > *name, > + const char *parent, void __iomem *reg, > + u8 shift, unsigned int *share_count) { > + struct clk_gate_shared *shgate; > + struct clk_gate *gate; > + struct clk_hw *hw; > + struct clk_init_data init; > + int ret; > + > + shgate = kzalloc(sizeof(*shgate), GFP_KERNEL); > + if (!shgate) > + return ERR_PTR(-ENOMEM); > + gate = &shgate->gate; > + > + init.name = name; > + init.ops = &clk_gate_shared_ops; > + init.flags = CLK_OPS_PARENT_ENABLE; > + init.parent_names = parent ? &parent : NULL; > + init.num_parents = parent ? 1 : 0; > + > + gate->reg = reg; > + gate->bit_idx = shift; > + gate->lock = NULL; > + gate->hw.init = &init; > + shgate->lock = &imx_ccm_lock; > + shgate->share_count = share_count; > + > + hw = &gate->hw; > + > + ret = clk_hw_register(NULL, hw); > + if (ret) { > + kfree(shgate); > + return ERR_PTR(ret); > + } > + > + return hw; > +} > diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index > f074dd8..51d6c26 100644 > --- a/drivers/clk/imx/clk.h > +++ b/drivers/clk/imx/clk.h > @@ -151,6 +151,10 @@ struct clk_hw *imx_clk_hw_sscg_pll(const char > *name, > void __iomem *base, > unsigned long flags); > > +struct clk_hw *imx_dev_clk_hw_gate_shared(struct device *dev, const char > *name, > + const char *parent, void __iomem *reg, > + u8 shift, unsigned int *share_count); > + > enum imx_pllv3_type { > IMX_PLLV3_GENERIC, > IMX_PLLV3_SYS, > -- > 2.7.4
diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile index 928f874c..799a8ef 100644 --- a/drivers/clk/imx/Makefile +++ b/drivers/clk/imx/Makefile @@ -27,7 +27,7 @@ obj-$(CONFIG_MXC_CLK_SCU) += \ obj-$(CONFIG_CLK_IMX8MM) += clk-imx8mm.o obj-$(CONFIG_CLK_IMX8MN) += clk-imx8mn.o -obj-$(CONFIG_CLK_IMX8MP) += clk-imx8mp.o +obj-$(CONFIG_CLK_IMX8MP) += clk-imx8mp.o clk-gate-shared.o obj-$(CONFIG_CLK_IMX8MQ) += clk-imx8mq.o obj-$(CONFIG_CLK_IMX8QXP) += clk-imx8qxp.o clk-imx8qxp-lpcg.o diff --git a/drivers/clk/imx/clk-gate-shared.c b/drivers/clk/imx/clk-gate-shared.c new file mode 100644 index 00000000..961a0e3 --- /dev/null +++ b/drivers/clk/imx/clk-gate-shared.c @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 NXP. + */ + +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/slab.h> +#include "clk.h" + +/** + * struct clk_gate_shared - i.MX specific gate clock having the gate flag + * shared with other gate clocks + */ +struct clk_gate_shared { + struct clk_gate gate; + spinlock_t *lock; + unsigned int *share_count; +}; + +static int clk_gate_shared_enable(struct clk_hw *hw) +{ + struct clk_gate *gate = to_clk_gate(hw); + struct clk_gate_shared *shgate = container_of(gate, + struct clk_gate_shared, gate); + unsigned long flags = 0; + int ret = 0; + + spin_lock_irqsave(shgate->lock, flags); + + if (shgate->share_count && (*shgate->share_count)++ > 0) + goto out; + + ret = clk_gate_ops.enable(hw); +out: + spin_unlock_irqrestore(shgate->lock, flags); + + return ret; +} + +static void clk_gate_shared_disable(struct clk_hw *hw) +{ + struct clk_gate *gate = to_clk_gate(hw); + struct clk_gate_shared *shgate = container_of(gate, + struct clk_gate_shared, gate); + unsigned long flags = 0; + + spin_lock_irqsave(shgate->lock, flags); + + if (shgate->share_count) { + if (WARN_ON(*shgate->share_count == 0)) + goto out; + else if (--(*shgate->share_count) > 0) + goto out; + } + + clk_gate_ops.disable(hw); +out: + spin_unlock_irqrestore(shgate->lock, flags); +} + +static int clk_gate_shared_is_enabled(struct clk_hw *hw) +{ + return clk_gate_ops.is_enabled(hw); +} + +static const struct clk_ops clk_gate_shared_ops = { + .enable = clk_gate_shared_enable, + .disable = clk_gate_shared_disable, + .is_enabled = clk_gate_shared_is_enabled, +}; + +struct clk_hw *imx_dev_clk_hw_gate_shared(struct device *dev, const char *name, + const char *parent, void __iomem *reg, + u8 shift, unsigned int *share_count) +{ + struct clk_gate_shared *shgate; + struct clk_gate *gate; + struct clk_hw *hw; + struct clk_init_data init; + int ret; + + shgate = kzalloc(sizeof(*shgate), GFP_KERNEL); + if (!shgate) + return ERR_PTR(-ENOMEM); + gate = &shgate->gate; + + init.name = name; + init.ops = &clk_gate_shared_ops; + init.flags = CLK_OPS_PARENT_ENABLE; + init.parent_names = parent ? &parent : NULL; + init.num_parents = parent ? 1 : 0; + + gate->reg = reg; + gate->bit_idx = shift; + gate->lock = NULL; + gate->hw.init = &init; + shgate->lock = &imx_ccm_lock; + shgate->share_count = share_count; + + hw = &gate->hw; + + ret = clk_hw_register(NULL, hw); + if (ret) { + kfree(shgate); + return ERR_PTR(ret); + } + + return hw; +} diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index f074dd8..51d6c26 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -151,6 +151,10 @@ struct clk_hw *imx_clk_hw_sscg_pll(const char *name, void __iomem *base, unsigned long flags); +struct clk_hw *imx_dev_clk_hw_gate_shared(struct device *dev, const char *name, + const char *parent, void __iomem *reg, + u8 shift, unsigned int *share_count); + enum imx_pllv3_type { IMX_PLLV3_GENERIC, IMX_PLLV3_SYS,
The newer i.MX platform use some gates that have a shared control bit between them. Signed-off-by: Abel Vesa <abel.vesa@nxp.com> --- drivers/clk/imx/Makefile | 2 +- drivers/clk/imx/clk-gate-shared.c | 111 ++++++++++++++++++++++++++++++++++++++ drivers/clk/imx/clk.h | 4 ++ 3 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/imx/clk-gate-shared.c