]> git.ipfire.org Git - people/ms/u-boot.git/blob - arch/powerpc/cpu/mpc8xx/serial.c
powerpc: Partialy restore core of mpc8xx
[people/ms/u-boot.git] / arch / powerpc / cpu / mpc8xx / serial.c
1 /*
2 * (C) Copyright 2000
3 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4 *
5 * SPDX-License-Identifier: GPL-2.0+
6 */
7
8 #include <common.h>
9 #include <commproc.h>
10 #include <command.h>
11 #include <serial.h>
12 #include <watchdog.h>
13 #include <linux/compiler.h>
14
15 DECLARE_GLOBAL_DATA_PTR;
16
17 #if !defined(CONFIG_8xx_CONS_NONE) /* No Console at all */
18
19 #if defined(CONFIG_8xx_CONS_SMC1) /* Console on SMC1 */
20 #define SMC_INDEX 0
21 #define PROFF_SMC PROFF_SMC1
22 #define CPM_CR_CH_SMC CPM_CR_CH_SMC1
23
24 #elif defined(CONFIG_8xx_CONS_SMC2) /* Console on SMC2 */
25 #define SMC_INDEX 1
26 #define PROFF_SMC PROFF_SMC2
27 #define CPM_CR_CH_SMC CPM_CR_CH_SMC2
28
29 #endif /* CONFIG_8xx_CONS_SMCx */
30
31 #if !defined(CONFIG_SYS_SMC_RXBUFLEN)
32 #define CONFIG_SYS_SMC_RXBUFLEN 1
33 #define CONFIG_SYS_MAXIDLE 0
34 #else
35 #if !defined(CONFIG_SYS_MAXIDLE)
36 #error "you must define CONFIG_SYS_MAXIDLE"
37 #endif
38 #endif
39
40 typedef volatile struct serialbuffer {
41 cbd_t rxbd; /* Rx BD */
42 cbd_t txbd; /* Tx BD */
43 uint rxindex; /* index for next character to read */
44 volatile uchar rxbuf[CONFIG_SYS_SMC_RXBUFLEN];/* rx buffers */
45 volatile uchar txbuf; /* tx buffers */
46 } serialbuffer_t;
47
48 static void serial_setdivisor(volatile cpm8xx_t *cp)
49 {
50 int divisor=(gd->cpu_clk + 8*gd->baudrate)/16/gd->baudrate;
51
52 if(divisor/16>0x1000) {
53 /* bad divisor, assume 50MHz clock and 9600 baud */
54 divisor=(50*1000*1000 + 8*9600)/16/9600;
55 }
56
57 #ifdef CONFIG_SYS_BRGCLK_PRESCALE
58 divisor /= CONFIG_SYS_BRGCLK_PRESCALE;
59 #endif
60
61 if(divisor<=0x1000) {
62 cp->cp_brgc1=((divisor-1)<<1) | CPM_BRG_EN;
63 } else {
64 cp->cp_brgc1=((divisor/16-1)<<1) | CPM_BRG_EN | CPM_BRG_DIV16;
65 }
66 }
67
68 /*
69 * Minimal serial functions needed to use one of the SMC ports
70 * as serial console interface.
71 */
72
73 static void smc_setbrg (void)
74 {
75 volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR;
76 volatile cpm8xx_t *cp = &(im->im_cpm);
77
78 /* Set up the baud rate generator.
79 * See 8xx_io/commproc.c for details.
80 *
81 * Wire BRG1 to SMCx
82 */
83
84 cp->cp_simode = 0x00000000;
85
86 serial_setdivisor(cp);
87 }
88
89 static int smc_init (void)
90 {
91 volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR;
92 volatile smc_t *sp;
93 volatile smc_uart_t *up;
94 volatile cpm8xx_t *cp = &(im->im_cpm);
95 uint dpaddr;
96 volatile serialbuffer_t *rtx;
97
98 /* initialize pointers to SMC */
99
100 sp = (smc_t *) &(cp->cp_smc[SMC_INDEX]);
101 up = (smc_uart_t *) &cp->cp_dparam[PROFF_SMC];
102 /* Disable relocation */
103 up->smc_rpbase = 0;
104
105 /* Disable transmitter/receiver. */
106 sp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN);
107
108 /* Enable SDMA. */
109 im->im_siu_conf.sc_sdcr = 1;
110
111 /* clear error conditions */
112 #ifdef CONFIG_SYS_SDSR
113 im->im_sdma.sdma_sdsr = CONFIG_SYS_SDSR;
114 #else
115 im->im_sdma.sdma_sdsr = 0x83;
116 #endif
117
118 /* clear SDMA interrupt mask */
119 #ifdef CONFIG_SYS_SDMR
120 im->im_sdma.sdma_sdmr = CONFIG_SYS_SDMR;
121 #else
122 im->im_sdma.sdma_sdmr = 0x00;
123 #endif
124
125 #if defined(CONFIG_8xx_CONS_SMC1)
126 /* Use Port B for SMC1 instead of other functions. */
127 cp->cp_pbpar |= 0x000000c0;
128 cp->cp_pbdir &= ~0x000000c0;
129 cp->cp_pbodr &= ~0x000000c0;
130 #else /* CONFIG_8xx_CONS_SMC2 */
131 /* Use Port B for SMC2 instead of other functions.
132 */
133 cp->cp_pbpar |= 0x00000c00;
134 cp->cp_pbdir &= ~0x00000c00;
135 cp->cp_pbodr &= ~0x00000c00;
136 #endif
137
138 /* Set the physical address of the host memory buffers in
139 * the buffer descriptors.
140 */
141 dpaddr = CPM_SERIAL_BASE;
142
143 rtx = (serialbuffer_t *)&cp->cp_dpmem[dpaddr];
144 /* Allocate space for two buffer descriptors in the DP ram.
145 * For now, this address seems OK, but it may have to
146 * change with newer versions of the firmware.
147 * damm: allocating space after the two buffers for rx/tx data
148 */
149
150 rtx->rxbd.cbd_bufaddr = (uint) &rtx->rxbuf;
151 rtx->rxbd.cbd_sc = 0;
152
153 rtx->txbd.cbd_bufaddr = (uint) &rtx->txbuf;
154 rtx->txbd.cbd_sc = 0;
155
156 /* Set up the uart parameters in the parameter ram. */
157 up->smc_rbase = dpaddr;
158 up->smc_tbase = dpaddr+sizeof(cbd_t);
159 up->smc_rfcr = SMC_EB;
160 up->smc_tfcr = SMC_EB;
161
162 /* Set UART mode, 8 bit, no parity, one stop.
163 * Enable receive and transmit.
164 */
165 sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART;
166
167 /* Mask all interrupts and remove anything pending.
168 */
169 sp->smc_smcm = 0;
170 sp->smc_smce = 0xff;
171
172 /* Set up the baud rate generator */
173 smc_setbrg ();
174
175 /* Make the first buffer the only buffer. */
176 rtx->txbd.cbd_sc |= BD_SC_WRAP;
177 rtx->rxbd.cbd_sc |= BD_SC_EMPTY | BD_SC_WRAP;
178
179 /* single/multi character receive. */
180 up->smc_mrblr = CONFIG_SYS_SMC_RXBUFLEN;
181 up->smc_maxidl = CONFIG_SYS_MAXIDLE;
182 rtx->rxindex = 0;
183
184 /* Initialize Tx/Rx parameters. */
185 while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */
186 ;
187
188 cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC, CPM_CR_INIT_TRX) | CPM_CR_FLG;
189
190 while (cp->cp_cpcr & CPM_CR_FLG) /* wait if cp is busy */
191 ;
192
193 /* Enable transmitter/receiver. */
194 sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN;
195
196 return (0);
197 }
198
199 static void
200 smc_putc(const char c)
201 {
202 volatile smc_uart_t *up;
203 volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR;
204 volatile cpm8xx_t *cpmp = &(im->im_cpm);
205 volatile serialbuffer_t *rtx;
206
207 if (c == '\n')
208 smc_putc ('\r');
209
210 up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC];
211
212 rtx = (serialbuffer_t *)&cpmp->cp_dpmem[up->smc_rbase];
213
214 /* Wait for last character to go. */
215 rtx->txbuf = c;
216 rtx->txbd.cbd_datlen = 1;
217 rtx->txbd.cbd_sc |= BD_SC_READY;
218 __asm__("eieio");
219
220 while (rtx->txbd.cbd_sc & BD_SC_READY) {
221 WATCHDOG_RESET ();
222 __asm__("eieio");
223 }
224 }
225
226 static void
227 smc_puts (const char *s)
228 {
229 while (*s) {
230 smc_putc (*s++);
231 }
232 }
233
234 static int
235 smc_getc(void)
236 {
237 volatile smc_uart_t *up;
238 volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR;
239 volatile cpm8xx_t *cpmp = &(im->im_cpm);
240 volatile serialbuffer_t *rtx;
241 unsigned char c;
242
243 up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC];
244 rtx = (serialbuffer_t *)&cpmp->cp_dpmem[up->smc_rbase];
245
246 /* Wait for character to show up. */
247 while (rtx->rxbd.cbd_sc & BD_SC_EMPTY)
248 WATCHDOG_RESET ();
249
250 /* the characters are read one by one,
251 * use the rxindex to know the next char to deliver
252 */
253 c = *(unsigned char *) (rtx->rxbd.cbd_bufaddr+rtx->rxindex);
254 rtx->rxindex++;
255
256 /* check if all char are readout, then make prepare for next receive */
257 if (rtx->rxindex >= rtx->rxbd.cbd_datlen) {
258 rtx->rxindex = 0;
259 rtx->rxbd.cbd_sc |= BD_SC_EMPTY;
260 }
261 return(c);
262 }
263
264 static int
265 smc_tstc(void)
266 {
267 volatile smc_uart_t *up;
268 volatile immap_t *im = (immap_t *)CONFIG_SYS_IMMR;
269 volatile cpm8xx_t *cpmp = &(im->im_cpm);
270 volatile serialbuffer_t *rtx;
271
272 up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC];
273
274 rtx = (serialbuffer_t *)&cpmp->cp_dpmem[up->smc_rbase];
275
276 return !(rtx->rxbd.cbd_sc & BD_SC_EMPTY);
277 }
278
279 struct serial_device serial_smc_device =
280 {
281 .name = "serial_smc",
282 .start = smc_init,
283 .stop = NULL,
284 .setbrg = smc_setbrg,
285 .getc = smc_getc,
286 .tstc = smc_tstc,
287 .putc = smc_putc,
288 .puts = smc_puts,
289 };
290
291 __weak struct serial_device *default_serial_console(void)
292 {
293 return &serial_smc_device;
294 }
295
296 void mpc8xx_serial_initialize(void)
297 {
298 serial_register(&serial_smc_device);
299 }
300
301 #endif /* CONFIG_8xx_CONS_NONE */