Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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

authored by

Peng Fan and committed by
Abel Vesa
0836c860 2b66f02e

+204
+1
drivers/clk/imx/Makefile
··· 12 12 mxc-clk-objs += clk-fixup-mux.o 13 13 mxc-clk-objs += clk-frac-pll.o 14 14 mxc-clk-objs += clk-gate2.o 15 + mxc-clk-objs += clk-gate-93.o 15 16 mxc-clk-objs += clk-gate-exclusive.o 16 17 mxc-clk-objs += clk-pfd.o 17 18 mxc-clk-objs += clk-pfdv2.o
+199
drivers/clk/imx/clk-gate-93.c
··· 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);
+4
drivers/clk/imx/clk.h
··· 451 451 imx93_clk_composite_flags(name, parent_names, num_parents, reg, domain_id \ 452 452 CLK_SET_RATE_NO_REPARENT | CLK_OPS_PARENT_ENABLE) 453 453 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 + 454 458 struct clk_hw *imx_clk_hw_divider_gate(const char *name, const char *parent_name, 455 459 unsigned long flags, void __iomem *reg, u8 shift, u8 width, 456 460 u8 clk_divider_flags, const struct clk_div_table *table,