public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] serial: DCC(JTAG) serial and console emulation support
@ 2010-10-07 18:36 Daniel Walker
  2010-10-07 19:25 ` Mike Frysinger
  2010-10-07 20:50 ` Alan Cox
  0 siblings, 2 replies; 50+ messages in thread
From: Daniel Walker @ 2010-10-07 18:36 UTC (permalink / raw)
  To: linux-kernel
  Cc: Daniel Walker, Hyok S. Choi, Tony Lindgren, Jeff Ohlstein,
	Greg Kroah-Hartman, Ben Dooks, Alan Cox, Kukjin Kim,
	Mike Frysinger, Feng Tang, Tobias Klauser, Jason Wessel,
	Philippe Langlais

Many of JTAG debuggers for ARM support DCC protocol over JTAG
connection, which is very useful to debug hardwares which has no
serial port. This patch adds DCC serial emulation and the console
support. System timer based polling method is used for the
emulation of serial input interrupt handling.

Most of the code was taken from Hyok S. Choi original work, but the
inline assmebly needed some work and updating. It now supports ARMv7.
Also the description above is from Hyok also.

CC: Hyok S. Choi <hyok.choi@samsung.com>
CC: Tony Lindgren <tony@atomide.com>
Signed-off-by: Jeff Ohlstein <johlstei@quicinc.com>
Signed-off-by: Daniel Walker <dwalker@codeaurora.org>
---
 drivers/serial/Kconfig      |   31 +++
 drivers/serial/Makefile     |    1 +
 drivers/serial/dcc.c        |  432 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/serial_core.h |    3 +
 4 files changed, 467 insertions(+), 0 deletions(-)
 create mode 100644 drivers/serial/dcc.c

diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 12900f7..a54a57c 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -495,6 +495,37 @@ config SERIAL_S3C2400
 	help
 	  Serial port support for the Samsung S3C2400 SoC
 
+config SERIAL_DCC
+	bool "JTAG ICE/ICD DCC serial port emulation support"
+	depends on ARM
+	select SERIAL_CORE
+	help
+	  This selects serial port emulation driver for ICE/ICD JTAG debugger
+	  (e.g. Trace32) for ARM architecture. You should make an terminal with
+	  DCC(JTAG1) protocol.
+
+	  if unsure, say N.
+
+config SERIAL_DCC_CONSOLE
+	bool "Support for console on JTAG ICE/ICD DCC"
+	depends on SERIAL_DCC
+	select SERIAL_CORE_CONSOLE
+	help
+	  Say Y here if you wish to use ICE/ICD JTAG DCC serial port emulation
+	  as the system console.
+
+	  if unsure, say N.
+
+config SERIAL_DCC_STDSERIAL
+	bool "Install JTAG ICE/ICD DCC as standard serial"
+	default y
+	depends on !SERIAL_8250 && SERIAL_DCC
+	help
+	  Say Y here if you want to install DCC driver as a normal serial port
+	  /dev/ttyS0 (major 4, minor 64). Otherwise, it appears as /dev/ttyJ0
+	  (major 4, minor 128) and can co-exist with other UARTs, such as
+	  8250/16C550 compatibles.
+
 config SERIAL_S3C2410
 	tristate "Samsung S3C2410 Serial port support"
 	depends on SERIAL_SAMSUNG && CPU_S3C2410
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index 1ca4fd5..896c3f6 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -31,6 +31,7 @@ obj-$(CONFIG_SERIAL_8250_MCA) += 8250_mca.o
 obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o
 obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o
 obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o
+obj-$(CONFIG_SERIAL_DCC) += dcc.o
 obj-$(CONFIG_SERIAL_PXA) += pxa.o
 obj-$(CONFIG_SERIAL_PNX8XXX) += pnx8xxx_uart.o
 obj-$(CONFIG_SERIAL_SA1100) += sa1100.o
