mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-08-19 05:05:25 +00:00

The pll1_d8 clock is enabled by the boot loader, and is ultimately a
parent for numerous clocks, including those used by APB and AXI buses.
Guodong Xu discovered that this clock got disabled while responding to
getting -EPROBE_DEFER when requesting a reset controller.
The needed clock (CLK_DMA, along with its parents) had already been
enabled. To respond to the probe deferral return, the CLK_DMA clock
was disabled, and this led to parent clocks also reducing their enable
count. When the enable count for pll1_d8 was decremented it became 0,
which caused it to be disabled. This led to a system hang.
Marking that clock critical resolves this by preventing it from being
disabled.
Define a new macro CCU_FACTOR_GATE_DEFINE() to allow clock flags to
be supplied for a CCU_FACTOR_GATE clock.
Fixes: 1b72c59db0
("clk: spacemit: Add clock support for SpacemiT K1 SoC")
Signed-off-by: Alex Elder <elder@riscstar.com>
Tested-by: Guodong Xu <guodong@riscstar.com>
Reviewed-by: Haylen Chu <heylenay@4d2.org>
Link: https://lore.kernel.org/r/20250612224856.1105924-1-elder@riscstar.com
Signed-off-by: Yixun Lan <dlan@gentoo.org>
224 lines
6.9 KiB
C
224 lines
6.9 KiB
C
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
/*
|
|
* Copyright (c) 2024 SpacemiT Technology Co. Ltd
|
|
* Copyright (c) 2024-2025 Haylen Chu <heylenay@4d2.org>
|
|
*/
|
|
|
|
#ifndef _CCU_MIX_H_
|
|
#define _CCU_MIX_H_
|
|
|
|
#include <linux/clk-provider.h>
|
|
|
|
#include "ccu_common.h"
|
|
|
|
/**
|
|
* struct ccu_gate_config - Gate configuration
|
|
*
|
|
* @mask: Mask to enable the gate. Some clocks may have more than one bit
|
|
* set in this field.
|
|
*/
|
|
struct ccu_gate_config {
|
|
u32 mask;
|
|
};
|
|
|
|
struct ccu_factor_config {
|
|
u32 div;
|
|
u32 mul;
|
|
};
|
|
|
|
struct ccu_mux_config {
|
|
u8 shift;
|
|
u8 width;
|
|
};
|
|
|
|
struct ccu_div_config {
|
|
u8 shift;
|
|
u8 width;
|
|
};
|
|
|
|
struct ccu_mix {
|
|
struct ccu_factor_config factor;
|
|
struct ccu_gate_config gate;
|
|
struct ccu_div_config div;
|
|
struct ccu_mux_config mux;
|
|
struct ccu_common common;
|
|
};
|
|
|
|
#define CCU_GATE_INIT(_mask) { .mask = _mask }
|
|
#define CCU_FACTOR_INIT(_div, _mul) { .div = _div, .mul = _mul }
|
|
#define CCU_MUX_INIT(_shift, _width) { .shift = _shift, .width = _width }
|
|
#define CCU_DIV_INIT(_shift, _width) { .shift = _shift, .width = _width }
|
|
|
|
#define CCU_PARENT_HW(_parent) { .hw = &_parent.common.hw }
|
|
#define CCU_PARENT_NAME(_name) { .fw_name = #_name }
|
|
|
|
#define CCU_MIX_INITHW(_name, _parent, _ops, _flags) \
|
|
.hw.init = &(struct clk_init_data) { \
|
|
.flags = _flags, \
|
|
.name = #_name, \
|
|
.parent_data = (const struct clk_parent_data[]) \
|
|
{ _parent }, \
|
|
.num_parents = 1, \
|
|
.ops = &_ops, \
|
|
}
|
|
|
|
#define CCU_MIX_INITHW_PARENTS(_name, _parents, _ops, _flags) \
|
|
.hw.init = CLK_HW_INIT_PARENTS_DATA(#_name, _parents, &_ops, _flags)
|
|
|
|
#define CCU_GATE_DEFINE(_name, _parent, _reg_ctrl, _mask_gate, _flags) \
|
|
static struct ccu_mix _name = { \
|
|
.gate = CCU_GATE_INIT(_mask_gate), \
|
|
.common = { \
|
|
.reg_ctrl = _reg_ctrl, \
|
|
CCU_MIX_INITHW(_name, _parent, spacemit_ccu_gate_ops, _flags), \
|
|
} \
|
|
}
|
|
|
|
#define CCU_FACTOR_DEFINE(_name, _parent, _div, _mul) \
|
|
static struct ccu_mix _name = { \
|
|
.factor = CCU_FACTOR_INIT(_div, _mul), \
|
|
.common = { \
|
|
CCU_MIX_INITHW(_name, _parent, spacemit_ccu_factor_ops, 0), \
|
|
} \
|
|
}
|
|
|
|
#define CCU_MUX_DEFINE(_name, _parents, _reg_ctrl, _shift, _width, _flags) \
|
|
static struct ccu_mix _name = { \
|
|
.mux = CCU_MUX_INIT(_shift, _width), \
|
|
.common = { \
|
|
.reg_ctrl = _reg_ctrl, \
|
|
CCU_MIX_INITHW_PARENTS(_name, _parents, spacemit_ccu_mux_ops, \
|
|
_flags), \
|
|
} \
|
|
}
|
|
|
|
#define CCU_DIV_DEFINE(_name, _parent, _reg_ctrl, _shift, _width, _flags) \
|
|
static struct ccu_mix _name = { \
|
|
.div = CCU_DIV_INIT(_shift, _width), \
|
|
.common = { \
|
|
.reg_ctrl = _reg_ctrl, \
|
|
CCU_MIX_INITHW(_name, _parent, spacemit_ccu_div_ops, _flags) \
|
|
} \
|
|
}
|
|
|
|
#define CCU_FACTOR_GATE_FLAGS_DEFINE(_name, _parent, _reg_ctrl, _mask_gate, _div, \
|
|
_mul, _flags) \
|
|
static struct ccu_mix _name = { \
|
|
.gate = CCU_GATE_INIT(_mask_gate), \
|
|
.factor = CCU_FACTOR_INIT(_div, _mul), \
|
|
.common = { \
|
|
.reg_ctrl = _reg_ctrl, \
|
|
CCU_MIX_INITHW(_name, _parent, spacemit_ccu_factor_gate_ops, _flags) \
|
|
} \
|
|
}
|
|
|
|
#define CCU_FACTOR_GATE_DEFINE(_name, _parent, _reg_ctrl, _mask_gate, _div, \
|
|
_mul) \
|
|
CCU_FACTOR_GATE_FLAGS_DEFINE(_name, _parent, _reg_ctrl, _mask_gate, _div, \
|
|
_mul, 0)
|
|
|
|
#define CCU_MUX_GATE_DEFINE(_name, _parents, _reg_ctrl, _shift, _width, \
|
|
_mask_gate, _flags) \
|
|
static struct ccu_mix _name = { \
|
|
.gate = CCU_GATE_INIT(_mask_gate), \
|
|
.mux = CCU_MUX_INIT(_shift, _width), \
|
|
.common = { \
|
|
.reg_ctrl = _reg_ctrl, \
|
|
CCU_MIX_INITHW_PARENTS(_name, _parents, \
|
|
spacemit_ccu_mux_gate_ops, _flags), \
|
|
} \
|
|
}
|
|
|
|
#define CCU_DIV_GATE_DEFINE(_name, _parent, _reg_ctrl, _shift, _width, \
|
|
_mask_gate, _flags) \
|
|
static struct ccu_mix _name = { \
|
|
.gate = CCU_GATE_INIT(_mask_gate), \
|
|
.div = CCU_DIV_INIT(_shift, _width), \
|
|
.common = { \
|
|
.reg_ctrl = _reg_ctrl, \
|
|
CCU_MIX_INITHW(_name, _parent, spacemit_ccu_div_gate_ops, \
|
|
_flags), \
|
|
} \
|
|
}
|
|
|
|
#define CCU_MUX_DIV_GATE_DEFINE(_name, _parents, _reg_ctrl, _mshift, _mwidth, \
|
|
_muxshift, _muxwidth, _mask_gate, _flags) \
|
|
static struct ccu_mix _name = { \
|
|
.gate = CCU_GATE_INIT(_mask_gate), \
|
|
.div = CCU_DIV_INIT(_mshift, _mwidth), \
|
|
.mux = CCU_MUX_INIT(_muxshift, _muxwidth), \
|
|
.common = { \
|
|
.reg_ctrl = _reg_ctrl, \
|
|
CCU_MIX_INITHW_PARENTS(_name, _parents, \
|
|
spacemit_ccu_mux_div_gate_ops, _flags), \
|
|
}, \
|
|
}
|
|
|
|
#define CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(_name, _parents, _reg_ctrl, _reg_fc, \
|
|
_mshift, _mwidth, _mask_fc, _muxshift, \
|
|
_muxwidth, _mask_gate, _flags) \
|
|
static struct ccu_mix _name = { \
|
|
.gate = CCU_GATE_INIT(_mask_gate), \
|
|
.div = CCU_DIV_INIT(_mshift, _mwidth), \
|
|
.mux = CCU_MUX_INIT(_muxshift, _muxwidth), \
|
|
.common = { \
|
|
.reg_ctrl = _reg_ctrl, \
|
|
.reg_fc = _reg_fc, \
|
|
.mask_fc = _mask_fc, \
|
|
CCU_MIX_INITHW_PARENTS(_name, _parents, \
|
|
spacemit_ccu_mux_div_gate_ops, _flags), \
|
|
}, \
|
|
}
|
|
|
|
#define CCU_MUX_DIV_GATE_FC_DEFINE(_name, _parents, _reg_ctrl, _mshift, _mwidth,\
|
|
_mask_fc, _muxshift, _muxwidth, _mask_gate, \
|
|
_flags) \
|
|
CCU_MUX_DIV_GATE_SPLIT_FC_DEFINE(_name, _parents, _reg_ctrl, _reg_ctrl, _mshift,\
|
|
_mwidth, _mask_fc, _muxshift, _muxwidth, \
|
|
_mask_gate, _flags)
|
|
|
|
#define CCU_MUX_DIV_FC_DEFINE(_name, _parents, _reg_ctrl, _mshift, _mwidth, \
|
|
_mask_fc, _muxshift, _muxwidth, _flags) \
|
|
static struct ccu_mix _name = { \
|
|
.div = CCU_DIV_INIT(_mshift, _mwidth), \
|
|
.mux = CCU_MUX_INIT(_muxshift, _muxwidth), \
|
|
.common = { \
|
|
.reg_ctrl = _reg_ctrl, \
|
|
.reg_fc = _reg_ctrl, \
|
|
.mask_fc = _mask_fc, \
|
|
CCU_MIX_INITHW_PARENTS(_name, _parents, \
|
|
spacemit_ccu_mux_div_ops, _flags), \
|
|
}, \
|
|
}
|
|
|
|
#define CCU_MUX_FC_DEFINE(_name, _parents, _reg_ctrl, _mask_fc, _muxshift, \
|
|
_muxwidth, _flags) \
|
|
static struct ccu_mix _name = { \
|
|
.mux = CCU_MUX_INIT(_muxshift, _muxwidth), \
|
|
.common = { \
|
|
.reg_ctrl = _reg_ctrl, \
|
|
.reg_fc = _reg_ctrl, \
|
|
.mask_fc = _mask_fc, \
|
|
CCU_MIX_INITHW_PARENTS(_name, _parents, spacemit_ccu_mux_ops, \
|
|
_flags) \
|
|
}, \
|
|
}
|
|
|
|
static inline struct ccu_mix *hw_to_ccu_mix(struct clk_hw *hw)
|
|
{
|
|
struct ccu_common *common = hw_to_ccu_common(hw);
|
|
|
|
return container_of(common, struct ccu_mix, common);
|
|
}
|
|
|
|
extern const struct clk_ops spacemit_ccu_gate_ops;
|
|
extern const struct clk_ops spacemit_ccu_factor_ops;
|
|
extern const struct clk_ops spacemit_ccu_mux_ops;
|
|
extern const struct clk_ops spacemit_ccu_div_ops;
|
|
extern const struct clk_ops spacemit_ccu_factor_gate_ops;
|
|
extern const struct clk_ops spacemit_ccu_div_gate_ops;
|
|
extern const struct clk_ops spacemit_ccu_mux_gate_ops;
|
|
extern const struct clk_ops spacemit_ccu_mux_div_ops;
|
|
extern const struct clk_ops spacemit_ccu_mux_div_gate_ops;
|
|
#endif /* _CCU_DIV_H_ */
|