1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright 2021 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 TIMEOUT_US 500U 18 19 #define CCM_DIV_SHIFT 0 20 #define CCM_DIV_WIDTH 8 21 #define CCM_MUX_SHIFT 8 22 #define CCM_MUX_MASK 3 23 #define CCM_OFF_SHIFT 24 24 #define CCM_BUSY_SHIFT 28 25 26 #define STAT_OFFSET 0x4 27 #define AUTHEN_OFFSET 0x30 28 #define TZ_NS_SHIFT 9 29 #define TZ_NS_MASK BIT(9) 30 31 #define WHITE_LIST_SHIFT 16 32 33 static int imx93_clk_composite_wait_ready(struct clk_hw *hw, void __iomem *reg) 34 { 35 int ret; 36 u32 val; 37 38 ret = readl_poll_timeout_atomic(reg + STAT_OFFSET, val, !(val & BIT(CCM_BUSY_SHIFT)), 39 0, TIMEOUT_US); 40 if (ret) 41 pr_err("Slice[%s] busy timeout\n", clk_hw_get_name(hw)); 42 43 return ret; 44 } 45 46 static void imx93_clk_composite_gate_endisable(struct clk_hw *hw, int enable) 47 { 48 struct clk_gate *gate = to_clk_gate(hw); 49 unsigned long flags; 50 u32 reg; 51 52 if (gate->lock) 53 spin_lock_irqsave(gate->lock, flags); 54 55 reg = readl(gate->reg); 56 57 if (enable) 58 reg &= ~BIT(gate->bit_idx); 59 else 60 reg |= BIT(gate->bit_idx); 61 62 writel(reg, gate->reg); 63 64 imx93_clk_composite_wait_ready(hw, gate->reg); 65 66 if (gate->lock) 67 spin_unlock_irqrestore(gate->lock, flags); 68 } 69 70 static int imx93_clk_composite_gate_enable(struct clk_hw *hw) 71 { 72 imx93_clk_composite_gate_endisable(hw, 1); 73 74 return 0; 75 } 76 77 static void imx93_clk_composite_gate_disable(struct clk_hw *hw) 78 { 79 /* 80 * Skip disable the root clock gate if mcore enabled. 81 * The root clock may be used by the mcore. 82 */ 83 if (mcore_booted) 84 return; 85 86 imx93_clk_composite_gate_endisable(hw, 0); 87 } 88 89 static const struct clk_ops imx93_clk_composite_gate_ops = { 90 .enable = imx93_clk_composite_gate_enable, 91 .disable = imx93_clk_composite_gate_disable, 92 .is_enabled = clk_gate_is_enabled, 93 }; 94 95 static unsigned long 96 imx93_clk_composite_divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) 97 { 98 return clk_divider_ops.recalc_rate(hw, parent_rate); 99 } 100 101 static int 102 imx93_clk_composite_divider_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) 103 { 104 return clk_divider_ops.determine_rate(hw, req); 105 } 106 107 static int imx93_clk_composite_divider_set_rate(struct clk_hw *hw, unsigned long rate, 108 unsigned long parent_rate) 109 { 110 struct clk_divider *divider = to_clk_divider(hw); 111 int value; 112 unsigned long flags = 0; 113 u32 val; 114 int ret; 115 116 value = divider_get_val(rate, parent_rate, divider->table, divider->width, divider->flags); 117 if (value < 0) 118 return value; 119 120 if (divider->lock) 121 spin_lock_irqsave(divider->lock, flags); 122 123 val = readl(divider->reg); 124 val &= ~(clk_div_mask(divider->width) << divider->shift); 125 val |= (u32)value << divider->shift; 126 writel(val, divider->reg); 127 128 ret = imx93_clk_composite_wait_ready(hw, divider->reg); 129 130 if (divider->lock) 131 spin_unlock_irqrestore(divider->lock, flags); 132 133 return ret; 134 } 135 136 static const struct clk_ops imx93_clk_composite_divider_ops = { 137 .recalc_rate = imx93_clk_composite_divider_recalc_rate, 138 .determine_rate = imx93_clk_composite_divider_determine_rate, 139 .set_rate = imx93_clk_composite_divider_set_rate, 140 }; 141 142 static u8 imx93_clk_composite_mux_get_parent(struct clk_hw *hw) 143 { 144 return clk_mux_ops.get_parent(hw); 145 } 146 147 static int imx93_clk_composite_mux_set_parent(struct clk_hw *hw, u8 index) 148 { 149 struct clk_mux *mux = to_clk_mux(hw); 150 u32 val = clk_mux_index_to_val(mux->table, mux->flags, index); 151 unsigned long flags = 0; 152 u32 reg; 153 int ret; 154 155 if (mux->lock) 156 spin_lock_irqsave(mux->lock, flags); 157 158 reg = readl(mux->reg); 159 reg &= ~(mux->mask << mux->shift); 160 val = val << mux->shift; 161 reg |= val; 162 writel(reg, mux->reg); 163 164 ret = imx93_clk_composite_wait_ready(hw, mux->reg); 165 166 if (mux->lock) 167 spin_unlock_irqrestore(mux->lock, flags); 168 169 return ret; 170 } 171 172 static int 173 imx93_clk_composite_mux_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) 174 { 175 return clk_mux_ops.determine_rate(hw, req); 176 } 177 178 static const struct clk_ops imx93_clk_composite_mux_ops = { 179 .get_parent = imx93_clk_composite_mux_get_parent, 180 .set_parent = imx93_clk_composite_mux_set_parent, 181 .determine_rate = imx93_clk_composite_mux_determine_rate, 182 }; 183 184 struct clk_hw *imx93_clk_composite_flags(const char *name, const char * const *parent_names, 185 int num_parents, void __iomem *reg, u32 domain_id, 186 unsigned long flags) 187 { 188 struct clk_hw *hw = ERR_PTR(-ENOMEM), *mux_hw; 189 struct clk_hw *div_hw, *gate_hw; 190 struct clk_divider *div = NULL; 191 struct clk_gate *gate = NULL; 192 struct clk_mux *mux = NULL; 193 bool clk_ro = false; 194 u32 authen; 195 196 mux = kzalloc(sizeof(*mux), GFP_KERNEL); 197 if (!mux) 198 goto fail; 199 200 mux_hw = &mux->hw; 201 mux->reg = reg; 202 mux->shift = CCM_MUX_SHIFT; 203 mux->mask = CCM_MUX_MASK; 204 mux->lock = &imx_ccm_lock; 205 206 div = kzalloc(sizeof(*div), GFP_KERNEL); 207 if (!div) 208 goto fail; 209 210 div_hw = &div->hw; 211 div->reg = reg; 212 div->shift = CCM_DIV_SHIFT; 213 div->width = CCM_DIV_WIDTH; 214 div->lock = &imx_ccm_lock; 215 div->flags = CLK_DIVIDER_ROUND_CLOSEST; 216 217 authen = readl(reg + AUTHEN_OFFSET); 218 if (!(authen & TZ_NS_MASK) || !(authen & BIT(WHITE_LIST_SHIFT + domain_id))) 219 clk_ro = true; 220 221 if (clk_ro) { 222 hw = clk_hw_register_composite(NULL, name, parent_names, num_parents, 223 mux_hw, &clk_mux_ro_ops, div_hw, 224 &clk_divider_ro_ops, NULL, NULL, flags); 225 } else { 226 gate = kzalloc(sizeof(*gate), GFP_KERNEL); 227 if (!gate) 228 goto fail; 229 230 gate_hw = &gate->hw; 231 gate->reg = reg; 232 gate->bit_idx = CCM_OFF_SHIFT; 233 gate->lock = &imx_ccm_lock; 234 gate->flags = CLK_GATE_SET_TO_DISABLE; 235 236 hw = clk_hw_register_composite(NULL, name, parent_names, num_parents, 237 mux_hw, &imx93_clk_composite_mux_ops, div_hw, 238 &imx93_clk_composite_divider_ops, gate_hw, 239 &imx93_clk_composite_gate_ops, 240 flags | CLK_SET_RATE_NO_REPARENT); 241 } 242 243 if (IS_ERR(hw)) 244 goto fail; 245 246 return hw; 247 248 fail: 249 kfree(gate); 250 kfree(div); 251 kfree(mux); 252 return ERR_CAST(hw); 253 } 254 EXPORT_SYMBOL_GPL(imx93_clk_composite_flags); 255