All of lore.kernel.org
 help / color / mirror / Atom feed
From: James Hogan <james.hogan@imgtec.com>
To: <linux-kernel@vger.kernel.org>,
	Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	Jiri Slaby <jslaby@suse.cz>
Cc: James Hogan <james.hogan@imgtec.com>,
	<linux-arch@vger.kernel.org>,
	"Alan Cox" <alan@lxorguk.ukuu.org.uk>,
	Andrew Morton <akpm@linux-foundation.org>,
	Mauro Carvalho Chehab <mchehab@redhat.com>,
	Cesar Eduardo Barros <cesarb@cesarb.net>,
	Joe Perches <joe@perches.com>,
	"David S. Miller" <davem@davemloft.net>
Subject: Re: [PATCH v3 43/44] tty/metag_da: Add metag DA TTY driver
Date: Fri, 25 Jan 2013 11:36:53 +0000	[thread overview]
Message-ID: <51026E55.80905@imgtec.com> (raw)
In-Reply-To: <50F3E3FF.4080803@imgtec.com>

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

Hi,

On 14/01/13 10:54, James Hogan wrote:
> Here's a new version addressing Jiri's feedback.
> 
> Add a TTY driver for communicating over a Meta DA (Debug Adapter)
> channel using the bios channel SWITCH operation.
> 
> Signed-off-by: James Hogan <james.hogan@imgtec.com>
> Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> Cc: Jiri Slaby <jslaby@suse.cz>
> Cc: Andrew Morton <akpm@linux-foundation.org>
> Cc: Mauro Carvalho Chehab <mchehab@redhat.com>
> Cc: Cesar Eduardo Barros <cesarb@cesarb.net>
> Cc: Joe Perches <joe@perches.com>
> Cc: "David S. Miller" <davem@davemloft.net>

If this looks okay, I'd appreciate if somebody could Ack it.

I have some further patches ready to simplify the code around
tty_prepare_flip_string and tty_flip_buffer_push to avoid taking a tty
kref, after Jiri's changes in that area in linux-next. I guess these can
be applied once both sets of changes are merged.

Thanks
James

