mirror of
https://github.com/u-boot/u-boot.git
synced 2024-12-03 01:23:29 +08:00
83d290c56f
When U-Boot started using SPDX tags we were among the early adopters and there weren't a lot of other examples to borrow from. So we picked the area of the file that usually had a full license text and replaced it with an appropriate SPDX-License-Identifier: entry. Since then, the Linux Kernel has adopted SPDX tags and they place it as the very first line in a file (except where shebangs are used, then it's second line) and with slightly different comment styles than us. In part due to community overlap, in part due to better tag visibility and in part for other minor reasons, switch over to that style. This commit changes all instances where we have a single declared license in the tag as both the before and after are identical in tag contents. There's also a few places where I found we did not have a tag and have introduced one. Signed-off-by: Tom Rini <trini@konsulko.com>
256 lines
6.3 KiB
C
256 lines
6.3 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* (C) Copyright 2000
|
|
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <command.h>
|
|
#include <serial.h>
|
|
#include <watchdog.h>
|
|
#include <asm/cpm_8xx.h>
|
|
#include <linux/compiler.h>
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
#if defined(CONFIG_8xx_CONS_SMC1) /* Console on SMC1 */
|
|
#define SMC_INDEX 0
|
|
#define PROFF_SMC PROFF_SMC1
|
|
#define CPM_CR_CH_SMC CPM_CR_CH_SMC1
|
|
#define IOPINS 0xc0
|
|
|
|
#elif defined(CONFIG_8xx_CONS_SMC2) /* Console on SMC2 */
|
|
#define SMC_INDEX 1
|
|
#define PROFF_SMC PROFF_SMC2
|
|
#define CPM_CR_CH_SMC CPM_CR_CH_SMC2
|
|
#define IOPINS 0xc00
|
|
|
|
#endif /* CONFIG_8xx_CONS_SMCx */
|
|
|
|
struct serialbuffer {
|
|
cbd_t rxbd; /* Rx BD */
|
|
cbd_t txbd; /* Tx BD */
|
|
uint rxindex; /* index for next character to read */
|
|
uchar rxbuf[CONFIG_SYS_SMC_RXBUFLEN];/* rx buffers */
|
|
uchar txbuf; /* tx buffers */
|
|
};
|
|
|
|
static void serial_setdivisor(cpm8xx_t __iomem *cp)
|
|
{
|
|
int divisor = (gd->cpu_clk + 8 * gd->baudrate) / 16 / gd->baudrate;
|
|
|
|
if (divisor / 16 > 0x1000) {
|
|
/* bad divisor, assume 50MHz clock and 9600 baud */
|
|
divisor = (50 * 1000 * 1000 + 8 * 9600) / 16 / 9600;
|
|
}
|
|
|
|
divisor /= CONFIG_SYS_BRGCLK_PRESCALE;
|
|
|
|
if (divisor <= 0x1000)
|
|
out_be32(&cp->cp_brgc1, ((divisor - 1) << 1) | CPM_BRG_EN);
|
|
else
|
|
out_be32(&cp->cp_brgc1, ((divisor / 16 - 1) << 1) | CPM_BRG_EN |
|
|
CPM_BRG_DIV16);
|
|
}
|
|
|
|
/*
|
|
* Minimal serial functions needed to use one of the SMC ports
|
|
* as serial console interface.
|
|
*/
|
|
|
|
static void smc_setbrg(void)
|
|
{
|
|
immap_t __iomem *im = (immap_t __iomem *)CONFIG_SYS_IMMR;
|
|
cpm8xx_t __iomem *cp = &(im->im_cpm);
|
|
|
|
/* Set up the baud rate generator.
|
|
* See 8xx_io/commproc.c for details.
|
|
*
|
|
* Wire BRG1 to SMCx
|
|
*/
|
|
|
|
out_be32(&cp->cp_simode, 0);
|
|
|
|
serial_setdivisor(cp);
|
|
}
|
|
|
|
static int smc_init(void)
|
|
{
|
|
immap_t __iomem *im = (immap_t __iomem *)CONFIG_SYS_IMMR;
|
|
smc_t __iomem *sp;
|
|
smc_uart_t __iomem *up;
|
|
cpm8xx_t __iomem *cp = &(im->im_cpm);
|
|
struct serialbuffer __iomem *rtx;
|
|
|
|
/* initialize pointers to SMC */
|
|
|
|
sp = cp->cp_smc + SMC_INDEX;
|
|
up = (smc_uart_t __iomem *)&cp->cp_dparam[PROFF_SMC];
|
|
/* Disable relocation */
|
|
out_be16(&up->smc_rpbase, 0);
|
|
|
|
/* Disable transmitter/receiver. */
|
|
clrbits_be16(&sp->smc_smcmr, SMCMR_REN | SMCMR_TEN);
|
|
|
|
/* Enable SDMA. */
|
|
out_be32(&im->im_siu_conf.sc_sdcr, 1);
|
|
|
|
/* clear error conditions */
|
|
out_8(&im->im_sdma.sdma_sdsr, CONFIG_SYS_SDSR);
|
|
|
|
/* clear SDMA interrupt mask */
|
|
out_8(&im->im_sdma.sdma_sdmr, CONFIG_SYS_SDMR);
|
|
|
|
/* Use Port B for SMCx instead of other functions. */
|
|
setbits_be32(&cp->cp_pbpar, IOPINS);
|
|
clrbits_be32(&cp->cp_pbdir, IOPINS);
|
|
clrbits_be16(&cp->cp_pbodr, IOPINS);
|
|
|
|
/* Set the physical address of the host memory buffers in
|
|
* the buffer descriptors.
|
|
*/
|
|
rtx = (struct serialbuffer __iomem *)&cp->cp_dpmem[CPM_SERIAL_BASE];
|
|
/* 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
|
|
*/
|
|
|
|
out_be32(&rtx->rxbd.cbd_bufaddr, (__force uint)&rtx->rxbuf);
|
|
out_be16(&rtx->rxbd.cbd_sc, 0);
|
|
|
|
out_be32(&rtx->txbd.cbd_bufaddr, (__force uint)&rtx->txbuf);
|
|
out_be16(&rtx->txbd.cbd_sc, 0);
|
|
|
|
/* Set up the uart parameters in the parameter ram. */
|
|
out_be16(&up->smc_rbase, CPM_SERIAL_BASE);
|
|
out_be16(&up->smc_tbase, CPM_SERIAL_BASE + sizeof(cbd_t));
|
|
out_8(&up->smc_rfcr, SMC_EB);
|
|
out_8(&up->smc_tfcr, SMC_EB);
|
|
|
|
/* Set UART mode, 8 bit, no parity, one stop.
|
|
* Enable receive and transmit.
|
|
*/
|
|
out_be16(&sp->smc_smcmr, smcr_mk_clen(9) | SMCMR_SM_UART);
|
|
|
|
/* Mask all interrupts and remove anything pending.
|
|
*/
|
|
out_8(&sp->smc_smcm, 0);
|
|
out_8(&sp->smc_smce, 0xff);
|
|
|
|
/* Set up the baud rate generator */
|
|
smc_setbrg();
|
|
|
|
/* Make the first buffer the only buffer. */
|
|
setbits_be16(&rtx->txbd.cbd_sc, BD_SC_WRAP);
|
|
setbits_be16(&rtx->rxbd.cbd_sc, BD_SC_EMPTY | BD_SC_WRAP);
|
|
|
|
/* single/multi character receive. */
|
|
out_be16(&up->smc_mrblr, CONFIG_SYS_SMC_RXBUFLEN);
|
|
out_be16(&up->smc_maxidl, CONFIG_SYS_MAXIDLE);
|
|
out_be32(&rtx->rxindex, 0);
|
|
|
|
/* Initialize Tx/Rx parameters. */
|
|
while (in_be16(&cp->cp_cpcr) & CPM_CR_FLG) /* wait if cp is busy */
|
|
;
|
|
|
|
out_be16(&cp->cp_cpcr,
|
|
mk_cr_cmd(CPM_CR_CH_SMC, CPM_CR_INIT_TRX) | CPM_CR_FLG);
|
|
|
|
while (in_be16(&cp->cp_cpcr) & CPM_CR_FLG) /* wait if cp is busy */
|
|
;
|
|
|
|
/* Enable transmitter/receiver. */
|
|
setbits_be16(&sp->smc_smcmr, SMCMR_REN | SMCMR_TEN);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void smc_putc(const char c)
|
|
{
|
|
immap_t __iomem *im = (immap_t __iomem *)CONFIG_SYS_IMMR;
|
|
cpm8xx_t __iomem *cpmp = &(im->im_cpm);
|
|
struct serialbuffer __iomem *rtx;
|
|
|
|
if (c == '\n')
|
|
smc_putc('\r');
|
|
|
|
rtx = (struct serialbuffer __iomem *)&cpmp->cp_dpmem[CPM_SERIAL_BASE];
|
|
|
|
/* Wait for last character to go. */
|
|
out_8(&rtx->txbuf, c);
|
|
out_be16(&rtx->txbd.cbd_datlen, 1);
|
|
setbits_be16(&rtx->txbd.cbd_sc, BD_SC_READY);
|
|
|
|
while (in_be16(&rtx->txbd.cbd_sc) & BD_SC_READY)
|
|
WATCHDOG_RESET();
|
|
}
|
|
|
|
static void smc_puts(const char *s)
|
|
{
|
|
while (*s)
|
|
smc_putc(*s++);
|
|
}
|
|
|
|
static int smc_getc(void)
|
|
{
|
|
immap_t __iomem *im = (immap_t __iomem *)CONFIG_SYS_IMMR;
|
|
cpm8xx_t __iomem *cpmp = &(im->im_cpm);
|
|
struct serialbuffer __iomem *rtx;
|
|
unsigned char c;
|
|
uint rxindex;
|
|
|
|
rtx = (struct serialbuffer __iomem *)&cpmp->cp_dpmem[CPM_SERIAL_BASE];
|
|
|
|
/* Wait for character to show up. */
|
|
while (in_be16(&rtx->rxbd.cbd_sc) & BD_SC_EMPTY)
|
|
WATCHDOG_RESET();
|
|
|
|
/* the characters are read one by one,
|
|
* use the rxindex to know the next char to deliver
|
|
*/
|
|
rxindex = in_be32(&rtx->rxindex);
|
|
c = in_8(rtx->rxbuf + rxindex);
|
|
rxindex++;
|
|
|
|
/* check if all char are readout, then make prepare for next receive */
|
|
if (rxindex >= in_be16(&rtx->rxbd.cbd_datlen)) {
|
|
rxindex = 0;
|
|
setbits_be16(&rtx->rxbd.cbd_sc, BD_SC_EMPTY);
|
|
}
|
|
out_be32(&rtx->rxindex, rxindex);
|
|
return c;
|
|
}
|
|
|
|
static int smc_tstc(void)
|
|
{
|
|
immap_t __iomem *im = (immap_t __iomem *)CONFIG_SYS_IMMR;
|
|
cpm8xx_t __iomem *cpmp = &(im->im_cpm);
|
|
struct serialbuffer __iomem *rtx;
|
|
|
|
rtx = (struct serialbuffer __iomem *)&cpmp->cp_dpmem[CPM_SERIAL_BASE];
|
|
|
|
return !(in_be16(&rtx->rxbd.cbd_sc) & BD_SC_EMPTY);
|
|
}
|
|
|
|
struct serial_device serial_smc_device = {
|
|
.name = "serial_smc",
|
|
.start = smc_init,
|
|
.stop = NULL,
|
|
.setbrg = smc_setbrg,
|
|
.getc = smc_getc,
|
|
.tstc = smc_tstc,
|
|
.putc = smc_putc,
|
|
.puts = smc_puts,
|
|
};
|
|
|
|
__weak struct serial_device *default_serial_console(void)
|
|
{
|
|
return &serial_smc_device;
|
|
}
|
|
|
|
void mpc8xx_serial_initialize(void)
|
|
{
|
|
serial_register(&serial_smc_device);
|
|
}
|