blob: f065f694cb65699705def0036c3f47e903e813ae [file] [log] [blame]
Laurent Pinchartabe844a2013-10-17 23:54:07 +02001/*
2 * r8a7790 Common Clock Framework support
3 *
4 * Copyright (C) 2013 Renesas Solutions Corp.
5 *
6 * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; version 2 of the License.
11 */
12
13#include <linux/clk-provider.h>
14#include <linux/clkdev.h>
15#include <linux/init.h>
16#include <linux/io.h>
17#include <linux/kernel.h>
18#include <linux/of.h>
19#include <linux/of_address.h>
20
21#define CPG_DIV6_CKSTP BIT(8)
22#define CPG_DIV6_DIV(d) ((d) & 0x3f)
23#define CPG_DIV6_DIV_MASK 0x3f
24
25/**
Wolfram Sang95aa4f92014-02-24 20:57:11 +010026 * struct div6_clock - CPG 6 bit divider clock
Laurent Pinchartabe844a2013-10-17 23:54:07 +020027 * @hw: handle between common and hardware-specific interfaces
28 * @reg: IO-remapped register
29 * @div: divisor value (1-64)
30 */
31struct div6_clock {
32 struct clk_hw hw;
33 void __iomem *reg;
34 unsigned int div;
35};
36
37#define to_div6_clock(_hw) container_of(_hw, struct div6_clock, hw)
38
39static int cpg_div6_clock_enable(struct clk_hw *hw)
40{
41 struct div6_clock *clock = to_div6_clock(hw);
42
43 clk_writel(CPG_DIV6_DIV(clock->div - 1), clock->reg);
44
45 return 0;
46}
47
48static void cpg_div6_clock_disable(struct clk_hw *hw)
49{
50 struct div6_clock *clock = to_div6_clock(hw);
51
52 /* DIV6 clocks require the divisor field to be non-zero when stopping
53 * the clock.
54 */
55 clk_writel(CPG_DIV6_CKSTP | CPG_DIV6_DIV(CPG_DIV6_DIV_MASK),
56 clock->reg);
57}
58
59static int cpg_div6_clock_is_enabled(struct clk_hw *hw)
60{
61 struct div6_clock *clock = to_div6_clock(hw);
62
63 return !(clk_readl(clock->reg) & CPG_DIV6_CKSTP);
64}
65
66static unsigned long cpg_div6_clock_recalc_rate(struct clk_hw *hw,
67 unsigned long parent_rate)
68{
69 struct div6_clock *clock = to_div6_clock(hw);
70 unsigned int div = (clk_readl(clock->reg) & CPG_DIV6_DIV_MASK) + 1;
71
72 return parent_rate / div;
73}
74
75static unsigned int cpg_div6_clock_calc_div(unsigned long rate,
76 unsigned long parent_rate)
77{
78 unsigned int div;
79
80 div = DIV_ROUND_CLOSEST(parent_rate, rate);
81 return clamp_t(unsigned int, div, 1, 64);
82}
83
84static long cpg_div6_clock_round_rate(struct clk_hw *hw, unsigned long rate,
85 unsigned long *parent_rate)
86{
87 unsigned int div = cpg_div6_clock_calc_div(rate, *parent_rate);
88
89 return *parent_rate / div;
90}
91
92static int cpg_div6_clock_set_rate(struct clk_hw *hw, unsigned long rate,
93 unsigned long parent_rate)
94{
95 struct div6_clock *clock = to_div6_clock(hw);
96 unsigned int div = cpg_div6_clock_calc_div(rate, parent_rate);
97
98 clock->div = div;
99
100 /* Only program the new divisor if the clock isn't stopped. */
101 if (!(clk_readl(clock->reg) & CPG_DIV6_CKSTP))
102 clk_writel(CPG_DIV6_DIV(clock->div - 1), clock->reg);
103
104 return 0;
105}
106
107static const struct clk_ops cpg_div6_clock_ops = {
108 .enable = cpg_div6_clock_enable,
109 .disable = cpg_div6_clock_disable,
110 .is_enabled = cpg_div6_clock_is_enabled,
111 .recalc_rate = cpg_div6_clock_recalc_rate,
112 .round_rate = cpg_div6_clock_round_rate,
113 .set_rate = cpg_div6_clock_set_rate,
114};
115
116static void __init cpg_div6_clock_init(struct device_node *np)
117{
118 struct clk_init_data init;
119 struct div6_clock *clock;
120 const char *parent_name;
121 const char *name;
122 struct clk *clk;
123 int ret;
124
125 clock = kzalloc(sizeof(*clock), GFP_KERNEL);
126 if (!clock) {
127 pr_err("%s: failed to allocate %s DIV6 clock\n",
128 __func__, np->name);
129 return;
130 }
131
132 /* Remap the clock register and read the divisor. Disabling the
133 * clock overwrites the divisor, so we need to cache its value for the
134 * enable operation.
135 */
136 clock->reg = of_iomap(np, 0);
137 if (clock->reg == NULL) {
138 pr_err("%s: failed to map %s DIV6 clock register\n",
139 __func__, np->name);
140 goto error;
141 }
142
143 clock->div = (clk_readl(clock->reg) & CPG_DIV6_DIV_MASK) + 1;
144
145 /* Parse the DT properties. */
146 ret = of_property_read_string(np, "clock-output-names", &name);
147 if (ret < 0) {
148 pr_err("%s: failed to get %s DIV6 clock output name\n",
149 __func__, np->name);
150 goto error;
151 }
152
153 parent_name = of_clk_get_parent_name(np, 0);
154 if (parent_name == NULL) {
155 pr_err("%s: failed to get %s DIV6 clock parent name\n",
156 __func__, np->name);
157 goto error;
158 }
159
160 /* Register the clock. */
161 init.name = name;
162 init.ops = &cpg_div6_clock_ops;
163 init.flags = CLK_IS_BASIC;
164 init.parent_names = &parent_name;
165 init.num_parents = 1;
166
167 clock->hw.init = &init;
168
169 clk = clk_register(NULL, &clock->hw);
170 if (IS_ERR(clk)) {
171 pr_err("%s: failed to register %s DIV6 clock (%ld)\n",
172 __func__, np->name, PTR_ERR(clk));
173 goto error;
174 }
175
176 of_clk_add_provider(np, of_clk_src_simple_get, clk);
177
178 return;
179
180error:
181 if (clock->reg)
182 iounmap(clock->reg);
183 kfree(clock);
184}
185CLK_OF_DECLARE(cpg_div6_clk, "renesas,cpg-div6-clock", cpg_div6_clock_init);