> ---
>  MAINTAINERS                            |    1 +
>  arch/metag/Kconfig                     |    4 +-
>  arch/metag/configs/meta1_defconfig     |    2 +
>  arch/metag/configs/meta2_defconfig     |    2 +
>  arch/metag/configs/meta2_smp_defconfig |    2 +
>  arch/metag/kernel/setup.c              |   12 +
>  drivers/tty/Kconfig                    |   13 +
>  drivers/tty/Makefile                   |    1 +
>  drivers/tty/metag_da.c                 |  679 ++++++++++++++++++++++++++++++++
>  9 files changed, 715 insertions(+), 1 deletions(-)
>  create mode 100644 drivers/tty/metag_da.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index dbc2bd6..63ccab6 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -5002,6 +5002,7 @@ S:	Supported
>  F:	arch/metag/
>  F:	Documentation/metag/
>  F:	Documentation/devicetree/bindings/metag/
> +F:	drivers/tty/metag_da.c
>  
>  MICROBLAZE ARCHITECTURE
>  M:	Michal Simek <monstr@monstr.eu>
> diff --git a/arch/metag/Kconfig b/arch/metag/Kconfig
> index 9bd0bc1..f4ef3bb 100644
> --- a/arch/metag/Kconfig
> +++ b/arch/metag/Kconfig
> @@ -221,7 +221,9 @@ config METAG_DA
>  	  of the DA will be detected automatically at boot, so it is safe to say
>  	  Y to this option even when booting without a DA.
>  
> -	  This enables support for services provided by DA JTAG debug adapters.
> +	  This enables support for services provided by DA JTAG debug adapters,
> +	  such as:
> +	  - communication over DA channels (such as the console driver).
>  
>  menu "Boot options"
>  
> diff --git a/arch/metag/configs/meta1_defconfig b/arch/metag/configs/meta1_defconfig
> index 0773425..ad663ca 100644
> --- a/arch/metag/configs/meta1_defconfig
> +++ b/arch/metag/configs/meta1_defconfig
> @@ -28,6 +28,8 @@ CONFIG_BLK_DEV_RAM_SIZE=16384
>  # CONFIG_SERIO is not set
>  # CONFIG_VT is not set
>  # CONFIG_LEGACY_PTYS is not set
> +CONFIG_DA_TTY=y
> +CONFIG_DA_CONSOLE=y
>  # CONFIG_DEVKMEM is not set
>  # CONFIG_HW_RANDOM is not set
>  # CONFIG_HWMON is not set
> diff --git a/arch/metag/configs/meta2_defconfig b/arch/metag/configs/meta2_defconfig
> index 001f9bf..47922e93 100644
> --- a/arch/metag/configs/meta2_defconfig
> +++ b/arch/metag/configs/meta2_defconfig
> @@ -29,6 +29,8 @@ CONFIG_BLK_DEV_RAM_SIZE=16384
>  # CONFIG_SERIO is not set
>  # CONFIG_VT is not set
>  # CONFIG_LEGACY_PTYS is not set
> +CONFIG_DA_TTY=y
> +CONFIG_DA_CONSOLE=y
>  # CONFIG_DEVKMEM is not set
>  # CONFIG_HW_RANDOM is not set
>  # CONFIG_HWMON is not set
> diff --git a/arch/metag/configs/meta2_smp_defconfig b/arch/metag/configs/meta2_smp_defconfig
> index d6f7e46..f508250 100644
> --- a/arch/metag/configs/meta2_smp_defconfig
> +++ b/arch/metag/configs/meta2_smp_defconfig
> @@ -30,6 +30,8 @@ CONFIG_BLK_DEV_RAM_SIZE=16384
>  # CONFIG_SERIO is not set
>  # CONFIG_VT is not set
>  # CONFIG_LEGACY_PTYS is not set
> +CONFIG_DA_TTY=y
> +CONFIG_DA_CONSOLE=y
>  # CONFIG_DEVKMEM is not set
>  # CONFIG_HW_RANDOM is not set
>  # CONFIG_HWMON is not set
> diff --git a/arch/metag/kernel/setup.c b/arch/metag/kernel/setup.c
> index 1410db0..0d46334 100644
> --- a/arch/metag/kernel/setup.c
> +++ b/arch/metag/kernel/setup.c
> @@ -61,6 +61,11 @@ extern char _heap_start[];
>  extern u32 __dtb_start[];
>  #endif
>  
> +#ifdef CONFIG_DA_CONSOLE
> +/* Our early channel based console driver */
> +extern struct console dash_console;
> +#endif
> +
>  struct machine_desc *machine_desc __initdata;
>  
>  /*
> @@ -182,6 +187,13 @@ void __init setup_arch(char **cmdline_p)
>  	metag_cache_probe();
>  
>  	metag_da_probe();
> +#ifdef CONFIG_DA_CONSOLE
> +	if (metag_da_enabled()) {
> +		/* An early channel based console driver */
> +		register_console(&dash_console);
> +		add_preferred_console("ttyDA", 1, NULL);
> +	}
> +#endif
>  
>  	/* try interpreting the argument as a device tree */
>  	machine_desc = setup_machine_fdt(original_cmd_line);
> diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig
> index 0ecf22b..5eef53d 100644
> --- a/drivers/tty/Kconfig
> +++ b/drivers/tty/Kconfig
> @@ -388,3 +388,16 @@ config PPC_EARLY_DEBUG_EHV_BC_HANDLE
>  	  If the number you specify is not a valid byte channel handle, then
>  	  there simply will be no early console output.  This is true also
>  	  if you don't boot under a hypervisor at all.
> +
> +config DA_TTY
> +	bool "DA TTY"
> +	depends on METAG_DA
> +	select SERIAL_NONSTANDARD
> +	help
> +	  This enables a TTY on a Dash channel.
> +
> +config DA_CONSOLE
> +	bool "DA Console"
> +	depends on DA_TTY
> +	help
> +	  This enables a console on a Dash channel.
> diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile
> index 2953059..dbd7745 100644
> --- a/drivers/tty/Makefile
> +++ b/drivers/tty/Makefile
> @@ -27,5 +27,6 @@ obj-$(CONFIG_SYNCLINK_GT)	+= synclink_gt.o
>  obj-$(CONFIG_SYNCLINKMP)	+= synclinkmp.o
>  obj-$(CONFIG_SYNCLINK)		+= synclink.o
>  obj-$(CONFIG_PPC_EPAPR_HV_BYTECHAN) += ehv_bytechan.o
> +obj-$(CONFIG_DA_TTY)	+= metag_da.o
>  
>  obj-y += ipwireless/
> diff --git a/drivers/tty/metag_da.c b/drivers/tty/metag_da.c
> new file mode 100644
> index 0000000..6a0b2e6
> --- /dev/null
> +++ b/drivers/tty/metag_da.c
> @@ -0,0 +1,679 @@
> +/*
> + *  dashtty.c - tty driver for Dash channels interface.
> + *
> + *  Copyright (C) 2007,2008,2012 Imagination Technologies
> + *
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License.  See the file COPYING in the main directory of this archive
> + * for more details.
> + *
> + */
> +
> +#include <linux/atomic.h>
> +#include <linux/completion.h>
> +#include <linux/console.h>
> +#include <linux/delay.h>
> +#include <linux/export.h>
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/kthread.h>
> +#include <linux/mutex.h>
> +#include <linux/sched.h>
> +#include <linux/serial.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/string.h>
> +#include <linux/timer.h>
> +#include <linux/tty.h>
> +#include <linux/tty_driver.h>
> +#include <linux/tty_flip.h>
> +#include <linux/uaccess.h>
> +
> +#include <asm/da.h>
> +
> +/* Channel error codes */
> +#define CONAOK	0
> +#define CONERR	1
> +#define CONBAD	2
> +#define CONPRM	3
> +#define CONADR	4
> +#define CONCNT	5
> +#define CONCBF	6
> +#define CONCBE	7
> +#define CONBSY	8
> +
> +/* Default channel for the console */
> +#define CONSOLE_CHANNEL      1
> +
> +#define NUM_TTY_CHANNELS     6
> +
> +/* Auto allocate */
> +#define DA_TTY_MAJOR        0
> +
> +/* A speedy poll rate helps the userland debug process connection response.
> + * But, if you set it too high then no other userland processes get much
> + * of a look in.
> + */
> +#define DA_TTY_POLL (HZ / 50)
> +
> +/*
> + * A short put delay improves latency but has a high throughput overhead
> + */
> +#define DA_TTY_PUT_DELAY (HZ / 100)
> +
> +static atomic_t num_channels_need_poll = ATOMIC_INIT(0);
> +
> +static struct timer_list poll_timer;
> +
> +static struct tty_driver *channel_driver;
> +
> +static struct timer_list put_timer;
> +static struct task_struct *dashtty_thread;
> +
> +#define RX_BUF_SIZE 1024
> +
> +enum {
> +	INCHR = 1,
> +	OUTCHR,
> +	RDBUF,
> +	WRBUF,
> +	RDSTAT
> +};
> +
> +/**
> + * struct dashtty_port - Wrapper struct for dashtty tty_port.
> + * @port:		TTY port data
> + * @rx_lock:		Lock for rx_buf.
> + *			This protects between the poll timer and user context.
> + *			It's also held during read SWITCH operations.
> + * @rx_buf:		Read buffer
> + * @xmit_lock:		Lock for xmit_*, and port.xmit_buf.
> + *			This protects between user context and kernel thread.
> + *			It's also held during write SWITCH operations.
> + * @xmit_cnt:		Size of xmit buffer contents
> + * @xmit_head:		Head of xmit buffer where data is written
> + * @xmit_tail:		Tail of xmit buffer where data is read
> + * @xmit_empty:		Completion for xmit buffer being empty
> + */
> +struct dashtty_port {
> +	struct tty_port		 port;
> +	spinlock_t		 rx_lock;
> +	void			*rx_buf;
> +	struct mutex		 xmit_lock;
> +	unsigned int		 xmit_cnt;
> +	unsigned int		 xmit_head;
> +	unsigned int		 xmit_tail;
> +	struct completion	 xmit_empty;
> +};
> +
> +static struct dashtty_port dashtty_ports[NUM_TTY_CHANNELS];
> +
> +static atomic_t dashtty_xmit_cnt = ATOMIC_INIT(0);
> +static wait_queue_head_t dashtty_waitqueue;
> +
> +/*
> + * Low-level DA channel access routines
> + */
> +static int chancall(int in_bios_function, int in_channel,
> +		    int in_arg2, void *in_arg3,
> +		    void *in_arg4)
> +{
> +	register int   bios_function asm("D1Ar1") = in_bios_function;
> +	register int   channel       asm("D0Ar2") = in_channel;
> +	register int   arg2          asm("D1Ar3") = in_arg2;
> +	register void *arg3          asm("D0Ar4") = in_arg3;
> +	register void *arg4          asm("D1Ar5") = in_arg4;
> +	register int   bios_call     asm("D0Ar6") = 3;
> +	register int   result        asm("D0Re0");
> +
> +	asm volatile (
> +		"MSETL	[A0StP++], %6,%4,%2\n\t"
> +		"ADD	A0StP, A0StP, #8\n\t"
> +		"SWITCH	#0x0C30208\n\t"
> +		"GETD	%0, [A0StP+#-8]\n\t"
> +		"SUB	A0StP, A0StP, #(4*6)+8\n\t"
> +		: "=d" (result)   /* outs */
> +		: "d" (bios_function),
> +		  "d" (channel),
> +		  "d" (arg2),
> +		  "d" (arg3),
> +		  "d" (arg4),
> +		  "d" (bios_call) /* ins */
> +		: "memory");
> +
> +	return result;
> +}
> +
> +/*
> + * Attempts to fetch count bytes from channel and returns actual count.
> + */
> +static int fetch_data(struct tty_struct *tty)
> +{
> +	unsigned int channel = tty->index;
> +	struct dashtty_port *dport = &dashtty_ports[channel];
> +	int received = 0;
> +
> +	spin_lock_bh(&dport->rx_lock);
> +	/* check the port isn't being shut down */
> +	if (!dport->rx_buf)
> +		goto unlock;
> +	if (chancall(RDBUF, channel, RX_BUF_SIZE,
> +		     (void *)dport->rx_buf, &received) == CONAOK) {
> +		if (received) {
> +			int space;
> +			unsigned char *cbuf;
> +
> +			space = tty_prepare_flip_string(tty, &cbuf, received);
> +
> +			if (space <= 0)
> +				goto unlock;
> +
> +			memcpy(cbuf, dport->rx_buf, space);
> +			tty_flip_buffer_push(tty);
> +		}
> +	}
> +unlock:
> +	spin_unlock_bh(&dport->rx_lock);
> +
> +	return received;
> +}
> +
> +/**
> + * find_channel_to_poll() - Returns kref to the next channel tty to poll.
> + * Returns:	The TTY of the next channel to poll, or NULL if no TTY needs
> + *		polling. Release with tty_kref_put().
> + */
> +static struct tty_struct *find_channel_to_poll(void)
> +{
> +	static int last_polled_channel;
> +	int last = last_polled_channel;
> +	int chan;
> +	struct tty_struct *tty = NULL;
> +
> +	for (chan = last + 1; ; ++chan) {
> +		if (chan >= NUM_TTY_CHANNELS)
> +			chan = 0;
> +
> +		tty = tty_port_tty_get(&dashtty_ports[chan].port);
> +		if (tty) {
> +			last_polled_channel = chan;
> +			return tty;
> +		}
> +
> +		if (chan == last)
> +			break;
> +	}
> +	return tty;
> +}
> +
> +/**
> + * put_channel_data() - Write out a block of channel data.
> + * @chan:	DA channel number.
> + *
> + * Write a single block of data out to the debug adapter. If the circular buffer
> + * is wrapped then only the first block is written.
> + *
> + * Returns:	1 if the remote buffer was too full to accept data.
> + *		0 otherwise.
> + */
> +static int put_channel_data(unsigned int chan)
> +{
> +	struct dashtty_port *dport;
> +	struct tty_struct *tty;
> +	int number_written;
> +	unsigned int count = 0;
> +
> +	dport = &dashtty_ports[chan];
> +	mutex_lock(&dport->xmit_lock);
> +	if (dport->xmit_cnt) {
> +		count = min((unsigned int)(SERIAL_XMIT_SIZE - dport->xmit_tail),
> +			    dport->xmit_cnt);
> +		chancall(WRBUF, chan, count,
> +			 dport->port.xmit_buf + dport->xmit_tail,
> +			 &number_written);
> +		dport->xmit_cnt -= number_written;
> +		if (!dport->xmit_cnt) {
> +			/* reset pointers to avoid wraps */
> +			dport->xmit_head = 0;
> +			dport->xmit_tail = 0;
> +			complete(&dport->xmit_empty);
> +		} else {
> +			dport->xmit_tail += number_written;
> +			if (dport->xmit_tail >= SERIAL_XMIT_SIZE)
> +				dport->xmit_tail -= SERIAL_XMIT_SIZE;
> +		}
> +		atomic_sub(number_written, &dashtty_xmit_cnt);
> +	}
> +	mutex_unlock(&dport->xmit_lock);
> +
> +	/* if we've made more data available, wake up tty */
> +	if (count && number_written) {
> +		tty = tty_port_tty_get(&dport->port);
> +		if (tty) {
> +			tty_wakeup(tty);
> +			tty_kref_put(tty);
> +		}
> +	}
> +
> +	/* did the write fail? */
> +	return count && !number_written;
> +}
> +
> +/**
> + * put_data() - Kernel thread to write out blocks of channel data to DA.
> + * @arg:	Unused.
> + *
> + * This kernel thread runs while @dashtty_xmit_cnt != 0, and loops over the
> + * channels to write out any buffered data. If any of the channels stall due to
> + * the remote buffer being full, a hold off happens to allow the debugger to
> + * drain the buffer.
> + */
> +static int put_data(void *arg)
> +{
> +	unsigned int chan, stall;
> +
> +	__set_current_state(TASK_RUNNING);
> +	while (!kthread_should_stop()) {
> +		/*
> +		 * For each channel see if there's anything to transmit in the
> +		 * port's xmit_buf.
> +		 */
> +		stall = 0;
> +		for (chan = 0; chan < NUM_TTY_CHANNELS; ++chan)
> +			stall += put_channel_data(chan);
> +
> +		/*
> +		 * If some of the buffers are full, hold off for a short while
> +		 * to allow them to empty.
> +		 */
> +		if (stall)
> +			msleep(25);
> +
> +		wait_event_interruptible(dashtty_waitqueue,
> +					 atomic_read(&dashtty_xmit_cnt));
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + *	This gets called every DA_TTY_POLL and polls the channels for data
> + */
> +static void dashtty_timer(unsigned long ignored)
> +{
> +	struct tty_struct *tty;
> +
> +	/* If there are no ports open do nothing and don't poll again. */
> +	if (!atomic_read(&num_channels_need_poll))
> +		return;
> +
> +	tty = find_channel_to_poll();
> +
> +	/* Did we find a channel to poll? */
> +	if (tty) {
> +		fetch_data(tty);
> +		tty_kref_put(tty);
> +	}
> +
> +	mod_timer_pinned(&poll_timer, jiffies + DA_TTY_POLL);
> +}
> +
> +static void add_poll_timer(struct timer_list *poll_timer)
> +{
> +	setup_timer(poll_timer, dashtty_timer, 0);
> +	poll_timer->expires = jiffies + DA_TTY_POLL;
> +
> +	/*
> +	 * Always attach the timer to the boot CPU. The DA channels are per-CPU
> +	 * so all polling should be from a single CPU.
> +	 */
> +	add_timer_on(poll_timer, 0);
> +}
> +
> +static int dashtty_port_activate(struct tty_port *port, struct tty_struct *tty)
> +{
> +	struct dashtty_port *dport = container_of(port, struct dashtty_port,
> +						  port);
> +	void *rx_buf;
> +
> +	/* Allocate the buffer we use for writing data */
> +	if (tty_port_alloc_xmit_buf(port) < 0)
> +		goto err;
> +
> +	/* Allocate the buffer we use for reading data */
> +	rx_buf = kzalloc(RX_BUF_SIZE, GFP_KERNEL);
> +	if (!rx_buf)
> +		goto err_free_xmit;
> +
> +	spin_lock_bh(&dport->rx_lock);
> +	dport->rx_buf = rx_buf;
> +	spin_unlock_bh(&dport->rx_lock);
> +
> +	/*
> +	 * Don't add the poll timer if we're opening a console. This
> +	 * avoids the overhead of polling the Dash but means it is not
> +	 * possible to have a login on /dev/console.
> +	 *
> +	 */
> +	if (dport != &dashtty_ports[CONSOLE_CHANNEL])
> +		if (atomic_inc_return(&num_channels_need_poll) == 1)
> +			add_poll_timer(&poll_timer);
> +
> +	return 0;
> +err_free_xmit:
> +	tty_port_free_xmit_buf(port);
> +err:
> +	return -ENOMEM;
> +}
> +
> +static void dashtty_port_shutdown(struct tty_port *port)
> +{
> +	struct dashtty_port *dport = container_of(port, struct dashtty_port,
> +						  port);
> +	void *rx_buf;
> +	unsigned int count;
> +
> +	/* stop reading */
> +	if (dport != &dashtty_ports[CONSOLE_CHANNEL])
> +		if (atomic_dec_and_test(&num_channels_need_poll))
> +			del_timer_sync(&poll_timer);
> +
> +	mutex_lock(&dport->xmit_lock);
> +	count = dport->xmit_cnt;
> +	mutex_unlock(&dport->xmit_lock);
> +	if (count) {
> +		/*
> +		 * There's still data to write out, so wake and wait for the
> +		 * writer thread to drain the buffer.
> +		 */
> +		del_timer(&put_timer);
> +		wake_up_interruptible(&dashtty_waitqueue);
> +		wait_for_completion(&dport->xmit_empty);
> +	}
> +
> +	/* Null the read buffer (timer could still be running!) */
> +	spin_lock_bh(&dport->rx_lock);
> +	rx_buf = dport->rx_buf;
> +	dport->rx_buf = NULL;
> +	spin_unlock_bh(&dport->rx_lock);
> +	/* Free the read buffer */
> +	kfree(rx_buf);
> +
> +	/* Free the write buffer */
> +	tty_port_free_xmit_buf(port);
> +}
> +
> +static const struct tty_port_operations dashtty_port_ops = {
> +	.activate	= dashtty_port_activate,
> +	.shutdown	= dashtty_port_shutdown,
> +};
> +
> +static int dashtty_install(struct tty_driver *driver, struct tty_struct *tty)
> +{
> +	return tty_port_install(&dashtty_ports[tty->index].port, driver, tty);
> +}
> +
> +static int dashtty_open(struct tty_struct *tty, struct file *filp)
> +{
> +	return tty_port_open(tty->port, tty, filp);
> +}
> +
> +static void dashtty_close(struct tty_struct *tty, struct file *filp)
> +{
> +	return tty_port_close(tty->port, tty, filp);
> +}
> +
> +static void dashtty_hangup(struct tty_struct *tty)
> +{
> +	int channel;
> +	struct dashtty_port *dport;
> +
> +	channel = tty->index;
> +	dport = &dashtty_ports[channel];
> +
> +	/* drop any data in the xmit buffer */
> +	mutex_lock(&dport->xmit_lock);
> +	if (dport->xmit_cnt) {
> +		atomic_sub(dport->xmit_cnt, &dashtty_xmit_cnt);
> +		dport->xmit_cnt = 0;
> +		dport->xmit_head = 0;
> +		dport->xmit_tail = 0;
> +		complete(&dport->xmit_empty);
> +	}
> +	mutex_unlock(&dport->xmit_lock);
> +
> +	tty_port_hangup(tty->port);
> +}
> +
> +/**
> + * dashtty_put_timer() - Delayed wake up of kernel thread.
> + * @ignored:	unused
> + *
> + * This timer function wakes up the kernel thread if any data exists in the
> + * buffers. It is used to delay the expensive writeout until the writer has
> + * stopped writing.
> + */
> +static void dashtty_put_timer(unsigned long ignored)
> +{
> +	if (atomic_read(&dashtty_xmit_cnt))
> +		wake_up_interruptible(&dashtty_waitqueue);
> +}
> +
> +static int dashtty_write(struct tty_struct *tty, const unsigned char *buf,
> +			 int total)
> +{
> +	int channel, count, block;
> +	struct dashtty_port *dport;
> +
> +	/* Determine the channel */
> +	channel = tty->index;
> +	dport = &dashtty_ports[channel];
> +
> +	/*
> +	 * Write to output buffer.
> +	 *
> +	 * The reason that we asynchronously write the buffer is because if we
> +	 * were to write the buffer synchronously then because DA channels are
> +	 * per-CPU the buffer would be written to the channel of whatever CPU
> +	 * we're running on.
> +	 *
> +	 * What we actually want to happen is have all input and output done on
> +	 * one CPU.
> +	 */
> +	mutex_lock(&dport->xmit_lock);
> +	/* work out how many bytes we can write to the xmit buffer */
> +	total = min(total, (int)(SERIAL_XMIT_SIZE - dport->xmit_cnt));
> +	atomic_add(total, &dashtty_xmit_cnt);
> +	dport->xmit_cnt += total;
> +	/* write the actual bytes (may need splitting if it wraps) */
> +	for (count = total; count; count -= block) {
> +		block = min(count, (int)(SERIAL_XMIT_SIZE - dport->xmit_head));
> +		memcpy(dport->port.xmit_buf + dport->xmit_head, buf, block);
> +		dport->xmit_head += block;
> +		if (dport->xmit_head >= SERIAL_XMIT_SIZE)
> +			dport->xmit_head -= SERIAL_XMIT_SIZE;
> +		buf += block;
> +	}
> +	count = dport->xmit_cnt;
> +	/* xmit buffer no longer empty? */
> +	if (count)
> +		INIT_COMPLETION(dport->xmit_empty);
> +	mutex_unlock(&dport->xmit_lock);
> +
> +	if (total) {
> +		/*
> +		 * If the buffer is full, wake up the kthread, otherwise allow
> +		 * some more time for the buffer to fill up a bit before waking
> +		 * it.
> +		 */
> +		if (count == SERIAL_XMIT_SIZE) {
> +			del_timer(&put_timer);
> +			wake_up_interruptible(&dashtty_waitqueue);
> +		} else {
> +			mod_timer(&put_timer, jiffies + DA_TTY_PUT_DELAY);
> +		}
> +	}
> +	return total;
> +}
> +
> +static int dashtty_write_room(struct tty_struct *tty)
> +{
> +	struct dashtty_port *dport;
> +	int channel;
> +	int room;
> +
> +	channel = tty->index;
> +	dport = &dashtty_ports[channel];
> +
> +	/* report the space in the xmit buffer */
> +	mutex_lock(&dport->xmit_lock);
> +	room = SERIAL_XMIT_SIZE - dport->xmit_cnt;
> +	mutex_unlock(&dport->xmit_lock);
> +
> +	return room;
> +}
> +
> +static int dashtty_chars_in_buffer(struct tty_struct *tty)
> +{
> +	struct dashtty_port *dport;
> +	int channel;
> +	int chars;
> +
> +	channel = tty->index;
> +	dport = &dashtty_ports[channel];
> +
> +	/* report the number of bytes in the xmit buffer */
> +	mutex_lock(&dport->xmit_lock);
> +	chars = dport->xmit_cnt;
> +	mutex_unlock(&dport->xmit_lock);
> +
> +	return chars;
> +}
> +
> +static const struct tty_operations dashtty_ops = {
> +	.install		= dashtty_install,
> +	.open			= dashtty_open,
> +	.close			= dashtty_close,
> +	.hangup			= dashtty_hangup,
> +	.write			= dashtty_write,
> +	.write_room		= dashtty_write_room,
> +	.chars_in_buffer	= dashtty_chars_in_buffer,
> +};
> +
> +static int __init dashtty_init(void)
> +{
> +	int ret;
> +	int nport;
> +	struct dashtty_port *dport;
> +
> +	if (!metag_da_enabled())
> +		return -ENODEV;
> +
> +	channel_driver = tty_alloc_driver(NUM_TTY_CHANNELS,
> +					  TTY_DRIVER_REAL_RAW);
> +	if (IS_ERR(channel_driver))
> +		return PTR_ERR(channel_driver);
> +
> +	channel_driver->driver_name = "metag_da";
> +	channel_driver->name = "ttyDA";
> +	channel_driver->major = DA_TTY_MAJOR;
> +	channel_driver->minor_start = 0;
> +	channel_driver->type = TTY_DRIVER_TYPE_SERIAL;
> +	channel_driver->subtype = SERIAL_TYPE_NORMAL;
> +	channel_driver->init_termios = tty_std_termios;
> +	channel_driver->init_termios.c_cflag |= CLOCAL;
> +
> +	tty_set_operations(channel_driver, &dashtty_ops);
> +	for (nport = 0; nport < NUM_TTY_CHANNELS; nport++) {
> +		dport = &dashtty_ports[nport];
> +		tty_port_init(&dport->port);
> +		dport->port.ops = &dashtty_port_ops;
> +		spin_lock_init(&dport->rx_lock);
> +		mutex_init(&dport->xmit_lock);
> +		/* the xmit buffer starts empty, i.e. completely written */
> +		init_completion(&dport->xmit_empty);
> +		complete(&dport->xmit_empty);
> +	}
> +
> +	setup_timer(&put_timer, dashtty_put_timer, 0);
> +
> +	init_waitqueue_head(&dashtty_waitqueue);
> +	dashtty_thread = kthread_create(put_data, NULL, "ttyDA");
> +	if (IS_ERR(dashtty_thread)) {
> +		pr_err("Couldn't create dashtty thread\n");
> +		ret = PTR_ERR(dashtty_thread);
> +		goto err_destroy_ports;
> +	}
> +	/*
> +	 * Bind the writer thread to the boot CPU so it can't migrate.
> +	 * DA channels are per-CPU and we want all channel I/O to be on a single
> +	 * predictable CPU.
> +	 */
> +	kthread_bind(dashtty_thread, 0);
> +	wake_up_process(dashtty_thread);
> +
> +	ret = tty_register_driver(channel_driver);
> +
> +	if (ret < 0) {
> +		pr_err("Couldn't install dashtty driver: err %d\n",
> +		       ret);
> +		goto err_stop_kthread;
> +	}
> +
> +	return 0;
> +
> +err_stop_kthread:
> +	kthread_stop(dashtty_thread);
> +err_destroy_ports:
> +	for (nport = 0; nport < NUM_TTY_CHANNELS; nport++) {
> +		dport = &dashtty_ports[nport];
> +		tty_port_destroy(&dport->port);
> +	}
> +	put_tty_driver(channel_driver);
> +	return ret;
> +}
> +
> +static void dashtty_exit(void)
> +{
> +	int nport;
> +	struct dashtty_port *dport;
> +
> +	del_timer_sync(&put_timer);
> +	kthread_stop(dashtty_thread);
> +	del_timer_sync(&poll_timer);
> +	tty_unregister_driver(channel_driver);
> +	for (nport = 0; nport < NUM_TTY_CHANNELS; nport++) {
> +		dport = &dashtty_ports[nport];
> +		tty_port_destroy(&dport->port);
> +	}
> +	put_tty_driver(channel_driver);
> +}
> +
> +module_init(dashtty_init);
> +module_exit(dashtty_exit);
> +
> +#ifdef CONFIG_DA_CONSOLE
> +
> +static void dash_console_write(struct console *co, const char *s,
> +			       unsigned int count)
> +{
> +	int actually_written;
> +
> +	chancall(WRBUF, CONSOLE_CHANNEL, count, (void *)s, &actually_written);
> +}
> +
> +static struct tty_driver *dash_console_device(struct console *c, int *index)
> +{
> +	*index = c->index;
> +	return channel_driver;
> +}
> +
> +struct console dash_console = {
> +	.name = "ttyDA",
> +	.write = dash_console_write,
> +	.device = dash_console_device,
> +	.flags = CON_PRINTBUFFER,
> +	.index = 1,
> +};
> +
> +#endif
> 


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

WARNING: multiple messages have this Message-ID (diff)
From: James Hogan <james.hogan@imgtec.com>
To: linux-kernel@vger.kernel.org,
	Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	Jiri Slaby <jslaby@suse.cz>
Cc: James Hogan <james.hogan@imgtec.com>,
	linux-arch@vger.kernel.org, Alan Cox <alan@lxorguk.ukuu.org.uk>,
	Andrew Morton <akpm@linux-foundation.org>,
	Mauro Carvalho Chehab <mchehab@redhat.com>,
	Cesar Eduardo Barros <cesarb@cesarb.net>,
	Joe Perches <joe@perches.com>,
	"David S. Miller" <davem@davemloft.net>
Subject: Re: [PATCH v3 43/44] tty/metag_da: Add metag DA TTY driver
Date: Fri, 25 Jan 2013 11:36:53 +0000	[thread overview]
Message-ID: <51026E55.80905@imgtec.com> (raw)
In-Reply-To: <50F3E3FF.4080803@imgtec.com>

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

Hi,

On 14/01/13 10:54, James Hogan wrote:
> Here's a new version addressing Jiri's feedback.
> 
> Add a TTY driver for communicating over a Meta DA (Debug Adapter)
> channel using the bios channel SWITCH operation.
> 
> Signed-off-by: James Hogan <james.hogan@imgtec.com>
> Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> Cc: Jiri Slaby <jslaby@suse.cz>
> Cc: Andrew Morton <akpm@linux-foundation.org>
> Cc: Mauro Carvalho Chehab <mchehab@redhat.com>
> Cc: Cesar Eduardo Barros <cesarb@cesarb.net>
> Cc: Joe Perches <joe@perches.com>
> Cc: "David S. Miller" <davem@davemloft.net>

If this looks okay, I'd appreciate if somebody could Ack it.

I have some further patches ready to simplify the code around
tty_prepare_flip_string and tty_flip_buffer_push to avoid taking a tty
kref, after Jiri's changes in that area in linux-next. I guess these can
be applied once both sets of changes are merged.

Thanks
James

> ---
>  MAINTAINERS                            |    1 +
>  arch/metag/Kconfig                     |    4 +-
>  arch/metag/configs/meta1_defconfig     |    2 +
>  arch/metag/configs/meta2_defconfig     |    2 +
>  arch/metag/configs/meta2_smp_defconfig |    2 +
>  arch/metag/kernel/setup.c              |   12 +
>  drivers/tty/Kconfig                    |   13 +
>  drivers/tty/Makefile                   |    1 +
>  drivers/tty/metag_da.c                 |  679 ++++++++++++++++++++++++++++++++
>  9 files changed, 715 insertions(+), 1 deletions(-)
>  create mode 100644 drivers/tty/metag_da.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index dbc2bd6..63ccab6 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -5002,6 +5002,7 @@ S:	Supported
>  F:	arch/metag/
>  F:	Documentation/metag/
>  F:	Documentation/devicetree/bindings/metag/
> +F:	drivers/tty/metag_da.c
>  
>  MICROBLAZE ARCHITECTURE
>  M:	Michal Simek <monstr@monstr.eu>
> diff --git a/arch/metag/Kconfig b/arch/metag/Kconfig
> index 9bd0bc1..f4ef3bb 100644
> --- a/arch/metag/Kconfig
> +++ b/arch/metag/Kconfig
> @@ -221,7 +221,9 @@ config METAG_DA
>  	  of the DA will be detected automatically at boot, so it is safe to say
>  	  Y to this option even when booting without a DA.
>  
> -	  This enables support for services provided by DA JTAG debug adapters.
> +	  This enables support for services provided by DA JTAG debug adapters,
> +	  such as:
> +	  - communication over DA channels (such as the console driver).
>  
>  menu "Boot options"
>  
> diff --git a/arch/metag/configs/meta1_defconfig b/arch/metag/configs/meta1_defconfig
> index 0773425..ad663ca 100644
> --- a/arch/metag/configs/meta1_defconfig
> +++ b/arch/metag/configs/meta1_defconfig
> @@ -28,6 +28,8 @@ CONFIG_BLK_DEV_RAM_SIZE=16384
>  # CONFIG_SERIO is not set
>  # CONFIG_VT is not set
>  # CONFIG_LEGACY_PTYS is not set
> +CONFIG_DA_TTY=y
> +CONFIG_DA_CONSOLE=y
>  # CONFIG_DEVKMEM is not set
>  # CONFIG_HW_RANDOM is not set
>  # CONFIG_HWMON is not set
> diff --git a/arch/metag/configs/meta2_defconfig b/arch/metag/configs/meta2_defconfig
> index 001f9bf..47922e93 100644
> --- a/arch/metag/configs/meta2_defconfig
> +++ b/arch/metag/configs/meta2_defconfig
> @@ -29,6 +29,8 @@ CONFIG_BLK_DEV_RAM_SIZE=16384
>  # CONFIG_SERIO is not set
>  # CONFIG_VT is not set
>  # CONFIG_LEGACY_PTYS is not set
> +CONFIG_DA_TTY=y
> +CONFIG_DA_CONSOLE=y
>  # CONFIG_DEVKMEM is not set
>  # CONFIG_HW_RANDOM is not set
>  # CONFIG_HWMON is not set
> diff --git a/arch/metag/configs/meta2_smp_defconfig b/arch/metag/configs/meta2_smp_defconfig
> index d6f7e46..f508250 100644
> --- a/arch/metag/configs/meta2_smp_defconfig
> +++ b/arch/metag/configs/meta2_smp_defconfig
> @@ -30,6 +30,8 @@ CONFIG_BLK_DEV_RAM_SIZE=16384
>  # CONFIG_SERIO is not set
>  # CONFIG_VT is not set
>  # CONFIG_LEGACY_PTYS is not set
> +CONFIG_DA_TTY=y
> +CONFIG_DA_CONSOLE=y
>  # CONFIG_DEVKMEM is not set
>  # CONFIG_HW_RANDOM is not set
>  # CONFIG_HWMON is not set
> diff --git a/arch/metag/kernel/setup.c b/arch/metag/kernel/setup.c
> index 1410db0..0d46334 100644
> --- a/arch/metag/kernel/setup.c
> +++ b/arch/metag/kernel/setup.c
> @@ -61,6 +61,11 @@ extern char _heap_start[];
>  extern u32 __dtb_start[];
>  #endif
>  
> +#ifdef CONFIG_DA_CONSOLE
> +/* Our early channel based console driver */
> +extern struct console dash_console;
> +#endif
> +
>  struct machine_desc *machine_desc __initdata;
>  
>  /*
> @@ -182,6 +187,13 @@ void __init setup_arch(char **cmdline_p)
>  	metag_cache_probe();
>  
>  	metag_da_probe();
> +#ifdef CONFIG_DA_CONSOLE
> +	if (metag_da_enabled()) {
> +		/* An early channel based console driver */
> +		register_console(&dash_console);
> +		add_preferred_console("ttyDA", 1, NULL);
> +	}
> +#endif
>  
>  	/* try interpreting the argument as a device tree */
>  	machine_desc = setup_machine_fdt(original_cmd_line);
> diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig
> index 0ecf22b..5eef53d 100644
> --- a/drivers/tty/Kconfig
> +++ b/drivers/tty/Kconfig
> @@ -388,3 +388,16 @@ config PPC_EARLY_DEBUG_EHV_BC_HANDLE
>  	  If the number you specify is not a valid byte channel handle, then
>  	  there simply will be no early console output.  This is true also
>  	  if you don't boot under a hypervisor at all.
> +
> +config DA_TTY
> +	bool "DA TTY"
> +	depends on METAG_DA
> +	select SERIAL_NONSTANDARD
> +	help
> +	  This enables a TTY on a Dash channel.
> +
> +config DA_CONSOLE
> +	bool "DA Console"
> +	depends on DA_TTY
> +	help
> +	  This enables a console on a Dash channel.
> diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile
> index 2953059..dbd7745 100644
> --- a/drivers/tty/Makefile
> +++ b/drivers/tty/Makefile
> @@ -27,5 +27,6 @@ obj-$(CONFIG_SYNCLINK_GT)	+= synclink_gt.o
>  obj-$(CONFIG_SYNCLINKMP)	+= synclinkmp.o
>  obj-$(CONFIG_SYNCLINK)		+= synclink.o
>  obj-$(CONFIG_PPC_EPAPR_HV_BYTECHAN) += ehv_bytechan.o
> +obj-$(CONFIG_DA_TTY)	+= metag_da.o
>  
>  obj-y += ipwireless/
> diff --git a/drivers/tty/metag_da.c b/drivers/tty/metag_da.c
> new file mode 100644
> index 0000000..6a0b2e6
> --- /dev/null
> +++ b/drivers/tty/metag_da.c
> @@ -0,0 +1,679 @@
> +/*
> + *  dashtty.c - tty driver for Dash channels interface.
> + *
> + *  Copyright (C) 2007,2008,2012 Imagination Technologies
> + *
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License.  See the file COPYING in the main directory of this archive
> + * for more details.
> + *
> + */
> +
> +#include <linux/atomic.h>
> +#include <linux/completion.h>
> +#include <linux/console.h>
> +#include <linux/delay.h>
> +#include <linux/export.h>
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/kthread.h>
> +#include <linux/mutex.h>
> +#include <linux/sched.h>
> +#include <linux/serial.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/string.h>
> +#include <linux/timer.h>
> +#include <linux/tty.h>
> +#include <linux/tty_driver.h>
> +#include <linux/tty_flip.h>
> +#include <linux/uaccess.h>
> +
> +#include <asm/da.h>
> +
> +/* Channel error codes */
> +#define CONAOK	0
> +#define CONERR	1
> +#define CONBAD	2
> +#define CONPRM	3
> +#define CONADR	4
> +#define CONCNT	5
> +#define CONCBF	6
> +#define CONCBE	7
> +#define CONBSY	8
> +
> +/* Default channel for the console */
> +#define CONSOLE_CHANNEL      1
> +
> +#define NUM_TTY_CHANNELS     6
> +
> +/* Auto allocate */
> +#define DA_TTY_MAJOR        0
> +
> +/* A speedy poll rate helps the userland debug process connection response.
> + * But, if you set it too high then no other userland processes get much
> + * of a look in.
> + */
> +#define DA_TTY_POLL (HZ / 50)
> +
> +/*
> + * A short put delay improves latency but has a high throughput overhead
> + */
> +#define DA_TTY_PUT_DELAY (HZ / 100)
> +
> +static atomic_t num_channels_need_poll = ATOMIC_INIT(0);
> +
> +static struct timer_list poll_timer;
> +
> +static struct tty_driver *channel_driver;
> +
> +static struct timer_list put_timer;
> +static struct task_struct *dashtty_thread;
> +
> +#define RX_BUF_SIZE 1024
> +
> +enum {
> +	INCHR = 1,
> +	OUTCHR,
> +	RDBUF,
> +	WRBUF,
> +	RDSTAT
> +};
> +
> +/**
> + * struct dashtty_port - Wrapper struct for dashtty tty_port.
> + * @port:		TTY port data
> + * @rx_lock:		Lock for rx_buf.
> + *			This protects between the poll timer and user context.
> + *			It's also held during read SWITCH operations.
> + * @rx_buf:		Read buffer
> + * @xmit_lock:		Lock for xmit_*, and port.xmit_buf.
> + *			This protects between user context and kernel thread.
> + *			It's also held during write SWITCH operations.
> + * @xmit_cnt:		Size of xmit buffer contents
> + * @xmit_head:		Head of xmit buffer where data is written
> + * @xmit_tail:		Tail of xmit buffer where data is read
> + * @xmit_empty:		Completion for xmit buffer being empty
> + */
> +struct dashtty_port {
> +	struct tty_port		 port;
> +	spinlock_t		 rx_lock;
> +	void			*rx_buf;
> +	struct mutex		 xmit_lock;
> +	unsigned int		 xmit_cnt;
> +	unsigned int		 xmit_head;
> +	unsigned int		 xmit_tail;
> +	struct completion	 xmit_empty;
> +};
> +
> +static struct dashtty_port dashtty_ports[NUM_TTY_CHANNELS];
> +
> +static atomic_t dashtty_xmit_cnt = ATOMIC_INIT(0);
> +static wait_queue_head_t dashtty_waitqueue;
> +
> +/*
> + * Low-level DA channel access routines
> + */
> +static int chancall(int in_bios_function, int in_channel,
> +		    int in_arg2, void *in_arg3,
> +		    void *in_arg4)
> +{
> +	register int   bios_function asm("D1Ar1") = in_bios_function;
> +	register int   channel       asm("D0Ar2") = in_channel;
> +	register int   arg2          asm("D1Ar3") = in_arg2;
> +	register void *arg3          asm("D0Ar4") = in_arg3;
> +	register void *arg4          asm("D1Ar5") = in_arg4;
> +	register int   bios_call     asm("D0Ar6") = 3;
> +	register int   result        asm("D0Re0");
> +
> +	asm volatile (
> +		"MSETL	[A0StP++], %6,%4,%2\n\t"
> +		"ADD	A0StP, A0StP, #8\n\t"
> +		"SWITCH	#0x0C30208\n\t"
> +		"GETD	%0, [A0StP+#-8]\n\t"
> +		"SUB	A0StP, A0StP, #(4*6)+8\n\t"
> +		: "=d" (result)   /* outs */
> +		: "d" (bios_function),
> +		  "d" (channel),
> +		  "d" (arg2),
> +		  "d" (arg3),
> +		  "d" (arg4),
> +		  "d" (bios_call) /* ins */
> +		: "memory");
> +
> +	return result;
> +}
> +
> +/*
> + * Attempts to fetch count bytes from channel and returns actual count.
> + */
> +static int fetch_data(struct tty_struct *tty)
> +{
> +	unsigned int channel = tty->index;
> +	struct dashtty_port *dport = &dashtty_ports[channel];
> +	int received = 0;
> +
> +	spin_lock_bh(&dport->rx_lock);
> +	/* check the port isn't being shut down */
> +	if (!dport->rx_buf)
> +		goto unlock;
> +	if (chancall(RDBUF, channel, RX_BUF_SIZE,
> +		     (void *)dport->rx_buf, &received) == CONAOK) {
> +		if (received) {
> +			int space;
> +			unsigned char *cbuf;
> +
> +			space = tty_prepare_flip_string(tty, &cbuf, received);
> +
> +			if (space <= 0)
> +				goto unlock;
> +
> +			memcpy(cbuf, dport->rx_buf, space);
> +			tty_flip_buffer_push(tty);
> +		}
> +	}
> +unlock:
> +	spin_unlock_bh(&dport->rx_lock);
> +
> +	return received;
> +}
> +
> +/**
> + * find_channel_to_poll() - Returns kref to the next channel tty to poll.
> + * Returns:	The TTY of the next channel to poll, or NULL if no TTY needs
> + *		polling. Release with tty_kref_put().
> + */
> +static struct tty_struct *find_channel_to_poll(void)
> +{
> +	static int last_polled_channel;
> +	int last = last_polled_channel;
> +	int chan;
> +	struct tty_struct *tty = NULL;
> +
> +	for (chan = last + 1; ; ++chan) {
> +		if (chan >= NUM_TTY_CHANNELS)
> +			chan = 0;
> +
> +		tty = tty_port_tty_get(&dashtty_ports[chan].port);
> +		if (tty) {
> +			last_polled_channel = chan;
> +			return tty;
> +		}
> +
> +		if (chan == last)
> +			break;
> +	}
> +	return tty;
> +}
> +
> +/**
> + * put_channel_data() - Write out a block of channel data.
> + * @chan:	DA channel number.
> + *
> + * Write a single block of data out to the debug adapter. If the circular buffer
> + * is wrapped then only the first block is written.
> + *
> + * Returns:	1 if the remote buffer was too full to accept data.
> + *		0 otherwise.
> + */
> +static int put_channel_data(unsigned int chan)
> +{
> +	struct dashtty_port *dport;
> +	struct tty_struct *tty;
> +	int number_written;
> +	unsigned int count = 0;
> +
> +	dport = &dashtty_ports[chan];
> +	mutex_lock(&dport->xmit_lock);
> +	if (dport->xmit_cnt) {
> +		count = min((unsigned int)(SERIAL_XMIT_SIZE - dport->xmit_tail),
> +			    dport->xmit_cnt);
> +		chancall(WRBUF, chan, count,
> +			 dport->port.xmit_buf + dport->xmit_tail,
> +			 &number_written);
> +		dport->xmit_cnt -= number_written;
> +		if (!dport->xmit_cnt) {
> +			/* reset pointers to avoid wraps */
> +			dport->xmit_head = 0;
> +			dport->xmit_tail = 0;
> +			complete(&dport->xmit_empty);
> +		} else {
> +			dport->xmit_tail += number_written;
> +			if (dport->xmit_tail >= SERIAL_XMIT_SIZE)
> +				dport->xmit_tail -= SERIAL_XMIT_SIZE;
> +		}
> +		atomic_sub(number_written, &dashtty_xmit_cnt);
> +	}
> +	mutex_unlock(&dport->xmit_lock);
> +
> +	/* if we've made more data available, wake up tty */
> +	if (count && number_written) {
> +		tty = tty_port_tty_get(&dport->port);
> +		if (tty) {
> +			tty_wakeup(tty);
> +			tty_kref_put(tty);
> +		}
> +	}
> +
> +	/* did the write fail? */
> +	return count && !number_written;
> +}
> +
> +/**
> + * put_data() - Kernel thread to write out blocks of channel data to DA.
> + * @arg:	Unused.
> + *
> + * This kernel thread runs while @dashtty_xmit_cnt != 0, and loops over the
> + * channels to write out any buffered data. If any of the channels stall due to
> + * the remote buffer being full, a hold off happens to allow the debugger to
> + * drain the buffer.
> + */
> +static int put_data(void *arg)
> +{
> +	unsigned int chan, stall;
> +
> +	__set_current_state(TASK_RUNNING);
> +	while (!kthread_should_stop()) {
> +		/*
> +		 * For each channel see if there's anything to transmit in the
> +		 * port's xmit_buf.
> +		 */
> +		stall = 0;
> +		for (chan = 0; chan < NUM_TTY_CHANNELS; ++chan)
> +			stall += put_channel_data(chan);
> +
> +		/*
> +		 * If some of the buffers are full, hold off for a short while
> +		 * to allow them to empty.
> +		 */
> +		if (stall)
> +			msleep(25);
> +
> +		wait_event_interruptible(dashtty_waitqueue,
> +					 atomic_read(&dashtty_xmit_cnt));
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + *	This gets called every DA_TTY_POLL and polls the channels for data
> + */
> +static void dashtty_timer(unsigned long ignored)
> +{
> +	struct tty_struct *tty;
> +
> +	/* If there are no ports open do nothing and don't poll again. */
> +	if (!atomic_read(&num_channels_need_poll))
> +		return;
> +
> +	tty = find_channel_to_poll();
> +
> +	/* Did we find a channel to poll? */
> +	if (tty) {
> +		fetch_data(tty);
> +		tty_kref_put(tty);
> +	}
> +
> +	mod_timer_pinned(&poll_timer, jiffies + DA_TTY_POLL);
> +}
> +
> +static void add_poll_timer(struct timer_list *poll_timer)
> +{
> +	setup_timer(poll_timer, dashtty_timer, 0);
> +	poll_timer->expires = jiffies + DA_TTY_POLL;
> +
> +	/*
> +	 * Always attach the timer to the boot CPU. The DA channels are per-CPU
> +	 * so all polling should be from a single CPU.
> +	 */
> +	add_timer_on(poll_timer, 0);
> +}
> +
> +static int dashtty_port_activate(struct tty_port *port, struct tty_struct *tty)
> +{
> +	struct dashtty_port *dport = container_of(port, struct dashtty_port,
> +						  port);
> +	void *rx_buf;
> +
> +	/* Allocate the buffer we use for writing data */
> +	if (tty_port_alloc_xmit_buf(port) < 0)
> +		goto err;
> +
> +	/* Allocate the buffer we use for reading data */
> +	rx_buf = kzalloc(RX_BUF_SIZE, GFP_KERNEL);
> +	if (!rx_buf)
> +		goto err_free_xmit;
> +
> +	spin_lock_bh(&dport->rx_lock);
> +	dport->rx_buf = rx_buf;
> +	spin_unlock_bh(&dport->rx_lock);
> +
> +	/*
> +	 * Don't add the poll timer if we're opening a console. This
> +	 * avoids the overhead of polling the Dash but means it is not
> +	 * possible to have a login on /dev/console.
> +	 *
> +	 */
> +	if (dport != &dashtty_ports[CONSOLE_CHANNEL])
> +		if (atomic_inc_return(&num_channels_need_poll) == 1)
> +			add_poll_timer(&poll_timer);
> +
> +	return 0;
> +err_free_xmit:
> +	tty_port_free_xmit_buf(port);
> +err:
> +	return -ENOMEM;
> +}
> +
> +static void dashtty_port_shutdown(struct tty_port *port)
> +{
> +	struct dashtty_port *dport = container_of(port, struct dashtty_port,
> +						  port);
> +	void *rx_buf;
> +	unsigned int count;
> +
> +	/* stop reading */
> +	if (dport != &dashtty_ports[CONSOLE_CHANNEL])
> +		if (atomic_dec_and_test(&num_channels_need_poll))
> +			del_timer_sync(&poll_timer);
> +
> +	mutex_lock(&dport->xmit_lock);
> +	count = dport->xmit_cnt;
> +	mutex_unlock(&dport->xmit_lock);
> +	if (count) {
> +		/*
> +		 * There's still data to write out, so wake and wait for the
> +		 * writer thread to drain the buffer.
> +		 */
> +		del_timer(&put_timer);
> +		wake_up_interruptible(&dashtty_waitqueue);
> +		wait_for_completion(&dport->xmit_empty);
> +	}
> +
> +	/* Null the read buffer (timer could still be running!) */
> +	spin_lock_bh(&dport->rx_lock);
> +	rx_buf = dport->rx_buf;
> +	dport->rx_buf = NULL;
> +	spin_unlock_bh(&dport->rx_lock);
> +	/* Free the read buffer */
> +	kfree(rx_buf);
> +
> +	/* Free the write buffer */
> +	tty_port_free_xmit_buf(port);
> +}
> +
> +static const struct tty_port_operations dashtty_port_ops = {
> +	.activate	= dashtty_port_activate,
> +	.shutdown	= dashtty_port_shutdown,
> +};
> +
> +static int dashtty_install(struct tty_driver *driver, struct tty_struct *tty)
> +{
> +	return tty_port_install(&dashtty_ports[tty->index].port, driver, tty);
> +}
> +
> +static int dashtty_open(struct tty_struct *tty, struct file *filp)
> +{
> +	return tty_port_open(tty->port, tty, filp);
> +}
> +
> +static void dashtty_close(struct tty_struct *tty, struct file *filp)
> +{
> +	return tty_port_close(tty->port, tty, filp);
> +}
> +
> +static void dashtty_hangup(struct tty_struct *tty)
> +{
> +	int channel;
> +	struct dashtty_port *dport;
> +
> +	channel = tty->index;
> +	dport = &dashtty_ports[channel];
> +
> +	/* drop any data in the xmit buffer */
> +	mutex_lock(&dport->xmit_lock);
> +	if (dport->xmit_cnt) {
> +		atomic_sub(dport->xmit_cnt, &dashtty_xmit_cnt);
> +		dport->xmit_cnt = 0;
> +		dport->xmit_head = 0;
> +		dport->xmit_tail = 0;
> +		complete(&dport->xmit_empty);
> +	}
> +	mutex_unlock(&dport->xmit_lock);
> +
> +	tty_port_hangup(tty->port);
> +}
> +
> +/**
> + * dashtty_put_timer() - Delayed wake up of kernel thread.
> + * @ignored:	unused
> + *
> + * This timer function wakes up the kernel thread if any data exists in the
> + * buffers. It is used to delay the expensive writeout until the writer has
> + * stopped writing.
> + */
> +static void dashtty_put_timer(unsigned long ignored)
> +{
> +	if (atomic_read(&dashtty_xmit_cnt))
> +		wake_up_interruptible(&dashtty_waitqueue);
> +}
> +
> +static int dashtty_write(struct tty_struct *tty, const unsigned char *buf,
> +			 int total)
> +{
> +	int channel, count, block;
> +	struct dashtty_port *dport;
> +
> +	/* Determine the channel */
> +	channel = tty->index;
> +	dport = &dashtty_ports[channel];
> +
> +	/*
> +	 * Write to output buffer.
> +	 *
> +	 * The reason that we asynchronously write the buffer is because if we
> +	 * were to write the buffer synchronously then because DA channels are
> +	 * per-CPU the buffer would be written to the channel of whatever CPU
> +	 * we're running on.
> +	 *
> +	 * What we actually want to happen is have all input and output done on
> +	 * one CPU.
> +	 */
> +	mutex_lock(&dport->xmit_lock);
> +	/* work out how many bytes we can write to the xmit buffer */
> +	total = min(total, (int)(SERIAL_XMIT_SIZE - dport->xmit_cnt));
> +	atomic_add(total, &dashtty_xmit_cnt);
> +	dport->xmit_cnt += total;
> +	/* write the actual bytes (may need splitting if it wraps) */
> +	for (count = total; count; count -= block) {
> +		block = min(count, (int)(SERIAL_XMIT_SIZE - dport->xmit_head));
> +		memcpy(dport->port.xmit_buf + dport->xmit_head, buf, block);
> +		dport->xmit_head += block;
> +		if (dport->xmit_head >= SERIAL_XMIT_SIZE)
> +			dport->xmit_head -= SERIAL_XMIT_SIZE;
> +		buf += block;
> +	}
> +	count = dport->xmit_cnt;
> +	/* xmit buffer no longer empty? */
> +	if (count)
> +		INIT_COMPLETION(dport->xmit_empty);
> +	mutex_unlock(&dport->xmit_lock);
> +
> +	if (total) {
> +		/*
> +		 * If the buffer is full, wake up the kthread, otherwise allow
> +		 * some more time for the buffer to fill up a bit before waking
> +		 * it.
> +		 */
> +		if (count == SERIAL_XMIT_SIZE) {
> +			del_timer(&put_timer);
> +			wake_up_interruptible(&dashtty_waitqueue);
> +		} else {
> +			mod_timer(&put_timer, jiffies + DA_TTY_PUT_DELAY);
> +		}
> +	}
> +	return total;
> +}
> +
> +static int dashtty_write_room(struct tty_struct *tty)
> +{
> +	struct dashtty_port *dport;
> +	int channel;
> +	int room;
> +
> +	channel = tty->index;
> +	dport = &dashtty_ports[channel];
> +
> +	/* report the space in the xmit buffer */
> +	mutex_lock(&dport->xmit_lock);
> +	room = SERIAL_XMIT_SIZE - dport->xmit_cnt;
> +	mutex_unlock(&dport->xmit_lock);
> +
> +	return room;
> +}
> +
> +static int dashtty_chars_in_buffer(struct tty_struct *tty)
> +{
> +	struct dashtty_port *dport;
> +	int channel;
> +	int chars;
> +
> +	channel = tty->index;
> +	dport = &dashtty_ports[channel];
> +
> +	/* report the number of bytes in the xmit buffer */
> +	mutex_lock(&dport->xmit_lock);
> +	chars = dport->xmit_cnt;
> +	mutex_unlock(&dport->xmit_lock);
> +
> +	return chars;
> +}
> +
> +static const struct tty_operations dashtty_ops = {
> +	.install		= dashtty_install,
> +	.open			= dashtty_open,
> +	.close			= dashtty_close,
> +	.hangup			= dashtty_hangup,
> +	.write			= dashtty_write,
> +	.write_room		= dashtty_write_room,
> +	.chars_in_buffer	= dashtty_chars_in_buffer,
> +};
> +
> +static int __init dashtty_init(void)
> +{
> +	int ret;
> +	int nport;
> +	struct dashtty_port *dport;
> +
> +	if (!metag_da_enabled())
> +		return -ENODEV;
> +
> +	channel_driver = tty_alloc_driver(NUM_TTY_CHANNELS,
> +					  TTY_DRIVER_REAL_RAW);
> +	if (IS_ERR(channel_driver))
> +		return PTR_ERR(channel_driver);
> +
> +	channel_driver->driver_name = "metag_da";
> +	channel_driver->name = "ttyDA";
> +	channel_driver->major = DA_TTY_MAJOR;
> +	channel_driver->minor_start = 0;
> +	channel_driver->type = TTY_DRIVER_TYPE_SERIAL;
> +	channel_driver->subtype = SERIAL_TYPE_NORMAL;
> +	channel_driver->init_termios = tty_std_termios;
> +	channel_driver->init_termios.c_cflag |= CLOCAL;
> +
> +	tty_set_operations(channel_driver, &dashtty_ops);
> +	for (nport = 0; nport < NUM_TTY_CHANNELS; nport++) {
> +		dport = &dashtty_ports[nport];
> +		tty_port_init(&dport->port);
> +		dport->port.ops = &dashtty_port_ops;
> +		spin_lock_init(&dport->rx_lock);
> +		mutex_init(&dport->xmit_lock);
> +		/* the xmit buffer starts empty, i.e. completely written */
> +		init_completion(&dport->xmit_empty);
> +		complete(&dport->xmit_empty);
> +	}
> +
> +	setup_timer(&put_timer, dashtty_put_timer, 0);
> +
> +	init_waitqueue_head(&dashtty_waitqueue);
> +	dashtty_thread = kthread_create(put_data, NULL, "ttyDA");
> +	if (IS_ERR(dashtty_thread)) {
> +		pr_err("Couldn't create dashtty thread\n");
> +		ret = PTR_ERR(dashtty_thread);
> +		goto err_destroy_ports;
> +	}
> +	/*
> +	 * Bind the writer thread to the boot CPU so it can't migrate.
> +	 * DA channels are per-CPU and we want all channel I/O to be on a single
> +	 * predictable CPU.
> +	 */
> +	kthread_bind(dashtty_thread, 0);
> +	wake_up_process(dashtty_thread);
> +
> +	ret = tty_register_driver(channel_driver);
> +
> +	if (ret < 0) {
> +		pr_err("Couldn't install dashtty driver: err %d\n",
> +		       ret);
> +		goto err_stop_kthread;
> +	}
> +
> +	return 0;
> +
> +err_stop_kthread:
> +	kthread_stop(dashtty_thread);
> +err_destroy_ports:
> +	for (nport = 0; nport < NUM_TTY_CHANNELS; nport++) {
> +		dport = &dashtty_ports[nport];
> +		tty_port_destroy(&dport->port);
> +	}
> +	put_tty_driver(channel_driver);
> +	return ret;
> +}
> +
> +static void dashtty_exit(void)
> +{
> +	int nport;
> +	struct dashtty_port *dport;
> +
> +	del_timer_sync(&put_timer);
> +	kthread_stop(dashtty_thread);
> +	del_timer_sync(&poll_timer);
> +	tty_unregister_driver(channel_driver);
> +	for (nport = 0; nport < NUM_TTY_CHANNELS; nport++) {
> +		dport = &dashtty_ports[nport];
> +		tty_port_destroy(&dport->port);
> +	}
> +	put_tty_driver(channel_driver);
> +}
> +
> +module_init(dashtty_init);
> +module_exit(dashtty_exit);
> +
> +#ifdef CONFIG_DA_CONSOLE
> +
> +static void dash_console_write(struct console *co, const char *s,
> +			       unsigned int count)
> +{
> +	int actually_written;
> +
> +	chancall(WRBUF, CONSOLE_CHANNEL, count, (void *)s, &actually_written);
> +}
> +
> +static struct tty_driver *dash_console_device(struct console *c, int *index)
> +{
> +	*index = c->index;
> +	return channel_driver;
> +}
> +
> +struct console dash_console = {
> +	.name = "ttyDA",
> +	.write = dash_console_write,
> +	.device = dash_console_device,
> +	.flags = CON_PRINTBUFFER,
> +	.index = 1,
> +};
> +
> +#endif
> 


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

  reply	other threads:[~2013-01-25 11:37 UTC|newest]

Thread overview: 162+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-01-10 15:30 [PATCH v3 00/44] Meta Linux Kernel Port James Hogan
2013-01-10 15:30 ` James Hogan
2013-01-10 15:30 ` [PATCH v3 01/44] asm-generic/io.h: check CONFIG_VIRT_TO_BUS James Hogan
2013-01-10 15:30   ` James Hogan
2013-01-10 15:30 ` [PATCH v3 02/44] asm-generic/unistd.h: handle symbol prefixes in cond_syscall James Hogan
2013-01-10 15:30   ` James Hogan
2013-01-10 15:30 ` [PATCH v3 03/44] Revert some of "binfmt_elf: cleanups" James Hogan
2013-01-10 15:30   ` James Hogan
2013-01-10 15:30 ` [PATCH v3 04/44] Add HAVE_64BIT_ALIGNED_ACCESS James Hogan
2013-01-10 15:30   ` James Hogan
2013-01-10 15:30 ` [PATCH v3 05/44] trace/ring_buffer: handle 64bit aligned structs James Hogan
2013-01-10 15:30   ` James Hogan
2013-01-10 15:30 ` [PATCH v3 06/44] metag: Add MAINTAINERS entry James Hogan
2013-01-10 15:30   ` James Hogan
2013-01-10 15:30 ` [PATCH v3 07/44] metag: Headers for core arch constants James Hogan
2013-01-10 15:30   ` James Hogan
2013-01-10 15:30 ` [PATCH v3 08/44] metag: Header for core memory mapped registers James Hogan
2013-01-10 15:30   ` James Hogan
2013-01-10 15:30 ` [PATCH v3 09/44] metag: Boot James Hogan
2013-01-10 15:30   ` James Hogan
2013-01-10 15:30 ` [PATCH v3 10/44] metag: TBX header James Hogan
2013-01-10 15:30   ` James Hogan
2013-01-10 15:30 ` [PATCH v3 11/44] metag: TBX source James Hogan
2013-01-10 15:30   ` James Hogan
2013-01-10 15:30 ` [PATCH v3 12/44] metag: Cache/TLB handling James Hogan
2013-01-10 15:30   ` James Hogan
2013-01-10 15:30 ` [PATCH v3 13/44] metag: Memory management James Hogan
2013-01-10 15:30   ` James Hogan
2013-01-10 15:30 ` [PATCH v3 14/44] metag: Memory handling James Hogan
2013-01-10 15:30   ` James Hogan
2013-01-10 15:30 ` [PATCH v3 15/44] metag: Huge TLB James Hogan
2013-01-10 15:30   ` James Hogan
2013-01-10 16:57   ` Dave Hansen
2013-01-11  9:58     ` James Hogan
2013-01-11  9:58       ` James Hogan
2013-01-10 15:30 ` [PATCH v3 16/44] metag: Highmem support James Hogan
2013-01-10 15:30   ` James Hogan
2013-01-10 15:30 ` [PATCH v3 17/44] metag: TCM support James Hogan
2013-01-10 15:30   ` James Hogan
2013-01-10 15:30 ` [PATCH v3 18/44] metag: Signal handling James Hogan
2013-01-10 15:30   ` James Hogan
2013-01-10 15:30 ` [PATCH v3 19/44] metag: Device tree James Hogan
2013-01-10 15:30   ` James Hogan
2013-02-06 13:06   ` Vineet Gupta
2013-02-06 13:06     ` Vineet Gupta
2013-02-06 13:47     ` James Hogan
2013-02-06 13:47       ` James Hogan
2013-01-10 15:30 ` [PATCH v3 20/44] metag: ptrace James Hogan
2013-01-10 15:30   ` James Hogan
2013-01-10 15:30 ` [PATCH v3 21/44] metag: Time keeping James Hogan
2013-01-10 15:30   ` James Hogan
2013-01-26  0:03   ` Arnd Bergmann
2013-01-28 12:59     ` James Hogan
2013-01-28 12:59       ` James Hogan
2013-01-10 15:30 ` [PATCH v3 22/44] metag: Traps James Hogan
2013-01-10 15:30   ` James Hogan
2013-01-10 15:30 ` [PATCH v3 23/44] metag: IRQ handling James Hogan
2013-01-10 15:30   ` James Hogan
2013-01-10 15:30 ` [PATCH v3 24/44] metag: Internal and external irqchips James Hogan
2013-01-10 15:30   ` James Hogan
2013-01-10 15:30   ` James Hogan
2013-01-10 15:30 ` [PATCH v3 25/44] metag: System Calls James Hogan
2013-01-10 15:30   ` James Hogan
2013-01-10 15:30 ` [PATCH v3 26/44] metag: Scheduling/Process management James Hogan
2013-01-10 15:30   ` James Hogan
2013-01-10 15:30 ` [PATCH v3 27/44] metag: Module support James Hogan
2013-01-10 15:30   ` James Hogan
2013-01-10 15:30 ` [PATCH v3 28/44] metag: Atomics, locks and bitops James Hogan
2013-01-10 15:30   ` James Hogan
2013-01-10 15:30 ` [PATCH v3 29/44] metag: Basic documentation James Hogan
2013-01-10 15:30   ` James Hogan
2013-01-10 15:30 ` [PATCH v3 30/44] metag: SMP support James Hogan
2013-01-10 15:30   ` James Hogan
2013-01-10 15:30 ` [PATCH v3 31/44] metag: DMA James Hogan
2013-01-10 15:30   ` James Hogan
2013-01-10 15:31 ` [PATCH v3 32/44] metag: Optimised library functions James Hogan
2013-01-10 15:31   ` James Hogan
2013-01-10 15:31 ` [PATCH v3 33/44] metag: Stack unwinding James Hogan
2013-01-10 15:31   ` James Hogan
2013-01-10 15:31 ` [PATCH v3 34/44] metag: Various other headers James Hogan
2013-01-10 15:31   ` James Hogan
2013-01-10 15:31 ` [PATCH v3 35/44] mm: define VM_GROWSUP for CONFIG_METAG James Hogan
2013-01-10 15:31   ` James Hogan
2013-01-10 15:31   ` James Hogan
2013-01-10 15:31 ` [PATCH v3 36/44] Kconfig.debug: add METAG to dependency lists James Hogan
2013-01-10 15:31   ` James Hogan
2013-01-10 15:31 ` [PATCH v3 37/44] metag: Build infrastructure James Hogan
2013-01-10 15:31   ` James Hogan
2013-01-10 15:31 ` [PATCH v3 38/44] metag: Perf James Hogan
2013-01-10 15:31   ` James Hogan
2013-01-10 15:31 ` [PATCH v3 39/44] metag: ftrace support James Hogan
2013-01-10 15:31   ` James Hogan
2013-01-10 15:59   ` Steven Rostedt
2013-01-10 16:00     ` Steven Rostedt
2013-01-10 16:13     ` James Hogan
2013-01-10 16:13       ` James Hogan
2013-01-10 15:31 ` [PATCH v3 40/44] scripts/checkstack.pl: Add metag support James Hogan
2013-01-10 15:31   ` James Hogan
2013-01-10 15:31 ` [PATCH v3 41/44] metag: OProfile James Hogan
2013-01-10 15:31   ` James Hogan
2013-01-10 17:12   ` Maynard Johnson
2013-01-11  9:43     ` James Hogan
2013-01-11  9:43       ` James Hogan
2013-01-10 15:31 ` [PATCH v3 42/44] metag: Add JTAG Debug Adapter (DA) support James Hogan
2013-01-10 15:31   ` James Hogan
2013-01-10 15:31 ` [PATCH v3 43/44] tty/metag_da: Add metag DA TTY driver James Hogan
2013-01-10 15:31   ` James Hogan
2013-01-10 16:03   ` Jiri Slaby
2013-01-11 10:50     ` James Hogan
2013-01-11 10:50       ` James Hogan
2013-01-14 10:15       ` Jiri Slaby
2013-01-14 10:54   ` James Hogan
2013-01-14 10:54     ` James Hogan
2013-01-25 11:36     ` James Hogan [this message]
2013-01-25 11:36       ` James Hogan
2013-01-30  5:14       ` Greg Kroah-Hartman
2013-01-30 10:43         ` James Hogan
2013-01-30 10:43           ` James Hogan
2013-01-10 15:31 ` [PATCH v3 44/44] fs: imgdafs: Add IMG DAFS filesystem for metag James Hogan
2013-01-10 15:31   ` James Hogan
2013-01-28 13:28   ` James Hogan
2013-01-28 13:28     ` James Hogan
2013-01-10 23:34 ` [PATCH v3 00/44] Meta Linux Kernel Port Stephen Rothwell
2013-01-10 23:34   ` Stephen Rothwell
2013-01-11  9:15   ` James Hogan
2013-01-11  9:15     ` James Hogan
2013-01-11 13:03     ` Stephen Rothwell
2013-01-11 13:03       ` Stephen Rothwell
2013-01-11 13:16       ` James Hogan
2013-01-11 13:16         ` James Hogan
2013-01-11 15:02       ` James Hogan
2013-01-11 15:02         ` James Hogan
2013-01-12  1:09         ` Stephen Rothwell
2013-01-12  1:09           ` Stephen Rothwell
2013-01-12  9:55           ` James Hogan
2013-01-12 14:56             ` Stephen Rothwell
2013-01-19  5:55             ` Al Viro
2013-01-22 11:35               ` James Hogan
2013-01-22 11:35                 ` James Hogan
2013-01-25 17:04 ` James Hogan
2013-01-25 17:04   ` James Hogan
2013-01-26  0:25   ` Arnd Bergmann
2013-01-26 21:53     ` James Hogan
2013-01-28  7:10     ` Vineet Gupta
2013-01-28  7:10       ` Vineet Gupta
2013-01-28 11:08       ` James Hogan
2013-01-28 11:08         ` James Hogan
2013-01-29 12:45       ` ARC Port for linux-next (was Re: [PATCH v3 00/44] Meta Linux Kernel Port) Vineet Gupta
2013-01-29 12:45         ` Vineet Gupta
2013-01-29 20:44       ` [PATCH v3 00/44] Meta Linux Kernel Port Stephen Rothwell
2013-01-29 20:44         ` Stephen Rothwell
2013-01-29 20:50         ` Stephen Rothwell
2013-01-29 20:50           ` Stephen Rothwell
2013-02-01  8:18         ` linux-net for ARC port Vineet Gupta
2013-02-01  8:18           ` Vineet Gupta
2013-02-01 12:05           ` Stephen Rothwell
2013-02-01 12:05             ` Stephen Rothwell
2013-02-01 12:33             ` Geert Uytterhoeven
2013-02-01 12:59               ` Stephen Rothwell
2013-02-05 13:54         ` [PATCH v3 00/44] Meta Linux Kernel Port Vineet Gupta
2013-02-05 13:54           ` Vineet Gupta
2013-02-05 23:40           ` rkuo

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=51026E55.80905@imgtec.com \
    --to=james.hogan@imgtec.com \
    --cc=akpm@linux-foundation.org \
    --cc=alan@lxorguk.ukuu.org.uk \
    --cc=cesarb@cesarb.net \
    --cc=davem@davemloft.net \
    --cc=gregkh@linuxfoundation.org \
    --cc=joe@perches.com \
    --cc=jslaby@suse.cz \
    --cc=linux-arch@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mchehab@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.