diff options
| -rw-r--r-- | arch/sh/Kconfig | 2 | ||||
| -rw-r--r-- | arch/sh/kernel/cpu/sh4a/clock-sh7722.c | 796 |
2 files changed, 138 insertions, 660 deletions
diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index dbc10ff495a9..b5629cdc37a0 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig | |||
| @@ -516,7 +516,7 @@ config SH_CLK_CPG | |||
| 516 | 516 | ||
| 517 | config SH_CLK_CPG_LEGACY | 517 | config SH_CLK_CPG_LEGACY |
| 518 | depends on SH_CLK_CPG | 518 | depends on SH_CLK_CPG |
| 519 | def_bool y if !CPU_SUBTYPE_SH7785 && !CPU_SUBTYPE_SH7723 && !CPU_SUBTYPE_SH7724 && !CPU_SUBTYPE_SH7343 && !CPU_SUBTYPE_SH7366 | 519 | def_bool y if !CPU_SUBTYPE_SH7785 && !ARCH_SHMOBILE |
| 520 | 520 | ||
| 521 | config SH_CLK_MD | 521 | config SH_CLK_MD |
| 522 | int "CPU Mode Pin Setting" | 522 | int "CPU Mode Pin Setting" |
diff --git a/arch/sh/kernel/cpu/sh4a/clock-sh7722.c b/arch/sh/kernel/cpu/sh4a/clock-sh7722.c index 6ca7c745580d..40f859354f79 100644 --- a/arch/sh/kernel/cpu/sh4a/clock-sh7722.c +++ b/arch/sh/kernel/cpu/sh4a/clock-sh7722.c | |||
| @@ -1,719 +1,197 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * arch/sh/kernel/cpu/sh4a/clock-sh7722.c | 2 | * arch/sh/kernel/cpu/sh4a/clock-sh7722.c |
| 3 | * | 3 | * |
| 4 | * SH7722 support for the clock framework | 4 | * SH7722 clock framework support |
| 5 | * | 5 | * |
| 6 | * Copyright (c) 2006-2007 Nomad Global Solutions Inc | 6 | * Copyright (C) 2009 Magnus Damm |
| 7 | * Based on code for sh7343 by Paul Mundt | ||
| 8 | * | 7 | * |
| 9 | * This file is subject to the terms and conditions of the GNU General Public | 8 | * This program is free software; you can redistribute it and/or modify |
| 10 | * License. See the file "COPYING" in the main directory of this archive | 9 | * it under the terms of the GNU General Public License as published by |
| 11 | * for more details. | 10 | * the Free Software Foundation; either version 2 of the License |
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 12 | */ | 20 | */ |
| 13 | #include <linux/init.h> | 21 | #include <linux/init.h> |
| 14 | #include <linux/kernel.h> | 22 | #include <linux/kernel.h> |
| 15 | #include <linux/io.h> | 23 | #include <linux/io.h> |
| 16 | #include <linux/errno.h> | ||
| 17 | #include <linux/stringify.h> | ||
| 18 | #include <asm/clock.h> | 24 | #include <asm/clock.h> |
| 19 | #include <asm/freq.h> | ||
| 20 | |||
| 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 | 25 | ||
| 36 | { N, 1 }, /* N:1 */ | 26 | /* SH7722 registers */ |
| 37 | { 3, 2 }, | 27 | #define FRQCR 0xa4150000 |
| 38 | { 4, 3 }, | 28 | #define VCLKCR 0xa4150004 |
| 39 | { 5, 4 }, | 29 | #define SCLKACR 0xa4150008 |
| 40 | 30 | #define SCLKBCR 0xa415000c | |
| 41 | { N, 1 } | 31 | #define IRDACLKCR 0xa4150018 |
| 32 | #define PLLCR 0xa4150024 | ||
| 33 | #define MSTPCR0 0xa4150030 | ||
| 34 | #define MSTPCR1 0xa4150034 | ||
| 35 | #define MSTPCR2 0xa4150038 | ||
| 36 | #define DLLFRQ 0xa4150050 | ||
| 37 | |||
| 38 | /* Fixed 32 KHz root clock for RTC and Power Management purposes */ | ||
| 39 | static struct clk r_clk = { | ||
| 40 | .name = "rclk", | ||
| 41 | .id = -1, | ||
| 42 | .rate = 32768, | ||
| 42 | }; | 43 | }; |
| 43 | 44 | ||
| 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 | /* | 45 | /* |
| 129 | * SH7722 uses a common set of multipliers and divisors, so this | 46 | * Default rate for the root input clock, reset this with clk_set_rate() |
| 130 | * is quite simple.. | 47 | * from the platform code. |
| 131 | */ | 48 | */ |
| 132 | 49 | struct clk extal_clk = { | |
| 133 | #define STCPLL(frqcr) (((frqcr >> 24) & 0x1f) + 1) | 50 | .name = "extal", |
| 134 | 51 | .id = -1, | |
| 135 | /* | 52 | .rate = 33333333, |
| 136 | * Instead of having two separate multipliers/divisors set, like this: | ||
| 137 | * | ||
| 138 | * static int multipliers[] = { 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; | ||
| 139 | * static int divisors[] = { 1, 3, 2, 5, 3, 4, 5, 6, 8, 10, 12, 16, 20 }; | ||
| 140 | * | ||
| 141 | * I created the divisors2 array, which is used to calculate rate like | ||
| 142 | * rate = parent * 2 / divisors2[ divisor ]; | ||
| 143 | */ | ||
| 144 | static int divisors2[] = { 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24, 32, 40 }; | ||
| 145 | |||
| 146 | static unsigned long master_clk_recalc(struct clk *clk) | ||
| 147 | { | ||
| 148 | unsigned frqcr = ctrl_inl(FRQCR); | ||
| 149 | |||
| 150 | return CONFIG_SH_PCLK_FREQ * STCPLL(frqcr); | ||
| 151 | } | ||
| 152 | |||
| 153 | static void master_clk_init(struct clk *clk) | ||
| 154 | { | ||
| 155 | clk->parent = NULL; | ||
| 156 | clk->rate = master_clk_recalc(clk); | ||
| 157 | } | ||
| 158 | |||
| 159 | static unsigned long module_clk_recalc(struct clk *clk) | ||
| 160 | { | ||
| 161 | unsigned long frqcr = ctrl_inl(FRQCR); | ||
| 162 | |||
| 163 | return clk->parent->rate / STCPLL(frqcr); | ||
| 164 | } | ||
| 165 | |||
| 166 | #define MASTERDIVS { 2, 3, 4, 6, 8, 16 } | ||
| 167 | #define STCMASK 0x1f | ||
| 168 | #define DIVCALC(div) (div-1) | ||
| 169 | #define FRQCRKICK 0x00000000 | ||
| 170 | |||
| 171 | static int master_clk_setrate(struct clk *clk, unsigned long rate, int id) | ||
| 172 | { | ||
| 173 | int div = rate / clk->rate; | ||
| 174 | int master_divs[] = MASTERDIVS; | ||
| 175 | int index; | ||
| 176 | unsigned long frqcr; | ||
| 177 | |||
| 178 | for (index = 1; index < ARRAY_SIZE(master_divs); index++) | ||
| 179 | if (div >= master_divs[index - 1] && div < master_divs[index]) | ||
| 180 | break; | ||
| 181 | |||
| 182 | if (index >= ARRAY_SIZE(master_divs)) | ||
| 183 | index = ARRAY_SIZE(master_divs); | ||
| 184 | div = master_divs[index - 1]; | ||
| 185 | |||
| 186 | frqcr = ctrl_inl(FRQCR); | ||
| 187 | frqcr &= ~(STCMASK << 24); | ||
| 188 | frqcr |= (DIVCALC(div) << 24); | ||
| 189 | frqcr |= FRQCRKICK; | ||
| 190 | ctrl_outl(frqcr, FRQCR); | ||
| 191 | |||
| 192 | return 0; | ||
| 193 | } | ||
| 194 | |||
| 195 | static struct clk_ops sh7722_master_clk_ops = { | ||
| 196 | .init = master_clk_init, | ||
| 197 | .recalc = master_clk_recalc, | ||
| 198 | .set_rate = master_clk_setrate, | ||
| 199 | }; | ||
| 200 | |||
| 201 | static struct clk_ops sh7722_module_clk_ops = { | ||
| 202 | .recalc = module_clk_recalc, | ||
| 203 | }; | ||
| 204 | |||
| 205 | struct frqcr_context { | ||
| 206 | unsigned mask; | ||
| 207 | unsigned shift; | ||
| 208 | }; | ||
| 209 | |||
| 210 | struct frqcr_context sh7722_get_clk_context(const char *name) | ||
| 211 | { | ||
| 212 | struct frqcr_context ctx = { 0, }; | ||
| 213 | |||
| 214 | if (!strcmp(name, "peripheral_clk")) { | ||
| 215 | ctx.shift = 0; | ||
| 216 | ctx.mask = 0xF; | ||
| 217 | } else if (!strcmp(name, "sdram_clk")) { | ||
| 218 | ctx.shift = 4; | ||
| 219 | ctx.mask = 0xF; | ||
| 220 | } else if (!strcmp(name, "bus_clk")) { | ||
| 221 | ctx.shift = 8; | ||
| 222 | ctx.mask = 0xF; | ||
| 223 | } else if (!strcmp(name, "sh_clk")) { | ||
| 224 | ctx.shift = 12; | ||
| 225 | ctx.mask = 0xF; | ||
| 226 | } else if (!strcmp(name, "umem_clk")) { | ||
| 227 | ctx.shift = 16; | ||
| 228 | ctx.mask = 0xF; | ||
| 229 | } else if (!strcmp(name, "cpu_clk")) { | ||
| 230 | ctx.shift = 20; | ||
| 231 | ctx.mask = 7; | ||
| 232 | } | ||
| 233 | return ctx; | ||
| 234 | } | ||
| 235 | |||
| 236 | /** | ||
| 237 | * sh7722_find_div_index - find divisor for setting rate | ||
| 238 | * | ||
| 239 | * All sh7722 clocks use the same set of multipliers/divisors. This function | ||
| 240 | * chooses correct divisor to set the rate of clock with parent clock that | ||
| 241 | * generates frequency of 'parent_rate' | ||
| 242 | * | ||
| 243 | * @parent_rate: rate of parent clock | ||
| 244 | * @rate: requested rate to be set | ||
| 245 | */ | ||
| 246 | static int sh7722_find_div_index(unsigned long parent_rate, unsigned rate) | ||
| 247 | { | ||
| 248 | unsigned div2 = parent_rate * 2 / rate; | ||
| 249 | int index; | ||
| 250 | |||
| 251 | if (rate > parent_rate) | ||
| 252 | return -EINVAL; | ||
| 253 | |||
| 254 | for (index = 1; index < ARRAY_SIZE(divisors2); index++) { | ||
| 255 | if (div2 > divisors2[index - 1] && div2 <= divisors2[index]) | ||
| 256 | break; | ||
| 257 | } | ||
| 258 | if (index >= ARRAY_SIZE(divisors2)) | ||
| 259 | index = ARRAY_SIZE(divisors2) - 1; | ||
| 260 | return index; | ||
| 261 | } | ||
| 262 | |||
| 263 | static unsigned long sh7722_frqcr_recalc(struct clk *clk) | ||
| 264 | { | ||
| 265 | struct frqcr_context ctx = sh7722_get_clk_context(clk->name); | ||
| 266 | unsigned long frqcr = ctrl_inl(FRQCR); | ||
| 267 | int index; | ||
| 268 | |||
| 269 | index = (frqcr >> ctx.shift) & ctx.mask; | ||
| 270 | return clk->parent->rate * 2 / divisors2[index]; | ||
| 271 | } | ||
| 272 | |||
| 273 | static int sh7722_frqcr_set_rate(struct clk *clk, unsigned long rate, | ||
| 274 | int algo_id) | ||
| 275 | { | ||
| 276 | struct frqcr_context ctx = sh7722_get_clk_context(clk->name); | ||
| 277 | unsigned long parent_rate = clk->parent->rate; | ||
| 278 | int div; | ||
| 279 | unsigned long frqcr; | ||
| 280 | int err = 0; | ||
| 281 | |||
| 282 | /* pretty invalid */ | ||
| 283 | if (parent_rate < rate) | ||
| 284 | return -EINVAL; | ||
| 285 | |||
| 286 | /* look for multiplier/divisor pair */ | ||
| 287 | div = sh7722_find_div_index(parent_rate, rate); | ||
| 288 | if (div<0) | ||
| 289 | return div; | ||
| 290 | |||
| 291 | /* calculate new value of clock rate */ | ||
| 292 | clk->rate = parent_rate * 2 / divisors2[div]; | ||
| 293 | frqcr = ctrl_inl(FRQCR); | ||
| 294 | |||
| 295 | /* FIXME: adjust as algo_id specifies */ | ||
| 296 | if (algo_id != NO_CHANGE) { | ||
| 297 | int originator; | ||
| 298 | char *algo_group_1[] = { "cpu_clk", "umem_clk", "sh_clk" }; | ||
| 299 | char *algo_group_2[] = { "sh_clk", "bus_clk" }; | ||
| 300 | char *algo_group_3[] = { "sh_clk", "sdram_clk" }; | ||
| 301 | char *algo_group_4[] = { "bus_clk", "peripheral_clk" }; | ||
| 302 | char *algo_group_5[] = { "cpu_clk", "peripheral_clk" }; | ||
| 303 | char **algo_current = NULL; | ||
| 304 | /* 3 is the maximum number of clocks in relation */ | ||
| 305 | struct clk *ck[3]; | ||
| 306 | unsigned long values[3]; /* the same comment as above */ | ||
| 307 | int part_length = -1; | ||
| 308 | int i; | ||
| 309 | |||
| 310 | /* | ||
| 311 | * all the steps below only required if adjustion was | ||
| 312 | * requested | ||
| 313 | */ | ||
| 314 | if (algo_id == IUS_N1_N1 || | ||
| 315 | algo_id == IUS_322 || | ||
| 316 | algo_id == IUS_522 || | ||
| 317 | algo_id == IUS_N11) { | ||
| 318 | algo_current = algo_group_1; | ||
| 319 | part_length = 3; | ||
| 320 | } | ||
| 321 | if (algo_id == SB_N1) { | ||
| 322 | algo_current = algo_group_2; | ||
| 323 | part_length = 2; | ||
| 324 | } | ||
| 325 | if (algo_id == SB3_N1 || | ||
| 326 | algo_id == SB3_32 || | ||
| 327 | algo_id == SB3_43 || | ||
| 328 | algo_id == SB3_54) { | ||
| 329 | algo_current = algo_group_3; | ||
| 330 | part_length = 2; | ||
| 331 | } | ||
| 332 | if (algo_id == BP_N1) { | ||
| 333 | algo_current = algo_group_4; | ||
| 334 | part_length = 2; | ||
| 335 | } | ||
| 336 | if (algo_id == IP_N1) { | ||
| 337 | algo_current = algo_group_5; | ||
| 338 | part_length = 2; | ||
| 339 | } | ||
| 340 | if (!algo_current) | ||
| 341 | goto incorrect_algo_id; | ||
| 342 | |||
| 343 | originator = -1; | ||
| 344 | for (i = 0; i < part_length; i ++ ) { | ||
| 345 | if (originator >= 0 && !strcmp(clk->name, | ||
| 346 | algo_current[i])) | ||
| 347 | originator = i; | ||
| 348 | ck[i] = clk_get(NULL, algo_current[i]); | ||
| 349 | values[i] = clk_get_rate(ck[i]); | ||
| 350 | } | ||
| 351 | |||
| 352 | if (originator >= 0) | ||
| 353 | adjust_clocks(originator, adjust_algos[algo_id], | ||
| 354 | values, part_length); | ||
| 355 | |||
| 356 | for (i = 0; i < part_length; i ++ ) { | ||
| 357 | struct frqcr_context part_ctx; | ||
| 358 | int part_div; | ||
| 359 | |||
| 360 | if (likely(!err)) { | ||
| 361 | part_div = sh7722_find_div_index(parent_rate, | ||
| 362 | rate); | ||
| 363 | if (part_div > 0) { | ||
| 364 | part_ctx = sh7722_get_clk_context( | ||
| 365 | ck[i]->name); | ||
| 366 | frqcr &= ~(part_ctx.mask << | ||
| 367 | part_ctx.shift); | ||
| 368 | frqcr |= part_div << part_ctx.shift; | ||
| 369 | } else | ||
| 370 | err = part_div; | ||
| 371 | } | ||
| 372 | |||
| 373 | ck[i]->ops->recalc(ck[i]); | ||
| 374 | clk_put(ck[i]); | ||
| 375 | } | ||
| 376 | } | ||
| 377 | |||
| 378 | /* was there any error during recalculation ? If so, bail out.. */ | ||
| 379 | if (unlikely(err!=0)) | ||
| 380 | goto out_err; | ||
| 381 | |||
| 382 | /* clear FRQCR bits */ | ||
| 383 | frqcr &= ~(ctx.mask << ctx.shift); | ||
| 384 | frqcr |= div << ctx.shift; | ||
| 385 | frqcr |= FRQCRKICK; | ||
| 386 | |||
| 387 | /* ...and perform actual change */ | ||
| 388 | ctrl_outl(frqcr, FRQCR); | ||
| 389 | return 0; | ||
| 390 | |||
| 391 | incorrect_algo_id: | ||
| 392 | return -EINVAL; | ||
| 393 | out_err: | ||
| 394 | return err; | ||
| 395 | } | ||
| 396 | |||
| 397 | static long sh7722_frqcr_round_rate(struct clk *clk, unsigned long rate) | ||
| 398 | { | ||
| 399 | unsigned long parent_rate = clk->parent->rate; | ||
| 400 | int div; | ||
| 401 | |||
| 402 | /* look for multiplier/divisor pair */ | ||
| 403 | div = sh7722_find_div_index(parent_rate, rate); | ||
| 404 | if (div < 0) | ||
| 405 | return clk->rate; | ||
| 406 | |||
| 407 | /* calculate new value of clock rate */ | ||
| 408 | return parent_rate * 2 / divisors2[div]; | ||
| 409 | } | ||
| 410 | |||
| 411 | static struct clk_ops sh7722_frqcr_clk_ops = { | ||
| 412 | .recalc = sh7722_frqcr_recalc, | ||
| 413 | .set_rate = sh7722_frqcr_set_rate, | ||
| 414 | .round_rate = sh7722_frqcr_round_rate, | ||
| 415 | }; | 53 | }; |
| 416 | 54 | ||
| 417 | /* | 55 | /* The dll block multiplies the 32khz r_clk, may be used instead of extal */ |
| 418 | * clock ops methods for SIU A/B and IrDA clock | 56 | static unsigned long dll_recalc(struct clk *clk) |
| 419 | */ | ||
| 420 | static int sh7722_siu_set_rate(struct clk *clk, unsigned long rate, int algo_id) | ||
| 421 | { | ||
| 422 | unsigned long r; | ||
| 423 | int div; | ||
| 424 | |||
| 425 | r = ctrl_inl(clk->arch_flags); | ||
| 426 | div = sh7722_find_div_index(clk->parent->rate, rate); | ||
| 427 | if (div < 0) | ||
| 428 | return div; | ||
| 429 | r = (r & ~0xF) | div; | ||
| 430 | ctrl_outl(r, clk->arch_flags); | ||
| 431 | return 0; | ||
| 432 | } | ||
| 433 | |||
| 434 | static unsigned long sh7722_siu_recalc(struct clk *clk) | ||
| 435 | { | ||
| 436 | unsigned long r; | ||
| 437 | |||
| 438 | r = ctrl_inl(clk->arch_flags); | ||
| 439 | return clk->parent->rate * 2 / divisors2[r & 0xF]; | ||
| 440 | } | ||
| 441 | |||
| 442 | static int sh7722_siu_start_stop(struct clk *clk, int enable) | ||
| 443 | { | 57 | { |
| 444 | unsigned long r; | 58 | unsigned long mult; |
| 445 | 59 | ||
| 446 | r = ctrl_inl(clk->arch_flags); | 60 | if (__raw_readl(PLLCR) & 0x1000) |
| 447 | if (enable) | 61 | mult = __raw_readl(DLLFRQ); |
| 448 | ctrl_outl(r & ~(1 << 8), clk->arch_flags); | ||
| 449 | else | 62 | else |
| 450 | ctrl_outl(r | (1 << 8), clk->arch_flags); | 63 | mult = 0; |
| 451 | return 0; | ||
| 452 | } | ||
| 453 | 64 | ||
| 454 | static int sh7722_siu_enable(struct clk *clk) | 65 | return clk->parent->rate * mult; |
| 455 | { | ||
| 456 | return sh7722_siu_start_stop(clk, 1); | ||
| 457 | } | ||
| 458 | |||
| 459 | static void sh7722_siu_disable(struct clk *clk) | ||
| 460 | { | ||
| 461 | sh7722_siu_start_stop(clk, 0); | ||
| 462 | } | 66 | } |
| 463 | 67 | ||
| 464 | static struct clk_ops sh7722_siu_clk_ops = { | 68 | static struct clk_ops dll_clk_ops = { |
| 465 | .recalc = sh7722_siu_recalc, | 69 | .recalc = dll_recalc, |
| 466 | .set_rate = sh7722_siu_set_rate, | ||
| 467 | .enable = sh7722_siu_enable, | ||
| 468 | .disable = sh7722_siu_disable, | ||
| 469 | }; | 70 | }; |
| 470 | 71 | ||
| 471 | static int sh7722_video_enable(struct clk *clk) | 72 | static struct clk dll_clk = { |
| 472 | { | 73 | .name = "dll_clk", |
| 473 | unsigned long r; | 74 | .id = -1, |
| 474 | 75 | .ops = &dll_clk_ops, | |
| 475 | r = ctrl_inl(VCLKCR); | 76 | .parent = &r_clk, |
| 476 | ctrl_outl( r & ~(1<<8), VCLKCR); | 77 | .flags = CLK_ENABLE_ON_INIT, |
| 477 | return 0; | 78 | }; |
| 478 | } | ||
| 479 | |||
| 480 | static void sh7722_video_disable(struct clk *clk) | ||
| 481 | { | ||
| 482 | unsigned long r; | ||
| 483 | |||
| 484 | r = ctrl_inl(VCLKCR); | ||
| 485 | ctrl_outl( r | (1<<8), VCLKCR); | ||
| 486 | } | ||
| 487 | 79 | ||
| 488 | static int sh7722_video_set_rate(struct clk *clk, unsigned long rate, | 80 | static unsigned long pll_recalc(struct clk *clk) |
| 489 | int algo_id) | ||
| 490 | { | 81 | { |
| 491 | unsigned long r; | 82 | unsigned long mult = 1; |
| 83 | unsigned long div = 1; | ||
| 492 | 84 | ||
| 493 | r = ctrl_inl(VCLKCR); | 85 | if (__raw_readl(PLLCR) & 0x4000) |
| 494 | r &= ~0x3F; | 86 | mult = (((__raw_readl(FRQCR) >> 24) & 0x1f) + 1); |
| 495 | r |= ((clk->parent->rate / rate - 1) & 0x3F); | 87 | else |
| 496 | ctrl_outl(r, VCLKCR); | 88 | div = 2; |
| 497 | return 0; | ||
| 498 | } | ||
| 499 | |||
| 500 | static unsigned long sh7722_video_recalc(struct clk *clk) | ||
| 501 | { | ||
| 502 | unsigned long r; | ||
| 503 | 89 | ||
| 504 | r = ctrl_inl(VCLKCR); | 90 | return (clk->parent->rate * mult) / div; |
| 505 | return clk->parent->rate / ((r & 0x3F) + 1); | ||
| 506 | } | 91 | } |
| 507 | 92 | ||
| 508 | static struct clk_ops sh7722_video_clk_ops = { | 93 | static struct clk_ops pll_clk_ops = { |
| 509 | .recalc = sh7722_video_recalc, | 94 | .recalc = pll_recalc, |
| 510 | .set_rate = sh7722_video_set_rate, | ||
| 511 | .enable = sh7722_video_enable, | ||
| 512 | .disable = sh7722_video_disable, | ||
| 513 | }; | ||
| 514 | /* | ||
| 515 | * and at last, clock definitions themselves | ||
| 516 | */ | ||
| 517 | static struct clk sh7722_umem_clock = { | ||
| 518 | .name = "umem_clk", | ||
| 519 | .ops = &sh7722_frqcr_clk_ops, | ||
| 520 | }; | 95 | }; |
| 521 | 96 | ||
| 522 | static struct clk sh7722_sh_clock = { | 97 | static struct clk pll_clk = { |
| 523 | .name = "sh_clk", | 98 | .name = "pll_clk", |
| 524 | .ops = &sh7722_frqcr_clk_ops, | 99 | .id = -1, |
| 100 | .ops = &pll_clk_ops, | ||
| 101 | .flags = CLK_ENABLE_ON_INIT, | ||
| 525 | }; | 102 | }; |
| 526 | 103 | ||
| 527 | static struct clk sh7722_peripheral_clock = { | 104 | struct clk *main_clks[] = { |
| 528 | .name = "peripheral_clk", | 105 | &r_clk, |
| 529 | .ops = &sh7722_frqcr_clk_ops, | 106 | &extal_clk, |
| 107 | &dll_clk, | ||
| 108 | &pll_clk, | ||
| 530 | }; | 109 | }; |
| 531 | 110 | ||
| 532 | static struct clk sh7722_sdram_clock = { | 111 | static int multipliers[] = { 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; |
| 533 | .name = "sdram_clk", | 112 | static int divisors[] = { 1, 3, 2, 5, 3, 4, 5, 6, 8, 10, 12, 16, 20 }; |
| 534 | .ops = &sh7722_frqcr_clk_ops, | ||
| 535 | }; | ||
| 536 | 113 | ||
| 537 | static struct clk sh7722_r_clock = { | 114 | static struct clk_div_mult_table div4_table = { |
| 538 | .name = "r_clk", | 115 | .divisors = divisors, |
| 539 | .rate = 32768, | 116 | .nr_divisors = ARRAY_SIZE(divisors), |
| 117 | .multipliers = multipliers, | ||
| 118 | .nr_multipliers = ARRAY_SIZE(multipliers), | ||
| 540 | }; | 119 | }; |
| 541 | 120 | ||
| 542 | /* | 121 | enum { DIV4_I, DIV4_U, DIV4_SH, DIV4_B, DIV4_B3, DIV4_P, |
| 543 | * these three clocks - SIU A, SIU B, IrDA - share the same clk_ops | 122 | DIV4_SIUA, DIV4_SIUB, DIV4_IRDA, DIV4_NR }; |
| 544 | * methods of clk_ops determine which register they should access by | ||
| 545 | * examining clk->name field | ||
| 546 | */ | ||
| 547 | static struct clk sh7722_siu_a_clock = { | ||
| 548 | .name = "siu_a_clk", | ||
| 549 | .arch_flags = SCLKACR, | ||
| 550 | .ops = &sh7722_siu_clk_ops, | ||
| 551 | }; | ||
| 552 | 123 | ||
| 553 | static struct clk sh7722_siu_b_clock = { | 124 | #define DIV4(_str, _reg, _bit, _mask, _flags) \ |
| 554 | .name = "siu_b_clk", | 125 | SH_CLK_DIV4(_str, &pll_clk, _reg, _bit, _mask, _flags) |
| 555 | .arch_flags = SCLKBCR, | ||
| 556 | .ops = &sh7722_siu_clk_ops, | ||
| 557 | }; | ||
| 558 | 126 | ||
| 559 | #if defined(CONFIG_CPU_SUBTYPE_SH7722) | 127 | struct clk div4_clks[DIV4_NR] = { |
| 560 | static struct clk sh7722_irda_clock = { | 128 | [DIV4_I] = DIV4("cpu_clk", FRQCR, 20, 0x1fef, CLK_ENABLE_ON_INIT), |
| 561 | .name = "irda_clk", | 129 | [DIV4_U] = DIV4("umem_clk", FRQCR, 16, 0x1fff, CLK_ENABLE_ON_INIT), |
| 562 | .arch_flags = IrDACLKCR, | 130 | [DIV4_SH] = DIV4("shyway_clk", FRQCR, 12, 0x1fff, CLK_ENABLE_ON_INIT), |
| 563 | .ops = &sh7722_siu_clk_ops, | 131 | [DIV4_B] = DIV4("bus_clk", FRQCR, 8, 0x1fff, CLK_ENABLE_ON_INIT), |
| 132 | [DIV4_B3] = DIV4("b3_clk", FRQCR, 4, 0x1fff, CLK_ENABLE_ON_INIT), | ||
| 133 | [DIV4_P] = DIV4("peripheral_clk", FRQCR, 0, 0x1fff, 0), | ||
| 134 | [DIV4_SIUA] = DIV4("siua_clk", SCLKACR, 0, 0x1fff, 0), | ||
| 135 | [DIV4_SIUB] = DIV4("siub_clk", SCLKBCR, 0, 0x1fff, 0), | ||
| 136 | [DIV4_IRDA] = DIV4("irda_clk", IRDACLKCR, 0, 0x1fff, 0), | ||
| 564 | }; | 137 | }; |
| 565 | #endif | ||
| 566 | 138 | ||
| 567 | static struct clk sh7722_video_clock = { | 139 | struct clk div6_clks[] = { |
| 568 | .name = "video_clk", | 140 | SH_CLK_DIV6("video_clk", &pll_clk, VCLKCR, 0), |
| 569 | .ops = &sh7722_video_clk_ops, | ||
| 570 | }; | 141 | }; |
| 571 | 142 | ||
| 572 | #define MSTPCR_ARCH_FLAGS(reg, bit) (((reg) << 8) | (bit)) | 143 | #define MSTP(_str, _parent, _reg, _bit, _flags) \ |
| 573 | #define MSTPCR_ARCH_FLAGS_REG(value) ((value) >> 8) | 144 | SH_CLK_MSTP32(_str, -1, _parent, _reg, _bit, _flags) |
| 574 | #define MSTPCR_ARCH_FLAGS_BIT(value) ((value) & 0xff) | ||
| 575 | |||
| 576 | static int sh7722_mstpcr_start_stop(struct clk *clk, int enable) | ||
| 577 | { | ||
| 578 | unsigned long bit = MSTPCR_ARCH_FLAGS_BIT(clk->arch_flags); | ||
| 579 | unsigned long reg; | ||
| 580 | unsigned long r; | ||
| 581 | 145 | ||
| 582 | switch(MSTPCR_ARCH_FLAGS_REG(clk->arch_flags)) { | 146 | static struct clk mstp_clks[] = { |
| 583 | case 0: | 147 | MSTP("uram0", &div4_clks[DIV4_U], MSTPCR0, 28, CLK_ENABLE_ON_INIT), |
| 584 | reg = MSTPCR0; | 148 | MSTP("xymem0", &div4_clks[DIV4_B], MSTPCR0, 26, CLK_ENABLE_ON_INIT), |
| 585 | break; | 149 | MSTP("tmu0", &div4_clks[DIV4_P], MSTPCR0, 15, 0), |
| 586 | case 1: | 150 | MSTP("cmt0", &r_clk, MSTPCR0, 14, 0), |
| 587 | reg = MSTPCR1; | 151 | MSTP("rwdt0", &r_clk, MSTPCR0, 13, 0), |
| 588 | break; | 152 | MSTP("flctl0", &div4_clks[DIV4_P], MSTPCR0, 10, 0), |
| 589 | case 2: | 153 | MSTP("scif0", &div4_clks[DIV4_P], MSTPCR0, 7, 0), |
| 590 | reg = MSTPCR2; | 154 | MSTP("scif1", &div4_clks[DIV4_P], MSTPCR0, 6, 0), |
| 591 | break; | 155 | MSTP("scif2", &div4_clks[DIV4_P], MSTPCR0, 5, 0), |
| 592 | default: | ||
| 593 | return -EINVAL; | ||
| 594 | } | ||
| 595 | 156 | ||
| 596 | r = ctrl_inl(reg); | 157 | MSTP("i2c0", &div4_clks[DIV4_P], MSTPCR1, 9, 0), |
| 597 | 158 | MSTP("rtc0", &r_clk, MSTPCR1, 8, 0), | |
| 598 | if (enable) | ||
| 599 | r &= ~(1 << bit); | ||
| 600 | else | ||
| 601 | r |= (1 << bit); | ||
| 602 | 159 | ||
| 603 | ctrl_outl(r, reg); | 160 | MSTP("sdhi0", &div4_clks[DIV4_P], MSTPCR2, 18, 0), |
| 604 | return 0; | 161 | MSTP("keysc0", &r_clk, MSTPCR2, 14, 0), |
| 605 | } | 162 | MSTP("usbf0", &div4_clks[DIV4_P], MSTPCR2, 11, 0), |
| 606 | 163 | MSTP("2dg0", &div4_clks[DIV4_B], MSTPCR2, 9, 0), | |
| 607 | static int sh7722_mstpcr_enable(struct clk *clk) | 164 | MSTP("siu0", &div4_clks[DIV4_B], MSTPCR2, 8, 0), |
| 608 | { | 165 | MSTP("vou0", &div4_clks[DIV4_B], MSTPCR2, 5, 0), |
| 609 | return sh7722_mstpcr_start_stop(clk, 1); | 166 | MSTP("jpu0", &div4_clks[DIV4_B], MSTPCR2, 6, CLK_ENABLE_ON_INIT), |
| 610 | } | 167 | MSTP("beu0", &div4_clks[DIV4_B], MSTPCR2, 4, 0), |
| 611 | 168 | MSTP("ceu0", &div4_clks[DIV4_B], MSTPCR2, 3, 0), | |
| 612 | static void sh7722_mstpcr_disable(struct clk *clk) | 169 | MSTP("veu0", &div4_clks[DIV4_B], MSTPCR2, 2, CLK_ENABLE_ON_INIT), |
| 613 | { | 170 | MSTP("vpu0", &div4_clks[DIV4_B], MSTPCR2, 1, CLK_ENABLE_ON_INIT), |
| 614 | sh7722_mstpcr_start_stop(clk, 0); | 171 | MSTP("lcdc0", &div4_clks[DIV4_B], MSTPCR2, 0, 0), |
| 615 | } | ||
| 616 | |||
| 617 | static struct clk_ops sh7722_mstpcr_clk_ops = { | ||
| 618 | .enable = sh7722_mstpcr_enable, | ||
| 619 | .disable = sh7722_mstpcr_disable, | ||
| 620 | .recalc = followparent_recalc, | ||
| 621 | }; | ||
| 622 | |||
| 623 | #define MSTPCR(_name, _parent, regnr, bitnr, _flags) \ | ||
| 624 | { \ | ||
| 625 | .name = _name, \ | ||
| 626 | .flags = _flags, \ | ||
| 627 | .arch_flags = MSTPCR_ARCH_FLAGS(regnr, bitnr), \ | ||
| 628 | .ops = (void *)_parent, \ | ||
| 629 | } | ||
| 630 | |||
| 631 | static struct clk sh7722_mstpcr_clocks[] = { | ||
| 632 | #if defined(CONFIG_CPU_SUBTYPE_SH7722) | ||
| 633 | MSTPCR("uram0", "umem_clk", 0, 28, CLK_ENABLE_ON_INIT), | ||
| 634 | MSTPCR("xymem0", "bus_clk", 0, 26, CLK_ENABLE_ON_INIT), | ||
| 635 | MSTPCR("tmu0", "peripheral_clk", 0, 15, 0), | ||
| 636 | MSTPCR("cmt0", "r_clk", 0, 14, 0), | ||
| 637 | MSTPCR("rwdt0", "r_clk", 0, 13, 0), | ||
| 638 | MSTPCR("flctl0", "peripheral_clk", 0, 10, 0), | ||
| 639 | MSTPCR("scif0", "peripheral_clk", 0, 7, 0), | ||
| 640 | MSTPCR("scif1", "peripheral_clk", 0, 6, 0), | ||
| 641 | MSTPCR("scif2", "peripheral_clk", 0, 5, 0), | ||
| 642 | MSTPCR("i2c0", "peripheral_clk", 1, 9, 0), | ||
| 643 | MSTPCR("rtc0", "r_clk", 1, 8, 0), | ||
| 644 | MSTPCR("sdhi0", "peripheral_clk", 2, 18, 0), | ||
| 645 | MSTPCR("keysc0", "r_clk", 2, 14, 0), | ||
| 646 | MSTPCR("usbf0", "peripheral_clk", 2, 11, 0), | ||
| 647 | MSTPCR("2dg0", "bus_clk", 2, 9, 0), | ||
| 648 | MSTPCR("siu0", "bus_clk", 2, 8, 0), | ||
| 649 | MSTPCR("vou0", "bus_clk", 2, 5, 0), | ||
| 650 | MSTPCR("jpu0", "bus_clk", 2, 6, CLK_ENABLE_ON_INIT), | ||
| 651 | MSTPCR("beu0", "bus_clk", 2, 4, 0), | ||
| 652 | MSTPCR("ceu0", "bus_clk", 2, 3, 0), | ||
| 653 | MSTPCR("veu0", "bus_clk", 2, 2, CLK_ENABLE_ON_INIT), | ||
| 654 | MSTPCR("vpu0", "bus_clk", 2, 1, CLK_ENABLE_ON_INIT), | ||
| 655 | MSTPCR("lcdc0", "bus_clk", 2, 0, 0), | ||
| 656 | #endif | ||
| 657 | }; | ||
| 658 | |||
| 659 | static struct clk *sh7722_clocks[] = { | ||
| 660 | &sh7722_umem_clock, | ||
| 661 | &sh7722_sh_clock, | ||
| 662 | &sh7722_peripheral_clock, | ||
| 663 | &sh7722_sdram_clock, | ||
| 664 | &sh7722_siu_a_clock, | ||
| 665 | &sh7722_siu_b_clock, | ||
| 666 | #if defined(CONFIG_CPU_SUBTYPE_SH7722) | ||
| 667 | &sh7722_irda_clock, | ||
| 668 | #endif | ||
| 669 | &sh7722_video_clock, | ||
| 670 | }; | ||
| 671 | |||
| 672 | /* | ||
| 673 | * init in order: master, module, bus, cpu | ||
| 674 | */ | ||
| 675 | struct clk_ops *onchip_ops[] = { | ||
| 676 | &sh7722_master_clk_ops, | ||
| 677 | &sh7722_module_clk_ops, | ||
| 678 | &sh7722_frqcr_clk_ops, | ||
| 679 | &sh7722_frqcr_clk_ops, | ||
| 680 | }; | 172 | }; |
| 681 | 173 | ||
| 682 | void __init | ||
| 683 | arch_init_clk_ops(struct clk_ops **ops, int type) | ||
| 684 | { | ||
| 685 | BUG_ON(type < 0 || type >= ARRAY_SIZE(onchip_ops)); | ||
| 686 | *ops = onchip_ops[type]; | ||
| 687 | } | ||
| 688 | |||
| 689 | int __init arch_clk_init(void) | 174 | int __init arch_clk_init(void) |
| 690 | { | 175 | { |
| 691 | struct clk *clk; | 176 | int k, ret = 0; |
| 692 | int i; | ||
| 693 | 177 | ||
| 694 | cpg_clk_init(); | 178 | /* autodetect extal or dll configuration */ |
| 179 | if (__raw_readl(PLLCR) & 0x1000) | ||
| 180 | pll_clk.parent = &dll_clk; | ||
| 181 | else | ||
| 182 | pll_clk.parent = &extal_clk; | ||
| 695 | 183 | ||
| 696 | clk = clk_get(NULL, "master_clk"); | 184 | for (k = 0; !ret && (k < ARRAY_SIZE(main_clks)); k++) |
| 697 | for (i = 0; i < ARRAY_SIZE(sh7722_clocks); i++) { | 185 | ret = clk_register(main_clks[k]); |
| 698 | pr_debug( "Registering clock '%s'\n", sh7722_clocks[i]->name); | ||
| 699 | sh7722_clocks[i]->parent = clk; | ||
| 700 | clk_register(sh7722_clocks[i]); | ||
| 701 | } | ||
| 702 | clk_put(clk); | ||
| 703 | 186 | ||
| 704 | clk_register(&sh7722_r_clock); | 187 | if (!ret) |
| 188 | ret = sh_clk_div4_register(div4_clks, DIV4_NR, &div4_table); | ||
| 705 | 189 | ||
| 706 | for (i = 0; i < ARRAY_SIZE(sh7722_mstpcr_clocks); i++) { | 190 | if (!ret) |
| 707 | pr_debug( "Registering mstpcr clock '%s'\n", | 191 | ret = sh_clk_div6_register(div6_clks, ARRAY_SIZE(div6_clks)); |
| 708 | sh7722_mstpcr_clocks[i].name); | ||
| 709 | clk = clk_get(NULL, (void *) sh7722_mstpcr_clocks[i].ops); | ||
| 710 | sh7722_mstpcr_clocks[i].parent = clk; | ||
| 711 | sh7722_mstpcr_clocks[i].ops = &sh7722_mstpcr_clk_ops; | ||
| 712 | clk_register(&sh7722_mstpcr_clocks[i]); | ||
| 713 | clk_put(clk); | ||
| 714 | } | ||
| 715 | 192 | ||
| 716 | propagate_rate(&sh7722_r_clock); /* make sure rate gets propagated */ | 193 | if (!ret) |
| 194 | ret = sh_clk_mstp32_register(mstp_clks, ARRAY_SIZE(mstp_clks)); | ||
| 717 | 195 | ||
| 718 | return 0; | 196 | return ret; |
| 719 | } | 197 | } |
