xref: /openbmc/linux/drivers/clk/rockchip/clk.c (revision a245fecbb8064641d9cc317b347b5bdb2b7a4bb6)
1*a245fecbSHeiko Stübner /*
2*a245fecbSHeiko Stübner  * Copyright (c) 2014 MundoReader S.L.
3*a245fecbSHeiko Stübner  * Author: Heiko Stuebner <heiko@sntech.de>
4*a245fecbSHeiko Stübner  *
5*a245fecbSHeiko Stübner  * based on
6*a245fecbSHeiko Stübner  *
7*a245fecbSHeiko Stübner  * samsung/clk.c
8*a245fecbSHeiko Stübner  * Copyright (c) 2013 Samsung Electronics Co., Ltd.
9*a245fecbSHeiko Stübner  * Copyright (c) 2013 Linaro Ltd.
10*a245fecbSHeiko Stübner  * Author: Thomas Abraham <thomas.ab@samsung.com>
11*a245fecbSHeiko Stübner  *
12*a245fecbSHeiko Stübner  * This program is free software; you can redistribute it and/or modify
13*a245fecbSHeiko Stübner  * it under the terms of the GNU General Public License as published by
14*a245fecbSHeiko Stübner  * the Free Software Foundation; either version 2 of the License, or
15*a245fecbSHeiko Stübner  * (at your option) any later version.
16*a245fecbSHeiko Stübner  *
17*a245fecbSHeiko Stübner  * This program is distributed in the hope that it will be useful,
18*a245fecbSHeiko Stübner  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19*a245fecbSHeiko Stübner  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20*a245fecbSHeiko Stübner  * GNU General Public License for more details.
21*a245fecbSHeiko Stübner  */
22*a245fecbSHeiko Stübner 
23*a245fecbSHeiko Stübner #include <linux/slab.h>
24*a245fecbSHeiko Stübner #include <linux/clk.h>
25*a245fecbSHeiko Stübner #include <linux/clk-provider.h>
26*a245fecbSHeiko Stübner #include "clk.h"
27*a245fecbSHeiko Stübner 
28*a245fecbSHeiko Stübner /**
29*a245fecbSHeiko Stübner  * Register a clock branch.
30*a245fecbSHeiko Stübner  * Most clock branches have a form like
31*a245fecbSHeiko Stübner  *
32*a245fecbSHeiko Stübner  * src1 --|--\
33*a245fecbSHeiko Stübner  *        |M |--[GATE]-[DIV]-
34*a245fecbSHeiko Stübner  * src2 --|--/
35*a245fecbSHeiko Stübner  *
36*a245fecbSHeiko Stübner  * sometimes without one of those components.
37*a245fecbSHeiko Stübner  */
38*a245fecbSHeiko Stübner struct clk *rockchip_clk_register_branch(const char *name,
39*a245fecbSHeiko Stübner 		const char **parent_names, u8 num_parents, void __iomem *base,
40*a245fecbSHeiko Stübner 		int muxdiv_offset, u8 mux_shift, u8 mux_width, u8 mux_flags,
41*a245fecbSHeiko Stübner 		u8 div_shift, u8 div_width, u8 div_flags,
42*a245fecbSHeiko Stübner 		struct clk_div_table *div_table, int gate_offset,
43*a245fecbSHeiko Stübner 		u8 gate_shift, u8 gate_flags, unsigned long flags,
44*a245fecbSHeiko Stübner 		spinlock_t *lock)
45*a245fecbSHeiko Stübner {
46*a245fecbSHeiko Stübner 	struct clk *clk;
47*a245fecbSHeiko Stübner 	struct clk_mux *mux = NULL;
48*a245fecbSHeiko Stübner 	struct clk_gate *gate = NULL;
49*a245fecbSHeiko Stübner 	struct clk_divider *div = NULL;
50*a245fecbSHeiko Stübner 	const struct clk_ops *mux_ops = NULL, *div_ops = NULL,
51*a245fecbSHeiko Stübner 			     *gate_ops = NULL;
52*a245fecbSHeiko Stübner 
53*a245fecbSHeiko Stübner 	if (num_parents > 1) {
54*a245fecbSHeiko Stübner 		mux = kzalloc(sizeof(*mux), GFP_KERNEL);
55*a245fecbSHeiko Stübner 		if (!mux)
56*a245fecbSHeiko Stübner 			return ERR_PTR(-ENOMEM);
57*a245fecbSHeiko Stübner 
58*a245fecbSHeiko Stübner 		mux->reg = base + muxdiv_offset;
59*a245fecbSHeiko Stübner 		mux->shift = mux_shift;
60*a245fecbSHeiko Stübner 		mux->mask = BIT(mux_width) - 1;
61*a245fecbSHeiko Stübner 		mux->flags = mux_flags;
62*a245fecbSHeiko Stübner 		mux->lock = lock;
63*a245fecbSHeiko Stübner 		mux_ops = (mux_flags & CLK_MUX_READ_ONLY) ? &clk_mux_ro_ops
64*a245fecbSHeiko Stübner 							: &clk_mux_ops;
65*a245fecbSHeiko Stübner 	}
66*a245fecbSHeiko Stübner 
67*a245fecbSHeiko Stübner 	if (gate_offset >= 0) {
68*a245fecbSHeiko Stübner 		gate = kzalloc(sizeof(*gate), GFP_KERNEL);
69*a245fecbSHeiko Stübner 		if (!gate)
70*a245fecbSHeiko Stübner 			return ERR_PTR(-ENOMEM);
71*a245fecbSHeiko Stübner 
72*a245fecbSHeiko Stübner 		gate->flags = gate_flags;
73*a245fecbSHeiko Stübner 		gate->reg = base + gate_offset;
74*a245fecbSHeiko Stübner 		gate->bit_idx = gate_shift;
75*a245fecbSHeiko Stübner 		gate->lock = lock;
76*a245fecbSHeiko Stübner 		gate_ops = &clk_gate_ops;
77*a245fecbSHeiko Stübner 	}
78*a245fecbSHeiko Stübner 
79*a245fecbSHeiko Stübner 	if (div_width > 0) {
80*a245fecbSHeiko Stübner 		div = kzalloc(sizeof(*div), GFP_KERNEL);
81*a245fecbSHeiko Stübner 		if (!div)
82*a245fecbSHeiko Stübner 			return ERR_PTR(-ENOMEM);
83*a245fecbSHeiko Stübner 
84*a245fecbSHeiko Stübner 		div->flags = div_flags;
85*a245fecbSHeiko Stübner 		div->reg = base + muxdiv_offset;
86*a245fecbSHeiko Stübner 		div->shift = div_shift;
87*a245fecbSHeiko Stübner 		div->width = div_width;
88*a245fecbSHeiko Stübner 		div->lock = lock;
89*a245fecbSHeiko Stübner 		div->table = div_table;
90*a245fecbSHeiko Stübner 		div_ops = (div_flags & CLK_DIVIDER_READ_ONLY)
91*a245fecbSHeiko Stübner 						? &clk_divider_ro_ops
92*a245fecbSHeiko Stübner 						: &clk_divider_ops;
93*a245fecbSHeiko Stübner 	}
94*a245fecbSHeiko Stübner 
95*a245fecbSHeiko Stübner 	clk = clk_register_composite(NULL, name, parent_names, num_parents,
96*a245fecbSHeiko Stübner 				     mux ? &mux->hw : NULL, mux_ops,
97*a245fecbSHeiko Stübner 				     div ? &div->hw : NULL, div_ops,
98*a245fecbSHeiko Stübner 				     gate ? &gate->hw : NULL, gate_ops,
99*a245fecbSHeiko Stübner 				     flags);
100*a245fecbSHeiko Stübner 
101*a245fecbSHeiko Stübner 	return clk;
102*a245fecbSHeiko Stübner }
103*a245fecbSHeiko Stübner 
104*a245fecbSHeiko Stübner static DEFINE_SPINLOCK(clk_lock);
105*a245fecbSHeiko Stübner static struct clk **clk_table;
106*a245fecbSHeiko Stübner static void __iomem *reg_base;
107*a245fecbSHeiko Stübner static struct clk_onecell_data clk_data;
108*a245fecbSHeiko Stübner 
109*a245fecbSHeiko Stübner void __init rockchip_clk_init(struct device_node *np, void __iomem *base,
110*a245fecbSHeiko Stübner 			      unsigned long nr_clks)
111*a245fecbSHeiko Stübner {
112*a245fecbSHeiko Stübner 	reg_base = base;
113*a245fecbSHeiko Stübner 
114*a245fecbSHeiko Stübner 	clk_table = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL);
115*a245fecbSHeiko Stübner 	if (!clk_table)
116*a245fecbSHeiko Stübner 		pr_err("%s: could not allocate clock lookup table\n", __func__);
117*a245fecbSHeiko Stübner 
118*a245fecbSHeiko Stübner 	clk_data.clks = clk_table;
119*a245fecbSHeiko Stübner 	clk_data.clk_num = nr_clks;
120*a245fecbSHeiko Stübner 	of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
121*a245fecbSHeiko Stübner }
122*a245fecbSHeiko Stübner 
123*a245fecbSHeiko Stübner void rockchip_clk_add_lookup(struct clk *clk, unsigned int id)
124*a245fecbSHeiko Stübner {
125*a245fecbSHeiko Stübner 	if (clk_table && id)
126*a245fecbSHeiko Stübner 		clk_table[id] = clk;
127*a245fecbSHeiko Stübner }
128*a245fecbSHeiko Stübner 
129*a245fecbSHeiko Stübner void __init rockchip_clk_register_branches(
130*a245fecbSHeiko Stübner 				      struct rockchip_clk_branch *list,
131*a245fecbSHeiko Stübner 				      unsigned int nr_clk)
132*a245fecbSHeiko Stübner {
133*a245fecbSHeiko Stübner 	struct clk *clk = NULL;
134*a245fecbSHeiko Stübner 	unsigned int idx;
135*a245fecbSHeiko Stübner 	unsigned long flags;
136*a245fecbSHeiko Stübner 
137*a245fecbSHeiko Stübner 	for (idx = 0; idx < nr_clk; idx++, list++) {
138*a245fecbSHeiko Stübner 		flags = list->flags;
139*a245fecbSHeiko Stübner 
140*a245fecbSHeiko Stübner 		/* catch simple muxes */
141*a245fecbSHeiko Stübner 		switch (list->branch_type) {
142*a245fecbSHeiko Stübner 		case branch_mux:
143*a245fecbSHeiko Stübner 			clk = clk_register_mux(NULL, list->name,
144*a245fecbSHeiko Stübner 				list->parent_names, list->num_parents,
145*a245fecbSHeiko Stübner 				flags, reg_base + list->muxdiv_offset,
146*a245fecbSHeiko Stübner 				list->mux_shift, list->mux_width,
147*a245fecbSHeiko Stübner 				list->mux_flags, &clk_lock);
148*a245fecbSHeiko Stübner 			break;
149*a245fecbSHeiko Stübner 		case branch_divider:
150*a245fecbSHeiko Stübner 			if (list->div_table)
151*a245fecbSHeiko Stübner 				clk = clk_register_divider_table(NULL,
152*a245fecbSHeiko Stübner 					list->name, list->parent_names[0],
153*a245fecbSHeiko Stübner 					flags, reg_base + list->muxdiv_offset,
154*a245fecbSHeiko Stübner 					list->div_shift, list->div_width,
155*a245fecbSHeiko Stübner 					list->div_flags, list->div_table,
156*a245fecbSHeiko Stübner 					&clk_lock);
157*a245fecbSHeiko Stübner 			else
158*a245fecbSHeiko Stübner 				clk = clk_register_divider(NULL, list->name,
159*a245fecbSHeiko Stübner 					list->parent_names[0], flags,
160*a245fecbSHeiko Stübner 					reg_base + list->muxdiv_offset,
161*a245fecbSHeiko Stübner 					list->div_shift, list->div_width,
162*a245fecbSHeiko Stübner 					list->div_flags, &clk_lock);
163*a245fecbSHeiko Stübner 			break;
164*a245fecbSHeiko Stübner 		case branch_fraction_divider:
165*a245fecbSHeiko Stübner 			/* unimplemented */
166*a245fecbSHeiko Stübner 			continue;
167*a245fecbSHeiko Stübner 			break;
168*a245fecbSHeiko Stübner 		case branch_gate:
169*a245fecbSHeiko Stübner 			flags |= CLK_SET_RATE_PARENT;
170*a245fecbSHeiko Stübner 
171*a245fecbSHeiko Stübner 			/* keep all gates untouched for now */
172*a245fecbSHeiko Stübner 			flags |= CLK_IGNORE_UNUSED;
173*a245fecbSHeiko Stübner 
174*a245fecbSHeiko Stübner 			clk = clk_register_gate(NULL, list->name,
175*a245fecbSHeiko Stübner 				list->parent_names[0], flags,
176*a245fecbSHeiko Stübner 				reg_base + list->gate_offset,
177*a245fecbSHeiko Stübner 				list->gate_shift, list->gate_flags, &clk_lock);
178*a245fecbSHeiko Stübner 			break;
179*a245fecbSHeiko Stübner 		case branch_composite:
180*a245fecbSHeiko Stübner 			/* keep all gates untouched for now */
181*a245fecbSHeiko Stübner 			flags |= CLK_IGNORE_UNUSED;
182*a245fecbSHeiko Stübner 
183*a245fecbSHeiko Stübner 			clk = rockchip_clk_register_branch(list->name,
184*a245fecbSHeiko Stübner 				list->parent_names, list->num_parents,
185*a245fecbSHeiko Stübner 				reg_base, list->muxdiv_offset, list->mux_shift,
186*a245fecbSHeiko Stübner 				list->mux_width, list->mux_flags,
187*a245fecbSHeiko Stübner 				list->div_shift, list->div_width,
188*a245fecbSHeiko Stübner 				list->div_flags, list->div_table,
189*a245fecbSHeiko Stübner 				list->gate_offset, list->gate_shift,
190*a245fecbSHeiko Stübner 				list->gate_flags, flags, &clk_lock);
191*a245fecbSHeiko Stübner 			break;
192*a245fecbSHeiko Stübner 		}
193*a245fecbSHeiko Stübner 
194*a245fecbSHeiko Stübner 		/* none of the cases above matched */
195*a245fecbSHeiko Stübner 		if (!clk) {
196*a245fecbSHeiko Stübner 			pr_err("%s: unknown clock type %d\n",
197*a245fecbSHeiko Stübner 			       __func__, list->branch_type);
198*a245fecbSHeiko Stübner 			continue;
199*a245fecbSHeiko Stübner 		}
200*a245fecbSHeiko Stübner 
201*a245fecbSHeiko Stübner 		if (IS_ERR(clk)) {
202*a245fecbSHeiko Stübner 			pr_err("%s: failed to register clock %s: %ld\n",
203*a245fecbSHeiko Stübner 			       __func__, list->name, PTR_ERR(clk));
204*a245fecbSHeiko Stübner 			continue;
205*a245fecbSHeiko Stübner 		}
206*a245fecbSHeiko Stübner 
207*a245fecbSHeiko Stübner 		rockchip_clk_add_lookup(clk, list->id);
208*a245fecbSHeiko Stübner 	}
209*a245fecbSHeiko Stübner }
210