2 * Copyright 2011-2012 Calxeda, Inc.
3 * Copyright (C) 2012-2013 Altera Corporation <www.altera.com>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * Based from clk-highbank.c
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include <linux/clk.h>
21 #include <linux/clkdev.h>
22 #include <linux/clk-provider.h>
26 /* Clock Manager offsets */
27 #define CLKMGR_CTRL 0x0
28 #define CLKMGR_BYPASS 0x4
30 /* Clock bypass bits */
31 #define MAINPLL_BYPASS (1<<0)
32 #define SDRAMPLL_BYPASS (1<<1)
33 #define SDRAMPLL_SRC_BYPASS (1<<2)
34 #define PERPLL_BYPASS (1<<3)
35 #define PERPLL_SRC_BYPASS (1<<4)
37 #define SOCFPGA_PLL_BG_PWRDWN 0
38 #define SOCFPGA_PLL_EXT_ENA 1
39 #define SOCFPGA_PLL_PWR_DOWN 2
40 #define SOCFPGA_PLL_DIVF_MASK 0x0000FFF8
41 #define SOCFPGA_PLL_DIVF_SHIFT 3
42 #define SOCFPGA_PLL_DIVQ_MASK 0x003F0000
43 #define SOCFPGA_PLL_DIVQ_SHIFT 16
45 extern void __iomem
*clk_mgr_base_addr
;
53 #define to_socfpga_clk(p) container_of(p, struct socfpga_clk, hw.hw)
55 static unsigned long clk_pll_recalc_rate(struct clk_hw
*hwclk
,
56 unsigned long parent_rate
)
58 struct socfpga_clk
*socfpgaclk
= to_socfpga_clk(hwclk
);
59 unsigned long divf
, divq
, vco_freq
, reg
;
62 reg
= readl(socfpgaclk
->hw
.reg
);
63 bypass
= readl(clk_mgr_base_addr
+ CLKMGR_BYPASS
);
64 if (bypass
& MAINPLL_BYPASS
)
67 divf
= (reg
& SOCFPGA_PLL_DIVF_MASK
) >> SOCFPGA_PLL_DIVF_SHIFT
;
68 divq
= (reg
& SOCFPGA_PLL_DIVQ_MASK
) >> SOCFPGA_PLL_DIVQ_SHIFT
;
69 vco_freq
= parent_rate
* (divf
+ 1);
70 return vco_freq
/ (1 + divq
);
74 static struct clk_ops clk_pll_ops
= {
75 .recalc_rate
= clk_pll_recalc_rate
,
78 static unsigned long clk_periclk_recalc_rate(struct clk_hw
*hwclk
,
79 unsigned long parent_rate
)
81 struct socfpga_clk
*socfpgaclk
= to_socfpga_clk(hwclk
);
84 if (socfpgaclk
->fixed_div
)
85 div
= socfpgaclk
->fixed_div
;
87 div
= ((readl(socfpgaclk
->hw
.reg
) & 0x1ff) + 1);
89 return parent_rate
/ div
;
92 static const struct clk_ops periclk_ops
= {
93 .recalc_rate
= clk_periclk_recalc_rate
,
96 static __init
struct clk
*socfpga_clk_init(struct device_node
*node
,
97 const struct clk_ops
*ops
)
101 struct socfpga_clk
*socfpga_clk
;
102 const char *clk_name
= node
->name
;
103 const char *parent_name
;
104 struct clk_init_data init
;
108 rc
= of_property_read_u32(node
, "reg", ®
);
112 socfpga_clk
= kzalloc(sizeof(*socfpga_clk
), GFP_KERNEL
);
113 if (WARN_ON(!socfpga_clk
))
116 socfpga_clk
->hw
.reg
= clk_mgr_base_addr
+ reg
;
118 rc
= of_property_read_u32(node
, "fixed-divider", &fixed_div
);
120 socfpga_clk
->fixed_div
= 0;
122 socfpga_clk
->fixed_div
= fixed_div
;
124 of_property_read_string(node
, "clock-output-names", &clk_name
);
126 init
.name
= clk_name
;
129 parent_name
= of_clk_get_parent_name(node
, 0);
130 init
.parent_names
= &parent_name
;
131 init
.num_parents
= 1;
133 socfpga_clk
->hw
.hw
.init
= &init
;
135 if (strcmp(clk_name
, "main_pll") || strcmp(clk_name
, "periph_pll") ||
136 strcmp(clk_name
, "sdram_pll")) {
137 socfpga_clk
->hw
.bit_idx
= SOCFPGA_PLL_EXT_ENA
;
138 clk_pll_ops
.enable
= clk_gate_ops
.enable
;
139 clk_pll_ops
.disable
= clk_gate_ops
.disable
;
142 clk
= clk_register(NULL
, &socfpga_clk
->hw
.hw
);
143 if (WARN_ON(IS_ERR(clk
))) {
147 rc
= of_clk_add_provider(node
, of_clk_src_simple_get
, clk
);
151 static void __init
socfpga_pll_init(struct device_node
*node
)
153 socfpga_clk_init(node
, &clk_pll_ops
);
155 CLK_OF_DECLARE(socfpga_pll
, "altr,socfpga-pll-clock", socfpga_pll_init
);
157 static void __init
socfpga_periph_init(struct device_node
*node
)
159 socfpga_clk_init(node
, &periclk_ops
);
161 CLK_OF_DECLARE(socfpga_periph
, "altr,socfpga-perip-clk", socfpga_periph_init
);
163 void __init
socfpga_init_clocks(void)
168 clk
= clk_register_fixed_factor(NULL
, "smp_twd", "mpuclk", 0, 1, 4);
169 ret
= clk_register_clkdev(clk
, NULL
, "smp_twd");
171 pr_err("smp_twd alias not registered\n");