xref: /linux/drivers/clk/sunxi-ng/ccu_gate.c (revision eceb995e04b74204c73f9dd0ccb19061d5082063)
1 /*
2  * Copyright (C) 2016 Maxime Ripard
3  * Maxime Ripard <maxime.ripard@free-electrons.com>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of
8  * the License, or (at your option) any later version.
9  */
10 
11 #include <linux/clk-provider.h>
12 #include <linux/io.h>
13 
14 #include "ccu_gate.h"
15 
16 void ccu_gate_helper_disable(struct ccu_common *common, u32 gate)
17 {
18 	unsigned long flags;
19 	u32 reg;
20 
21 	if (!gate)
22 		return;
23 
24 	spin_lock_irqsave(common->lock, flags);
25 
26 	reg = readl(common->base + common->reg);
27 	writel(reg & ~gate, common->base + common->reg);
28 
29 	spin_unlock_irqrestore(common->lock, flags);
30 }
31 
32 static void ccu_gate_disable(struct clk_hw *hw)
33 {
34 	struct ccu_gate *cg = hw_to_ccu_gate(hw);
35 
36 	return ccu_gate_helper_disable(&cg->common, cg->enable);
37 }
38 
39 int ccu_gate_helper_enable(struct ccu_common *common, u32 gate)
40 {
41 	unsigned long flags;
42 	u32 reg;
43 
44 	if (!gate)
45 		return 0;
46 
47 	spin_lock_irqsave(common->lock, flags);
48 
49 	reg = readl(common->base + common->reg);
50 	writel(reg | gate, common->base + common->reg);
51 
52 	spin_unlock_irqrestore(common->lock, flags);
53 
54 	return 0;
55 }
56 
57 static int ccu_gate_enable(struct clk_hw *hw)
58 {
59 	struct ccu_gate *cg = hw_to_ccu_gate(hw);
60 
61 	return ccu_gate_helper_enable(&cg->common, cg->enable);
62 }
63 
64 int ccu_gate_helper_is_enabled(struct ccu_common *common, u32 gate)
65 {
66 	if (!gate)
67 		return 1;
68 
69 	return readl(common->base + common->reg) & gate;
70 }
71 
72 static int ccu_gate_is_enabled(struct clk_hw *hw)
73 {
74 	struct ccu_gate *cg = hw_to_ccu_gate(hw);
75 
76 	return ccu_gate_helper_is_enabled(&cg->common, cg->enable);
77 }
78 
79 static unsigned long ccu_gate_recalc_rate(struct clk_hw *hw,
80 					  unsigned long parent_rate)
81 {
82 	struct ccu_gate *cg = hw_to_ccu_gate(hw);
83 	unsigned long rate = parent_rate;
84 
85 	if (cg->common.features & CCU_FEATURE_ALL_PREDIV)
86 		rate /= cg->common.prediv;
87 
88 	return rate;
89 }
90 
91 static long ccu_gate_round_rate(struct clk_hw *hw, unsigned long rate,
92 				unsigned long *prate)
93 {
94 	struct ccu_gate *cg = hw_to_ccu_gate(hw);
95 	int div = 1;
96 
97 	if (cg->common.features & CCU_FEATURE_ALL_PREDIV)
98 		div = cg->common.prediv;
99 
100 	if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) {
101 		unsigned long best_parent = rate;
102 
103 		if (cg->common.features & CCU_FEATURE_ALL_PREDIV)
104 			best_parent *= div;
105 		*prate = clk_hw_round_rate(clk_hw_get_parent(hw), best_parent);
106 	}
107 
108 	return *prate / div;
109 }
110 
111 static int ccu_gate_set_rate(struct clk_hw *hw, unsigned long rate,
112 			     unsigned long parent_rate)
113 {
114 	/*
115 	 * We must report success but we can do so unconditionally because
116 	 * clk_factor_round_rate returns values that ensure this call is a
117 	 * nop.
118 	 */
119 
120 	return 0;
121 }
122 
123 const struct clk_ops ccu_gate_ops = {
124 	.disable	= ccu_gate_disable,
125 	.enable		= ccu_gate_enable,
126 	.is_enabled	= ccu_gate_is_enabled,
127 	.round_rate	= ccu_gate_round_rate,
128 	.set_rate	= ccu_gate_set_rate,
129 	.recalc_rate	= ccu_gate_recalc_rate,
130 };
131