d342df1172
Previously we assumed that the root clock slice is enabled
by default when kernel boot up. But the bootloader may disable
the clocks before jump into kernel. The gate ops should be registered
rather than NULL to make sure the disabled clock can be enabled
when kernel boot up. Refine the code to skip disable the clock
if mcore booted.
Fixes: a740d7350f
("clk: imx: imx93: add mcore_booted module paratemter")
Signed-off-by: Jacky Bai <ping.bai@nxp.com>
Reviewed-by: Peng Fan <peng.fan@nxp.com>
Tested-by: Chancel Liu <chancel.liu@nxp.com>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
Reviewed-by: Abel Vesa <abel.vesa@linaro.org>
Link: https://lore.kernel.org/r/20240607133347.3291040-3-peng.fan@oss.nxp.com
Signed-off-by: Abel Vesa <abel.vesa@linaro.org>
262 lines
6.1 KiB
C
262 lines
6.1 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright 2021 NXP
|
|
*
|
|
* Peng Fan <peng.fan@nxp.com>
|
|
*/
|
|
|
|
#include <linux/clk-provider.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/export.h>
|
|
#include <linux/io.h>
|
|
#include <linux/iopoll.h>
|
|
#include <linux/slab.h>
|
|
|
|
#include "clk.h"
|
|
|
|
#define TIMEOUT_US 500U
|
|
|
|
#define CCM_DIV_SHIFT 0
|
|
#define CCM_DIV_WIDTH 8
|
|
#define CCM_MUX_SHIFT 8
|
|
#define CCM_MUX_MASK 3
|
|
#define CCM_OFF_SHIFT 24
|
|
#define CCM_BUSY_SHIFT 28
|
|
|
|
#define STAT_OFFSET 0x4
|
|
#define AUTHEN_OFFSET 0x30
|
|
#define TZ_NS_SHIFT 9
|
|
#define TZ_NS_MASK BIT(9)
|
|
|
|
#define WHITE_LIST_SHIFT 16
|
|
|
|
static int imx93_clk_composite_wait_ready(struct clk_hw *hw, void __iomem *reg)
|
|
{
|
|
int ret;
|
|
u32 val;
|
|
|
|
ret = readl_poll_timeout_atomic(reg + STAT_OFFSET, val, !(val & BIT(CCM_BUSY_SHIFT)),
|
|
0, TIMEOUT_US);
|
|
if (ret)
|
|
pr_err("Slice[%s] busy timeout\n", clk_hw_get_name(hw));
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void imx93_clk_composite_gate_endisable(struct clk_hw *hw, int enable)
|
|
{
|
|
struct clk_gate *gate = to_clk_gate(hw);
|
|
unsigned long flags;
|
|
u32 reg;
|
|
|
|
if (gate->lock)
|
|
spin_lock_irqsave(gate->lock, flags);
|
|
|
|
reg = readl(gate->reg);
|
|
|
|
if (enable)
|
|
reg &= ~BIT(gate->bit_idx);
|
|
else
|
|
reg |= BIT(gate->bit_idx);
|
|
|
|
writel(reg, gate->reg);
|
|
|
|
imx93_clk_composite_wait_ready(hw, gate->reg);
|
|
|
|
if (gate->lock)
|
|
spin_unlock_irqrestore(gate->lock, flags);
|
|
}
|
|
|
|
static int imx93_clk_composite_gate_enable(struct clk_hw *hw)
|
|
{
|
|
imx93_clk_composite_gate_endisable(hw, 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void imx93_clk_composite_gate_disable(struct clk_hw *hw)
|
|
{
|
|
/*
|
|
* Skip disable the root clock gate if mcore enabled.
|
|
* The root clock may be used by the mcore.
|
|
*/
|
|
if (mcore_booted)
|
|
return;
|
|
|
|
imx93_clk_composite_gate_endisable(hw, 0);
|
|
}
|
|
|
|
static const struct clk_ops imx93_clk_composite_gate_ops = {
|
|
.enable = imx93_clk_composite_gate_enable,
|
|
.disable = imx93_clk_composite_gate_disable,
|
|
.is_enabled = clk_gate_is_enabled,
|
|
};
|
|
|
|
static unsigned long
|
|
imx93_clk_composite_divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
|
|
{
|
|
return clk_divider_ops.recalc_rate(hw, parent_rate);
|
|
}
|
|
|
|
static long
|
|
imx93_clk_composite_divider_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate)
|
|
{
|
|
return clk_divider_ops.round_rate(hw, rate, prate);
|
|
}
|
|
|
|
static int
|
|
imx93_clk_composite_divider_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
|
|
{
|
|
return clk_divider_ops.determine_rate(hw, req);
|
|
}
|
|
|
|
static int imx93_clk_composite_divider_set_rate(struct clk_hw *hw, unsigned long rate,
|
|
unsigned long parent_rate)
|
|
{
|
|
struct clk_divider *divider = to_clk_divider(hw);
|
|
int value;
|
|
unsigned long flags = 0;
|
|
u32 val;
|
|
int ret;
|
|
|
|
value = divider_get_val(rate, parent_rate, divider->table, divider->width, divider->flags);
|
|
if (value < 0)
|
|
return value;
|
|
|
|
if (divider->lock)
|
|
spin_lock_irqsave(divider->lock, flags);
|
|
|
|
val = readl(divider->reg);
|
|
val &= ~(clk_div_mask(divider->width) << divider->shift);
|
|
val |= (u32)value << divider->shift;
|
|
writel(val, divider->reg);
|
|
|
|
ret = imx93_clk_composite_wait_ready(hw, divider->reg);
|
|
|
|
if (divider->lock)
|
|
spin_unlock_irqrestore(divider->lock, flags);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const struct clk_ops imx93_clk_composite_divider_ops = {
|
|
.recalc_rate = imx93_clk_composite_divider_recalc_rate,
|
|
.round_rate = imx93_clk_composite_divider_round_rate,
|
|
.determine_rate = imx93_clk_composite_divider_determine_rate,
|
|
.set_rate = imx93_clk_composite_divider_set_rate,
|
|
};
|
|
|
|
static u8 imx93_clk_composite_mux_get_parent(struct clk_hw *hw)
|
|
{
|
|
return clk_mux_ops.get_parent(hw);
|
|
}
|
|
|
|
static int imx93_clk_composite_mux_set_parent(struct clk_hw *hw, u8 index)
|
|
{
|
|
struct clk_mux *mux = to_clk_mux(hw);
|
|
u32 val = clk_mux_index_to_val(mux->table, mux->flags, index);
|
|
unsigned long flags = 0;
|
|
u32 reg;
|
|
int ret;
|
|
|
|
if (mux->lock)
|
|
spin_lock_irqsave(mux->lock, flags);
|
|
|
|
reg = readl(mux->reg);
|
|
reg &= ~(mux->mask << mux->shift);
|
|
val = val << mux->shift;
|
|
reg |= val;
|
|
writel(reg, mux->reg);
|
|
|
|
ret = imx93_clk_composite_wait_ready(hw, mux->reg);
|
|
|
|
if (mux->lock)
|
|
spin_unlock_irqrestore(mux->lock, flags);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
imx93_clk_composite_mux_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
|
|
{
|
|
return clk_mux_ops.determine_rate(hw, req);
|
|
}
|
|
|
|
static const struct clk_ops imx93_clk_composite_mux_ops = {
|
|
.get_parent = imx93_clk_composite_mux_get_parent,
|
|
.set_parent = imx93_clk_composite_mux_set_parent,
|
|
.determine_rate = imx93_clk_composite_mux_determine_rate,
|
|
};
|
|
|
|
struct clk_hw *imx93_clk_composite_flags(const char *name, const char * const *parent_names,
|
|
int num_parents, void __iomem *reg, u32 domain_id,
|
|
unsigned long flags)
|
|
{
|
|
struct clk_hw *hw = ERR_PTR(-ENOMEM), *mux_hw;
|
|
struct clk_hw *div_hw, *gate_hw;
|
|
struct clk_divider *div = NULL;
|
|
struct clk_gate *gate = NULL;
|
|
struct clk_mux *mux = NULL;
|
|
bool clk_ro = false;
|
|
u32 authen;
|
|
|
|
mux = kzalloc(sizeof(*mux), GFP_KERNEL);
|
|
if (!mux)
|
|
goto fail;
|
|
|
|
mux_hw = &mux->hw;
|
|
mux->reg = reg;
|
|
mux->shift = CCM_MUX_SHIFT;
|
|
mux->mask = CCM_MUX_MASK;
|
|
mux->lock = &imx_ccm_lock;
|
|
|
|
div = kzalloc(sizeof(*div), GFP_KERNEL);
|
|
if (!div)
|
|
goto fail;
|
|
|
|
div_hw = &div->hw;
|
|
div->reg = reg;
|
|
div->shift = CCM_DIV_SHIFT;
|
|
div->width = CCM_DIV_WIDTH;
|
|
div->lock = &imx_ccm_lock;
|
|
div->flags = CLK_DIVIDER_ROUND_CLOSEST;
|
|
|
|
authen = readl(reg + AUTHEN_OFFSET);
|
|
if (!(authen & TZ_NS_MASK) || !(authen & BIT(WHITE_LIST_SHIFT + domain_id)))
|
|
clk_ro = true;
|
|
|
|
if (clk_ro) {
|
|
hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
|
|
mux_hw, &clk_mux_ro_ops, div_hw,
|
|
&clk_divider_ro_ops, NULL, NULL, flags);
|
|
} else {
|
|
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
|
|
if (!gate)
|
|
goto fail;
|
|
|
|
gate_hw = &gate->hw;
|
|
gate->reg = reg;
|
|
gate->bit_idx = CCM_OFF_SHIFT;
|
|
gate->lock = &imx_ccm_lock;
|
|
gate->flags = CLK_GATE_SET_TO_DISABLE;
|
|
|
|
hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
|
|
mux_hw, &imx93_clk_composite_mux_ops, div_hw,
|
|
&imx93_clk_composite_divider_ops, gate_hw,
|
|
&imx93_clk_composite_gate_ops,
|
|
flags | CLK_SET_RATE_NO_REPARENT);
|
|
}
|
|
|
|
if (IS_ERR(hw))
|
|
goto fail;
|
|
|
|
return hw;
|
|
|
|
fail:
|
|
kfree(gate);
|
|
kfree(div);
|
|
kfree(mux);
|
|
return ERR_CAST(hw);
|
|
}
|
|
EXPORT_SYMBOL_GPL(imx93_clk_composite_flags);
|