diff options
Diffstat (limited to 'cpu/mpc8xx/serial.c')
| -rw-r--r-- | cpu/mpc8xx/serial.c | 640 | 
1 files changed, 640 insertions, 0 deletions
| diff --git a/cpu/mpc8xx/serial.c b/cpu/mpc8xx/serial.c new file mode 100644 index 000000000..0690300b2 --- /dev/null +++ b/cpu/mpc8xx/serial.c @@ -0,0 +1,640 @@ +/* + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <commproc.h> +#include <command.h> + +#if !defined(CONFIG_8xx_CONS_NONE)	/* No Console at all */ + +#if defined(CONFIG_8xx_CONS_SMC1)	/* Console on SMC1 */ +#define	SMC_INDEX	0 +#undef 	SCC_INDEX +#define PROFF_SMC	PROFF_SMC1 +#define CPM_CR_CH_SMC	CPM_CR_CH_SMC1 + +#elif defined(CONFIG_8xx_CONS_SMC2)	/* Console on SMC2 */ +#define SMC_INDEX	1 +#undef 	SCC_INDEX +#define PROFF_SMC	PROFF_SMC2 +#define CPM_CR_CH_SMC	CPM_CR_CH_SMC2 + +#elif defined(CONFIG_8xx_CONS_SCC1)	/* Console on SCC1 */ +#undef  SMC_INDEX +#define SCC_INDEX	0 +#define PROFF_SCC	PROFF_SCC1 +#define CPM_CR_CH_SCC	CPM_CR_CH_SCC1 + +#elif defined(CONFIG_8xx_CONS_SCC2)	/* Console on SCC2 */ +#undef  SMC_INDEX +#define SCC_INDEX	1 +#define PROFF_SCC	PROFF_SCC2 +#define CPM_CR_CH_SCC	CPM_CR_CH_SCC2 + +#elif defined(CONFIG_8xx_CONS_SCC3)	/* Console on SCC3 */ +#undef  SMC_INDEX +#define SCC_INDEX	2 +#define PROFF_SCC	PROFF_SCC3 +#define CPM_CR_CH_SCC	CPM_CR_CH_SCC3 + +#elif defined(CONFIG_8xx_CONS_SCC4)	/* Console on SCC4 */ +#undef  SMC_INDEX +#define SCC_INDEX	3 +#define PROFF_SCC	PROFF_SCC4 +#define CPM_CR_CH_SCC	CPM_CR_CH_SCC4 + +#else /* CONFIG_8xx_CONS_? */ +#error "console not correctly defined" +#endif + +#if (defined (CONFIG_8xx_CONS_SMC1) || defined (CONFIG_8xx_CONS_SMC2)) + +/* + * Minimal serial functions needed to use one of the SMC ports + * as serial console interface. + */ + +int serial_init (void) +{ +        volatile immap_t *im = (immap_t *)CFG_IMMR; +	volatile smc_t *sp; +	volatile smc_uart_t *up; +	volatile cbd_t *tbdf, *rbdf; +	volatile cpm8xx_t *cp = &(im->im_cpm); +#if (!defined(CONFIG_8xx_CONS_SMC1)) && (defined(CONFIG_MPC823) || defined(CONFIG_MPC850)) +	volatile iop8xx_t *ip = (iop8xx_t *)&(im->im_ioport); +#endif +	uint	dpaddr; + +	/* initialize pointers to SMC */ + +	sp = (smc_t *) &(cp->cp_smc[SMC_INDEX]); +	up = (smc_uart_t *) &cp->cp_dparam[PROFF_SMC]; + +	/* Disable transmitter/receiver. +	*/ +	sp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN); + +	/* Enable SDMA. +	*/ +	im->im_siu_conf.sc_sdcr = 1; + +	/* clear error conditions */ +#ifdef	CFG_SDSR +	im->im_sdma.sdma_sdsr = CFG_SDSR; +#else +	im->im_sdma.sdma_sdsr = 0x83; +#endif + +	/* clear SDMA interrupt mask */ +#ifdef	CFG_SDMR +	im->im_sdma.sdma_sdmr = CFG_SDMR; +#else +	im->im_sdma.sdma_sdmr = 0x00; +#endif + +#if defined(CONFIG_8xx_CONS_SMC1) +	/* Use Port B for SMC1 instead of other functions. +	*/ +	cp->cp_pbpar |=  0x000000c0; +	cp->cp_pbdir &= ~0x000000c0; +	cp->cp_pbodr &= ~0x000000c0; +#else	/* CONFIG_8xx_CONS_SMC2 */ +# if defined(CONFIG_MPC823) || defined(CONFIG_MPC850) +	/* Use Port A for SMC2 instead of other functions. +	*/ +	ip->iop_papar |=  0x00c0; +	ip->iop_padir &= ~0x00c0; +	ip->iop_paodr &= ~0x00c0; +# else	/* must be a 860 then */ +	/* Use Port B for SMC2 instead of other functions. +	*/ +	cp->cp_pbpar |=  0x00000c00; +	cp->cp_pbdir &= ~0x00000c00; +	cp->cp_pbodr &= ~0x00000c00; +# endif +#endif + +#if defined(CONFIG_FADS) +	/* Enable RS232 */ +#if defined(CONFIG_8xx_CONS_SMC1) +	*((uint *) BCSR1) &= ~BCSR1_RS232EN_1; +#else +	*((uint *) BCSR1) &= ~BCSR1_RS232EN_2; +#endif +#endif	/* CONFIG_FADS */ + +#if defined(CONFIG_RPXLITE) || defined(CONFIG_RPXCLASSIC) +	/* Enable Monitor Port Transceiver */ +	*((uchar *) BCSR0) |= BCSR0_ENMONXCVR ; +#endif /* CONFIG_RPXLITE */ + +	/* Set the physical address of the host memory buffers in +	 * the buffer descriptors. +	 */ + +#ifdef CFG_ALLOC_DPRAM +	dpaddr = dpram_alloc_align (sizeof(cbd_t)*2 + 2, 8) ; +#else +	dpaddr = CPM_SERIAL_BASE ; +#endif + +	/* Allocate space for two buffer descriptors in the DP ram. +	 * For now, this address seems OK, but it may have to +	 * change with newer versions of the firmware. +	 * damm: allocating space after the two buffers for rx/tx data +	 */ + +	rbdf = (cbd_t *)&cp->cp_dpmem[dpaddr]; +	rbdf->cbd_bufaddr = (uint) (rbdf+2); +	rbdf->cbd_sc = 0; +	tbdf = rbdf + 1; +	tbdf->cbd_bufaddr = ((uint) (rbdf+2)) + 1; +	tbdf->cbd_sc = 0; + +	/* Set up the uart parameters in the parameter ram. +	*/ +	up->smc_rbase = dpaddr; +	up->smc_tbase = dpaddr+sizeof(cbd_t); +	up->smc_rfcr = SMC_EB; +	up->smc_tfcr = SMC_EB; + +#if defined(CONFIG_MBX) +	board_serial_init(); +#endif	/* CONFIG_MBX */ + +	/* Set UART mode, 8 bit, no parity, one stop. +	 * Enable receive and transmit. +	 */ +	sp->smc_smcmr = smcr_mk_clen(9) |  SMCMR_SM_UART; + +	/* Mask all interrupts and remove anything pending. +	*/ +	sp->smc_smcm = 0; +	sp->smc_smce = 0xff; + +	/* Set up the baud rate generator. +	*/ +	serial_setbrg (); + +	/* Make the first buffer the only buffer. +	*/ +	tbdf->cbd_sc |= BD_SC_WRAP; +	rbdf->cbd_sc |= BD_SC_EMPTY | BD_SC_WRAP; + +	/* Single character receive. +	*/ +	up->smc_mrblr = 1; +	up->smc_maxidl = 0; + +	/* Initialize Tx/Rx parameters. +	*/ + +	while (cp->cp_cpcr & CPM_CR_FLG)  /* wait if cp is busy */ +	  ; + +	cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC, CPM_CR_INIT_TRX) | CPM_CR_FLG; + +	while (cp->cp_cpcr & CPM_CR_FLG)  /* wait if cp is busy */ +	  ; + +	/* Enable transmitter/receiver. +	*/ +	sp->smc_smcmr |= SMCMR_REN | SMCMR_TEN; + +	return (0); +} + +void +serial_setbrg (void) +{ +	DECLARE_GLOBAL_DATA_PTR; + +        volatile immap_t *im = (immap_t *)CFG_IMMR; +	volatile cpm8xx_t *cp = &(im->im_cpm); + +	/* Set up the baud rate generator. +	 * See 8xx_io/commproc.c for details. +	 * +	 * Wire BRG1 to SMCx +	 */ + +	cp->cp_simode = 0x00000000; + +	cp->cp_brgc1 = +		(((gd->cpu_clk / 16 / gd->baudrate)-1) << 1) | CPM_BRG_EN; +} + +void +serial_putc(const char c) +{ +	volatile cbd_t		*tbdf; +	volatile char		*buf; +	volatile smc_uart_t	*up; +        volatile immap_t	*im = (immap_t *)CFG_IMMR; +	volatile cpm8xx_t	*cpmp = &(im->im_cpm); + +	if (c == '\n') +		serial_putc ('\r'); + +	up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC]; + +	tbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_tbase]; + +	/* Wait for last character to go. +	*/ + +	buf = (char *)tbdf->cbd_bufaddr; +#if 0 +	__asm__("eieio"); +	while (tbdf->cbd_sc & BD_SC_READY) +		__asm__("eieio"); +#endif + +	*buf = c; +	tbdf->cbd_datlen = 1; +	tbdf->cbd_sc |= BD_SC_READY; +	__asm__("eieio"); +#if 1 +	while (tbdf->cbd_sc & BD_SC_READY) +		__asm__("eieio"); +#endif +} + +int +serial_getc(void) +{ +	volatile cbd_t		*rbdf; +	volatile unsigned char	*buf; +	volatile smc_uart_t	*up; +        volatile immap_t	*im = (immap_t *)CFG_IMMR; +	volatile cpm8xx_t	*cpmp = &(im->im_cpm); +	unsigned char		c; + +	up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC]; + +	rbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase]; + +	/* Wait for character to show up. +	*/ +	buf = (unsigned char *)rbdf->cbd_bufaddr; +	while (rbdf->cbd_sc & BD_SC_EMPTY) +		; +	c = *buf; +	rbdf->cbd_sc |= BD_SC_EMPTY; + +	return(c); +} + +int +serial_tstc() +{ +	volatile cbd_t		*rbdf; +	volatile smc_uart_t	*up; +        volatile immap_t	*im = (immap_t *)CFG_IMMR; +	volatile cpm8xx_t	*cpmp = &(im->im_cpm); + +	up = (smc_uart_t *)&cpmp->cp_dparam[PROFF_SMC]; + +	rbdf = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase]; + +	return(!(rbdf->cbd_sc & BD_SC_EMPTY)); +} + +#else	/* ! CONFIG_8xx_CONS_SMC1, CONFIG_8xx_CONS_SMC2 */ + +int serial_init (void) +{ +        volatile immap_t *im = (immap_t *)CFG_IMMR; +	volatile scc_t *sp; +	volatile scc_uart_t *up; +	volatile cbd_t *tbdf, *rbdf; +	volatile cpm8xx_t *cp = &(im->im_cpm); +	uint	 dpaddr; +#if (SCC_INDEX != 2) || !defined(CONFIG_MPC850) +	volatile iop8xx_t *ip = (iop8xx_t *)&(im->im_ioport); +#endif + +	/* initialize pointers to SCC */ + +	sp = (scc_t *) &(cp->cp_scc[SCC_INDEX]); +	up = (scc_uart_t *) &cp->cp_dparam[PROFF_SCC]; + +#if defined(CONFIG_LWMON) && defined(CONFIG_8xx_CONS_SCC2) +    {	/* Disable Ethernet, enable Serial */ +	uchar c; + +	c = pic_read  (0x61); +	c &= ~0x40;	/* enable COM3 */ +	c |=  0x80;	/* disable Ethernet */ +	pic_write (0x61, c); + +	/* enable RTS2 */ +	cp->cp_pbpar |=  0x2000; +	cp->cp_pbdat |=  0x2000; +	cp->cp_pbdir |=  0x2000; +    } +#endif	/* CONFIG_LWMON */ + +	/* Disable transmitter/receiver. +	*/ +	sp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT); + +#if (SCC_INDEX == 2) && defined(CONFIG_MPC850) +	/* +	 * The MPC850 has SCC3 on Port B +	 */ +	cp->cp_pbpar |=  0x06; +	cp->cp_pbdir &= ~0x06; +	cp->cp_pbodr &= ~0x06; + +#elif (SCC_INDEX < 2) || !defined(CONFIG_IP860) +	/* +	 * Standard configuration for SCC's is on Part A +	 */ +	ip->iop_papar |=  ((3 << (2 * SCC_INDEX))); +	ip->iop_padir &= ~((3 << (2 * SCC_INDEX))); +	ip->iop_paodr &= ~((3 << (2 * SCC_INDEX))); +#else +	/* +	 * The IP860 has SCC3 and SCC4 on Port D +	 */ +	ip->iop_pdpar |=  ((3 << (2 * SCC_INDEX))); +#endif + +	/* Allocate space for two buffer descriptors in the DP ram. +	 */ + +#ifdef CFG_ALLOC_DPRAM +	dpaddr = dpram_alloc_align (sizeof(cbd_t)*2 + 2, 8) ; +#else +	dpaddr = CPM_SERIAL_BASE ; +#endif + +	/* Enable SDMA. +	*/ +	im->im_siu_conf.sc_sdcr = 0x0001; + +	/* Set the physical address of the host memory buffers in +	 * the buffer descriptors. +	 */ + +	rbdf = (cbd_t *)&cp->cp_dpmem[dpaddr]; +	rbdf->cbd_bufaddr = (uint) (rbdf+2); +	rbdf->cbd_sc = 0; +	tbdf = rbdf + 1; +	tbdf->cbd_bufaddr = ((uint) (rbdf+2)) + 1; +	tbdf->cbd_sc = 0; + +	/* Set up the baud rate generator. +	*/ +	serial_setbrg (); + +	/* Set up the uart parameters in the parameter ram. +	*/ +	up->scc_genscc.scc_rbase = dpaddr; +	up->scc_genscc.scc_tbase = dpaddr+sizeof(cbd_t); + +	/* Initialize Tx/Rx parameters. +	*/ +	while (cp->cp_cpcr & CPM_CR_FLG)  /* wait if cp is busy */ +		; +	cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SCC, CPM_CR_INIT_TRX) | CPM_CR_FLG; + +	while (cp->cp_cpcr & CPM_CR_FLG)  /* wait if cp is busy */ +		; + +	up->scc_genscc.scc_rfcr  = SCC_EB | 0x05; +	up->scc_genscc.scc_tfcr  = SCC_EB | 0x05; + +	up->scc_genscc.scc_mrblr = 1;	/* Single character receive */ +	up->scc_maxidl = 0;		/* disable max idle */ +	up->scc_brkcr  = 1;		/* send one break character on stop TX */ +	up->scc_parec  = 0; +	up->scc_frmec  = 0; +	up->scc_nosec  = 0; +	up->scc_brkec  = 0; +	up->scc_uaddr1 = 0; +	up->scc_uaddr2 = 0; +	up->scc_toseq  = 0; +	up->scc_char1  = 0x8000; +	up->scc_char2  = 0x8000; +	up->scc_char3  = 0x8000; +	up->scc_char4  = 0x8000; +	up->scc_char5  = 0x8000; +	up->scc_char6  = 0x8000; +	up->scc_char7  = 0x8000; +	up->scc_char8  = 0x8000; +	up->scc_rccm   = 0xc0ff; + +	/* Set low latency / small fifo. +	 */ +	sp->scc_gsmrh = SCC_GSMRH_RFW; + +	/* Set SCC(x) clock mode to 16x +	 * See 8xx_io/commproc.c for details. +	 * +	 * Wire BRG1 to SCCn +	 */ + +	/* Set UART mode, clock divider 16 on Tx and Rx +	 */ +	sp->scc_gsmrl |= +		(SCC_GSMRL_MODE_UART | SCC_GSMRL_TDCR_16 | SCC_GSMRL_RDCR_16); + +	sp->scc_psmr  |= SCU_PSMR_CL; + +	/* Mask all interrupts and remove anything pending. +	*/ +	sp->scc_sccm = 0; +	sp->scc_scce = 0xffff; +	sp->scc_dsr  = 0x7e7e; +	sp->scc_psmr = 0x3000; + +	/* Make the first buffer the only buffer. +	*/ +	tbdf->cbd_sc |= BD_SC_WRAP; +	rbdf->cbd_sc |= BD_SC_EMPTY | BD_SC_WRAP; + +	/* Enable transmitter/receiver. +	*/ +	sp->scc_gsmrl |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT); + +	return (0); +} + +void +serial_setbrg (void) +{ +	DECLARE_GLOBAL_DATA_PTR; + +        volatile immap_t *im = (immap_t *)CFG_IMMR; +	volatile cpm8xx_t *cp = &(im->im_cpm); + +	/* Set up the baud rate generator. +	 * See 8xx_io/commproc.c for details. +	 * +	 * Wire BRG1 to SCCx +	 */ + +	cp->cp_sicr &= ~(0x000000FF << (8 * SCC_INDEX)); +	/* no |= needed, since BRG1 is 000 */ + +	cp->cp_brgc1 = +		(((gd->cpu_clk / 16 / gd->baudrate)-1) << 1) | CPM_BRG_EN; +} + +void +serial_putc(const char c) +{ +	volatile cbd_t		*tbdf; +	volatile char		*buf; +	volatile scc_uart_t	*up; +        volatile immap_t	*im = (immap_t *)CFG_IMMR; +	volatile cpm8xx_t	*cpmp = &(im->im_cpm); + +	if (c == '\n') +		serial_putc ('\r'); + +	up = (scc_uart_t *)&cpmp->cp_dparam[PROFF_SCC]; + +	tbdf = (cbd_t *)&cpmp->cp_dpmem[up->scc_genscc.scc_tbase]; + +	/* Wait for last character to go. +	*/ + +	buf = (char *)tbdf->cbd_bufaddr; +#if 0 +	__asm__("eieio"); +	while (tbdf->cbd_sc & BD_SC_READY) +		__asm__("eieio"); +#endif + +	*buf = c; +	tbdf->cbd_datlen = 1; +	tbdf->cbd_sc |= BD_SC_READY; +	__asm__("eieio"); +#if 1 +	while (tbdf->cbd_sc & BD_SC_READY) +		__asm__("eieio"); +#endif +} + +int +serial_getc(void) +{ +	volatile cbd_t		*rbdf; +	volatile unsigned char	*buf; +	volatile scc_uart_t	*up; +        volatile immap_t	*im = (immap_t *)CFG_IMMR; +	volatile cpm8xx_t	*cpmp = &(im->im_cpm); +	unsigned char		c; + +	up = (scc_uart_t *)&cpmp->cp_dparam[PROFF_SCC]; + +	rbdf = (cbd_t *)&cpmp->cp_dpmem[up->scc_genscc.scc_rbase]; + +	/* Wait for character to show up. +	*/ +	buf = (unsigned char *)rbdf->cbd_bufaddr; +	while (rbdf->cbd_sc & BD_SC_EMPTY) +		; +	c = *buf; +	rbdf->cbd_sc |= BD_SC_EMPTY; + +	return(c); +} + +int +serial_tstc() +{ +	volatile cbd_t		*rbdf; +	volatile scc_uart_t	*up; +        volatile immap_t	*im = (immap_t *)CFG_IMMR; +	volatile cpm8xx_t	*cpmp = &(im->im_cpm); + +	up = (scc_uart_t *)&cpmp->cp_dparam[PROFF_SCC]; + +	rbdf = (cbd_t *)&cpmp->cp_dpmem[up->scc_genscc.scc_rbase]; + +	return(!(rbdf->cbd_sc & BD_SC_EMPTY)); +} + +#endif	/* CONFIG_8xx_CONS_SMC1, CONFIG_8xx_CONS_SMC2 */ + + +void +serial_puts (const char *s) +{ +	while (*s) { +		serial_putc (*s++); +	} +} + + +#if (CONFIG_COMMANDS & CFG_CMD_KGDB) + +void +kgdb_serial_init(void) +{ +#if defined(CONFIG_8xx_CONS_SMC1) +	serial_printf("[on SMC1] "); +#elif defined(CONFIG_8xx_CONS_SMC2) +	serial_printf("[on SMC2] "); +#elif defined(CONFIG_8xx_CONS_SCC1) +	serial_printf("[on SCC1] "); +#elif defined(CONFIG_8xx_CONS_SCC2) +	serial_printf("[on SCC2] "); +#elif defined(CONFIG_8xx_CONS_SCC3) +	serial_printf("[on SCC3] "); +#elif defined(CONFIG_8xx_CONS_SCC4) +	serial_printf("[on SCC4] "); +#endif +} + +void +putDebugChar (int c) +{ +	serial_putc (c); +} + +void +putDebugStr (const char *str) +{ +	serial_puts (str); +} + +int +getDebugChar (void) +{ +	return serial_getc(); +} + +void +kgdb_interruptible (int yes) +{ +	return; +} +#endif	/* CFG_CMD_KGDB	*/ + +#endif	/* CONFIG_8xx_CONS_NONE */ |