| 1 | From 27ff621cd9a5347efda4be502abbef13a99146ce Mon Sep 17 00:00:00 2001 |
| 2 | From: Maarten ter Huurne <maarten@treewalker.org> |
| 3 | Date: Sun, 29 Aug 2010 08:11:00 +0200 |
| 4 | Subject: [PATCH 11/21] MIPS: JZ4740: Added setting of PLL rate and main |
| 5 | dividers. |
| 6 | |
| 7 | This functionality makes a cpufreq driver possible. |
| 8 | Squashed version of the development done in the jz-2.6.39 branch. |
| 9 | --- |
| 10 | arch/mips/jz4740/clock.c | 230 ++++++++++++++++++++++++++++++++++++++++++++-- |
| 11 | arch/mips/jz4740/clock.h | 4 + |
| 12 | 2 files changed, 224 insertions(+), 10 deletions(-) |
| 13 | |
| 14 | --- a/arch/mips/jz4740/clock.c |
| 15 | +++ b/arch/mips/jz4740/clock.c |
| 16 | @@ -1,5 +1,8 @@ |
| 17 | /* |
| 18 | + * Copyright (c) 2006-2007, Ingenic Semiconductor Inc. |
| 19 | * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> |
| 20 | + * Copyright (c) 2010, Ulrich Hecht <ulrich.hecht@gmail.com> |
| 21 | + * Copyright (c) 2010, Maarten ter Huurne <maarten@treewalker.org> |
| 22 | * JZ4740 SoC clock support |
| 23 | * |
| 24 | * This program is free software; you can redistribute it and/or modify it |
| 25 | @@ -41,16 +44,20 @@ |
| 26 | #define JZ_CLOCK_CTRL_I2S_SRC_PLL BIT(31) |
| 27 | #define JZ_CLOCK_CTRL_KO_ENABLE BIT(30) |
| 28 | #define JZ_CLOCK_CTRL_UDC_SRC_PLL BIT(29) |
| 29 | -#define JZ_CLOCK_CTRL_UDIV_MASK 0x1f800000 |
| 30 | #define JZ_CLOCK_CTRL_CHANGE_ENABLE BIT(22) |
| 31 | #define JZ_CLOCK_CTRL_PLL_HALF BIT(21) |
| 32 | -#define JZ_CLOCK_CTRL_LDIV_MASK 0x001f0000 |
| 33 | #define JZ_CLOCK_CTRL_UDIV_OFFSET 23 |
| 34 | #define JZ_CLOCK_CTRL_LDIV_OFFSET 16 |
| 35 | #define JZ_CLOCK_CTRL_MDIV_OFFSET 12 |
| 36 | #define JZ_CLOCK_CTRL_PDIV_OFFSET 8 |
| 37 | #define JZ_CLOCK_CTRL_HDIV_OFFSET 4 |
| 38 | #define JZ_CLOCK_CTRL_CDIV_OFFSET 0 |
| 39 | +#define JZ_CLOCK_CTRL_UDIV_MASK (0x3f << JZ_CLOCK_CTRL_UDIV_OFFSET) |
| 40 | +#define JZ_CLOCK_CTRL_LDIV_MASK (0x1f << JZ_CLOCK_CTRL_LDIV_OFFSET) |
| 41 | +#define JZ_CLOCK_CTRL_MDIV_MASK (0x0f << JZ_CLOCK_CTRL_MDIV_OFFSET) |
| 42 | +#define JZ_CLOCK_CTRL_PDIV_MASK (0x0f << JZ_CLOCK_CTRL_PDIV_OFFSET) |
| 43 | +#define JZ_CLOCK_CTRL_HDIV_MASK (0x0f << JZ_CLOCK_CTRL_HDIV_OFFSET) |
| 44 | +#define JZ_CLOCK_CTRL_CDIV_MASK (0x0f << JZ_CLOCK_CTRL_CDIV_OFFSET) |
| 45 | |
| 46 | #define JZ_CLOCK_GATE_UART0 BIT(0) |
| 47 | #define JZ_CLOCK_GATE_TCU BIT(1) |
| 48 | @@ -90,6 +97,7 @@ |
| 49 | #define JZ_CLOCK_PLL_M_OFFSET 23 |
| 50 | #define JZ_CLOCK_PLL_N_OFFSET 18 |
| 51 | #define JZ_CLOCK_PLL_OD_OFFSET 16 |
| 52 | +#define JZ_CLOCK_PLL_STABILIZE_OFFSET 0 |
| 53 | |
| 54 | #define JZ_CLOCK_LOW_POWER_MODE_DOZE BIT(2) |
| 55 | #define JZ_CLOCK_LOW_POWER_MODE_SLEEP BIT(0) |
| 56 | @@ -97,10 +105,15 @@ |
| 57 | #define JZ_CLOCK_SLEEP_CTRL_SUSPEND_UHC BIT(7) |
| 58 | #define JZ_CLOCK_SLEEP_CTRL_ENABLE_UDC BIT(6) |
| 59 | |
| 60 | +#define JZ_REG_EMC_RTCNT 0x88 |
| 61 | +#define JZ_REG_EMC_RTCOR 0x8C |
| 62 | + |
| 63 | static void __iomem *jz_clock_base; |
| 64 | static spinlock_t jz_clock_lock; |
| 65 | static LIST_HEAD(jz_clocks); |
| 66 | |
| 67 | +static void __iomem *jz_emc_base; |
| 68 | + |
| 69 | struct main_clk { |
| 70 | struct clk clk; |
| 71 | uint32_t div_offset; |
| 72 | @@ -204,25 +217,88 @@ static int jz_clk_ko_is_enabled(struct c |
| 73 | return !!(jz_clk_reg_read(JZ_REG_CLOCK_CTRL) & JZ_CLOCK_CTRL_KO_ENABLE); |
| 74 | } |
| 75 | |
| 76 | +static struct static_clk jz_clk_ext; |
| 77 | + |
| 78 | +static unsigned long jz_clk_pll_calc_rate( |
| 79 | + unsigned int in_div, unsigned int feedback, unsigned int out_div) |
| 80 | +{ |
| 81 | + return ((jz_clk_ext.rate / in_div) * feedback) / out_div; |
| 82 | +} |
| 83 | + |
| 84 | +static void jz_clk_pll_calc_dividers(unsigned long rate, |
| 85 | + unsigned int *in_div, unsigned int *feedback, unsigned int *out_div) |
| 86 | +{ |
| 87 | + unsigned int target; |
| 88 | + |
| 89 | + /* The frequency after the input divider must be between 1 and 15 MHz. |
| 90 | + The highest divider yields the best resolution. */ |
| 91 | + *in_div = jz_clk_ext.rate / 1000000; |
| 92 | + if (*in_div >= 34) |
| 93 | + *in_div = 33; |
| 94 | + |
| 95 | + /* The frequency before the output divider must be between 100 and |
| 96 | + 500 MHz. The lowest target rate is more energy efficient. */ |
| 97 | + if (rate < 25000000) { |
| 98 | + *out_div = 4; |
| 99 | + target = 25000000 * 4; |
| 100 | + } else if (rate <= 50000000) { |
| 101 | + *out_div = 4; |
| 102 | + target = rate * 4; |
| 103 | + } else if (rate <= 100000000) { |
| 104 | + *out_div = 2; |
| 105 | + target = rate * 2; |
| 106 | + } else if (rate <= 500000000) { |
| 107 | + *out_div = 1; |
| 108 | + target = rate; |
| 109 | + } else { |
| 110 | + *out_div = 1; |
| 111 | + target = 500000000; |
| 112 | + } |
| 113 | + |
| 114 | + /* Compute the feedback divider. |
| 115 | + Since the divided input is at least 1 MHz and the target frequency |
| 116 | + at most 500 MHz, the feedback will be at most 500 and will therefore |
| 117 | + always fit in the 9-bit register. |
| 118 | + Similarly, the divided input is at most 15 MHz and the target |
| 119 | + frequency at least 100 MHz, so the feedback will be at least 6 |
| 120 | + where the minimum supported value is 2. */ |
| 121 | + *feedback = ((target / 1000) * *in_div) / (jz_clk_ext.rate / 1000); |
| 122 | +} |
| 123 | + |
| 124 | +static unsigned long jz_clk_pll_round_rate(struct clk *clk, unsigned long rate) |
| 125 | +{ |
| 126 | + unsigned int in_div, feedback, out_div; |
| 127 | + /* The PLL frequency must be a multiple of 24 MHz, since the LCD pixel |
| 128 | + * clock must be exactly 12 MHz for the TV-out to work. |
| 129 | + * TODO: A multiple of 12 MHz for the PLL would work if the PLL would |
| 130 | + * not be divided by 2 before being passed to the set of derived |
| 131 | + * clocks that includes the LCD pixel clock. |
| 132 | + * TODO: Systemwide decisions like this should be made by the board |
| 133 | + * support code, so add some kind of hook for that. |
| 134 | + */ |
| 135 | + unsigned long rate24 = (rate / 24000000) * 24000000; |
| 136 | + |
| 137 | + jz_clk_pll_calc_dividers(rate24, &in_div, &feedback, &out_div); |
| 138 | + return jz_clk_pll_calc_rate(in_div, feedback, out_div); |
| 139 | +} |
| 140 | + |
| 141 | static const int pllno[] = {1, 2, 2, 4}; |
| 142 | |
| 143 | static unsigned long jz_clk_pll_get_rate(struct clk *clk) |
| 144 | { |
| 145 | uint32_t val; |
| 146 | - int m; |
| 147 | - int n; |
| 148 | - int od; |
| 149 | + unsigned int in_div, feedback, out_div; |
| 150 | |
| 151 | val = jz_clk_reg_read(JZ_REG_CLOCK_PLL); |
| 152 | |
| 153 | if (val & JZ_CLOCK_PLL_BYPASS) |
| 154 | return clk_get_rate(clk->parent); |
| 155 | |
| 156 | - m = ((val >> 23) & 0x1ff) + 2; |
| 157 | - n = ((val >> 18) & 0x1f) + 2; |
| 158 | - od = (val >> 16) & 0x3; |
| 159 | + feedback = ((val >> 23) & 0x1ff) + 2; |
| 160 | + in_div = ((val >> 18) & 0x1f) + 2; |
| 161 | + out_div = pllno[(val >> 16) & 0x3]; |
| 162 | |
| 163 | - return ((clk_get_rate(clk->parent) / n) * m) / pllno[od]; |
| 164 | + return jz_clk_pll_calc_rate(in_div, feedback, out_div); |
| 165 | } |
| 166 | |
| 167 | static unsigned long jz_clk_pll_half_get_rate(struct clk *clk) |
| 168 | @@ -235,7 +311,77 @@ static unsigned long jz_clk_pll_half_get |
| 169 | return jz_clk_pll_get_rate(clk->parent) >> 1; |
| 170 | } |
| 171 | |
| 172 | -static const int jz_clk_main_divs[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32}; |
| 173 | +#define SDRAM_TREF 15625 /* Refresh period: 4096 refresh cycles/64ms */ |
| 174 | + |
| 175 | +static void sdram_set_pll(unsigned int pllin) |
| 176 | +{ |
| 177 | + unsigned int ns, sdramclock; |
| 178 | + |
| 179 | + ns = 1000000000 / pllin; |
| 180 | + sdramclock = (SDRAM_TREF / ns) / 64 + 1; |
| 181 | + if (sdramclock > 0xff) sdramclock = 0xff; |
| 182 | + /* Set refresh registers */ |
| 183 | + writew(sdramclock, jz_emc_base + JZ_REG_EMC_RTCOR); |
| 184 | + writew(sdramclock, jz_emc_base + JZ_REG_EMC_RTCNT); |
| 185 | +} |
| 186 | + |
| 187 | +static int jz_clk_pll_set_rate(struct clk *clk, unsigned long rate) |
| 188 | +{ |
| 189 | + unsigned int ctrl, plcr1; |
| 190 | + unsigned int feedback, in_div, out_div, pllout, pllout2; |
| 191 | + |
| 192 | + jz_clk_pll_calc_dividers(rate, &in_div, &feedback, &out_div); |
| 193 | + |
| 194 | + ctrl = jz_clk_reg_read(JZ_REG_CLOCK_CTRL); |
| 195 | + pllout = jz_clk_pll_calc_rate(in_div, feedback, out_div); |
| 196 | + pllout2 = (ctrl & JZ_CLOCK_CTRL_PLL_HALF) ? pllout : (pllout / 2); |
| 197 | + |
| 198 | + /* Init UHC clock */ |
| 199 | + writel(pllout2 / 48000000 - 1, jz_clock_base + JZ_REG_CLOCK_UHC); |
| 200 | + |
| 201 | + plcr1 = ((feedback - 2) << JZ_CLOCK_PLL_M_OFFSET) | |
| 202 | + ((in_div - 2) << JZ_CLOCK_PLL_N_OFFSET) | |
| 203 | + ((out_div - 1) << JZ_CLOCK_PLL_OD_OFFSET) | |
| 204 | + (0x20 << JZ_CLOCK_PLL_STABILIZE_OFFSET) | |
| 205 | + JZ_CLOCK_PLL_ENABLED; |
| 206 | + |
| 207 | + sdram_set_pll(pllout); |
| 208 | + |
| 209 | + /* LCD pixclock */ |
| 210 | + writel(pllout2 / 12000000 - 1, jz_clock_base + JZ_REG_CLOCK_LCD); |
| 211 | + |
| 212 | + /* configure PLL */ |
| 213 | + __asm__ __volatile__( |
| 214 | + ".set noreorder\n\t" |
| 215 | + ".align 5\n" |
| 216 | + "sw %1,0(%0)\n\t" |
| 217 | + "nop\n\t" |
| 218 | + "nop\n\t" |
| 219 | + "nop\n\t" |
| 220 | + "nop\n\t" |
| 221 | + "nop\n\t" |
| 222 | + "nop\n\t" |
| 223 | + "nop\n\t" |
| 224 | + ".set reorder\n\t" |
| 225 | + : |
| 226 | + : "r" (jz_clock_base + JZ_REG_CLOCK_PLL), "r" (plcr1)); |
| 227 | + |
| 228 | + /* MtH: For some reason the MSC will have problems if this flag is not |
| 229 | + restored, even though the MSC is supposedly the only divider |
| 230 | + that is not affected by this flag. */ |
| 231 | + jz_clk_reg_set_bits(JZ_REG_CLOCK_CTRL, JZ_CLOCK_CTRL_CHANGE_ENABLE); |
| 232 | + |
| 233 | + return 0; |
| 234 | +} |
| 235 | + |
| 236 | +static const unsigned int jz_clk_main_divs[] = { |
| 237 | + 1, 2, 3, 4, 6, 8, 12, 16, 24, 32 |
| 238 | +}; |
| 239 | +static const unsigned int jz_clk_main_divs_inv[] = { |
| 240 | + -1, 0, 1, 2, 3, -1, 4, -1, 5, -1, -1, -1, 6, -1, -1, -1, |
| 241 | + 7, -1, -1, -1, -1, -1, -1, -1, 8, -1, -1, -1, -1, -1, -1, -1, |
| 242 | + 9 |
| 243 | +}; |
| 244 | |
| 245 | static unsigned long jz_clk_main_round_rate(struct clk *clk, unsigned long rate) |
| 246 | { |
| 247 | @@ -290,6 +436,64 @@ static int jz_clk_main_set_rate(struct c |
| 248 | return 0; |
| 249 | } |
| 250 | |
| 251 | +static struct main_clk jz_clk_cpu; |
| 252 | + |
| 253 | +int clk_main_set_dividers(bool immediate, unsigned int cdiv, unsigned int hdiv, |
| 254 | + unsigned int mdiv, unsigned int pdiv) |
| 255 | +{ |
| 256 | + unsigned int cdiv_enc, hdiv_enc, mdiv_enc, pdiv_enc; |
| 257 | + unsigned int ctrl; |
| 258 | + unsigned int tmp, wait; |
| 259 | + |
| 260 | + if (cdiv >= ARRAY_SIZE(jz_clk_main_divs_inv) || |
| 261 | + hdiv >= ARRAY_SIZE(jz_clk_main_divs_inv) || |
| 262 | + mdiv >= ARRAY_SIZE(jz_clk_main_divs_inv) || |
| 263 | + pdiv >= ARRAY_SIZE(jz_clk_main_divs_inv)) |
| 264 | + return -EINVAL; |
| 265 | + cdiv_enc = jz_clk_main_divs_inv[cdiv]; |
| 266 | + hdiv_enc = jz_clk_main_divs_inv[hdiv]; |
| 267 | + mdiv_enc = jz_clk_main_divs_inv[mdiv]; |
| 268 | + pdiv_enc = jz_clk_main_divs_inv[pdiv]; |
| 269 | + if (cdiv_enc == (unsigned int)-1 || |
| 270 | + hdiv_enc == (unsigned int)-1 || |
| 271 | + mdiv_enc == (unsigned int)-1 || |
| 272 | + pdiv_enc == (unsigned int)-1) |
| 273 | + return -EINVAL; |
| 274 | + |
| 275 | + ctrl = jz_clk_reg_read(JZ_REG_CLOCK_CTRL); |
| 276 | + ctrl &= ~(JZ_CLOCK_CTRL_CHANGE_ENABLE | |
| 277 | + JZ_CLOCK_CTRL_CDIV_MASK | JZ_CLOCK_CTRL_HDIV_MASK | |
| 278 | + JZ_CLOCK_CTRL_MDIV_MASK | JZ_CLOCK_CTRL_PDIV_MASK); |
| 279 | + if (immediate) ctrl |= JZ_CLOCK_CTRL_CHANGE_ENABLE; |
| 280 | + ctrl |= (cdiv_enc << JZ_CLOCK_CTRL_CDIV_OFFSET) | |
| 281 | + (hdiv_enc << JZ_CLOCK_CTRL_HDIV_OFFSET) | |
| 282 | + (mdiv_enc << JZ_CLOCK_CTRL_MDIV_OFFSET) | |
| 283 | + (pdiv_enc << JZ_CLOCK_CTRL_PDIV_OFFSET); |
| 284 | + |
| 285 | + /* set dividers */ |
| 286 | + /* delay loops lifted from the old Ingenic cpufreq driver */ |
| 287 | + wait = ((clk_get_rate(&jz_clk_cpu.clk) / 1000000) * 500) / 1000; |
| 288 | + __asm__ __volatile__( |
| 289 | + ".set noreorder\n\t" |
| 290 | + ".align 5\n" |
| 291 | + "sw %2,0(%1)\n\t" |
| 292 | + "li %0,0\n\t" |
| 293 | + "1:\n\t" |
| 294 | + "bne %0,%3,1b\n\t" |
| 295 | + "addi %0, 1\n\t" |
| 296 | + "nop\n\t" |
| 297 | + "nop\n\t" |
| 298 | + "nop\n\t" |
| 299 | + "nop\n\t" |
| 300 | + ".set reorder\n\t" |
| 301 | + : "=r" (tmp) |
| 302 | + : "r" (jz_clock_base + JZ_REG_CLOCK_CTRL), "r" (ctrl), |
| 303 | + "r" (wait)); |
| 304 | + |
| 305 | + return 0; |
| 306 | +} |
| 307 | +EXPORT_SYMBOL_GPL(clk_main_set_dividers); |
| 308 | + |
| 309 | static struct clk_ops jz_clk_static_ops = { |
| 310 | .get_rate = jz_clk_static_get_rate, |
| 311 | .enable = jz_clk_enable_gating, |
| 312 | @@ -307,6 +511,8 @@ static struct static_clk jz_clk_ext = { |
| 313 | |
| 314 | static struct clk_ops jz_clk_pll_ops = { |
| 315 | .get_rate = jz_clk_pll_get_rate, |
| 316 | + .set_rate = jz_clk_pll_set_rate, |
| 317 | + .round_rate = jz_clk_pll_round_rate, |
| 318 | }; |
| 319 | |
| 320 | static struct clk jz_clk_pll = { |
| 321 | @@ -897,6 +1103,10 @@ static int jz4740_clock_init(void) |
| 322 | if (!jz_clock_base) |
| 323 | return -EBUSY; |
| 324 | |
| 325 | + jz_emc_base = ioremap(JZ4740_EMC_BASE_ADDR, 0x100); |
| 326 | + if (!jz_emc_base) |
| 327 | + return -EBUSY; |
| 328 | + |
| 329 | spin_lock_init(&jz_clock_lock); |
| 330 | |
| 331 | jz_clk_ext.rate = jz4740_clock_bdata.ext_rate; |
| 332 | --- a/arch/mips/jz4740/clock.h |
| 333 | +++ b/arch/mips/jz4740/clock.h |
| 334 | @@ -17,6 +17,7 @@ |
| 335 | #define __MIPS_JZ4740_CLOCK_H__ |
| 336 | |
| 337 | #include <linux/list.h> |
| 338 | +#include <linux/types.h> |
| 339 | |
| 340 | struct jz4740_clock_board_data { |
| 341 | unsigned long ext_rate; |
| 342 | @@ -63,6 +64,9 @@ struct clk { |
| 343 | |
| 344 | int clk_is_enabled(struct clk *clk); |
| 345 | |
| 346 | +int clk_main_set_dividers(bool immediate, unsigned int cdiv, unsigned int hdiv, |
| 347 | + unsigned int mdiv, unsigned int pdiv); |
| 348 | + |
| 349 | #ifdef CONFIG_DEBUG_FS |
| 350 | void jz4740_clock_debugfs_init(void); |
| 351 | void jz4740_clock_debugfs_add_clk(struct clk *clk); |
| 352 | |