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