diff --git a/drivers/serial/dcc.c b/drivers/serial/dcc.c
new file mode 100644
index 0000000..233caf7
--- /dev/null
+++ b/drivers/serial/dcc.c
@@ -0,0 +1,432 @@
+/*
+ *  linux/drivers/serial/dcc.c
+ *
+ *  serial port emulation driver for the JTAG DCC Terminal.
+ *
+ * DCC(JTAG1) protocol version for JTAG ICE/ICD Debuggers:
+ *	Copyright (C) 2003, 2004, 2005 Hyok S. Choi (hyok.choi@samsung.com)
+ *	SAMSUNG ELECTRONICS Co.,Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  Changelog:
+ *   Oct-2003 Hyok S. Choi	Created
+ *   Feb-2004 Hyok S. Choi	Updated for serial_core.c and 2.6 kernel
+ *   Mar-2005 Hyok S. Choi	renamed from T32 to DCC
+ *   Apr-2006 Hyok S. Choi	revised including the MAJOR number
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/serial.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+
+#include <linux/io.h>
+#include <linux/irq.h>
+
+#include <linux/serial_core.h>
+
+#define DCC_POLL_RUN		0
+#define DCC_POLL_STOP		1
+#define DCC_POLL_STOPPED	2
+
+static struct uart_port dcc_port;
+static struct delayed_work dcc_poll_task;
+static void dcc_poll(struct work_struct *work);
+static int dcc_poll_state = DCC_POLL_STOPPED;
+
+#define UART_NR			1	/* we have only one JTAG port */
+
+#ifdef CONFIG_SERIAL_DCC_STDSERIAL
+/* use ttyS for emulation of standard serial driver */
+#define SERIAL_DCC_NAME		"ttyS"
+#define SERIAL_DCC_MINOR	64
+#else
+/* use ttyJ0(128) */
+#define SERIAL_DCC_NAME		"ttyJ"
+#define SERIAL_DCC_MINOR	(64 + 64)
+#endif
+#define SERIAL_DCC_MAJOR	TTY_MAJOR
+
+/* DCC Status Bits */
+#define DCC_STATUS_RX		(1 << 30)
+#define DCC_STATUS_TX		(1 << 29)
+
+static inline u32 __dcc_getstatus(void)
+{
+	u32 __ret;
+
+	asm("mrc p14, 0, %0, c0, c1, 0	@ read comms ctrl reg"
+		: "=r" (__ret) : : "cc");
+
+	return __ret;
+}
+
+
+#if defined(CONFIG_CPU_V7)
+static inline char __dcc_getchar(void)
+{
+	char __c;
+
+	asm("get_wait:	mrc p14, 0, pc, c0, c1, 0                          \n\
+			bne get_wait                                       \n\
+			mrc p14, 0, %0, c0, c5, 0	@ read comms data reg"
+		: "=r" (__c) : : "cc");
+
+	return __c;
+}
+#else
+static inline char __dcc_getchar(void)
+{
+	char __c;
+
+	asm("mrc p14, 0, %0, c0, c5, 0	@ read comms data reg"
+		: "=r" (__c));
+
+	return __c;
+}
+#endif
+
+#if defined(CONFIG_CPU_V7)
+static inline void __dcc_putchar(char c)
+{
+	asm("put_wait:	mrc p14, 0, pc, c0, c1, 0                 \n\
+			bcs put_wait                              \n\
+			mcr p14, 0, %0, c0, c5, 0                   "
+	: : "r" (c) : "cc");
+}
+#else
+static inline void __dcc_putchar(char c)
+{
+	asm("mcr p14, 0, %0, c0, c5, 0	@ write a char"
+		: /* no output register */
+		: "r" (c));
+}
+#endif
+
+static void dcc_putchar(struct uart_port *port, int ch)
+{
+	while (__dcc_getstatus() & DCC_STATUS_TX)
+		cpu_relax();
+	__dcc_putchar((char)(ch & 0xFF));
+}
+
+static inline void xmit_string(struct uart_port *port, char *p, int len)
+{
+	for ( ; len; len--, p++)
+		dcc_putchar(port, *p);
+}
+
+static inline void dcc_transmit_buffer(struct uart_port *port)
+{
+	struct circ_buf *xmit = &port->state->xmit;
+	int pendings = uart_circ_chars_pending(xmit);
+
+	if (pendings + xmit->tail > UART_XMIT_SIZE) {
+		xmit_string(port, &(xmit->buf[xmit->tail]),
+			UART_XMIT_SIZE - xmit->tail);
+		xmit_string(port, &(xmit->buf[0]), xmit->head);
+	} else
+		xmit_string(port, &(xmit->buf[xmit->tail]), pendings);
+
+	xmit->tail = (xmit->tail + pendings) & (UART_XMIT_SIZE-1);
+	port->icount.tx += pendings;
+}
+
+static inline void dcc_transmit_x_char(struct uart_port *port)
+{
+	dcc_putchar(port, port->x_char);
+	port->icount.tx++;
+	port->x_char = 0;
+}
+
+static void dcc_start_tx(struct uart_port *port)
+{
+	dcc_transmit_buffer(port);
+}
+
+static inline void dcc_rx_chars(struct uart_port *port)
+{
+	unsigned char ch;
+	struct tty_struct *tty = port->state->port.tty;
+
+	/*
+	 * check input.
+	 * checking JTAG flag is better to resolve the status test.
+	 * incount is NOT used for JTAG1 protocol.
+	 */
+
+	if (__dcc_getstatus() & DCC_STATUS_RX) {
+
+		/* for JTAG 1 protocol, incount is always 1. */
+		ch = __dcc_getchar();
+
+		if (tty) {
+			tty_insert_flip_char(tty, ch, TTY_NORMAL);
+			port->icount.rx++;
+			tty_flip_buffer_push(tty);
+		}
+	}
+}
+
+static inline void dcc_overrun_chars(struct uart_port *port)
+{
+	port->icount.overrun++;
+}
+
+static inline void dcc_tx_chars(struct uart_port *port)
+{
+	struct circ_buf *xmit = &port->state->xmit;
+
+	if (port->x_char) {
+		dcc_transmit_x_char(port);
+		return;
+	}
+
+	if (uart_circ_empty(xmit) || uart_tx_stopped(port))
+		return;
+
+	dcc_transmit_buffer(port);
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+		uart_write_wakeup(port);
+}
+
+static void dcc_poll(struct work_struct *work)
+{
+	spin_lock(&dcc_port.lock);
+
+	if (dcc_poll_state != DCC_POLL_RUN) {
+		dcc_poll_state = DCC_POLL_STOPPED;
+		goto dcc_poll_stop;
+	}
+
+	dcc_rx_chars(&dcc_port);
+	dcc_tx_chars(&dcc_port);
+
+	schedule_delayed_work(&dcc_poll_task, 1);
+
+dcc_poll_stop:
+	spin_unlock(&dcc_port.lock);
+}
+
+static unsigned int dcc_tx_empty(struct uart_port *port)
+{
+	return TIOCSER_TEMT;
+}
+
+static unsigned int dcc_get_mctrl(struct uart_port *port)
+{
+	return TIOCM_CTS | TIOCM_DSR | TIOCM_CD;
+}
+
+static int dcc_startup(struct uart_port *port)
+{
+	/* Initialize the work, and shcedule it. */
+	INIT_DELAYED_WORK(&dcc_poll_task, dcc_poll);
+	spin_lock(&port->lock);
+	if (dcc_poll_state != DCC_POLL_RUN)
+		dcc_poll_state = DCC_POLL_RUN;
+	schedule_delayed_work(&dcc_poll_task, 1);
+	spin_unlock(&port->lock);
+
+	return 0;
+}
+
+static void dcc_shutdown(struct uart_port *port)
+{
+	spin_lock(&port->lock);
+	dcc_poll_state = DCC_POLL_STOP;
+	spin_unlock(&port->lock);
+}
+
+static void
+dcc_set_termios(struct uart_port *port, struct ktermios *termios,
+		struct ktermios *old)
+{
+	unsigned int baud, quot;
+
+	/*
+	 * We don't support parity, stop bits, or anything other
+	 * than 8 bits, so clear these termios flags.
+	 */
+	termios->c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD | CREAD);
+	termios->c_cflag |= CS8;
+
+	/*
+	 * We don't appear to support any error conditions either.
+	 */
+	termios->c_iflag &= ~(INPCK | IGNPAR | IGNBRK | BRKINT);
+
+	/*
+	 * Ask the core to calculate the divisor for us.
+	 */
+	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
+	quot = uart_get_divisor(port, baud);
+
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+}
+
+static const char * dcc_type(struct uart_port *port)
+{
+	return port->type == PORT_DCC_JTAG1 ? "DCC" : NULL;
+}
+
+static int dcc_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+/*
+ * Configure/autoconfigure the port.
+ */
+static void dcc_config_port(struct uart_port *port, int flags)
+{
+	if (flags & UART_CONFIG_TYPE) {
+		port->type = PORT_DCC_JTAG1;
+		dcc_request_port(port);
+	}
+}
+
+/*
+ * verify the new serial_struct (for TIOCSSERIAL).
+ */
+static int dcc_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	int ret = 0;
+	if (ser->type != PORT_UNKNOWN && ser->type != PORT_DCC_JTAG1)
+		ret = -EINVAL;
+	if (ser->irq < 0 || ser->irq >= NR_IRQS)
+		ret = -EINVAL;
+	if (ser->baud_base < 9600)
+		ret = -EINVAL;
+	return ret;
+}
+
+/* dummy operation handlers for uart_ops */
+static void dcc_dummy_ops(struct uart_port *port)
+{
+}
+static void dcc_dummy_ops_ui(struct uart_port *port, unsigned int ui)
+{
+}
+static void dcc_dummy_ops_i(struct uart_port *port, int i)
+{
+}
+
+static struct uart_ops dcc_pops = {
+	.tx_empty	= dcc_tx_empty,
+	.set_mctrl	= dcc_dummy_ops_ui,
+	.get_mctrl	= dcc_get_mctrl,
+	.stop_tx	= dcc_dummy_ops,
+	.start_tx	= dcc_start_tx,
+	.stop_rx	= dcc_dummy_ops,
+	.enable_ms	= dcc_dummy_ops,
+	.break_ctl	= dcc_dummy_ops_i,
+	.startup	= dcc_startup,
+	.shutdown	= dcc_shutdown,
+	.set_termios	= dcc_set_termios,
+	.type		= dcc_type,
+	.release_port	= dcc_dummy_ops,
+	.request_port	= dcc_request_port,
+	.config_port	= dcc_config_port,
+	.verify_port	= dcc_verify_port,
+};
+
+static struct uart_port dcc_port = {
+	.membase	= (char *)0x12345678,	/* we need these garbages */
+	.mapbase	= 0x12345678,		/* for serial_core.c */
+	.iotype		= UPIO_MEM,
+	.irq		= 0,
+	.uartclk	= 14745600,
+	.fifosize	= 0,
+	.ops		= &dcc_pops,
+	.flags		= UPF_BOOT_AUTOCONF,
+	.line		= 0,
+};
+
+#ifdef CONFIG_SERIAL_DCC_CONSOLE
+static void
+dcc_console_write(struct console *co, const char *s, unsigned int count)
+{
+	uart_console_write(&dcc_port, s, count, dcc_putchar);
+}
+
+static int __init dcc_console_setup(struct console *co, char *options)
+{
+	struct uart_port *port = &dcc_port;
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver dcc_reg;
+static struct console dcc_console = {
+	.name		= SERIAL_DCC_NAME,
+	.write		= dcc_console_write,
+	.device		= uart_console_device,
+	.setup		= dcc_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &dcc_reg,
+};
+
+static int __init dcc_console_init(void)
+{
+	register_console(&dcc_console);
+	return 0;
+}
+console_initcall(dcc_console_init);
+
+#define DCC_CONSOLE	&dcc_console
+#else
+#define DCC_CONSOLE	NULL
+#endif
+
+static struct uart_driver dcc_reg = {
+	.owner		= THIS_MODULE,
+	.driver_name	= SERIAL_DCC_NAME,
+	.dev_name	= SERIAL_DCC_NAME,
+	.major		= SERIAL_DCC_MAJOR,
+	.minor		= SERIAL_DCC_MINOR,
+	.nr		= UART_NR,
+	.cons		= DCC_CONSOLE,
+};
+
+static int __init dcc_init(void)
+{
+	int ret;
+
+	printk(KERN_INFO "DCC: JTAG1 Serial emulation driver\n");
+
+	ret = uart_register_driver(&dcc_reg);
+
+	if (ret)
+		return ret;
+
+	uart_add_one_port(&dcc_reg, &dcc_port);
+
+	return 0;
+}
+
+device_initcall(dcc_init);
+
+MODULE_DESCRIPTION("DCC(JTAG1) JTAG debugger console emulation driver");
+MODULE_AUTHOR("Hyok S. Choi <hyok.choi@samsung.com>");
+MODULE_SUPPORTED_DEVICE(SERIAL_DCC_NAME);
+MODULE_LICENSE("GPL");
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 563e234..a360c3a 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -196,6 +196,9 @@
 /* High Speed UART for Medfield */
 #define PORT_MFD	95
 
+/* DCC(JTAG) emulation port types */
+#define PORT_DCC_JTAG1	96
+
 #ifdef __KERNEL__
 
 #include <linux/compiler.h>
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 50+ messages in thread
* Re: [PATCH] serial: DCC(JTAG) serial and console emulation support
@ 2010-10-08  4:59 Daniel Walker
  2010-10-08  6:05 ` Mike Frysinger
  0 siblings, 1 reply; 50+ messages in thread
From: Daniel Walker @ 2010-10-08  4:59 UTC (permalink / raw)
  To: Mike Frysinger
  Cc: Greg KH, Alan Cox, linux-kernel, Hyok S. Choi, Tony Lindgren,
	Jeff Ohlstein, Ben Dooks, Alan Cox, Kukjin Kim, Feng Tang,
	Tobias Klauser, Jason Wessel, Philippe Langlais

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=utf-8, Size: 1771 bytes --]

I'm not disputing the serial core part. I ok with converting it to tty.

Daniel

Mike Frysinger <vapier@gentoo.org> wrote:

>On Thu, Oct 7, 2010 at 18:11, Daniel Walker wrote:
>> On Thu, 2010-10-07 at 14:52 -0700, Greg KH wrote:
>>> On Thu, Oct 07, 2010 at 02:47:18PM -0700, Daniel Walker wrote:
>>> > On Thu, 2010-10-07 at 14:15 -0700, Greg KH wrote:
>>> > > On Thu, Oct 07, 2010 at 01:51:18PM -0700, Daniel Walker wrote:
>>> > > > > We've said no over a period of about ten years to a lot of attempts to
>>> > > > > just borrow the ttyS0 range. If we'd said yes it would have been a
>>> > > > > complete mess by now.
>>> > > > >
>>> > > > > So the answer is no.
>>> > > >
>>> > > > Nothing can be unilateral, there's always room for exceptions. You
>>> > > > should say something more like "it's possible, but unlikely".
>>> > >
>>> > > Hm, how about this, as the TTY and serial driver[1] maintainer, I will
>>> > > not accept this kind of patch at all.
>>> > >
>>> > > Is that final enough for you?
>>> >
>>> > So you don't like it, that's fair enough .. <thinks>I wonder what other
>>> > maintainers I can send this too</thinks> ;)
>>> >
>>> > Can you be more specific about your objections
>>>
>>> See everything that Mike and Alan wrote, do I need to repeat it?
>>
>> I'm not sure .. I would say "Yes" if I didn't get the feeling your
>> already to tear my head off.
>>
>> I think the only disputed issue is the use of ttyS as options part of
>> the driver ..
>
>and using "serial_core" at all in favor of HVC or tty_core (the latter
>might be easier for you as the former is a bit of unknown atm)
>-mike
ÿôèº{.nÇ+‰·Ÿ®‰­†+%ŠËÿ±éݶ\x17¥Šwÿº{.nÇ+‰·¥Š{±þG«éÿŠ{ayº\x1dʇڙë,j\a­¢f£¢·hšïêÿ‘êçz_è®\x03(­éšŽŠÝ¢j"ú\x1a¶^[m§ÿÿ¾\a«þG«éÿ¢¸?™¨è­Ú&£ø§~á¶iO•æ¬z·švØ^\x14\x04\x1a¶^[m§ÿÿÃ\fÿ¶ìÿ¢¸?–I¥

^ permalink raw reply	[flat|nested] 50+ messages in thread
* Re: [PATCH] serial: DCC(JTAG) serial and console emulation support
@ 2010-10-08 19:34 matthieu castet
  2010-10-08 19:52 ` Daniel Walker
  2010-10-08 19:55 ` Daniel Walker
  0 siblings, 2 replies; 50+ messages in thread
From: matthieu castet @ 2010-10-08 19:34 UTC (permalink / raw)
  To: Daniel Walker, Linux Kernel list

[-- Attachment #1: Type: text/plain, Size: 352 bytes --]

Hi,

for the record I attach a modified version of the original samsung one that I did.

I improved a bit the reactivity, and did some hack to make polling in idle loop.
With recent kernel this should be done in a thread with SCHED_IDLE policy.

I also supported board with irq for rx and tx (not sure if the original driver supported it).



Matthieu

[-- Attachment #2: dcc.diff --]
[-- Type: text/x-diff, Size: 14540 bytes --]

diff --git a/drivers/parrot/serial/dcc.c b/drivers/parrot/serial/dcc.c
new file mode 100644
index 0000000..e778b0c
--- /dev/null
+++ b/drivers/parrot/serial/dcc.c
@@ -0,0 +1,590 @@
+/*
+ *  linux/drivers/serial/dcc_serial.c
+ *
+ *  serial port emulation driver for the JTAG DCC Terminal.
+ *
+ * DCC(JTAG1) protocol version for JTAG ICE/ICD Debuggers:
+ * 	Copyright (C) 2003, 2004, 2005 Hyok S. Choi (hyok.choi@samsung.com)
+ * 	SAMSUNG ELECTRONICS Co.,Ltd.
+ *  Copyright (C) 2008 Parrot SA by matthieu.castet@parrot.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *  Changelog:
+ *   Oct-2003 Hyok S. Choi	Created
+ *   Feb-2004 Hyok S. Choi	Updated for serial_core.c and 2.6 kernel
+ *   Mar-2005 Hyok S. Choi	renamed from T32 to DCC
+ *   Apr-2006 Hyok S. Choi	revised including the MAJOR number
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/serial.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include <linux/serial_core.h>
+
+/* for PORT_DCC_JTAG1 */
+#include <mach/parrot_kernel_ids.h>
+
+//#undef CONFIG_ARCH_PARROT
+/* XXX may be use a platform driver for this */
+#ifdef CONFIG_ARCH_PARROT
+#define DCC_IRQ_USED
+#define IRQ_RX 27
+#define IRQ_TX 28
+#else
+/* polling mode */
+#define IRQ_RX 0x12345678
+#define IRQ_TX 0
+
+#warning polling mode
+#endif
+
+#define UART_NR			1	/* we have only one JTAG port */
+
+#ifdef CONFIG_SERIAL_DCC_STDSERIAL
+/* use ttyS for emulation of standard serial driver */
+#define SERIAL_DCC_NAME		"ttyS"
+#define SERIAL_DCC_MAJOR	TTY_MAJOR
+#define SERIAL_DCC_MINOR	64
+#else
+/* use ttyJ0(128) */
+#define SERIAL_DCC_NAME		"ttyJ"
+#define SERIAL_DCC_MAJOR	204
+#define SERIAL_DCC_MINOR	186
+#endif
+
+/* DCC Status Bits */
+#define DCC_STATUS_RX		(1 << 0)
+#define DCC_STATUS_TX		(1 << 1)
+
+/* primitive JTAG1 protocol utilities */
+static inline u32 __dcc_getstatus(void)
+{
+	u32 ret;
+
+	asm __volatile__ ("mrc p14, 0, %0, c0, c0	@ read comms ctrl reg"
+		: "=r" (ret));
+
+	return ret;
+}
+
+static inline unsigned char __dcc_getchar(void)
+{
+	unsigned char c;
+
+	asm __volatile__ ("mrc p14, 0, %0, c1, c0	@ read comms data reg"
+		: "=r" (c));
+
+	return c;
+}
+
+static inline void __dcc_putchar(unsigned char c)
+{
+	asm __volatile__ ("mcr p14, 0, %0, c1, c0	@ write a char"
+		: /* no output register */
+		: "r" (c));
+}
+/* end of JTAG1 dependencies */
+
+static void dcc_serial_tx_chars(struct uart_port *port, int max_count);
+static void dcc_serial_rx_chars(struct uart_port *port);
+#ifdef DCC_IRQ_USED /* real IRQ used */
+/* check if there is a char to read and when don't read too much char */
+#define dcc_serial_tx_ready(port) \
+	((__dcc_getstatus() & DCC_STATUS_TX) == 0)
+
+enum {
+	TX_OFF,
+	TX_ON,
+};
+static int dcc_serial_tx_enable;
+enum {
+	RX_OFF,
+	RX_ON,
+	RX_PAUSE,
+};
+static int dcc_serial_rx_enable;
+/* port locked, interrupts locally disabled */
+static void dcc_serial_start_tx(struct uart_port *port)
+{
+	if (dcc_serial_tx_enable == TX_OFF) {
+		enable_irq(port->irq);
+		dcc_serial_tx_enable = TX_ON;
+	}
+}
+
+/* port locked, interrupts locally disabled */
+static void dcc_serial_stop_tx(struct uart_port *port)
+{
+	if (dcc_serial_tx_enable == TX_ON) {
+		disable_irq(port->irq);
+		dcc_serial_tx_enable = TX_OFF;
+	}
+}
+
+/* port locked, interrupts locally disabled */
+static void dcc_serial_stop_rx(struct uart_port *port)
+{
+	if (dcc_serial_rx_enable == RX_ON) {
+		disable_irq((int)port->membase);
+	}
+	dcc_serial_rx_enable = RX_OFF;
+}
+
+/* port locked, interrupts locally disabled */
+static void dcc_serial_throttle_rx(struct uart_port *port, int stop)
+{
+	if (stop && dcc_serial_rx_enable == RX_ON) {
+		disable_irq((int)port->membase);
+		dcc_serial_rx_enable = RX_PAUSE;
+	}
+	else if (stop == 0 && dcc_serial_rx_enable == RX_PAUSE) {
+		enable_irq((int)port->membase);
+		dcc_serial_rx_enable = RX_ON;
+	}
+}
+
+#if 0
+static void dcc_serial_rx_mchars(struct uart_port *port)
+{
+	int timeout;
+	unsigned int read = 0;
+	unsigned int it = 0;
+	unsigned int it1 = 0;
+	do {
+		timeout = 1000;
+		/* wait for a char */
+		//while ((__dcc_getstatus() & DCC_STATUS_RX) == 0 && timeout-- > 0) {
+		while ((readl(0xfc000008) & 0x08000000) == 0) {
+			cpu_relax();
+		}
+		/* read it */
+		if (timeout >= 0) {
+			if (readl(0xfc000008) & 0x08000000)
+				it++;
+			read++;
+			if (read & 0x100) {
+				printk("dcc_serial stat %u %u %u\n", read, it, it1);
+				read = it = it1 = 0;
+			}
+			dcc_serial_rx_chars(port);
+			if (readl(0xfc000008) & 0x08000000)
+				it1++;
+		}
+	} while (/*timeout >= 0*/1);
+}
+#endif
+#ifdef CONFIG_SERIAL_DCC_IDLE_POLL
+int dcc_idle(void)
+{
+	return 0;
+}
+#endif
+
+static irqreturn_t dcc_serial_int_rx(int irq, void *dev_id)
+{
+	struct uart_port *port = dev_id;
+	spin_lock(&port->lock);
+	dcc_serial_rx_chars(port);
+	spin_unlock(&port->lock);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t dcc_serial_int_tx(int irq, void *dev_id)
+{
+	struct uart_port *port = dev_id;
+	spin_lock(&port->lock);
+
+	dcc_serial_tx_chars(port, 64);
+	spin_unlock(&port->lock);
+
+	return IRQ_HANDLED;
+}
+
+static int dcc_serial_startup(struct uart_port *port)
+{
+	int retval;
+
+	dcc_serial_tx_enable = TX_ON;
+	dcc_serial_rx_enable = RX_ON;
+	/* Allocate the IRQ */
+	retval = request_irq(port->irq, dcc_serial_int_tx, IRQF_DISABLED,
+			     "serial_dcc_serial_tx", port);
+	if (retval)
+		return retval;
+
+	/* Allocate the IRQ */
+	retval = request_irq((int)port->membase, dcc_serial_int_rx, IRQF_DISABLED,
+			     "serial_dcc_serial_rx", port);
+	if (retval)
+		return retval;
+
+	return 0;
+}
+
+static void dcc_serial_shutdown(struct uart_port *port)
+{
+	free_irq(port->irq, port);
+	free_irq((int)port->membase, port);
+}
+#else /* emulation by scheduled work */
+static void dcc_serial_poll(struct work_struct *work);
+static DECLARE_DELAYED_WORK(dcc_serial_poll_task, dcc_serial_poll);
+static struct uart_port dcc_serial_port;
+static int dcc_serial_active;
+
+/* in polling mode, we wait for the next character */
+static int dcc_serial_tx_ready(struct uart_port *port) {
+	while (__dcc_getstatus() & DCC_STATUS_TX) {
+		/* while waiting, we can check rx : it is free */
+		dcc_serial_rx_chars(port);
+		cpu_relax();
+	}
+	return 1;
+}
+
+static void dcc_serial_start_tx(struct uart_port *port)
+{
+	dcc_serial_tx_chars(port, 0xFFFF);
+}
+
+static void dcc_serial_stop_tx(struct uart_port *port)
+{
+}
+
+static void dcc_serial_throttle_rx(struct uart_port *port, int stop)
+{
+}
+
+static void dcc_serial_stop_rx(struct uart_port *port)
+{
+}
+
+/* you can call this on your idle loop instead of sleeping
+ */
+#ifdef CONFIG_SERIAL_DCC_IDLE_POLL
+int dcc_idle(void)
+{
+	struct uart_port *port = &dcc_serial_port;
+	if (dcc_serial_active == 0)
+		return 0;
+
+	spin_lock(&port->lock);
+
+	dcc_serial_rx_chars(port);
+	dcc_serial_tx_chars(port, 64);
+	dcc_serial_rx_chars(port);
+
+	spin_unlock(&port->lock);
+	return 0;
+}
+#endif
+
+/* we poll dcc every jiffies */
+static void dcc_serial_poll(struct work_struct *work)
+{
+	struct uart_port *port = &dcc_serial_port;
+
+	spin_lock(&port->lock);
+
+	dcc_serial_rx_chars(port);
+	dcc_serial_tx_chars(port, 0xFFFF);
+	dcc_serial_rx_chars(port);
+
+	schedule_delayed_work(&dcc_serial_poll_task, 1);
+
+	spin_unlock(&port->lock);
+}
+static int dcc_serial_startup(struct uart_port *port)
+{
+	/* shcedule the polling work */
+	schedule_delayed_work(&dcc_serial_poll_task, 1);
+	dcc_serial_active = 1;
+
+	return 0;
+}
+
+static void dcc_serial_shutdown(struct uart_port *port)
+{
+	cancel_rearming_delayed_work(&dcc_serial_poll_task);
+	dcc_serial_active = 0;
+}
+#endif /* end of DCC_IRQ_USED */
+
+
+static void dcc_serial_putchar(struct uart_port *port, int ch)
+{
+	while (__dcc_getstatus() & DCC_STATUS_TX)
+		cpu_relax();
+	__dcc_putchar((char)(ch & 0xFF));
+}
+
+static void dcc_serial_rx_chars(struct uart_port *port)
+{
+	unsigned char ch;
+	/* max char to send */
+	int count = 64;
+	struct tty_struct *tty = port->info->port.tty;
+
+	/*
+	 * check input.
+	 * checking JTAG flag is better to resolve the status test.
+	 * incount is NOT used for JTAG1 protocol.
+	 */
+
+	while (__dcc_getstatus() & DCC_STATUS_RX && count-- > 0) {
+		/* for JTAG 1 protocol, incount is always 1. */
+		ch = __dcc_getchar();
+
+		tty_insert_flip_char(tty, ch, TTY_NORMAL);
+		port->icount.rx++;
+	}
+	tty_flip_buffer_push(tty);
+}
+
+static void dcc_serial_tx_chars(struct uart_port *port, int max_count)
+{
+	struct circ_buf *xmit = &port->info->xmit;
+
+	if (port->x_char) {
+		dcc_serial_putchar(port, port->x_char);
+		port->icount.tx++;
+		port->x_char = 0;
+		goto out;
+	}
+	if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
+		dcc_serial_stop_tx(port);
+		goto out;
+	}
+
+	while (!uart_circ_empty(xmit) && dcc_serial_tx_ready(port)
+			&& max_count-- > 0) {
+		__dcc_putchar(xmit->buf[xmit->tail]);
+		xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+		port->icount.tx++;
+	}
+
+	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) {
+		uart_write_wakeup(port);
+	}
+	if (uart_circ_empty(xmit)) {
+		dcc_serial_stop_tx(port);
+	}
+
+out:
+	return;
+}
+
+static unsigned int dcc_serial_tx_empty(struct uart_port *port)
+{
+	return (__dcc_getstatus() & DCC_STATUS_TX) ? 0:TIOCSER_TEMT;
+}
+
+static unsigned int dcc_serial_get_mctrl(struct uart_port *port)
+{
+	return TIOCM_CTS | TIOCM_DSR | TIOCM_CD;
+}
+
+/* need for throttle/unthrottle */
+static void dcc_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+	/* throttle clear TIOCM_RTS, unthrottle set it */
+	dcc_serial_throttle_rx(port, !(mctrl & TIOCM_RTS));
+}
+
+static void dcc_serial_set_termios(struct uart_port *port, struct ktermios *termios,
+		   struct ktermios *old)
+{
+	unsigned long flags;
+	unsigned int baud, quot;
+
+	/*
+	 * We don't support parity, stop bits, or anything other
+	 * than 8 bits, so clear these termios flags.
+	 */
+	termios->c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD | CREAD);
+	termios->c_cflag |= CS8;
+
+	/*
+	 * We don't appear to support any error conditions either.
+	 */
+	termios->c_iflag &= ~(INPCK | IGNPAR | IGNBRK | BRKINT);
+
+	/*
+	 * Ask the core to calculate the divisor for us.
+	 */
+	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); 
+	quot = uart_get_divisor(port, baud);
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static const char * dcc_serial_type(struct uart_port *port)
+{
+	return port->type == PORT_DCC_JTAG1 ? "DCC" : NULL;
+}
+
+static int dcc_serial_request_port(struct uart_port *port)
+{
+	return 0;
+}
+
+/*
+ * Configure/autoconfigure the port.
+ */
+static void dcc_serial_config_port(struct uart_port *port, int flags)
+{
+	if (flags & UART_CONFIG_TYPE) {
+		port->type = PORT_DCC_JTAG1;
+		dcc_serial_request_port(port);
+	}
+}
+
+/*
+ * verify the new serial_struct (for TIOCSSERIAL).
+ */
+static int dcc_serial_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+	int ret = 0;
+	if (ser->type != PORT_UNKNOWN && ser->type != PORT_DCC_JTAG1)
+		ret = -EINVAL;
+	if (ser->irq < 0 || ser->irq >= NR_IRQS)
+		ret = -EINVAL;
+	if (ser->baud_base < 9600)
+		ret = -EINVAL;
+	return ret;
+}
+
+/* dummy operation handlers for uart_ops */
+static void dcc_serial_dummy_ops(struct uart_port *port)
+{
+}
+
+static void dcc_serial_break_ctl(struct uart_port *port, int i)
+{
+}
+
+static struct uart_ops dcc_serial_pops = {
+	.tx_empty	= dcc_serial_tx_empty,
+	.set_mctrl	= dcc_serial_set_mctrl,
+	.get_mctrl	= dcc_serial_get_mctrl,
+	.stop_tx	= dcc_serial_stop_tx,
+	.start_tx	= dcc_serial_start_tx,
+	.stop_rx	= dcc_serial_stop_rx,
+	.enable_ms	= dcc_serial_dummy_ops,
+	.break_ctl	= dcc_serial_break_ctl,
+	.startup	= dcc_serial_startup,
+	.shutdown	= dcc_serial_shutdown,
+	.set_termios	= dcc_serial_set_termios,
+	.type		= dcc_serial_type,
+	.release_port	= dcc_serial_dummy_ops,
+	.request_port	= dcc_serial_request_port,
+	.config_port	= dcc_serial_config_port,
+	.verify_port	= dcc_serial_verify_port,
+};
+
+static struct uart_port dcc_serial_port = {
+	.mapbase	= 0x12345678,		/* for serial_core.c */
+	.iotype		= UPIO_MEM,	
+	.membase	= (char*)IRQ_RX,	/* we need these garbages */
+	.irq		= IRQ_TX,
+	.uartclk	= 14745600,
+	.fifosize	= 0,
+	.ops		= &dcc_serial_pops,
+	.flags		= UPF_BOOT_AUTOCONF,
+	.line		= 0,
+};
+
+#ifdef CONFIG_SERIAL_DCC_CONSOLE
+static void dcc_serial_console_write(struct console *co, const char *s, unsigned int count)
+{
+	uart_console_write(&dcc_serial_port, s, count, dcc_serial_putchar);
+}
+
+static int __init dcc_serial_console_setup(struct console *co, char *options)
+{
+	struct uart_port *port = &dcc_serial_port;
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int flow = 'n';
+
+	if (options)
+		uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+	return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct uart_driver dcc_serial_reg;
+static struct console dcc_serial_console = {
+	.name		= SERIAL_DCC_NAME,
+	.write		= dcc_serial_console_write,
+	.device		= uart_console_device,
+	.setup		= dcc_serial_console_setup,
+	.flags		= CON_PRINTBUFFER,
+	.index		= -1,
+	.data		= &dcc_serial_reg,
+};
+
+static int __init dcc_serial_console_init(void)
+{
+	register_console(&dcc_serial_console);
+	return 0;
+}
+console_initcall(dcc_serial_console_init);
+
+#define DCC_CONSOLE	&dcc_serial_console
+#else
+#define DCC_CONSOLE	NULL
+#endif
+
+static struct uart_driver dcc_serial_reg = {
+	.owner		= THIS_MODULE,
+	.driver_name	= SERIAL_DCC_NAME,
+	.dev_name	= SERIAL_DCC_NAME,
+	.major		= SERIAL_DCC_MAJOR,
+	.minor		= SERIAL_DCC_MINOR,
+	.nr		= UART_NR,
+	.cons		= DCC_CONSOLE,
+};
+
+static int __init dcc_serial_init(void)
+{
+	int ret;
+
+	printk(KERN_INFO "DCC: JTAG1 Serial emulation driver driver $Revision: 1.9 $\n");
+
+	ret = uart_register_driver(&dcc_serial_reg);
+
+	if (ret)
+		return ret;
+
+	uart_add_one_port(&dcc_serial_reg, &dcc_serial_port);
+
+	return 0;
+}
+
+__initcall(dcc_serial_init);
+
+MODULE_DESCRIPTION("DCC(JTAG1) JTAG debugger console emulation driver");
+MODULE_AUTHOR("Hyok S. Choi <hyok.choi@samsung.com>");
+MODULE_SUPPORTED_DEVICE(SERIAL_DCC_NAME);
+MODULE_LICENSE("GPL");


^ permalink raw reply related	[flat|nested] 50+ messages in thread

end of thread, other threads:[~2010-10-09  5:39 UTC | newest]

Thread overview: 50+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-10-07 18:36 [PATCH] serial: DCC(JTAG) serial and console emulation support Daniel Walker
2010-10-07 19:25 ` Mike Frysinger
2010-10-07 19:39   ` Daniel Walker
2010-10-07 19:48     ` Mike Frysinger
2010-10-07 19:58       ` Daniel Walker
2010-10-07 20:02         ` Mike Frysinger
2010-10-07 20:06           ` Daniel Walker
2010-10-07 20:47             ` Mike Frysinger
2010-10-07 20:59               ` Daniel Walker
2010-10-07 21:05                 ` Mike Frysinger
2010-10-07 21:17                   ` Daniel Walker
2010-10-07 21:32                     ` Mike Frysinger
2010-10-07 21:50                       ` Daniel Walker
2010-10-07 20:52   ` Alan Cox
2010-10-07 20:37     ` Daniel Walker
2010-10-07 21:08       ` Alan Cox
2010-10-07 20:50 ` Alan Cox
2010-10-07 20:36   ` Daniel Walker
2010-10-07 21:05     ` Alan Cox
2010-10-07 20:51       ` Daniel Walker
2010-10-07 21:03         ` Mike Frysinger
2010-10-07 21:14           ` Daniel Walker
2010-10-08  8:13             ` Alan Cox
2010-10-08 15:23               ` Daniel Walker
2010-10-08 15:40                 ` Greg KH
2010-10-08 16:11                   ` Daniel Walker
2010-10-08 16:56                 ` Alan Cox
2010-10-08 16:45                   ` Daniel Walker
2010-10-08 18:38                     ` Mike Frysinger
2010-10-08 19:01                       ` Daniel Walker
2010-10-08 19:20                         ` Mike Frysinger
2010-10-08 19:50                           ` Daniel Walker
2010-10-08 22:02                             ` Mike Frysinger
2010-10-08 22:22                               ` Daniel Walker
2010-10-09  5:38                                 ` Mike Frysinger
2010-10-07 21:15         ` Greg KH
2010-10-07 21:47           ` Daniel Walker
2010-10-07 21:52             ` Greg KH
2010-10-07 22:11               ` Daniel Walker
2010-10-08  2:04                 ` Mike Frysinger
2010-10-07 21:38         ` Alan Cox
2010-10-07 21:41           ` Daniel Walker
2010-10-08  8:18             ` Alan Cox
2010-10-08 15:16               ` Daniel Walker
  -- strict thread matches above, loose matches on Subject: below --
2010-10-08  4:59 Daniel Walker
2010-10-08  6:05 ` Mike Frysinger
2010-10-08 19:34 matthieu castet
2010-10-08 19:52 ` Daniel Walker
2010-10-08 19:55 ` Daniel Walker
2010-10-08 20:40   ` matthieu castet

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox