blob: 50c28a0d39f07f682e9a005a101c3ac6ce1eb773 [file] [log] [blame]
wdenkfe8c2802002-11-03 00:38:21 +00001/*
Wolfgang Denkb9365a22006-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 Roese550650d2010-09-20 16:05:31 +02005 * (C) Copyright 2010
6 * Stefan Roese, DENX Software Engineering, sr@denx.de.
7 *
Wolfgang Denk46263f22013-07-28 22:12:45 +02008 * SPDX-License-Identifier: GPL-2.0 ibm-pibs
wdenkfe8c2802002-11-03 00:38:21 +00009 */
Stefan Roese3248f632007-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 Roese3248f632007-10-22 16:22:40 +020014#include <asm/io.h>
wdenkfe8c2802002-11-03 00:38:21 +000015#include <watchdog.h>
Stefan Roeseb36df562010-09-09 19:18:00 +020016#include <asm/ppc4xx.h>
wdenkfe8c2802002-11-03 00:38:21 +000017
Wolfgang Denkd87080b2006-03-31 18:32:53 +020018DECLARE_GLOBAL_DATA_PTR;
19
Matthias Fuchs3fb85882013-08-07 12:10:38 +020020#if defined(CONFIG_405GP) || \
Stefan Roesee01bd212007-03-21 13:38:59 +010021 defined(CONFIG_405EP) || defined(CONFIG_405EZ) || \
Stefan Roesedbbd1252007-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 Roese6e7fb6e2005-11-29 18:18:21 +010025
Stefan Roese887e2ec2006-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 Roese887e2ec2006-09-07 11:51:23 +020030#define UDIV_SUBTRACT 1
Stefan Roesed1c3b272009-09-09 16:25:29 +020031#define UART0_SDR CPC0_CR0
Stefan Roese887e2ec2006-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 Roesed1c3b272009-09-09 16:25:29 +020040#define UART0_SDR SDR0_UART0
41#define UART1_SDR SDR0_UART1
Stefan Roeseb2815f72008-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 Roese8ac41e32008-03-11 15:05:26 +010045 defined(CONFIG_460EX) || defined(CONFIG_460GT)
Stefan Roesed1c3b272009-09-09 16:25:29 +020046#define UART2_SDR SDR0_UART2
Stefan Roese887e2ec2006-09-07 11:51:23 +020047#endif
Stefan Roeseb2815f72008-06-06 16:10:41 +020048#if defined(CONFIG_440EP) || defined(CONFIG_440EPX) || \
49 defined(CONFIG_440GR) || defined(CONFIG_440GRX) || \
Stefan Roese8ac41e32008-03-11 15:05:26 +010050 defined(CONFIG_460EX) || defined(CONFIG_460GT)
Stefan Roesed1c3b272009-09-09 16:25:29 +020051#define UART3_SDR SDR0_UART3
Stefan Roese887e2ec2006-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 Roesee01bd212007-03-21 13:38:59 +010056#elif defined(CONFIG_405EP) || defined(CONFIG_405EZ)
stroese8749cfb2003-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 Roesedbbd1252007-10-05 17:10:59 +020062#elif defined(CONFIG_405EX)
Stefan Roese550650d2010-09-20 16:05:31 +020063#define MFREG(a, d) mfsdr(a, d)
64#define MTREG(a, d) mtsdr(a, d)
Stefan Roesedbbd1252007-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 Roesed1c3b272009-09-09 16:25:29 +020069#define UART0_SDR SDR0_UART0
70#define UART1_SDR SDR0_UART1
Matthias Fuchs3fb85882013-08-07 12:10:38 +020071#else /* CONFIG_405GP */
wdenkfe8c2802002-11-03 00:38:21 +000072#define CR0_MASK 0x00001fff
stroesed7787c62003-02-17 16:06:06 +000073#define CR0_EXTCLK_ENA 0x000000c0
wdenkfe8c2802002-11-03 00:38:21 +000074#define CR0_UDIV_POS 1
stroese8749cfb2003-05-23 11:25:57 +000075#define UDIV_MAX 32
76#endif
77
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +020078#if defined(CONFIG_405EP) && defined(CONFIG_SYS_EXT_SERIAL_CLOCK)
Wolfgang Denk0c8721a2005-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 Roese550650d2010-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 Roesee01bd212007-03-21 13:38:59 +010092 sys_info_t sysinfo;
Stefan Roese550650d2010-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 Roesee01bd212007-03-21 13:38:59 +0100107 get_sys_info(&sysinfo);
wdenkfe8c2802002-11-03 00:38:21 +0000108
Stefan Roese550650d2010-09-20 16:05:31 +0200109#if defined(CONFIG_405EZ)
Stefan Roese273db7e2007-08-13 09:05:33 +0200110 /* check the pll feedback source */
Stefan Roesed1c3b272009-09-09 16:25:29 +0200111 mfcpr(CPR0_PLLC, cpr_pllc);
Stefan Roese429d9572007-08-14 15:03:17 +0200112 plloutb = ((CONFIG_SYS_CLK_FREQ * ((cpr_pllc & PLLC_SRC_MASK) ?
Stefan Roese3248f632007-10-22 16:22:40 +0200113 sysinfo.pllFwdDivB : sysinfo.pllFwdDiv) *
114 sysinfo.pllFbkDiv) / sysinfo.pllFwdDivB);
Stefan Roesee01bd212007-03-21 13:38:59 +0100115 div = plloutb / (16 * baudrate); /* total divisor */
116 umin = (plloutb / get_OPB_freq()) << 1; /* 2 x OPB divisor */
Stefan Roese550650d2010-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 */
Stefan Roesee01bd212007-03-21 13:38:59 +0100123
Stefan Roese550650d2010-09-20 16:05:31 +0200124 *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
Stefan Roesee01bd212007-03-21 13:38:59 +0100129 * search to umin.
130 */
Stefan Roese550650d2010-09-20 16:05:31 +0200131 for (i = max; i > umin; i--) {
Stefan Roesee01bd212007-03-21 13:38:59 +0100132 ibdiv = div / i;
133 est = i * ibdiv;
Stefan Roese550650d2010-09-20 16:05:31 +0200134 idiff = (est > div) ? (est - div) : (div - est);
Stefan Roesee01bd212007-03-21 13:38:59 +0100135 if (idiff == 0) {
Stefan Roese550650d2010-09-20 16:05:31 +0200136 *udiv = i;
137 break; /* can't do better */
Stefan Roesee01bd212007-03-21 13:38:59 +0100138 } else if (idiff < diff) {
Stefan Roese550650d2010-09-20 16:05:31 +0200139 *udiv = i; /* best so far */
140 diff = idiff; /* update lowest diff*/
Stefan Roesee01bd212007-03-21 13:38:59 +0100141 }
142 }
143
Stefan Roese550650d2010-09-20 16:05:31 +0200144#if defined(CONFIG_405EZ)
Stefan Roeseafabb492010-09-12 06:21:37 +0200145 mfcpr(CPR0_PERD0, reg);
Stefan Roesee01bd212007-03-21 13:38:59 +0100146 reg &= ~0x0000ffff;
Stefan Roese550650d2010-09-20 16:05:31 +0200147 reg |= ((*udiv - 0) << 8) | (*udiv - 0);
Stefan Roeseafabb492010-09-12 06:21:37 +0200148 mtcpr(CPR0_PERD0, reg);
wdenkfe8c2802002-11-03 00:38:21 +0000149#endif
150
Stefan Roese550650d2010-09-20 16:05:31 +0200151 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;
Stefan Roese550650d2010-09-20 16:05:31 +0200164#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
Matthias Fuchs3fb85882013-08-07 12:10:38 +0200176#if defined(CONFIG_405GP)
Stefan Roese550650d2010-09-20 16:05:31 +0200177 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 */
Wolfgang Denk32bb34a2011-10-29 09:37:52 +0000187 {
188 u32 tmp = CONFIG_SYS_BASE_BAUD * 16;
189
190 udiv = (clk + tmp / 2) / tmp;
191 }
Stefan Roese550650d2010-09-20 16:05:31 +0200192 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
Matthias Fuchs3fb85882013-08-07 12:10:38 +0200203#endif
Stefan Roese550650d2010-09-20 16:05:31 +0200204
205#if defined(CONFIG_405EP)
Wolfgang Denk32bb34a2011-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 Roese550650d2010-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 */
220
221#if defined(CONFIG_405EX) || defined(CONFIG_440)
Stefan Roese887e2ec2006-09-07 11:51:23 +0200222 MFREG(UART0_SDR, reg);
wdenkba56f622004-02-06 23:19:44 +0000223 reg &= ~CR0_MASK;
Stefan Roese887e2ec2006-09-07 11:51:23 +0200224
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200225#ifdef CONFIG_SYS_EXT_SERIAL_CLOCK
wdenkfe8c2802002-11-03 00:38:21 +0000226 reg |= CR0_EXTCLK_ENA;
227 udiv = 1;
Stefan Roese550650d2010-09-20 16:05:31 +0200228 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 Roese887e2ec2006-09-07 11:51:23 +0200233 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);
wdenkff36fd82005-01-09 22:28:56 +0000242#endif
Stefan Roese887e2ec2006-09-07 11:51:23 +0200243#if defined(UART2_SDR)
244 MTREG(UART2_SDR, reg);
245#endif
246#if defined(UART3_SDR)
247 MTREG(UART3_SDR, reg);
wdenkba56f622004-02-06 23:19:44 +0000248#endif
Stefan Roese550650d2010-09-20 16:05:31 +0200249#endif /* CONFIG_405EX ... */
wdenkff36fd82005-01-09 22:28:56 +0000250
Stefan Roese550650d2010-09-20 16:05:31 +0200251#if defined(CONFIG_405EZ)
252 clk = gd->baudrate * serial_bdiv(gd->baudrate, &udiv) * 16;
253#endif /* CONFIG_405EZ */
Stefan Roesedbbd1252007-10-05 17:10:59 +0200254
255 /*
Stefan Roese550650d2010-09-20 16:05:31 +0200256 * Correct UART frequency in bd-info struct now that
257 * the UART divisor is available
Stefan Roesedbbd1252007-10-05 17:10:59 +0200258 */
Jean-Christophe PLAGNIOL-VILLARD6d0f6bc2008-10-16 15:01:15 +0200259#ifdef CONFIG_SYS_EXT_SERIAL_CLOCK
Simon Glass3a1dc8f2012-12-13 20:48:59 +0000260 gd->arch.uart_clk = CONFIG_SYS_EXT_SERIAL_CLOCK;
wdenkfe8c2802002-11-03 00:38:21 +0000261#else
Stefan Roese550650d2010-09-20 16:05:31 +0200262 get_sys_info(&sys_info);
Simon Glass3a1dc8f2012-12-13 20:48:59 +0000263 gd->arch.uart_clk = sys_info.freqUART / udiv;
Jon Loeliger068b60a2007-07-10 10:27:39 -0500264#endif
wdenkfe8c2802002-11-03 00:38:21 +0000265
Stefan Roese550650d2010-09-20 16:05:31 +0200266 return clk;
wdenkff36fd82005-01-09 22:28:56 +0000267}
Matthias Fuchs3fb85882013-08-07 12:10:38 +0200268#endif /* CONFIG_405GP */