diff options
Diffstat (limited to 'drivers/serial')
-rw-r--r-- | drivers/serial/atmel_usart.c | 116 | ||||
-rw-r--r-- | drivers/serial/ns16550.c | 9 | ||||
-rw-r--r-- | drivers/serial/serial-uclass.c | 112 | ||||
-rw-r--r-- | drivers/serial/serial_pl01x.c | 48 | ||||
-rw-r--r-- | drivers/serial/serial_sh.h | 2 | ||||
-rw-r--r-- | drivers/serial/serial_tegra.c | 16 | ||||
-rw-r--r-- | drivers/serial/serial_uniphier.c | 19 | ||||
-rw-r--r-- | drivers/serial/usbtty.c | 2 |
8 files changed, 260 insertions, 64 deletions
diff --git a/drivers/serial/atmel_usart.c b/drivers/serial/atmel_usart.c index 8f0e3489a0..4fe992bf2b 100644 --- a/drivers/serial/atmel_usart.c +++ b/drivers/serial/atmel_usart.c @@ -7,11 +7,16 @@ * SPDX-License-Identifier: GPL-2.0+ */ #include <common.h> +#include <dm.h> +#include <errno.h> #include <watchdog.h> #include <serial.h> #include <linux/compiler.h> #include <asm/io.h> +#ifdef CONFIG_DM_SERIAL +#include <asm/arch/atmel_serial.h> +#endif #include <asm/arch/clk.h> #include <asm/arch/hardware.h> @@ -19,9 +24,9 @@ DECLARE_GLOBAL_DATA_PTR; -static void atmel_serial_setbrg(void) +static void atmel_serial_setbrg_internal(atmel_usart3_t *usart, int id, + int baudrate) { - atmel_usart3_t *usart = (atmel_usart3_t *)CONFIG_USART_BASE; unsigned long divisor; unsigned long usart_hz; @@ -30,15 +35,13 @@ static void atmel_serial_setbrg(void) * Baud Rate = -------------- * 16 * CD */ - usart_hz = get_usart_clk_rate(CONFIG_USART_ID); - divisor = (usart_hz / 16 + gd->baudrate / 2) / gd->baudrate; + usart_hz = get_usart_clk_rate(id); + divisor = (usart_hz / 16 + baudrate / 2) / baudrate; writel(USART3_BF(CD, divisor), &usart->brgr); } -static int atmel_serial_init(void) +static void atmel_serial_init_internal(atmel_usart3_t *usart) { - atmel_usart3_t *usart = (atmel_usart3_t *)CONFIG_USART_BASE; - /* * Just in case: drain transmitter register * 1000us is enough for baudrate >= 9600 @@ -47,9 +50,10 @@ static int atmel_serial_init(void) __udelay(1000); writel(USART3_BIT(RSTRX) | USART3_BIT(RSTTX), &usart->cr); +} - serial_setbrg(); - +static void atmel_serial_activate(atmel_usart3_t *usart) +{ writel((USART3_BF(USART_MODE, USART3_USART_MODE_NORMAL) | USART3_BF(USCLKS, USART3_USCLKS_MCK) | USART3_BF(CHRL, USART3_CHRL_8) @@ -59,6 +63,22 @@ static int atmel_serial_init(void) writel(USART3_BIT(RXEN) | USART3_BIT(TXEN), &usart->cr); /* 100us is enough for the new settings to be settled */ __udelay(100); +} + +#ifndef CONFIG_DM_SERIAL +static void atmel_serial_setbrg(void) +{ + atmel_serial_setbrg_internal((atmel_usart3_t *)CONFIG_USART_BASE, + CONFIG_USART_ID, gd->baudrate); +} + +static int atmel_serial_init(void) +{ + atmel_usart3_t *usart = (atmel_usart3_t *)CONFIG_USART_BASE; + + atmel_serial_init_internal(usart); + serial_setbrg(); + atmel_serial_activate(usart); return 0; } @@ -109,3 +129,81 @@ __weak struct serial_device *default_serial_console(void) { return &atmel_serial_drv; } +#endif + +#ifdef CONFIG_DM_SERIAL + +struct atmel_serial_priv { + atmel_usart3_t *usart; +}; + +int atmel_serial_setbrg(struct udevice *dev, int baudrate) +{ + struct atmel_serial_priv *priv = dev_get_priv(dev); + + atmel_serial_setbrg_internal(priv->usart, 0 /* ignored */, baudrate); + atmel_serial_activate(priv->usart); + + return 0; +} + +static int atmel_serial_getc(struct udevice *dev) +{ + struct atmel_serial_priv *priv = dev_get_priv(dev); + + if (!(readl(&priv->usart->csr) & USART3_BIT(RXRDY))) + return -EAGAIN; + + return readl(&priv->usart->rhr); +} + +static int atmel_serial_putc(struct udevice *dev, const char ch) +{ + struct atmel_serial_priv *priv = dev_get_priv(dev); + + if (!(readl(&priv->usart->csr) & USART3_BIT(TXRDY))) + return -EAGAIN; + + writel(ch, &priv->usart->thr); + + return 0; +} + +static int atmel_serial_pending(struct udevice *dev, bool input) +{ + struct atmel_serial_priv *priv = dev_get_priv(dev); + uint32_t csr = readl(&priv->usart->csr); + + if (input) + return csr & USART3_BIT(RXRDY) ? 1 : 0; + else + return csr & USART3_BIT(TXEMPTY) ? 0 : 1; +} + +static const struct dm_serial_ops atmel_serial_ops = { + .putc = atmel_serial_putc, + .pending = atmel_serial_pending, + .getc = atmel_serial_getc, + .setbrg = atmel_serial_setbrg, +}; + +static int atmel_serial_probe(struct udevice *dev) +{ + struct atmel_serial_platdata *plat = dev->platdata; + struct atmel_serial_priv *priv = dev_get_priv(dev); + + priv->usart = (atmel_usart3_t *)plat->base_addr; + atmel_serial_init_internal(priv->usart); + + return 0; +} + +U_BOOT_DRIVER(serial_atmel) = { + .name = "serial_atmel", + .id = UCLASS_SERIAL, + .probe = atmel_serial_probe, + .ops = &atmel_serial_ops, + .flags = DM_FLAG_PRE_RELOC, + .priv_auto_alloc_size = sizeof(struct atmel_serial_priv), +}; +#endif diff --git a/drivers/serial/ns16550.c b/drivers/serial/ns16550.c index 8f051914f5..af5beba39f 100644 --- a/drivers/serial/ns16550.c +++ b/drivers/serial/ns16550.c @@ -132,11 +132,12 @@ static void NS16550_setbrg(NS16550_t com_port, int baud_divisor) void NS16550_init(NS16550_t com_port, int baud_divisor) { -#if (defined(CONFIG_SPL_BUILD) && defined(CONFIG_OMAP34XX)) +#if (defined(CONFIG_SPL_BUILD) && \ + (defined(CONFIG_OMAP34XX) || defined(CONFIG_OMAP44XX))) /* - * On some OMAP3 devices when UART3 is configured for boot mode before - * SPL starts only THRE bit is set. We have to empty the transmitter - * before initialization starts. + * On some OMAP3/OMAP4 devices when UART3 is configured for boot mode + * before SPL starts only THRE bit is set. We have to empty the + * transmitter before initialization starts. */ if ((serial_in(&com_port->lsr) & (UART_LSR_TEMT | UART_LSR_THRE)) == UART_LSR_THRE) { diff --git a/drivers/serial/serial-uclass.c b/drivers/serial/serial-uclass.c index 71f1a5cb91..d1b5777cec 100644 --- a/drivers/serial/serial-uclass.c +++ b/drivers/serial/serial-uclass.c @@ -6,6 +6,7 @@ #include <common.h> #include <dm.h> +#include <environment.h> #include <errno.h> #include <fdtdec.h> #include <os.h> @@ -19,8 +20,10 @@ DECLARE_GLOBAL_DATA_PTR; -/* The currently-selected console serial device */ -struct udevice *cur_dev __attribute__ ((section(".data"))); +/* + * Table with supported baudrates (defined in config_xyz.h) + */ +static const unsigned long baudrate_table[] = CONFIG_SYS_BAUDRATE_TABLE; #ifndef CONFIG_SYS_MALLOC_F_LEN #error "Serial is required before relocation - define CONFIG_SYS_MALLOC_F_LEN to make this work" @@ -28,25 +31,30 @@ struct udevice *cur_dev __attribute__ ((section(".data"))); static void serial_find_console_or_panic(void) { + struct udevice *dev; + #ifdef CONFIG_OF_CONTROL int node; /* Check for a chosen console */ node = fdtdec_get_chosen_node(gd->fdt_blob, "stdout-path"); if (node < 0) - node = fdtdec_get_alias_node(gd->fdt_blob, "console"); - if (!uclass_get_device_by_of_offset(UCLASS_SERIAL, node, &cur_dev)) + node = fdt_path_offset(gd->fdt_blob, "console"); + if (!uclass_get_device_by_of_offset(UCLASS_SERIAL, node, &dev)) { + gd->cur_serial_dev = dev; return; + } /* * If the console is not marked to be bound before relocation, bind * it anyway. */ if (node > 0 && - !lists_bind_fdt(gd->dm_root, gd->fdt_blob, node, &cur_dev)) { - if (!device_probe(cur_dev)) + !lists_bind_fdt(gd->dm_root, gd->fdt_blob, node, &dev)) { + if (!device_probe(dev)) { + gd->cur_serial_dev = dev; return; - cur_dev = NULL; + } } #endif /* @@ -61,11 +69,12 @@ static void serial_find_console_or_panic(void) #else #define INDEX 0 #endif - if (uclass_get_device_by_seq(UCLASS_SERIAL, INDEX, &cur_dev) && - uclass_get_device(UCLASS_SERIAL, INDEX, &cur_dev) && - (uclass_first_device(UCLASS_SERIAL, &cur_dev) || !cur_dev)) + if (uclass_get_device_by_seq(UCLASS_SERIAL, INDEX, &dev) && + uclass_get_device(UCLASS_SERIAL, INDEX, &dev) && + (uclass_first_device(UCLASS_SERIAL, &dev) || !dev)) panic("No serial driver found"); #undef INDEX + gd->cur_serial_dev = dev; } /* Called prior to relocation */ @@ -127,40 +136,42 @@ static int _serial_tstc(struct udevice *dev) void serial_putc(char ch) { - _serial_putc(cur_dev, ch); + _serial_putc(gd->cur_serial_dev, ch); } void serial_puts(const char *str) { - _serial_puts(cur_dev, str); + _serial_puts(gd->cur_serial_dev, str); } int serial_getc(void) { - return _serial_getc(cur_dev); + return _serial_getc(gd->cur_serial_dev); } int serial_tstc(void) { - return _serial_tstc(cur_dev); + return _serial_tstc(gd->cur_serial_dev); } void serial_setbrg(void) { - struct dm_serial_ops *ops = serial_get_ops(cur_dev); + struct dm_serial_ops *ops = serial_get_ops(gd->cur_serial_dev); if (ops->setbrg) - ops->setbrg(cur_dev, gd->baudrate); + ops->setbrg(gd->cur_serial_dev, gd->baudrate); } void serial_stdio_init(void) { } +#ifdef CONFIG_DM_STDIO static void serial_stub_putc(struct stdio_dev *sdev, const char ch) { _serial_putc(sdev->priv, ch); } +#endif void serial_stub_puts(struct stdio_dev *sdev, const char *str) { @@ -177,11 +188,74 @@ int serial_stub_tstc(struct stdio_dev *sdev) return _serial_tstc(sdev->priv); } +/** + * on_baudrate() - Update the actual baudrate when the env var changes + * + * This will check for a valid baudrate and only apply it if valid. + */ +static int on_baudrate(const char *name, const char *value, enum env_op op, + int flags) +{ + int i; + int baudrate; + + switch (op) { + case env_op_create: + case env_op_overwrite: + /* + * Switch to new baudrate if new baudrate is supported + */ + baudrate = simple_strtoul(value, NULL, 10); + + /* Not actually changing */ + if (gd->baudrate == baudrate) + return 0; + + for (i = 0; i < ARRAY_SIZE(baudrate_table); ++i) { + if (baudrate == baudrate_table[i]) + break; + } + if (i == ARRAY_SIZE(baudrate_table)) { + if ((flags & H_FORCE) == 0) + printf("## Baudrate %d bps not supported\n", + baudrate); + return 1; + } + if ((flags & H_INTERACTIVE) != 0) { + printf("## Switch baudrate to %d bps and press ENTER ...\n", + baudrate); + udelay(50000); + } + + gd->baudrate = baudrate; + + serial_setbrg(); + + udelay(50000); + + if ((flags & H_INTERACTIVE) != 0) + while (1) { + if (getc() == '\r') + break; + } + + return 0; + case env_op_delete: + printf("## Baudrate may not be deleted\n"); + return 1; + default: + return 0; + } +} +U_BOOT_ENV_CALLBACK(baudrate, on_baudrate); + static int serial_post_probe(struct udevice *dev) { - struct stdio_dev sdev; struct dm_serial_ops *ops = serial_get_ops(dev); +#ifdef CONFIG_DM_STDIO struct serial_dev_priv *upriv = dev->uclass_priv; + struct stdio_dev sdev; +#endif int ret; /* Set the baud rate */ @@ -191,9 +265,9 @@ static int serial_post_probe(struct udevice *dev) return ret; } +#ifdef CONFIG_DM_STDIO if (!(gd->flags & GD_FLG_RELOC)) return 0; - memset(&sdev, '\0', sizeof(sdev)); strncpy(sdev.name, dev->name, sizeof(sdev.name)); @@ -204,7 +278,7 @@ static int serial_post_probe(struct udevice *dev) sdev.getc = serial_stub_getc; sdev.tstc = serial_stub_tstc; stdio_register_dev(&sdev, &upriv->sdev); - +#endif return 0; } diff --git a/drivers/serial/serial_pl01x.c b/drivers/serial/serial_pl01x.c index 38dda91021..e1bf4965f9 100644 --- a/drivers/serial/serial_pl01x.c +++ b/drivers/serial/serial_pl01x.c @@ -72,30 +72,39 @@ static int pl01x_tstc(struct pl01x_regs *regs) static int pl01x_generic_serial_init(struct pl01x_regs *regs, enum pl01x_type type) { - unsigned int lcr; - + switch (type) { + case TYPE_PL010: + /* disable everything */ + writel(0, ®s->pl010_cr); + break; + case TYPE_PL011: #ifdef CONFIG_PL011_SERIAL_FLUSH_ON_INIT - if (type == TYPE_PL011) { /* Empty RX fifo if necessary */ if (readl(®s->pl011_cr) & UART_PL011_CR_UARTEN) { while (!(readl(®s->fr) & UART_PL01x_FR_RXFE)) readl(®s->dr); } - } #endif + /* disable everything */ + writel(0, ®s->pl011_cr); + break; + default: + return -EINVAL; + } - /* First, disable everything */ - writel(0, ®s->pl010_cr); + return 0; +} - /* Set the UART to be 8 bits, 1 stop bit, no parity, fifo enabled */ +static int set_line_control(struct pl01x_regs *regs) +{ + unsigned int lcr; + /* + * Internal update of baud rate register require line + * control register write + */ lcr = UART_PL011_LCRH_WLEN_8 | UART_PL011_LCRH_FEN; - writel(lcr, ®s->pl011_lcrh); - - switch (type) { - case TYPE_PL010: - break; - case TYPE_PL011: { #ifdef CONFIG_PL011_SERIAL_RLCR + { int i; /* @@ -107,15 +116,9 @@ static int pl01x_generic_serial_init(struct pl01x_regs *regs, writel(lcr, ®s->fr); writel(lcr, ®s->pl011_rlcr); - /* lcrh needs to be set again for change to be effective */ - writel(lcr, ®s->pl011_lcrh); -#endif - break; - } - default: - return -EINVAL; } - +#endif + writel(lcr, ®s->pl011_lcrh); return 0; } @@ -175,6 +178,7 @@ static int pl01x_generic_setbrg(struct pl01x_regs *regs, enum pl01x_type type, writel(divider, ®s->pl011_ibrd); writel(fraction, ®s->pl011_fbrd); + set_line_control(regs); /* Finally, enable the UART */ writel(UART_PL011_CR_UARTEN | UART_PL011_CR_TXE | UART_PL011_CR_RXE | UART_PL011_CR_RTS, ®s->pl011_cr); @@ -201,7 +205,7 @@ static void pl01x_serial_init_baud(int baudrate) base_regs = (struct pl01x_regs *)port[CONFIG_CONS_INDEX]; pl01x_generic_serial_init(base_regs, pl01x_type); - pl01x_generic_setbrg(base_regs, TYPE_PL010, clock, baudrate); + pl01x_generic_setbrg(base_regs, pl01x_type, clock, baudrate); } /* diff --git a/drivers/serial/serial_sh.h b/drivers/serial/serial_sh.h index 53406e5855..ef88c8f273 100644 --- a/drivers/serial/serial_sh.h +++ b/drivers/serial/serial_sh.h @@ -433,7 +433,7 @@ static inline void sci_##name##_out(struct uart_port *port,\ SCI_OUT(sci_size, sci_offset, value);\ } -#if defined(CONFIG_SH3) || \ +#if defined(CONFIG_CPU_SH3) || \ defined(CONFIG_ARCH_SH7367) || \ defined(CONFIG_ARCH_SH7377) || \ defined(CONFIG_ARCH_SH7372) || \ diff --git a/drivers/serial/serial_tegra.c b/drivers/serial/serial_tegra.c index 7eb70e1de1..b9227f0563 100644 --- a/drivers/serial/serial_tegra.c +++ b/drivers/serial/serial_tegra.c @@ -9,6 +9,7 @@ #include <ns16550.h> #include <serial.h> +#ifdef CONFIG_OF_CONTROL static const struct udevice_id tegra_serial_ids[] = { { .compatible = "nvidia,tegra20-uart" }, { } @@ -26,13 +27,28 @@ static int tegra_serial_ofdata_to_platdata(struct udevice *dev) return 0; } +#else +struct ns16550_platdata tegra_serial = { + .base = CONFIG_SYS_NS16550_COM1, + .reg_shift = 2, + .clock = V_NS16550_CLK, +}; + +U_BOOT_DEVICE(ns16550_serial) = { + "serial_tegra20", &tegra_serial +}; +#endif + U_BOOT_DRIVER(serial_ns16550) = { .name = "serial_tegra20", .id = UCLASS_SERIAL, +#ifdef CONFIG_OF_CONTROL .of_match = tegra_serial_ids, .ofdata_to_platdata = tegra_serial_ofdata_to_platdata, .platdata_auto_alloc_size = sizeof(struct ns16550_platdata), +#endif .priv_auto_alloc_size = sizeof(struct NS16550), .probe = ns16550_serial_probe, .ops = &ns16550_serial_ops, + .flags = DM_FLAG_PRE_RELOC, }; diff --git a/drivers/serial/serial_uniphier.c b/drivers/serial/serial_uniphier.c index 6046efb51f..e8a1608b99 100644 --- a/drivers/serial/serial_uniphier.c +++ b/drivers/serial/serial_uniphier.c @@ -11,6 +11,7 @@ #include <dm/device.h> #include <dm/platform_data/serial-uniphier.h> #include <serial.h> +#include <fdtdec.h> #define UART_REG(x) \ u8 x; \ @@ -113,19 +114,21 @@ static int uniphier_serial_remove(struct udevice *dev) } #ifdef CONFIG_OF_CONTROL -static const struct udevice_id uniphier_uart_of_match = { - { .compatible = "panasonic,uniphier-uart"}, +static const struct udevice_id uniphier_uart_of_match[] = { + { .compatible = "panasonic,uniphier-uart" }, {}, }; static int uniphier_serial_ofdata_to_platdata(struct udevice *dev) { - /* - * TODO: Masahiro Yamada (yamada.m@jp.panasonic.com) - * - * Implement conversion code from DTB to platform data - * when supporting CONFIG_OF_CONTROL on UniPhir platform. - */ + struct uniphier_serial_platform_data *plat = dev_get_platdata(dev); + DECLARE_GLOBAL_DATA_PTR; + + plat->base = fdtdec_get_addr(gd->fdt_blob, dev->of_offset, "reg"); + plat->uartclk = fdtdec_get_int(gd->fdt_blob, dev->of_offset, + "clock-frequency", 0); + + return 0; } #endif diff --git a/drivers/serial/usbtty.c b/drivers/serial/usbtty.c index 7fb0b92078..75f0ec31bb 100644 --- a/drivers/serial/usbtty.c +++ b/drivers/serial/usbtty.c @@ -882,7 +882,7 @@ static int write_buffer (circbuf_t * buf) space_avail = current_urb->buffer_length - current_urb->actual_length; - popnum = min(space_avail, buf->size); + popnum = min(space_avail, (int)buf->size); if (popnum == 0) break; |