root/drivers/clk/qcom/clk-branch.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. clk_branch_in_hwcg_mode
  2. clk_branch_check_halt
  3. clk_branch2_check_halt
  4. clk_branch_wait
  5. clk_branch_toggle
  6. clk_branch_enable
  7. clk_branch_disable
  8. clk_branch2_enable
  9. clk_branch2_disable

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * Copyright (c) 2013, The Linux Foundation. All rights reserved.
   4  */
   5 
   6 #include <linux/kernel.h>
   7 #include <linux/bitops.h>
   8 #include <linux/err.h>
   9 #include <linux/delay.h>
  10 #include <linux/export.h>
  11 #include <linux/clk-provider.h>
  12 #include <linux/regmap.h>
  13 
  14 #include "clk-branch.h"
  15 
  16 static bool clk_branch_in_hwcg_mode(const struct clk_branch *br)
  17 {
  18         u32 val;
  19 
  20         if (!br->hwcg_reg)
  21                 return false;
  22 
  23         regmap_read(br->clkr.regmap, br->hwcg_reg, &val);
  24 
  25         return !!(val & BIT(br->hwcg_bit));
  26 }
  27 
  28 static bool clk_branch_check_halt(const struct clk_branch *br, bool enabling)
  29 {
  30         bool invert = (br->halt_check == BRANCH_HALT_ENABLE);
  31         u32 val;
  32 
  33         regmap_read(br->clkr.regmap, br->halt_reg, &val);
  34 
  35         val &= BIT(br->halt_bit);
  36         if (invert)
  37                 val = !val;
  38 
  39         return !!val == !enabling;
  40 }
  41 
  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)
  46 
  47 static bool clk_branch2_check_halt(const struct clk_branch *br, bool enabling)
  48 {
  49         u32 val;
  50         u32 mask;
  51 
  52         mask = BRANCH_NOC_FSM_STATUS_MASK << BRANCH_NOC_FSM_STATUS_SHIFT;
  53         mask |= BRANCH_CLK_OFF;
  54 
  55         regmap_read(br->clkr.regmap, br->halt_reg, &val);
  56 
  57         if (enabling) {
  58                 val &= mask;
  59                 return (val & BRANCH_CLK_OFF) == 0 ||
  60                         val == BRANCH_NOC_FSM_STATUS_ON;
  61         } else {
  62                 return val & BRANCH_CLK_OFF;
  63         }
  64 }
  65 
  66 static int clk_branch_wait(const struct clk_branch *br, bool enabling,
  67                 bool (check_halt)(const struct clk_branch *, bool))
  68 {
  69         bool voted = br->halt_check & BRANCH_VOTED;
  70         const char *name = clk_hw_get_name(&br->clkr.hw);
  71 
  72         /*
  73          * Skip checking halt bit if we're explicitly ignoring the bit or the
  74          * clock is in hardware gated mode
  75          */
  76         if (br->halt_check == BRANCH_HALT_SKIP || clk_branch_in_hwcg_mode(br))
  77                 return 0;
  78 
  79         if (br->halt_check == BRANCH_HALT_DELAY || (!enabling && voted)) {
  80                 udelay(10);
  81         } else if (br->halt_check == BRANCH_HALT_ENABLE ||
  82                    br->halt_check == BRANCH_HALT ||
  83                    (enabling && voted)) {
  84                 int count = 200;
  85 
  86                 while (count-- > 0) {
  87                         if (check_halt(br, enabling))
  88                                 return 0;
  89                         udelay(1);
  90                 }
  91                 WARN(1, "%s status stuck at 'o%s'", name,
  92                                 enabling ? "ff" : "n");
  93                 return -EBUSY;
  94         }
  95         return 0;
  96 }
  97 
  98 static int clk_branch_toggle(struct clk_hw *hw, bool en,
  99                 bool (check_halt)(const struct clk_branch *, bool))
 100 {
 101         struct clk_branch *br = to_clk_branch(hw);
 102         int ret;
 103 
 104         if (en) {
 105                 ret = clk_enable_regmap(hw);
 106                 if (ret)
 107                         return ret;
 108         } else {
 109                 clk_disable_regmap(hw);
 110         }
 111 
 112         return clk_branch_wait(br, en, check_halt);
 113 }
 114 
 115 static int clk_branch_enable(struct clk_hw *hw)
 116 {
 117         return clk_branch_toggle(hw, true, clk_branch_check_halt);
 118 }
 119 
 120 static void clk_branch_disable(struct clk_hw *hw)
 121 {
 122         clk_branch_toggle(hw, false, clk_branch_check_halt);
 123 }
 124 
 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,
 129 };
 130 EXPORT_SYMBOL_GPL(clk_branch_ops);
 131 
 132 static int clk_branch2_enable(struct clk_hw *hw)
 133 {
 134         return clk_branch_toggle(hw, true, clk_branch2_check_halt);
 135 }
 136 
 137 static void clk_branch2_disable(struct clk_hw *hw)
 138 {
 139         clk_branch_toggle(hw, false, clk_branch2_check_halt);
 140 }
 141 
 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,
 146 };
 147 EXPORT_SYMBOL_GPL(clk_branch2_ops);
 148 
 149 const struct clk_ops clk_branch2_aon_ops = {
 150         .enable = clk_branch2_enable,
 151         .is_enabled = clk_is_enabled_regmap,
 152 };
 153 EXPORT_SYMBOL_GPL(clk_branch2_aon_ops);
 154 
 155 const struct clk_ops clk_branch_simple_ops = {
 156         .enable = clk_enable_regmap,
 157         .disable = clk_disable_regmap,
 158         .is_enabled = clk_is_enabled_regmap,
 159 };
 160 EXPORT_SYMBOL_GPL(clk_branch_simple_ops);

/* [<][>][^][v][top][bottom][index][help] */