2 * r8a7790 Common Clock Framework support
4 * Copyright (C) 2013 Renesas Solutions Corp.
6 * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
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.
13 #include <linux/clk-provider.h>
14 #include <linux/clkdev.h>
15 #include <linux/init.h>
17 #include <linux/kernel.h>
19 #include <linux/of_address.h>
21 #define CPG_DIV6_CKSTP BIT(8)
22 #define CPG_DIV6_DIV(d) ((d) & 0x3f)
23 #define CPG_DIV6_DIV_MASK 0x3f
26 * struct div6_clock - MSTP gating clock
27 * @hw: handle between common and hardware-specific interfaces
28 * @reg: IO-remapped register
29 * @div: divisor value (1-64)
37 #define to_div6_clock(_hw) container_of(_hw, struct div6_clock, hw)
39 static int cpg_div6_clock_enable(struct clk_hw
*hw
)
41 struct div6_clock
*clock
= to_div6_clock(hw
);
43 clk_writel(CPG_DIV6_DIV(clock
->div
- 1), clock
->reg
);
48 static void cpg_div6_clock_disable(struct clk_hw
*hw
)
50 struct div6_clock
*clock
= to_div6_clock(hw
);
52 /* DIV6 clocks require the divisor field to be non-zero when stopping
55 clk_writel(CPG_DIV6_CKSTP
| CPG_DIV6_DIV(CPG_DIV6_DIV_MASK
),
59 static int cpg_div6_clock_is_enabled(struct clk_hw
*hw
)
61 struct div6_clock
*clock
= to_div6_clock(hw
);
63 return !(clk_readl(clock
->reg
) & CPG_DIV6_CKSTP
);
66 static unsigned long cpg_div6_clock_recalc_rate(struct clk_hw
*hw
,
67 unsigned long parent_rate
)
69 struct div6_clock
*clock
= to_div6_clock(hw
);
70 unsigned int div
= (clk_readl(clock
->reg
) & CPG_DIV6_DIV_MASK
) + 1;
72 return parent_rate
/ div
;
75 static unsigned int cpg_div6_clock_calc_div(unsigned long rate
,
76 unsigned long parent_rate
)
80 div
= DIV_ROUND_CLOSEST(parent_rate
, rate
);
81 return clamp_t(unsigned int, div
, 1, 64);
84 static long cpg_div6_clock_round_rate(struct clk_hw
*hw
, unsigned long rate
,
85 unsigned long *parent_rate
)
87 unsigned int div
= cpg_div6_clock_calc_div(rate
, *parent_rate
);
89 return *parent_rate
/ div
;
92 static int cpg_div6_clock_set_rate(struct clk_hw
*hw
, unsigned long rate
,
93 unsigned long parent_rate
)
95 struct div6_clock
*clock
= to_div6_clock(hw
);
96 unsigned int div
= cpg_div6_clock_calc_div(rate
, parent_rate
);
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
);
107 static 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
,
116 static void __init
cpg_div6_clock_init(struct device_node
*np
)
118 struct clk_init_data init
;
119 struct div6_clock
*clock
;
120 const char *parent_name
;
125 clock
= kzalloc(sizeof(*clock
), GFP_KERNEL
);
127 pr_err("%s: failed to allocate %s DIV6 clock\n",
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
136 clock
->reg
= of_iomap(np
, 0);
137 if (clock
->reg
== NULL
) {
138 pr_err("%s: failed to map %s DIV6 clock register\n",
143 clock
->div
= (clk_readl(clock
->reg
) & CPG_DIV6_DIV_MASK
) + 1;
145 /* Parse the DT properties. */
146 ret
= of_property_read_string(np
, "clock-output-names", &name
);
148 pr_err("%s: failed to get %s DIV6 clock output name\n",
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",
160 /* Register the clock. */
162 init
.ops
= &cpg_div6_clock_ops
;
163 init
.flags
= CLK_IS_BASIC
;
164 init
.parent_names
= &parent_name
;
165 init
.num_parents
= 1;
167 clock
->hw
.init
= &init
;
169 clk
= clk_register(NULL
, &clock
->hw
);
171 pr_err("%s: failed to register %s DIV6 clock (%ld)\n",
172 __func__
, np
->name
, PTR_ERR(clk
));
176 of_clk_add_provider(np
, of_clk_src_simple_get
, clk
);
185 CLK_OF_DECLARE(cpg_div6_clk
, "renesas,cpg-div6-clock", cpg_div6_clock_init
);