blob: c02058f79bba253ad9cfa6b90155d3f47ac6dfde [file] [log] [blame]
wdenkfe8c2802002-11-03 00:38:21 +00001/*
Wolfgang Denkc80857e2006-07-21 11:56:05 +02002 * (C) Copyright 2000-2006
wdenkfe8c2802002-11-03 00:38:21 +00003 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4 *
Stefan Roese3ddce572010-09-20 16:05:31 +02005 * (C) Copyright 2010
6 * Stefan Roese, DENX Software Engineering, sr@denx.de.
7 *
Wolfgang Denk815c9672013-09-17 11:24:06 +02008 * SPDX-License-Identifier: GPL-2.0 IBM-pibs
wdenkfe8c2802002-11-03 00:38:21 +00009 */
Stefan Roese1a471152007-10-22 16:22:40 +020010
wdenkfe8c2802002-11-03 00:38:21 +000011#include <common.h>
12#include <commproc.h>
13#include <asm/processor.h>
Stefan Roese1a471152007-10-22 16:22:40 +020014#include <asm/io.h>
wdenkfe8c2802002-11-03 00:38:21 +000015#include <watchdog.h>
Stefan Roese247e9d72010-09-09 19:18:00 +020016#include <asm/ppc4xx.h>
wdenkfe8c2802002-11-03 00:38:21 +000017
Wolfgang Denk6405a152006-03-31 18:32:53 +020018DECLARE_GLOBAL_DATA_PTR;
19
Matthias Fuchse54a67f2013-08-07 12:10:38 +020020#if defined(CONFIG_405GP) || \
Stefan Roese17ffbc82007-03-21 13:38:59 +010021 defined(CONFIG_405EP) || defined(CONFIG_405EZ) || \
Stefan Roese153b3e22007-10-05 17:10:59 +020022 defined(CONFIG_405EX) || defined(CONFIG_440)
wdenkfe8c2802002-11-03 00:38:21 +000023
24#if defined(CONFIG_440)
Stefan Roesebdd13d12008-03-11 15:05:26 +010025
Stefan Roese42fbddd2006-09-07 11:51:23 +020026#if defined(CONFIG_440GP)
wdenkfe8c2802002-11-03 00:38:21 +000027#define CR0_MASK 0x3fff0000
28#define CR0_EXTCLK_ENA 0x00600000
29#define CR0_UDIV_POS 16
Stefan Roese42fbddd2006-09-07 11:51:23 +020030#define UDIV_SUBTRACT 1
Stefan Roese918010a2009-09-09 16:25:29 +020031#define UART0_SDR CPC0_CR0
Stefan Roese42fbddd2006-09-07 11:51:23 +020032#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
Stefan Roese918010a2009-09-09 16:25:29 +020040#define UART0_SDR SDR0_UART0
41#define UART1_SDR SDR0_UART1
Stefan Roese422853e2008-06-06 16:10:41 +020042#if defined(CONFIG_440EP) || defined(CONFIG_440EPX) || \
43 defined(CONFIG_440GR) || defined(CONFIG_440GRX) || \
44 defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \
Stefan Roesebdd13d12008-03-11 15:05:26 +010045 defined(CONFIG_460EX) || defined(CONFIG_460GT)
Stefan Roese918010a2009-09-09 16:25:29 +020046#define UART2_SDR SDR0_UART2
Stefan Roese42fbddd2006-09-07 11:51:23 +020047#endif
Stefan Roese422853e2008-06-06 16:10:41 +020048#if defined(CONFIG_440EP) || defined(CONFIG_440EPX) || \
49 defined(CONFIG_440GR) || defined(CONFIG_440GRX) || \
Stefan Roesebdd13d12008-03-11 15:05:26 +010050 defined(CONFIG_460EX) || defined(CONFIG_460GT)
Stefan Roese918010a2009-09-09 16:25:29 +020051#define UART3_SDR SDR0_UART3
Stefan Roese42fbddd2006-09-07 11:51:23 +020052#endif
53#define MFREG(a, d) mfsdr(a, d)
54#define MTREG(a, d) mtsdr(a, d)
55#endif /* #if defined(CONFIG_440GP) */
Stefan Roese17ffbc82007-03-21 13:38:59 +010056#elif defined(CONFIG_405EP) || defined(CONFIG_405EZ)
stroese937d6672003-05-23 11:25:57 +000057#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
Stefan Roese153b3e22007-10-05 17:10:59 +020062#elif defined(CONFIG_405EX)
Stefan Roese3ddce572010-09-20 16:05:31 +020063#define MFREG(a, d) mfsdr(a, d)
64#define MTREG(a, d) mtsdr(a, d)
Stefan Roese153b3e22007-10-05 17:10:59 +020065#define CR0_MASK 0x000000ff
66#define CR0_EXTCLK_ENA 0x00800000
67#define CR0_UDIV_POS 0
68#define UDIV_SUBTRACT 0
Stefan Roese918010a2009-09-09 16:25:29 +020069#define UART0_SDR SDR0_UART0
70#define UART1_SDR SDR0_UART1
Matthias Fuchse54a67f2013-08-07 12:10:38 +020071#else /* CONFIG_405GP */
wdenkfe8c2802002-11-03 00:38:21 +000072#define CR0_MASK 0x00001fff
stroese85d0fec2003-02-17 16:06:06 +000073#define CR0_EXTCLK_ENA 0x000000c0
wdenkfe8c2802002-11-03 00:38:21 +000074#define CR0_UDIV_POS 1
stroese937d6672003-05-23 11:25:57 +000075#define UDIV_MAX 32
76#endif
77
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +020078#if defined(CONFIG_405EP) && defined(CONFIG_SYS_EXT_SERIAL_CLOCK)
Wolfgang Denk0ee70772005-09-23 11:05:55 +020079#error "External serial clock not supported on AMCC PPC405EP!"
wdenkfe8c2802002-11-03 00:38:21 +000080#endif
81
Stefan Roese3ddce572010-09-20 16:05:31 +020082#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)
wdenkfe8c2802002-11-03 00:38:21 +000091{
Stefan Roese17ffbc82007-03-21 13:38:59 +010092 sys_info_t sysinfo;
Stefan Roese3ddce572010-09-20 16:05:31 +020093 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
wdenkfe8c2802002-11-03 00:38:21 +0000106
Stefan Roese17ffbc82007-03-21 13:38:59 +0100107 get_sys_info(&sysinfo);
wdenkfe8c2802002-11-03 00:38:21 +0000108
Stefan Roese3ddce572010-09-20 16:05:31 +0200109#if defined(CONFIG_405EZ)
Stefan Roese87476ba2007-08-13 09:05:33 +0200110 /* check the pll feedback source */
Stefan Roese918010a2009-09-09 16:25:29 +0200111 mfcpr(CPR0_PLLC, cpr_pllc);
Stefan Roese3dae28e2007-08-14 15:03:17 +0200112 plloutb = ((CONFIG_SYS_CLK_FREQ * ((cpr_pllc & PLLC_SRC_MASK) ?
Stefan Roese1a471152007-10-22 16:22:40 +0200113 sysinfo.pllFwdDivB : sysinfo.pllFwdDiv) *
114 sysinfo.pllFbkDiv) / sysinfo.pllFwdDivB);
Stefan Roese17ffbc82007-03-21 13:38:59 +0100115 div = plloutb / (16 * baudrate); /* total divisor */
116 umin = (plloutb / get_OPB_freq()) << 1; /* 2 x OPB divisor */
Stefan Roese3ddce572010-09-20 16:05:31 +0200117 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;
Stefan Roese17ffbc82007-03-21 13:38:59 +0100125
Stefan Roese3ddce572010-09-20 16:05:31 +0200126 /*
127 * i is the test udiv value -- start with the largest
128 * possible (max) to minimize serial clock and constrain
Stefan Roese17ffbc82007-03-21 13:38:59 +0100129 * search to umin.
130 */
Stefan Roese3ddce572010-09-20 16:05:31 +0200131 for (i = max; i > umin; i--) {
Stefan Roese17ffbc82007-03-21 13:38:59 +0100132 ibdiv = div / i;
133 est = i * ibdiv;
Stefan Roese3ddce572010-09-20 16:05:31 +0200134 idiff = (est > div) ? (est - div) : (div - est);
Stefan Roese17ffbc82007-03-21 13:38:59 +0100135 if (idiff == 0) {
Stefan Roese3ddce572010-09-20 16:05:31 +0200136 *udiv = i;
137 break; /* can't do better */
Stefan Roese17ffbc82007-03-21 13:38:59 +0100138 } else if (idiff < diff) {
Stefan Roese3ddce572010-09-20 16:05:31 +0200139 *udiv = i; /* best so far */
140 diff = idiff; /* update lowest diff*/
Stefan Roese17ffbc82007-03-21 13:38:59 +0100141 }
142 }
143
Stefan Roese3ddce572010-09-20 16:05:31 +0200144#if defined(CONFIG_405EZ)
Stefan Roese8cb251a2010-09-12 06:21:37 +0200145 mfcpr(CPR0_PERD0, reg);
Stefan Roese17ffbc82007-03-21 13:38:59 +0100146 reg &= ~0x0000ffff;
Stefan Roese3ddce572010-09-20 16:05:31 +0200147 reg |= ((*udiv - 0) << 8) | (*udiv - 0);
Stefan Roese8cb251a2010-09-12 06:21:37 +0200148 mtcpr(CPR0_PERD0, reg);
Stefan Roese3ddce572010-09-20 16:05:31 +0200149#endif
150
151 return div / *udiv;
wdenkfe8c2802002-11-03 00:38:21 +0000152}
Stefan Roese3ddce572010-09-20 16:05:31 +0200153#endif /* #if (defined(CONFIG_405EP) ... */
wdenkfe8c2802002-11-03 00:38:21 +0000154
wdenkfe8c2802002-11-03 00:38:21 +0000155/*
Stefan Roese3ddce572010-09-20 16:05:31 +0200156 * 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.
wdenkfe8c2802002-11-03 00:38:21 +0000159 */
Stefan Roese3ddce572010-09-20 16:05:31 +0200160int get_serial_clock(void)
wdenkfe8c2802002-11-03 00:38:21 +0000161{
Stefan Roese3ddce572010-09-20 16:05:31 +0200162 u32 clk;
163 u32 udiv;
Stefan Roese3ddce572010-09-20 16:05:31 +0200164#if !defined(CONFIG_405EZ)
165 u32 reg;
wdenk96c7a8c2005-01-09 22:28:56 +0000166#endif
Stefan Roese3ddce572010-09-20 16:05:31 +0200167#if !defined(CONFIG_SYS_EXT_SERIAL_CLOCK)
168 PPC4xx_SYS_INFO sys_info;
Stefan Roese153b3e22007-10-05 17:10:59 +0200169#endif
Stefan Roese153b3e22007-10-05 17:10:59 +0200170
171 /*
Stefan Roese3ddce572010-09-20 16:05:31 +0200172 * Programming of the internal divisors is SoC specific.
173 * Let's handle this in some #ifdef's for the SoC's.
Stefan Roese153b3e22007-10-05 17:10:59 +0200174 */
Stefan Roese153b3e22007-10-05 17:10:59 +0200175
Matthias Fuchse54a67f2013-08-07 12:10:38 +0200176#if defined(CONFIG_405GP)
Stefan Roese918010a2009-09-09 16:25:29 +0200177 reg = mfdcr(CPC0_CR0) & ~CR0_MASK;
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200178#ifdef CONFIG_SYS_EXT_SERIAL_CLOCK
179 clk = CONFIG_SYS_EXT_SERIAL_CLOCK;
wdenkfe8c2802002-11-03 00:38:21 +0000180 udiv = 1;
181 reg |= CR0_EXTCLK_ENA;
Stefan Roese3ddce572010-09-20 16:05:31 +0200182#else /* CONFIG_SYS_EXT_SERIAL_CLOCK */
wdenkfe8c2802002-11-03 00:38:21 +0000183 clk = gd->cpu_clk;
Jean-Christophe PLAGNIOL-VILLARD03836942008-10-16 15:01:15 +0200184#ifdef CONFIG_SYS_405_UART_ERRATA_59
wdenkfe8c2802002-11-03 00:38:21 +0000185 udiv = 31; /* Errata 59: stuck at 31 */
Stefan Roese3ddce572010-09-20 16:05:31 +0200186#else /* CONFIG_SYS_405_UART_ERRATA_59 */
Wolfgang Denkdf592922011-10-29 09:37:52 +0000187 {
188 u32 tmp = CONFIG_SYS_BASE_BAUD * 16;
189
190 udiv = (clk + tmp / 2) / tmp;
191 }
stroese937d6672003-05-23 11:25:57 +0000192 if (udiv > UDIV_MAX) /* max. n bits for udiv */
193 udiv = UDIV_MAX;
Stefan Roese3ddce572010-09-20 16:05:31 +0200194#endif /* CONFIG_SYS_405_UART_ERRATA_59 */
195#endif /* CONFIG_SYS_EXT_SERIAL_CLOCK */
wdenkfe8c2802002-11-03 00:38:21 +0000196 reg |= (udiv - 1) << CR0_UDIV_POS; /* set the UART divisor */
Stefan Roese918010a2009-09-09 16:25:29 +0200197 mtdcr (CPC0_CR0, reg);
Stefan Roese3ddce572010-09-20 16:05:31 +0200198#ifdef CONFIG_SYS_EXT_SERIAL_CLOCK
199 clk = CONFIG_SYS_EXT_SERIAL_CLOCK;
wdenkfe8c2802002-11-03 00:38:21 +0000200#else
Stefan Roese3ddce572010-09-20 16:05:31 +0200201 clk = CONFIG_SYS_BASE_BAUD * 16;
wdenkfe8c2802002-11-03 00:38:21 +0000202#endif
Matthias Fuchse54a67f2013-08-07 12:10:38 +0200203#endif
wdenkfe8c2802002-11-03 00:38:21 +0000204
Stefan Roese3ddce572010-09-20 16:05:31 +0200205#if defined(CONFIG_405EP)
Wolfgang Denkdf592922011-10-29 09:37:52 +0000206 {
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 }
Stefan Roese3ddce572010-09-20 16:05:31 +0200215 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 */
wdenkfe8c2802002-11-03 00:38:21 +0000220
Stefan Roese3ddce572010-09-20 16:05:31 +0200221#if defined(CONFIG_405EX) || defined(CONFIG_440)
222 MFREG(UART0_SDR, reg);
223 reg &= ~CR0_MASK;
wdenkfe8c2802002-11-03 00:38:21 +0000224
Stefan Roese3ddce572010-09-20 16:05:31 +0200225#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 */
wdenkfe8c2802002-11-03 00:38:21 +0000232
Stefan Roese3ddce572010-09-20 16:05:31 +0200233 reg |= (udiv - UDIV_SUBTRACT) << CR0_UDIV_POS; /* set the UART divisor */
wdenkfe8c2802002-11-03 00:38:21 +0000234
wdenkfe8c2802002-11-03 00:38:21 +0000235 /*
Stefan Roese3ddce572010-09-20 16:05:31 +0200236 * Configure input clock to baudrate generator for all
237 * available serial ports here
wdenkfe8c2802002-11-03 00:38:21 +0000238 */
Stefan Roese3ddce572010-09-20 16:05:31 +0200239 MTREG(UART0_SDR, reg);
240#if defined(UART1_SDR)
241 MTREG(UART1_SDR, reg);
Jon Loeliger07efe2a2007-07-10 10:27:39 -0500242#endif
Stefan Roese3ddce572010-09-20 16:05:31 +0200243#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 ... */
wdenk96c7a8c2005-01-09 22:28:56 +0000250
Stefan Roese3ddce572010-09-20 16:05:31 +0200251#if defined(CONFIG_405EZ)
252 clk = gd->baudrate * serial_bdiv(gd->baudrate, &udiv) * 16;
253#endif /* CONFIG_405EZ */
wdenk96c7a8c2005-01-09 22:28:56 +0000254
Stefan Roese3ddce572010-09-20 16:05:31 +0200255 /*
256 * Correct UART frequency in bd-info struct now that
257 * the UART divisor is available
258 */
259#ifdef CONFIG_SYS_EXT_SERIAL_CLOCK
Simon Glass004cc852012-12-13 20:48:59 +0000260 gd->arch.uart_clk = CONFIG_SYS_EXT_SERIAL_CLOCK;
Stefan Roese1a471152007-10-22 16:22:40 +0200261#else
Stefan Roese3ddce572010-09-20 16:05:31 +0200262 get_sys_info(&sys_info);
Simon Glass004cc852012-12-13 20:48:59 +0000263 gd->arch.uart_clk = sys_info.freqUART / udiv;
Stefan Roese3ddce572010-09-20 16:05:31 +0200264#endif
Stefan Roese1a471152007-10-22 16:22:40 +0200265
Stefan Roese3ddce572010-09-20 16:05:31 +0200266 return clk;
Stefan Roese1a471152007-10-22 16:22:40 +0200267}
Matthias Fuchse54a67f2013-08-07 12:10:38 +0200268#endif /* CONFIG_405GP */