1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright 2011-2012 Calxeda, Inc. |
4 | * Copyright (C) 2012-2013 Altera Corporation <www.altera.com> |
5 | * |
6 | * Based from clk-highbank.c |
7 | */ |
8 | #include <linux/slab.h> |
9 | #include <linux/clk-provider.h> |
10 | #include <linux/io.h> |
11 | #include <linux/mfd/syscon.h> |
12 | #include <linux/of.h> |
13 | #include <linux/regmap.h> |
14 | |
15 | #include "clk.h" |
16 | |
17 | #define SOCFPGA_L4_MP_CLK "l4_mp_clk" |
18 | #define SOCFPGA_L4_SP_CLK "l4_sp_clk" |
19 | #define SOCFPGA_NAND_CLK "nand_clk" |
20 | #define SOCFPGA_NAND_X_CLK "nand_x_clk" |
21 | #define SOCFPGA_MMC_CLK "sdmmc_clk" |
22 | #define SOCFPGA_GPIO_DB_CLK_OFFSET 0xA8 |
23 | |
24 | #define to_socfpga_gate_clk(p) container_of(p, struct socfpga_gate_clk, hw.hw) |
25 | |
26 | /* SDMMC Group for System Manager defines */ |
27 | #define SYSMGR_SDMMCGRP_CTRL_OFFSET 0x108 |
28 | |
29 | static u8 socfpga_clk_get_parent(struct clk_hw *hwclk) |
30 | { |
31 | u32 l4_src; |
32 | u32 perpll_src; |
33 | const char *name = clk_hw_get_name(hw: hwclk); |
34 | |
35 | if (streq(name, SOCFPGA_L4_MP_CLK)) { |
36 | l4_src = readl(addr: clk_mgr_base_addr + CLKMGR_L4SRC); |
37 | return l4_src & 0x1; |
38 | } |
39 | if (streq(name, SOCFPGA_L4_SP_CLK)) { |
40 | l4_src = readl(addr: clk_mgr_base_addr + CLKMGR_L4SRC); |
41 | return !!(l4_src & 2); |
42 | } |
43 | |
44 | perpll_src = readl(addr: clk_mgr_base_addr + CLKMGR_PERPLL_SRC); |
45 | if (streq(name, SOCFPGA_MMC_CLK)) |
46 | return perpll_src & 0x3; |
47 | if (streq(name, SOCFPGA_NAND_CLK) || |
48 | streq(name, SOCFPGA_NAND_X_CLK)) |
49 | return (perpll_src >> 2) & 3; |
50 | |
51 | /* QSPI clock */ |
52 | return (perpll_src >> 4) & 3; |
53 | |
54 | } |
55 | |
56 | static int socfpga_clk_set_parent(struct clk_hw *hwclk, u8 parent) |
57 | { |
58 | u32 src_reg; |
59 | const char *name = clk_hw_get_name(hw: hwclk); |
60 | |
61 | if (streq(name, SOCFPGA_L4_MP_CLK)) { |
62 | src_reg = readl(addr: clk_mgr_base_addr + CLKMGR_L4SRC); |
63 | src_reg &= ~0x1; |
64 | src_reg |= parent; |
65 | writel(val: src_reg, addr: clk_mgr_base_addr + CLKMGR_L4SRC); |
66 | } else if (streq(name, SOCFPGA_L4_SP_CLK)) { |
67 | src_reg = readl(addr: clk_mgr_base_addr + CLKMGR_L4SRC); |
68 | src_reg &= ~0x2; |
69 | src_reg |= (parent << 1); |
70 | writel(val: src_reg, addr: clk_mgr_base_addr + CLKMGR_L4SRC); |
71 | } else { |
72 | src_reg = readl(addr: clk_mgr_base_addr + CLKMGR_PERPLL_SRC); |
73 | if (streq(name, SOCFPGA_MMC_CLK)) { |
74 | src_reg &= ~0x3; |
75 | src_reg |= parent; |
76 | } else if (streq(name, SOCFPGA_NAND_CLK) || |
77 | streq(name, SOCFPGA_NAND_X_CLK)) { |
78 | src_reg &= ~0xC; |
79 | src_reg |= (parent << 2); |
80 | } else {/* QSPI clock */ |
81 | src_reg &= ~0x30; |
82 | src_reg |= (parent << 4); |
83 | } |
84 | writel(val: src_reg, addr: clk_mgr_base_addr + CLKMGR_PERPLL_SRC); |
85 | } |
86 | |
87 | return 0; |
88 | } |
89 | |
90 | static u32 socfpga_clk_get_div(struct socfpga_gate_clk *socfpgaclk) |
91 | { |
92 | u32 div = 1, val; |
93 | |
94 | if (socfpgaclk->fixed_div) |
95 | div = socfpgaclk->fixed_div; |
96 | else if (socfpgaclk->div_reg) { |
97 | val = readl(addr: socfpgaclk->div_reg) >> socfpgaclk->shift; |
98 | val &= GENMASK(socfpgaclk->width - 1, 0); |
99 | /* Check for GPIO_DB_CLK by its offset */ |
100 | if ((uintptr_t) socfpgaclk->div_reg & SOCFPGA_GPIO_DB_CLK_OFFSET) |
101 | div = val + 1; |
102 | else |
103 | div = (1 << val); |
104 | } |
105 | |
106 | return div; |
107 | } |
108 | |
109 | static unsigned long socfpga_clk_recalc_rate(struct clk_hw *hwclk, |
110 | unsigned long parent_rate) |
111 | { |
112 | struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk); |
113 | u32 div = socfpga_clk_get_div(socfpgaclk); |
114 | |
115 | return parent_rate / div; |
116 | } |
117 | |
118 | |
119 | static int socfpga_clk_determine_rate(struct clk_hw *hwclk, |
120 | struct clk_rate_request *req) |
121 | { |
122 | struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk); |
123 | u32 div = socfpga_clk_get_div(socfpgaclk); |
124 | |
125 | req->rate = req->best_parent_rate / div; |
126 | |
127 | return 0; |
128 | } |
129 | |
130 | static struct clk_ops gateclk_ops = { |
131 | .recalc_rate = socfpga_clk_recalc_rate, |
132 | .determine_rate = socfpga_clk_determine_rate, |
133 | .get_parent = socfpga_clk_get_parent, |
134 | .set_parent = socfpga_clk_set_parent, |
135 | }; |
136 | |
137 | void __init socfpga_gate_init(struct device_node *node) |
138 | { |
139 | u32 clk_gate[2]; |
140 | u32 div_reg[3]; |
141 | u32 fixed_div; |
142 | struct clk_hw *hw_clk; |
143 | struct socfpga_gate_clk *socfpga_clk; |
144 | const char *clk_name = node->name; |
145 | const char *parent_name[SOCFPGA_MAX_PARENTS]; |
146 | struct clk_init_data init; |
147 | struct clk_ops *ops; |
148 | int rc; |
149 | |
150 | socfpga_clk = kzalloc(size: sizeof(*socfpga_clk), GFP_KERNEL); |
151 | if (WARN_ON(!socfpga_clk)) |
152 | return; |
153 | |
154 | ops = kmemdup(p: &gateclk_ops, size: sizeof(gateclk_ops), GFP_KERNEL); |
155 | if (WARN_ON(!ops)) |
156 | goto err_kmemdup; |
157 | |
158 | rc = of_property_read_u32_array(np: node, propname: "clk-gate" , out_values: clk_gate, sz: 2); |
159 | if (rc) |
160 | clk_gate[0] = 0; |
161 | |
162 | if (clk_gate[0]) { |
163 | socfpga_clk->hw.reg = clk_mgr_base_addr + clk_gate[0]; |
164 | socfpga_clk->hw.bit_idx = clk_gate[1]; |
165 | |
166 | ops->enable = clk_gate_ops.enable; |
167 | ops->disable = clk_gate_ops.disable; |
168 | } |
169 | |
170 | rc = of_property_read_u32(np: node, propname: "fixed-divider" , out_value: &fixed_div); |
171 | if (rc) |
172 | socfpga_clk->fixed_div = 0; |
173 | else |
174 | socfpga_clk->fixed_div = fixed_div; |
175 | |
176 | rc = of_property_read_u32_array(np: node, propname: "div-reg" , out_values: div_reg, sz: 3); |
177 | if (!rc) { |
178 | socfpga_clk->div_reg = clk_mgr_base_addr + div_reg[0]; |
179 | socfpga_clk->shift = div_reg[1]; |
180 | socfpga_clk->width = div_reg[2]; |
181 | } else { |
182 | socfpga_clk->div_reg = NULL; |
183 | } |
184 | |
185 | of_property_read_string(np: node, propname: "clock-output-names" , out_string: &clk_name); |
186 | |
187 | init.name = clk_name; |
188 | init.ops = ops; |
189 | init.flags = 0; |
190 | |
191 | init.num_parents = of_clk_parent_fill(np: node, parents: parent_name, SOCFPGA_MAX_PARENTS); |
192 | if (init.num_parents < 2) { |
193 | ops->get_parent = NULL; |
194 | ops->set_parent = NULL; |
195 | } |
196 | |
197 | init.parent_names = parent_name; |
198 | socfpga_clk->hw.hw.init = &init; |
199 | |
200 | hw_clk = &socfpga_clk->hw.hw; |
201 | |
202 | rc = clk_hw_register(NULL, hw: hw_clk); |
203 | if (rc) { |
204 | pr_err("Could not register clock:%s\n" , clk_name); |
205 | goto err_clk_hw_register; |
206 | } |
207 | |
208 | rc = of_clk_add_hw_provider(np: node, get: of_clk_hw_simple_get, data: hw_clk); |
209 | if (rc) { |
210 | pr_err("Could not register clock provider for node:%s\n" , |
211 | clk_name); |
212 | goto err_of_clk_add_hw_provider; |
213 | } |
214 | |
215 | return; |
216 | |
217 | err_of_clk_add_hw_provider: |
218 | clk_hw_unregister(hw: hw_clk); |
219 | err_clk_hw_register: |
220 | kfree(objp: ops); |
221 | err_kmemdup: |
222 | kfree(objp: socfpga_clk); |
223 | } |
224 | |