Skip to content

Commit 0836c86

Browse files
MrVanabelvesa
authored andcommitted
clk: imx: add i.MX93 clk gate
i.MX93 LPCG is different from i.MX8M CCGR. Although imx_clk_hw_gate4_flags is used here, it not strictly match i.MX93. i.MX93 has such design: - LPCG_DIRECT use BIT0 as on/off gate when LPCG_AUTHEN CPU_LPM is 0 - LPCG_LPM_CUR use BIT[2:0] as on/off gate when LPCG_AUTHEN CPU_LPM is 1 The current implementation suppose CPU_LPM is 0, and use LPCG_DIRECT BIT[1:0] as on/off gate. Although BIT1 is touched, actually BIT1 is reserved. And imx_clk_hw_gate4_flags use mask 0x3 to determine whether the clk is enabled or not, but i.MX93 LPCG only use BIT0 to control when CPU_LPM is 0. So clk disabled unused during kernel boot not able to gate off the unused clocks. To match i.MX93 LPCG, introduce imx93_clk_gate. Signed-off-by: Peng Fan <peng.fan@nxp.com> Reviewed-by: Ye Li <ye.li@nxp.com> Reviewed-by: Jacky Bai <ping.bai@nxp.com> Reviewed-by: Abel Vesa <abel.vesa@linaro.org> Signed-off-by: Abel Vesa <abel.vesa@linaro.org> Link: https://lore.kernel.org/r/20220830033137.4149542-6-peng.fan@oss.nxp.com
1 parent 2b66f02 commit 0836c86

File tree

3 files changed

+204
-0
lines changed

3 files changed

+204
-0
lines changed

drivers/clk/imx/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ mxc-clk-objs += clk-fixup-div.o
1212
mxc-clk-objs += clk-fixup-mux.o
1313
mxc-clk-objs += clk-frac-pll.o
1414
mxc-clk-objs += clk-gate2.o
15+
mxc-clk-objs += clk-gate-93.o
1516
mxc-clk-objs += clk-gate-exclusive.o
1617
mxc-clk-objs += clk-pfd.o
1718
mxc-clk-objs += clk-pfdv2.o

