1/*
2 * Copyright 2012 Freescale Semiconductor, Inc.
3 *
4 * The code contained herein is licensed under the GNU General Public
5 * License. You may obtain a copy of the GNU General Public License
6 * Version 2 or later at the following locations:
7 *
8 * http://www.opensource.org/licenses/gpl-license.html
9 * http://www.gnu.org/copyleft/gpl.html
10 */
11
12#include <linux/clk.h>
13#include <linux/clk-provider.h>
14#include <linux/err.h>
15#include <linux/io.h>
16#include <linux/slab.h>
17#include "clk.h"
18
19/**
20 * struct clk_frac - mxs fractional divider clock
21 * @hw: clk_hw for the fractional divider clock
22 * @reg: register address
23 * @shift: the divider bit shift
24 * @width: the divider bit width
25 * @busy: busy bit shift
26 *
27 * The clock is an adjustable fractional divider with a busy bit to wait
28 * when the divider is adjusted.
29 */
30struct clk_frac {
31	struct clk_hw hw;
32	void __iomem *reg;
33	u8 shift;
34	u8 width;
35	u8 busy;
36};
37
38#define to_clk_frac(_hw) container_of(_hw, struct clk_frac, hw)
39
40static unsigned long clk_frac_recalc_rate(struct clk_hw *hw,
41					  unsigned long parent_rate)
42{
43	struct clk_frac *frac = to_clk_frac(hw);
44	u32 div;
45
46	div = readl_relaxed(frac->reg) >> frac->shift;
47	div &= (1 << frac->width) - 1;
48
49	return (parent_rate >> frac->width) * div;
50}
51
52static long clk_frac_round_rate(struct clk_hw *hw, unsigned long rate,
53				unsigned long *prate)
54{
55	struct clk_frac *frac = to_clk_frac(hw);
56	unsigned long parent_rate = *prate;
57	u32 div;
58	u64 tmp;
59
60	if (rate > parent_rate)
61		return -EINVAL;
62
63	tmp = rate;
64	tmp <<= frac->width;
65	do_div(tmp, parent_rate);
66	div = tmp;
67
68	if (!div)
69		return -EINVAL;
70
71	return (parent_rate >> frac->width) * div;
72}
73
74static int clk_frac_set_rate(struct clk_hw *hw, unsigned long rate,
75			     unsigned long parent_rate)
76{
77	struct clk_frac *frac = to_clk_frac(hw);
78	unsigned long flags;
79	u32 div, val;
80	u64 tmp;
81
82	if (rate > parent_rate)
83		return -EINVAL;
84
85	tmp = rate;
86	tmp <<= frac->width;
87	do_div(tmp, parent_rate);
88	div = tmp;
89
90	if (!div)
91		return -EINVAL;
92
93	spin_lock_irqsave(&mxs_lock, flags);
94
95	val = readl_relaxed(frac->reg);
96	val &= ~(((1 << frac->width) - 1) << frac->shift);
97	val |= div << frac->shift;
98	writel_relaxed(val, frac->reg);
99
100	spin_unlock_irqrestore(&mxs_lock, flags);
101
102	return mxs_clk_wait(frac->reg, frac->busy);
103}
104
105static struct clk_ops clk_frac_ops = {
106	.recalc_rate = clk_frac_recalc_rate,
107	.round_rate = clk_frac_round_rate,
108	.set_rate = clk_frac_set_rate,
109};
110
111struct clk *mxs_clk_frac(const char *name, const char *parent_name,
112			 void __iomem *reg, u8 shift, u8 width, u8 busy)
113{
114	struct clk_frac *frac;
115	struct clk *clk;
116	struct clk_init_data init;
117
118	frac = kzalloc(sizeof(*frac), GFP_KERNEL);
119	if (!frac)
120		return ERR_PTR(-ENOMEM);
121
122	init.name = name;
123	init.ops = &clk_frac_ops;
124	init.flags = CLK_SET_RATE_PARENT;
125	init.parent_names = (parent_name ? &parent_name: NULL);
126	init.num_parents = (parent_name ? 1 : 0);
127
128	frac->reg = reg;
129	frac->shift = shift;
130	frac->width = width;
131	frac->busy = busy;
132	frac->hw.init = &init;
133
134	clk = clk_register(NULL, &frac->hw);
135	if (IS_ERR(clk))
136		kfree(frac);
137
138	return clk;
139}
140