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