Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (c) 2013, The Linux Foundation. All rights reserved.
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      * Skip checking halt bit if we're explicitly ignoring the bit or the
0074      * clock is in hardware gated mode
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);