1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (C) 2016 Freescale Semiconductor, Inc.
4 * Copyright 2017~2018 NXP
8 #include <linux/bits.h>
9 #include <linux/clk-provider.h>
10 #include <linux/err.h>
12 #include <linux/slab.h>
14 #include "../clk-fractional-divider.h"
17 #define PCG_PR_MASK BIT(31)
18 #define PCG_PCS_SHIFT 24
19 #define PCG_PCS_MASK 0x7
20 #define PCG_CGC_SHIFT 30
21 #define PCG_FRAC_SHIFT 3
22 #define PCG_FRAC_WIDTH 1
23 #define PCG_PCD_SHIFT 0
24 #define PCG_PCD_WIDTH 3
26 #define SW_RST BIT(28)
28 static int pcc_gate_enable(struct clk_hw
*hw
)
30 struct clk_gate
*gate
= to_clk_gate(hw
);
35 ret
= clk_gate_ops
.enable(hw
);
39 spin_lock_irqsave(gate
->lock
, flags
);
41 * release the sw reset for peripherals associated with
42 * with this pcc clock.
44 val
= readl(gate
->reg
);
46 writel(val
, gate
->reg
);
48 spin_unlock_irqrestore(gate
->lock
, flags
);
53 static void pcc_gate_disable(struct clk_hw
*hw
)
55 clk_gate_ops
.disable(hw
);
58 static int pcc_gate_is_enabled(struct clk_hw
*hw
)
60 return clk_gate_ops
.is_enabled(hw
);
63 static const struct clk_ops pcc_gate_ops
= {
64 .enable
= pcc_gate_enable
,
65 .disable
= pcc_gate_disable
,
66 .is_enabled
= pcc_gate_is_enabled
,
69 static struct clk_hw
*imx_ulp_clk_hw_composite(const char *name
,
70 const char * const *parent_names
,
71 int num_parents
, bool mux_present
,
72 bool rate_present
, bool gate_present
,
73 void __iomem
*reg
, bool has_swrst
)
75 struct clk_hw
*mux_hw
= NULL
, *fd_hw
= NULL
, *gate_hw
= NULL
;
76 struct clk_fractional_divider
*fd
= NULL
;
77 struct clk_gate
*gate
= NULL
;
78 struct clk_mux
*mux
= NULL
;
83 if (!(val
& PCG_PR_MASK
)) {
84 pr_info("PCC PR is 0 for clk:%s, bypass\n", name
);
89 mux
= kzalloc(sizeof(*mux
), GFP_KERNEL
);
91 return ERR_PTR(-ENOMEM
);
94 mux
->shift
= PCG_PCS_SHIFT
;
95 mux
->mask
= PCG_PCS_MASK
;
97 mux
->lock
= &imx_ccm_lock
;
101 fd
= kzalloc(sizeof(*fd
), GFP_KERNEL
);
104 return ERR_PTR(-ENOMEM
);
108 fd
->mshift
= PCG_FRAC_SHIFT
;
109 fd
->mwidth
= PCG_FRAC_WIDTH
;
110 fd
->nshift
= PCG_PCD_SHIFT
;
111 fd
->nwidth
= PCG_PCD_WIDTH
;
112 fd
->flags
= CLK_FRAC_DIVIDER_ZERO_BASED
;
114 fd
->lock
= &imx_ccm_lock
;
118 gate
= kzalloc(sizeof(*gate
), GFP_KERNEL
);
122 return ERR_PTR(-ENOMEM
);
126 gate
->bit_idx
= PCG_CGC_SHIFT
;
128 gate
->lock
= &imx_ccm_lock
;
130 * make sure clock is gated during clock tree initialization,
131 * the HW ONLY allow clock parent/rate changed with clock gated,
132 * during clock tree initialization, clocks could be enabled
133 * by bootloader, so the HW status will mismatch with clock tree
134 * prepare count, then clock core driver will allow parent/rate
135 * change since the prepare count is zero, but HW actually
136 * prevent the parent/rate change due to the clock is enabled.
138 val
= readl_relaxed(reg
);
139 val
&= ~(1 << PCG_CGC_SHIFT
);
140 writel_relaxed(val
, reg
);
143 hw
= clk_hw_register_composite(NULL
, name
, parent_names
, num_parents
,
144 mux_hw
, &clk_mux_ops
, fd_hw
,
145 &clk_fractional_divider_ops
, gate_hw
,
146 has_swrst
? &pcc_gate_ops
: &clk_gate_ops
, CLK_SET_RATE_GATE
|
147 CLK_SET_PARENT_GATE
| CLK_SET_RATE_NO_REPARENT
);
157 struct clk_hw
*imx7ulp_clk_hw_composite(const char *name
, const char * const *parent_names
,
158 int num_parents
, bool mux_present
, bool rate_present
,
159 bool gate_present
, void __iomem
*reg
)
161 return imx_ulp_clk_hw_composite(name
, parent_names
, num_parents
, mux_present
, rate_present
,
162 gate_present
, reg
, false);
165 struct clk_hw
*imx8ulp_clk_hw_composite(const char *name
, const char * const *parent_names
,
166 int num_parents
, bool mux_present
, bool rate_present
,
167 bool gate_present
, void __iomem
*reg
, bool has_swrst
)
169 return imx_ulp_clk_hw_composite(name
, parent_names
, num_parents
, mux_present
, rate_present
,
170 gate_present
, reg
, has_swrst
);
172 EXPORT_SYMBOL_GPL(imx8ulp_clk_hw_composite
);