uboot/post/cpu/ppc4xx/uart.c
<<
>>
Prefs
   1/*
   2 * (C) Copyright 2007
   3 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
   4 *
   5 * Author: Igor Lisitsin <igor@emcraft.com>
   6 *
   7 * See file CREDITS for list of people who contributed to this
   8 * project.
   9 *
  10 * This program is free software; you can redistribute it and/or
  11 * modify it under the terms of the GNU General Public License as
  12 * published by the Free Software Foundation; either version 2 of
  13 * the License, or (at your option) any later version.
  14 *
  15 * This program is distributed in the hope that it will be useful,
  16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18 * GNU General Public License for more details.
  19 *
  20 * You should have received a copy of the GNU General Public License
  21 * along with this program; if not, write to the Free Software
  22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  23 * MA 02111-1307 USA
  24 */
  25
  26#include <common.h>
  27
  28/*
  29 * UART test
  30 *
  31 * The controllers are configured to loopback mode and several
  32 * characters are transmitted.
  33 */
  34
  35#include <post.h>
  36
  37#if CONFIG_POST & CONFIG_SYS_POST_UART
  38
  39/*
  40 * This table defines the UART's that should be tested and can
  41 * be overridden in the board config file
  42 */
  43#ifndef CONFIG_SYS_POST_UART_TABLE
  44#define CONFIG_SYS_POST_UART_TABLE      {UART0_BASE, UART1_BASE, UART2_BASE, UART3_BASE}
  45#endif
  46
  47#include <asm/processor.h>
  48#include <serial.h>
  49
  50#if defined(CONFIG_440)
  51#if defined(CONFIG_440EP) || defined(CONFIG_440GR) || \
  52    defined(CONFIG_440EPX) || defined(CONFIG_440GRX)
  53#define UART0_BASE  CONFIG_SYS_PERIPHERAL_BASE + 0x00000300
  54#define UART1_BASE  CONFIG_SYS_PERIPHERAL_BASE + 0x00000400
  55#define UART2_BASE  CONFIG_SYS_PERIPHERAL_BASE + 0x00000500
  56#define UART3_BASE  CONFIG_SYS_PERIPHERAL_BASE + 0x00000600
  57#else
  58#define UART0_BASE  CONFIG_SYS_PERIPHERAL_BASE + 0x00000200
  59#define UART1_BASE  CONFIG_SYS_PERIPHERAL_BASE + 0x00000300
  60#endif
  61
  62#if defined(CONFIG_440SP) || defined(CONFIG_440SPE)
  63#define UART2_BASE  CONFIG_SYS_PERIPHERAL_BASE + 0x00000600
  64#endif
  65
  66#if defined(CONFIG_440GP)
  67#define CR0_MASK        0x3fff0000
  68#define CR0_EXTCLK_ENA  0x00600000
  69#define CR0_UDIV_POS    16
  70#define UDIV_SUBTRACT   1
  71#define UART0_SDR       cntrl0
  72#define MFREG(a, d)     d = mfdcr(a)
  73#define MTREG(a, d)     mtdcr(a, d)
  74#else /* #if defined(CONFIG_440GP) */
  75/* all other 440 PPC's access clock divider via sdr register */
  76#define CR0_MASK        0xdfffffff
  77#define CR0_EXTCLK_ENA  0x00800000
  78#define CR0_UDIV_POS    0
  79#define UDIV_SUBTRACT   0
  80#define UART0_SDR       sdr_uart0
  81#define UART1_SDR       sdr_uart1
  82#if defined(CONFIG_440EP) || defined(CONFIG_440EPX) || \
  83    defined(CONFIG_440GR) || defined(CONFIG_440GRX) || \
  84    defined(CONFIG_440SP) || defined(CONFIG_440SPE)
  85#define UART2_SDR       sdr_uart2
  86#endif
  87#if defined(CONFIG_440EP) || defined(CONFIG_440EPX) || \
  88    defined(CONFIG_440GR) || defined(CONFIG_440GRX)
  89#define UART3_SDR       sdr_uart3
  90#endif
  91#define MFREG(a, d)     mfsdr(a, d)
  92#define MTREG(a, d)     mtsdr(a, d)
  93#endif /* #if defined(CONFIG_440GP) */
  94#elif defined(CONFIG_405EP) || defined(CONFIG_405EZ)
  95#define UART0_BASE      0xef600300
  96#define UART1_BASE      0xef600400
  97#define UCR0_MASK       0x0000007f
  98#define UCR1_MASK       0x00007f00
  99#define UCR0_UDIV_POS   0
 100#define UCR1_UDIV_POS   8
 101#define UDIV_MAX        127
 102#elif defined(CONFIG_405EX)
 103#define UART0_BASE      0xef600200
 104#define UART1_BASE      0xef600300
 105#define CR0_MASK        0x000000ff
 106#define CR0_EXTCLK_ENA  0x00800000
 107#define CR0_UDIV_POS    0
 108#define UDIV_SUBTRACT   0
 109#define UART0_SDR       sdr_uart0
 110#define UART1_SDR       sdr_uart1
 111#define MFREG(a, d)     mfsdr(a, d)
 112#define MTREG(a, d)     mtsdr(a, d)
 113#else /* CONFIG_405GP || CONFIG_405CR */
 114#define UART0_BASE      0xef600300
 115#define UART1_BASE      0xef600400
 116#define CR0_MASK        0x00001fff
 117#define CR0_EXTCLK_ENA  0x000000c0
 118#define CR0_UDIV_POS    1
 119#define UDIV_MAX        32
 120#endif
 121
 122#define UART_RBR    0x00
 123#define UART_THR    0x00
 124#define UART_IER    0x01
 125#define UART_IIR    0x02
 126#define UART_FCR    0x02
 127#define UART_LCR    0x03
 128#define UART_MCR    0x04
 129#define UART_LSR    0x05
 130#define UART_MSR    0x06
 131#define UART_SCR    0x07
 132#define UART_DLL    0x00
 133#define UART_DLM    0x01
 134
 135/*
 136 * Line Status Register.
 137 */
 138#define asyncLSRDataReady1            0x01
 139#define asyncLSROverrunError1         0x02
 140#define asyncLSRParityError1          0x04
 141#define asyncLSRFramingError1         0x08
 142#define asyncLSRBreakInterrupt1       0x10
 143#define asyncLSRTxHoldEmpty1          0x20
 144#define asyncLSRTxShiftEmpty1         0x40
 145#define asyncLSRRxFifoError1          0x80
 146
 147DECLARE_GLOBAL_DATA_PTR;
 148
 149#if defined(CONFIG_440) || defined(CONFIG_405EX)
 150#if !defined(CONFIG_SYS_EXT_SERIAL_CLOCK)
 151static void serial_divs (int baudrate, unsigned long *pudiv,
 152                         unsigned short *pbdiv)
 153{
 154        sys_info_t sysinfo;
 155        unsigned long div;              /* total divisor udiv * bdiv */
 156        unsigned long umin;             /* minimum udiv */
 157        unsigned short diff;            /* smallest diff */
 158        unsigned long udiv;             /* best udiv */
 159        unsigned short idiff;           /* current diff */
 160        unsigned short ibdiv;           /* current bdiv */
 161        unsigned long i;
 162        unsigned long est;              /* current estimate */
 163
 164        get_sys_info(&sysinfo);
 165
 166        udiv = 32;                      /* Assume lowest possible serial clk */
 167        div = sysinfo.freqPLB / (16 * baudrate); /* total divisor */
 168        umin = sysinfo.pllOpbDiv << 1;  /* 2 x OPB divisor */
 169        diff = 32;                      /* highest possible */
 170
 171        /* i is the test udiv value -- start with the largest
 172         * possible (32) to minimize serial clock and constrain
 173         * search to umin.
 174         */
 175        for (i = 32; i > umin; i--) {
 176                ibdiv = div / i;
 177                est = i * ibdiv;
 178                idiff = (est > div) ? (est-div) : (div-est);
 179                if (idiff == 0) {
 180                        udiv = i;
 181                        break;  /* can't do better */
 182                } else if (idiff < diff) {
 183                        udiv = i;       /* best so far */
 184                        diff = idiff;   /* update lowest diff*/
 185                }
 186        }
 187
 188        *pudiv = udiv;
 189        *pbdiv = div / udiv;
 190}
 191#endif
 192
 193static int uart_post_init (unsigned long dev_base)
 194{
 195        unsigned long reg = 0;
 196        unsigned long udiv;
 197        unsigned short bdiv;
 198        volatile char val;
 199#ifdef CONFIG_SYS_EXT_SERIAL_CLOCK
 200        unsigned long tmp;
 201#endif
 202        int i;
 203
 204        for (i = 0; i < 3500; i++) {
 205                if (in8 (dev_base + UART_LSR) & asyncLSRTxHoldEmpty1)
 206                        break;
 207                udelay (100);
 208        }
 209        MFREG(UART0_SDR, reg);
 210        reg &= ~CR0_MASK;
 211
 212#ifdef CONFIG_SYS_EXT_SERIAL_CLOCK
 213        reg |= CR0_EXTCLK_ENA;
 214        udiv = 1;
 215        tmp  = gd->baudrate * 16;
 216        bdiv = (CONFIG_SYS_EXT_SERIAL_CLOCK + tmp / 2) / tmp;
 217#else
 218        /* For 440, the cpu clock is on divider chain A, UART on divider
 219         * chain B ... so cpu clock is irrelevant. Get the "optimized"
 220         * values that are subject to the 1/2 opb clock constraint
 221         */
 222        serial_divs (gd->baudrate, &udiv, &bdiv);
 223#endif
 224
 225        reg |= (udiv - UDIV_SUBTRACT) << CR0_UDIV_POS;  /* set the UART divisor */
 226
 227        /*
 228         * Configure input clock to baudrate generator for all
 229         * available serial ports here
 230         */
 231        MTREG(UART0_SDR, reg);
 232#if defined(UART1_SDR)
 233        MTREG(UART1_SDR, reg);
 234#endif
 235#if defined(UART2_SDR)
 236        MTREG(UART2_SDR, reg);
 237#endif
 238#if defined(UART3_SDR)
 239        MTREG(UART3_SDR, reg);
 240#endif
 241
 242        out8(dev_base + UART_LCR, 0x80);        /* set DLAB bit */
 243        out8(dev_base + UART_DLL, bdiv);        /* set baudrate divisor */
 244        out8(dev_base + UART_DLM, bdiv >> 8);   /* set baudrate divisor */
 245        out8(dev_base + UART_LCR, 0x03);        /* clear DLAB; set 8 bits, no parity */
 246        out8(dev_base + UART_FCR, 0x00);        /* disable FIFO */
 247        out8(dev_base + UART_MCR, 0x10);        /* enable loopback mode */
 248        val = in8(dev_base + UART_LSR);         /* clear line status */
 249        val = in8(dev_base + UART_RBR);         /* read receive buffer */
 250        out8(dev_base + UART_SCR, 0x00);        /* set scratchpad */
 251        out8(dev_base + UART_IER, 0x00);        /* set interrupt enable reg */
 252
 253        return 0;
 254}
 255
 256#else /* CONFIG_440 */
 257
 258static int uart_post_init (unsigned long dev_base)
 259{
 260        unsigned long reg;
 261        unsigned long tmp;
 262        unsigned long clk;
 263        unsigned long udiv;
 264        unsigned short bdiv;
 265        volatile char val;
 266        int i;
 267
 268        for (i = 0; i < 3500; i++) {
 269                if (in8 (dev_base + UART_LSR) & asyncLSRTxHoldEmpty1)
 270                        break;
 271                udelay (100);
 272        }
 273
 274#if defined(CONFIG_405EZ)
 275        serial_divs(gd->baudrate, &udiv, &bdiv);
 276        clk = tmp = reg = 0;
 277#else
 278#ifdef CONFIG_405EP
 279        reg = mfdcr(cpc0_ucr) & ~(UCR0_MASK | UCR1_MASK);
 280        clk = gd->cpu_clk;
 281        tmp = CONFIG_SYS_BASE_BAUD * 16;
 282        udiv = (clk + tmp / 2) / tmp;
 283        if (udiv > UDIV_MAX)                    /* max. n bits for udiv */
 284                udiv = UDIV_MAX;
 285        reg |= (udiv) << UCR0_UDIV_POS;         /* set the UART divisor */
 286        reg |= (udiv) << UCR1_UDIV_POS;         /* set the UART divisor */
 287        mtdcr (cpc0_ucr, reg);
 288#else /* CONFIG_405EP */
 289        reg = mfdcr(cntrl0) & ~CR0_MASK;
 290#ifdef CONFIG_SYS_EXT_SERIAL_CLOCK
 291        clk = CONFIG_SYS_EXT_SERIAL_CLOCK;
 292        udiv = 1;
 293        reg |= CR0_EXTCLK_ENA;
 294#else
 295        clk = gd->cpu_clk;
 296#ifdef CONFIG_SYS_405_UART_ERRATA_59
 297        udiv = 31;                      /* Errata 59: stuck at 31 */
 298#else
 299        tmp = CONFIG_SYS_BASE_BAUD * 16;
 300        udiv = (clk + tmp / 2) / tmp;
 301        if (udiv > UDIV_MAX)                    /* max. n bits for udiv */
 302                udiv = UDIV_MAX;
 303#endif
 304#endif
 305        reg |= (udiv - 1) << CR0_UDIV_POS;      /* set the UART divisor */
 306        mtdcr (cntrl0, reg);
 307#endif /* CONFIG_405EP */
 308        tmp = gd->baudrate * udiv * 16;
 309        bdiv = (clk + tmp / 2) / tmp;
 310#endif /* CONFIG_405EZ */
 311
 312        out8(dev_base + UART_LCR, 0x80);        /* set DLAB bit */
 313        out8(dev_base + UART_DLL, bdiv);        /* set baudrate divisor */
 314        out8(dev_base + UART_DLM, bdiv >> 8);   /* set baudrate divisor */
 315        out8(dev_base + UART_LCR, 0x03);        /* clear DLAB; set 8 bits, no parity */
 316        out8(dev_base + UART_FCR, 0x00);        /* disable FIFO */
 317        out8(dev_base + UART_MCR, 0x10);        /* enable loopback mode */
 318        val = in8(dev_base + UART_LSR); /* clear line status */
 319        val = in8(dev_base + UART_RBR); /* read receive buffer */
 320        out8(dev_base + UART_SCR, 0x00);        /* set scratchpad */
 321        out8(dev_base + UART_IER, 0x00);        /* set interrupt enable reg */
 322
 323        return (0);
 324}
 325#endif /* CONFIG_440 */
 326
 327static void uart_post_putc (unsigned long dev_base, char c)
 328{
 329        int i;
 330
 331        out8 (dev_base + UART_THR, c);  /* put character out */
 332
 333        /* Wait for transfer completion */
 334        for (i = 0; i < 3500; i++) {
 335                if (in8 (dev_base + UART_LSR) & asyncLSRTxHoldEmpty1)
 336                        break;
 337                udelay (100);
 338        }
 339}
 340
 341static int uart_post_getc (unsigned long dev_base)
 342{
 343        int i;
 344
 345        /* Wait for character available */
 346        for (i = 0; i < 3500; i++) {
 347                if (in8 (dev_base + UART_LSR) & asyncLSRDataReady1)
 348                        break;
 349                udelay (100);
 350        }
 351        return 0xff & in8 (dev_base + UART_RBR);
 352}
 353
 354static int test_ctlr (unsigned long dev_base, int index)
 355{
 356        int res = -1;
 357        char test_str[] = "*** UART Test String ***\r\n";
 358        int i;
 359
 360        uart_post_init (dev_base);
 361
 362        for (i = 0; i < sizeof (test_str) - 1; i++) {
 363                uart_post_putc (dev_base, test_str[i]);
 364                if (uart_post_getc (dev_base) != test_str[i])
 365                        goto done;
 366        }
 367        res = 0;
 368done:
 369        if (res)
 370                post_log ("uart%d test failed\n", index);
 371
 372        return res;
 373}
 374
 375int uart_post_test (int flags)
 376{
 377        int i, res = 0;
 378        static unsigned long base[] = CONFIG_SYS_POST_UART_TABLE;
 379
 380        for (i = 0; i < sizeof (base) / sizeof (base[0]); i++) {
 381                if (test_ctlr (base[i], i))
 382                        res = -1;
 383        }
 384        serial_reinit_all ();
 385
 386        return res;
 387}
 388
 389#endif /* CONFIG_POST & CONFIG_SYS_POST_UART */
 390