X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=drivers%2Fserial%2Fserial_sh.c;h=c07ddc70dc81ba198989140dca92d271fd6521c1;hb=af2f44267fdc006e47166187632f63d396822bed;hp=78186326f0df2e90a84830c93a6cca7c57a14d5f;hpb=6e1bbe6e3edf5f508de89114577ce7b7caa89c8a;p=u-boot diff --git a/drivers/serial/serial_sh.c b/drivers/serial/serial_sh.c index 78186326f0..c07ddc70dc 100644 --- a/drivers/serial/serial_sh.c +++ b/drivers/serial/serial_sh.c @@ -1,177 +1,369 @@ /* * SuperH SCIF device driver. - * Copyright (c) 2007 Nobuhiro Iwamatsu - * - * 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. + * Copyright (C) 2013 Renesas Electronics Corporation + * Copyright (C) 2007,2008,2010, 2014 Nobuhiro Iwamatsu + * Copyright (C) 2002 - 2008 Paul Mundt * - * 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 + * SPDX-License-Identifier: GPL-2.0+ */ #include +#include +#include +#include +#include #include +#include +#include +#include +#include "serial_sh.h" -#ifdef CFG_SCIF_CONSOLE +DECLARE_GLOBAL_DATA_PTR; -#if defined (CONFIG_CONS_SCIF0) -#define SCIF_BASE SCIF0_BASE -#elif defined (CONFIG_CONS_SCIF1) -#define SCIF_BASE SCIF1_BASE +#if defined(CONFIG_CPU_SH7760) || \ + defined(CONFIG_CPU_SH7780) || \ + defined(CONFIG_CPU_SH7785) || \ + defined(CONFIG_CPU_SH7786) +static int scif_rxfill(struct uart_port *port) +{ + return sci_in(port, SCRFDR) & 0xff; +} +#elif defined(CONFIG_CPU_SH7763) +static int scif_rxfill(struct uart_port *port) +{ + if ((port->mapbase == 0xffe00000) || + (port->mapbase == 0xffe08000)) { + /* SCIF0/1*/ + return sci_in(port, SCRFDR) & 0xff; + } else { + /* SCIF2 */ + return sci_in(port, SCFDR) & SCIF2_RFDC_MASK; + } +} +#elif defined(CONFIG_ARCH_SH7372) +static int scif_rxfill(struct uart_port *port) +{ + if (port->type == PORT_SCIFA) + return sci_in(port, SCFDR) & SCIF_RFDC_MASK; + else + return sci_in(port, SCRFDR); +} #else -#error "Default SCIF doesn't set....." +static int scif_rxfill(struct uart_port *port) +{ + return sci_in(port, SCFDR) & SCIF_RFDC_MASK; +} #endif -#define SCSMR (vu_short *)(SCIF_BASE + 0x0) -#define SCBRR (vu_char *)(SCIF_BASE + 0x4) -#define SCSCR (vu_short *)(SCIF_BASE + 0x8) -#define SCFTDR (vu_char *)(SCIF_BASE + 0xC) -#define SCFSR (vu_short *)(SCIF_BASE + 0x10) -#define SCFRDR (vu_char *)(SCIF_BASE + 0x14) -#define SCFCR (vu_short *)(SCIF_BASE + 0x18) -#define SCFDR (vu_short *)(SCIF_BASE + 0x1C) -#if defined(CONFIG_SH4A) -#define SCRFDR (vu_short *)(SCIF_BASE + 0x20) -#define SCSPTR (vu_short *)(SCIF_BASE + 0x24) -#define SCLSR (vu_short *)(SCIF_BASE + 0x28) -#define SCRER (vu_short *)(SCIF_BASE + 0x2C) -#elif defined (CONFIG_SH4) -#define SCSPTR (vu_short *)(SCIF_BASE + 0x20) -#define SCLSR (vu_short *)(SCIF_BASE + 0x24) -#elif defined (CONFIG_SH3) -#define SCLSR (vu_short *)(SCIF_BASE + 0x24) -#endif +static void sh_serial_init_generic(struct uart_port *port) +{ + sci_out(port, SCSCR , SCSCR_INIT(port)); + sci_out(port, SCSCR , SCSCR_INIT(port)); + sci_out(port, SCSMR, 0); + sci_out(port, SCSMR, 0); + sci_out(port, SCFCR, SCFCR_RFRST|SCFCR_TFRST); + sci_in(port, SCFCR); + sci_out(port, SCFCR, 0); +} -#define SCR_RE (1 << 4) -#define SCR_TE (1 << 5) -#define FCR_RFRST (1 << 1) /* RFCL */ -#define FCR_TFRST (1 << 2) /* TFCL */ -#define FSR_DR (1 << 0) -#define FSR_RDF (1 << 1) -#define FSR_FER (1 << 3) -#define FSR_BRK (1 << 4) -#define FSR_FER (1 << 3) -#define FSR_TEND (1 << 6) -#define FSR_ER (1 << 7) +static void +sh_serial_setbrg_generic(struct uart_port *port, int clk, int baudrate) +{ + if (port->clk_mode == EXT_CLK) { + unsigned short dl = DL_VALUE(baudrate, clk); + sci_out(port, DL, dl); + /* Need wait: Clock * 1/dl * 1/16 */ + udelay((1000000 * dl * 16 / clk) * 1000 + 1); + } else { + sci_out(port, SCBRR, SCBRR_VALUE(baudrate, clk)); + } +} -/*----------------------------------------------------------------------*/ +static void handle_error(struct uart_port *port) +{ + sci_in(port, SCxSR); + sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port)); + sci_in(port, SCLSR); + sci_out(port, SCLSR, 0x00); +} -void serial_setbrg (void) +static int serial_raw_putc(struct uart_port *port, const char c) { - DECLARE_GLOBAL_DATA_PTR; - int divisor = gd->baudrate * 32; + /* Tx fifo is empty */ + if (!(sci_in(port, SCxSR) & SCxSR_TEND(port))) + return -EAGAIN; + + sci_out(port, SCxTDR, c); + sci_out(port, SCxSR, sci_in(port, SCxSR) & ~SCxSR_TEND(port)); - *SCBRR = (CONFIG_SYS_CLK_FREQ + (divisor / 2)) / - (gd->baudrate * 32) - 1; + return 0; } -int serial_init (void) +static int serial_rx_fifo_level(struct uart_port *port) { - *SCSCR = (SCR_RE | SCR_TE); - *SCSMR = 0 ; - *SCSMR = 0; - *SCFCR = (FCR_RFRST | FCR_TFRST); - *SCFCR; - *SCFCR = 0; + return scif_rxfill(port); +} - serial_setbrg(); - return 0; +static int sh_serial_tstc_generic(struct uart_port *port) +{ + if (sci_in(port, SCxSR) & SCIF_ERRORS) { + handle_error(port); + return 0; + } + + return serial_rx_fifo_level(port) ? 1 : 0; } -static int serial_tx_fifo_level (void) +static int serial_getc_check(struct uart_port *port) { - return (*SCFDR >> 8) & 0x1F; + unsigned short status; + + status = sci_in(port, SCxSR); + + if (status & SCIF_ERRORS) + handle_error(port); + if (sci_in(port, SCLSR) & SCxSR_ORER(port)) + handle_error(port); + return status & (SCIF_DR | SCxSR_RDxF(port)); } -static int serial_rx_fifo_level (void) +static int sh_serial_getc_generic(struct uart_port *port) { - return (*SCFDR >> 0) & 0x1F; + unsigned short status; + char ch; + + if (!serial_getc_check(port)) + return -EAGAIN; + + ch = sci_in(port, SCxRDR); + status = sci_in(port, SCxSR); + + sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); + + if (status & SCIF_ERRORS) + handle_error(port); + + if (sci_in(port, SCLSR) & SCxSR_ORER(port)) + handle_error(port); + + return ch; } -void serial_raw_putc (const char c) +#ifdef CONFIG_DM_SERIAL + +static int sh_serial_pending(struct udevice *dev, bool input) { - unsigned int fsr_bits_to_clear; + struct uart_port *priv = dev_get_priv(dev); - while (1) { - if (*SCFSR & FSR_TEND) { /* Tx fifo is empty */ - fsr_bits_to_clear = FSR_TEND; - break; - } - } + return sh_serial_tstc_generic(priv); +} + +static int sh_serial_putc(struct udevice *dev, const char ch) +{ + struct uart_port *priv = dev_get_priv(dev); + + return serial_raw_putc(priv, ch); +} - *SCFTDR = c; - if (fsr_bits_to_clear != 0) - *SCFSR &= ~fsr_bits_to_clear; +static int sh_serial_getc(struct udevice *dev) +{ + struct uart_port *priv = dev_get_priv(dev); + + return sh_serial_getc_generic(priv); } -void serial_putc (const char c) +static int sh_serial_setbrg(struct udevice *dev, int baudrate) { - if (c == '\n') - serial_raw_putc ('\r'); - serial_raw_putc (c); + struct sh_serial_platdata *plat = dev_get_platdata(dev); + struct uart_port *priv = dev_get_priv(dev); + + sh_serial_setbrg_generic(priv, plat->clk, baudrate); + + return 0; } -void serial_puts (const char *s) +static int sh_serial_probe(struct udevice *dev) { - char c; - while ((c = *s++) != 0) - serial_putc (c); + struct sh_serial_platdata *plat = dev_get_platdata(dev); + struct uart_port *priv = dev_get_priv(dev); + + priv->membase = (unsigned char *)plat->base; + priv->mapbase = plat->base; + priv->type = plat->type; + priv->clk_mode = plat->clk_mode; + + sh_serial_init_generic(priv); + + return 0; } -int serial_tstc (void) +static const struct dm_serial_ops sh_serial_ops = { + .putc = sh_serial_putc, + .pending = sh_serial_pending, + .getc = sh_serial_getc, + .setbrg = sh_serial_setbrg, +}; + +#ifdef CONFIG_OF_CONTROL +static const struct udevice_id sh_serial_id[] ={ + {.compatible = "renesas,sci", .data = PORT_SCI}, + {.compatible = "renesas,scif", .data = PORT_SCIF}, + {.compatible = "renesas,scifa", .data = PORT_SCIFA}, + {} +}; + +static int sh_serial_ofdata_to_platdata(struct udevice *dev) { - return serial_rx_fifo_level() ? 1 : 0; + struct sh_serial_platdata *plat = dev_get_platdata(dev); + struct clk sh_serial_clk; + fdt_addr_t addr; + int ret; + + addr = devfdt_get_addr(dev); + if (!addr) + return -EINVAL; + + plat->base = addr; + + ret = clk_get_by_name(dev, "fck", &sh_serial_clk); + if (!ret) { + ret = clk_enable(&sh_serial_clk); + if (!ret) + plat->clk = clk_get_rate(&sh_serial_clk); + } else { + plat->clk = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), + "clock", 1); + } + + plat->type = dev_get_driver_data(dev); + return 0; } +#endif -#define FSR_ERR_CLEAR 0x0063 -#define RDRF_CLEAR 0x00fc -#define LSR_ORER 1 -void handle_error( void ){ +U_BOOT_DRIVER(serial_sh) = { + .name = "serial_sh", + .id = UCLASS_SERIAL, + .of_match = of_match_ptr(sh_serial_id), + .ofdata_to_platdata = of_match_ptr(sh_serial_ofdata_to_platdata), + .platdata_auto_alloc_size = sizeof(struct sh_serial_platdata), + .probe = sh_serial_probe, + .ops = &sh_serial_ops, + .flags = DM_FLAG_PRE_RELOC, + .priv_auto_alloc_size = sizeof(struct uart_port), +}; - (void)*SCFSR ; - *SCFSR = FSR_ERR_CLEAR ; - (void)*SCLSR ; - *SCLSR = 0x00 ; +#else /* CONFIG_DM_SERIAL */ + +#if defined(CONFIG_CONS_SCIF0) +# define SCIF_BASE SCIF0_BASE +#elif defined(CONFIG_CONS_SCIF1) +# define SCIF_BASE SCIF1_BASE +#elif defined(CONFIG_CONS_SCIF2) +# define SCIF_BASE SCIF2_BASE +#elif defined(CONFIG_CONS_SCIF3) +# define SCIF_BASE SCIF3_BASE +#elif defined(CONFIG_CONS_SCIF4) +# define SCIF_BASE SCIF4_BASE +#elif defined(CONFIG_CONS_SCIF5) +# define SCIF_BASE SCIF5_BASE +#elif defined(CONFIG_CONS_SCIF6) +# define SCIF_BASE SCIF6_BASE +#elif defined(CONFIG_CONS_SCIF7) +# define SCIF_BASE SCIF7_BASE +#else +# error "Default SCIF doesn't set....." +#endif + +#if defined(CONFIG_SCIF_A) + #define SCIF_BASE_PORT PORT_SCIFA +#elif defined(CONFIG_SCI) + #define SCIF_BASE_PORT PORT_SCI +#else + #define SCIF_BASE_PORT PORT_SCIF +#endif + +static struct uart_port sh_sci = { + .membase = (unsigned char *)SCIF_BASE, + .mapbase = SCIF_BASE, + .type = SCIF_BASE_PORT, +#ifdef CONFIG_SCIF_USE_EXT_CLK + .clk_mode = EXT_CLK, +#endif +}; + +static void sh_serial_setbrg(void) +{ + DECLARE_GLOBAL_DATA_PTR; + struct uart_port *port = &sh_sci; + + sh_serial_setbrg_generic(port, CONFIG_SH_SCIF_CLK_FREQ, gd->baudrate); } -int serial_getc_check( void ){ - unsigned short status; +static int sh_serial_init(void) +{ + struct uart_port *port = &sh_sci; - status = *SCFSR ; + sh_serial_init_generic(port); + serial_setbrg(); - if (status & (FSR_FER | FSR_FER | FSR_ER | FSR_BRK)) - handle_error(); - if( *SCLSR & LSR_ORER ) - handle_error(); - return (status & ( FSR_DR | FSR_RDF )); + return 0; } -int serial_getc (void) +static void sh_serial_putc(const char c) { - unsigned short status ; - char ch; - while(!serial_getc_check()); + struct uart_port *port = &sh_sci; - ch = *SCFRDR; - status = *SCFSR ; + if (c == '\n') { + while (1) { + if (serial_raw_putc(port, '\r') != -EAGAIN) + break; + } + } + while (1) { + if (serial_raw_putc(port, c) != -EAGAIN) + break; + } +} - *SCFSR = RDRF_CLEAR ; +static int sh_serial_tstc(void) +{ + struct uart_port *port = &sh_sci; - if (status & (FSR_FER | FSR_FER | FSR_ER | FSR_BRK)) - handle_error(); + return sh_serial_tstc_generic(port); +} + +static int sh_serial_getc(void) +{ + struct uart_port *port = &sh_sci; + int ch; + + while (1) { + ch = sh_serial_getc_generic(port); + if (ch != -EAGAIN) + break; + } + + return ch; +} - if( *SCLSR & LSR_ORER ) - handle_error(); +static struct serial_device sh_serial_drv = { + .name = "sh_serial", + .start = sh_serial_init, + .stop = NULL, + .setbrg = sh_serial_setbrg, + .putc = sh_serial_putc, + .puts = default_serial_puts, + .getc = sh_serial_getc, + .tstc = sh_serial_tstc, +}; - return ch ; +void sh_serial_initialize(void) +{ + serial_register(&sh_serial_drv); } -#endif /* CFG_SCIF_CONSOLE */ +__weak struct serial_device *default_serial_console(void) +{ + return &sh_serial_drv; +} +#endif /* CONFIG_DM_SERIAL */