X-Git-Url: https://git.kernelconcepts.de/?a=blobdiff_plain;f=drivers%2Fserial%2Fns16550.c;h=af5beba39ffac5bfc5395a4f35abfc0d882194e4;hb=9b416a9f4ca7cf5ac4d5f7143d67edde7f7d7326;hp=079f67d3801b0c3297076beb62f7d8c02565323e;hpb=6defdc0b5552ab1af4a66a8abac8196cbb6b9e15;p=karo-tx-uboot.git diff --git a/drivers/serial/ns16550.c b/drivers/serial/ns16550.c index 079f67d380..af5beba39f 100644 --- a/drivers/serial/ns16550.c +++ b/drivers/serial/ns16550.c @@ -4,18 +4,26 @@ * modified to use CONFIG_SYS_ISA_MEM and new defines */ -#include +#include +#include +#include +#include #include +#include #include #include #include +DECLARE_GLOBAL_DATA_PTR; + #define UART_LCRVAL UART_LCR_8N1 /* 8 data, 1 stop, no parity */ #define UART_MCRVAL (UART_MCR_DTR | \ UART_MCR_RTS) /* RTS/DTR */ #define UART_FCRVAL (UART_FCR_FIFO_EN | \ UART_FCR_RXSR | \ UART_FCR_TXSR) /* Clear & enable FIFOs */ + +#ifndef CONFIG_DM_SERIAL #ifdef CONFIG_SYS_NS16550_PORT_MAPPED #define serial_out(x, y) outb(x, (ulong)y) #define serial_in(y) inb((ulong)y) @@ -29,6 +37,7 @@ #define serial_out(x, y) writeb(x, y) #define serial_in(y) readb(y) #endif +#endif /* !CONFIG_DM_SERIAL */ #if defined(CONFIG_SOC_KEYSTONE) #define UART_REG_VAL_PWREMU_MGMT_UART_DISABLE 0 @@ -45,20 +54,95 @@ #define CONFIG_SYS_NS16550_IER 0x00 #endif /* CONFIG_SYS_NS16550_IER */ +#ifdef CONFIG_DM_SERIAL +static void ns16550_writeb(NS16550_t port, int offset, int value) +{ + struct ns16550_platdata *plat = port->plat; + unsigned char *addr; + + offset *= 1 << plat->reg_shift; + addr = map_sysmem(plat->base, 0) + offset; + /* + * As far as we know it doesn't make sense to support selection of + * these options at run-time, so use the existing CONFIG options. + */ +#ifdef CONFIG_SYS_NS16550_PORT_MAPPED + outb(value, (ulong)addr); +#elif defined(CONFIG_SYS_NS16550_MEM32) && !defined(CONFIG_SYS_BIG_ENDIAN) + out_le32(addr, value); +#elif defined(CONFIG_SYS_NS16550_MEM32) && defined(CONFIG_SYS_BIG_ENDIAN) + out_be32(addr, value); +#elif defined(CONFIG_SYS_BIG_ENDIAN) + writeb(value, addr + (1 << plat->reg_shift) - 1); +#else + writeb(value, addr); +#endif +} + +static int ns16550_readb(NS16550_t port, int offset) +{ + struct ns16550_platdata *plat = port->plat; + unsigned char *addr; + + offset *= 1 << plat->reg_shift; + addr = map_sysmem(plat->base, 0) + offset; +#ifdef CONFIG_SYS_NS16550_PORT_MAPPED + return inb((ulong)addr); +#elif defined(CONFIG_SYS_NS16550_MEM32) && !defined(CONFIG_SYS_BIG_ENDIAN) + return in_le32(addr); +#elif defined(CONFIG_SYS_NS16550_MEM32) && defined(CONFIG_SYS_BIG_ENDIAN) + return in_be32(addr); +#elif defined(CONFIG_SYS_BIG_ENDIAN) + return readb(addr + (1 << plat->reg_shift) - 1); +#else + return readb(addr); +#endif +} + +/* We can clean these up once everything is moved to driver model */ +#define serial_out(value, addr) \ + ns16550_writeb(com_port, addr - (unsigned char *)com_port, value) +#define serial_in(addr) \ + ns16550_readb(com_port, addr - (unsigned char *)com_port) +#endif + +int ns16550_calc_divisor(NS16550_t port, int clock, int baudrate) +{ + const unsigned int mode_x_div = 16; + +#ifdef CONFIG_OMAP1510 + /* If can't cleanly clock 115200 set div to 1 */ + if ((clock == 12000000) && (baudrate == 115200)) { + port->osc_12m_sel = OSC_12M_SEL; /* enable 6.5 * divisor */ + return 1; /* return 1 for base divisor */ + } + port->osc_12m_sel = 0; /* clear if previsouly set */ +#endif + + return DIV_ROUND_CLOSEST(clock, mode_x_div * baudrate); +} + +static void NS16550_setbrg(NS16550_t com_port, int baud_divisor) +{ + serial_out(UART_LCR_BKSE | UART_LCRVAL, &com_port->lcr); + serial_out(baud_divisor & 0xff, &com_port->dll); + serial_out((baud_divisor >> 8) & 0xff, &com_port->dlm); + serial_out(UART_LCRVAL, &com_port->lcr); +} + 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) { - serial_out(UART_LCR_DLAB, &com_port->lcr); - serial_out(baud_divisor & 0xff, &com_port->dll); - serial_out((baud_divisor >> 8) & 0xff, &com_port->dlm); - serial_out(UART_LCRVAL, &com_port->lcr); + if (baud_divisor != -1) + NS16550_setbrg(com_port, baud_divisor); serial_out(0, &com_port->mdr1); } #endif @@ -71,16 +155,11 @@ void NS16550_init(NS16550_t com_port, int baud_divisor) defined(CONFIG_TI81XX) || defined(CONFIG_AM43XX) serial_out(0x7, &com_port->mdr1); /* mode select reset TL16C750*/ #endif - serial_out(UART_LCR_BKSE | UART_LCRVAL, &com_port->lcr); - serial_out(0, &com_port->dll); - serial_out(0, &com_port->dlm); - serial_out(UART_LCRVAL, &com_port->lcr); + NS16550_setbrg(com_port, 0); serial_out(UART_MCRVAL, &com_port->mcr); serial_out(UART_FCRVAL, &com_port->fcr); - serial_out(UART_LCR_BKSE | UART_LCRVAL, &com_port->lcr); - serial_out(baud_divisor & 0xff, &com_port->dll); - serial_out((baud_divisor >> 8) & 0xff, &com_port->dlm); - serial_out(UART_LCRVAL, &com_port->lcr); + if (baud_divisor != -1) + NS16550_setbrg(com_port, baud_divisor); #if defined(CONFIG_OMAP) || \ defined(CONFIG_AM33XX) || defined(CONFIG_SOC_DA8XX) || \ defined(CONFIG_TI81XX) || defined(CONFIG_AM43XX) @@ -97,16 +176,10 @@ void NS16550_init(NS16550_t com_port, int baud_divisor) void NS16550_reinit(NS16550_t com_port, int baud_divisor) { serial_out(CONFIG_SYS_NS16550_IER, &com_port->ier); - serial_out(UART_LCR_BKSE | UART_LCRVAL, &com_port->lcr); - serial_out(0, &com_port->dll); - serial_out(0, &com_port->dlm); - serial_out(UART_LCRVAL, &com_port->lcr); + NS16550_setbrg(com_port, 0); serial_out(UART_MCRVAL, &com_port->mcr); serial_out(UART_FCRVAL, &com_port->fcr); - serial_out(UART_LCR_BKSE, &com_port->lcr); - serial_out(baud_divisor & 0xff, &com_port->dll); - serial_out((baud_divisor >> 8) & 0xff, &com_port->dlm); - serial_out(UART_LCRVAL, &com_port->lcr); + NS16550_setbrg(com_port, baud_divisor); } #endif /* CONFIG_NS16550_MIN_FUNCTIONS */ @@ -145,3 +218,93 @@ int NS16550_tstc(NS16550_t com_port) } #endif /* CONFIG_NS16550_MIN_FUNCTIONS */ + +#ifdef CONFIG_DM_SERIAL +static int ns16550_serial_putc(struct udevice *dev, const char ch) +{ + struct NS16550 *const com_port = dev_get_priv(dev); + + if (!(serial_in(&com_port->lsr) & UART_LSR_THRE)) + return -EAGAIN; + serial_out(ch, &com_port->thr); + + /* + * Call watchdog_reset() upon newline. This is done here in putc + * since the environment code uses a single puts() to print the complete + * environment upon "printenv". So we can't put this watchdog call + * in puts(). + */ + if (ch == '\n') + WATCHDOG_RESET(); + + return 0; +} + +static int ns16550_serial_pending(struct udevice *dev, bool input) +{ + struct NS16550 *const com_port = dev_get_priv(dev); + + if (input) + return serial_in(&com_port->lsr) & UART_LSR_DR ? 1 : 0; + else + return serial_in(&com_port->lsr) & UART_LSR_THRE ? 0 : 1; +} + +static int ns16550_serial_getc(struct udevice *dev) +{ + struct NS16550 *const com_port = dev_get_priv(dev); + + if (!(serial_in(&com_port->lsr) & UART_LSR_DR)) + return -EAGAIN; + + return serial_in(&com_port->rbr); +} + +static int ns16550_serial_setbrg(struct udevice *dev, int baudrate) +{ + struct NS16550 *const com_port = dev_get_priv(dev); + struct ns16550_platdata *plat = com_port->plat; + int clock_divisor; + + clock_divisor = ns16550_calc_divisor(com_port, plat->clock, baudrate); + + NS16550_setbrg(com_port, clock_divisor); + + return 0; +} + +int ns16550_serial_probe(struct udevice *dev) +{ + struct NS16550 *const com_port = dev_get_priv(dev); + + com_port->plat = dev_get_platdata(dev); + NS16550_init(com_port, -1); + + return 0; +} + +#ifdef CONFIG_OF_CONTROL +int ns16550_serial_ofdata_to_platdata(struct udevice *dev) +{ + struct ns16550_platdata *plat = dev->platdata; + fdt_addr_t addr; + + addr = fdtdec_get_addr(gd->fdt_blob, dev->of_offset, "reg"); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + plat->base = addr; + plat->reg_shift = fdtdec_get_int(gd->fdt_blob, dev->of_offset, + "reg-shift", 1); + + return 0; +} +#endif + +const struct dm_serial_ops ns16550_serial_ops = { + .putc = ns16550_serial_putc, + .pending = ns16550_serial_pending, + .getc = ns16550_serial_getc, + .setbrg = ns16550_serial_setbrg, +}; +#endif /* CONFIG_DM_SERIAL */