2002-04-01 23:29:03 +09:00
|
|
|
/*
|
|
|
|
* COM1 NS16550 support
|
2010-04-15 23:07:28 +09:00
|
|
|
* originally from linux source (arch/powerpc/boot/ns16550.c)
|
2008-10-16 22:01:15 +09:00
|
|
|
* modified to use CONFIG_SYS_ISA_MEM and new defines
|
2002-04-01 23:29:03 +09:00
|
|
|
*/
|
|
|
|
|
2014-09-05 07:27:32 +09:00
|
|
|
#include <common.h>
|
2016-09-08 15:47:29 +09:00
|
|
|
#include <clk.h>
|
2014-09-05 07:27:34 +09:00
|
|
|
#include <dm.h>
|
|
|
|
#include <errno.h>
|
2002-04-01 23:29:03 +09:00
|
|
|
#include <ns16550.h>
|
2014-09-05 07:27:34 +09:00
|
|
|
#include <serial.h>
|
2010-02-02 07:34:25 +09:00
|
|
|
#include <watchdog.h>
|
2010-04-23 23:05:46 +09:00
|
|
|
#include <linux/types.h>
|
|
|
|
#include <asm/io.h>
|
2002-04-01 23:29:03 +09:00
|
|
|
|
2014-09-05 07:27:34 +09:00
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
|
2009-04-03 18:53:01 +09:00
|
|
|
#define UART_LCRVAL UART_LCR_8N1 /* 8 data, 1 stop, no parity */
|
|
|
|
#define UART_MCRVAL (UART_MCR_DTR | \
|
|
|
|
UART_MCR_RTS) /* RTS/DTR */
|
2014-09-05 07:27:34 +09:00
|
|
|
|
|
|
|
#ifndef CONFIG_DM_SERIAL
|
2010-04-23 23:05:46 +09:00
|
|
|
#ifdef CONFIG_SYS_NS16550_PORT_MAPPED
|
2011-10-16 04:14:09 +09:00
|
|
|
#define serial_out(x, y) outb(x, (ulong)y)
|
|
|
|
#define serial_in(y) inb((ulong)y)
|
2011-09-02 07:47:14 +09:00
|
|
|
#elif defined(CONFIG_SYS_NS16550_MEM32) && (CONFIG_SYS_NS16550_REG_SIZE > 0)
|
2011-10-16 04:14:09 +09:00
|
|
|
#define serial_out(x, y) out_be32(y, x)
|
|
|
|
#define serial_in(y) in_be32(y)
|
2011-09-02 07:47:14 +09:00
|
|
|
#elif defined(CONFIG_SYS_NS16550_MEM32) && (CONFIG_SYS_NS16550_REG_SIZE < 0)
|
2011-10-16 04:14:09 +09:00
|
|
|
#define serial_out(x, y) out_le32(y, x)
|
|
|
|
#define serial_in(y) in_le32(y)
|
2010-04-23 23:05:46 +09:00
|
|
|
#else
|
2011-10-16 04:14:09 +09:00
|
|
|
#define serial_out(x, y) writeb(x, y)
|
|
|
|
#define serial_in(y) readb(y)
|
2010-04-23 23:05:46 +09:00
|
|
|
#endif
|
2014-09-05 07:27:34 +09:00
|
|
|
#endif /* !CONFIG_DM_SERIAL */
|
2002-04-01 23:29:03 +09:00
|
|
|
|
2014-07-16 06:59:25 +09:00
|
|
|
#if defined(CONFIG_SOC_KEYSTONE)
|
2014-04-05 02:16:53 +09:00
|
|
|
#define UART_REG_VAL_PWREMU_MGMT_UART_DISABLE 0
|
|
|
|
#define UART_REG_VAL_PWREMU_MGMT_UART_ENABLE ((1 << 14) | (1 << 13) | (1 << 0))
|
2014-04-10 04:38:46 +09:00
|
|
|
#undef UART_MCRVAL
|
|
|
|
#ifdef CONFIG_SERIAL_HW_FLOW_CONTROL
|
|
|
|
#define UART_MCRVAL (UART_MCR_RTS | UART_MCR_AFE)
|
|
|
|
#else
|
|
|
|
#define UART_MCRVAL (UART_MCR_RTS)
|
|
|
|
#endif
|
2014-04-05 02:16:53 +09:00
|
|
|
#endif
|
|
|
|
|
2010-10-28 01:28:31 +09:00
|
|
|
#ifndef CONFIG_SYS_NS16550_IER
|
|
|
|
#define CONFIG_SYS_NS16550_IER 0x00
|
|
|
|
#endif /* CONFIG_SYS_NS16550_IER */
|
|
|
|
|
2015-02-28 14:06:26 +09:00
|
|
|
static inline void serial_out_shift(void *addr, int shift, int value)
|
2015-01-27 10:27:08 +09:00
|
|
|
{
|
2014-09-05 07:27:34 +09:00
|
|
|
#ifdef CONFIG_SYS_NS16550_PORT_MAPPED
|
2014-10-10 22:49:13 +09:00
|
|
|
outb(value, (ulong)addr);
|
2014-09-05 07:27:34 +09:00
|
|
|
#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);
|
2015-05-13 05:55:02 +09:00
|
|
|
#elif defined(CONFIG_SYS_NS16550_MEM32)
|
|
|
|
writel(value, addr);
|
2014-09-05 07:27:34 +09:00
|
|
|
#elif defined(CONFIG_SYS_BIG_ENDIAN)
|
2015-01-27 10:27:08 +09:00
|
|
|
writeb(value, addr + (1 << shift) - 1);
|
2014-09-05 07:27:34 +09:00
|
|
|
#else
|
|
|
|
writeb(value, addr);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2015-02-28 14:06:26 +09:00
|
|
|
static inline int serial_in_shift(void *addr, int shift)
|
2014-09-05 07:27:34 +09:00
|
|
|
{
|
|
|
|
#ifdef CONFIG_SYS_NS16550_PORT_MAPPED
|
2014-10-10 22:49:13 +09:00
|
|
|
return inb((ulong)addr);
|
2014-09-05 07:27:34 +09:00
|
|
|
#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);
|
2015-05-13 05:55:02 +09:00
|
|
|
#elif defined(CONFIG_SYS_NS16550_MEM32)
|
|
|
|
return readl(addr);
|
2014-09-05 07:27:34 +09:00
|
|
|
#elif defined(CONFIG_SYS_BIG_ENDIAN)
|
2015-02-28 16:55:36 +09:00
|
|
|
return readb(addr + (1 << shift) - 1);
|
2014-09-05 07:27:34 +09:00
|
|
|
#else
|
|
|
|
return readb(addr);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2016-05-25 09:13:03 +09:00
|
|
|
#ifdef CONFIG_DM_SERIAL
|
|
|
|
|
|
|
|
#ifndef CONFIG_SYS_NS16550_CLK
|
|
|
|
#define CONFIG_SYS_NS16550_CLK 0
|
|
|
|
#endif
|
|
|
|
|
2015-01-27 10:27:08 +09:00
|
|
|
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;
|
2016-05-17 15:43:26 +09:00
|
|
|
addr = (unsigned char *)plat->base + offset;
|
|
|
|
|
2015-01-27 10:27:08 +09:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2016-02-17 00:17:49 +09:00
|
|
|
serial_out_shift(addr + plat->reg_offset, plat->reg_shift, value);
|
2015-01-27 10:27:08 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
static int ns16550_readb(NS16550_t port, int offset)
|
|
|
|
{
|
|
|
|
struct ns16550_platdata *plat = port->plat;
|
|
|
|
unsigned char *addr;
|
|
|
|
|
|
|
|
offset *= 1 << plat->reg_shift;
|
2016-05-17 15:43:26 +09:00
|
|
|
addr = (unsigned char *)plat->base + offset;
|
2015-01-27 10:27:08 +09:00
|
|
|
|
2016-02-17 00:17:49 +09:00
|
|
|
return serial_in_shift(addr + plat->reg_offset, plat->reg_shift);
|
2015-01-27 10:27:08 +09:00
|
|
|
}
|
|
|
|
|
2016-12-01 10:06:29 +09:00
|
|
|
static u32 ns16550_getfcr(NS16550_t port)
|
|
|
|
{
|
|
|
|
struct ns16550_platdata *plat = port->plat;
|
|
|
|
|
|
|
|
return plat->fcr;
|
|
|
|
}
|
|
|
|
|
2014-09-05 07:27:34 +09:00
|
|
|
/* We can clean these up once everything is moved to driver model */
|
|
|
|
#define serial_out(value, addr) \
|
2015-02-28 14:06:26 +09:00
|
|
|
ns16550_writeb(com_port, \
|
|
|
|
(unsigned char *)addr - (unsigned char *)com_port, value)
|
2014-09-05 07:27:34 +09:00
|
|
|
#define serial_in(addr) \
|
2015-02-28 14:06:26 +09:00
|
|
|
ns16550_readb(com_port, \
|
|
|
|
(unsigned char *)addr - (unsigned char *)com_port)
|
2016-12-01 10:06:29 +09:00
|
|
|
#else
|
|
|
|
static u32 ns16550_getfcr(NS16550_t port)
|
|
|
|
{
|
2017-01-18 16:05:49 +09:00
|
|
|
return UART_FCR_DEFVAL;
|
2016-12-01 10:06:29 +09:00
|
|
|
}
|
2014-09-05 07:27:34 +09:00
|
|
|
#endif
|
|
|
|
|
2016-05-25 09:13:16 +09:00
|
|
|
int ns16550_calc_divisor(NS16550_t port, int clock, int baudrate)
|
2014-09-05 07:27:32 +09:00
|
|
|
{
|
|
|
|
const unsigned int mode_x_div = 16;
|
|
|
|
|
2015-01-27 10:27:09 +09:00
|
|
|
return DIV_ROUND_CLOSEST(clock, mode_x_div * baudrate);
|
|
|
|
}
|
|
|
|
|
2014-09-05 07:27:33 +09:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2011-10-16 04:14:09 +09:00
|
|
|
void NS16550_init(NS16550_t com_port, int baud_divisor)
|
2002-04-01 23:29:03 +09:00
|
|
|
{
|
2014-11-11 04:04:10 +09:00
|
|
|
#if (defined(CONFIG_SPL_BUILD) && \
|
|
|
|
(defined(CONFIG_OMAP34XX) || defined(CONFIG_OMAP44XX)))
|
2013-03-29 11:52:36 +09:00
|
|
|
/*
|
2014-11-11 04:04:10 +09:00
|
|
|
* 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.
|
2013-03-29 11:52:36 +09:00
|
|
|
*/
|
|
|
|
if ((serial_in(&com_port->lsr) & (UART_LSR_TEMT | UART_LSR_THRE))
|
|
|
|
== UART_LSR_THRE) {
|
2014-09-05 07:27:34 +09:00
|
|
|
if (baud_divisor != -1)
|
|
|
|
NS16550_setbrg(com_port, baud_divisor);
|
2013-03-29 11:52:36 +09:00
|
|
|
serial_out(0, &com_port->mdr1);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-09-19 08:19:05 +09:00
|
|
|
while (!(serial_in(&com_port->lsr) & UART_LSR_TEMT))
|
|
|
|
;
|
|
|
|
|
2010-10-28 01:28:31 +09:00
|
|
|
serial_out(CONFIG_SYS_NS16550_IER, &com_port->ier);
|
2017-05-13 11:33:16 +09:00
|
|
|
#if defined(CONFIG_ARCH_OMAP2PLUS)
|
2010-04-23 23:05:46 +09:00
|
|
|
serial_out(0x7, &com_port->mdr1); /* mode select reset TL16C750*/
|
2003-07-17 06:53:01 +09:00
|
|
|
#endif
|
2010-04-23 23:05:46 +09:00
|
|
|
serial_out(UART_MCRVAL, &com_port->mcr);
|
2016-12-01 10:06:29 +09:00
|
|
|
serial_out(ns16550_getfcr(com_port), &com_port->fcr);
|
2014-09-05 07:27:34 +09:00
|
|
|
if (baud_divisor != -1)
|
|
|
|
NS16550_setbrg(com_port, baud_divisor);
|
2017-05-13 11:33:16 +09:00
|
|
|
#if defined(CONFIG_ARCH_OMAP2PLUS) || defined(CONFIG_SOC_DA8XX)
|
2011-10-16 04:14:09 +09:00
|
|
|
/* /16 is proper to hit 115200 with 48MHz */
|
|
|
|
serial_out(0, &com_port->mdr1);
|
2017-05-13 11:33:16 +09:00
|
|
|
#endif
|
2014-07-16 06:59:25 +09:00
|
|
|
#if defined(CONFIG_SOC_KEYSTONE)
|
2014-04-05 02:16:53 +09:00
|
|
|
serial_out(UART_REG_VAL_PWREMU_MGMT_UART_ENABLE, &com_port->regC);
|
|
|
|
#endif
|
2002-04-01 23:29:03 +09:00
|
|
|
}
|
|
|
|
|
2009-02-19 07:30:44 +09:00
|
|
|
#ifndef CONFIG_NS16550_MIN_FUNCTIONS
|
2011-10-16 04:14:09 +09:00
|
|
|
void NS16550_reinit(NS16550_t com_port, int baud_divisor)
|
2002-04-01 23:29:03 +09:00
|
|
|
{
|
2010-10-28 01:28:31 +09:00
|
|
|
serial_out(CONFIG_SYS_NS16550_IER, &com_port->ier);
|
2014-09-05 07:27:33 +09:00
|
|
|
NS16550_setbrg(com_port, 0);
|
2010-04-23 23:05:46 +09:00
|
|
|
serial_out(UART_MCRVAL, &com_port->mcr);
|
2016-12-01 10:06:29 +09:00
|
|
|
serial_out(ns16550_getfcr(com_port), &com_port->fcr);
|
2014-09-05 07:27:33 +09:00
|
|
|
NS16550_setbrg(com_port, baud_divisor);
|
2002-04-01 23:29:03 +09:00
|
|
|
}
|
2009-02-19 07:30:44 +09:00
|
|
|
#endif /* CONFIG_NS16550_MIN_FUNCTIONS */
|
2002-04-01 23:29:03 +09:00
|
|
|
|
2011-10-16 04:14:09 +09:00
|
|
|
void NS16550_putc(NS16550_t com_port, char c)
|
2002-04-01 23:29:03 +09:00
|
|
|
{
|
2011-10-16 04:14:09 +09:00
|
|
|
while ((serial_in(&com_port->lsr) & UART_LSR_THRE) == 0)
|
|
|
|
;
|
2010-04-23 23:05:46 +09:00
|
|
|
serial_out(c, &com_port->thr);
|
2010-10-12 16:39:45 +09:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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 (c == '\n')
|
|
|
|
WATCHDOG_RESET();
|
2002-04-01 23:29:03 +09:00
|
|
|
}
|
|
|
|
|
2009-02-19 07:30:44 +09:00
|
|
|
#ifndef CONFIG_NS16550_MIN_FUNCTIONS
|
2011-10-16 04:14:09 +09:00
|
|
|
char NS16550_getc(NS16550_t com_port)
|
2002-04-01 23:29:03 +09:00
|
|
|
{
|
2010-04-23 23:05:46 +09:00
|
|
|
while ((serial_in(&com_port->lsr) & UART_LSR_DR) == 0) {
|
2012-09-15 17:25:19 +09:00
|
|
|
#if !defined(CONFIG_SPL_BUILD) && defined(CONFIG_USB_TTY)
|
2004-03-12 09:14:09 +09:00
|
|
|
extern void usbtty_poll(void);
|
|
|
|
usbtty_poll();
|
|
|
|
#endif
|
2010-02-02 07:34:25 +09:00
|
|
|
WATCHDOG_RESET();
|
2004-03-12 09:14:09 +09:00
|
|
|
}
|
2010-04-23 23:05:46 +09:00
|
|
|
return serial_in(&com_port->rbr);
|
2002-04-01 23:29:03 +09:00
|
|
|
}
|
|
|
|
|
2011-10-16 04:14:09 +09:00
|
|
|
int NS16550_tstc(NS16550_t com_port)
|
2002-04-01 23:29:03 +09:00
|
|
|
{
|
2011-10-16 04:14:09 +09:00
|
|
|
return (serial_in(&com_port->lsr) & UART_LSR_DR) != 0;
|
2002-04-01 23:29:03 +09:00
|
|
|
}
|
|
|
|
|
2009-02-19 07:30:44 +09:00
|
|
|
#endif /* CONFIG_NS16550_MIN_FUNCTIONS */
|
2014-09-05 07:27:34 +09:00
|
|
|
|
2015-01-27 10:27:09 +09:00
|
|
|
#ifdef CONFIG_DEBUG_UART_NS16550
|
|
|
|
|
|
|
|
#include <debug_uart.h>
|
|
|
|
|
2015-10-19 10:51:23 +09:00
|
|
|
static inline void _debug_uart_init(void)
|
2015-01-27 10:27:09 +09:00
|
|
|
{
|
|
|
|
struct NS16550 *com_port = (struct NS16550 *)CONFIG_DEBUG_UART_BASE;
|
|
|
|
int baud_divisor;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We copy the code from above because it is already horribly messy.
|
|
|
|
* Trying to refactor to nicely remove the duplication doesn't seem
|
|
|
|
* feasible. The better fix is to move all users of this driver to
|
|
|
|
* driver model.
|
|
|
|
*/
|
2016-05-25 09:13:16 +09:00
|
|
|
baud_divisor = ns16550_calc_divisor(com_port, CONFIG_DEBUG_UART_CLOCK,
|
|
|
|
CONFIG_BAUDRATE);
|
2015-06-24 06:39:06 +09:00
|
|
|
serial_dout(&com_port->ier, CONFIG_SYS_NS16550_IER);
|
|
|
|
serial_dout(&com_port->mcr, UART_MCRVAL);
|
2017-01-18 16:05:49 +09:00
|
|
|
serial_dout(&com_port->fcr, UART_FCR_DEFVAL);
|
2015-06-24 06:39:06 +09:00
|
|
|
|
|
|
|
serial_dout(&com_port->lcr, UART_LCR_BKSE | UART_LCRVAL);
|
|
|
|
serial_dout(&com_port->dll, baud_divisor & 0xff);
|
|
|
|
serial_dout(&com_port->dlm, (baud_divisor >> 8) & 0xff);
|
|
|
|
serial_dout(&com_port->lcr, UART_LCRVAL);
|
2015-01-27 10:27:09 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void _debug_uart_putc(int ch)
|
|
|
|
{
|
|
|
|
struct NS16550 *com_port = (struct NS16550 *)CONFIG_DEBUG_UART_BASE;
|
|
|
|
|
2015-06-24 06:39:06 +09:00
|
|
|
while (!(serial_din(&com_port->lsr) & UART_LSR_THRE))
|
2015-01-27 10:27:09 +09:00
|
|
|
;
|
2015-06-24 06:39:06 +09:00
|
|
|
serial_dout(&com_port->thr, ch);
|
2015-01-27 10:27:09 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
DEBUG_UART_FUNCS
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2017-04-22 19:27:25 +09:00
|
|
|
#ifdef CONFIG_DEBUG_UART_OMAP
|
|
|
|
|
|
|
|
#include <debug_uart.h>
|
|
|
|
|
|
|
|
static inline void _debug_uart_init(void)
|
|
|
|
{
|
|
|
|
struct NS16550 *com_port = (struct NS16550 *)CONFIG_DEBUG_UART_BASE;
|
|
|
|
int baud_divisor;
|
|
|
|
|
|
|
|
baud_divisor = ns16550_calc_divisor(com_port, CONFIG_DEBUG_UART_CLOCK,
|
|
|
|
CONFIG_BAUDRATE);
|
|
|
|
serial_dout(&com_port->ier, CONFIG_SYS_NS16550_IER);
|
|
|
|
serial_dout(&com_port->mdr1, 0x7);
|
|
|
|
serial_dout(&com_port->mcr, UART_MCRVAL);
|
|
|
|
serial_dout(&com_port->fcr, UART_FCR_DEFVAL);
|
|
|
|
|
|
|
|
serial_dout(&com_port->lcr, UART_LCR_BKSE | UART_LCRVAL);
|
|
|
|
serial_dout(&com_port->dll, baud_divisor & 0xff);
|
|
|
|
serial_dout(&com_port->dlm, (baud_divisor >> 8) & 0xff);
|
|
|
|
serial_dout(&com_port->lcr, UART_LCRVAL);
|
|
|
|
serial_dout(&com_port->mdr1, 0x0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void _debug_uart_putc(int ch)
|
|
|
|
{
|
|
|
|
struct NS16550 *com_port = (struct NS16550 *)CONFIG_DEBUG_UART_BASE;
|
|
|
|
|
|
|
|
while (!(serial_din(&com_port->lsr) & UART_LSR_THRE))
|
|
|
|
;
|
|
|
|
serial_dout(&com_port->thr, ch);
|
|
|
|
}
|
|
|
|
|
|
|
|
DEBUG_UART_FUNCS
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2014-09-05 07:27:34 +09:00
|
|
|
#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);
|
|
|
|
|
2014-10-23 12:37:03 +09:00
|
|
|
if (!(serial_in(&com_port->lsr) & UART_LSR_DR))
|
2014-09-05 07:27:34 +09:00
|
|
|
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);
|
|
|
|
|
2014-10-23 12:37:05 +09:00
|
|
|
com_port->plat = dev_get_platdata(dev);
|
2014-09-05 07:27:34 +09:00
|
|
|
NS16550_init(com_port, -1);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-12-01 10:06:30 +09:00
|
|
|
#if CONFIG_IS_ENABLED(OF_CONTROL)
|
|
|
|
enum {
|
|
|
|
PORT_NS16550 = 0,
|
2016-12-01 10:06:31 +09:00
|
|
|
PORT_JZ4780,
|
2016-12-01 10:06:30 +09:00
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2016-07-05 02:58:23 +09:00
|
|
|
#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
|
2014-09-05 07:27:34 +09:00
|
|
|
int ns16550_serial_ofdata_to_platdata(struct udevice *dev)
|
|
|
|
{
|
|
|
|
struct ns16550_platdata *plat = dev->platdata;
|
2016-12-01 10:06:31 +09:00
|
|
|
const u32 port_type = dev_get_driver_data(dev);
|
2014-09-05 07:27:34 +09:00
|
|
|
fdt_addr_t addr;
|
2016-09-26 20:45:27 +09:00
|
|
|
struct clk clk;
|
|
|
|
int err;
|
2014-09-05 07:27:34 +09:00
|
|
|
|
2014-12-31 17:05:12 +09:00
|
|
|
/* try Processor Local Bus device first */
|
2017-06-12 21:21:56 +09:00
|
|
|
addr = dev_read_addr(dev);
|
2015-11-30 05:17:54 +09:00
|
|
|
#if defined(CONFIG_PCI) && defined(CONFIG_DM_PCI)
|
2014-12-31 17:05:12 +09:00
|
|
|
if (addr == FDT_ADDR_T_NONE) {
|
|
|
|
/* then try pci device */
|
|
|
|
struct fdt_pci_addr pci_addr;
|
|
|
|
u32 bar;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* we prefer to use a memory-mapped register */
|
2017-01-18 08:52:55 +09:00
|
|
|
ret = fdtdec_get_pci_addr(gd->fdt_blob, dev_of_offset(dev),
|
2014-12-31 17:05:12 +09:00
|
|
|
FDT_PCI_SPACE_MEM32, "reg",
|
|
|
|
&pci_addr);
|
|
|
|
if (ret) {
|
|
|
|
/* try if there is any i/o-mapped register */
|
|
|
|
ret = fdtdec_get_pci_addr(gd->fdt_blob,
|
2017-01-18 08:52:55 +09:00
|
|
|
dev_of_offset(dev),
|
2014-12-31 17:05:12 +09:00
|
|
|
FDT_PCI_SPACE_IO,
|
|
|
|
"reg", &pci_addr);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-11-30 05:17:54 +09:00
|
|
|
ret = fdtdec_get_pci_bar32(dev, &pci_addr, &bar);
|
2014-12-31 17:05:12 +09:00
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
addr = bar;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-09-05 07:27:34 +09:00
|
|
|
if (addr == FDT_ADDR_T_NONE)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2016-05-17 15:43:26 +09:00
|
|
|
#ifdef CONFIG_SYS_NS16550_PORT_MAPPED
|
2014-10-23 12:37:04 +09:00
|
|
|
plat->base = addr;
|
2016-05-17 15:43:26 +09:00
|
|
|
#else
|
|
|
|
plat->base = (unsigned long)map_physmem(addr, 0, MAP_NOCACHE);
|
|
|
|
#endif
|
|
|
|
|
2017-06-08 01:46:02 +09:00
|
|
|
plat->reg_offset = dev_read_u32_default(dev, "reg-offset", 0);
|
|
|
|
plat->reg_shift = dev_read_u32_default(dev, "reg-shift", 0);
|
2016-09-08 15:47:29 +09:00
|
|
|
|
|
|
|
err = clk_get_by_index(dev, 0, &clk);
|
|
|
|
if (!err) {
|
|
|
|
err = clk_get_rate(&clk);
|
|
|
|
if (!IS_ERR_VALUE(err))
|
|
|
|
plat->clock = err;
|
2016-09-30 17:37:00 +09:00
|
|
|
} else if (err != -ENOENT && err != -ENODEV && err != -ENOSYS) {
|
2016-09-08 15:47:29 +09:00
|
|
|
debug("ns16550 failed to get clock\n");
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!plat->clock)
|
2017-06-08 01:46:02 +09:00
|
|
|
plat->clock = dev_read_u32_default(dev, "clock-frequency",
|
|
|
|
CONFIG_SYS_NS16550_CLK);
|
ns16550: add generic binding to unify the drivers
Add generic binding to unify ns16550 drivers. There are
several drivers using almost the same code, such as serial_dw,
serial_keystone, serial_omap, serial_ppc, serial_rockchip,
serial_tegra.c, and serial_x86. But each is platform specific.
The key difference between these drivers is the way to get
input clock frequency. With this unified approach, fixed clock
frequency should be extracted from "clock-frequency" property of
device tree blob. If this property is not available, the macro
CONFIG_SYS_NS16550_CLK will be used. It can be a constant or a
function to get clock, eg, get_serial_clock().
Signed-off-by: Thomas Chou <thomas@wytron.com.tw>
Reviewed-by: Tom Rini <trini@konsulko.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Heiko Schocher <hs@denx.de>
Acked-by: Simon Glass <sjg@chromium.org>
2015-11-19 22:48:05 +09:00
|
|
|
if (!plat->clock) {
|
|
|
|
debug("ns16550 clock not defined\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2014-09-05 07:27:34 +09:00
|
|
|
|
2017-01-18 16:05:49 +09:00
|
|
|
plat->fcr = UART_FCR_DEFVAL;
|
2016-12-01 10:06:31 +09:00
|
|
|
if (port_type == PORT_JZ4780)
|
|
|
|
plat->fcr |= UART_FCR_UME;
|
2016-12-01 10:06:29 +09:00
|
|
|
|
2014-09-05 07:27:34 +09:00
|
|
|
return 0;
|
|
|
|
}
|
2014-10-23 12:37:05 +09:00
|
|
|
#endif
|
2014-09-05 07:27:34 +09:00
|
|
|
|
|
|
|
const struct dm_serial_ops ns16550_serial_ops = {
|
|
|
|
.putc = ns16550_serial_putc,
|
|
|
|
.pending = ns16550_serial_pending,
|
|
|
|
.getc = ns16550_serial_getc,
|
|
|
|
.setbrg = ns16550_serial_setbrg,
|
|
|
|
};
|
ns16550: add generic binding to unify the drivers
Add generic binding to unify ns16550 drivers. There are
several drivers using almost the same code, such as serial_dw,
serial_keystone, serial_omap, serial_ppc, serial_rockchip,
serial_tegra.c, and serial_x86. But each is platform specific.
The key difference between these drivers is the way to get
input clock frequency. With this unified approach, fixed clock
frequency should be extracted from "clock-frequency" property of
device tree blob. If this property is not available, the macro
CONFIG_SYS_NS16550_CLK will be used. It can be a constant or a
function to get clock, eg, get_serial_clock().
Signed-off-by: Thomas Chou <thomas@wytron.com.tw>
Reviewed-by: Tom Rini <trini@konsulko.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Heiko Schocher <hs@denx.de>
Acked-by: Simon Glass <sjg@chromium.org>
2015-11-19 22:48:05 +09:00
|
|
|
|
2017-03-28 04:54:19 +09:00
|
|
|
#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
|
2015-12-14 21:45:09 +09:00
|
|
|
/*
|
|
|
|
* Please consider existing compatible strings before adding a new
|
|
|
|
* one to keep this table compact. Or you may add a generic "ns16550"
|
|
|
|
* compatible string to your dts.
|
|
|
|
*/
|
ns16550: add generic binding to unify the drivers
Add generic binding to unify ns16550 drivers. There are
several drivers using almost the same code, such as serial_dw,
serial_keystone, serial_omap, serial_ppc, serial_rockchip,
serial_tegra.c, and serial_x86. But each is platform specific.
The key difference between these drivers is the way to get
input clock frequency. With this unified approach, fixed clock
frequency should be extracted from "clock-frequency" property of
device tree blob. If this property is not available, the macro
CONFIG_SYS_NS16550_CLK will be used. It can be a constant or a
function to get clock, eg, get_serial_clock().
Signed-off-by: Thomas Chou <thomas@wytron.com.tw>
Reviewed-by: Tom Rini <trini@konsulko.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Heiko Schocher <hs@denx.de>
Acked-by: Simon Glass <sjg@chromium.org>
2015-11-19 22:48:05 +09:00
|
|
|
static const struct udevice_id ns16550_serial_ids[] = {
|
2016-12-01 10:06:30 +09:00
|
|
|
{ .compatible = "ns16550", .data = PORT_NS16550 },
|
|
|
|
{ .compatible = "ns16550a", .data = PORT_NS16550 },
|
2016-12-01 10:06:31 +09:00
|
|
|
{ .compatible = "ingenic,jz4780-uart", .data = PORT_JZ4780 },
|
2016-12-01 10:06:30 +09:00
|
|
|
{ .compatible = "nvidia,tegra20-uart", .data = PORT_NS16550 },
|
|
|
|
{ .compatible = "snps,dw-apb-uart", .data = PORT_NS16550 },
|
|
|
|
{ .compatible = "ti,omap2-uart", .data = PORT_NS16550 },
|
|
|
|
{ .compatible = "ti,omap3-uart", .data = PORT_NS16550 },
|
|
|
|
{ .compatible = "ti,omap4-uart", .data = PORT_NS16550 },
|
|
|
|
{ .compatible = "ti,am3352-uart", .data = PORT_NS16550 },
|
|
|
|
{ .compatible = "ti,am4372-uart", .data = PORT_NS16550 },
|
|
|
|
{ .compatible = "ti,dra742-uart", .data = PORT_NS16550 },
|
ns16550: add generic binding to unify the drivers
Add generic binding to unify ns16550 drivers. There are
several drivers using almost the same code, such as serial_dw,
serial_keystone, serial_omap, serial_ppc, serial_rockchip,
serial_tegra.c, and serial_x86. But each is platform specific.
The key difference between these drivers is the way to get
input clock frequency. With this unified approach, fixed clock
frequency should be extracted from "clock-frequency" property of
device tree blob. If this property is not available, the macro
CONFIG_SYS_NS16550_CLK will be used. It can be a constant or a
function to get clock, eg, get_serial_clock().
Signed-off-by: Thomas Chou <thomas@wytron.com.tw>
Reviewed-by: Tom Rini <trini@konsulko.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Heiko Schocher <hs@denx.de>
Acked-by: Simon Glass <sjg@chromium.org>
2015-11-19 22:48:05 +09:00
|
|
|
{}
|
|
|
|
};
|
2017-03-28 04:54:19 +09:00
|
|
|
#endif /* OF_CONTROL && !OF_PLATDATA */
|
ns16550: add generic binding to unify the drivers
Add generic binding to unify ns16550 drivers. There are
several drivers using almost the same code, such as serial_dw,
serial_keystone, serial_omap, serial_ppc, serial_rockchip,
serial_tegra.c, and serial_x86. But each is platform specific.
The key difference between these drivers is the way to get
input clock frequency. With this unified approach, fixed clock
frequency should be extracted from "clock-frequency" property of
device tree blob. If this property is not available, the macro
CONFIG_SYS_NS16550_CLK will be used. It can be a constant or a
function to get clock, eg, get_serial_clock().
Signed-off-by: Thomas Chou <thomas@wytron.com.tw>
Reviewed-by: Tom Rini <trini@konsulko.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Heiko Schocher <hs@denx.de>
Acked-by: Simon Glass <sjg@chromium.org>
2015-11-19 22:48:05 +09:00
|
|
|
|
2015-12-14 13:36:59 +09:00
|
|
|
#if CONFIG_IS_ENABLED(SERIAL_PRESENT)
|
2017-03-28 04:54:19 +09:00
|
|
|
|
|
|
|
/* TODO(sjg@chromium.org): Integrate this into a macro like CONFIG_IS_ENABLED */
|
|
|
|
#if !defined(CONFIG_TPL_BUILD) || defined(CONFIG_TPL_DM_SERIAL)
|
ns16550: add generic binding to unify the drivers
Add generic binding to unify ns16550 drivers. There are
several drivers using almost the same code, such as serial_dw,
serial_keystone, serial_omap, serial_ppc, serial_rockchip,
serial_tegra.c, and serial_x86. But each is platform specific.
The key difference between these drivers is the way to get
input clock frequency. With this unified approach, fixed clock
frequency should be extracted from "clock-frequency" property of
device tree blob. If this property is not available, the macro
CONFIG_SYS_NS16550_CLK will be used. It can be a constant or a
function to get clock, eg, get_serial_clock().
Signed-off-by: Thomas Chou <thomas@wytron.com.tw>
Reviewed-by: Tom Rini <trini@konsulko.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Heiko Schocher <hs@denx.de>
Acked-by: Simon Glass <sjg@chromium.org>
2015-11-19 22:48:05 +09:00
|
|
|
U_BOOT_DRIVER(ns16550_serial) = {
|
|
|
|
.name = "ns16550_serial",
|
|
|
|
.id = UCLASS_SERIAL,
|
2017-03-28 04:54:19 +09:00
|
|
|
#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
|
ns16550: add generic binding to unify the drivers
Add generic binding to unify ns16550 drivers. There are
several drivers using almost the same code, such as serial_dw,
serial_keystone, serial_omap, serial_ppc, serial_rockchip,
serial_tegra.c, and serial_x86. But each is platform specific.
The key difference between these drivers is the way to get
input clock frequency. With this unified approach, fixed clock
frequency should be extracted from "clock-frequency" property of
device tree blob. If this property is not available, the macro
CONFIG_SYS_NS16550_CLK will be used. It can be a constant or a
function to get clock, eg, get_serial_clock().
Signed-off-by: Thomas Chou <thomas@wytron.com.tw>
Reviewed-by: Tom Rini <trini@konsulko.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Heiko Schocher <hs@denx.de>
Acked-by: Simon Glass <sjg@chromium.org>
2015-11-19 22:48:05 +09:00
|
|
|
.of_match = ns16550_serial_ids,
|
|
|
|
.ofdata_to_platdata = ns16550_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,
|
2015-12-05 00:58:38 +09:00
|
|
|
.flags = DM_FLAG_PRE_RELOC,
|
ns16550: add generic binding to unify the drivers
Add generic binding to unify ns16550 drivers. There are
several drivers using almost the same code, such as serial_dw,
serial_keystone, serial_omap, serial_ppc, serial_rockchip,
serial_tegra.c, and serial_x86. But each is platform specific.
The key difference between these drivers is the way to get
input clock frequency. With this unified approach, fixed clock
frequency should be extracted from "clock-frequency" property of
device tree blob. If this property is not available, the macro
CONFIG_SYS_NS16550_CLK will be used. It can be a constant or a
function to get clock, eg, get_serial_clock().
Signed-off-by: Thomas Chou <thomas@wytron.com.tw>
Reviewed-by: Tom Rini <trini@konsulko.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Reviewed-by: Heiko Schocher <hs@denx.de>
Acked-by: Simon Glass <sjg@chromium.org>
2015-11-19 22:48:05 +09:00
|
|
|
};
|
2015-12-14 13:36:59 +09:00
|
|
|
#endif
|
2017-03-28 04:54:19 +09:00
|
|
|
#endif /* SERIAL_PRESENT */
|
|
|
|
|
2014-09-05 07:27:34 +09:00
|
|
|
#endif /* CONFIG_DM_SERIAL */
|