drivers/clk/imx/clk-gate-93.c

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
// SPDX-License-Identifier: GPL-2.0+
2+
/*
3+
* Copyright 2022 NXP
4+
*
5+
* Peng Fan <peng.fan@nxp.com>
6+
*/
7+
8+
#include <linux/clk-provider.h>
9+
#include <linux/errno.h>
10+
#include <linux/export.h>
11+
#include <linux/io.h>
12+
#include <linux/iopoll.h>
13+
#include <linux/slab.h>
14+
15+
#include "clk.h"
16+
17+
#define DIRECT_OFFSET 0x0
18+
19+
/*
20+
* 0b000 - LPCG will be OFF in any CPU mode.
21+
* 0b100 - LPCG will be ON in any CPU mode.
22+
*/
23+
#define LPM_SETTING_OFF 0x0
24+
#define LPM_SETTING_ON 0x4
25+
26+
#define LPM_CUR_OFFSET 0x1c
27+
28+
#define AUTHEN_OFFSET 0x30
29+
#define CPULPM_EN BIT(2)
30+
#define TZ_NS_SHIFT 9
31+
#define TZ_NS_MASK BIT(9)
32+
33+
#define WHITE_LIST_SHIFT 16
34+
35+
struct imx93_clk_gate {
36+
struct clk_hw hw;
37+
void __iomem *reg;
38+
u32 bit_idx;
39+
u32 val;
40+
u32 mask;
41+
spinlock_t *lock;
42+
unsigned int *share_count;
43+
};
44+
45+
#define to_imx93_clk_gate(_hw) container_of(_hw, struct imx93_clk_gate, hw)
46+
47+
static void imx93_clk_gate_do_hardware(struct clk_hw *hw, bool enable)
48+
{
49+
struct imx93_clk_gate *gate = to_imx93_clk_gate(hw);
50+
u32 val;
51+
52+
val = readl(gate->reg + AUTHEN_OFFSET);
53+
if (val & CPULPM_EN) {
54+
val = enable ? LPM_SETTING_ON : LPM_SETTING_OFF;
55+
writel(val, gate->reg + LPM_CUR_OFFSET);
56+
} else {
57+
val = readl(gate->reg + DIRECT_OFFSET);
58+
val &= ~(gate->mask << gate->bit_idx);
59+
if (enable)
60+
val |= (gate->val & gate->mask) << gate->bit_idx;
61+
writel(val, gate->reg + DIRECT_OFFSET);
62+
}
63+
}
64+
65+
static int imx93_clk_gate_enable(struct clk_hw *hw)
66+
{
67+
struct imx93_clk_gate *gate = to_imx93_clk_gate(hw);
68+
unsigned long flags;
69+
70+
spin_lock_irqsave(gate->lock, flags);
71+
72+
if (gate->share_count && (*gate->share_count)++ > 0)
73+
goto out;
74+
75+
imx93_clk_gate_do_hardware(hw, true);
76+
out:
77+
spin_unlock_irqrestore(gate->lock, flags);
78+
79+
return 0;
80+
}
81+
82+
static void imx93_clk_gate_disable(struct clk_hw *hw)
83+
{
84+
struct imx93_clk_gate *gate = to_imx93_clk_gate(hw);
85+
unsigned long flags;
86+
87+
spin_lock_irqsave(gate->lock, flags);
88+
89+
if (gate->share_count) {
90+
if (WARN_ON(*gate->share_count == 0))
91+
goto out;
92+
else if (--(*gate->share_count) > 0)
93+
goto out;
94+
}
95+
96+
imx93_clk_gate_do_hardware(hw, false);
97+
out:
98+
spin_unlock_irqrestore(gate->lock, flags);
99+
}
100+
101+
static int imx93_clk_gate_reg_is_enabled(struct imx93_clk_gate *gate)
102+
{
103+
u32 val = readl(gate->reg + AUTHEN_OFFSET);
104+
105+
if (val & CPULPM_EN) {
106+
val = readl(gate->reg + LPM_CUR_OFFSET);
107+
if (val == LPM_SETTING_ON)
108+
return 1;
109+
} else {
110+
val = readl(gate->reg);
111+
if (((val >> gate->bit_idx) & gate->mask) == gate->val)
112+
return 1;
113+
}
114+
115+
return 0;
116+
}
117+
118+
static int imx93_clk_gate_is_enabled(struct clk_hw *hw)
119+
{
120+
struct imx93_clk_gate *gate = to_imx93_clk_gate(hw);
121+
unsigned long flags;
122+
int ret;
123+
124+
spin_lock_irqsave(gate->lock, flags);
125+
126+
ret = imx93_clk_gate_reg_is_enabled(gate);
127+
128+
spin_unlock_irqrestore(gate->lock, flags);
129+
130+
return ret;
131+
}
132+
133+
static void imx93_clk_gate_disable_unused(struct clk_hw *hw)
134+
{
135+
struct imx93_clk_gate *gate = to_imx93_clk_gate(hw);
136+
unsigned long flags;
137+
138+
spin_lock_irqsave(gate->lock, flags);
139+
140+
if (!gate->share_count || *gate->share_count == 0)
141+
imx93_clk_gate_do_hardware(hw, false);
142+
143+
spin_unlock_irqrestore(gate->lock, flags);
144+
}
145+
146+
static const struct clk_ops imx93_clk_gate_ops = {
147+
.enable = imx93_clk_gate_enable,
148+
.disable = imx93_clk_gate_disable,
149+
.disable_unused = imx93_clk_gate_disable_unused,
150+
.is_enabled = imx93_clk_gate_is_enabled,
151+
};
152+
153+
static const struct clk_ops imx93_clk_gate_ro_ops = {
154+
.is_enabled = imx93_clk_gate_is_enabled,
155+
};
156+
157+
struct clk_hw *imx93_clk_gate(struct device *dev, const char *name, const char *parent_name,
158+
unsigned long flags, void __iomem *reg, u32 bit_idx, u32 val,
159+
u32 mask, u32 domain_id, unsigned int *share_count)
160+
{
161+
struct imx93_clk_gate *gate;
162+
struct clk_hw *hw;
163+
struct clk_init_data init;
164+
int ret;
165+
u32 authen;
166+
167+
gate = kzalloc(sizeof(struct imx93_clk_gate), GFP_KERNEL);
168+
if (!gate)
169+
return ERR_PTR(-ENOMEM);
170+
171+
gate->reg = reg;
172+
gate->lock = &imx_ccm_lock;
173+
gate->bit_idx = bit_idx;
174+
gate->val = val;
175+
gate->mask = mask;
176+
gate->share_count = share_count;
177+
178+
init.name = name;
179+
init.ops = &imx93_clk_gate_ops;
180+
init.flags = flags | CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE;
181+
init.parent_names = parent_name ? &parent_name : NULL;
182+
init.num_parents = parent_name ? 1 : 0;
183+
184+
gate->hw.init = &init;
185+
hw = &gate->hw;
186+
187+
authen = readl(reg + AUTHEN_OFFSET);
188+
if (!(authen & TZ_NS_MASK) || !(authen & BIT(WHITE_LIST_SHIFT + domain_id)))
189+
init.ops = &imx93_clk_gate_ro_ops;
190+
191+
ret = clk_hw_register(dev, hw);
192+
if (ret) {
193+
kfree(gate);
194+
return ERR_PTR(ret);
195+
}
196+
197+
return hw;
198+
}
199+
EXPORT_SYMBOL_GPL(imx93_clk_gate);

drivers/clk/imx/clk.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,10 @@ struct clk_hw *imx93_clk_composite_flags(const char *name,
451451
imx93_clk_composite_flags(name, parent_names, num_parents, reg, domain_id \
452452
CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE)
453453

454+
struct clk_hw *imx93_clk_gate(struct device *dev, const char *name, const char *parent_name,
455+
unsigned long flags, void __iomem *reg, u32 bit_idx, u32 val,
456+
u32 mask, u32 domain_id, unsigned int *share_count);
457+
454458
struct clk_hw *imx_clk_hw_divider_gate(const char *name, const char *parent_name,
455459
unsigned long flags, void __iomem *reg, u8 shift, u8 width,
456460
u8 clk_divider_flags, const struct clk_div_table *table,

0 commit comments

Comments
 (0)