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 35struct 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 47static 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 65static 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); 76out: 77 spin_unlock_irqrestore(gate->lock, flags); 78 79 return 0; 80} 81 82static 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); 97out: 98 spin_unlock_irqrestore(gate->lock, flags); 99} 100 101static 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 118static 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 133static 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 146static 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 153static const struct clk_ops imx93_clk_gate_ro_ops = { 154 .is_enabled = imx93_clk_gate_is_enabled, 155}; 156 157struct 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} 199EXPORT_SYMBOL_GPL(imx93_clk_gate); 200