uboot/arch/m68k/cpu/mcf532x/speed.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 *
   4 * (C) Copyright 2000-2003
   5 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
   6 *
   7 * Copyright (C) 2004-2008, 2012 Freescale Semiconductor, Inc.
   8 * TsiChung Liew (Tsi-Chung.Liew@freescale.com)
   9 */
  10
  11#include <common.h>
  12#include <clock_legacy.h>
  13#include <asm/global_data.h>
  14#include <asm/processor.h>
  15
  16#include <asm/immap.h>
  17#include <asm/io.h>
  18
  19DECLARE_GLOBAL_DATA_PTR;
  20
  21/* PLL min/max specifications */
  22#define MAX_FVCO        500000  /* KHz */
  23#define MAX_FSYS        80000   /* KHz */
  24#define MIN_FSYS        58333   /* KHz */
  25
  26#ifdef CONFIG_MCF5301x
  27#define FREF            20000   /* KHz */
  28#define MAX_MFD         63      /* Multiplier */
  29#define MIN_MFD         0       /* Multiplier */
  30#define USBDIV          8
  31
  32/* Low Power Divider specifications */
  33#define MIN_LPD         (0)     /* Divider (not encoded) */
  34#define MAX_LPD         (15)    /* Divider (not encoded) */
  35#define DEFAULT_LPD     (0)     /* Divider (not encoded) */
  36#endif
  37
  38#ifdef CONFIG_MCF532x
  39#define FREF            16000   /* KHz */
  40#define MAX_MFD         135     /* Multiplier */
  41#define MIN_MFD         88      /* Multiplier */
  42
  43/* Low Power Divider specifications */
  44#define MIN_LPD         (1 << 0)        /* Divider (not encoded) */
  45#define MAX_LPD         (1 << 15)       /* Divider (not encoded) */
  46#define DEFAULT_LPD     (1 << 1)        /* Divider (not encoded) */
  47#endif
  48
  49#define BUSDIV          6       /* Divider */
  50
  51/* Get the value of the current system clock */
  52int get_sys_clock(void)
  53{
  54        ccm_t *ccm = (ccm_t *)(MMAP_CCM);
  55        pll_t *pll = (pll_t *)(MMAP_PLL);
  56        int divider;
  57
  58        /* Test to see if device is in LIMP mode */
  59        if (in_be16(&ccm->misccr) & CCM_MISCCR_LIMP) {
  60                divider = in_be16(&ccm->cdr) & CCM_CDR_LPDIV(0xF);
  61#ifdef CONFIG_MCF5301x
  62                return (FREF / (3 * (1 << divider)));
  63#endif
  64#ifdef CONFIG_MCF532x
  65                return (FREF / (2 << divider));
  66#endif
  67        } else {
  68#ifdef CONFIG_MCF5301x
  69                u32 pfdr = (in_be32(&pll->pcr) & 0x3F) + 1;
  70                u32 refdiv = (1 << ((in_be32(&pll->pcr) & PLL_PCR_REFDIV(7)) >> 8));
  71                u32 busdiv = ((in_be32(&pll->pdr) & 0x00F0) >> 4) + 1;
  72
  73                return (((FREF * pfdr) / refdiv) / busdiv);
  74#endif
  75#ifdef CONFIG_MCF532x
  76                return (FREF * in_8(&pll->pfdr)) / (BUSDIV * 4);
  77#endif
  78        }
  79}
  80
  81/*
  82 * Initialize the Low Power Divider circuit
  83 *
  84 * Parameters:
  85 *  div     Desired system frequency divider
  86 *
  87 * Return Value:
  88 *  The resulting output system frequency
  89 */
  90int clock_limp(int div)
  91{
  92        ccm_t *ccm = (ccm_t *)(MMAP_CCM);
  93        u32 temp;
  94
  95        /* Check bounds of divider */
  96        if (div < MIN_LPD)
  97                div = MIN_LPD;
  98        if (div > MAX_LPD)
  99                div = MAX_LPD;
 100
 101        /* Save of the current value of the SSIDIV so we don't overwrite the value */
 102        temp = (in_be16(&ccm->cdr) & CCM_CDR_SSIDIV(0xFF));
 103
 104        /* Apply the divider to the system clock */
 105        out_be16(&ccm->cdr, CCM_CDR_LPDIV(div) | CCM_CDR_SSIDIV(temp));
 106
 107        setbits_be16(&ccm->misccr, CCM_MISCCR_LIMP);
 108
 109        return (FREF / (3 * (1 << div)));
 110}
 111
 112/* Exit low power LIMP mode */
 113int clock_exit_limp(void)
 114{
 115        ccm_t *ccm = (ccm_t *)(MMAP_CCM);
 116        int fout;
 117
 118        /* Exit LIMP mode */
 119        clrbits_be16(&ccm->misccr, CCM_MISCCR_LIMP);
 120
 121        /* Wait for PLL to lock */
 122        while (!(in_be16(&ccm->misccr) & CCM_MISCCR_PLL_LOCK))
 123                ;
 124
 125        fout = get_sys_clock();
 126
 127        return fout;
 128}
 129
 130/* Initialize the PLL
 131 *
 132 * Parameters:
 133 *  fref    PLL reference clock frequency in KHz
 134 *  fsys    Desired PLL output frequency in KHz
 135 *  flags   Operating parameters
 136 *
 137 * Return Value:
 138 *  The resulting output system frequency
 139 */
 140int clock_pll(int fsys, int flags)
 141{
 142#ifdef CONFIG_MCF532x
 143        u32 *sdram_workaround = (u32 *)(MMAP_SDRAM + 0x80);
 144#endif
 145        sdram_t *sdram = (sdram_t *)(MMAP_SDRAM);
 146        pll_t *pll = (pll_t *)(MMAP_PLL);
 147        int fref, temp, fout, mfd;
 148        u32 i;
 149
 150        fref = FREF;
 151
 152        if (fsys == 0) {
 153                /* Return current PLL output */
 154#ifdef CONFIG_MCF5301x
 155                u32 busdiv = ((in_be32(&pll->pdr) >> 4) & 0x0F) + 1;
 156                mfd = (in_be32(&pll->pcr) & 0x3F) + 1;
 157
 158                return (fref * mfd) / busdiv;
 159#endif
 160#ifdef CONFIG_MCF532x
 161                mfd = in_8(&pll->pfdr);
 162
 163                return (fref * mfd / (BUSDIV * 4));
 164#endif
 165        }
 166
 167        /* Check bounds of requested system clock */
 168        if (fsys > MAX_FSYS)
 169                fsys = MAX_FSYS;
 170
 171        if (fsys < MIN_FSYS)
 172                fsys = MIN_FSYS;
 173
 174        /*
 175         * Multiplying by 100 when calculating the temp value,
 176         * and then dividing by 100 to calculate the mfd allows
 177         * for exact values without needing to include floating
 178         * point libraries.
 179         */
 180        temp = (100 * fsys) / fref;
 181#ifdef CONFIG_MCF5301x
 182        mfd = (BUSDIV * temp) / 100;
 183
 184        /* Determine the output frequency for selected values */
 185        fout = ((fref * mfd) / BUSDIV);
 186#endif
 187#ifdef CONFIG_MCF532x
 188        mfd = (4 * BUSDIV * temp) / 100;
 189
 190        /* Determine the output frequency for selected values */
 191        fout = ((fref * mfd) / (BUSDIV * 4));
 192#endif
 193
 194/* must not tamper with SDRAMC if running from SDRAM */
 195#if !defined(CONFIG_MONITOR_IS_IN_RAM)
 196        /*
 197         * Check to see if the SDRAM has already been initialized.
 198         * If it has then the SDRAM needs to be put into self refresh
 199         * mode before reprogramming the PLL.
 200         */
 201        if (in_be32(&sdram->ctrl) & SDRAMC_SDCR_REF)
 202                clrbits_be32(&sdram->ctrl, SDRAMC_SDCR_CKE);
 203
 204        /*
 205         * Initialize the PLL to generate the new system clock frequency.
 206         * The device must be put into LIMP mode to reprogram the PLL.
 207         */
 208
 209        /* Enter LIMP mode */
 210        clock_limp(DEFAULT_LPD);
 211
 212#ifdef CONFIG_MCF5301x
 213        out_be32(&pll->pdr,
 214                PLL_PDR_OUTDIV1((BUSDIV / 3) - 1) |
 215                PLL_PDR_OUTDIV2(BUSDIV - 1)     |
 216                PLL_PDR_OUTDIV3((BUSDIV / 2) - 1) |
 217                PLL_PDR_OUTDIV4(USBDIV - 1));
 218
 219        clrbits_be32(&pll->pcr, ~PLL_PCR_FBDIV_UNMASK);
 220        setbits_be32(&pll->pcr, PLL_PCR_FBDIV(mfd - 1));
 221#endif
 222#ifdef CONFIG_MCF532x
 223        /* Reprogram PLL for desired fsys */
 224        out_8(&pll->podr,
 225                PLL_PODR_CPUDIV(BUSDIV / 3) | PLL_PODR_BUSDIV(BUSDIV));
 226
 227        out_8(&pll->pfdr, mfd);
 228#endif
 229
 230        /* Exit LIMP mode */
 231        clock_exit_limp();
 232
 233        /* Return the SDRAM to normal operation if it is in use. */
 234        if (in_be32(&sdram->ctrl) & SDRAMC_SDCR_REF)
 235                setbits_be32(&sdram->ctrl, SDRAMC_SDCR_CKE);
 236
 237#ifdef CONFIG_MCF532x
 238        /*
 239         * software workaround for SDRAM opeartion after exiting LIMP
 240         * mode errata
 241         */
 242        out_be32(sdram_workaround, CFG_SYS_SDRAM_BASE);
 243#endif
 244
 245        /* wait for DQS logic to relock */
 246        for (i = 0; i < 0x200; i++) ;
 247#endif /* !defined(CONFIG_MONITOR_IS_IN_RAM) */
 248
 249        return fout;
 250}
 251
 252/* get_clocks() fills in gd->cpu_clock and gd->bus_clk */
 253int get_clocks(void)
 254{
 255        gd->bus_clk = clock_pll(CFG_SYS_CLK / 1000, 0) * 1000;
 256        gd->cpu_clk = (gd->bus_clk * 3);
 257
 258#ifdef CONFIG_SYS_I2C_FSL
 259        gd->arch.i2c1_clk = gd->bus_clk;
 260#endif
 261
 262        return (0);
 263}
 264