1 // SPDX-License-Identifier: GPL-2.0 2 // 3 // Spreadtrum gate clock driver 4 // 5 // Copyright (C) 2017 Spreadtrum, Inc. 6 // Author: Chunyan Zhang <chunyan.zhang@spreadtrum.com> 7 8 #include <linux/clk-provider.h> 9 #include <linux/regmap.h> 10 11 #include "gate.h" 12 13 static void clk_gate_toggle(const struct sprd_gate *sg, bool en) 14 { 15 const struct sprd_clk_common *common = &sg->common; 16 unsigned int reg; 17 bool set = sg->flags & CLK_GATE_SET_TO_DISABLE ? true : false; 18 19 set ^= en; 20 21 regmap_read(common->regmap, common->reg, ®); 22 23 if (set) 24 reg |= sg->enable_mask; 25 else 26 reg &= ~sg->enable_mask; 27 28 regmap_write(common->regmap, common->reg, reg); 29 } 30 31 static void clk_sc_gate_toggle(const struct sprd_gate *sg, bool en) 32 { 33 const struct sprd_clk_common *common = &sg->common; 34 bool set = sg->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0; 35 unsigned int offset; 36 37 set ^= en; 38 39 /* 40 * Each set/clear gate clock has three registers: 41 * common->reg - base register 42 * common->reg + offset - set register 43 * common->reg + 2 * offset - clear register 44 */ 45 offset = set ? sg->sc_offset : sg->sc_offset * 2; 46 47 regmap_write(common->regmap, common->reg + offset, 48 sg->enable_mask); 49 } 50 51 static void sprd_gate_disable(struct clk_hw *hw) 52 { 53 struct sprd_gate *sg = hw_to_sprd_gate(hw); 54 55 clk_gate_toggle(sg, false); 56 } 57 58 static int sprd_gate_enable(struct clk_hw *hw) 59 { 60 struct sprd_gate *sg = hw_to_sprd_gate(hw); 61 62 clk_gate_toggle(sg, true); 63 64 return 0; 65 } 66 67 static void sprd_sc_gate_disable(struct clk_hw *hw) 68 { 69 struct sprd_gate *sg = hw_to_sprd_gate(hw); 70 71 clk_sc_gate_toggle(sg, false); 72 } 73 74 static int sprd_sc_gate_enable(struct clk_hw *hw) 75 { 76 struct sprd_gate *sg = hw_to_sprd_gate(hw); 77 78 clk_sc_gate_toggle(sg, true); 79 80 return 0; 81 } 82 83 static int sprd_pll_sc_gate_prepare(struct clk_hw *hw) 84 { 85 struct sprd_gate *sg = hw_to_sprd_gate(hw); 86 87 clk_sc_gate_toggle(sg, true); 88 udelay(sg->udelay); 89 90 return 0; 91 } 92 93 static int sprd_gate_is_enabled(struct clk_hw *hw) 94 { 95 struct sprd_gate *sg = hw_to_sprd_gate(hw); 96 struct sprd_clk_common *common = &sg->common; 97 struct clk_hw *parent; 98 unsigned int reg; 99 100 if (sg->flags & SPRD_GATE_NON_AON) { 101 parent = clk_hw_get_parent(hw); 102 if (!parent || !clk_hw_is_enabled(parent)) 103 return 0; 104 } 105 106 regmap_read(common->regmap, common->reg, ®); 107 108 if (sg->flags & CLK_GATE_SET_TO_DISABLE) 109 reg ^= sg->enable_mask; 110 111 reg &= sg->enable_mask; 112 113 return reg ? 1 : 0; 114 } 115 116 const struct clk_ops sprd_gate_ops = { 117 .disable = sprd_gate_disable, 118 .enable = sprd_gate_enable, 119 .is_enabled = sprd_gate_is_enabled, 120 }; 121 EXPORT_SYMBOL_GPL(sprd_gate_ops); 122 123 const struct clk_ops sprd_sc_gate_ops = { 124 .disable = sprd_sc_gate_disable, 125 .enable = sprd_sc_gate_enable, 126 .is_enabled = sprd_gate_is_enabled, 127 }; 128 EXPORT_SYMBOL_GPL(sprd_sc_gate_ops); 129 130 const struct clk_ops sprd_pll_sc_gate_ops = { 131 .unprepare = sprd_sc_gate_disable, 132 .prepare = sprd_pll_sc_gate_prepare, 133 .is_enabled = sprd_gate_is_enabled, 134 }; 135 EXPORT_SYMBOL_GPL(sprd_pll_sc_gate_ops); 136