0001
0002
0003
0004
0005
0006 #include <linux/kernel.h>
0007 #include <linux/bitops.h>
0008 #include <linux/err.h>
0009 #include <linux/delay.h>
0010 #include <linux/export.h>
0011 #include <linux/clk-provider.h>
0012 #include <linux/regmap.h>
0013
0014 #include "clk-branch.h"
0015
0016 static bool clk_branch_in_hwcg_mode(const struct clk_branch *br)
0017 {
0018 u32 val;
0019
0020 if (!br->hwcg_reg)
0021 return false;
0022
0023 regmap_read(br->clkr.regmap, br->hwcg_reg, &val);
0024
0025 return !!(val & BIT(br->hwcg_bit));
0026 }
0027
0028 static bool clk_branch_check_halt(const struct clk_branch *br, bool enabling)
0029 {
0030 bool invert = (br->halt_check == BRANCH_HALT_ENABLE);
0031 u32 val;
0032
0033 regmap_read(br->clkr.regmap, br->halt_reg, &val);
0034
0035 val &= BIT(br->halt_bit);
0036 if (invert)
0037 val = !val;
0038
0039 return !!val == !enabling;
0040 }
0041
0042 #define BRANCH_CLK_OFF BIT(31)
0043 #define BRANCH_NOC_FSM_STATUS_SHIFT 28
0044 #define BRANCH_NOC_FSM_STATUS_MASK 0x7
0045 #define BRANCH_NOC_FSM_STATUS_ON (0x2 << BRANCH_NOC_FSM_STATUS_SHIFT)
0046
0047 static bool clk_branch2_check_halt(const struct clk_branch *br, bool enabling)
0048 {
0049 u32 val;
0050 u32 mask;
0051
0052 mask = BRANCH_NOC_FSM_STATUS_MASK << BRANCH_NOC_FSM_STATUS_SHIFT;
0053 mask |= BRANCH_CLK_OFF;
0054
0055 regmap_read(br->clkr.regmap, br->halt_reg, &val);
0056
0057 if (enabling) {
0058 val &= mask;
0059 return (val & BRANCH_CLK_OFF) == 0 ||
0060 val == BRANCH_NOC_FSM_STATUS_ON;
0061 } else {
0062 return val & BRANCH_CLK_OFF;
0063 }
0064 }
0065
0066 static int clk_branch_wait(const struct clk_branch *br, bool enabling,
0067 bool (check_halt)(const struct clk_branch *, bool))
0068 {
0069 bool voted = br->halt_check & BRANCH_VOTED;
0070 const char *name = clk_hw_get_name(&br->clkr.hw);
0071
0072
0073
0074
0075
0076 if (br->halt_check == BRANCH_HALT_SKIP || clk_branch_in_hwcg_mode(br))
0077 return 0;
0078
0079 if (br->halt_check == BRANCH_HALT_DELAY || (!enabling && voted)) {
0080 udelay(10);
0081 } else if (br->halt_check == BRANCH_HALT_ENABLE ||
0082 br->halt_check == BRANCH_HALT ||
0083 (enabling && voted)) {
0084 int count = 200;
0085
0086 while (count-- > 0) {
0087 if (check_halt(br, enabling))
0088 return 0;
0089 udelay(1);
0090 }
0091 WARN(1, "%s status stuck at 'o%s'", name,
0092 enabling ? "ff" : "n");
0093 return -EBUSY;
0094 }
0095 return 0;
0096 }
0097
0098 static int clk_branch_toggle(struct clk_hw *hw, bool en,
0099 bool (check_halt)(const struct clk_branch *, bool))
0100 {
0101 struct clk_branch *br = to_clk_branch(hw);
0102 int ret;
0103
0104 if (en) {
0105 ret = clk_enable_regmap(hw);
0106 if (ret)
0107 return ret;
0108 } else {
0109 clk_disable_regmap(hw);
0110 }
0111
0112 return clk_branch_wait(br, en, check_halt);
0113 }
0114
0115 static int clk_branch_enable(struct clk_hw *hw)
0116 {
0117 return clk_branch_toggle(hw, true, clk_branch_check_halt);
0118 }
0119
0120 static void clk_branch_disable(struct clk_hw *hw)
0121 {
0122 clk_branch_toggle(hw, false, clk_branch_check_halt);
0123 }
0124
0125 const struct clk_ops clk_branch_ops = {
0126 .enable = clk_branch_enable,
0127 .disable = clk_branch_disable,
0128 .is_enabled = clk_is_enabled_regmap,
0129 };
0130 EXPORT_SYMBOL_GPL(clk_branch_ops);
0131
0132 static int clk_branch2_enable(struct clk_hw *hw)
0133 {
0134 return clk_branch_toggle(hw, true, clk_branch2_check_halt);
0135 }
0136
0137 static void clk_branch2_disable(struct clk_hw *hw)
0138 {
0139 clk_branch_toggle(hw, false, clk_branch2_check_halt);
0140 }
0141
0142 const struct clk_ops clk_branch2_ops = {
0143 .enable = clk_branch2_enable,
0144 .disable = clk_branch2_disable,
0145 .is_enabled = clk_is_enabled_regmap,
0146 };
0147 EXPORT_SYMBOL_GPL(clk_branch2_ops);
0148
0149 const struct clk_ops clk_branch2_aon_ops = {
0150 .enable = clk_branch2_enable,
0151 .is_enabled = clk_is_enabled_regmap,
0152 };
0153 EXPORT_SYMBOL_GPL(clk_branch2_aon_ops);
0154
0155 const struct clk_ops clk_branch_simple_ops = {
0156 .enable = clk_enable_regmap,
0157 .disable = clk_disable_regmap,
0158 .is_enabled = clk_is_enabled_regmap,
0159 };
0160 EXPORT_SYMBOL_GPL(clk_branch_simple_ops);