1/*
2 * Copyright (c) 2013 Samsung Electronics Co., Ltd.
3 * Copyright (c) 2013 Linaro Ltd.
4 * Author: Thomas Abraham <thomas.ab@samsung.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * This file includes utility functions to register clocks to common
11 * clock framework for Samsung platforms.
12*/
13
14#include <linux/of_address.h>
15#include <linux/syscore_ops.h>
16
17#include "clk.h"
18
19static LIST_HEAD(clock_reg_cache_list);
20
21void samsung_clk_save(void __iomem *base,
22				    struct samsung_clk_reg_dump *rd,
23				    unsigned int num_regs)
24{
25	for (; num_regs > 0; --num_regs, ++rd)
26		rd->value = readl(base + rd->offset);
27}
28
29void samsung_clk_restore(void __iomem *base,
30				      const struct samsung_clk_reg_dump *rd,
31				      unsigned int num_regs)
32{
33	for (; num_regs > 0; --num_regs, ++rd)
34		writel(rd->value, base + rd->offset);
35}
36
37struct samsung_clk_reg_dump *samsung_clk_alloc_reg_dump(
38						const unsigned long *rdump,
39						unsigned long nr_rdump)
40{
41	struct samsung_clk_reg_dump *rd;
42	unsigned int i;
43
44	rd = kcalloc(nr_rdump, sizeof(*rd), GFP_KERNEL);
45	if (!rd)
46		return NULL;
47
48	for (i = 0; i < nr_rdump; ++i)
49		rd[i].offset = rdump[i];
50
51	return rd;
52}
53
54/* setup the essentials required to support clock lookup using ccf */
55struct samsung_clk_provider *__init samsung_clk_init(struct device_node *np,
56			void __iomem *base, unsigned long nr_clks)
57{
58	struct samsung_clk_provider *ctx;
59	struct clk **clk_table;
60	int i;
61
62	ctx = kzalloc(sizeof(struct samsung_clk_provider), GFP_KERNEL);
63	if (!ctx)
64		panic("could not allocate clock provider context.\n");
65
66	clk_table = kcalloc(nr_clks, sizeof(struct clk *), GFP_KERNEL);
67	if (!clk_table)
68		panic("could not allocate clock lookup table\n");
69
70	for (i = 0; i < nr_clks; ++i)
71		clk_table[i] = ERR_PTR(-ENOENT);
72
73	ctx->reg_base = base;
74	ctx->clk_data.clks = clk_table;
75	ctx->clk_data.clk_num = nr_clks;
76	spin_lock_init(&ctx->lock);
77
78	return ctx;
79}
80
81void __init samsung_clk_of_add_provider(struct device_node *np,
82				struct samsung_clk_provider *ctx)
83{
84	if (np) {
85		if (of_clk_add_provider(np, of_clk_src_onecell_get,
86					&ctx->clk_data))
87			panic("could not register clk provider\n");
88	}
89}
90
91/* add a clock instance to the clock lookup table used for dt based lookup */
92void samsung_clk_add_lookup(struct samsung_clk_provider *ctx, struct clk *clk,
93				unsigned int id)
94{
95	if (ctx->clk_data.clks && id)
96		ctx->clk_data.clks[id] = clk;
97}
98
99/* register a list of aliases */
100void __init samsung_clk_register_alias(struct samsung_clk_provider *ctx,
101				struct samsung_clock_alias *list,
102				unsigned int nr_clk)
103{
104	struct clk *clk;
105	unsigned int idx, ret;
106
107	if (!ctx->clk_data.clks) {
108		pr_err("%s: clock table missing\n", __func__);
109		return;
110	}
111
112	for (idx = 0; idx < nr_clk; idx++, list++) {
113		if (!list->id) {
114			pr_err("%s: clock id missing for index %d\n", __func__,
115				idx);
116			continue;
117		}
118
119		clk = ctx->clk_data.clks[list->id];
120		if (!clk) {
121			pr_err("%s: failed to find clock %d\n", __func__,
122				list->id);
123			continue;
124		}
125
126		ret = clk_register_clkdev(clk, list->alias, list->dev_name);
127		if (ret)
128			pr_err("%s: failed to register lookup %s\n",
129					__func__, list->alias);
130	}
131}
132
133/* register a list of fixed clocks */
134void __init samsung_clk_register_fixed_rate(struct samsung_clk_provider *ctx,
135		struct samsung_fixed_rate_clock *list, unsigned int nr_clk)
136{
137	struct clk *clk;
138	unsigned int idx, ret;
139
140	for (idx = 0; idx < nr_clk; idx++, list++) {
141		clk = clk_register_fixed_rate(NULL, list->name,
142			list->parent_name, list->flags, list->fixed_rate);
143		if (IS_ERR(clk)) {
144			pr_err("%s: failed to register clock %s\n", __func__,
145				list->name);
146			continue;
147		}
148
149		samsung_clk_add_lookup(ctx, clk, list->id);
150
151		/*
152		 * Unconditionally add a clock lookup for the fixed rate clocks.
153		 * There are not many of these on any of Samsung platforms.
154		 */
155		ret = clk_register_clkdev(clk, list->name, NULL);
156		if (ret)
157			pr_err("%s: failed to register clock lookup for %s",
158				__func__, list->name);
159	}
160}
161
162/* register a list of fixed factor clocks */
163void __init samsung_clk_register_fixed_factor(struct samsung_clk_provider *ctx,
164		struct samsung_fixed_factor_clock *list, unsigned int nr_clk)
165{
166	struct clk *clk;
167	unsigned int idx;
168
169	for (idx = 0; idx < nr_clk; idx++, list++) {
170		clk = clk_register_fixed_factor(NULL, list->name,
171			list->parent_name, list->flags, list->mult, list->div);
172		if (IS_ERR(clk)) {
173			pr_err("%s: failed to register clock %s\n", __func__,
174				list->name);
175			continue;
176		}
177
178		samsung_clk_add_lookup(ctx, clk, list->id);
179	}
180}
181
182/* register a list of mux clocks */
183void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx,
184				struct samsung_mux_clock *list,
185				unsigned int nr_clk)
186{
187	struct clk *clk;
188	unsigned int idx, ret;
189
190	for (idx = 0; idx < nr_clk; idx++, list++) {
191		clk = clk_register_mux(NULL, list->name, list->parent_names,
192			list->num_parents, list->flags,
193			ctx->reg_base + list->offset,
194			list->shift, list->width, list->mux_flags, &ctx->lock);
195		if (IS_ERR(clk)) {
196			pr_err("%s: failed to register clock %s\n", __func__,
197				list->name);
198			continue;
199		}
200
201		samsung_clk_add_lookup(ctx, clk, list->id);
202
203		/* register a clock lookup only if a clock alias is specified */
204		if (list->alias) {
205			ret = clk_register_clkdev(clk, list->alias,
206						list->dev_name);
207			if (ret)
208				pr_err("%s: failed to register lookup %s\n",
209						__func__, list->alias);
210		}
211	}
212}
213
214/* register a list of div clocks */
215void __init samsung_clk_register_div(struct samsung_clk_provider *ctx,
216				struct samsung_div_clock *list,
217				unsigned int nr_clk)
218{
219	struct clk *clk;
220	unsigned int idx, ret;
221
222	for (idx = 0; idx < nr_clk; idx++, list++) {
223		if (list->table)
224			clk = clk_register_divider_table(NULL, list->name,
225				list->parent_name, list->flags,
226				ctx->reg_base + list->offset,
227				list->shift, list->width, list->div_flags,
228				list->table, &ctx->lock);
229		else
230			clk = clk_register_divider(NULL, list->name,
231				list->parent_name, list->flags,
232				ctx->reg_base + list->offset, list->shift,
233				list->width, list->div_flags, &ctx->lock);
234		if (IS_ERR(clk)) {
235			pr_err("%s: failed to register clock %s\n", __func__,
236				list->name);
237			continue;
238		}
239
240		samsung_clk_add_lookup(ctx, clk, list->id);
241
242		/* register a clock lookup only if a clock alias is specified */
243		if (list->alias) {
244			ret = clk_register_clkdev(clk, list->alias,
245						list->dev_name);
246			if (ret)
247				pr_err("%s: failed to register lookup %s\n",
248						__func__, list->alias);
249		}
250	}
251}
252
253/* register a list of gate clocks */
254void __init samsung_clk_register_gate(struct samsung_clk_provider *ctx,
255				struct samsung_gate_clock *list,
256				unsigned int nr_clk)
257{
258	struct clk *clk;
259	unsigned int idx, ret;
260
261	for (idx = 0; idx < nr_clk; idx++, list++) {
262		clk = clk_register_gate(NULL, list->name, list->parent_name,
263				list->flags, ctx->reg_base + list->offset,
264				list->bit_idx, list->gate_flags, &ctx->lock);
265		if (IS_ERR(clk)) {
266			pr_err("%s: failed to register clock %s\n", __func__,
267				list->name);
268			continue;
269		}
270
271		/* register a clock lookup only if a clock alias is specified */
272		if (list->alias) {
273			ret = clk_register_clkdev(clk, list->alias,
274							list->dev_name);
275			if (ret)
276				pr_err("%s: failed to register lookup %s\n",
277					__func__, list->alias);
278		}
279
280		samsung_clk_add_lookup(ctx, clk, list->id);
281	}
282}
283
284/*
285 * obtain the clock speed of all external fixed clock sources from device
286 * tree and register it
287 */
288void __init samsung_clk_of_register_fixed_ext(struct samsung_clk_provider *ctx,
289			struct samsung_fixed_rate_clock *fixed_rate_clk,
290			unsigned int nr_fixed_rate_clk,
291			const struct of_device_id *clk_matches)
292{
293	const struct of_device_id *match;
294	struct device_node *clk_np;
295	u32 freq;
296
297	for_each_matching_node_and_match(clk_np, clk_matches, &match) {
298		if (of_property_read_u32(clk_np, "clock-frequency", &freq))
299			continue;
300		fixed_rate_clk[(unsigned long)match->data].fixed_rate = freq;
301	}
302	samsung_clk_register_fixed_rate(ctx, fixed_rate_clk, nr_fixed_rate_clk);
303}
304
305/* utility function to get the rate of a specified clock */
306unsigned long _get_rate(const char *clk_name)
307{
308	struct clk *clk;
309
310	clk = __clk_lookup(clk_name);
311	if (!clk) {
312		pr_err("%s: could not find clock %s\n", __func__, clk_name);
313		return 0;
314	}
315
316	return clk_get_rate(clk);
317}
318
319#ifdef CONFIG_PM_SLEEP
320static int samsung_clk_suspend(void)
321{
322	struct samsung_clock_reg_cache *reg_cache;
323
324	list_for_each_entry(reg_cache, &clock_reg_cache_list, node)
325		samsung_clk_save(reg_cache->reg_base, reg_cache->rdump,
326				reg_cache->rd_num);
327	return 0;
328}
329
330static void samsung_clk_resume(void)
331{
332	struct samsung_clock_reg_cache *reg_cache;
333
334	list_for_each_entry(reg_cache, &clock_reg_cache_list, node)
335		samsung_clk_restore(reg_cache->reg_base, reg_cache->rdump,
336				reg_cache->rd_num);
337}
338
339static struct syscore_ops samsung_clk_syscore_ops = {
340	.suspend = samsung_clk_suspend,
341	.resume = samsung_clk_resume,
342};
343
344static void samsung_clk_sleep_init(void __iomem *reg_base,
345		const unsigned long *rdump,
346		unsigned long nr_rdump)
347{
348	struct samsung_clock_reg_cache *reg_cache;
349
350	reg_cache = kzalloc(sizeof(struct samsung_clock_reg_cache),
351			GFP_KERNEL);
352	if (!reg_cache)
353		panic("could not allocate register reg_cache.\n");
354	reg_cache->rdump = samsung_clk_alloc_reg_dump(rdump, nr_rdump);
355
356	if (!reg_cache->rdump)
357		panic("could not allocate register dump storage.\n");
358
359	if (list_empty(&clock_reg_cache_list))
360		register_syscore_ops(&samsung_clk_syscore_ops);
361
362	reg_cache->reg_base = reg_base;
363	reg_cache->rd_num = nr_rdump;
364	list_add_tail(&reg_cache->node, &clock_reg_cache_list);
365}
366
367#else
368static void samsung_clk_sleep_init(void __iomem *reg_base,
369		const unsigned long *rdump,
370		unsigned long nr_rdump) {}
371#endif
372
373/*
374 * Common function which registers plls, muxes, dividers and gates
375 * for each CMU. It also add CMU register list to register cache.
376 */
377struct samsung_clk_provider * __init samsung_cmu_register_one(
378			struct device_node *np,
379			struct samsung_cmu_info *cmu)
380{
381	void __iomem *reg_base;
382	struct samsung_clk_provider *ctx;
383
384	reg_base = of_iomap(np, 0);
385	if (!reg_base) {
386		panic("%s: failed to map registers\n", __func__);
387		return NULL;
388	}
389
390	ctx = samsung_clk_init(np, reg_base, cmu->nr_clk_ids);
391	if (!ctx) {
392		panic("%s: unable to alllocate ctx\n", __func__);
393		return ctx;
394	}
395
396	if (cmu->pll_clks)
397		samsung_clk_register_pll(ctx, cmu->pll_clks, cmu->nr_pll_clks,
398			reg_base);
399	if (cmu->mux_clks)
400		samsung_clk_register_mux(ctx, cmu->mux_clks,
401			cmu->nr_mux_clks);
402	if (cmu->div_clks)
403		samsung_clk_register_div(ctx, cmu->div_clks, cmu->nr_div_clks);
404	if (cmu->gate_clks)
405		samsung_clk_register_gate(ctx, cmu->gate_clks,
406			cmu->nr_gate_clks);
407	if (cmu->fixed_clks)
408		samsung_clk_register_fixed_rate(ctx, cmu->fixed_clks,
409			cmu->nr_fixed_clks);
410	if (cmu->fixed_factor_clks)
411		samsung_clk_register_fixed_factor(ctx, cmu->fixed_factor_clks,
412			cmu->nr_fixed_factor_clks);
413	if (cmu->clk_regs)
414		samsung_clk_sleep_init(reg_base, cmu->clk_regs,
415			cmu->nr_clk_regs);
416
417	samsung_clk_of_add_provider(np, ctx);
418
419	return ctx;
420}
421