diff mbox

[U-Boot,2/6] serial: add UniPhier serial driver

Message ID 1404469158-22703-3-git-send-email-yamada.m@jp.panasonic.com
State Superseded
Delegated to: Albert ARIBAUD
Headers show

Commit Message

Masahiro Yamada July 4, 2014, 10:19 a.m. UTC
The driver for on-chip UART used on Panasonic UniPhier platform.

Signed-off-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
---

 drivers/serial/Makefile          |   1 +
 drivers/serial/serial.c          |   2 +
 drivers/serial/serial_uniphier.c | 206 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 209 insertions(+)
 create mode 100644 drivers/serial/serial_uniphier.c

Comments

Marek Vasut July 4, 2014, 2:40 p.m. UTC | #1
On Friday, July 04, 2014 at 12:19:14 PM, Masahiro Yamada wrote:
> The driver for on-chip UART used on Panasonic UniPhier platform.
> 
> Signed-off-by: Masahiro Yamada <yamada.m@jp.panasonic.com>

[...]

> +static void uniphier_serial_init(struct uniphier_serial *port)
> +{
> +	writeb(UART_LCR_WLS_8, &port->lcr);
> +
> +#define MODE_X_DIV 16

You can use just const unsigned here instead of #define.

> +
> +	/* Compute divisor value. Normally, we should simply return:
> +	 *   CONFIG_SYS_NS16550_CLK) / MODE_X_DIV / gd->baudrate
> +	 * but we need to round that value by adding 0.5.
> +	 * Rounding is especially important at high baud rates.
> +	 */
> +	writew((CONFIG_SYS_UNIPHIER_UART_CLK + (gd->baudrate *
> +		(MODE_X_DIV / 2))) / (MODE_X_DIV * gd->baudrate), &port->dlr);
> +}
> +
> +static void uniphier_serial_setbrg(struct uniphier_serial *port)
> +{
> +	uniphier_serial_init(port);
> +}
> +
> +static int uniphier_serial_tstc(struct uniphier_serial *port)
> +{
> +	return (readb(&port->lsr) & UART_LSR_DR) != 0;
> +}
> +
> +static int uniphier_serial_getc(struct uniphier_serial *port)
> +{
> +	while (!uniphier_serial_tstc(port))
> +		;
> +
> +	return readb(&port->rbr);
> +}
> +
> +static void uniphier_serial_putc(struct uniphier_serial *port, const char
> c) +{
> +	if (c == '\n')
> +		uniphier_serial_putc(port, '\r');
> +
> +	while (!(readb(&port->lsr) & UART_LSR_THRE))
> +		;

I think in this function, you can avoid such completely unbounded loop.

[...]

Best regard,
Marek Vasut
Masahiro Yamada July 10, 2014, 10:06 a.m. UTC | #2
Hi Marek,


On Fri, 4 Jul 2014 16:40:29 +0200
Marek Vasut <marex@denx.de> wrote:


> > +static void uniphier_serial_init(struct uniphier_serial *port)
> > +{
> > +	writeb(UART_LCR_WLS_8, &port->lcr);
> > +
> > +#define MODE_X_DIV 16
> 
> You can use just const unsigned here instead of #define.


I adjusted drivers/serial/serial_ns16550.c for my own.

Is using a macro here so bad?
If so, should I also fix the line 138 of drivers/serial/serial_ns16550.c ?




> > +static void uniphier_serial_putc(struct uniphier_serial *port, const char
> > c) +{
> > +	if (c == '\n')
> > +		uniphier_serial_putc(port, '\r');
> > +
> > +	while (!(readb(&port->lsr) & UART_LSR_THRE))
> > +		;
> 
> I think in this function, you can avoid such completely unbounded loop.
> 
> [...]


Why?
Could you give me more detailed explanation?



Best Regards
Masahiro Yamada
Marek Vasut July 10, 2014, 10:14 a.m. UTC | #3
On Thursday, July 10, 2014 at 12:06:50 PM, Masahiro Yamada wrote:
> Hi Marek,
> 
> 
> On Fri, 4 Jul 2014 16:40:29 +0200
> 
> Marek Vasut <marex@denx.de> wrote:
> > > +static void uniphier_serial_init(struct uniphier_serial *port)
> > > +{
> > > +	writeb(UART_LCR_WLS_8, &port->lcr);
> > > +
> > > +#define MODE_X_DIV 16
> > 
> > You can use just const unsigned here instead of #define.
> 
> I adjusted drivers/serial/serial_ns16550.c for my own.
> 
> Is using a macro here so bad?
> If so, should I also fix the line 138 of drivers/serial/serial_ns16550.c ?

You're loosing the typechecking, that's all.

> > > +static void uniphier_serial_putc(struct uniphier_serial *port, const
> > > char c) +{
> > > +	if (c == '\n')
> > > +		uniphier_serial_putc(port, '\r');
> > > +
> > > +	while (!(readb(&port->lsr) & UART_LSR_THRE))
> > > +		;
> > 
> > I think in this function, you can avoid such completely unbounded loop.
> > 
> > [...]
> 
> Why?
> Could you give me more detailed explanation?

In case the LSR_THRE bit would never get cleared, this loop would keep spinning 
forever. On the other hand, if there was some kind of a timeout, U-Boot would be 
able to continue doing at least something when exiting this loop by timing out. 
Though you're right that something would most likely have gone really bonkers if 
this LSR_THRE never got cleared.

Best regards,
Marek Vasut
Masahiro Yamada July 10, 2014, 11:31 a.m. UTC | #4
Hi Marek,



On Thu, 10 Jul 2014 12:14:35 +0200
Marek Vasut <marex@denx.de> wrote:


> 
> > > > +static void uniphier_serial_putc(struct uniphier_serial *port, const
> > > > char c) +{
> > > > +	if (c == '\n')
> > > > +		uniphier_serial_putc(port, '\r');
> > > > +
> > > > +	while (!(readb(&port->lsr) & UART_LSR_THRE))
> > > > +		;
> > > 
> > > I think in this function, you can avoid such completely unbounded loop.
> > > 
> > > [...]
> > 
> > Why?
> > Could you give me more detailed explanation?
> 
> In case the LSR_THRE bit would never get cleared, this loop would keep spinning 
> forever. On the other hand, if there was some kind of a timeout, U-Boot would be 
> able to continue doing at least something when exiting this loop by timing out. 
> Though you're right that something would most likely have gone really bonkers if 
> this LSR_THRE never got cleared.
> 


I guess you mentioned the case where the flow control with CTS/RTS
results in the dead lock.

I am not sure if we need to be so careful.

Is there any other UART driver using timeout check?

(What would U-Boot continue to do in case console cannot display the error message?)




Best Regards
Masahiro Masahiro
Marek Vasut July 10, 2014, 12:37 p.m. UTC | #5
On Thursday, July 10, 2014 at 01:31:01 PM, Masahiro Yamada wrote:
> Hi Marek,

Hi!

> On Thu, 10 Jul 2014 12:14:35 +0200
> 
> Marek Vasut <marex@denx.de> wrote:
> > > > > +static void uniphier_serial_putc(struct uniphier_serial *port,
> > > > > const char c) +{
> > > > > +	if (c == '\n')
> > > > > +		uniphier_serial_putc(port, '\r');
> > > > > +
> > > > > +	while (!(readb(&port->lsr) & UART_LSR_THRE))
> > > > > +		;
> > > > 
> > > > I think in this function, you can avoid such completely unbounded
> > > > loop.
> > > > 
> > > > [...]
> > > 
> > > Why?
> > > Could you give me more detailed explanation?
> > 
> > In case the LSR_THRE bit would never get cleared, this loop would keep
> > spinning forever. On the other hand, if there was some kind of a
> > timeout, U-Boot would be able to continue doing at least something when
> > exiting this loop by timing out. Though you're right that something
> > would most likely have gone really bonkers if this LSR_THRE never got
> > cleared.
> 
> I guess you mentioned the case where the flow control with CTS/RTS
> results in the dead lock.

Either that or plain hardware failure.

> I am not sure if we need to be so careful.
> 
> Is there any other UART driver using timeout check?

I don't know. If you feel this is not needed, then please just ignore my 
suggestion. I'm just pushing the usual "let's not do unbounded loops where 
reasonable" thing here ;-)

> (What would U-Boot continue to do in case console cannot display the error
> message?)

It can still boot kernel, even if it cannot print on console.

Best regards,
Marek Vasut
Masahiro Yamada July 11, 2014, 5:37 a.m. UTC | #6
Hi Marek,


On Thu, 10 Jul 2014 14:37:18 +0200
Marek Vasut <marex@denx.de> wrote:

> On Thursday, July 10, 2014 at 01:31:01 PM, Masahiro Yamada wrote:
> > Hi Marek,
> 
> Hi!
> 
> > On Thu, 10 Jul 2014 12:14:35 +0200
> > 
> > Marek Vasut <marex@denx.de> wrote:
> > > > > > +static void uniphier_serial_putc(struct uniphier_serial *port,
> > > > > > const char c) +{
> > > > > > +	if (c == '\n')
> > > > > > +		uniphier_serial_putc(port, '\r');
> > > > > > +
> > > > > > +	while (!(readb(&port->lsr) & UART_LSR_THRE))
> > > > > > +		;
> > > > > 
> > > > > I think in this function, you can avoid such completely unbounded
> > > > > loop.
> > > > > 
> > > > > [...]
> > > > 
> > > > Why?
> > > > Could you give me more detailed explanation?
> > > 
> > > In case the LSR_THRE bit would never get cleared, this loop would keep
> > > spinning forever. On the other hand, if there was some kind of a
> > > timeout, U-Boot would be able to continue doing at least something when
> > > exiting this loop by timing out. Though you're right that something
> > > would most likely have gone really bonkers if this LSR_THRE never got
> > > cleared.
> > 
> > I guess you mentioned the case where the flow control with CTS/RTS
> > results in the dead lock.
> 
> Either that or plain hardware failure.
> 
> > I am not sure if we need to be so careful.
> > 
> > Is there any other UART driver using timeout check?
> 
> I don't know. If you feel this is not needed, then please just ignore my 
> suggestion. I'm just pushing the usual "let's not do unbounded loops where 
> reasonable" thing here ;-)
> 
> > (What would U-Boot continue to do in case console cannot display the error
> > message?)
> 
> It can still boot kernel, even if it cannot print on console.
> 


Finally, I decided to keep this as is because:

[1] Our on-chip UART do not have the flow control feature.
    It uses only TX/RX signals.

[2] So, the eternal loop occurrs only when the hardware is broken.
   In this case, the situation is really fatal and I don't think
    it is reasonable to continue to do something.

[3] I checked some (but not all) other serial drivers but I could not find ones
    using timeout and break thing.

Best Regards
Masahiro Yamada
Marek Vasut July 11, 2014, 8:07 a.m. UTC | #7
On Friday, July 11, 2014 at 07:37:13 AM, Masahiro Yamada wrote:
[...]
> > It can still boot kernel, even if it cannot print on console.
> 
> Finally, I decided to keep this as is because:
> 
> [1] Our on-chip UART do not have the flow control feature.
>     It uses only TX/RX signals.
> 
> [2] So, the eternal loop occurrs only when the hardware is broken.
>    In this case, the situation is really fatal and I don't think
>     it is reasonable to continue to do something.
> 
> [3] I checked some (but not all) other serial drivers but I could not find
> ones using timeout and break thing.

Hi!

All right then.

Best regards,
Marek Vasut
diff mbox

Patch

diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index 571c18f..385b2f9 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -34,6 +34,7 @@  obj-$(CONFIG_BFIN_SERIAL) += serial_bfin.o
 obj-$(CONFIG_FSL_LPUART) += serial_lpuart.o
 obj-$(CONFIG_MXS_AUART) += mxs_auart.o
 obj-$(CONFIG_ARC_SERIAL) += serial_arc.o
+obj-$(CONFIG_UNIPHIER_SERIAL) += serial_uniphier.o
 
 ifndef CONFIG_SPL_BUILD
 obj-$(CONFIG_USB_TTY) += usbtty.o
diff --git a/drivers/serial/serial.c b/drivers/serial/serial.c
index fd61a5e..9a05bef 100644
--- a/drivers/serial/serial.c
+++ b/drivers/serial/serial.c
@@ -157,6 +157,7 @@  serial_initfunc(sh_serial_initialize);
 serial_initfunc(arm_dcc_initialize);
 serial_initfunc(mxs_auart_initialize);
 serial_initfunc(arc_serial_initialize);
+serial_initfunc(uniphier_serial_initialize);
 
 /**
  * serial_register() - Register serial driver with serial driver core
@@ -250,6 +251,7 @@  void serial_initialize(void)
 	arm_dcc_initialize();
 	mxs_auart_initialize();
 	arc_serial_initialize();
+	uniphier_serial_initialize();
 
 	serial_assign(default_serial_console()->name);
 }
diff --git a/drivers/serial/serial_uniphier.c b/drivers/serial/serial_uniphier.c
new file mode 100644
index 0000000..82c754c
--- /dev/null
+++ b/drivers/serial/serial_uniphier.c
@@ -0,0 +1,206 @@ 
+/*
+ * Copyright (C) 2012-2014 Panasonic Corporation
+ *   Author: Masahiro Yamada <yamada.m@jp.panasonic.com>
+ *
+ * Based on serial_ns16550.c
+ * (C) Copyright 2000
+ * Rob Taylor, Flying Pig Systems. robt@flyingpig.com.
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <serial.h>
+
+#define UART_REG(x)					\
+	u8 x;						\
+	u8 postpad_##x[3];
+
+/*
+ * Note: Register map is slightly different from that of 16550.
+ */
+struct uniphier_serial {
+	UART_REG(rbr);		/* 0x00 */
+	UART_REG(ier);		/* 0x04 */
+	UART_REG(iir);		/* 0x08 */
+	UART_REG(fcr);		/* 0x0c */
+	u8 mcr;			/* 0x10 */
+	u8 lcr;
+	u16 __postpad;
+	UART_REG(lsr);		/* 0x14 */
+	UART_REG(msr);		/* 0x18 */
+	u32 __none1;
+	u32 __none2;
+	u16 dlr;
+	u16 __postpad2;
+};
+
+#define thr rbr
+
+/*
+ * These are the definitions for the Line Control Register
+ */
+#define UART_LCR_WLS_8	0x03		/* 8 bit character length */
+
+/*
+ * These are the definitions for the Line Status Register
+ */
+#define UART_LSR_DR	0x01		/* Data ready */
+#define UART_LSR_THRE	0x20		/* Xmit holding register empty */
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static void uniphier_serial_init(struct uniphier_serial *port)
+{
+	writeb(UART_LCR_WLS_8, &port->lcr);
+
+#define MODE_X_DIV 16
+
+	/* Compute divisor value. Normally, we should simply return:
+	 *   CONFIG_SYS_NS16550_CLK) / MODE_X_DIV / gd->baudrate
+	 * but we need to round that value by adding 0.5.
+	 * Rounding is especially important at high baud rates.
+	 */
+	writew((CONFIG_SYS_UNIPHIER_UART_CLK + (gd->baudrate *
+		(MODE_X_DIV / 2))) / (MODE_X_DIV * gd->baudrate), &port->dlr);
+}
+
+static void uniphier_serial_setbrg(struct uniphier_serial *port)
+{
+	uniphier_serial_init(port);
+}
+
+static int uniphier_serial_tstc(struct uniphier_serial *port)
+{
+	return (readb(&port->lsr) & UART_LSR_DR) != 0;
+}
+
+static int uniphier_serial_getc(struct uniphier_serial *port)
+{
+	while (!uniphier_serial_tstc(port))
+		;
+
+	return readb(&port->rbr);
+}
+
+static void uniphier_serial_putc(struct uniphier_serial *port, const char c)
+{
+	if (c == '\n')
+		uniphier_serial_putc(port, '\r');
+
+	while (!(readb(&port->lsr) & UART_LSR_THRE))
+		;
+
+	writeb(c, &port->thr);
+}
+
+static struct uniphier_serial *serial_ports[4] = {
+#ifdef CONFIG_SYS_UNIPHIER_SERIAL_BASE0
+	(struct uniphier_serial *)CONFIG_SYS_UNIPHIER_SERIAL_BASE0,
+#else
+	NULL,
+#endif
+#ifdef CONFIG_SYS_UNIPHIER_SERIAL_BASE1
+	(struct uniphier_serial *)CONFIG_SYS_UNIPHIER_SERIAL_BASE1,
+#else
+	NULL,
+#endif
+#ifdef CONFIG_SYS_UNIPHIER_SERIAL_BASE2
+	(struct uniphier_serial *)CONFIG_SYS_UNIPHIER_SERIAL_BASE2,
+#else
+	NULL,
+#endif
+#ifdef CONFIG_SYS_UNIPHIER_SERIAL_BASE3
+	(struct uniphier_serial *)CONFIG_SYS_UNIPHIER_SERIAL_BASE3,
+#else
+	NULL,
+#endif
+};
+
+/* Multi serial device functions */
+#define DECLARE_ESERIAL_FUNCTIONS(port) \
+	static int  eserial##port##_init(void) \
+	{ \
+		uniphier_serial_init(serial_ports[port]); \
+		return 0 ; \
+	} \
+	static void eserial##port##_setbrg(void) \
+	{ \
+		uniphier_serial_setbrg(serial_ports[port]); \
+	} \
+	static int  eserial##port##_getc(void) \
+	{ \
+		return uniphier_serial_getc(serial_ports[port]); \
+	} \
+	static int  eserial##port##_tstc(void) \
+	{ \
+		return uniphier_serial_tstc(serial_ports[port]); \
+	} \
+	static void eserial##port##_putc(const char c) \
+	{ \
+		uniphier_serial_putc(serial_ports[port], c); \
+	}
+
+/* Serial device descriptor */
+#define INIT_ESERIAL_STRUCTURE(port, __name) {	\
+	.name	= __name,			\
+	.start	= eserial##port##_init,		\
+	.stop	= NULL,				\
+	.setbrg	= eserial##port##_setbrg,	\
+	.getc	= eserial##port##_getc,		\
+	.tstc	= eserial##port##_tstc,		\
+	.putc	= eserial##port##_putc,		\
+	.puts	= default_serial_puts,		\
+}
+
+#if defined(CONFIG_SYS_UNIPHIER_SERIAL_BASE0)
+DECLARE_ESERIAL_FUNCTIONS(0);
+struct serial_device uniphier_serial0_device =
+	INIT_ESERIAL_STRUCTURE(0, "ttyS0");
+#endif
+#if defined(CONFIG_SYS_UNIPHIER_SERIAL_BASE1)
+DECLARE_ESERIAL_FUNCTIONS(1);
+struct serial_device uniphier_serial1_device =
+	INIT_ESERIAL_STRUCTURE(1, "ttyS1");
+#endif
+#if defined(CONFIG_SYS_UNIPHIER_SERIAL_BASE2)
+DECLARE_ESERIAL_FUNCTIONS(2);
+struct serial_device uniphier_serial2_device =
+	INIT_ESERIAL_STRUCTURE(2, "ttyS2");
+#endif
+#if defined(CONFIG_SYS_UNIPHIER_SERIAL_BASE3)
+DECLARE_ESERIAL_FUNCTIONS(3);
+struct serial_device uniphier_serial3_device =
+	INIT_ESERIAL_STRUCTURE(3, "ttyS3");
+#endif
+
+__weak struct serial_device *default_serial_console(void)
+{
+#if defined(CONFIG_SYS_UNIPHIER_SERIAL_BASE0)
+	return &uniphier_serial0_device;
+#elif defined(CONFIG_SYS_UNIPHIER_SERIAL_BASE1)
+	return &uniphier_serial1_device;
+#elif defined(CONFIG_SYS_UNIPHIER_SERIAL_BASE2)
+	return &uniphier_serial2_device;
+#elif defined(CONFIG_SYS_UNIPHIER_SERIAL_BASE3)
+	return &uniphier_serial3_device;
+#else
+#error "No uniphier serial ports configured."
+#endif
+}
+
+void uniphier_serial_initialize(void)
+{
+#if defined(CONFIG_SYS_UNIPHIER_SERIAL_BASE0)
+	serial_register(&uniphier_serial0_device);
+#endif
+#if defined(CONFIG_SYS_UNIPHIER_SERIAL_BASE1)
+	serial_register(&uniphier_serial1_device);
+#endif
+#if defined(CONFIG_SYS_UNIPHIER_SERIAL_BASE2)
+	serial_register(&uniphier_serial2_device);
+#endif
+#if defined(CONFIG_SYS_UNIPHIER_SERIAL_BASE3)
+	serial_register(&uniphier_serial3_device);
+#endif
+}