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 * See file CREDITS for list of people who contributed to this
   9 * project.
  10 *
  11 * This program is free software; you can redistribute it and/or
  12 * modify it under the terms of the GNU General Public License as
  13 * published by the Free Software Foundation; either version 2 of
  14 * the License, or (at your option) any later version.
  15 *
  16 * This program is distributed in the hope that it will be useful,
  17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19 * GNU General Public License for more details.
  20 *
  21 * You should have received a copy of the GNU General Public License
  22 * along with this program; if not, write to the Free Software
  23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  24 * MA 02111-1307 USA
  25 */
  26
  27/*
  28 * This source code is dual-licensed.  You may use it under the terms of the
  29 * GNU General Public License version 2, or under the license below.
  30 *
  31 * This source code has been made available to you by IBM on an AS-IS
  32 * basis.  Anyone receiving this source is licensed under IBM
  33 * copyrights to use it in any way he or she deems fit, including
  34 * copying it, modifying it, compiling it, and redistributing it either
  35 * with or without modifications.  No license under IBM patents or
  36 * patent applications is to be implied by the copyright license.
  37 *
  38 * Any user of this software should understand that IBM cannot provide
  39 * technical support for this software and will not be responsible for
  40 * any consequences resulting from the use of this software.
  41 *
  42 * Any person who transfers this source code or any derivative work
  43 * must include the IBM copyright notice, this paragraph, and the
  44 * preceding two paragraphs in the transferred software.
  45 *
  46 * COPYRIGHT   I B M   CORPORATION 1995
  47 * LICENSED MATERIAL  -  PROGRAM PROPERTY OF I B M
  48 */
  49
  50#include <common.h>
  51#include <commproc.h>
  52#include <asm/processor.h>
  53#include <asm/io.h>
  54#include <watchdog.h>
  55#include <asm/ppc4xx.h>
  56
  57DECLARE_GLOBAL_DATA_PTR;
  58
  59#if defined(CONFIG_405GP) || defined(CONFIG_405CR) || \
  60    defined(CONFIG_405EP) || defined(CONFIG_405EZ) || \
  61    defined(CONFIG_405EX) || defined(CONFIG_440)
  62
  63#if defined(CONFIG_440)
  64
  65#if defined(CONFIG_440GP)
  66#define CR0_MASK        0x3fff0000
  67#define CR0_EXTCLK_ENA  0x00600000
  68#define CR0_UDIV_POS    16
  69#define UDIV_SUBTRACT   1
  70#define UART0_SDR       CPC0_CR0
  71#define MFREG(a, d)     d = mfdcr(a)
  72#define MTREG(a, d)     mtdcr(a, d)
  73#else /* #if defined(CONFIG_440GP) */
  74/* all other 440 PPC's access clock divider via sdr register */
  75#define CR0_MASK        0xdfffffff
  76#define CR0_EXTCLK_ENA  0x00800000
  77#define CR0_UDIV_POS    0
  78#define UDIV_SUBTRACT   0
  79#define UART0_SDR       SDR0_UART0
  80#define UART1_SDR       SDR0_UART1
  81#if defined(CONFIG_440EP) || defined(CONFIG_440EPX) || \
  82    defined(CONFIG_440GR) || defined(CONFIG_440GRX) || \
  83    defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \
  84    defined(CONFIG_460EX) || defined(CONFIG_460GT)
  85#define UART2_SDR       SDR0_UART2
  86#endif
  87#if defined(CONFIG_440EP) || defined(CONFIG_440EPX) || \
  88    defined(CONFIG_440GR) || defined(CONFIG_440GRX) || \
  89    defined(CONFIG_460EX) || defined(CONFIG_460GT)
  90#define UART3_SDR       SDR0_UART3
  91#endif
  92#define MFREG(a, d)     mfsdr(a, d)
  93#define MTREG(a, d)     mtsdr(a, d)
  94#endif /* #if defined(CONFIG_440GP) */
  95#elif defined(CONFIG_405EP) || defined(CONFIG_405EZ)
  96#define UCR0_MASK       0x0000007f
  97#define UCR1_MASK       0x00007f00
  98#define UCR0_UDIV_POS   0
  99#define UCR1_UDIV_POS   8
 100#define UDIV_MAX        127
 101#elif defined(CONFIG_405EX)
 102#define MFREG(a, d)     mfsdr(a, d)
 103#define MTREG(a, d)     mtsdr(a, d)
 104#define CR0_MASK        0x000000ff
 105#define CR0_EXTCLK_ENA  0x00800000
 106#define CR0_UDIV_POS    0
 107#define UDIV_SUBTRACT   0
 108#define UART0_SDR       SDR0_UART0
 109#define UART1_SDR       SDR0_UART1
 110#else /* CONFIG_405GP || CONFIG_405CR */
 111#define CR0_MASK        0x00001fff
 112#define CR0_EXTCLK_ENA  0x000000c0
 113#define CR0_UDIV_POS    1
 114#define UDIV_MAX        32
 115#endif
 116
 117#if defined(CONFIG_405EP) && defined(CONFIG_SYS_EXT_SERIAL_CLOCK)
 118#error "External serial clock not supported on AMCC PPC405EP!"
 119#endif
 120
 121#if (defined(CONFIG_405EX) || defined(CONFIG_405EZ) ||  \
 122     defined(CONFIG_440)) && !defined(CONFIG_SYS_EXT_SERIAL_CLOCK)
 123/*
 124 * For some SoC's, the cpu clock is on divider chain A, UART on
 125 * divider chain B ... so cpu clock is irrelevant. Get the
 126 * "optimized" values that are subject to the 1/2 opb clock
 127 * constraint.
 128 */
 129static u16 serial_bdiv(int baudrate, u32 *udiv)
 130{
 131        sys_info_t sysinfo;
 132        u32 div;                /* total divisor udiv * bdiv */
 133        u32 umin;               /* minimum udiv */
 134        u16 diff;               /* smallest diff */
 135        u16 idiff;              /* current diff */
 136        u16 ibdiv;              /* current bdiv */
 137        u32 i;
 138        u32 est;                /* current estimate */
 139        u32 max;
 140#if defined(CONFIG_405EZ)
 141        u32 cpr_pllc;
 142        u32 plloutb;
 143        u32 reg;
 144#endif
 145
 146        get_sys_info(&sysinfo);
 147
 148#if defined(CONFIG_405EZ)
 149        /* check the pll feedback source */
 150        mfcpr(CPR0_PLLC, cpr_pllc);
 151        plloutb = ((CONFIG_SYS_CLK_FREQ * ((cpr_pllc & PLLC_SRC_MASK) ?
 152                                           sysinfo.pllFwdDivB : sysinfo.pllFwdDiv) *
 153                    sysinfo.pllFbkDiv) / sysinfo.pllFwdDivB);
 154        div = plloutb / (16 * baudrate); /* total divisor */
 155        umin = (plloutb / get_OPB_freq()) << 1; /* 2 x OPB divisor */
 156        max = 256;                      /* highest possible */
 157#else /* 405EZ */
 158        div = sysinfo.freqPLB / (16 * baudrate); /* total divisor */
 159        umin = sysinfo.pllOpbDiv << 1;  /* 2 x OPB divisor */
 160        max = 32;                       /* highest possible */
 161#endif /* 405EZ */
 162
 163        *udiv = diff = max;
 164
 165        /*
 166         * i is the test udiv value -- start with the largest
 167         * possible (max) to minimize serial clock and constrain
 168         * search to umin.
 169         */
 170        for (i = max; i > umin; i--) {
 171                ibdiv = div / i;
 172                est = i * ibdiv;
 173                idiff = (est > div) ? (est - div) : (div - est);
 174                if (idiff == 0) {
 175                        *udiv = i;
 176                        break;          /* can't do better */
 177                } else if (idiff < diff) {
 178                        *udiv = i;      /* best so far */
 179                        diff = idiff;   /* update lowest diff*/
 180                }
 181        }
 182
 183#if defined(CONFIG_405EZ)
 184        mfcpr(CPR0_PERD0, reg);
 185        reg &= ~0x0000ffff;
 186        reg |= ((*udiv - 0) << 8) | (*udiv - 0);
 187        mtcpr(CPR0_PERD0, reg);
 188#endif
 189
 190        return div / *udiv;
 191}
 192#endif /* #if (defined(CONFIG_405EP) ... */
 193
 194/*
 195 * This function returns the UART clock used by the common
 196 * NS16550 driver. Additionally the SoC internal divisors for
 197 * optimal UART baudrate are configured.
 198 */
 199int get_serial_clock(void)
 200{
 201        u32 clk;
 202        u32 udiv;
 203#if defined(CONFIG_405CR) || defined(CONFIG_405EP) || defined(CONFIG_405GP)
 204        u32 tmp;
 205#endif
 206#if !defined(CONFIG_405EZ)
 207        u32 reg;
 208#endif
 209#if !defined(CONFIG_SYS_EXT_SERIAL_CLOCK)
 210        PPC4xx_SYS_INFO sys_info;
 211#endif
 212
 213        /*
 214         * Programming of the internal divisors is SoC specific.
 215         * Let's handle this in some #ifdef's for the SoC's.
 216         */
 217
 218#if defined(CONFIG_405CR) || defined(CONFIG_405GP)
 219        tmp = 0;
 220        reg = mfdcr(CPC0_CR0) & ~CR0_MASK;
 221#ifdef CONFIG_SYS_EXT_SERIAL_CLOCK
 222        clk = CONFIG_SYS_EXT_SERIAL_CLOCK;
 223        udiv = 1;
 224        reg |= CR0_EXTCLK_ENA;
 225#else /* CONFIG_SYS_EXT_SERIAL_CLOCK */
 226        clk = gd->cpu_clk;
 227#ifdef CONFIG_SYS_405_UART_ERRATA_59
 228        udiv = 31;                      /* Errata 59: stuck at 31 */
 229#else /* CONFIG_SYS_405_UART_ERRATA_59 */
 230        tmp = CONFIG_SYS_BASE_BAUD * 16;
 231        udiv = (clk + tmp / 2) / tmp;
 232        if (udiv > UDIV_MAX)                    /* max. n bits for udiv */
 233                udiv = UDIV_MAX;
 234#endif /* CONFIG_SYS_405_UART_ERRATA_59 */
 235#endif /* CONFIG_SYS_EXT_SERIAL_CLOCK */
 236        reg |= (udiv - 1) << CR0_UDIV_POS;      /* set the UART divisor */
 237        mtdcr (CPC0_CR0, reg);
 238#ifdef CONFIG_SYS_EXT_SERIAL_CLOCK
 239        clk = CONFIG_SYS_EXT_SERIAL_CLOCK;
 240#else
 241        clk = CONFIG_SYS_BASE_BAUD * 16;
 242#endif
 243#endif /* CONFIG_405CR */
 244
 245#if defined(CONFIG_405EP)
 246        reg = mfdcr(CPC0_UCR) & ~(UCR0_MASK | UCR1_MASK);
 247        clk = gd->cpu_clk;
 248        tmp = CONFIG_SYS_BASE_BAUD * 16;
 249        udiv = (clk + tmp / 2) / tmp;
 250        if (udiv > UDIV_MAX)                    /* max. n bits for udiv */
 251                udiv = UDIV_MAX;
 252        reg |= udiv << UCR0_UDIV_POS;           /* set the UART divisor */
 253        reg |= udiv << UCR1_UDIV_POS;           /* set the UART divisor */
 254        mtdcr(CPC0_UCR, reg);
 255        clk = CONFIG_SYS_BASE_BAUD * 16;
 256#endif /* CONFIG_405EP */
 257
 258#if defined(CONFIG_405EX) || defined(CONFIG_440)
 259        MFREG(UART0_SDR, reg);
 260        reg &= ~CR0_MASK;
 261
 262#ifdef CONFIG_SYS_EXT_SERIAL_CLOCK
 263        reg |= CR0_EXTCLK_ENA;
 264        udiv = 1;
 265        clk = CONFIG_SYS_EXT_SERIAL_CLOCK;
 266#else /* CONFIG_SYS_EXT_SERIAL_CLOCK */
 267        clk = gd->baudrate * serial_bdiv(gd->baudrate, &udiv) * 16;
 268#endif /* CONFIG_SYS_EXT_SERIAL_CLOCK */
 269
 270        reg |= (udiv - UDIV_SUBTRACT) << CR0_UDIV_POS;  /* set the UART divisor */
 271
 272        /*
 273         * Configure input clock to baudrate generator for all
 274         * available serial ports here
 275         */
 276        MTREG(UART0_SDR, reg);
 277#if defined(UART1_SDR)
 278        MTREG(UART1_SDR, reg);
 279#endif
 280#if defined(UART2_SDR)
 281        MTREG(UART2_SDR, reg);
 282#endif
 283#if defined(UART3_SDR)
 284        MTREG(UART3_SDR, reg);
 285#endif
 286#endif /* CONFIG_405EX ... */
 287
 288#if defined(CONFIG_405EZ)
 289        clk = gd->baudrate * serial_bdiv(gd->baudrate, &udiv) * 16;
 290#endif /* CONFIG_405EZ */
 291
 292        /*
 293         * Correct UART frequency in bd-info struct now that
 294         * the UART divisor is available
 295         */
 296#ifdef CONFIG_SYS_EXT_SERIAL_CLOCK
 297        gd->uart_clk = CONFIG_SYS_EXT_SERIAL_CLOCK;
 298#else
 299        get_sys_info(&sys_info);
 300        gd->uart_clk = sys_info.freqUART / udiv;
 301#endif
 302
 303        return clk;
 304}
 305#endif  /* CONFIG_405GP || CONFIG_405CR */
 306