162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2021 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 TIMEOUT_US 500U 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define CCM_DIV_SHIFT 0 2062306a36Sopenharmony_ci#define CCM_DIV_WIDTH 8 2162306a36Sopenharmony_ci#define CCM_MUX_SHIFT 8 2262306a36Sopenharmony_ci#define CCM_MUX_MASK 3 2362306a36Sopenharmony_ci#define CCM_OFF_SHIFT 24 2462306a36Sopenharmony_ci#define CCM_BUSY_SHIFT 28 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define STAT_OFFSET 0x4 2762306a36Sopenharmony_ci#define AUTHEN_OFFSET 0x30 2862306a36Sopenharmony_ci#define TZ_NS_SHIFT 9 2962306a36Sopenharmony_ci#define TZ_NS_MASK BIT(9) 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define WHITE_LIST_SHIFT 16 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic int imx93_clk_composite_wait_ready(struct clk_hw *hw, void __iomem *reg) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci int ret; 3662306a36Sopenharmony_ci u32 val; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci ret = readl_poll_timeout_atomic(reg + STAT_OFFSET, val, !(val & BIT(CCM_BUSY_SHIFT)), 3962306a36Sopenharmony_ci 0, TIMEOUT_US); 4062306a36Sopenharmony_ci if (ret) 4162306a36Sopenharmony_ci pr_err("Slice[%s] busy timeout\n", clk_hw_get_name(hw)); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci return ret; 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic void imx93_clk_composite_gate_endisable(struct clk_hw *hw, int enable) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci struct clk_gate *gate = to_clk_gate(hw); 4962306a36Sopenharmony_ci unsigned long flags; 5062306a36Sopenharmony_ci u32 reg; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci if (gate->lock) 5362306a36Sopenharmony_ci spin_lock_irqsave(gate->lock, flags); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci reg = readl(gate->reg); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (enable) 5862306a36Sopenharmony_ci reg &= ~BIT(gate->bit_idx); 5962306a36Sopenharmony_ci else 6062306a36Sopenharmony_ci reg |= BIT(gate->bit_idx); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci writel(reg, gate->reg); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci imx93_clk_composite_wait_ready(hw, gate->reg); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci if (gate->lock) 6762306a36Sopenharmony_ci spin_unlock_irqrestore(gate->lock, flags); 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic int imx93_clk_composite_gate_enable(struct clk_hw *hw) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci imx93_clk_composite_gate_endisable(hw, 1); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci return 0; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic void imx93_clk_composite_gate_disable(struct clk_hw *hw) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci imx93_clk_composite_gate_endisable(hw, 0); 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic const struct clk_ops imx93_clk_composite_gate_ops = { 8362306a36Sopenharmony_ci .enable = imx93_clk_composite_gate_enable, 8462306a36Sopenharmony_ci .disable = imx93_clk_composite_gate_disable, 8562306a36Sopenharmony_ci .is_enabled = clk_gate_is_enabled, 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic unsigned long 8962306a36Sopenharmony_ciimx93_clk_composite_divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci return clk_divider_ops.recalc_rate(hw, parent_rate); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic long 9562306a36Sopenharmony_ciimx93_clk_composite_divider_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci return clk_divider_ops.round_rate(hw, rate, prate); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic int 10162306a36Sopenharmony_ciimx93_clk_composite_divider_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci return clk_divider_ops.determine_rate(hw, req); 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic int imx93_clk_composite_divider_set_rate(struct clk_hw *hw, unsigned long rate, 10762306a36Sopenharmony_ci unsigned long parent_rate) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci struct clk_divider *divider = to_clk_divider(hw); 11062306a36Sopenharmony_ci int value; 11162306a36Sopenharmony_ci unsigned long flags = 0; 11262306a36Sopenharmony_ci u32 val; 11362306a36Sopenharmony_ci int ret; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci value = divider_get_val(rate, parent_rate, divider->table, divider->width, divider->flags); 11662306a36Sopenharmony_ci if (value < 0) 11762306a36Sopenharmony_ci return value; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (divider->lock) 12062306a36Sopenharmony_ci spin_lock_irqsave(divider->lock, flags); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci val = readl(divider->reg); 12362306a36Sopenharmony_ci val &= ~(clk_div_mask(divider->width) << divider->shift); 12462306a36Sopenharmony_ci val |= (u32)value << divider->shift; 12562306a36Sopenharmony_ci writel(val, divider->reg); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci ret = imx93_clk_composite_wait_ready(hw, divider->reg); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (divider->lock) 13062306a36Sopenharmony_ci spin_unlock_irqrestore(divider->lock, flags); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci return ret; 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic const struct clk_ops imx93_clk_composite_divider_ops = { 13662306a36Sopenharmony_ci .recalc_rate = imx93_clk_composite_divider_recalc_rate, 13762306a36Sopenharmony_ci .round_rate = imx93_clk_composite_divider_round_rate, 13862306a36Sopenharmony_ci .determine_rate = imx93_clk_composite_divider_determine_rate, 13962306a36Sopenharmony_ci .set_rate = imx93_clk_composite_divider_set_rate, 14062306a36Sopenharmony_ci}; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic u8 imx93_clk_composite_mux_get_parent(struct clk_hw *hw) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci return clk_mux_ops.get_parent(hw); 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic int imx93_clk_composite_mux_set_parent(struct clk_hw *hw, u8 index) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci struct clk_mux *mux = to_clk_mux(hw); 15062306a36Sopenharmony_ci u32 val = clk_mux_index_to_val(mux->table, mux->flags, index); 15162306a36Sopenharmony_ci unsigned long flags = 0; 15262306a36Sopenharmony_ci u32 reg; 15362306a36Sopenharmony_ci int ret; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (mux->lock) 15662306a36Sopenharmony_ci spin_lock_irqsave(mux->lock, flags); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci reg = readl(mux->reg); 15962306a36Sopenharmony_ci reg &= ~(mux->mask << mux->shift); 16062306a36Sopenharmony_ci val = val << mux->shift; 16162306a36Sopenharmony_ci reg |= val; 16262306a36Sopenharmony_ci writel(reg, mux->reg); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci ret = imx93_clk_composite_wait_ready(hw, mux->reg); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci if (mux->lock) 16762306a36Sopenharmony_ci spin_unlock_irqrestore(mux->lock, flags); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci return ret; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic int 17362306a36Sopenharmony_ciimx93_clk_composite_mux_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci return clk_mux_ops.determine_rate(hw, req); 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic const struct clk_ops imx93_clk_composite_mux_ops = { 17962306a36Sopenharmony_ci .get_parent = imx93_clk_composite_mux_get_parent, 18062306a36Sopenharmony_ci .set_parent = imx93_clk_composite_mux_set_parent, 18162306a36Sopenharmony_ci .determine_rate = imx93_clk_composite_mux_determine_rate, 18262306a36Sopenharmony_ci}; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistruct clk_hw *imx93_clk_composite_flags(const char *name, const char * const *parent_names, 18562306a36Sopenharmony_ci int num_parents, void __iomem *reg, u32 domain_id, 18662306a36Sopenharmony_ci unsigned long flags) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci struct clk_hw *hw = ERR_PTR(-ENOMEM), *mux_hw; 18962306a36Sopenharmony_ci struct clk_hw *div_hw, *gate_hw; 19062306a36Sopenharmony_ci struct clk_divider *div = NULL; 19162306a36Sopenharmony_ci struct clk_gate *gate = NULL; 19262306a36Sopenharmony_ci struct clk_mux *mux = NULL; 19362306a36Sopenharmony_ci bool clk_ro = false; 19462306a36Sopenharmony_ci u32 authen; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci mux = kzalloc(sizeof(*mux), GFP_KERNEL); 19762306a36Sopenharmony_ci if (!mux) 19862306a36Sopenharmony_ci goto fail; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci mux_hw = &mux->hw; 20162306a36Sopenharmony_ci mux->reg = reg; 20262306a36Sopenharmony_ci mux->shift = CCM_MUX_SHIFT; 20362306a36Sopenharmony_ci mux->mask = CCM_MUX_MASK; 20462306a36Sopenharmony_ci mux->lock = &imx_ccm_lock; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci div = kzalloc(sizeof(*div), GFP_KERNEL); 20762306a36Sopenharmony_ci if (!div) 20862306a36Sopenharmony_ci goto fail; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci div_hw = &div->hw; 21162306a36Sopenharmony_ci div->reg = reg; 21262306a36Sopenharmony_ci div->shift = CCM_DIV_SHIFT; 21362306a36Sopenharmony_ci div->width = CCM_DIV_WIDTH; 21462306a36Sopenharmony_ci div->lock = &imx_ccm_lock; 21562306a36Sopenharmony_ci div->flags = CLK_DIVIDER_ROUND_CLOSEST; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci authen = readl(reg + AUTHEN_OFFSET); 21862306a36Sopenharmony_ci if (!(authen & TZ_NS_MASK) || !(authen & BIT(WHITE_LIST_SHIFT + domain_id))) 21962306a36Sopenharmony_ci clk_ro = true; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci if (clk_ro) { 22262306a36Sopenharmony_ci hw = clk_hw_register_composite(NULL, name, parent_names, num_parents, 22362306a36Sopenharmony_ci mux_hw, &clk_mux_ro_ops, div_hw, 22462306a36Sopenharmony_ci &clk_divider_ro_ops, NULL, NULL, flags); 22562306a36Sopenharmony_ci } else if (!mcore_booted) { 22662306a36Sopenharmony_ci gate = kzalloc(sizeof(*gate), GFP_KERNEL); 22762306a36Sopenharmony_ci if (!gate) 22862306a36Sopenharmony_ci goto fail; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci gate_hw = &gate->hw; 23162306a36Sopenharmony_ci gate->reg = reg; 23262306a36Sopenharmony_ci gate->bit_idx = CCM_OFF_SHIFT; 23362306a36Sopenharmony_ci gate->lock = &imx_ccm_lock; 23462306a36Sopenharmony_ci gate->flags = CLK_GATE_SET_TO_DISABLE; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci hw = clk_hw_register_composite(NULL, name, parent_names, num_parents, 23762306a36Sopenharmony_ci mux_hw, &imx93_clk_composite_mux_ops, div_hw, 23862306a36Sopenharmony_ci &imx93_clk_composite_divider_ops, gate_hw, 23962306a36Sopenharmony_ci &imx93_clk_composite_gate_ops, 24062306a36Sopenharmony_ci flags | CLK_SET_RATE_NO_REPARENT); 24162306a36Sopenharmony_ci } else { 24262306a36Sopenharmony_ci hw = clk_hw_register_composite(NULL, name, parent_names, num_parents, 24362306a36Sopenharmony_ci mux_hw, &imx93_clk_composite_mux_ops, div_hw, 24462306a36Sopenharmony_ci &imx93_clk_composite_divider_ops, NULL, 24562306a36Sopenharmony_ci &imx93_clk_composite_gate_ops, 24662306a36Sopenharmony_ci flags | CLK_SET_RATE_NO_REPARENT); 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (IS_ERR(hw)) 25062306a36Sopenharmony_ci goto fail; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci return hw; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cifail: 25562306a36Sopenharmony_ci kfree(gate); 25662306a36Sopenharmony_ci kfree(div); 25762306a36Sopenharmony_ci kfree(mux); 25862306a36Sopenharmony_ci return ERR_CAST(hw); 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(imx93_clk_composite_flags); 261