clk: baikal-t1: Add SATA internal ref clock buffer
It turns out the internal SATA reference clock signal will stay
unavailable for the SATA interface consumer until the buffer on it's way
is ungated. So aside with having the actual clock divider enabled we need
to ungate a buffer placed on the signal way to the SATA controller (most
likely some rudiment from the initial SoC release). Seeing the switch flag
is placed in the same register as the SATA-ref clock divider at a
non-standard ffset, let's implement it as a separate clock controller with
the set-rate propagation to the parental clock divider wrapper. As such
we'll be able to disable/enable and still change the original clock source
rate.
Fixes: 353afa3a8d
("clk: Add Baikal-T1 CCU Dividers driver")
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Link: https://lore.kernel.org/r/20220929225402.9696-5-Sergey.Semin@baikalelectronics.ru
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
This commit is contained in:
parent
e2eef31276
commit
081a9b7c74
|
@ -34,6 +34,7 @@
|
||||||
#define CCU_DIV_CTL_CLKDIV_MASK(_width) \
|
#define CCU_DIV_CTL_CLKDIV_MASK(_width) \
|
||||||
GENMASK((_width) + CCU_DIV_CTL_CLKDIV_FLD - 1, CCU_DIV_CTL_CLKDIV_FLD)
|
GENMASK((_width) + CCU_DIV_CTL_CLKDIV_FLD - 1, CCU_DIV_CTL_CLKDIV_FLD)
|
||||||
#define CCU_DIV_CTL_LOCK_SHIFTED BIT(27)
|
#define CCU_DIV_CTL_LOCK_SHIFTED BIT(27)
|
||||||
|
#define CCU_DIV_CTL_GATE_REF_BUF BIT(28)
|
||||||
#define CCU_DIV_CTL_LOCK_NORMAL BIT(31)
|
#define CCU_DIV_CTL_LOCK_NORMAL BIT(31)
|
||||||
|
|
||||||
#define CCU_DIV_RST_DELAY_US 1
|
#define CCU_DIV_RST_DELAY_US 1
|
||||||
|
@ -170,6 +171,40 @@ static int ccu_div_gate_is_enabled(struct clk_hw *hw)
|
||||||
return !!(val & CCU_DIV_CTL_EN);
|
return !!(val & CCU_DIV_CTL_EN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ccu_div_buf_enable(struct clk_hw *hw)
|
||||||
|
{
|
||||||
|
struct ccu_div *div = to_ccu_div(hw);
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&div->lock, flags);
|
||||||
|
regmap_update_bits(div->sys_regs, div->reg_ctl,
|
||||||
|
CCU_DIV_CTL_GATE_REF_BUF, 0);
|
||||||
|
spin_unlock_irqrestore(&div->lock, flags);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ccu_div_buf_disable(struct clk_hw *hw)
|
||||||
|
{
|
||||||
|
struct ccu_div *div = to_ccu_div(hw);
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&div->lock, flags);
|
||||||
|
regmap_update_bits(div->sys_regs, div->reg_ctl,
|
||||||
|
CCU_DIV_CTL_GATE_REF_BUF, CCU_DIV_CTL_GATE_REF_BUF);
|
||||||
|
spin_unlock_irqrestore(&div->lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ccu_div_buf_is_enabled(struct clk_hw *hw)
|
||||||
|
{
|
||||||
|
struct ccu_div *div = to_ccu_div(hw);
|
||||||
|
u32 val = 0;
|
||||||
|
|
||||||
|
regmap_read(div->sys_regs, div->reg_ctl, &val);
|
||||||
|
|
||||||
|
return !(val & CCU_DIV_CTL_GATE_REF_BUF);
|
||||||
|
}
|
||||||
|
|
||||||
static unsigned long ccu_div_var_recalc_rate(struct clk_hw *hw,
|
static unsigned long ccu_div_var_recalc_rate(struct clk_hw *hw,
|
||||||
unsigned long parent_rate)
|
unsigned long parent_rate)
|
||||||
{
|
{
|
||||||
|
@ -323,6 +358,7 @@ static const struct ccu_div_dbgfs_bit ccu_div_bits[] = {
|
||||||
CCU_DIV_DBGFS_BIT_ATTR("div_en", CCU_DIV_CTL_EN),
|
CCU_DIV_DBGFS_BIT_ATTR("div_en", CCU_DIV_CTL_EN),
|
||||||
CCU_DIV_DBGFS_BIT_ATTR("div_rst", CCU_DIV_CTL_RST),
|
CCU_DIV_DBGFS_BIT_ATTR("div_rst", CCU_DIV_CTL_RST),
|
||||||
CCU_DIV_DBGFS_BIT_ATTR("div_bypass", CCU_DIV_CTL_SET_CLKDIV),
|
CCU_DIV_DBGFS_BIT_ATTR("div_bypass", CCU_DIV_CTL_SET_CLKDIV),
|
||||||
|
CCU_DIV_DBGFS_BIT_ATTR("div_buf", CCU_DIV_CTL_GATE_REF_BUF),
|
||||||
CCU_DIV_DBGFS_BIT_ATTR("div_lock", CCU_DIV_CTL_LOCK_NORMAL)
|
CCU_DIV_DBGFS_BIT_ATTR("div_lock", CCU_DIV_CTL_LOCK_NORMAL)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -441,6 +477,9 @@ static void ccu_div_var_debug_init(struct clk_hw *hw, struct dentry *dentry)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!strcmp("div_buf", name))
|
||||||
|
continue;
|
||||||
|
|
||||||
bits[didx] = ccu_div_bits[bidx];
|
bits[didx] = ccu_div_bits[bidx];
|
||||||
bits[didx].div = div;
|
bits[didx].div = div;
|
||||||
|
|
||||||
|
@ -477,6 +516,21 @@ static void ccu_div_gate_debug_init(struct clk_hw *hw, struct dentry *dentry)
|
||||||
&ccu_div_dbgfs_fixed_clkdiv_fops);
|
&ccu_div_dbgfs_fixed_clkdiv_fops);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ccu_div_buf_debug_init(struct clk_hw *hw, struct dentry *dentry)
|
||||||
|
{
|
||||||
|
struct ccu_div *div = to_ccu_div(hw);
|
||||||
|
struct ccu_div_dbgfs_bit *bit;
|
||||||
|
|
||||||
|
bit = kmalloc(sizeof(*bit), GFP_KERNEL);
|
||||||
|
if (!bit)
|
||||||
|
return;
|
||||||
|
|
||||||
|
*bit = ccu_div_bits[3];
|
||||||
|
bit->div = div;
|
||||||
|
debugfs_create_file_unsafe(bit->name, ccu_div_dbgfs_mode, dentry, bit,
|
||||||
|
&ccu_div_dbgfs_bit_fops);
|
||||||
|
}
|
||||||
|
|
||||||
static void ccu_div_fixed_debug_init(struct clk_hw *hw, struct dentry *dentry)
|
static void ccu_div_fixed_debug_init(struct clk_hw *hw, struct dentry *dentry)
|
||||||
{
|
{
|
||||||
struct ccu_div *div = to_ccu_div(hw);
|
struct ccu_div *div = to_ccu_div(hw);
|
||||||
|
@ -489,6 +543,7 @@ static void ccu_div_fixed_debug_init(struct clk_hw *hw, struct dentry *dentry)
|
||||||
|
|
||||||
#define ccu_div_var_debug_init NULL
|
#define ccu_div_var_debug_init NULL
|
||||||
#define ccu_div_gate_debug_init NULL
|
#define ccu_div_gate_debug_init NULL
|
||||||
|
#define ccu_div_buf_debug_init NULL
|
||||||
#define ccu_div_fixed_debug_init NULL
|
#define ccu_div_fixed_debug_init NULL
|
||||||
|
|
||||||
#endif /* !CONFIG_DEBUG_FS */
|
#endif /* !CONFIG_DEBUG_FS */
|
||||||
|
@ -520,6 +575,13 @@ static const struct clk_ops ccu_div_gate_ops = {
|
||||||
.debug_init = ccu_div_gate_debug_init
|
.debug_init = ccu_div_gate_debug_init
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct clk_ops ccu_div_buf_ops = {
|
||||||
|
.enable = ccu_div_buf_enable,
|
||||||
|
.disable = ccu_div_buf_disable,
|
||||||
|
.is_enabled = ccu_div_buf_is_enabled,
|
||||||
|
.debug_init = ccu_div_buf_debug_init
|
||||||
|
};
|
||||||
|
|
||||||
static const struct clk_ops ccu_div_fixed_ops = {
|
static const struct clk_ops ccu_div_fixed_ops = {
|
||||||
.recalc_rate = ccu_div_fixed_recalc_rate,
|
.recalc_rate = ccu_div_fixed_recalc_rate,
|
||||||
.round_rate = ccu_div_fixed_round_rate,
|
.round_rate = ccu_div_fixed_round_rate,
|
||||||
|
@ -566,6 +628,8 @@ struct ccu_div *ccu_div_hw_register(const struct ccu_div_init_data *div_init)
|
||||||
} else if (div_init->type == CCU_DIV_GATE) {
|
} else if (div_init->type == CCU_DIV_GATE) {
|
||||||
hw_init.ops = &ccu_div_gate_ops;
|
hw_init.ops = &ccu_div_gate_ops;
|
||||||
div->divider = div_init->divider;
|
div->divider = div_init->divider;
|
||||||
|
} else if (div_init->type == CCU_DIV_BUF) {
|
||||||
|
hw_init.ops = &ccu_div_buf_ops;
|
||||||
} else if (div_init->type == CCU_DIV_FIXED) {
|
} else if (div_init->type == CCU_DIV_FIXED) {
|
||||||
hw_init.ops = &ccu_div_fixed_ops;
|
hw_init.ops = &ccu_div_fixed_ops;
|
||||||
div->divider = div_init->divider;
|
div->divider = div_init->divider;
|
||||||
|
|
|
@ -15,8 +15,10 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CCU Divider private clock IDs
|
* CCU Divider private clock IDs
|
||||||
|
* @CCU_SYS_SATA_CLK: CCU SATA internal clock
|
||||||
* @CCU_SYS_XGMAC_CLK: CCU XGMAC internal clock
|
* @CCU_SYS_XGMAC_CLK: CCU XGMAC internal clock
|
||||||
*/
|
*/
|
||||||
|
#define CCU_SYS_SATA_CLK -1
|
||||||
#define CCU_SYS_XGMAC_CLK -2
|
#define CCU_SYS_XGMAC_CLK -2
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -37,11 +39,13 @@
|
||||||
* enum ccu_div_type - CCU Divider types
|
* enum ccu_div_type - CCU Divider types
|
||||||
* @CCU_DIV_VAR: Clocks gate with variable divider.
|
* @CCU_DIV_VAR: Clocks gate with variable divider.
|
||||||
* @CCU_DIV_GATE: Clocks gate with fixed divider.
|
* @CCU_DIV_GATE: Clocks gate with fixed divider.
|
||||||
|
* @CCU_DIV_BUF: Clock gate with no divider.
|
||||||
* @CCU_DIV_FIXED: Ungateable clock with fixed divider.
|
* @CCU_DIV_FIXED: Ungateable clock with fixed divider.
|
||||||
*/
|
*/
|
||||||
enum ccu_div_type {
|
enum ccu_div_type {
|
||||||
CCU_DIV_VAR,
|
CCU_DIV_VAR,
|
||||||
CCU_DIV_GATE,
|
CCU_DIV_GATE,
|
||||||
|
CCU_DIV_BUF,
|
||||||
CCU_DIV_FIXED
|
CCU_DIV_FIXED
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -76,6 +76,16 @@
|
||||||
.divider = _divider \
|
.divider = _divider \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define CCU_DIV_BUF_INFO(_id, _name, _pname, _base, _flags) \
|
||||||
|
{ \
|
||||||
|
.id = _id, \
|
||||||
|
.name = _name, \
|
||||||
|
.parent_name = _pname, \
|
||||||
|
.base = _base, \
|
||||||
|
.type = CCU_DIV_BUF, \
|
||||||
|
.flags = _flags \
|
||||||
|
}
|
||||||
|
|
||||||
#define CCU_DIV_FIXED_INFO(_id, _name, _pname, _divider) \
|
#define CCU_DIV_FIXED_INFO(_id, _name, _pname, _divider) \
|
||||||
{ \
|
{ \
|
||||||
.id = _id, \
|
.id = _id, \
|
||||||
|
@ -188,11 +198,14 @@ static const struct ccu_div_rst_map axi_rst_map[] = {
|
||||||
* for the SoC devices registers IO-operations.
|
* for the SoC devices registers IO-operations.
|
||||||
*/
|
*/
|
||||||
static const struct ccu_div_info sys_info[] = {
|
static const struct ccu_div_info sys_info[] = {
|
||||||
CCU_DIV_VAR_INFO(CCU_SYS_SATA_REF_CLK, "sys_sata_ref_clk",
|
CCU_DIV_VAR_INFO(CCU_SYS_SATA_CLK, "sys_sata_clk",
|
||||||
"sata_clk", CCU_SYS_SATA_REF_BASE, 4,
|
"sata_clk", CCU_SYS_SATA_REF_BASE, 4,
|
||||||
CLK_SET_RATE_GATE,
|
CLK_SET_RATE_GATE,
|
||||||
CCU_DIV_SKIP_ONE | CCU_DIV_LOCK_SHIFTED |
|
CCU_DIV_SKIP_ONE | CCU_DIV_LOCK_SHIFTED |
|
||||||
CCU_DIV_RESET_DOMAIN),
|
CCU_DIV_RESET_DOMAIN),
|
||||||
|
CCU_DIV_BUF_INFO(CCU_SYS_SATA_REF_CLK, "sys_sata_ref_clk",
|
||||||
|
"sys_sata_clk", CCU_SYS_SATA_REF_BASE,
|
||||||
|
CLK_SET_RATE_PARENT),
|
||||||
CCU_DIV_VAR_INFO(CCU_SYS_APB_CLK, "sys_apb_clk",
|
CCU_DIV_VAR_INFO(CCU_SYS_APB_CLK, "sys_apb_clk",
|
||||||
"pcie_clk", CCU_SYS_APB_BASE, 5,
|
"pcie_clk", CCU_SYS_APB_BASE, 5,
|
||||||
CLK_IS_CRITICAL, CCU_DIV_RESET_DOMAIN),
|
CLK_IS_CRITICAL, CCU_DIV_RESET_DOMAIN),
|
||||||
|
@ -398,6 +411,9 @@ static int ccu_div_clk_register(struct ccu_div_data *data)
|
||||||
init.base = info->base;
|
init.base = info->base;
|
||||||
init.sys_regs = data->sys_regs;
|
init.sys_regs = data->sys_regs;
|
||||||
init.divider = info->divider;
|
init.divider = info->divider;
|
||||||
|
} else if (init.type == CCU_DIV_BUF) {
|
||||||
|
init.base = info->base;
|
||||||
|
init.sys_regs = data->sys_regs;
|
||||||
} else {
|
} else {
|
||||||
init.divider = info->divider;
|
init.divider = info->divider;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue