Date:2010-08-29 08:11:00 (12 years 1 month ago)
Author:Maarten ter Huurne
Commit:babfe8f2ca61b7d8926906c403925393cf971350
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.35 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 (0x1f << JZ_CLOCK_CTRL_MDIV_OFFSET)
58#define JZ_CLOCK_CTRL_PDIV_MASK (0x1f << JZ_CLOCK_CTRL_PDIV_OFFSET)
59#define JZ_CLOCK_CTRL_HDIV_MASK (0x1f << JZ_CLOCK_CTRL_HDIV_OFFSET)
60#define JZ_CLOCK_CTRL_CDIV_MASK (0x1f << 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    return 0;
370}
371
372static const unsigned int jz_clk_main_divs[] = {
373    1, 2, 3, 4, 6, 8, 12, 16, 24, 32
374};
375static const unsigned int jz_clk_main_divs_inv[] = {
376    -1, 0, 1, 2, 3, -1, 4, -1, 5, -1, -1, -1, 6, -1, -1, -1,
377     7, -1, -1, -1, -1, -1, -1, -1, 8, -1, -1, -1, -1, -1, -1, -1,
378     9
379};
239380
240381static unsigned long jz_clk_main_round_rate(struct clk *clk, unsigned long rate)
241382{
...... 
290431    return 0;
291432}
292433
434static struct main_clk jz_clk_cpu;
435
436int clk_main_set_dividers(bool immediate, unsigned int cdiv, unsigned int hdiv,
437              unsigned int mdiv, unsigned int pdiv)
438{
439    unsigned int cdiv_enc, hdiv_enc, mdiv_enc, pdiv_enc;
440    unsigned int ctrl;
441    unsigned int tmp, wait;
442
443    if (cdiv >= ARRAY_SIZE(jz_clk_main_divs_inv) ||
444        hdiv >= ARRAY_SIZE(jz_clk_main_divs_inv) ||
445        mdiv >= ARRAY_SIZE(jz_clk_main_divs_inv) ||
446        pdiv >= ARRAY_SIZE(jz_clk_main_divs_inv))
447        return -EINVAL;
448    cdiv_enc = jz_clk_main_divs_inv[cdiv];
449    hdiv_enc = jz_clk_main_divs_inv[hdiv];
450    mdiv_enc = jz_clk_main_divs_inv[mdiv];
451    pdiv_enc = jz_clk_main_divs_inv[pdiv];
452    if (cdiv_enc == (unsigned int)-1 ||
453        hdiv_enc == (unsigned int)-1 ||
454        mdiv_enc == (unsigned int)-1 ||
455        pdiv_enc == (unsigned int)-1)
456        return -EINVAL;
457
458    ctrl = jz_clk_reg_read(JZ_REG_CLOCK_CTRL);
459    ctrl &= ~(JZ_CLOCK_CTRL_CHANGE_ENABLE |
460          JZ_CLOCK_CTRL_CDIV_MASK | JZ_CLOCK_CTRL_HDIV_MASK |
461          JZ_CLOCK_CTRL_MDIV_MASK | JZ_CLOCK_CTRL_PDIV_MASK);
462    if (immediate) ctrl |= JZ_CLOCK_CTRL_CHANGE_ENABLE;
463    ctrl |= (cdiv_enc << JZ_CLOCK_CTRL_CDIV_OFFSET) |
464        (hdiv_enc << JZ_CLOCK_CTRL_HDIV_OFFSET) |
465        (mdiv_enc << JZ_CLOCK_CTRL_MDIV_OFFSET) |
466        (pdiv_enc << JZ_CLOCK_CTRL_PDIV_OFFSET);
467
468    /* set dividers */
469    /* delay loops lifted from the old Ingenic cpufreq driver */
470    wait = ((clk_get_rate(&jz_clk_cpu.clk) / 1000000) * 500) / 1000;
471    __asm__ __volatile__(
472        ".set noreorder\n\t"
473        ".align 5\n"
474        "sw %2,0(%1)\n\t"
475        "li %0,0\n\t"
476        "1:\n\t"
477        "bne %0,%3,1b\n\t"
478        "addi %0, 1\n\t"
479        "nop\n\t"
480        "nop\n\t"
481        "nop\n\t"
482        "nop\n\t"
483        ".set reorder\n\t"
484        : "=r" (tmp)
485        : "r" (jz_clock_base + JZ_REG_CLOCK_CTRL), "r" (ctrl),
486          "r" (wait));
487
488    return 0;
489}
490EXPORT_SYMBOL_GPL(clk_main_set_dividers);
491
293492static struct clk_ops jz_clk_static_ops = {
294493    .get_rate = jz_clk_static_get_rate,
295494    .enable = jz_clk_enable_gating,
...... 
307506
308507static struct clk_ops jz_clk_pll_ops = {
309508    .get_rate = jz_clk_pll_get_rate,
509    .set_rate = jz_clk_pll_set_rate,
510    .round_rate = jz_clk_pll_round_rate,
310511};
311512
312513static struct clk jz_clk_pll = {
...... 
8971098    if (!jz_clock_base)
8981099        return -EBUSY;
8991100
1101    jz_emc_base = ioremap(CPHYSADDR(JZ4740_EMC_BASE_ADDR), 0x100);
1102    if (!jz_emc_base)
1103        return -EBUSY;
1104
9001105    spin_lock_init(&jz_clock_lock);
9011106
9021107    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