uboot/arch/powerpc/cpu/ppc4xx/4xx_uart.c
<<
>>
Prefs
   1/*
   2 * (C) Copyright 2000-2006
   3 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
   4 *
   5 * (C) Copyright 2010
   6 * Stefan Roese, DENX Software Engineering, sr@denx.de.
   7 *
   8 * SPDX-License-Identifier:     GPL-2.0 IBM-pibs
   9 */
  10
  11#include <common.h>
  12#include <commproc.h>
  13#include <asm/processor.h>
  14#include <asm/io.h>
  15#include <watchdog.h>
  16#include <asm/ppc4xx.h>
  17
  18DECLARE_GLOBAL_DATA_PTR;
  19
  20#if defined(CONFIG_405GP) || \
  21    defined(CONFIG_405EP) || defined(CONFIG_405EZ) || \
  22    defined(CONFIG_405EX) || defined(CONFIG_440)
  23
  24#if defined(CONFIG_440)
  25
  26#if defined(CONFIG_440GP)
  27#define CR0_MASK        0x3fff0000
  28#define CR0_EXTCLK_ENA  0x00600000
  29#define CR0_UDIV_POS    16
  30#define UDIV_SUBTRACT   1
  31#define UART0_SDR       CPC0_CR0
  32#define MFREG(a, d)     d = mfdcr(a)
  33#define MTREG(a, d)     mtdcr(a, d)
  34#else /* #if defined(CONFIG_440GP) */
  35/* all other 440 PPC's access clock divider via sdr register */
  36#define CR0_MASK        0xdfffffff
  37#define CR0_EXTCLK_ENA  0x00800000
  38#define CR0_UDIV_POS    0
  39#define UDIV_SUBTRACT   0
  40#define UART0_SDR       SDR0_UART0
  41#define UART1_SDR       SDR0_UART1
  42#if defined(CONFIG_440EP) || defined(CONFIG_440EPX) || \
  43    defined(CONFIG_440GR) || defined(CONFIG_440GRX) || \
  44    defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \
  45    defined(CONFIG_460EX) || defined(CONFIG_460GT)
  46#define UART2_SDR       SDR0_UART2
  47#endif
  48#if defined(CONFIG_440EP) || defined(CONFIG_440EPX) || \
  49    defined(CONFIG_440GR) || defined(CONFIG_440GRX) || \
  50    defined(CONFIG_460EX) || defined(CONFIG_460GT)
  51#define UART3_SDR       SDR0_UART3
  52#endif
  53#define MFREG(a, d)     mfsdr(a, d)
  54#define MTREG(a, d)     mtsdr(a, d)
  55#endif /* #if defined(CONFIG_440GP) */
  56#elif defined(CONFIG_405EP) || defined(CONFIG_405EZ)
  57#define UCR0_MASK       0x0000007f
  58#define UCR1_MASK       0x00007f00
  59#define UCR0_UDIV_POS   0
  60#define UCR1_UDIV_POS   8
  61#define UDIV_MAX        127
  62#elif defined(CONFIG_405EX)
  63#define MFREG(a, d)     mfsdr(a, d)
  64#define MTREG(a, d)     mtsdr(a, d)
  65#define CR0_MASK        0x000000ff
  66#define CR0_EXTCLK_ENA  0x00800000
  67#define CR0_UDIV_POS    0
  68#define UDIV_SUBTRACT   0
  69#define UART0_SDR       SDR0_UART0
  70#define UART1_SDR       SDR0_UART1
  71#else /* CONFIG_405GP */
  72#define CR0_MASK        0x00001fff
  73#define CR0_EXTCLK_ENA  0x000000c0
  74#define CR0_UDIV_POS    1
  75#define UDIV_MAX        32
  76#endif
  77
  78#if defined(CONFIG_405EP) && defined(CONFIG_SYS_EXT_SERIAL_CLOCK)
  79#error "External serial clock not supported on AMCC PPC405EP!"
  80#endif
  81
  82#if (defined(CONFIG_405EX) || defined(CONFIG_405EZ) ||  \
  83     defined(CONFIG_440)) && !defined(CONFIG_SYS_EXT_SERIAL_CLOCK)
  84/*
  85 * For some SoC's, the cpu clock is on divider chain A, UART on
  86 * divider chain B ... so cpu clock is irrelevant. Get the
  87 * "optimized" values that are subject to the 1/2 opb clock
  88 * constraint.
  89 */
  90static u16 serial_bdiv(int baudrate, u32 *udiv)
  91{
  92        sys_info_t sysinfo;
  93        u32 div;                /* total divisor udiv * bdiv */
  94        u32 umin;               /* minimum udiv */
  95        u16 diff;               /* smallest diff */
  96        u16 idiff;              /* current diff */
  97        u16 ibdiv;              /* current bdiv */
  98        u32 i;
  99        u32 est;                /* current estimate */
 100        u32 max;
 101#if defined(CONFIG_405EZ)
 102        u32 cpr_pllc;
 103        u32 plloutb;
 104        u32 reg;
 105#endif
 106
 107        get_sys_info(&sysinfo);
 108
 109#if defined(CONFIG_405EZ)
 110        /* check the pll feedback source */
 111        mfcpr(CPR0_PLLC, cpr_pllc);
 112        plloutb = ((CONFIG_SYS_CLK_FREQ * ((cpr_pllc & PLLC_SRC_MASK) ?
 113                                           sysinfo.pllFwdDivB : sysinfo.pllFwdDiv) *
 114                    sysinfo.pllFbkDiv) / sysinfo.pllFwdDivB);
 115        div = plloutb / (16 * baudrate); /* total divisor */
 116        umin = (plloutb / get_OPB_freq()) << 1; /* 2 x OPB divisor */
 117        max = 256;                      /* highest possible */
 118#else /* 405EZ */
 119        div = sysinfo.freqPLB / (16 * baudrate); /* total divisor */
 120        umin = sysinfo.pllOpbDiv << 1;  /* 2 x OPB divisor */
 121        max = 32;                       /* highest possible */
 122#endif /* 405EZ */
 123
 124        *udiv = diff = max;
 125
 126        /*
 127         * i is the test udiv value -- start with the largest
 128         * possible (max) to minimize serial clock and constrain
 129         * search to umin.
 130         */
 131        for (i = max; i > umin; i--) {
 132                ibdiv = div / i;
 133                est = i * ibdiv;
 134                idiff = (est > div) ? (est - div) : (div - est);
 135                if (idiff == 0) {
 136                        *udiv = i;
 137                        break;          /* can't do better */
 138                } else if (idiff < diff) {
 139                        *udiv = i;      /* best so far */
 140                        diff = idiff;   /* update lowest diff*/
 141                }
 142        }
 143
 144#if defined(CONFIG_405EZ)
 145        mfcpr(CPR0_PERD0, reg);
 146        reg &= ~0x0000ffff;
 147        reg |= ((*udiv - 0) << 8) | (*udiv - 0);
 148        mtcpr(CPR0_PERD0, reg);
 149#endif
 150
 151        return div / *udiv;
 152}
 153#endif /* #if (defined(CONFIG_405EP) ... */
 154
 155/*
 156 * This function returns the UART clock used by the common
 157 * NS16550 driver. Additionally the SoC internal divisors for
 158 * optimal UART baudrate are configured.
 159 */
 160int get_serial_clock(void)
 161{
 162        u32 clk;
 163        u32 udiv;
 164#if !defined(CONFIG_405EZ)
 165        u32 reg;
 166#endif
 167#if !defined(CONFIG_SYS_EXT_SERIAL_CLOCK)
 168        PPC4xx_SYS_INFO sys_info;
 169#endif
 170
 171        /*
 172         * Programming of the internal divisors is SoC specific.
 173         * Let's handle this in some #ifdef's for the SoC's.
 174         */
 175
 176#if defined(CONFIG_405GP)
 177        reg = mfdcr(CPC0_CR0) & ~CR0_MASK;
 178#ifdef CONFIG_SYS_EXT_SERIAL_CLOCK
 179        clk = CONFIG_SYS_EXT_SERIAL_CLOCK;
 180        udiv = 1;
 181        reg |= CR0_EXTCLK_ENA;
 182#else /* CONFIG_SYS_EXT_SERIAL_CLOCK */
 183        clk = gd->cpu_clk;
 184#ifdef CONFIG_SYS_405_UART_ERRATA_59
 185        udiv = 31;                      /* Errata 59: stuck at 31 */
 186#else /* CONFIG_SYS_405_UART_ERRATA_59 */
 187        {
 188                u32 tmp = CONFIG_SYS_BASE_BAUD * 16;
 189
 190                udiv = (clk + tmp / 2) / tmp;
 191        }
 192        if (udiv > UDIV_MAX)                    /* max. n bits for udiv */
 193                udiv = UDIV_MAX;
 194#endif /* CONFIG_SYS_405_UART_ERRATA_59 */
 195#endif /* CONFIG_SYS_EXT_SERIAL_CLOCK */
 196        reg |= (udiv - 1) << CR0_UDIV_POS;      /* set the UART divisor */
 197        mtdcr (CPC0_CR0, reg);
 198#ifdef CONFIG_SYS_EXT_SERIAL_CLOCK
 199        clk = CONFIG_SYS_EXT_SERIAL_CLOCK;
 200#else
 201        clk = CONFIG_SYS_BASE_BAUD * 16;
 202#endif
 203#endif
 204
 205#if defined(CONFIG_405EP)
 206        {
 207                u32 tmp = CONFIG_SYS_BASE_BAUD * 16;
 208
 209                reg = mfdcr(CPC0_UCR) & ~(UCR0_MASK | UCR1_MASK);
 210                clk = gd->cpu_clk;
 211                udiv = (clk + tmp / 2) / tmp;
 212                if (udiv > UDIV_MAX)                    /* max. n bits for udiv */
 213                        udiv = UDIV_MAX;
 214        }
 215        reg |= udiv << UCR0_UDIV_POS;           /* set the UART divisor */
 216        reg |= udiv << UCR1_UDIV_POS;           /* set the UART divisor */
 217        mtdcr(CPC0_UCR, reg);
 218        clk = CONFIG_SYS_BASE_BAUD * 16;
 219#endif /* CONFIG_405EP */
 220
 221#if defined(CONFIG_405EX) || defined(CONFIG_440)
 222        MFREG(UART0_SDR, reg);
 223        reg &= ~CR0_MASK;
 224
 225#ifdef CONFIG_SYS_EXT_SERIAL_CLOCK
 226        reg |= CR0_EXTCLK_ENA;
 227        udiv = 1;
 228        clk = CONFIG_SYS_EXT_SERIAL_CLOCK;
 229#else /* CONFIG_SYS_EXT_SERIAL_CLOCK */
 230        clk = gd->baudrate * serial_bdiv(gd->baudrate, &udiv) * 16;
 231#endif /* CONFIG_SYS_EXT_SERIAL_CLOCK */
 232
 233        reg |= (udiv - UDIV_SUBTRACT) << CR0_UDIV_POS;  /* set the UART divisor */
 234
 235        /*
 236         * Configure input clock to baudrate generator for all
 237         * available serial ports here
 238         */
 239        MTREG(UART0_SDR, reg);
 240#if defined(UART1_SDR)
 241        MTREG(UART1_SDR, reg);
 242#endif
 243#if defined(UART2_SDR)
 244        MTREG(UART2_SDR, reg);
 245#endif
 246#if defined(UART3_SDR)
 247        MTREG(UART3_SDR, reg);
 248#endif
 249#endif /* CONFIG_405EX ... */
 250
 251#if defined(CONFIG_405EZ)
 252        clk = gd->baudrate * serial_bdiv(gd->baudrate, &udiv) * 16;
 253#endif /* CONFIG_405EZ */
 254
 255        /*
 256         * Correct UART frequency in bd-info struct now that
 257         * the UART divisor is available
 258         */
 259#ifdef CONFIG_SYS_EXT_SERIAL_CLOCK
 260        gd->arch.uart_clk = CONFIG_SYS_EXT_SERIAL_CLOCK;
 261#else
 262        get_sys_info(&sys_info);
 263        gd->arch.uart_clk = sys_info.freqUART / udiv;
 264#endif
 265
 266        return clk;
 267}
 268#endif  /* CONFIG_405GP */
 269