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