162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2022 NXP 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Peng Fan <peng.fan@nxp.com> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/clk-provider.h> 962306a36Sopenharmony_ci#include <linux/errno.h> 1062306a36Sopenharmony_ci#include <linux/export.h> 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <linux/iopoll.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "clk.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define DIRECT_OFFSET 0x0 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* 2062306a36Sopenharmony_ci * 0b000 - LPCG will be OFF in any CPU mode. 2162306a36Sopenharmony_ci * 0b100 - LPCG will be ON in any CPU mode. 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci#define LPM_SETTING_OFF 0x0 2462306a36Sopenharmony_ci#define LPM_SETTING_ON 0x4 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define LPM_CUR_OFFSET 0x1c 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define AUTHEN_OFFSET 0x30 2962306a36Sopenharmony_ci#define CPULPM_EN BIT(2) 3062306a36Sopenharmony_ci#define TZ_NS_SHIFT 9 3162306a36Sopenharmony_ci#define TZ_NS_MASK BIT(9) 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define WHITE_LIST_SHIFT 16 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistruct imx93_clk_gate { 3662306a36Sopenharmony_ci struct clk_hw hw; 3762306a36Sopenharmony_ci void __iomem *reg; 3862306a36Sopenharmony_ci u32 bit_idx; 3962306a36Sopenharmony_ci u32 val; 4062306a36Sopenharmony_ci u32 mask; 4162306a36Sopenharmony_ci spinlock_t *lock; 4262306a36Sopenharmony_ci unsigned int *share_count; 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define to_imx93_clk_gate(_hw) container_of(_hw, struct imx93_clk_gate, hw) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic void imx93_clk_gate_do_hardware(struct clk_hw *hw, bool enable) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct imx93_clk_gate *gate = to_imx93_clk_gate(hw); 5062306a36Sopenharmony_ci u32 val; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci val = readl(gate->reg + AUTHEN_OFFSET); 5362306a36Sopenharmony_ci if (val & CPULPM_EN) { 5462306a36Sopenharmony_ci val = enable ? LPM_SETTING_ON : LPM_SETTING_OFF; 5562306a36Sopenharmony_ci writel(val, gate->reg + LPM_CUR_OFFSET); 5662306a36Sopenharmony_ci } else { 5762306a36Sopenharmony_ci val = readl(gate->reg + DIRECT_OFFSET); 5862306a36Sopenharmony_ci val &= ~(gate->mask << gate->bit_idx); 5962306a36Sopenharmony_ci if (enable) 6062306a36Sopenharmony_ci val |= (gate->val & gate->mask) << gate->bit_idx; 6162306a36Sopenharmony_ci writel(val, gate->reg + DIRECT_OFFSET); 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic int imx93_clk_gate_enable(struct clk_hw *hw) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci struct imx93_clk_gate *gate = to_imx93_clk_gate(hw); 6862306a36Sopenharmony_ci unsigned long flags; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci spin_lock_irqsave(gate->lock, flags); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci if (gate->share_count && (*gate->share_count)++ > 0) 7362306a36Sopenharmony_ci goto out; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci imx93_clk_gate_do_hardware(hw, true); 7662306a36Sopenharmony_ciout: 7762306a36Sopenharmony_ci spin_unlock_irqrestore(gate->lock, flags); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci return 0; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic void imx93_clk_gate_disable(struct clk_hw *hw) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci struct imx93_clk_gate *gate = to_imx93_clk_gate(hw); 8562306a36Sopenharmony_ci unsigned long flags; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci spin_lock_irqsave(gate->lock, flags); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (gate->share_count) { 9062306a36Sopenharmony_ci if (WARN_ON(*gate->share_count == 0)) 9162306a36Sopenharmony_ci goto out; 9262306a36Sopenharmony_ci else if (--(*gate->share_count) > 0) 9362306a36Sopenharmony_ci goto out; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci imx93_clk_gate_do_hardware(hw, false); 9762306a36Sopenharmony_ciout: 9862306a36Sopenharmony_ci spin_unlock_irqrestore(gate->lock, flags); 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic int imx93_clk_gate_reg_is_enabled(struct imx93_clk_gate *gate) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci u32 val = readl(gate->reg + AUTHEN_OFFSET); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (val & CPULPM_EN) { 10662306a36Sopenharmony_ci val = readl(gate->reg + LPM_CUR_OFFSET); 10762306a36Sopenharmony_ci if (val == LPM_SETTING_ON) 10862306a36Sopenharmony_ci return 1; 10962306a36Sopenharmony_ci } else { 11062306a36Sopenharmony_ci val = readl(gate->reg); 11162306a36Sopenharmony_ci if (((val >> gate->bit_idx) & gate->mask) == gate->val) 11262306a36Sopenharmony_ci return 1; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return 0; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic int imx93_clk_gate_is_enabled(struct clk_hw *hw) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci struct imx93_clk_gate *gate = to_imx93_clk_gate(hw); 12162306a36Sopenharmony_ci unsigned long flags; 12262306a36Sopenharmony_ci int ret; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci spin_lock_irqsave(gate->lock, flags); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci ret = imx93_clk_gate_reg_is_enabled(gate); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci spin_unlock_irqrestore(gate->lock, flags); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci return ret; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic void imx93_clk_gate_disable_unused(struct clk_hw *hw) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci struct imx93_clk_gate *gate = to_imx93_clk_gate(hw); 13662306a36Sopenharmony_ci unsigned long flags; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci spin_lock_irqsave(gate->lock, flags); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (!gate->share_count || *gate->share_count == 0) 14162306a36Sopenharmony_ci imx93_clk_gate_do_hardware(hw, false); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci spin_unlock_irqrestore(gate->lock, flags); 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic const struct clk_ops imx93_clk_gate_ops = { 14762306a36Sopenharmony_ci .enable = imx93_clk_gate_enable, 14862306a36Sopenharmony_ci .disable = imx93_clk_gate_disable, 14962306a36Sopenharmony_ci .disable_unused = imx93_clk_gate_disable_unused, 15062306a36Sopenharmony_ci .is_enabled = imx93_clk_gate_is_enabled, 15162306a36Sopenharmony_ci}; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic const struct clk_ops imx93_clk_gate_ro_ops = { 15462306a36Sopenharmony_ci .is_enabled = imx93_clk_gate_is_enabled, 15562306a36Sopenharmony_ci}; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistruct clk_hw *imx93_clk_gate(struct device *dev, const char *name, const char *parent_name, 15862306a36Sopenharmony_ci unsigned long flags, void __iomem *reg, u32 bit_idx, u32 val, 15962306a36Sopenharmony_ci u32 mask, u32 domain_id, unsigned int *share_count) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci struct imx93_clk_gate *gate; 16262306a36Sopenharmony_ci struct clk_hw *hw; 16362306a36Sopenharmony_ci struct clk_init_data init; 16462306a36Sopenharmony_ci int ret; 16562306a36Sopenharmony_ci u32 authen; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci gate = kzalloc(sizeof(struct imx93_clk_gate), GFP_KERNEL); 16862306a36Sopenharmony_ci if (!gate) 16962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci gate->reg = reg; 17262306a36Sopenharmony_ci gate->lock = &imx_ccm_lock; 17362306a36Sopenharmony_ci gate->bit_idx = bit_idx; 17462306a36Sopenharmony_ci gate->val = val; 17562306a36Sopenharmony_ci gate->mask = mask; 17662306a36Sopenharmony_ci gate->share_count = share_count; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci init.name = name; 17962306a36Sopenharmony_ci init.ops = &imx93_clk_gate_ops; 18062306a36Sopenharmony_ci init.flags = flags | CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE; 18162306a36Sopenharmony_ci init.parent_names = parent_name ? &parent_name : NULL; 18262306a36Sopenharmony_ci init.num_parents = parent_name ? 1 : 0; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci gate->hw.init = &init; 18562306a36Sopenharmony_ci hw = &gate->hw; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci authen = readl(reg + AUTHEN_OFFSET); 18862306a36Sopenharmony_ci if (!(authen & TZ_NS_MASK) || !(authen & BIT(WHITE_LIST_SHIFT + domain_id))) 18962306a36Sopenharmony_ci init.ops = &imx93_clk_gate_ro_ops; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci ret = clk_hw_register(dev, hw); 19262306a36Sopenharmony_ci if (ret) { 19362306a36Sopenharmony_ci kfree(gate); 19462306a36Sopenharmony_ci return ERR_PTR(ret); 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci return hw; 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(imx93_clk_gate); 200