1 /* 2 * Copyright (c) 2013 Samsung Electronics Co., Ltd. 3 * Copyright (c) 2013 Linaro Ltd. 4 * Author: Thomas Abraham <thomas.ab@samsung.com> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 * 10 * This file includes utility functions to register clocks to common 11 * clock framework for Samsung platforms. 12 */ 13 14 #include <linux/syscore_ops.h> 15 #include "clk.h" 16 17 static DEFINE_SPINLOCK(lock); 18 static struct clk **clk_table; 19 static void __iomem *reg_base; 20 #ifdef CONFIG_OF 21 static struct clk_onecell_data clk_data; 22 #endif 23 24 #ifdef CONFIG_PM_SLEEP 25 static struct samsung_clk_reg_dump *reg_dump; 26 static unsigned long nr_reg_dump; 27 28 static int samsung_clk_suspend(void) 29 { 30 struct samsung_clk_reg_dump *rd = reg_dump; 31 unsigned long i; 32 33 for (i = 0; i < nr_reg_dump; i++, rd++) 34 rd->value = __raw_readl(reg_base + rd->offset); 35 36 return 0; 37 } 38 39 static void samsung_clk_resume(void) 40 { 41 struct samsung_clk_reg_dump *rd = reg_dump; 42 unsigned long i; 43 44 for (i = 0; i < nr_reg_dump; i++, rd++) 45 __raw_writel(rd->value, reg_base + rd->offset); 46 } 47 48 static struct syscore_ops samsung_clk_syscore_ops = { 49 .suspend = samsung_clk_suspend, 50 .resume = samsung_clk_resume, 51 }; 52 #endif /* CONFIG_PM_SLEEP */ 53 54 /* setup the essentials required to support clock lookup using ccf */ 55 void __init samsung_clk_init(struct device_node *np, void __iomem *base, 56 unsigned long nr_clks, unsigned long *rdump, 57 unsigned long nr_rdump, unsigned long *soc_rdump, 58 unsigned long nr_soc_rdump) 59 { 60 reg_base = base; 61 62 #ifdef CONFIG_PM_SLEEP 63 if (rdump && nr_rdump) { 64 unsigned int idx; 65 reg_dump = kzalloc(sizeof(struct samsung_clk_reg_dump) 66 * (nr_rdump + nr_soc_rdump), GFP_KERNEL); 67 if (!reg_dump) { 68 pr_err("%s: memory alloc for register dump failed\n", 69 __func__); 70 return; 71 } 72 73 for (idx = 0; idx < nr_rdump; idx++) 74 reg_dump[idx].offset = rdump[idx]; 75 for (idx = 0; idx < nr_soc_rdump; idx++) 76 reg_dump[nr_rdump + idx].offset = soc_rdump[idx]; 77 nr_reg_dump = nr_rdump + nr_soc_rdump; 78 register_syscore_ops(&samsung_clk_syscore_ops); 79 } 80 #endif 81 82 clk_table = kzalloc(sizeof(struct clk *) * nr_clks, GFP_KERNEL); 83 if (!clk_table) 84 panic("could not allocate clock lookup table\n"); 85 86 if (!np) 87 return; 88 89 #ifdef CONFIG_OF 90 clk_data.clks = clk_table; 91 clk_data.clk_num = nr_clks; 92 of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); 93 #endif 94 } 95 96 /* add a clock instance to the clock lookup table used for dt based lookup */ 97 void samsung_clk_add_lookup(struct clk *clk, unsigned int id) 98 { 99 if (clk_table && id) 100 clk_table[id] = clk; 101 } 102 103 /* register a list of aliases */ 104 void __init samsung_clk_register_alias(struct samsung_clock_alias *list, 105 unsigned int nr_clk) 106 { 107 struct clk *clk; 108 unsigned int idx, ret; 109 110 if (!clk_table) { 111 pr_err("%s: clock table missing\n", __func__); 112 return; 113 } 114 115 for (idx = 0; idx < nr_clk; idx++, list++) { 116 if (!list->id) { 117 pr_err("%s: clock id missing for index %d\n", __func__, 118 idx); 119 continue; 120 } 121 122 clk = clk_table[list->id]; 123 if (!clk) { 124 pr_err("%s: failed to find clock %d\n", __func__, 125 list->id); 126 continue; 127 } 128 129 ret = clk_register_clkdev(clk, list->alias, list->dev_name); 130 if (ret) 131 pr_err("%s: failed to register lookup %s\n", 132 __func__, list->alias); 133 } 134 } 135 136 /* register a list of fixed clocks */ 137 void __init samsung_clk_register_fixed_rate( 138 struct samsung_fixed_rate_clock *list, unsigned int nr_clk) 139 { 140 struct clk *clk; 141 unsigned int idx, ret; 142 143 for (idx = 0; idx < nr_clk; idx++, list++) { 144 clk = clk_register_fixed_rate(NULL, list->name, 145 list->parent_name, list->flags, list->fixed_rate); 146 if (IS_ERR(clk)) { 147 pr_err("%s: failed to register clock %s\n", __func__, 148 list->name); 149 continue; 150 } 151 152 samsung_clk_add_lookup(clk, list->id); 153 154 /* 155 * Unconditionally add a clock lookup for the fixed rate clocks. 156 * There are not many of these on any of Samsung platforms. 157 */ 158 ret = clk_register_clkdev(clk, list->name, NULL); 159 if (ret) 160 pr_err("%s: failed to register clock lookup for %s", 161 __func__, list->name); 162 } 163 } 164 165 /* register a list of fixed factor clocks */ 166 void __init samsung_clk_register_fixed_factor( 167 struct samsung_fixed_factor_clock *list, unsigned int nr_clk) 168 { 169 struct clk *clk; 170 unsigned int idx; 171 172 for (idx = 0; idx < nr_clk; idx++, list++) { 173 clk = clk_register_fixed_factor(NULL, list->name, 174 list->parent_name, list->flags, list->mult, list->div); 175 if (IS_ERR(clk)) { 176 pr_err("%s: failed to register clock %s\n", __func__, 177 list->name); 178 continue; 179 } 180 181 samsung_clk_add_lookup(clk, list->id); 182 } 183 } 184 185 /* register a list of mux clocks */ 186 void __init samsung_clk_register_mux(struct samsung_mux_clock *list, 187 unsigned int nr_clk) 188 { 189 struct clk *clk; 190 unsigned int idx, ret; 191 192 for (idx = 0; idx < nr_clk; idx++, list++) { 193 clk = clk_register_mux(NULL, list->name, list->parent_names, 194 list->num_parents, list->flags, reg_base + list->offset, 195 list->shift, list->width, list->mux_flags, &lock); 196 if (IS_ERR(clk)) { 197 pr_err("%s: failed to register clock %s\n", __func__, 198 list->name); 199 continue; 200 } 201 202 samsung_clk_add_lookup(clk, list->id); 203 204 /* register a clock lookup only if a clock alias is specified */ 205 if (list->alias) { 206 ret = clk_register_clkdev(clk, list->alias, 207 list->dev_name); 208 if (ret) 209 pr_err("%s: failed to register lookup %s\n", 210 __func__, list->alias); 211 } 212 } 213 } 214 215 /* register a list of div clocks */ 216 void __init samsung_clk_register_div(struct samsung_div_clock *list, 217 unsigned int nr_clk) 218 { 219 struct clk *clk; 220 unsigned int idx, ret; 221 222 for (idx = 0; idx < nr_clk; idx++, list++) { 223 if (list->table) 224 clk = clk_register_divider_table(NULL, list->name, 225 list->parent_name, list->flags, 226 reg_base + list->offset, list->shift, 227 list->width, list->div_flags, 228 list->table, &lock); 229 else 230 clk = clk_register_divider(NULL, list->name, 231 list->parent_name, list->flags, 232 reg_base + list->offset, list->shift, 233 list->width, list->div_flags, &lock); 234 if (IS_ERR(clk)) { 235 pr_err("%s: failed to register clock %s\n", __func__, 236 list->name); 237 continue; 238 } 239 240 samsung_clk_add_lookup(clk, list->id); 241 242 /* register a clock lookup only if a clock alias is specified */ 243 if (list->alias) { 244 ret = clk_register_clkdev(clk, list->alias, 245 list->dev_name); 246 if (ret) 247 pr_err("%s: failed to register lookup %s\n", 248 __func__, list->alias); 249 } 250 } 251 } 252 253 /* register a list of gate clocks */ 254 void __init samsung_clk_register_gate(struct samsung_gate_clock *list, 255 unsigned int nr_clk) 256 { 257 struct clk *clk; 258 unsigned int idx, ret; 259 260 for (idx = 0; idx < nr_clk; idx++, list++) { 261 clk = clk_register_gate(NULL, list->name, list->parent_name, 262 list->flags, reg_base + list->offset, 263 list->bit_idx, list->gate_flags, &lock); 264 if (IS_ERR(clk)) { 265 pr_err("%s: failed to register clock %s\n", __func__, 266 list->name); 267 continue; 268 } 269 270 /* register a clock lookup only if a clock alias is specified */ 271 if (list->alias) { 272 ret = clk_register_clkdev(clk, list->alias, 273 list->dev_name); 274 if (ret) 275 pr_err("%s: failed to register lookup %s\n", 276 __func__, list->alias); 277 } 278 279 samsung_clk_add_lookup(clk, list->id); 280 } 281 } 282 283 /* 284 * obtain the clock speed of all external fixed clock sources from device 285 * tree and register it 286 */ 287 #ifdef CONFIG_OF 288 void __init samsung_clk_of_register_fixed_ext( 289 struct samsung_fixed_rate_clock *fixed_rate_clk, 290 unsigned int nr_fixed_rate_clk, 291 struct of_device_id *clk_matches) 292 { 293 const struct of_device_id *match; 294 struct device_node *np; 295 u32 freq; 296 297 for_each_matching_node_and_match(np, clk_matches, &match) { 298 if (of_property_read_u32(np, "clock-frequency", &freq)) 299 continue; 300 fixed_rate_clk[(u32)match->data].fixed_rate = freq; 301 } 302 samsung_clk_register_fixed_rate(fixed_rate_clk, nr_fixed_rate_clk); 303 } 304 #endif 305 306 /* utility function to get the rate of a specified clock */ 307 unsigned long _get_rate(const char *clk_name) 308 { 309 struct clk *clk; 310 311 clk = __clk_lookup(clk_name); 312 if (!clk) { 313 pr_err("%s: could not find clock %s\n", __func__, clk_name); 314 return 0; 315 } 316 317 return clk_get_rate(clk); 318 } 319