Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

sh: SH7722 clock framework support.

This adds support for the SH7722 (MobileR) to the clock framework.

Signed-off-by: dmitry pervushin <dimka@nomadgs.com>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>

authored by

dmitry pervushin and committed by
Paul Mundt
1929cb34 34a780a0

+694 -7
+32
Documentation/sh/clk.txt
··· 1 + Clock framework on SuperH architecture 2 + 3 + The framework on SH extends existing API by the function clk_set_rate_ex, 4 + which prototype is as follows: 5 + 6 + clk_set_rate_ex (struct clk *clk, unsigned long rate, int algo_id) 7 + 8 + The algo_id parameter is used to specify algorithm used to recalculate clocks, 9 + adjanced to clock, specified as first argument. It is assumed that algo_id==0 10 + means no changes to adjanced clock 11 + 12 + Internally, the clk_set_rate_ex forwards request to clk->ops->set_rate method, 13 + if it is present in ops structure. The method should set the clock rate and adjust 14 + all needed clocks according to the passed algo_id. 15 + Exact values for algo_id are machine-dependend. For the sh7722, the following 16 + values are defined: 17 + 18 + NO_CHANGE = 0, 19 + IUS_N1_N1, /* I:U = N:1, U:Sh = N:1 */ 20 + IUS_322, /* I:U:Sh = 3:2:2 */ 21 + IUS_522, /* I:U:Sh = 5:2:2 */ 22 + IUS_N11, /* I:U:Sh = N:1:1 */ 23 + SB_N1, /* Sh:B = N:1 */ 24 + SB3_N1, /* Sh:B3 = N:1 */ 25 + SB3_32, /* Sh:B3 = 3:2 */ 26 + SB3_43, /* Sh:B3 = 4:3 */ 27 + SB3_54, /* Sh:B3 = 5:4 */ 28 + BP_N1, /* B:P = N:1 */ 29 + IP_N1 /* I:P = N:1 */ 30 + 31 + Each of these constants means relation between clocks that can be set via the FRQCR 32 + register
+24 -4
arch/sh/kernel/cpu/clock.c
··· 98 98 if (clk->ops && clk->ops->init) 99 99 clk->ops->init(clk); 100 100 101 + kref_get(&clk->kref); 102 + 101 103 if (clk->flags & CLK_ALWAYS_ENABLED) 102 104 return 0; 103 105 104 106 if (likely(clk->ops && clk->ops->enable)) 105 107 clk->ops->enable(clk); 106 108 107 - kref_get(&clk->kref); 108 109 return 0; 109 110 } 110 111 ··· 128 127 129 128 void __clk_disable(struct clk *clk) 130 129 { 130 + int count = kref_put(&clk->kref, clk_kref_release); 131 + 131 132 if (clk->flags & CLK_ALWAYS_ENABLED) 132 133 return; 133 134 134 - kref_put(&clk->kref, clk_kref_release); 135 + if (!count) { /* count reaches zero, disable the clock */ 136 + if (likely(clk->ops && clk->ops->disable)) 137 + clk->ops->disable(clk); 138 + } 135 139 } 136 140 137 141 void clk_disable(struct clk *clk) ··· 157 151 158 152 mutex_unlock(&clock_list_sem); 159 153 154 + if (clk->flags & CLK_ALWAYS_ENABLED) { 155 + pr_debug( "Clock '%s' is ALWAYS_ENABLED\n", clk->name); 156 + if (clk->ops && clk->ops->init) 157 + clk->ops->init(clk); 158 + if (clk->ops && clk->ops->enable) 159 + clk->ops->enable(clk); 160 + pr_debug( "Enabled."); 161 + } 162 + 160 163 return 0; 161 164 } 162 165 ··· 183 168 184 169 int clk_set_rate(struct clk *clk, unsigned long rate) 185 170 { 171 + return clk_set_rate_ex(clk, rate, 0); 172 + } 173 + 174 + int clk_set_rate_ex(struct clk *clk, unsigned long rate, int algo_id) 175 + { 186 176 int ret = -EOPNOTSUPP; 187 177 188 178 if (likely(clk->ops && clk->ops->set_rate)) { 189 179 unsigned long flags; 190 180 191 181 spin_lock_irqsave(&clock_lock, flags); 192 - ret = clk->ops->set_rate(clk, rate); 182 + ret = clk->ops->set_rate(clk, rate, algo_id); 193 183 spin_unlock_irqrestore(&clock_lock, flags); 194 184 } 195 185 ··· 276 256 277 257 arch_init_clk_ops(&clk->ops, i); 278 258 ret |= clk_register(clk); 279 - clk_enable(clk); 280 259 } 281 260 282 261 /* Kick the child clocks.. */ ··· 317 298 EXPORT_SYMBOL_GPL(clk_get_rate); 318 299 EXPORT_SYMBOL_GPL(clk_set_rate); 319 300 EXPORT_SYMBOL_GPL(clk_recalc_rate); 301 + EXPORT_SYMBOL_GPL(clk_set_rate_ex);
+2 -1
arch/sh/kernel/cpu/sh4/clock-sh4-202.c
··· 82 82 for (i = 0; i < ARRAY_SIZE(frqcr3_divisors); i++) { 83 83 int divisor = frqcr3_divisors[i]; 84 84 85 - if (clk->ops->set_rate(clk, clk->parent->rate / divisor) == 0) 85 + if (clk->ops->set_rate(clk, clk->parent->rate / 86 + divisor, 0) == 0) 86 87 break; 87 88 } 88 89
+1 -1
arch/sh/kernel/cpu/sh4a/Makefile
··· 16 16 clock-$(CONFIG_CPU_SUBTYPE_SH7780) := clock-sh7780.o 17 17 clock-$(CONFIG_CPU_SUBTYPE_SH7785) := clock-sh7785.o 18 18 clock-$(CONFIG_CPU_SUBTYPE_SH7343) := clock-sh7343.o 19 - clock-$(CONFIG_CPU_SUBTYPE_SH7722) := clock-sh7343.o 19 + clock-$(CONFIG_CPU_SUBTYPE_SH7722) := clock-sh7722.o 20 20 21 21 obj-y += $(clock-y)
+600
arch/sh/kernel/cpu/sh4a/clock-sh7722.c
··· 1 + /* 2 + * arch/sh/kernel/cpu/sh4a/clock-sh7722.c 3 + * 4 + * SH7722 support for the clock framework 5 + * 6 + * Copyright (c) 2006-2007 Nomad Global Solutions Inc 7 + * Based on code for sh7343 by Paul Mundt 8 + * 9 + * This file is subject to the terms and conditions of the GNU General Public 10 + * License. See the file "COPYING" in the main directory of this archive 11 + * for more details. 12 + */ 13 + #include <linux/init.h> 14 + #include <linux/kernel.h> 15 + #include <linux/io.h> 16 + #include <linux/errno.h> 17 + #include <asm/clock.h> 18 + #include <asm/freq.h> 19 + 20 + #define SH7722_PLL_FREQ (32000000/8) 21 + #define N (-1) 22 + #define NM (-2) 23 + #define ROUND_NEAREST 0 24 + #define ROUND_DOWN -1 25 + #define ROUND_UP +1 26 + 27 + static int adjust_algos[][3] = { 28 + {}, /* NO_CHANGE */ 29 + { NM, N, 1 }, /* N:1, N:1 */ 30 + { 3, 2, 2 }, /* 3:2:2 */ 31 + { 5, 2, 2 }, /* 5:2:2 */ 32 + { N, 1, 1 }, /* N:1:1 */ 33 + 34 + { N, 1 }, /* N:1 */ 35 + 36 + { N, 1 }, /* N:1 */ 37 + { 3, 2 }, 38 + { 4, 3 }, 39 + { 5, 4 }, 40 + 41 + { N, 1 } 42 + }; 43 + 44 + static unsigned long adjust_pair_of_clocks(unsigned long r1, unsigned long r2, 45 + int m1, int m2, int round_flag) 46 + { 47 + unsigned long rem, div; 48 + int the_one = 0; 49 + 50 + pr_debug( "Actual values: r1 = %ld\n", r1); 51 + pr_debug( "...............r2 = %ld\n", r2); 52 + 53 + if (m1 == m2) { 54 + r2 = r1; 55 + pr_debug( "setting equal rates: r2 now %ld\n", r2); 56 + } else if ((m2 == N && m1 == 1) || 57 + (m2 == NM && m1 == N)) { /* N:1 or NM:N */ 58 + pr_debug( "Setting rates as 1:N (N:N*M)\n"); 59 + rem = r2 % r1; 60 + pr_debug( "...remainder = %ld\n", rem); 61 + if (rem) { 62 + div = r2 / r1; 63 + pr_debug( "...div = %ld\n", div); 64 + switch (round_flag) { 65 + case ROUND_NEAREST: 66 + the_one = rem >= r1/2 ? 1 : 0; break; 67 + case ROUND_UP: 68 + the_one = 1; break; 69 + case ROUND_DOWN: 70 + the_one = 0; break; 71 + } 72 + 73 + r2 = r1 * (div + the_one); 74 + pr_debug( "...setting r2 to %ld\n", r2); 75 + } 76 + } else if ((m2 == 1 && m1 == N) || 77 + (m2 == N && m1 == NM)) { /* 1:N or N:NM */ 78 + pr_debug( "Setting rates as N:1 (N*M:N)\n"); 79 + rem = r1 % r2; 80 + pr_debug( "...remainder = %ld\n", rem); 81 + if (rem) { 82 + div = r1 / r2; 83 + pr_debug( "...div = %ld\n", div); 84 + switch (round_flag) { 85 + case ROUND_NEAREST: 86 + the_one = rem > r2/2 ? 1 : 0; break; 87 + case ROUND_UP: 88 + the_one = 0; break; 89 + case ROUND_DOWN: 90 + the_one = 1; break; 91 + } 92 + 93 + r2 = r1 / (div + the_one); 94 + pr_debug( "...setting r2 to %ld\n", r2); 95 + } 96 + } else { /* value:value */ 97 + pr_debug( "Setting rates as %d:%d\n", m1, m2); 98 + div = r1 / m1; 99 + r2 = div * m2; 100 + pr_debug( "...div = %ld\n", div); 101 + pr_debug( "...setting r2 to %ld\n", r2); 102 + } 103 + 104 + return r2; 105 + } 106 + 107 + static void adjust_clocks(int originate, int *l, unsigned long v[], 108 + int n_in_line) 109 + { 110 + int x; 111 + 112 + pr_debug( "Go down from %d...\n", originate); 113 + /* go up recalculation clocks */ 114 + for (x = originate; x>0; x -- ) 115 + v[x-1] = adjust_pair_of_clocks(v[x], v[x-1], 116 + l[x], l[x-1], 117 + ROUND_UP); 118 + 119 + pr_debug( "Go up from %d...\n", originate); 120 + /* go down recalculation clocks */ 121 + for (x = originate; x<n_in_line - 1; x ++ ) 122 + v[x+1] = adjust_pair_of_clocks(v[x], v[x+1], 123 + l[x], l[x+1], 124 + ROUND_UP); 125 + } 126 + 127 + 128 + /* 129 + * SH7722 uses a common set of multipliers and divisors, so this 130 + * is quite simple.. 131 + */ 132 + 133 + /* 134 + * Instead of having two separate multipliers/divisors set, like this: 135 + * 136 + * static int multipliers[] = { 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; 137 + * static int divisors[] = { 1, 3, 2, 5, 3, 4, 5, 6, 8, 10, 12, 16, 20 }; 138 + * 139 + * I created the divisors2 array, which is used to calculate rate like 140 + * rate = parent * 2 / divisors2[ divisor ]; 141 + */ 142 + static int divisors2[] = { 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24, 32, 40 }; 143 + 144 + static void master_clk_init(struct clk *clk) 145 + { 146 + clk_set_rate(clk, clk_get_rate(clk)); 147 + } 148 + 149 + static void master_clk_recalc(struct clk *clk) 150 + { 151 + unsigned long frqcr = ctrl_inl(FRQCR); 152 + 153 + clk->rate = CONFIG_SH_PCLK_FREQ * (1 + (frqcr >> 24 & 0xF)); 154 + } 155 + 156 + static int master_clk_setrate(struct clk *clk, unsigned long rate, int id) 157 + { 158 + int div = rate / SH7722_PLL_FREQ; 159 + int master_divs[] = { 2, 3, 4, 6, 8, 16 }; 160 + int index; 161 + unsigned long frqcr; 162 + 163 + if (rate < SH7722_PLL_FREQ * 2) 164 + return -EINVAL; 165 + 166 + for (index = 1; index < ARRAY_SIZE(master_divs); index++) 167 + if (div >= master_divs[index - 1] && div < master_divs[index]) 168 + break; 169 + 170 + if (index >= ARRAY_SIZE(master_divs)) 171 + index = ARRAY_SIZE(master_divs); 172 + div = master_divs[index - 1]; 173 + 174 + frqcr = ctrl_inl(FRQCR); 175 + frqcr &= ~(0xF << 24); 176 + frqcr |= ( (div-1) << 24); 177 + ctrl_outl(frqcr, FRQCR); 178 + 179 + return 0; 180 + } 181 + 182 + static struct clk_ops sh7722_master_clk_ops = { 183 + .init = master_clk_init, 184 + .recalc = master_clk_recalc, 185 + .set_rate = master_clk_setrate, 186 + }; 187 + 188 + struct frqcr_context { 189 + unsigned mask; 190 + unsigned shift; 191 + }; 192 + 193 + struct frqcr_context sh7722_get_clk_context(const char *name) 194 + { 195 + struct frqcr_context ctx = { 0, }; 196 + 197 + if (!strcmp(name, "peripheral_clk")) { 198 + ctx.shift = 0; 199 + ctx.mask = 0xF; 200 + } else if (!strcmp(name, "sdram_clk")) { 201 + ctx.shift = 4; 202 + ctx.mask = 0xF; 203 + } else if (!strcmp(name, "bus_clk")) { 204 + ctx.shift = 8; 205 + ctx.mask = 0xF; 206 + } else if (!strcmp(name, "sh_clk")) { 207 + ctx.shift = 12; 208 + ctx.mask = 0xF; 209 + } else if (!strcmp(name, "umem_clk")) { 210 + ctx.shift = 16; 211 + ctx.mask = 0xF; 212 + } else if (!strcmp(name, "cpu_clk")) { 213 + ctx.shift = 20; 214 + ctx.mask = 7; 215 + } 216 + return ctx; 217 + } 218 + 219 + /** 220 + * sh7722_find_divisors - find divisor for setting rate 221 + * 222 + * All sh7722 clocks use the same set of multipliers/divisors. This function 223 + * chooses correct divisor to set the rate of clock with parent clock that 224 + * generates frequency of 'parent_rate' 225 + * 226 + * @parent_rate: rate of parent clock 227 + * @rate: requested rate to be set 228 + */ 229 + static int sh7722_find_divisors(unsigned long parent_rate, unsigned rate) 230 + { 231 + unsigned div2 = parent_rate * 2 / rate; 232 + int index; 233 + 234 + if (rate > parent_rate) 235 + return -EINVAL; 236 + 237 + for (index = 1; index < ARRAY_SIZE(divisors2); index++) { 238 + if (div2 > divisors2[index] && div2 <= divisors2[index]) 239 + break; 240 + } 241 + if (index >= ARRAY_SIZE(divisors2)) 242 + index = ARRAY_SIZE(divisors2) - 1; 243 + return divisors2[index]; 244 + } 245 + 246 + static void sh7722_frqcr_recalc(struct clk *clk) 247 + { 248 + struct frqcr_context ctx = sh7722_get_clk_context(clk->name); 249 + unsigned long frqcr = ctrl_inl(FRQCR); 250 + int index; 251 + 252 + index = (frqcr >> ctx.shift) & ctx.mask; 253 + clk->rate = clk->parent->rate * 2 / divisors2[index]; 254 + } 255 + 256 + static int sh7722_frqcr_set_rate(struct clk *clk, unsigned long rate, 257 + int algo_id) 258 + { 259 + struct frqcr_context ctx = sh7722_get_clk_context(clk->name); 260 + unsigned long parent_rate = clk->parent->rate; 261 + int div; 262 + unsigned long frqcr; 263 + int err = 0; 264 + 265 + /* pretty invalid */ 266 + if (parent_rate < rate) 267 + return -EINVAL; 268 + 269 + /* look for multiplier/divisor pair */ 270 + div = sh7722_find_divisors(parent_rate, rate); 271 + if (div<0) 272 + return div; 273 + 274 + /* calculate new value of clock rate */ 275 + clk->rate = parent_rate * 2 / div; 276 + frqcr = ctrl_inl(FRQCR); 277 + 278 + /* FIXME: adjust as algo_id specifies */ 279 + if (algo_id != NO_CHANGE) { 280 + int originator; 281 + char *algo_group_1[] = { "cpu_clk", "umem_clk", "sh_clk" }; 282 + char *algo_group_2[] = { "sh_clk", "bus_clk" }; 283 + char *algo_group_3[] = { "sh_clk", "sdram_clk" }; 284 + char *algo_group_4[] = { "bus_clk", "peripheral_clk" }; 285 + char *algo_group_5[] = { "cpu_clk", "peripheral_clk" }; 286 + char **algo_current = NULL; 287 + /* 3 is the maximum number of clocks in relation */ 288 + struct clk *ck[3]; 289 + unsigned long values[3]; /* the same comment as above */ 290 + int part_length = -1; 291 + int i; 292 + 293 + /* 294 + * all the steps below only required if adjustion was 295 + * requested 296 + */ 297 + if (algo_id == IUS_N1_N1 || 298 + algo_id == IUS_322 || 299 + algo_id == IUS_522 || 300 + algo_id == IUS_N11) { 301 + algo_current = algo_group_1; 302 + part_length = 3; 303 + } 304 + if (algo_id == SB_N1) { 305 + algo_current = algo_group_2; 306 + part_length = 2; 307 + } 308 + if (algo_id == SB3_N1 || 309 + algo_id == SB3_32 || 310 + algo_id == SB3_43 || 311 + algo_id == SB3_54) { 312 + algo_current = algo_group_3; 313 + part_length = 2; 314 + } 315 + if (algo_id == BP_N1) { 316 + algo_current = algo_group_4; 317 + part_length = 2; 318 + } 319 + if (algo_id == IP_N1) { 320 + algo_current = algo_group_5; 321 + part_length = 2; 322 + } 323 + if (!algo_current) 324 + goto incorrect_algo_id; 325 + 326 + originator = -1; 327 + for (i = 0; i < part_length; i ++ ) { 328 + if (originator >= 0 && !strcmp(clk->name, 329 + algo_current[i])) 330 + originator = i; 331 + ck[i] = clk_get(NULL, algo_current[i]); 332 + values[i] = clk_get_rate(ck[i]); 333 + } 334 + 335 + if (originator >= 0) 336 + adjust_clocks(originator, adjust_algos[algo_id], 337 + values, part_length); 338 + 339 + for (i = 0; i < part_length; i ++ ) { 340 + struct frqcr_context part_ctx; 341 + int part_div; 342 + 343 + if (likely(!err)) { 344 + part_div = sh7722_find_divisors(parent_rate, 345 + rate); 346 + if (part_div > 0) { 347 + part_ctx = sh7722_get_clk_context( 348 + ck[i]->name); 349 + frqcr &= ~(part_ctx.mask << 350 + part_ctx.shift); 351 + frqcr |= part_div << part_ctx.shift; 352 + } else 353 + err = part_div; 354 + } 355 + 356 + ck[i]->ops->recalc(ck[i]); 357 + clk_put(ck[i]); 358 + } 359 + } 360 + 361 + /* was there any error during recalculation ? If so, bail out.. */ 362 + if (unlikely(err!=0)) 363 + goto out_err; 364 + 365 + /* clear FRQCR bits */ 366 + frqcr &= ~(ctx.mask << ctx.shift); 367 + frqcr |= div << ctx.shift; 368 + 369 + /* ...and perform actual change */ 370 + ctrl_outl(frqcr, FRQCR); 371 + return 0; 372 + 373 + incorrect_algo_id: 374 + return -EINVAL; 375 + out_err: 376 + return err; 377 + } 378 + 379 + static struct clk_ops sh7722_frqcr_clk_ops = { 380 + .recalc = sh7722_frqcr_recalc, 381 + .set_rate = sh7722_frqcr_set_rate, 382 + }; 383 + 384 + /* 385 + * clock ops methods for SIU A/B and IrDA clock 386 + * 387 + */ 388 + static int sh7722_siu_which(struct clk *clk) 389 + { 390 + if (!strcmp(clk->name, "siu_a_clk")) 391 + return 0; 392 + if (!strcmp(clk->name, "siu_b_clk")) 393 + return 1; 394 + if (!strcmp(clk->name, "irda_clk")) 395 + return 2; 396 + return -EINVAL; 397 + } 398 + 399 + static unsigned long sh7722_siu_regs[] = { 400 + [0] = SCLKACR, 401 + [1] = SCLKBCR, 402 + [2] = IrDACLKCR, 403 + }; 404 + 405 + static int sh7722_siu_start_stop(struct clk *clk, int enable) 406 + { 407 + int siu = sh7722_siu_which(clk); 408 + unsigned long r; 409 + 410 + if (siu < 0) 411 + return siu; 412 + BUG_ON(siu > 2); 413 + r = ctrl_inl(sh7722_siu_regs[siu]); 414 + if (enable) 415 + ctrl_outl(r & ~(1 << 8), sh7722_siu_regs[siu]); 416 + else 417 + ctrl_outl(r | (1 << 8), sh7722_siu_regs[siu]); 418 + return 0; 419 + } 420 + 421 + static void sh7722_siu_enable(struct clk *clk) 422 + { 423 + sh7722_siu_start_stop(clk, 1); 424 + } 425 + 426 + static void sh7722_siu_disable(struct clk *clk) 427 + { 428 + sh7722_siu_start_stop(clk, 0); 429 + } 430 + 431 + static void sh7722_video_enable(struct clk *clk) 432 + { 433 + unsigned long r; 434 + 435 + r = ctrl_inl(VCLKCR); 436 + ctrl_outl( r & ~(1<<8), VCLKCR); 437 + } 438 + 439 + static void sh7722_video_disable(struct clk *clk) 440 + { 441 + unsigned long r; 442 + 443 + r = ctrl_inl(VCLKCR); 444 + ctrl_outl( r | (1<<8), VCLKCR); 445 + } 446 + 447 + static int sh7722_video_set_rate(struct clk *clk, unsigned long rate, 448 + int algo_id) 449 + { 450 + unsigned long r; 451 + 452 + r = ctrl_inl(VCLKCR); 453 + r &= ~0x3F; 454 + r |= ((clk->parent->rate / rate - 1) & 0x3F); 455 + ctrl_outl(r, VCLKCR); 456 + return 0; 457 + } 458 + 459 + static void sh7722_video_recalc(struct clk *clk) 460 + { 461 + unsigned long r; 462 + 463 + r = ctrl_inl(VCLKCR); 464 + clk->rate = clk->parent->rate / ((r & 0x3F) + 1); 465 + } 466 + 467 + static int sh7722_siu_set_rate(struct clk *clk, unsigned long rate, int algo_id) 468 + { 469 + int siu = sh7722_siu_which(clk); 470 + unsigned long r; 471 + int div; 472 + 473 + if (siu < 0) 474 + return siu; 475 + BUG_ON(siu > 2); 476 + r = ctrl_inl(sh7722_siu_regs[siu]); 477 + div = sh7722_find_divisors(clk->parent->rate, rate); 478 + if (div < 0) 479 + return div; 480 + r = (r & ~0xF) | div; 481 + ctrl_outl(r, sh7722_siu_regs[siu]); 482 + return 0; 483 + } 484 + 485 + static void sh7722_siu_recalc(struct clk *clk) 486 + { 487 + int siu = sh7722_siu_which(clk); 488 + unsigned long r; 489 + 490 + if (siu < 0) 491 + return /* siu */ ; 492 + BUG_ON(siu > 1); 493 + r = ctrl_inl(sh7722_siu_regs[siu]); 494 + clk->rate = clk->parent->rate * 2 / divisors2[r & 0xF]; 495 + } 496 + 497 + static struct clk_ops sh7722_siu_clk_ops = { 498 + .recalc = sh7722_siu_recalc, 499 + .set_rate = sh7722_siu_set_rate, 500 + .enable = sh7722_siu_enable, 501 + .disable = sh7722_siu_disable, 502 + }; 503 + 504 + static struct clk_ops sh7722_video_clk_ops = { 505 + .recalc = sh7722_video_recalc, 506 + .set_rate = sh7722_video_set_rate, 507 + .enable = sh7722_video_enable, 508 + .disable = sh7722_video_disable, 509 + }; 510 + /* 511 + * and at last, clock definitions themselves 512 + */ 513 + static struct clk sh7722_umem_clock = { 514 + .name = "umem_clk", 515 + .ops = &sh7722_frqcr_clk_ops, 516 + }; 517 + 518 + static struct clk sh7722_sh_clock = { 519 + .name = "sh_clk", 520 + .ops = &sh7722_frqcr_clk_ops, 521 + }; 522 + 523 + static struct clk sh7722_peripheral_clock = { 524 + .name = "peripheral_clk", 525 + .ops = &sh7722_frqcr_clk_ops, 526 + }; 527 + 528 + static struct clk sh7722_sdram_clock = { 529 + .name = "sdram_clk", 530 + .ops = &sh7722_frqcr_clk_ops, 531 + }; 532 + 533 + /* 534 + * these three clocks - SIU A, SIU B, IrDA - share the same clk_ops 535 + * methods of clk_ops determine which register they should access by 536 + * examining clk->name field 537 + */ 538 + static struct clk sh7722_siu_a_clock = { 539 + .name = "siu_a_clk", 540 + .ops = &sh7722_siu_clk_ops, 541 + }; 542 + 543 + static struct clk sh7722_siu_b_clock = { 544 + .name = "siu_b_clk", 545 + .ops = &sh7722_siu_clk_ops, 546 + }; 547 + 548 + static struct clk sh7722_irda_clock = { 549 + .name = "irda_clk", 550 + .ops = &sh7722_siu_clk_ops, 551 + }; 552 + 553 + static struct clk sh7722_video_clock = { 554 + .name = "video_clk", 555 + .ops = &sh7722_video_clk_ops, 556 + }; 557 + 558 + static struct clk *sh7722_clocks[] = { 559 + &sh7722_umem_clock, 560 + &sh7722_sh_clock, 561 + &sh7722_peripheral_clock, 562 + &sh7722_sdram_clock, 563 + &sh7722_siu_a_clock, 564 + &sh7722_siu_b_clock, 565 + &sh7722_irda_clock, 566 + &sh7722_video_clock, 567 + }; 568 + 569 + /* 570 + * init in order: master, module, bus, cpu 571 + */ 572 + struct clk_ops *onchip_ops[] = { 573 + &sh7722_master_clk_ops, 574 + &sh7722_frqcr_clk_ops, 575 + &sh7722_frqcr_clk_ops, 576 + &sh7722_frqcr_clk_ops, 577 + }; 578 + 579 + void __init 580 + arch_init_clk_ops(struct clk_ops **ops, int type) 581 + { 582 + BUG_ON(type < 0 || type > ARRAY_SIZE(onchip_ops)); 583 + *ops = onchip_ops[type]; 584 + } 585 + 586 + int __init sh7722_clock_init(void) 587 + { 588 + struct clk *master; 589 + int i; 590 + 591 + master = clk_get(NULL, "master_clk"); 592 + for (i = 0; i < ARRAY_SIZE(sh7722_clocks); i++) { 593 + pr_debug( "Registering clock '%s'\n", sh7722_clocks[i]->name); 594 + sh7722_clocks[i]->parent = master; 595 + clk_register(sh7722_clocks[i]); 596 + } 597 + clk_put(master); 598 + return 0; 599 + } 600 + arch_initcall(sh7722_clock_init);
+31 -1
include/asm-sh/clock.h
··· 13 13 void (*enable)(struct clk *clk); 14 14 void (*disable)(struct clk *clk); 15 15 void (*recalc)(struct clk *clk); 16 - int (*set_rate)(struct clk *clk, unsigned long rate); 16 + int (*set_rate)(struct clk *clk, unsigned long rate, int algo_id); 17 17 }; 18 18 19 19 struct clk { ··· 50 50 51 51 int show_clocks(struct seq_file *m); 52 52 53 + /* the exported API, in addition to clk_set_rate */ 54 + /** 55 + * clk_set_rate_ex - set the clock rate for a clock source, with additional parameter 56 + * @clk: clock source 57 + * @rate: desired clock rate in Hz 58 + * @algo_id: algorithm id to be passed down to ops->set_rate 59 + * 60 + * Returns success (0) or negative errno. 61 + */ 62 + int clk_set_rate_ex(struct clk *clk, unsigned long rate, int algo_id); 63 + 64 + enum clk_sh_algo_id { 65 + NO_CHANGE = 0, 66 + 67 + IUS_N1_N1, 68 + IUS_322, 69 + IUS_522, 70 + IUS_N11, 71 + 72 + SB_N1, 73 + 74 + SB3_N1, 75 + SB3_32, 76 + SB3_43, 77 + SB3_54, 78 + 79 + BP_N1, 80 + 81 + IP_N1, 82 + }; 53 83 #endif /* __ASM_SH_CLOCK_H */
+4
include/asm-sh/cpu-sh4/freq.h
··· 12 12 13 13 #if defined(CONFIG_CPU_SUBTYPE_SH73180) || defined(CONFIG_CPU_SUBTYPE_SH7722) 14 14 #define FRQCR 0xa4150000 15 + #define VCLKCR 0xa4150004 16 + #define SCLKACR 0xa4150008 17 + #define SCLKBCR 0xa415000c 18 + #define IrDACLKCR 0xa4150010 15 19 #elif defined(CONFIG_CPU_SUBTYPE_SH7780) 16 20 #define FRQCR 0xffc80000 17 21 #elif defined(CONFIG_CPU_SUBTYPE_SH7785)