summaryrefslogtreecommitdiffstats
path: root/target/linux/sunxi/patches-3.12/110-clk-sunxi-fix-pll5-6.patch
blob: ad5935c89fbb365cb129ff94980d7b9beb692bad (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
From 11e7ff129807394d87c937b880bb58972dc91fc0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Emilio=20L=C3=B3pez?= <emilio@elopez.com.ar>
Date: Thu, 28 Nov 2013 09:00:47 -0300
Subject: [PATCH] fixup! clk: sunxi: add PLL5 and PLL6 support

---
 drivers/clk/sunxi/clk-sunxi.c | 83 +++++++++++++++++++++++++++++++++++--------
 1 file changed, 69 insertions(+), 14 deletions(-)

diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c
index d2b8d3c..3ce33b8 100644
--- a/drivers/clk/sunxi/clk-sunxi.c
+++ b/drivers/clk/sunxi/clk-sunxi.c
@@ -807,10 +807,11 @@ struct divs_data {
 		struct clk_div_table *table; /* is it a table based divisor? */
 		u8 shift; /* otherwise it's a normal divisor with this shift */
 		u8 pow;   /* is it power-of-two based? */
+		u8 gate;  /* is it independently gateable? */
 	} div[SUNXI_DIVS_MAX_QTY];
 };
 
-static struct clk_div_table pll6_sata_table[] = {
+static struct clk_div_table pll6_sata_tbl[] = {
 	{ .val = 0, .div = 6, },
 	{ .val = 1, .div = 12, },
 	{ .val = 2, .div = 18, },
@@ -829,7 +830,7 @@ struct divs_data {
 static const struct divs_data pll6_divs_data __initconst = {
 	.factors = &sun4i_pll5_data,
 	.div = {
-		{ .shift = 0, .table = pll6_sata_table }, /* M, SATA */
+		{ .shift = 0, .table = pll6_sata_tbl, .gate = 14 }, /* M, SATA */
 		{ .fixed = 2 }, /* P, other */
 	}
 };
@@ -852,6 +853,11 @@ static void __init sunxi_divs_clk_setup(struct device_node *node,
 	const char *parent  = node->name;
 	const char *clk_name;
 	struct clk **clks, *pclk;
+	struct clk_hw *gate_hw, *rate_hw;
+	const struct clk_ops *rate_ops;
+	struct clk_gate *gate = NULL;
+	struct clk_fixed_factor *fix_factor;
+	struct clk_divider *divider;
 	void *reg;
 	int i = 0;
 	int flags, clkflags;
@@ -866,10 +872,9 @@ static void __init sunxi_divs_clk_setup(struct device_node *node,
 		return;
 
 	clks = kzalloc(SUNXI_DIVS_MAX_QTY * sizeof(struct clk *), GFP_KERNEL);
-	if (!clks) {
-		kfree(clk_data);
-		return;
-	}
+	if (!clks)
+		goto free_clkdata;
+
 	clk_data->clks = clks;
 
 	/* It's not a good idea to have automatic reparenting changing
@@ -881,19 +886,60 @@ static void __init sunxi_divs_clk_setup(struct device_node *node,
 						  i, &clk_name) != 0)
 			break;
 
+		gate_hw = NULL;
+		rate_hw = NULL;
+		rate_ops = NULL;
+
+		/* If this leaf clock can be gated, create a gate */
+		if (data->div[i].gate) {
+			gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+			if (!gate)
+				goto free_clks;
+
+			gate->reg = reg;
+			gate->bit_idx = data->div[i].gate;
+			gate->lock = &clk_lock;
+
+			gate_hw = &gate->hw;
+		}
+
+		/* Leaves can be fixed or configurable divisors */
 		if (data->div[i].fixed) {
-			clks[i] = clk_register_fixed_factor(NULL, clk_name,
-						parent, clkflags,
-						1, data->div[i].fixed);
+			fix_factor = kzalloc(sizeof(*fix_factor), GFP_KERNEL);
+			if (!fix_factor)
+				goto free_gate;
+
+			fix_factor->mult = 1;
+			fix_factor->div = data->div[i].fixed;
+
+			rate_hw = &fix_factor->hw;
+			rate_ops = &clk_fixed_factor_ops;
 		} else {
+			divider = kzalloc(sizeof(*divider), GFP_KERNEL);
+			if (!divider)
+				goto free_gate;
+
 			flags = data->div[i].pow ? CLK_DIVIDER_POWER_OF_TWO : 0;
-			clks[i] = clk_register_divider_table(NULL, clk_name,
-						parent, clkflags, reg,
-						data->div[i].shift,
-						SUNXI_DIVISOR_WIDTH, flags,
-						data->div[i].table, &clk_lock);
+
+			divider->reg = reg;
+			divider->shift = data->div[i].shift;
+			divider->width = SUNXI_DIVISOR_WIDTH;
+			divider->flags = flags;
+			divider->lock = &clk_lock;
+			divider->table = data->div[i].table;
+
+			rate_hw = &divider->hw;
+			rate_ops = &clk_divider_ops;
 		}
 
+		/* Wrap the (potential) gate and the divisor on a composite
+		 * clock to unify them */
+		clks[i] = clk_register_composite(NULL, clk_name, &parent, 1,
+						 NULL, NULL,
+						 rate_hw, rate_ops,
+						 gate_hw, &clk_gate_ops,
+						 clkflags);
+
 		WARN_ON(IS_ERR(clk_data->clks[i]));
 		clk_register_clkdev(clks[i], clk_name, NULL);
 	}
@@ -905,6 +951,15 @@ static void __init sunxi_divs_clk_setup(struct device_node *node,
 	clk_data->clk_num = i;
 
 	of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+
+	return;
+
+free_gate:
+	kfree(gate);
+free_clks:
+	kfree(clks);
+free_clkdata:
+	kfree(clk_data);
 }
 
 
-- 
1.8.5.1