176a323c1SA.s. Dong // SPDX-License-Identifier: GPL-2.0+
276a323c1SA.s. Dong /*
376a323c1SA.s. Dong * Copyright (C) 2016 Freescale Semiconductor, Inc.
476a323c1SA.s. Dong * Copyright 2017~2018 NXP
576a323c1SA.s. Dong *
676a323c1SA.s. Dong */
776a323c1SA.s. Dong
87d6b5e4fSAnson Huang #include <linux/bits.h>
976a323c1SA.s. Dong #include <linux/clk-provider.h>
1076a323c1SA.s. Dong #include <linux/err.h>
110f6e3c15SAnson Huang #include <linux/io.h>
1276a323c1SA.s. Dong #include <linux/slab.h>
1376a323c1SA.s. Dong
14928f9e26SAndy Shevchenko #include "../clk-fractional-divider.h"
1576a323c1SA.s. Dong #include "clk.h"
1676a323c1SA.s. Dong
1776a323c1SA.s. Dong #define PCG_PCS_SHIFT 24
1876a323c1SA.s. Dong #define PCG_PCS_MASK 0x7
1976a323c1SA.s. Dong #define PCG_CGC_SHIFT 30
2076a323c1SA.s. Dong #define PCG_FRAC_SHIFT 3
2176a323c1SA.s. Dong #define PCG_FRAC_WIDTH 1
2276a323c1SA.s. Dong #define PCG_PCD_SHIFT 0
2376a323c1SA.s. Dong #define PCG_PCD_WIDTH 3
2476a323c1SA.s. Dong
25b40ba806SJacky Bai #define SW_RST BIT(28)
26b40ba806SJacky Bai
pcc_gate_enable(struct clk_hw * hw)27b40ba806SJacky Bai static int pcc_gate_enable(struct clk_hw *hw)
28b40ba806SJacky Bai {
29b40ba806SJacky Bai struct clk_gate *gate = to_clk_gate(hw);
303fa36200SJacky Bai unsigned long flags;
31b40ba806SJacky Bai u32 val;
32b40ba806SJacky Bai int ret;
33b40ba806SJacky Bai
34b40ba806SJacky Bai ret = clk_gate_ops.enable(hw);
35b40ba806SJacky Bai if (ret)
36b40ba806SJacky Bai return ret;
37b40ba806SJacky Bai
383fa36200SJacky Bai spin_lock_irqsave(gate->lock, flags);
39b40ba806SJacky Bai /*
40b40ba806SJacky Bai * release the sw reset for peripherals associated with
41b40ba806SJacky Bai * with this pcc clock.
42b40ba806SJacky Bai */
43b40ba806SJacky Bai val = readl(gate->reg);
44b40ba806SJacky Bai val |= SW_RST;
45b40ba806SJacky Bai writel(val, gate->reg);
46b40ba806SJacky Bai
473fa36200SJacky Bai spin_unlock_irqrestore(gate->lock, flags);
483fa36200SJacky Bai
49b40ba806SJacky Bai return 0;
50b40ba806SJacky Bai }
51b40ba806SJacky Bai
pcc_gate_disable(struct clk_hw * hw)52b40ba806SJacky Bai static void pcc_gate_disable(struct clk_hw *hw)
53b40ba806SJacky Bai {
54b40ba806SJacky Bai clk_gate_ops.disable(hw);
55b40ba806SJacky Bai }
56b40ba806SJacky Bai
pcc_gate_is_enabled(struct clk_hw * hw)57b40ba806SJacky Bai static int pcc_gate_is_enabled(struct clk_hw *hw)
58b40ba806SJacky Bai {
59b40ba806SJacky Bai return clk_gate_ops.is_enabled(hw);
60b40ba806SJacky Bai }
61b40ba806SJacky Bai
62b40ba806SJacky Bai static const struct clk_ops pcc_gate_ops = {
63b40ba806SJacky Bai .enable = pcc_gate_enable,
64b40ba806SJacky Bai .disable = pcc_gate_disable,
65b40ba806SJacky Bai .is_enabled = pcc_gate_is_enabled,
66b40ba806SJacky Bai };
67b40ba806SJacky Bai
imx_ulp_clk_hw_composite(const char * name,const char * const * parent_names,int num_parents,bool mux_present,bool rate_present,bool gate_present,void __iomem * reg,bool has_swrst)68b40ba806SJacky Bai static struct clk_hw *imx_ulp_clk_hw_composite(const char *name,
6976a323c1SA.s. Dong const char * const *parent_names,
7076a323c1SA.s. Dong int num_parents, bool mux_present,
7176a323c1SA.s. Dong bool rate_present, bool gate_present,
72b40ba806SJacky Bai void __iomem *reg, bool has_swrst)
7376a323c1SA.s. Dong {
7476a323c1SA.s. Dong struct clk_hw *mux_hw = NULL, *fd_hw = NULL, *gate_hw = NULL;
7576a323c1SA.s. Dong struct clk_fractional_divider *fd = NULL;
7676a323c1SA.s. Dong struct clk_gate *gate = NULL;
7776a323c1SA.s. Dong struct clk_mux *mux = NULL;
7876a323c1SA.s. Dong struct clk_hw *hw;
790f6e3c15SAnson Huang u32 val;
8076a323c1SA.s. Dong
8176a323c1SA.s. Dong if (mux_present) {
8276a323c1SA.s. Dong mux = kzalloc(sizeof(*mux), GFP_KERNEL);
8376a323c1SA.s. Dong if (!mux)
8476a323c1SA.s. Dong return ERR_PTR(-ENOMEM);
8576a323c1SA.s. Dong mux_hw = &mux->hw;
8676a323c1SA.s. Dong mux->reg = reg;
8776a323c1SA.s. Dong mux->shift = PCG_PCS_SHIFT;
8876a323c1SA.s. Dong mux->mask = PCG_PCS_MASK;
893fa36200SJacky Bai if (has_swrst)
903fa36200SJacky Bai mux->lock = &imx_ccm_lock;
9176a323c1SA.s. Dong }
9276a323c1SA.s. Dong
9376a323c1SA.s. Dong if (rate_present) {
9476a323c1SA.s. Dong fd = kzalloc(sizeof(*fd), GFP_KERNEL);
9576a323c1SA.s. Dong if (!fd) {
9676a323c1SA.s. Dong kfree(mux);
9776a323c1SA.s. Dong return ERR_PTR(-ENOMEM);
9876a323c1SA.s. Dong }
9976a323c1SA.s. Dong fd_hw = &fd->hw;
10076a323c1SA.s. Dong fd->reg = reg;
10176a323c1SA.s. Dong fd->mshift = PCG_FRAC_SHIFT;
10276a323c1SA.s. Dong fd->mwidth = PCG_FRAC_WIDTH;
10376a323c1SA.s. Dong fd->nshift = PCG_PCD_SHIFT;
10476a323c1SA.s. Dong fd->nwidth = PCG_PCD_WIDTH;
10576a323c1SA.s. Dong fd->flags = CLK_FRAC_DIVIDER_ZERO_BASED;
1063fa36200SJacky Bai if (has_swrst)
1073fa36200SJacky Bai fd->lock = &imx_ccm_lock;
10876a323c1SA.s. Dong }
10976a323c1SA.s. Dong
11076a323c1SA.s. Dong if (gate_present) {
11176a323c1SA.s. Dong gate = kzalloc(sizeof(*gate), GFP_KERNEL);
11276a323c1SA.s. Dong if (!gate) {
11376a323c1SA.s. Dong kfree(mux);
11476a323c1SA.s. Dong kfree(fd);
11576a323c1SA.s. Dong return ERR_PTR(-ENOMEM);
11676a323c1SA.s. Dong }
11776a323c1SA.s. Dong gate_hw = &gate->hw;
11876a323c1SA.s. Dong gate->reg = reg;
11976a323c1SA.s. Dong gate->bit_idx = PCG_CGC_SHIFT;
1203fa36200SJacky Bai if (has_swrst)
1213fa36200SJacky Bai gate->lock = &imx_ccm_lock;
1220f6e3c15SAnson Huang /*
1230f6e3c15SAnson Huang * make sure clock is gated during clock tree initialization,
1240f6e3c15SAnson Huang * the HW ONLY allow clock parent/rate changed with clock gated,
1250f6e3c15SAnson Huang * during clock tree initialization, clocks could be enabled
1260f6e3c15SAnson Huang * by bootloader, so the HW status will mismatch with clock tree
1270f6e3c15SAnson Huang * prepare count, then clock core driver will allow parent/rate
1280f6e3c15SAnson Huang * change since the prepare count is zero, but HW actually
1290f6e3c15SAnson Huang * prevent the parent/rate change due to the clock is enabled.
1300f6e3c15SAnson Huang */
1310f6e3c15SAnson Huang val = readl_relaxed(reg);
1320f6e3c15SAnson Huang val &= ~(1 << PCG_CGC_SHIFT);
1330f6e3c15SAnson Huang writel_relaxed(val, reg);
13476a323c1SA.s. Dong }
13576a323c1SA.s. Dong
13676a323c1SA.s. Dong hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
13776a323c1SA.s. Dong mux_hw, &clk_mux_ops, fd_hw,
13876a323c1SA.s. Dong &clk_fractional_divider_ops, gate_hw,
139b40ba806SJacky Bai has_swrst ? &pcc_gate_ops : &clk_gate_ops, CLK_SET_RATE_GATE |
14075c6f1a0SJacky Bai CLK_SET_PARENT_GATE | CLK_SET_RATE_NO_REPARENT);
14176a323c1SA.s. Dong if (IS_ERR(hw)) {
14276a323c1SA.s. Dong kfree(mux);
14376a323c1SA.s. Dong kfree(fd);
14476a323c1SA.s. Dong kfree(gate);
14576a323c1SA.s. Dong }
14676a323c1SA.s. Dong
14776a323c1SA.s. Dong return hw;
14876a323c1SA.s. Dong }
149b40ba806SJacky Bai
imx7ulp_clk_hw_composite(const char * name,const char * const * parent_names,int num_parents,bool mux_present,bool rate_present,bool gate_present,void __iomem * reg)150b40ba806SJacky Bai struct clk_hw *imx7ulp_clk_hw_composite(const char *name, const char * const *parent_names,
151b40ba806SJacky Bai int num_parents, bool mux_present, bool rate_present,
152b40ba806SJacky Bai bool gate_present, void __iomem *reg)
153b40ba806SJacky Bai {
154b40ba806SJacky Bai return imx_ulp_clk_hw_composite(name, parent_names, num_parents, mux_present, rate_present,
155b40ba806SJacky Bai gate_present, reg, false);
156b40ba806SJacky Bai }
157b40ba806SJacky Bai
imx8ulp_clk_hw_composite(const char * name,const char * const * parent_names,int num_parents,bool mux_present,bool rate_present,bool gate_present,void __iomem * reg,bool has_swrst)158b40ba806SJacky Bai struct clk_hw *imx8ulp_clk_hw_composite(const char *name, const char * const *parent_names,
159b40ba806SJacky Bai int num_parents, bool mux_present, bool rate_present,
160b40ba806SJacky Bai bool gate_present, void __iomem *reg, bool has_swrst)
161b40ba806SJacky Bai {
162b40ba806SJacky Bai return imx_ulp_clk_hw_composite(name, parent_names, num_parents, mux_present, rate_present,
163b40ba806SJacky Bai gate_present, reg, has_swrst);
164b40ba806SJacky Bai }
165*d4e6c054SJacky Bai EXPORT_SYMBOL_GPL(imx8ulp_clk_hw_composite);
166