linux/drivers/clk/spacemit/ccu_mix.h
Alex Elder 7554729de2
clk: spacemit: mark K1 pll1_d8 as critical
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>
2025-07-04 09:06:33 +08:00

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_ */