1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (c) 2013, The Linux Foundation. All rights reserved.
6 #include <linux/kernel.h>
7 #include <linux/bitops.h>
9 #include <linux/delay.h>
10 #include <linux/export.h>
11 #include <linux/clk-provider.h>
12 #include <linux/regmap.h>
14 #include "clk-branch.h"
16 static bool clk_branch_in_hwcg_mode(const struct clk_branch
*br
)
23 regmap_read(br
->clkr
.regmap
, br
->hwcg_reg
, &val
);
25 return !!(val
& BIT(br
->hwcg_bit
));
28 static bool clk_branch_check_halt(const struct clk_branch
*br
, bool enabling
)
30 bool invert
= (br
->halt_check
== BRANCH_HALT_ENABLE
);
33 regmap_read(br
->clkr
.regmap
, br
->halt_reg
, &val
);
35 val
&= BIT(br
->halt_bit
);
39 return !!val
== !enabling
;
42 #define BRANCH_CLK_OFF BIT(31)
43 #define BRANCH_NOC_FSM_STATUS_SHIFT 28
44 #define BRANCH_NOC_FSM_STATUS_MASK 0x7
45 #define BRANCH_NOC_FSM_STATUS_ON (0x2 << BRANCH_NOC_FSM_STATUS_SHIFT)
47 static bool clk_branch2_check_halt(const struct clk_branch
*br
, bool enabling
)
52 mask
= BRANCH_NOC_FSM_STATUS_MASK
<< BRANCH_NOC_FSM_STATUS_SHIFT
;
53 mask
|= BRANCH_CLK_OFF
;
55 regmap_read(br
->clkr
.regmap
, br
->halt_reg
, &val
);
59 return (val
& BRANCH_CLK_OFF
) == 0 ||
60 val
== BRANCH_NOC_FSM_STATUS_ON
;
62 return val
& BRANCH_CLK_OFF
;
66 static int clk_branch_wait(const struct clk_branch
*br
, bool enabling
,
67 bool (check_halt
)(const struct clk_branch
*, bool))
69 bool voted
= br
->halt_check
& BRANCH_VOTED
;
70 const char *name
= clk_hw_get_name(&br
->clkr
.hw
);
73 * Skip checking halt bit if we're explicitly ignoring the bit or the
74 * clock is in hardware gated mode
76 if (br
->halt_check
== BRANCH_HALT_SKIP
|| clk_branch_in_hwcg_mode(br
))
79 if (br
->halt_check
== BRANCH_HALT_DELAY
|| (!enabling
&& voted
)) {
81 } else if (br
->halt_check
== BRANCH_HALT_ENABLE
||
82 br
->halt_check
== BRANCH_HALT
||
83 (enabling
&& voted
)) {
87 if (check_halt(br
, enabling
))
91 WARN(1, "%s status stuck at 'o%s'", name
,
92 enabling
? "ff" : "n");
98 static int clk_branch_toggle(struct clk_hw
*hw
, bool en
,
99 bool (check_halt
)(const struct clk_branch
*, bool))
101 struct clk_branch
*br
= to_clk_branch(hw
);
105 ret
= clk_enable_regmap(hw
);
109 clk_disable_regmap(hw
);
112 return clk_branch_wait(br
, en
, check_halt
);
115 static int clk_branch_enable(struct clk_hw
*hw
)
117 return clk_branch_toggle(hw
, true, clk_branch_check_halt
);
120 static void clk_branch_disable(struct clk_hw
*hw
)
122 clk_branch_toggle(hw
, false, clk_branch_check_halt
);
125 const struct clk_ops clk_branch_ops
= {
126 .enable
= clk_branch_enable
,
127 .disable
= clk_branch_disable
,
128 .is_enabled
= clk_is_enabled_regmap
,
130 EXPORT_SYMBOL_GPL(clk_branch_ops
);
132 static int clk_branch2_enable(struct clk_hw
*hw
)
134 return clk_branch_toggle(hw
, true, clk_branch2_check_halt
);
137 static void clk_branch2_disable(struct clk_hw
*hw
)
139 clk_branch_toggle(hw
, false, clk_branch2_check_halt
);
142 const struct clk_ops clk_branch2_ops
= {
143 .enable
= clk_branch2_enable
,
144 .disable
= clk_branch2_disable
,
145 .is_enabled
= clk_is_enabled_regmap
,
147 EXPORT_SYMBOL_GPL(clk_branch2_ops
);
149 const struct clk_ops clk_branch_simple_ops
= {
150 .enable
= clk_enable_regmap
,
151 .disable
= clk_disable_regmap
,
152 .is_enabled
= clk_is_enabled_regmap
,
154 EXPORT_SYMBOL_GPL(clk_branch_simple_ops
);