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