Date:2010-08-29 08:11:00 (13 years 6 months ago)
Author:Maarten ter Huurne
Commit:2ab204fc94838234636c4657712e463aa0c462a9
Message:MIPS: JZ4740: Added setting of PLL rate and main dividers.

This functionality makes a cpufreq driver possible.
Squashed version of the development done in the jz-2.6.39 branch.
Files: arch/mips/jz4740/clock.c (9 diffs)
arch/mips/jz4740/clock.h (2 diffs)

Change Details

arch/mips/jz4740/clock.c
11/*
2 * Copyright (c) 2006-2007, Ingenic Semiconductor Inc.
23 * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
4 * Copyright (c) 2010, Ulrich Hecht <ulrich.hecht@gmail.com>
5 * Copyright (c) 2010, Maarten ter Huurne <maarten@treewalker.org>
36 * JZ4740 SoC clock support
47 *
58 * This program is free software; you can redistribute it and/or modify it
...... 
4144#define JZ_CLOCK_CTRL_I2S_SRC_PLL BIT(31)
4245#define JZ_CLOCK_CTRL_KO_ENABLE BIT(30)
4346#define JZ_CLOCK_CTRL_UDC_SRC_PLL BIT(29)
44#define JZ_CLOCK_CTRL_UDIV_MASK 0x1f800000
4547#define JZ_CLOCK_CTRL_CHANGE_ENABLE BIT(22)
4648#define JZ_CLOCK_CTRL_PLL_HALF BIT(21)
47#define JZ_CLOCK_CTRL_LDIV_MASK 0x001f0000
4849#define JZ_CLOCK_CTRL_UDIV_OFFSET 23
4950#define JZ_CLOCK_CTRL_LDIV_OFFSET 16
5051#define JZ_CLOCK_CTRL_MDIV_OFFSET 12
5152#define JZ_CLOCK_CTRL_PDIV_OFFSET 8
5253#define JZ_CLOCK_CTRL_HDIV_OFFSET 4
5354#define JZ_CLOCK_CTRL_CDIV_OFFSET 0
55#define JZ_CLOCK_CTRL_UDIV_MASK (0x3f << JZ_CLOCK_CTRL_UDIV_OFFSET)
56#define JZ_CLOCK_CTRL_LDIV_MASK (0x1f << JZ_CLOCK_CTRL_LDIV_OFFSET)
57#define JZ_CLOCK_CTRL_MDIV_MASK (0x0f << JZ_CLOCK_CTRL_MDIV_OFFSET)
58#define JZ_CLOCK_CTRL_PDIV_MASK (0x0f << JZ_CLOCK_CTRL_PDIV_OFFSET)
59#define JZ_CLOCK_CTRL_HDIV_MASK (0x0f << JZ_CLOCK_CTRL_HDIV_OFFSET)
60#define JZ_CLOCK_CTRL_CDIV_MASK (0x0f << JZ_CLOCK_CTRL_CDIV_OFFSET)
5461
5562#define JZ_CLOCK_GATE_UART0 BIT(0)
5663#define JZ_CLOCK_GATE_TCU BIT(1)
...... 
9097#define JZ_CLOCK_PLL_M_OFFSET 23
9198#define JZ_CLOCK_PLL_N_OFFSET 18
9299#define JZ_CLOCK_PLL_OD_OFFSET 16
100#define JZ_CLOCK_PLL_STABILIZE_OFFSET 0
93101
94102#define JZ_CLOCK_LOW_POWER_MODE_DOZE BIT(2)
95103#define JZ_CLOCK_LOW_POWER_MODE_SLEEP BIT(0)
...... 
97105#define JZ_CLOCK_SLEEP_CTRL_SUSPEND_UHC BIT(7)
98106#define JZ_CLOCK_SLEEP_CTRL_ENABLE_UDC BIT(6)
99107
108#define JZ_REG_EMC_RTCNT 0x88
109#define JZ_REG_EMC_RTCOR 0x8C
110
100111static void __iomem *jz_clock_base;
101112static spinlock_t jz_clock_lock;
102113static LIST_HEAD(jz_clocks);
103114
115static void __iomem *jz_emc_base;
116
104117struct main_clk {
105118    struct clk clk;
106119    uint32_t div_offset;
...... 
204217    return !!(jz_clk_reg_read(JZ_REG_CLOCK_CTRL) & JZ_CLOCK_CTRL_KO_ENABLE);
205218}
206219
220static struct static_clk jz_clk_ext;
221
222static unsigned long jz_clk_pll_calc_rate(
223    unsigned int in_div, unsigned int feedback, unsigned int out_div)
224{
225    return ((jz_clk_ext.rate / in_div) * feedback) / out_div;
226}
227
228static void jz_clk_pll_calc_dividers(unsigned long rate,
229    unsigned int *in_div, unsigned int *feedback, unsigned int *out_div)
230{
231    unsigned int target;
232
233    /* The frequency after the input divider must be between 1 and 15 MHz.
234       The highest divider yields the best resolution. */
235    *in_div = jz_clk_ext.rate / 1000000;
236    if (*in_div >= 34)
237        *in_div = 33;
238
239    /* The frequency before the output divider must be between 100 and
240       500 MHz. The lowest target rate is more energy efficient. */
241    if (rate < 25000000) {
242        *out_div = 4;
243        target = 25000000 * 4;
244    } else if (rate <= 50000000) {
245        *out_div = 4;
246        target = rate * 4;
247    } else if (rate <= 100000000) {
248        *out_div = 2;
249        target = rate * 2;
250    } else if (rate <= 500000000) {
251        *out_div = 1;
252        target = rate;
253    } else {
254        *out_div = 1;
255        target = 500000000;
256    }
257
258    /* Compute the feedback divider.
259       Since the divided input is at least 1 MHz and the target frequency
260       at most 500 MHz, the feedback will be at most 500 and will therefore
261       always fit in the 9-bit register.
262       Similarly, the divided input is at most 15 MHz and the target
263       frequency at least 100 MHz, so the feedback will be at least 6
264       where the minimum supported value is 2. */
265    *feedback = ((target / 1000) * *in_div) / (jz_clk_ext.rate / 1000);
266}
267
268static unsigned long jz_clk_pll_round_rate(struct clk *clk, unsigned long rate)
269{
270    unsigned int in_div, feedback, out_div;
271    /* The PLL frequency must be a multiple of 24 MHz, since the LCD pixel
272     * clock must be exactly 12 MHz for the TV-out to work.
273     * TODO: A multiple of 12 MHz for the PLL would work if the PLL would
274     * not be divided by 2 before being passed to the set of derived
275     * clocks that includes the LCD pixel clock.
276     * TODO: Systemwide decisions like this should be made by the board
277     * support code, so add some kind of hook for that.
278     */
279    unsigned long rate24 = (rate / 24000000) * 24000000;
280
281    jz_clk_pll_calc_dividers(rate24, &in_div, &feedback, &out_div);
282    return jz_clk_pll_calc_rate(in_div, feedback, out_div);
283}
284
207285static const int pllno[] = {1, 2, 2, 4};
208286
209287static unsigned long jz_clk_pll_get_rate(struct clk *clk)
210288{
211289    uint32_t val;
212    int m;
213    int n;
214    int od;
290    unsigned int in_div, feedback, out_div;
215291
216292    val = jz_clk_reg_read(JZ_REG_CLOCK_PLL);
217293
218294    if (val & JZ_CLOCK_PLL_BYPASS)
219295        return clk_get_rate(clk->parent);
220296
221    m = ((val >> 23) & 0x1ff) + 2;
222    n = ((val >> 18) & 0x1f) + 2;
223    od = (val >> 16) & 0x3;
297    feedback = ((val >> 23) & 0x1ff) + 2;
298    in_div = ((val >> 18) & 0x1f) + 2;
299    out_div = pllno[(val >> 16) & 0x3];
224300
225    return ((clk_get_rate(clk->parent) / n) * m) / pllno[od];
301    return jz_clk_pll_calc_rate(in_div, feedback, out_div);
226302}
227303
228304static unsigned long jz_clk_pll_half_get_rate(struct clk *clk)
...... 
235311    return jz_clk_pll_get_rate(clk->parent) >> 1;
236312}
237313
238static const int jz_clk_main_divs[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32};
314#define SDRAM_TREF 15625 /* Refresh period: 4096 refresh cycles/64ms */
315
316static void sdram_set_pll(unsigned int pllin)
317{
318    unsigned int ns, sdramclock;
319
320    ns = 1000000000 / pllin;
321    sdramclock = (SDRAM_TREF / ns) / 64 + 1;
322    if (sdramclock > 0xff) sdramclock = 0xff;
323    /* Set refresh registers */
324    writew(sdramclock, jz_emc_base + JZ_REG_EMC_RTCOR);
325    writew(sdramclock, jz_emc_base + JZ_REG_EMC_RTCNT);
326}
327
328static int jz_clk_pll_set_rate(struct clk *clk, unsigned long rate)
329{
330    unsigned int ctrl, plcr1;
331    unsigned int feedback, in_div, out_div, pllout, pllout2;
332
333    jz_clk_pll_calc_dividers(rate, &in_div, &feedback, &out_div);
334
335    ctrl = jz_clk_reg_read(JZ_REG_CLOCK_CTRL);
336    pllout = jz_clk_pll_calc_rate(in_div, feedback, out_div);
337    pllout2 = (ctrl & JZ_CLOCK_CTRL_PLL_HALF) ? pllout : (pllout / 2);
338
339    /* Init UHC clock */
340    writel(pllout2 / 48000000 - 1, jz_clock_base + JZ_REG_CLOCK_UHC);
341
342    plcr1 = ((feedback - 2) << JZ_CLOCK_PLL_M_OFFSET) |
343        ((in_div - 2) << JZ_CLOCK_PLL_N_OFFSET) |
344        ((out_div - 1) << JZ_CLOCK_PLL_OD_OFFSET) |
345        (0x20 << JZ_CLOCK_PLL_STABILIZE_OFFSET) |
346        JZ_CLOCK_PLL_ENABLED;
347
348    sdram_set_pll(pllout);
349
350    /* LCD pixclock */
351    writel(pllout2 / 12000000 - 1, jz_clock_base + JZ_REG_CLOCK_LCD);
352
353    /* configure PLL */
354    __asm__ __volatile__(
355        ".set noreorder\n\t"
356        ".align 5\n"
357        "sw %1,0(%0)\n\t"
358        "nop\n\t"
359        "nop\n\t"
360        "nop\n\t"
361        "nop\n\t"
362        "nop\n\t"
363        "nop\n\t"
364        "nop\n\t"
365        ".set reorder\n\t"
366        :
367        : "r" (jz_clock_base + JZ_REG_CLOCK_PLL), "r" (plcr1));
368
369    /* MtH: For some reason the MSC will have problems if this flag is not
370            restored, even though the MSC is supposedly the only divider
371            that is not affected by this flag. */
372    jz_clk_reg_set_bits(JZ_REG_CLOCK_CTRL, JZ_CLOCK_CTRL_CHANGE_ENABLE);
373
374    return 0;
375}
376
377static const unsigned int jz_clk_main_divs[] = {
378    1, 2, 3, 4, 6, 8, 12, 16, 24, 32
379};
380static const unsigned int jz_clk_main_divs_inv[] = {
381    -1, 0, 1, 2, 3, -1, 4, -1, 5, -1, -1, -1, 6, -1, -1, -1,
382     7, -1, -1, -1, -1, -1, -1, -1, 8, -1, -1, -1, -1, -1, -1, -1,
383     9
384};
239385
240386static unsigned long jz_clk_main_round_rate(struct clk *clk, unsigned long rate)
241387{
...... 
290436    return 0;
291437}
292438
439static struct main_clk jz_clk_cpu;
440
441int clk_main_set_dividers(bool immediate, unsigned int cdiv, unsigned int hdiv,
442              unsigned int mdiv, unsigned int pdiv)
443{
444    unsigned int cdiv_enc, hdiv_enc, mdiv_enc, pdiv_enc;
445    unsigned int ctrl;
446    unsigned int tmp, wait;
447
448    if (cdiv >= ARRAY_SIZE(jz_clk_main_divs_inv) ||
449        hdiv >= ARRAY_SIZE(jz_clk_main_divs_inv) ||
450        mdiv >= ARRAY_SIZE(jz_clk_main_divs_inv) ||
451        pdiv >= ARRAY_SIZE(jz_clk_main_divs_inv))
452        return -EINVAL;
453    cdiv_enc = jz_clk_main_divs_inv[cdiv];
454    hdiv_enc = jz_clk_main_divs_inv[hdiv];
455    mdiv_enc = jz_clk_main_divs_inv[mdiv];
456    pdiv_enc = jz_clk_main_divs_inv[pdiv];
457    if (cdiv_enc == (unsigned int)-1 ||
458        hdiv_enc == (unsigned int)-1 ||
459        mdiv_enc == (unsigned int)-1 ||
460        pdiv_enc == (unsigned int)-1)
461        return -EINVAL;
462
463    ctrl = jz_clk_reg_read(JZ_REG_CLOCK_CTRL);
464    ctrl &= ~(JZ_CLOCK_CTRL_CHANGE_ENABLE |
465          JZ_CLOCK_CTRL_CDIV_MASK | JZ_CLOCK_CTRL_HDIV_MASK |
466          JZ_CLOCK_CTRL_MDIV_MASK | JZ_CLOCK_CTRL_PDIV_MASK);
467    if (immediate) ctrl |= JZ_CLOCK_CTRL_CHANGE_ENABLE;
468    ctrl |= (cdiv_enc << JZ_CLOCK_CTRL_CDIV_OFFSET) |
469        (hdiv_enc << JZ_CLOCK_CTRL_HDIV_OFFSET) |
470        (mdiv_enc << JZ_CLOCK_CTRL_MDIV_OFFSET) |
471        (pdiv_enc << JZ_CLOCK_CTRL_PDIV_OFFSET);
472
473    /* set dividers */
474    /* delay loops lifted from the old Ingenic cpufreq driver */
475    wait = ((clk_get_rate(&jz_clk_cpu.clk) / 1000000) * 500) / 1000;
476    __asm__ __volatile__(
477        ".set noreorder\n\t"
478        ".align 5\n"
479        "sw %2,0(%1)\n\t"
480        "li %0,0\n\t"
481        "1:\n\t"
482        "bne %0,%3,1b\n\t"
483        "addi %0, 1\n\t"
484        "nop\n\t"
485        "nop\n\t"
486        "nop\n\t"
487        "nop\n\t"
488        ".set reorder\n\t"
489        : "=r" (tmp)
490        : "r" (jz_clock_base + JZ_REG_CLOCK_CTRL), "r" (ctrl),
491          "r" (wait));
492
493    return 0;
494}
495EXPORT_SYMBOL_GPL(clk_main_set_dividers);
496
293497static struct clk_ops jz_clk_static_ops = {
294498    .get_rate = jz_clk_static_get_rate,
295499    .enable = jz_clk_enable_gating,
...... 
307511
308512static struct clk_ops jz_clk_pll_ops = {
309513    .get_rate = jz_clk_pll_get_rate,
514    .set_rate = jz_clk_pll_set_rate,
515    .round_rate = jz_clk_pll_round_rate,
310516};
311517
312518static struct clk jz_clk_pll = {
...... 
8971103    if (!jz_clock_base)
8981104        return -EBUSY;
8991105
1106    jz_emc_base = ioremap(JZ4740_EMC_BASE_ADDR, 0x100);
1107    if (!jz_emc_base)
1108        return -EBUSY;
1109
9001110    spin_lock_init(&jz_clock_lock);
9011111
9021112    jz_clk_ext.rate = jz4740_clock_bdata.ext_rate;
arch/mips/jz4740/clock.h
1717#define __MIPS_JZ4740_CLOCK_H__
1818
1919#include <linux/list.h>
20#include <linux/types.h>
2021
2122struct jz4740_clock_board_data {
2223    unsigned long ext_rate;
...... 
6364
6465int clk_is_enabled(struct clk *clk);
6566
67int clk_main_set_dividers(bool immediate, unsigned int cdiv, unsigned int hdiv,
68              unsigned int mdiv, unsigned int pdiv);
69
6670#ifdef CONFIG_DEBUG_FS
6771void jz4740_clock_debugfs_init(void);
6872void jz4740_clock_debugfs_add_clk(struct clk *clk);

Archive Download the corresponding diff file



interactive